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, InlineError, } 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 const action = async ({ request }) => { // const { admin } = await authenticate.admin(request); // const formData = await request.formData(); // const brandId = formData.get("brandId"); // const rawCount = formData.get("productCount"); // const productCount = parseInt(rawCount, 10) || 10; // const { getTurn14AccessTokenFromMetafield } = await import("../utils/turn14Token.server"); // const accessToken = await getTurn14AccessTokenFromMetafield(request); // // Fetch items from Turn14 API // const itemsRes = await fetch( // `https://turn14.data4autos.com/v1/items/brandallitems/${brandId}`, // { // headers: { // Authorization: `Bearer ${accessToken}`, // "Content-Type": "application/json", // }, // } // ); // const itemsData = await itemsRes.json(); // function slugify(str) { // return str // .toString() // .trim() // .toLowerCase() // .replace(/[^a-z0-9]+/g, '-') // .replace(/^-+|-+$/g, ''); // } // const items = Array.isArray(itemsData) ? itemsData.slice(0, productCount) : []; // const results = []; // for (const item of items) { // const attrs = item.attributes; // // Build and normalize collection titles // const category = attrs.category; // const subcategory = attrs.subcategory || ""; // const brand = attrs.brand; // const subcats = subcategory // .split(/[,\/]/) // .map((s) => s.trim()) // .filter(Boolean); // const collectionTitles = Array.from( // new Set([category, ...subcats, brand].filter(Boolean)) // ); // // Find or create collections, collect their IDs // const collectionIds = []; // for (const title of collectionTitles) { // const lookupRes = await admin.graphql(` // { // collections(first: 1, query: "title:\\"${title}\\" AND collection_type:manual") { // nodes { id } // } // } // `); // const lookupJson = await lookupRes.json(); // const existing = lookupJson.data.collections.nodes; // if (existing.length) { // collectionIds.push(existing[0].id); // } else { // const createColRes = await admin.graphql(` // mutation($input: CollectionInput!) { // collectionCreate(input: $input) { // collection { id } // userErrors { field message } // } // } // `, { variables: { input: { title } } }); // const createColJson = await createColRes.json(); // const errs = createColJson.data.collectionCreate.userErrors; // if (errs.length) { // throw new Error(`Could not create collection "${title}": ${errs.map(e => e.message).join(", ")}`); // } // collectionIds.push(createColJson.data.collectionCreate.collection.id); // } // } // // Build tags // const tags = [ // attrs.category, // ...subcats, // attrs.brand, // attrs.part_number, // attrs.mfr_part_number, // attrs.price_group, // attrs.units_per_sku && `${attrs.units_per_sku} per SKU`, // attrs.barcode // ].filter(Boolean).map((t) => t.trim()); // // Prepare media inputs // const mediaInputs = (attrs.files || []) // .filter((f) => f.type === "Image" && f.url) // .map((file) => ({ // originalSource: file.url, // mediaContentType: "IMAGE", // alt: `${attrs.product_name} — ${file.media_content}`, // })); // // Pick the longest "Market Description" or fallback to part_description // const marketDescs = (attrs.descriptions || []) // .filter((d) => d.type === "Market Description") // .map((d) => d.description); // const descriptionHtml = marketDescs.length // ? marketDescs.reduce((a, b) => (b.length > a.length ? b : a)) // : attrs.part_description; // // Create product + attach to collections + add media // const createProdRes = await admin.graphql(` // mutation($prod: ProductCreateInput!, $media: [CreateMediaInput!]) { // productCreate(product: $prod, media: $media) { // product { // id // variants(first: 1) { // nodes { id inventoryItem { id } } // } // } // userErrors { field message } // } // } // `, { // variables: { // prod: { // title: attrs.product_name, // descriptionHtml: descriptionHtml, // vendor: attrs.brand, // productType: attrs.category, // handle: slugify(attrs.part_number || attrs.product_name), // tags, // collectionsToJoin: collectionIds, // status: "ACTIVE", // }, // media: mediaInputs, // }, // }); // const createProdJson = await createProdRes.json(); // const prodErrs = createProdJson.data.productCreate.userErrors; // if (prodErrs.length) { // const taken = prodErrs.some((e) => /already in use/i.test(e.message)); // if (taken) { // results.push({ skippedHandle: attrs.part_number, reason: "handle in use" }); // continue; // } // throw new Error(`ProductCreate errors: ${prodErrs.map(e => e.message).join(", ")}`); // } // const product = createProdJson.data.productCreate.product; // const variantNode = product.variants.nodes[0]; // const variantId = variantNode.id; // const inventoryItemId = variantNode.inventoryItem.id; // // Bulk-update variant (price, compare-at, barcode) // const price = parseFloat(attrs.price) || 1000; // const comparePrice = parseFloat(attrs.compare_price) || null; // const barcode = attrs.barcode || ""; // const bulkRes = await admin.graphql(` // mutation($productId: ID!, $variants: [ProductVariantsBulkInput!]!) { // productVariantsBulkUpdate(productId: $productId, variants: $variants) { // productVariants { id price compareAtPrice barcode } // userErrors { field message } // } // } // `, { // variables: { // productId: product.id, // variants: [{ // id: variantId, // price, // ...(comparePrice !== null && { compareAtPrice: comparePrice }), // ...(barcode && { barcode }), // }], // }, // }); // const bulkJson = await bulkRes.json(); // const bulkErrs = bulkJson.data.productVariantsBulkUpdate.userErrors; // if (bulkErrs.length) { // throw new Error(`Bulk update errors: ${bulkErrs.map(e => e.message).join(", ")}`); // } // const updatedVariant = bulkJson.data.productVariantsBulkUpdate.productVariants[0]; // // Update inventory item (SKU, cost & weight) // const costPerItem = parseFloat(attrs.purchase_cost) || 0; // const weightValue = parseFloat(attrs.dimensions?.[0]?.weight) || 0; // const invRes = await admin.graphql(` // mutation($id: ID!, $input: InventoryItemInput!) { // inventoryItemUpdate(id: $id, input: $input) { // inventoryItem { // id // sku // measurement { // weight { value unit } // } // } // userErrors { field message } // } // } // `, { // variables: { // id: inventoryItemId, // input: { // sku: attrs.part_number, // cost: costPerItem, // measurement: { // weight: { value: weightValue, unit: "POUNDS" } // }, // }, // }, // }); // const invJson = await invRes.json(); // const invErrs = invJson.data.inventoryItemUpdate.userErrors; // if (invErrs.length) { // throw new Error(`Inventory update errors: ${invErrs.map(e => e.message).join(", ")}`); // } // const inventoryItem = invJson.data.inventoryItemUpdate.inventoryItem; // // Collect results // results.push({ // productId: product.id, // variant: { // id: updatedVariant.id, // price: updatedVariant.price, // compareAtPrice: updatedVariant.compareAtPrice, // sku: inventoryItem.sku, // barcode: updatedVariant.barcode, // weight: inventoryItem.measurement.weight.value, // weightUnit: inventoryItem.measurement.weight.unit, // }, // collections: collectionTitles, // tags, // }); // } // return json({ success: true, results }); // }; // export const action = async ({ request }) => { // const { admin } = await authenticate.admin(request); // const formData = await request.formData(); // const brandId = formData.get("brandId"); // const rawCount = formData.get("productCount"); // const productCount = parseInt(rawCount, 10) || 10; // const { getTurn14AccessTokenFromMetafield } = await import("../utils/turn14Token.server"); // const accessToken = await getTurn14AccessTokenFromMetafield(request); // // Fetch items from Turn14 API // console.log("Fetching items from Turn14 API..."); // const itemsRes = await fetch( // `https://turn14.data4autos.com/v1/items/brandallitems/${brandId}`, // { // headers: { // Authorization: `Bearer ${accessToken}`, // "Content-Type": "application/json", // }, // } // ); // const itemsData = await itemsRes.json(); // console.log("Items data fetched:", itemsData); // function slugify(str) { // return str // .toString() // .trim() // .toLowerCase() // .replace(/[^a-z0-9]+/g, '-') // .replace(/^-+|-+$/g, ''); // } // const items = Array.isArray(itemsData) ? itemsData.slice(0, productCount) : []; // console.log(`Processing ${items.length} items...`); // const results = []; // for (const item of items) { // const attrs = item.attributes; // console.log("Processing item:", attrs); // // Build and normalize collection titles // const category = attrs.category; // const subcategory = attrs.subcategory || ""; // const brand = attrs.brand; // const subcats = subcategory // .split(/[,\/]/) // .map((s) => s.trim()) // .filter(Boolean); // const collectionTitles = Array.from( // new Set([category, ...subcats, brand].filter(Boolean)) // ); // console.log("Collection Titles:", collectionTitles); // // Find or create collections, collect their IDs // const collectionIds = []; // for (const title of collectionTitles) { // console.log(`Searching for collection with title: ${title}`); // const lookupRes = await admin.graphql(` // { // collections(first: 1, query: "title:\\"${title}\\" AND collection_type:manual") { // nodes { id } // } // } // `); // const lookupJson = await lookupRes.json(); // console.log("Lookup response for collections:", lookupJson); // const existing = lookupJson.data.collections.nodes; // if (existing.length) { // console.log(`Found existing collection for title: ${title}`); // collectionIds.push(existing[0].id); // } else { // console.log(`Creating new collection for title: ${title}`); // const createColRes = await admin.graphql(` // mutation($input: CollectionInput!) { // collectionCreate(input: $input) { // collection { id } // userErrors { field message } // } // } // `, { variables: { input: { title } } }); // const createColJson = await createColRes.json(); // console.log("Create collection response:", createColJson); // const errs = createColJson.data.collectionCreate.userErrors; // if (errs.length) { // throw new Error(`Could not create collection "${title}": ${errs.map(e => e.message).join(", ")}`); // } // collectionIds.push(createColJson.data.collectionCreate.collection.id); // } // } // // Build tags // const tags = [ // attrs.category, // ...subcats, // attrs.brand, // attrs.part_number, // attrs.mfr_part_number, // attrs.price_group, // attrs.units_per_sku && `${attrs.units_per_sku} per SKU`, // attrs.barcode // ].filter(Boolean).map((t) => t.trim()); // console.log("Tags:", tags); // // Prepare media inputs // const mediaInputs = (attrs.files || []) // .filter((f) => f.type === "Image" && f.url) // .map((file) => ({ // originalSource: file.url, // mediaContentType: "IMAGE", // alt: `${attrs.product_name} — ${file.media_content}`, // })); // console.log("Media inputs:", mediaInputs); // // Pick the longest "Market Description" or fallback to part_description // const marketDescs = (attrs.descriptions || []) // .filter((d) => d.type === "Market Description") // .map((d) => d.description); // const descriptionHtml = marketDescs.length // ? marketDescs.reduce((a, b) => (b.length > a.length ? b : a)) // : attrs.part_description; // console.log("Description HTML:", descriptionHtml); // // Create product + attach to collections + add media // const createProdRes = await admin.graphql(` // mutation($prod: ProductCreateInput!, $media: [CreateMediaInput!]) { // productCreate(product: $prod, media: $media) { // product { // id // variants(first: 1) { // nodes { id inventoryItem { id } } // } // } // userErrors { field message } // } // } // `, { // variables: { // prod: { // title: attrs.product_name, // descriptionHtml: descriptionHtml, // vendor: attrs.brand, // productType: attrs.category, // handle: slugify(attrs.part_number || attrs.product_name), // tags, // collectionsToJoin: collectionIds, // status: "ACTIVE", // }, // media: mediaInputs, // }, // }); // const createProdJson = await createProdRes.json(); // console.log("Create product response:", createProdJson); // const prodErrs = createProdJson.data.productCreate.userErrors; // if (prodErrs.length) { // const taken = prodErrs.some((e) => /already in use/i.test(e.message)); // if (taken) { // results.push({ skippedHandle: attrs.part_number, reason: "handle in use" }); // continue; // } // throw new Error(`ProductCreate errors: ${prodErrs.map(e => e.message).join(", ")}`); // } // const product = createProdJson.data.productCreate.product; // const variantNode = product.variants.nodes[0]; // const variantId = variantNode.id; // const inventoryItemId = variantNode.inventoryItem.id; // // Fetch the Online Store publication ID // console.log("Fetching Online Store publication ID..."); // const publicationsRes = await admin.graphql(` // query { // publications(first: 10) { // edges { // node { // id // name // } // } // } // } // `); // const publicationsJson = await publicationsRes.json(); // console.log("Publications response:", publicationsJson); // const onlineStorePublication = publicationsJson.data.publications.edges.find(pub => pub.node.name === 'Online Store'); // console.log("Online Store Publication:", onlineStorePublication); // const onlineStorePublicationId = onlineStorePublication ? onlineStorePublication.node.id : null; // if (onlineStorePublicationId) { // console.log("Publishing product to Online Store..."); // // Publish the product to the Online Store // const publishRes = await admin.graphql(` // mutation($id: ID!, $publicationId: ID!) { // publishablePublish(id: $id, input: { publicationId: $publicationId }) { // publishable { // ... on Product { // id // title // status // } // } // userErrors { field message } // } // } // `, { // variables: { // id: product.id, // publicationId: onlineStorePublicationId, // }, // }); // const publishJson = await publishRes.json(); // console.log("Publish response:", publishJson); // const publishErrs = publishJson.data.publishablePublish.userErrors; // if (publishErrs.length) { // throw new Error(`Publish errors: ${publishErrs.map(e => e.message).join(", ")}`); // } // } else { // throw new Error("Online Store publication not found."); // } // // Update inventory item (SKU, cost & weight) // const costPerItem = parseFloat(attrs.purchase_cost) || 0; // const weightValue = parseFloat(attrs.dimensions?.[0]?.weight) || 0; // console.log("Updating inventory item..."); // const invRes = await admin.graphql(` // mutation($id: ID!, $input: InventoryItemInput!) { // inventoryItemUpdate(id: $id, input: $input) { // inventoryItem { // id // sku // measurement { // weight { value unit } // } // } // userErrors { field message } // } // } // `, { // variables: { // id: inventoryItemId, // input: { // sku: attrs.part_number, // cost: costPerItem, // measurement: { // weight: { value: weightValue, unit: "POUNDS" } // }, // }, // }, // }); // const invJson = await invRes.json(); // console.log("Inventory update response:", invJson); // const invErrs = invJson.data.inventoryItemUpdate.userErrors; // if (invErrs.length) { // throw new Error(`Inventory update errors: ${invErrs.map(e => e.message).join(", ")}`); // } // const inventoryItem = invJson.data.inventoryItemUpdate.inventoryItem; // results.push({ // productId: product.id, // variant: { // id: variantId, // Use the variantId from variantNode // price: variantNode.price, // Use price from variantNode // compareAtPrice: variantNode.compareAtPrice, // Use compareAtPrice from variantNode // sku: inventoryItem.sku, // SKU from the updated inventory item // barcode: variantNode.barcode, // Barcode from the variant // weight: inventoryItem.measurement.weight.value, // Weight from inventory item // weightUnit: inventoryItem.measurement.weight.unit, // Weight unit from inventory item // }, // collections: collectionTitles, // tags, // }); // } // return json({ success: true, results }); // }; export const action = async ({ request }) => { const { admin } = await authenticate.admin(request); const formData = await request.formData(); const brandId = formData.get("brandId"); const rawCount = formData.get("productCount"); const productCount = parseInt(rawCount, 10) || 10; const { getTurn14AccessTokenFromMetafield } = await import("../utils/turn14Token.server"); const accessToken = await getTurn14AccessTokenFromMetafield(request); // Fetch items from Turn14 API console.log("Fetching items from Turn14 API..."); const itemsRes = await fetch( `https://turn14.data4autos.com/v1/items/brandallitems/${brandId}`, { headers: { Authorization: `Bearer ${accessToken}`, "Content-Type": "application/json", }, } ); const itemsData = await itemsRes.json(); console.log("Items data fetched:", itemsData); function slugify(str) { return str .toString() .trim() .toLowerCase() .replace(/[^a-z0-9]+/g, '-') .replace(/^-+|-+$/g, ''); } const items = Array.isArray(itemsData) ? itemsData.slice(0, productCount) : []; console.log(`Processing ${items.length} items...`); const results = []; for (const item of items) { const attrs = item.attributes; console.log("Processing item:", attrs); // Build and normalize collection titles const category = attrs.category; const subcategory = attrs.subcategory || ""; const brand = attrs.brand; const subcats = subcategory .split(/[,\/]/) .map((s) => s.trim()) .filter(Boolean); const collectionTitles = Array.from( new Set([category, ...subcats, brand].filter(Boolean)) ); console.log("Collection Titles:", collectionTitles); // Find or create collections, collect their IDs const collectionIds = []; for (const title of collectionTitles) { console.log(`Searching for collection with title: ${title}`); const lookupRes = await admin.graphql(` { collections(first: 1, query: "title:\\"${title}\\" AND collection_type:manual") { nodes { id } } } `); const lookupJson = await lookupRes.json(); console.log("Lookup response for collections:", lookupJson); const existing = lookupJson.data?.collections?.nodes || []; if (existing.length) { console.log(`Found existing collection for title: ${title}`); collectionIds.push(existing[0]?.id); // Use optional chaining } else { console.log(`Creating new collection for title: ${title}`); const createColRes = await admin.graphql(` mutation($input: CollectionInput!) { collectionCreate(input: $input) { collection { id } userErrors { field message } } }`, { variables: { input: { title } } }); const createColJson = await createColRes.json(); console.log("Create collection response:", createColJson); const errs = createColJson.data?.collectionCreate?.userErrors || []; if (errs.length) { throw new Error(`Could not create collection "${title}": ${errs.map(e => e.message).join(", ")}`); } collectionIds.push(createColJson.data?.collectionCreate?.collection?.id); // Optional chaining here too } } // Build tags const tags = [ attrs.category, ...subcats, attrs.brand, attrs.part_number, attrs.mfr_part_number, attrs.price_group, attrs.units_per_sku && `${attrs.units_per_sku} per SKU`, attrs.barcode ].filter(Boolean).map((t) => t.trim()); console.log("Tags:", tags); // Prepare media inputs const mediaInputs = (attrs.files || []) .filter((f) => f.type === "Image" && f.url) .map((file) => ({ originalSource: file.url, mediaContentType: "IMAGE", alt: `${attrs.product_name} — ${file.media_content}`, })); console.log("Media inputs:", mediaInputs); // Pick the longest "Market Description" or fallback to part_description const marketDescs = (attrs.descriptions || []) .filter((d) => d.type === "Market Description") .map((d) => d.description); const descriptionHtml = marketDescs.length ? marketDescs.reduce((a, b) => (b.length > a.length ? b : a)) : attrs.part_description; console.log("Description HTML:", descriptionHtml); // Create product + attach to collections + add media // const createProdRes = await admin.graphql(` // mutation($prod: ProductCreateInput!, $media: [CreateMediaInput!]) { // productCreate(product: $prod, media: $media) { // product { // id // variants(first: 1) { // nodes { id inventoryItem { id } price compareAtPrice barcode } // } // } // userErrors { field message } // } // } // `, { // variables: { // prod: { // title: attrs.product_name, // descriptionHtml: descriptionHtml, // vendor: attrs.brand, // productType: attrs.category, // handle: slugify(attrs.part_number || attrs.product_name), // tags, // collectionsToJoin: collectionIds, // status: "ACTIVE", // }, // media: mediaInputs, // }, // }); const createProdRes = await admin.graphql(` mutation($prod: ProductCreateInput!, $media: [CreateMediaInput!]) { productCreate(product: $prod, media: $media) { product { id variants(first: 1) { nodes { id inventoryItem { id } price compareAtPrice barcode } } } userErrors { field message } } } `, { variables: { prod: { title: attrs.product_name, descriptionHtml: descriptionHtml, vendor: attrs.brand, productType: attrs.category, handle: slugify(item.id+"-"+attrs.mfr_part_number || attrs.product_name), tags, collectionsToJoin: collectionIds, status: "ACTIVE", }, media: mediaInputs, }, }); const createProdJson = await createProdRes.json(); console.log("Create product response:", createProdJson); const prodErrs = createProdJson.data?.productCreate?.userErrors || []; if (prodErrs.length) { const taken = prodErrs.some((e) => /already in use/i.test(e.message)); if (taken) { results.push({ skippedHandle: attrs.part_number, reason: "handle in use" }); continue; } throw new Error(`ProductCreate errors: ${prodErrs.map(e => e.message).join(", ")}`); } const product = createProdJson.data.productCreate.product; const variantNode = product.variants?.nodes?.[0]; if (!variantNode) { console.error("Variant node is undefined for product:", product.id); continue; } const variantId = variantNode.id; const inventoryItemId = variantNode.inventoryItem?.id; // Fetch the Online Store publication ID console.log("Fetching Online Store publication ID..."); const publicationsRes = await admin.graphql(` query { publications(first: 10) { edges { node { id name } } } } `); const publicationsJson = await publicationsRes.json(); console.log("Publications response:", publicationsJson); const onlineStorePublication = publicationsJson.data.publications.edges.find(pub => pub.node.name === 'Online Store'); const onlineStorePublicationId = onlineStorePublication ? onlineStorePublication.node.id : null; if (onlineStorePublicationId) { console.log("Publishing product to Online Store..."); const publishRes = await admin.graphql(` mutation($id: ID!, $publicationId: ID!) { publishablePublish(id: $id, input: { publicationId: $publicationId }) { publishable { ... on Product { id title status } } userErrors { field message } } } `, { variables: { id: product.id, publicationId: onlineStorePublicationId, }, }); const publishJson = await publishRes.json(); console.log("Publish response:", publishJson); const publishErrs = publishJson.data.publishablePublish.userErrors; if (publishErrs.length) { throw new Error(`Publish errors: ${publishErrs.map(e => e.message).join(", ")}`); } } else { throw new Error("Online Store publication not found."); } // Update inventory item (SKU, cost & weight) const costPerItem = parseFloat(attrs.purchase_cost) || 0; const weightValue = parseFloat(attrs.dimensions?.[0]?.weight) || 0; console.log("Updating inventory item..."); const invRes = await admin.graphql(` mutation($id: ID!, $input: InventoryItemInput!) { inventoryItemUpdate(id: $id, input: $input) { inventoryItem { id sku measurement { weight { value unit } } } userErrors { field message } } } `, { variables: { id: inventoryItemId, input: { sku: attrs.part_number, cost: costPerItem, measurement: { weight: { value: weightValue, unit: "POUNDS" } }, }, }, }); const invJson = await invRes.json(); console.log("Inventory update response:", invJson); const invErrs = invJson.data.inventoryItemUpdate.userErrors; if (invErrs.length) { throw new Error(`Inventory update errors: ${invErrs.map(e => e.message).join(", ")}`); } const inventoryItem = invJson.data.inventoryItemUpdate.inventoryItem; // Collect results results.push({ productId: product.id, variant: { id: variantId, price: variantNode.price, compareAtPrice: variantNode.compareAtPrice, sku: inventoryItem.sku, barcode: variantNode.barcode, weight: inventoryItem.measurement.weight.value, weightUnit: inventoryItem.measurement.weight.unit, }, collections: collectionTitles, tags, }); } return json({ success: true, results }); }; 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("10"); const [initialLoad, setInitialLoad] = useState(true); const toggleAllBrands = async () => { for (const brand of brands) { await toggleBrandItems(brand.id); } }; 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/brandallitems/${brandId}`, { // headers: { // Authorization: `Bearer ${accessToken}`, // "Content-Type": "application/json", // }, // }); // const data = await res.json(); // setItemsMap((prev) => ({ ...prev, [brandId]: data })); // } catch (err) { // console.error("Error fetching items:", err); // } // setLoadingMap((prev) => ({ ...prev, [brandId]: false })); // } // } // }; 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/brandallitems/${brandId}`, { headers: { Authorization: `Bearer ${accessToken}`, "Content-Type": "application/json", }, }); const data = await res.json(); // Ensure we have an array of valid items const validItems = Array.isArray(data) ? data.filter(item => item && item.id && item.attributes) : []; setItemsMap((prev) => ({ ...prev, [brandId]: validItems })); } catch (err) { console.error("Error fetching items:", err); setItemsMap((prev) => ({ ...prev, [brandId]: [] })); // Set empty array on error } setLoadingMap((prev) => ({ ...prev, [brandId]: false })); } } }; return ( {brands.length === 0 ? (

No brands selected yet.

) : ( {brands.map((brand, index) => ( {brand.id} {itemsMap[brand.id]?.length || 0} ))} )} {brands.map( (brand) => expandedBrand === brand.id && ( {actionData?.success && (

{actionData.results.map((r) => ( Product {r.productId} – Variant {r.variant.id} @ ${r.variant.price} (SKU: {r.variant.sku})
))}

)}
{loadingMap[brand.id] ? ( ) : (
{/*

Total Products Available: {(itemsMap[brand.id] || []).length}

{( itemsMap[brand.id] && itemsMap[brand.id].length > 0 ? itemsMap[brand.id] : [] ).map((item) => (

Part Number: {item?.attributes.part_number}

Category: {item?.attributes.category} > {item?.attributes.subcategory}

Price: ${item?.attributes.price}

Description: {item?.attributes.part_description}

))} */} {( itemsMap[brand.id] && itemsMap[brand.id].length > 0 ? itemsMap[brand.id].filter(item => item && item.id) // Filter out null/undefined items : [] ).map((item) => (

Part Number: {item?.attributes?.part_number || 'N/A'}

Category: {item?.attributes?.category || 'N/A'} > {item?.attributes?.subcategory || 'N/A'}

Price: ${item?.attributes?.price || '0.00'}

Description: {item?.attributes?.part_description || 'No description available'}

))}
)}
) )}
); }