232 lines
8.0 KiB
JavaScript
232 lines
8.0 KiB
JavaScript
|
||
import React, { useEffect, useState } from "react";
|
||
import { json } from "@remix-run/node";
|
||
import { useLoaderData, Form, useActionData } from "@remix-run/react";
|
||
import {
|
||
Page,
|
||
Layout,
|
||
IndexTable,
|
||
Card,
|
||
Thumbnail,
|
||
TextContainer,
|
||
Spinner,
|
||
Button,
|
||
TextField,
|
||
Banner,
|
||
} from "@shopify/polaris";
|
||
import { authenticate } from "../shopify.server";
|
||
import { TitleBar } from "@shopify/app-bridge-react";
|
||
|
||
export const loader = async ({ request }) => {
|
||
const { admin } = await authenticate.admin(request);
|
||
const { getTurn14AccessTokenFromMetafield } = await import("../utils/turn14Token.server");
|
||
const accessToken = await getTurn14AccessTokenFromMetafield(request);
|
||
|
||
const res = await admin.graphql(`{
|
||
shop {
|
||
metafield(namespace: "turn14", key: "selected_brands") {
|
||
value
|
||
}
|
||
}
|
||
}`);
|
||
const data = await res.json();
|
||
const rawValue = data?.data?.shop?.metafield?.value;
|
||
|
||
let brands = [];
|
||
try {
|
||
brands = JSON.parse(rawValue);
|
||
} catch (err) {
|
||
console.error("❌ Failed to parse metafield value:", err);
|
||
}
|
||
|
||
return json({ brands, accessToken });
|
||
};
|
||
|
||
export default function ManageBrandProducts() {
|
||
const actionData = useActionData();
|
||
const { brands, accessToken } = useLoaderData();
|
||
const [expandedBrand, setExpandedBrand] = useState(null);
|
||
const [itemsMap, setItemsMap] = useState({});
|
||
const [loadingMap, setLoadingMap] = useState({});
|
||
const [productCount, setProductCount] = useState("0");
|
||
|
||
|
||
|
||
|
||
const [initialLoad, setInitialLoad] = useState(true);
|
||
|
||
// Function to toggle all brands
|
||
const toggleAllBrands = async () => {
|
||
for (const brand of brands) {
|
||
await toggleBrandItems(brand.id);
|
||
}
|
||
};
|
||
|
||
// Run on initial load
|
||
useEffect(() => {
|
||
if (initialLoad && brands.length > 0) {
|
||
toggleAllBrands();
|
||
setInitialLoad(false);
|
||
}
|
||
}, [brands, initialLoad]);
|
||
|
||
|
||
|
||
const toggleBrandItems = async (brandId) => {
|
||
const isExpanded = expandedBrand === brandId;
|
||
if (isExpanded) {
|
||
setExpandedBrand(null);
|
||
} else {
|
||
setExpandedBrand(brandId);
|
||
|
||
if (!itemsMap[brandId]) {
|
||
setLoadingMap((prev) => ({ ...prev, [brandId]: true }));
|
||
try {
|
||
// const res = await fetch(`https://turn14.data4autos.com/v1/items/brand/${brandId}?page=1`, {
|
||
const res = await fetch(`https://turn14.data4autos.com/v1/items/brandallitems/${brandId}`, {
|
||
headers: {
|
||
Authorization: `Bearer ${accessToken}`,
|
||
"Content-Type": "application/json",
|
||
},
|
||
});
|
||
const data = await res.json();
|
||
setProductCount(data.length)
|
||
setItemsMap((prev) => ({ ...prev, [brandId]: data }));
|
||
} catch (err) {
|
||
console.error("Error fetching items:", err);
|
||
}
|
||
setLoadingMap((prev) => ({ ...prev, [brandId]: false }));
|
||
} else {
|
||
setProductCount(itemsMap[brandId].length)
|
||
}
|
||
}
|
||
};
|
||
|
||
return (
|
||
<Page title="Data4Autos Turn14 Manage Brand Products">
|
||
<TitleBar title="Data4Autos Turn14 Integration" />
|
||
<Layout>
|
||
{brands.length === 0 ? (
|
||
<Layout.Section>
|
||
<Card sectioned>
|
||
<p>No brands selected yet.</p>
|
||
</Card>
|
||
</Layout.Section>
|
||
) : (
|
||
<Layout.Section>
|
||
<Card>
|
||
<IndexTable
|
||
resourceName={{ singular: "brand", plural: "brands" }}
|
||
itemCount={brands.length}
|
||
headings={[
|
||
{ title: "Brand ID" },
|
||
{ title: "Logo" },
|
||
{ title: "Action" },
|
||
{ title: "Products Count" },
|
||
]}
|
||
selectable={false}
|
||
>
|
||
{brands.map((brand, index) => (
|
||
<IndexTable.Row id={brand.id.toString()} key={brand.id} position={index}>
|
||
<IndexTable.Cell>{brand.id}</IndexTable.Cell>
|
||
<IndexTable.Cell>
|
||
<Thumbnail
|
||
source={
|
||
brand.logo ||
|
||
"https://cdn.shopify.com/s/files/1/0757/9955/files/no-image_280x@2x.png"
|
||
}
|
||
alt={brand.name}
|
||
size="small"
|
||
/>
|
||
</IndexTable.Cell>
|
||
<IndexTable.Cell>
|
||
<Button onClick={() => toggleBrandItems(brand.id)}>
|
||
{expandedBrand === brand.id ? "Hide Products" : "Show Products"}
|
||
</Button>
|
||
</IndexTable.Cell>
|
||
<IndexTable.Cell>{itemsMap[brand.id]?.length || 0}</IndexTable.Cell>
|
||
</IndexTable.Row>
|
||
))}
|
||
</IndexTable>
|
||
</Card>
|
||
</Layout.Section>
|
||
)}
|
||
|
||
{brands.map(
|
||
(brand) =>
|
||
expandedBrand === brand.id && (
|
||
<Layout.Section fullWidth key={brand.id + "-expanded"}>
|
||
<Card sectioned>
|
||
{actionData?.success && (
|
||
<Banner title="✅ Product created!" status="success">
|
||
<p>
|
||
{actionData.results.map((r) => (
|
||
<span key={r.variant.id}>
|
||
Product {r.productId} – Variant {r.variant.id} @ ${r.variant.price} (SKU: {r.variant.sku})<br />
|
||
</span>
|
||
))}
|
||
</p>
|
||
</Banner>
|
||
)}
|
||
|
||
</Card>
|
||
|
||
<Card title={`Items from ${brand.name}`} sectioned>
|
||
{loadingMap[brand.id] ? (
|
||
<Spinner accessibilityLabel="Loading items" size="small" />
|
||
) : (
|
||
|
||
|
||
<div style={{ paddingTop: "1rem" }}>
|
||
<Form method="post">
|
||
<input type="hidden" name="brandId" value={brand.id} />
|
||
<TextField
|
||
label="Number of products to add"
|
||
type="number"
|
||
name="productCount"
|
||
value={productCount}
|
||
onChange={setProductCount}
|
||
autoComplete="off"
|
||
/>
|
||
<Button submit primary style={{ marginTop: "1rem" }}>
|
||
Add First {productCount} Products to Store
|
||
</Button>
|
||
</Form>
|
||
Total Products Count : {(itemsMap[brand.id] || []).length}
|
||
{(
|
||
itemsMap[brand.id] && itemsMap[brand.id].length > 0
|
||
? itemsMap[brand.id]
|
||
: []
|
||
).map((item) => (
|
||
<Card key={item?.id} title={item?.attributes.product_name} sectioned>
|
||
<Layout>
|
||
<Layout.Section oneThird>
|
||
<Thumbnail
|
||
source={
|
||
item?.attributes.thumbnail ||
|
||
"https://cdn.shopify.com/s/files/1/0757/9955/files/no-image_280x@2x.png"
|
||
}
|
||
alt={item?.attributes.product_name}
|
||
size="large"
|
||
/>
|
||
</Layout.Section>
|
||
<Layout.Section>
|
||
<TextContainer spacing="tight">
|
||
<p><strong>Part Number:</strong> {item?.attributes.part_number}</p>
|
||
<p><strong>Category:</strong> {item?.attributes.category} > {item?.attributes.subcategory}</p>
|
||
</TextContainer>
|
||
</Layout.Section>
|
||
</Layout>
|
||
</Card>
|
||
))}
|
||
</div>
|
||
)}
|
||
</Card>
|
||
</Layout.Section>
|
||
)
|
||
)}
|
||
</Layout>
|
||
</Page>
|
||
);
|
||
}
|