diff --git a/src/lib/components/Product.svelte b/src/lib/components/Product.svelte index 0199b3c2..00ee5182 100644 --- a/src/lib/components/Product.svelte +++ b/src/lib/components/Product.svelte @@ -22,9 +22,14 @@ export let priceOverride = null; export let VariantSelector = null; + + // Per-variant timeline override carried by VariantSelector (e.g., harness + // JSON overrides like Tesla B's "4-8 weeks"). Treated as the authoritative + // backorder signal when present — shown regardless of Shopify stock state. + let variantBackordered = null; function handleVariantSelection(variant) { selectedVariantId = variant?.id || null; - backordered = variant?.currentlyNotInStock ? (variant.backordered || '1-12 weeks') : null; + variantBackordered = variant?.backordered || null; } let currentImageIndex = 0; @@ -72,17 +77,23 @@ } } + // Priority: static prop > per-variant JSON override (always shown) > + // Shopify-driven default ('1-12 weeks' when currentlyNotInStock). + $: effectiveBackordered = backordered + || variantBackordered + || (selectedVariant?.currentlyNotInStock ? '1-12 weeks' : null); + let addToCartLabel; $: { if (disableBuyButtonText) { addToCartLabel = disableBuyButtonText; } else if (forceOutOfStock || (selectedVariant && !selectedVariant.availableForSale)) { addToCartLabel = "Out of stock"; - if (backordered) { - addToCartLabel += ` (${backorderedPrefix}${backordered})`; + if (effectiveBackordered) { + addToCartLabel += ` (${backorderedPrefix}${effectiveBackordered})`; } - } else if (backordered) { - addToCartLabel = `Add to cart (${backorderedPrefix}${backordered})`; + } else if (effectiveBackordered) { + addToCartLabel = `Add to cart (${backorderedPrefix}${effectiveBackordered})`; } else { addToCartLabel = "Add to cart"; } diff --git a/src/lib/components/ProductDescriptions/CommaFour.svelte b/src/lib/components/ProductDescriptions/CommaFour.svelte index a6293cf0..e727abb3 100644 --- a/src/lib/components/ProductDescriptions/CommaFour.svelte +++ b/src/lib/components/ProductDescriptions/CommaFour.svelte @@ -79,7 +79,7 @@ backordered = null; disableBuyButtonText = null; } else if (value) { - backordered = value.currentlyNotInStock ? (value.backordered || '1-12 weeks') : null; + backordered = value.backordered || (value.currentlyNotInStock ? '1-12 weeks' : null); disableBuyButtonText = null; } else { backordered = null; diff --git a/src/lib/data/products.js b/src/lib/data/products.js index a6e91bae..a932796a 100644 --- a/src/lib/data/products.js +++ b/src/lib/data/products.js @@ -290,6 +290,7 @@ export const products = { "harness-connector": { title: "harness connector", id: "gid://shopify/Product/4310075310143", + inventoryFromProductId: "gid://shopify/Product/4447447908415", // shares physical inventory with car-harness; matched by variant title route: "/shop/harness-connector", category: "accessories", price: "$50", diff --git a/src/routes/shop/[product]/+page.js b/src/routes/shop/[product]/+page.js index 5e74c9c0..f9eb750b 100644 --- a/src/routes/shop/[product]/+page.js +++ b/src/routes/shop/[product]/+page.js @@ -39,11 +39,33 @@ export async function load({ url, params }) { } } - // Fetch from Shopify - const response = await getProduct(productInfo.id); + // Fetch from Shopify (in parallel with optional sibling for inventory overlay) + const [response, sourceResponse] = await Promise.all([ + getProduct(productInfo.id), + productInfo.inventoryFromProductId ? getProduct(productInfo.inventoryFromProductId) : Promise.resolve(null), + ]); if (response.status === 200) { const product = response.body?.data?.product; if (product) { + // TODO: remove and use product bundles + // When this product shares physical stock with another Shopify product, + // overlay the sibling's inventory flags onto our variants by title match. + if (sourceResponse?.status === 200) { + const sourceVariants = sourceResponse.body?.data?.product?.variants?.nodes || []; + const sourceByTitle = new Map(sourceVariants.map(v => [v.title, v])); + product.variants = { + ...product.variants, + nodes: (product.variants?.nodes || []).map(v => { + const source = sourceByTitle.get(v.title); + if (!source) return v; + return { + ...v, + currentlyNotInStock: source.currentlyNotInStock, + availableForSale: source.availableForSale, + }; + }), + }; + } return { product: { ...product,