1import deferrableData from"deferrable-data";const API_BASE="https://print-room-chatbot-api.vercel.app/api/leavers",ARTWORK_UPLOAD_URL="https://print-room-chatbot-api.vercel.app/api/quote-artwork-upload",MIN_QUANTITY=24,MAX_DECORATIONS=4,ONLINE_STORE_MIN_UNITS=50,PRICING_DEBOUNCE_MS=500,INIT_FETCH_TIMEOUT_MS=15e3,EXCLUDED_TYPES=["apparel","corporate"],STORAGE_KEY="pr-leavers-pkg-v1";function preloadLeaversTurnstile(siteKey){try{window.PrintRoomTurnstile?.preload(siteKey)}catch{}}async function getLeaversTurnstileToken(siteKey){try{const token=await window.PrintRoomTurnstile?.getToken(siteKey,"leavers-submit");return typeof token=="string"?token:""}catch{return""}}function getErrorMessage(error){return error instanceof Error&&error.message?error.message:"Unknown error"}async function fetchJsonWithTimeout(url,{label,timeoutMs=INIT_FETCH_TIMEOUT_MS}={}){const controller=typeof AbortController<"u"?new AbortController:null;let timeoutId=null;try{controller&&(timeoutId=setTimeout(()=>controller.abort(),timeoutMs));const response=await fetch(url,controller?{signal:controller.signal}:void 0),bodyText=await response.text();let payload={};if(bodyText)try{payload=JSON.parse(bodyText)}catch{throw new Error(`${label} returned invalid JSON`)}if(!response.ok){const apiMessage=payload&&typeof payload.error=="string"?payload.error:`HTTP ${response.status}`;throw new Error(`Failed to load ${label} (${response.status}): ${apiMessage}`)}return payload}catch(error){throw error&&error.name==="AbortError"?new Error(`Failed to load ${label}: request timed out after ${timeoutMs}ms`):error}finally{timeoutId&&clearTimeout(timeoutId)}}function emptyDecoration(){return{decorationType:"",detail:"",location:"",artworkType:"custom",artworkFileId:"",standardDesignId:"",_artworkFileName:"",_standardDesignName:""}}function formatPrice(n){return n==null||isNaN(n)?"--":"$"+Number(n).toFixed(2)}const PLACEHOLDER_SVGS={hoody:'<svg viewBox="0 0 200 200" fill="none" xmlns="http://www.w3.org/2000/svg"><rect width="200" height="200" fill="#f5f5f0"/><path d="M60 60 C60 45,80 35,100 35 C120 35,140 45,140 60 L155 80 L145 85 L140 75 L140 160 L60 160 L60 75 L55 85 L45 80 Z" stroke="#ccc" stroke-width="2" fill="none"/><path d="M85 35 C85 50,100 55,100 55 C100 55,115 50,115 35" stroke="#ccc" stroke-width="2" fill="none"/></svg>',crew:'<svg viewBox="0 0 200 200" fill="none" xmlns="http://www.w3.org/2000/svg"><rect width="200" height="200" fill="#f5f5f0"/><path d="M70 55 L55 75 L45 70 L65 45 C75 35,125 35,135 45 L155 70 L145 75 L130 55 L130 160 L70 160 Z" stroke="#ccc" stroke-width="2" fill="none"/><ellipse cx="100" cy="45" rx="15" ry="8" stroke="#ccc" stroke-width="2" fill="none"/></svg>',"crew-neck":'<svg viewBox="0 0 200 200" fill="none" xmlns="http://www.w3.org/2000/svg"><rect width="200" height="200" fill="#f5f5f0"/><path d="M70 55 L55 75 L45 70 L65 45 C75 35,125 35,135 45 L155 70 L145 75 L130 55 L130 160 L70 160 Z" stroke="#ccc" stroke-width="2" fill="none"/><ellipse cx="100" cy="45" rx="15" ry="8" stroke="#ccc" stroke-width="2" fill="none"/></svg>',tee:'<svg viewBox="0 0 200 200" fill="none" xmlns="http://www.w3.org/2000/svg"><rect width="200" height="200" fill="#f5f5f0"/><path d="M70 55 L45 70 L40 60 L65 40 C75 32,125 32,135 40 L160 60 L155 70 L130 55 L130 160 L70 160 Z" stroke="#ccc" stroke-width="2" fill="none"/><ellipse cx="100" cy="40" rx="18" ry="10" stroke="#ccc" stroke-width="2" fill="none"/></svg>',shorts:'<svg viewBox="0 0 200 200" fill="none" xmlns="http://www.w3.org/2000/svg"><rect width="200" height="200" fill="#f5f5f0"/><path d="M55 50 L145 50 L145 100 L115 130 L100 100 L85 130 L55 100 Z" stroke="#ccc" stroke-width="2" fill="none"/><line x1="55" y1="55" x2="145" y2="55" stroke="#ccc" stroke-width="1.5"/></svg>',pants:'<svg viewBox="0 0 200 200" fill="none" xmlns="http://www.w3.org/2000/svg"><rect width="200" height="200" fill="#f5f5f0"/><path d="M60 40 L140 40 L140 90 L120 170 L100 90 L80 170 L60 90 Z" stroke="#ccc" stroke-width="2" fill="none"/><line x1="60" y1="45" x2="140" y2="45" stroke="#ccc" stroke-width="1.5"/></svg>'};function getPlaceholderSvg(productType){return PLACEHOLDER_SVGS[productType]||PLACEHOLDER_SVGS.tee}
1var leaversPackageStore={items:[],init(){this._restore()},get count(){return this.items.length},get total(){return this.items.reduce((sum,item)=>sum+(item.pricing?.lineSubtotal||0),0)},get totalInclGst(){return this.total*1.15},add(item){const idx=this.items.findIndex(i=>i.id===item.id);idx>=0?this.items[idx]=item:this.items.push(item),this.items=[...this.items],this._persist()},remove(id){this.items=this.items.filter(i=>i.id!==id),this._persist()},clear(){this.items=[],this._persist()},_persist(){try{localStorage.setItem(STORAGE_KEY,JSON.stringify(this.items))}catch(e){console.error("[LeaversPackage] Error saving to storage:",e)}},_restore(){try{const stored=localStorage.getItem(STORAGE_KEY);if(stored){const parsed=JSON.parse(stored);this.items=Array.isArray(parsed)?parsed:[]}}catch(e){console.error("[LeaversPackage] Error loading from storage:",e),this.items=[]}}};window.alpineIsInitialized&&window.Alpine?Alpine.store("leaversPackage",leaversPackageStore):document.addEventListener("alpine:init",()=>{Alpine.store("leaversPackage",leaversPackageStore)});var leavers_product_grid_default=({apiEndpoint="",turnstileSiteKey=""})=>({apiBase:apiEndpoint||API_BASE,turnstileSiteKey,quoteSessionId:"",loading:!0,error:null,products:[],productTypes:[],markupTiers:[],decorationTypes:[],decorationPricingTiers:[],decorationLocations:[],standardDesigns:[],activeCategory:null,selectedProduct:null,drawerColour:"",drawerSelectedSwatchIdx:null,drawerQuantity:MIN_QUANTITY,drawerSizeQuantities:{},drawerDecorations:[emptyDecoration()],drawerPricing:null,drawerPricingLoading:!1,_drawerPricingTimer:null,drawerError:null,designPickerOpen:!1,designPickerDecoIdx:null,showQuoteModal:!1,customerDetails:{fullName:"",email:"",schoolName:"",phoneNumber:"",teacherName:"",teacherEmail:"",requiredOrderDate:"",orderingMethod:"spreadsheet"},submitting:!1,submitSuccess:!1,submitError:null,async init(){this.quoteSessionId=crypto.randomUUID(),preloadLeaversTurnstile(this.turnstileSiteKey),this.$store.modals.register("leaversProduct","rightDrawer"),this.$store.modals.register("leaversPackage","rightDrawer");try{const[prodRes,decoRes,designRes]=await Promise.all([fetchJsonWithTimeout(`${this.apiBase}/products`,{label:"leavers products"}),fetchJsonWithTimeout(`${this.apiBase}/decorations`,{label:"leavers decorations"}),fetchJsonWithTimeout(`${this.apiBase}/designs`,{label:"leavers designs"})]);this.products=prodRes.products||[],this.productTypes=prodRes.productTypes||[],this.markupTiers=prodRes.markupTiers||[],this.decorationTypes=decoRes.types||[],this.decorationPricingTiers=decoRes.pricingTiers||[],this.decorationLocations=decoRes.locations||[],this.standardDesigns=designRes.designs||[]}catch(err){console.error("[LeaversProductGrid] Failed to load data:",err),this.error=getErrorMessage(err)}this.loading=!1},retryInit(){this.error=null,this.loading=!0,this.init()},get visibleProductTypes(){return this.productTypes.filter(pt=>!EXCLUDED_TYPES.includes(pt.name.toLowerCase()))},get filteredProducts(){let types=this.visibleProductTypes;this.activeCategory&&(types=types.filter(pt=>pt.name===this.activeCategory));const products=[];for(const pt of types)for(const brand of pt.brands)for(const product of brand.products)products.push(product);return products},setCategory(cat){this.activeCategory=cat},openProduct(product){this.selectedProduct=product,this.drawerColour="",this.drawerSelectedSwatchIdx=null,this.drawerQuantity=0,this.drawerSizeQuantities={},this.drawerDecorations=[emptyDecoration()],this.drawerPricing=null,this.drawerPricingLoading=!1,this.drawerError=null,this.designPickerOpen=!1,this.$store.modals.open("leaversProduct")},isInPackage(productId){return this.$store.leaversPackage.items.some(i=>i.id===productId)},openPackage(){this.$store.modals.open("leaversPackage")},getDecoTypesForProduct(){return this.selectedProduct?this.decorationTypes.filter(dt=>!dt.excludedFamilies.some(ef=>ef.toLowerCase()===this.selectedProduct.productType.toLowerCase())):this.decorationTypes},getDetailsForDecoType(decoTypeName){const dt=this.decorationTypes.find(t=>t.name===decoTypeName);return dt?dt.details:[]},onDecoTypeChange(decoIdx){const deco=this.drawerDecorations[decoIdx],details=this.getDetailsForDecoType(deco.decorationType);details.length===1?deco.detail=details[0]:deco.detail="",this.debouncePricing()},addDecoration(){this.drawerDecorations.length<MAX_DECORATIONS&&this.drawerDecorations.push(emptyDecoration())},removeDecoration(decoIdx){this.drawerDecorations.length>1&&(this.drawerDecorations.splice(decoIdx,1),this.debouncePricing())},async uploadArtwork(decoIdx,event){const file=event.target.files?.[0];if(!file)return;const deco=this.drawerDecorations[decoIdx];try{const res=await fetch(ARTWORK_UPLOAD_URL,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({fileName:file.name,mimeType:file.type,quoteSessionId:this.quoteSessionId,fileSize:file.size})});if(!res.ok){const err=await res.json();alert(err.error||"Upload failed");return}const{signedUrl,storagePath}=await res.json();if(!(await fetch(signedUrl,{method:"PUT",headers:{"Content-Type":file.type,"x-upsert":"true"},body:file})).ok){alert("File upload failed. Please try again.");return}deco.artworkFileId=storagePath,deco.artworkType="custom",deco._artworkFileName=file.name}catch(err){console.error("[LeaversProductGrid] Artwork upload error:",err),alert("Upload failed. Please try again.")}},openDesignPicker(decoIdx){this.designPickerDecoIdx=decoIdx,this.designPickerOpen=!0},selectDesign(designId){if(this.designPickerDecoIdx==null)return;const deco=this.drawerDecorations[this.designPickerDecoIdx];deco.artworkType="standard",deco.standardDesignId=designId;const design=this.standardDesigns.find(d=>d.id===designId);deco._standardDesignName=design?design.name:"",this.designPickerOpen=!1,this.designPickerDecoIdx=null},debouncePricing(){this._drawerPricingTimer&&clearTimeout(this._drawerPricingTimer),this._drawerPricingTimer=setTimeout(()=>this.fetchDrawerPricing(),PRICING_DEBOUNCE_MS)},async fetchDrawerPricing(){if(!this.selectedProduct)return;if(!(this.drawerQuantity>=MIN_QUANTITY&&this.drawerDecorations.some(d=>d.decorationType&&d.detail))){this.drawerPricing=null;return}this.drawerPricingLoading=!0;try{const res=await fetch(`${this.apiBase}/pricing`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({garmentLines:[{productId:this.selectedProduct.id,productType:this.selectedProduct.productType,brand:this.selectedProduct.brand,productName:this.selectedProduct.productName,quantity:parseInt(this.drawerQuantity)||0,decorations:this.drawerDecorations.filter(d=>d.decorationType&&d.detail).map(d=>({decorationType:d.decorationType,detail:d.detail,location:d.location,artworkType:d.artworkType,artworkFileId:d.artworkFileId||void 0,standardDesignId:d.standardDesignId||void 0}))}]})});if(res.ok){const data=await res.json();this.drawerPricing=data.lines?.[0]||null}}catch(err){console.error("[LeaversProductGrid] Pricing error:",err)}this.drawerPricingLoading=!1},addToPackage(){if(!this.selectedProduct)return;const errors=[];this.drawerQuantity<MIN_QUANTITY&&errors.push(`Minimum ${MIN_QUANTITY} units required`),this.drawerDecorations.filter(d=>d.decorationType&&d.detail).length===0&&errors.push("Add at least one decoration");for(let i=0;i<this.drawerDecorations.length;i++){const d=this.drawerDecorations[i];d.decorationType&&!d.detail&&errors.push(`Decoration ${i+1}: select a detail`),d.decorationType&&!d.location&&errors.push(`Decoration ${i+1}: select a location`)}if(errors.length>0){this.drawerError=errors.join(". ");return}this.drawerError=null;const item={id:this.selectedProduct.id,productType:this.selectedProduct.productType,brand:this.selectedProduct.brand,productName:this.selectedProduct.productName,imageUrl:this.selectedProduct.imageUrl||"",basePrice:this.selectedProduct.basePrice,garmentColour:this.drawerColour,sizeBreakdown:{...this.drawerSizeQuantities},quantity:parseInt(this.drawerQuantity)||MIN_QUANTITY,decorations:this.drawerDecorations.filter(d=>d.decorationType&&d.detail).map(d=>({decorationType:d.decorationType,detail:d.detail,location:d.location,artworkType:d.artworkType,artworkFileId:d.artworkFileId||void 0,standardDesignId:d.standardDesignId||void 0})),pricing:this.drawerPricing?{unitPriceExcl:this.drawerPricing.unitPriceExclGst,unitPriceIncl:this.drawerPricing.unitPriceInclGst,lineSubtotal:this.drawerPricing.lineSubtotalExclGst||this.drawerPricing.unitPriceExclGst*parseInt(this.drawerQuantity),tierCards:this.drawerPricing.tierCards||[]}:null};this.$store.leaversPackage.add(item),this.$store.modals.open("leaversPackage")},openQuoteModal(){this.$store.modals.close("leaversPackage"),this.showQuoteModal=!0,this.submitError=null,this.submitSuccess=!1},closeQuoteModal(){this.showQuoteModal=!1},validateQuoteForm(){const errors=[],d=this.customerDetails;return d.fullName.trim()||errors.push("Full name is required"),d.email.includes("@")||errors.push("Valid email is required"),d.schoolName.trim()||errors.push("School name is required"),d.teacherName.trim()||errors.push("Teacher name is required"),d.teacherEmail.includes("@")||errors.push("Valid teacher email is required"),d.requiredOrderDate||errors.push("Required order date is needed"),d.orderingMethod==="online_store"&&this.$store.leaversPackage.items.reduce((s,i)=>s+(i.quantity||0),0)<ONLINE_STORE_MIN_UNITS&&errors.push(`Online store ordering requires minimum ${ONLINE_STORE_MIN_UNITS} total units`),errors},async submitQuote(){const errors=this.validateQuoteForm();if(errors.length>0){this.submitError=errors.join(". ");return}this.submitError=null,this.submitting=!0;try{const turnstileToken=await getLeaversTurnstileToken(this.turnstileSiteKey),res=await fetch(`${this.apiBase}/submit`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({customerDetails:this.customerDetails,garmentLines:this.$store.leaversPackage.items.map(item=>({productId:item.id,productType:item.productType,brand:item.brand,productName:item.productName,quantity:item.quantity,decorations:item.decorations})),gstMode:"incl",quoteSessionId:this.quoteSessionId,turnstileToken})}),data=await res.json();res.ok&&data.success?(this.submitSuccess=!0,this.$store.leaversPackage.clear()):this.submitError=data.error||"Failed to submit quote. Please try again."}catch(err){console.error("[LeaversProductGrid] Submit error:",err),this.submitError="Network error. Please check your connection and try again."}this.submitting=!1},selectColour(swatch,idx){this.drawerColour=swatch.label,this.drawerSelectedSwatchIdx=idx},updateTotalQuantity(){this.drawerSizeQuantities={...this.drawerSizeQuantities},this.drawerQuantity=Object.values(this.drawerSizeQuantities).reduce((sum,qty)=>sum+(parseInt(qty)||0),0)},formatPrice,getPlaceholderSvg});deferrableData("LeaversProductGrid",leavers_product_grid_default); 2//# sourceMappingURL=/cdn/shop/t/59/assets/leavers-product-grid.bundle.js.map?v=101839867699394922961
2787098435
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.