{"version":3,"file":"variants.DccSOoAR.mjs","names":[],"sources":["https:/framerusercontent.com/modules/7YsGDU9oR06chWIhsZlS/Epl6SWFUjul3YlgWOEqD/catalogStore.js","https:/framerusercontent.com/modules/XZkEoAxCgJcoDO8UmVHY/Yk2biHRALXrX9UvLmJVn/catalog.js","https:/framerusercontent.com/modules/KLFToRZvHkZjfdAa7rhG/8VhH46MbfIFdboey72Md/variants.js"],"sourcesContent":["/**\n * FILE: catalogStore.tsx\n * LOCATION: framer/stores/catalogStore.tsx\n * LAYER: Store (Domain State – Catalog)\n *\n * RESPONSIBILITY:\n * - Hold enriched catalog items fetched from /catalog/information\n * - Track fetch status and error state\n * - Provide reactive catalog state to UI\n *\n * DOES NOT:\n * - Call catalog API directly (api/catalog.tsx owns fetching)\n * - Persist catalog state\n * - Hold PDP UI state (selectedVariantId lives in productStore)\n *\n * AUTHORITY:\n * - Canonical catalog data defined by importa-catalog lambda\n *\n * DATA FLOW:\n * api/catalog.tsx → catalogStore → ProductList, AromaList, PDP overrides\n */import{createStore}from\"https://framer.com/m/framer/store.js@^1.0.0\";export const useCatalogStore=createStore({status:\"idle\",items:[],error:null});\nexport const __FramerMetadata__ = {\"exports\":{\"CatalogStatus\":{\"type\":\"tsType\",\"annotations\":{\"framerContractVersion\":\"1\"}},\"CatalogState\":{\"type\":\"tsType\",\"annotations\":{\"framerContractVersion\":\"1\"}},\"useCatalogStore\":{\"type\":\"variable\",\"annotations\":{\"framerContractVersion\":\"1\"}},\"__FramerMetadata__\":{\"type\":\"variable\"}}}\n//# sourceMappingURL=./catalogStore.map","import{RenderTarget}from\"framer\";import{apiFetch,fetchWithRetry}from\"https://framerusercontent.com/modules/pBL9xlYciLON0IYG5ZYl/bYjtj8ul2uIPjV2V279d/apiClient.js\";const CACHE_KEY=\"importa_catalog\";const CACHE_TTL=10*60*1e3// 10 minutes\n;let _fetching=false;let _loaded=false;/* -------------------------------------------------------\n   hydrateFromSessionStorage\n------------------------------------------------------- */export function hydrateFromSessionStorage(setState){try{const raw=sessionStorage.getItem(CACHE_KEY);if(!raw)return;const parsed=JSON.parse(raw);if(Array.isArray(parsed?.items)&&typeof parsed?.timestamp===\"number\"&&Date.now()-parsed.timestamp<CACHE_TTL){setState({status:\"loaded\",items:parsed.items,error:null});_loaded=true;}}catch{}}/* -------------------------------------------------------\n   loadCatalog\n------------------------------------------------------- */export async function loadCatalog(setState){if(RenderTarget.current()===RenderTarget.canvas)return;if(_fetching||_loaded)return;_fetching=true;setState({status:\"loading\",error:null});try{const{ok,data}=await fetchWithRetry(()=>apiFetch(\"/catalog/information\"),{attempts:3,baseDelayMs:1e3});if(!ok||!Array.isArray(data?.items)){throw new Error(data?.error||\"Failed to load catalog\");}const items=data.items;setState({status:\"loaded\",items,error:null});_loaded=true;try{sessionStorage.setItem(CACHE_KEY,JSON.stringify({items,timestamp:Date.now()}));}catch{}}catch(err){setState({status:\"error\",error:err?.message??\"Unknown error\"});}finally{_fetching=false;}}/* -------------------------------------------------------\n   getProductByHandle\n------------------------------------------------------- */export function getProductByHandle(items,handle){if(!handle||!Array.isArray(items))return null;return items.find(p=>p.handle===handle)??null;}\nexport const __FramerMetadata__ = {\"exports\":{\"getProductByHandle\":{\"type\":\"function\",\"annotations\":{\"framerContractVersion\":\"1\"}},\"loadCatalog\":{\"type\":\"function\",\"annotations\":{\"framerContractVersion\":\"1\"}},\"hydrateFromSessionStorage\":{\"type\":\"function\",\"annotations\":{\"framerContractVersion\":\"1\"}},\"__FramerMetadata__\":{\"type\":\"variable\"}}}","/* api/variants.tsx\n *\n * Variant helper utilities shared by PDP, PLP and subscription PDP overrides.\n *\n * Background: variants now have two dimensions — grind (Ground / Whole Beans)\n * and weight (250g / 500g). This module isolates the option-matching logic so\n * override files can ask \"is this variant Whole Beans @ 250g?\" without parsing\n * variant.title or hardcoding the Shopify option name (which differs per\n * shop). Matching is value-based (regex on selectedOptions[].value), so it is\n * agnostic to whether Shopify named the option \"Format\", \"Grind\", \"Style\", etc.\n */const GRIND_REGEX={ground:/^\\s*ground\\s*$/i,whole_beans:/^\\s*(whole\\s+)?beans\\s*$/i};const WEIGHT_REGEX={\"250g\":/^\\s*250\\s*g\\s*$/i,\"500g\":/^\\s*500\\s*g\\s*$/i};function matchValue(value,patterns){const keys=Object.keys(patterns);for(const k of keys){if(patterns[k].test(value))return k;}return null;}export function getGrind(variant){for(const opt of variant?.selectedOptions??[]){const m=matchValue(String(opt?.value??\"\"),GRIND_REGEX);if(m)return m;}return null;}export function getWeight(variant){for(const opt of variant?.selectedOptions??[]){const m=matchValue(String(opt?.value??\"\"),WEIGHT_REGEX);if(m)return m;}return null;}export function findVariant(variants,grind,weight){return variants?.find(v=>getGrind(v)===grind&&getWeight(v)===weight)??null;}export function findVariantByGrind(variants,grind,preferWeight){if(!Array.isArray(variants))return null;if(preferWeight){const exact=findVariant(variants,grind,preferWeight);if(exact)return exact;}return variants.find(v=>getGrind(v)===grind)??null;}export function findVariantByWeight(variants,weight,preferGrind){if(!Array.isArray(variants))return null;if(preferGrind){const exact=findVariant(variants,preferGrind,weight);if(exact)return exact;}return variants.find(v=>getWeight(v)===weight)??null;}// Decision A: PDP default = Whole Beans / 500g, fallback to variants[0] for\n// products that don't have that exact pair (single-variant accessories,\n// non-coffee products, etc.).\nexport function getDefaultVariant(variants){if(!Array.isArray(variants)||variants.length===0)return null;// Prefer the canonical Whole Beans / 500g pick, but only if it's sellable;\n// otherwise seed the first in-stock variant so the PDP + Build-a-Box land on\n// an available option (and the switcher shows that variant's in-stock hero\n// instead of the OOS hero). Falls back to the canonical pair — or variants[0]\n// — when the WHOLE product is out of stock, so seeding never returns null.\nconst preferred=findVariant(variants,\"whole_beans\",\"500g\");if(preferred&&isVariantAvailable(preferred))return preferred;const firstInStock=variants.find(v=>isVariantAvailable(v));if(firstInStock)return firstInStock;return preferred??variants[0];}/**\n * Single source of truth for \"can this variant be purchased right now\".\n *\n * Out of stock when Shopify marks the variant unsellable OR a TRACKED inventory\n * count is depleted. `inventoryQuantity` (Shopify quantityAvailable) is null\n * when inventory isn't tracked — treat null as in-stock so untracked products\n * aren't falsely disabled. Checking the quantity (not just availableForSale) is\n * what catches Shopify's \"Continue selling when out of stock\" setting, where\n * availableForSale stays true at 0 inventory.\n *\n * Shared by AddToCart (click guard + OutOfStock variant) and PDPHeroSwitcher\n * (OOS hero slot) so the two never drift.\n */export function isVariantAvailable(variant){if(!variant)return false;const qty=variant.inventoryQuantity;return variant.availableForSale===true&&!(typeof qty===\"number\"&&qty<=0);}export function hasGrind(variants,grind){return Array.isArray(variants)&&variants.some(v=>getGrind(v)===grind);}export function hasWeight(variants,weight){return Array.isArray(variants)&&variants.some(v=>getWeight(v)===weight);}/* =========================================================\n   In-stock predicates\n   Mirror of hasGrind / hasWeight but constrained to variants\n   that are currently sellable. Used by selectors to render the\n   disabled style when every variant on a given axis is OOS.\n   `hasGrind` / `hasWeight` above are deliberately kept for\n   the seeding effects (which want \"does the product offer\n   this option at all\", not \"is it in stock right now\").\n   NOTE: this uses `isVariantAvailable` (the SAME rule as\n   AddToCart + PDPHeroSwitcher) so the selector disabled state,\n   the OOS hero, and the OOS button never disagree — including\n   the \"availableForSale:true but 0 tracked inventory\" case.\n========================================================= */export function isVariantInStock(variant){return isVariantAvailable(variant);}export function hasGrindInStock(variants,grind){return Array.isArray(variants)&&variants.some(v=>getGrind(v)===grind&&isVariantInStock(v));}export function hasWeightInStock(variants,weight){return Array.isArray(variants)&&variants.some(v=>getWeight(v)===weight&&isVariantInStock(v));}\nexport const __FramerMetadata__ = {\"exports\":{\"isVariantInStock\":{\"type\":\"function\",\"annotations\":{\"framerContractVersion\":\"1\"}},\"getWeight\":{\"type\":\"function\",\"annotations\":{\"framerContractVersion\":\"1\"}},\"hasGrind\":{\"type\":\"function\",\"annotations\":{\"framerContractVersion\":\"1\"}},\"findVariant\":{\"type\":\"function\",\"annotations\":{\"framerContractVersion\":\"1\"}},\"findVariantByGrind\":{\"type\":\"function\",\"annotations\":{\"framerContractVersion\":\"1\"}},\"getGrind\":{\"type\":\"function\",\"annotations\":{\"framerContractVersion\":\"1\"}},\"Grind\":{\"type\":\"tsType\",\"annotations\":{\"framerContractVersion\":\"1\"}},\"hasWeightInStock\":{\"type\":\"function\",\"annotations\":{\"framerContractVersion\":\"1\"}},\"Weight\":{\"type\":\"tsType\",\"annotations\":{\"framerContractVersion\":\"1\"}},\"hasGrindInStock\":{\"type\":\"function\",\"annotations\":{\"framerContractVersion\":\"1\"}},\"hasWeight\":{\"type\":\"function\",\"annotations\":{\"framerContractVersion\":\"1\"}},\"findVariantByWeight\":{\"type\":\"function\",\"annotations\":{\"framerContractVersion\":\"1\"}},\"isVariantAvailable\":{\"type\":\"function\",\"annotations\":{\"framerContractVersion\":\"1\"}},\"getDefaultVariant\":{\"type\":\"function\",\"annotations\":{\"framerContractVersion\":\"1\"}},\"__FramerMetadata__\":{\"type\":\"variable\"}}}\n//# sourceMappingURL=./variants.map"],"mappings":"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"}