/* ============================================================ MOCK DATA — stands in for API/database responses. Every array below maps to one database table. Replace each with a fetch; no component reads anything but these shapes. ============================================================ */ const SITE_CONFIG = { brand: "बेदरे बंधू सुवर्णकार", // replace with logo asset tagline: "आपली माणसं", whatsappNumber: "917507054168", // config, never inline in UI taxRate: 0.03, // Dummy placeholders — replace with the real profile URLs. A blank value hides that icon in the footer. social: { instagram: "https://instagram.com/abstudiodesign", facebook: "https://facebook.com/abstudiodesign", youtube: "https://youtube.com/@abstudiodesign" }, }; /* table: rates — { id, metal, purity, rate, unit, updatedAt } Now fetched from MySQL via api/rates.php — see loadCatalogData() below. */ let RATES = []; /* table: categories — { id, name, slug, parentId, order, active } Rendered as-is: add a row and the mega menu grows. Now fetched from MySQL via api/categories.php — see loadCatalogData() below. */ let CATEGORIES = []; /* table: banners — desktop/mobile image, title, subtitle, cta, order Now fetched from MySQL via api/banners.php — see loadCatalogData() below. */ let BANNERS = []; /* table: items — one row per product. customFields is an open bag: add keys and the attribute list renders them, no UI change needed. Now fetched from MySQL via api/products.php — see loadCatalogData() below. */ let PRODUCTS = []; /* Brand film heading/copy — edited in the admin's Slider/Film page. Now fetched via api/brand-film.php — see loadCatalogData() below. */ let BRAND_FILM = { title: "", subtitle: "" }; /* Fetches categories + products from the PHP/MySQL API and fills the CATEGORIES / PRODUCTS arrays in place before any page renders. Must be awaited by every entry script (home.jsx, listing.jsx, product.jsx) before calling ReactDOM.createRoot(...).render(...). */ async function loadCatalogData() { const [categories, products, rates, banners, brandFilm] = await Promise.all([ fetch("api/categories.php").then(r => r.json()), fetch("api/products.php").then(r => r.json()), fetch("api/rates.php").then(r => r.json()), fetch("api/banners.php").then(r => r.json()), fetch("api/brand-film.php").then(r => r.json()), ]); CATEGORIES.length = 0; CATEGORIES.push(...categories); PRODUCTS.length = 0; PRODUCTS.push(...products); RATES.length = 0; RATES.push(...rates); BANNERS.length = 0; BANNERS.push(...banners); Object.assign(BRAND_FILM, brandFilm); } /* Shop-by-category tiles — driven by the same categories table. count is left out here and computed live from PRODUCTS at render time. */ const CATEGORY_TILES = [ { name: "Gold", slug: "gold", slotId: "tile-gold", image: "images/tile-gold.png" }, { name: "Silver", slug: "silver", slotId: "tile-silver", image: "images/tile-silver.png" }, { name: "Diamond", slug: "diamond", slotId: "tile-diamond", image: "images/tile-diamond.png" }, { name: "Gifts", slug: "gifts", slotId: "tile-gifts", image: "images/tile-gifts.png" }, ]; /* ---------- pricing: metal value from today's rate, never hard-coded ---------- */ function rateFor(metal, purity) { const exact = RATES.find(r => r.metal === metal && r.purity === purity); if (exact) return exact; // No published row for this purity: derive it proportionally from the // finest row of the same metal rather than borrowing an unrelated rate. const rows = RATES.filter(r => r.metal === metal); if (!rows.length) return null; const karat = s => parseFloat(String(s).replace(/[^0-9.]/g, "")) || null; const want = karat(purity), base = rows[0], baseK = karat(base.purity); if (!want || !baseK) return base; return { ...base, purity, rate: Math.round(base.rate * (want / baseK)), derived: true }; } function priceBreakdown(p) { const r = rateFor(p.metal, p.purity); const perGram = r ? (r.unit === "kg" ? r.rate / 1000 : r.rate / 10) : 0; const metalValue = perGram * p.netWeight; const making = p.makingChargesType === "percent" ? metalValue * (p.makingCharges / 100) : p.makingCharges; const subtotal = metalValue + making + p.stoneCharges + (p.otherCharges || 0); const tax = subtotal * SITE_CONFIG.taxRate; return { rate: r, perGram, metalValue, making, stone: p.stoneCharges, other: p.otherCharges || 0, tax, total: subtotal + tax, }; } const inr = n => "₹" + Math.round(n).toLocaleString("en-IN"); function whatsappLink(p) { let msg = "Hello, I would like to know more about your jewellery collection."; if (p && p.isGeneral) { msg = `Hello, I am interested in ${p.name}.\n\nPlease share more details.`; } else if (p) { const b = priceBreakdown(p); const imageUrl = p.images && p.images[0] ? `${location.origin}/${p.images[0]}` : ""; const rateLine = b.rate ? `\nMetal Rate: ${inr(b.rate.rate)} / ${b.rate.unit}` : ""; msg = `Hello, I am interested in this jewellery item.\n\nProduct: ${p.name}\nItem Code: ${p.code}\nMetal: ${p.metal} ${p.purity}\nWeight: ${p.netWeight} g net${rateLine}\nPrice: ${inr(b.total)}\n\nPlease provide more details.${imageUrl ? `\n\n${imageUrl}` : ""}`; } return `https://wa.me/${SITE_CONFIG.whatsappNumber}?text=${encodeURIComponent(msg)}`; } /* Saves a customer inquiry to the inquirylist table via api/inquiry.php. */ async function submitInquiry(product, { name, mobile, message }) { const res = await fetch("api/inquiry.php", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ productId: product ? product.id : null, productName: product ? product.name : "General enquiry", productCode: product ? product.code : null, productImage: product && product.images ? product.images[0] : null, customerName: name || null, customerMobile: mobile, message: message || null, }), }); const data = await res.json(); if (!res.ok) throw new Error(data.error || "Could not send enquiry"); return data; } Object.assign(window, { SITE_CONFIG, RATES, CATEGORIES, BANNERS, PRODUCTS, BRAND_FILM, CATEGORY_TILES, priceBreakdown, inr, whatsappLink, submitInquiry });