@neowhale/storefront 0.2.7 → 0.2.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/react/index.cjs +372 -124
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.d.cts +0 -20
- package/dist/react/index.d.ts +0 -20
- package/dist/react/index.js +372 -124
- package/dist/react/index.js.map +1 -1
- package/package.json +1 -1
package/dist/react/index.d.cts
CHANGED
|
@@ -366,34 +366,14 @@ declare function useCoupons(): {
|
|
|
366
366
|
};
|
|
367
367
|
|
|
368
368
|
interface QRLandingPageProps {
|
|
369
|
-
/** QR code string (from URL param) */
|
|
370
369
|
code: string;
|
|
371
|
-
/** Gateway URL — defaults to https://whale-gateway.fly.dev */
|
|
372
370
|
gatewayUrl?: string;
|
|
373
|
-
/** Custom component for product display */
|
|
374
371
|
renderProduct?: (data: QRLandingData) => React.ReactNode;
|
|
375
|
-
/** Custom component for COA/lab results */
|
|
376
372
|
renderCOA?: (data: QRLandingData) => React.ReactNode;
|
|
377
|
-
/** Custom component for the full page (overrides default layout) */
|
|
378
373
|
renderPage?: (data: QRLandingData) => React.ReactNode;
|
|
379
|
-
/** Called when data is loaded */
|
|
380
374
|
onDataLoaded?: (data: QRLandingData) => void;
|
|
381
|
-
/** Called on error */
|
|
382
375
|
onError?: (error: Error) => void;
|
|
383
376
|
}
|
|
384
|
-
/**
|
|
385
|
-
* QR Landing Page — drop-in component that renders a branded landing page
|
|
386
|
-
* for any QR code. Fetches data from the gateway's public endpoint.
|
|
387
|
-
*
|
|
388
|
-
* Usage:
|
|
389
|
-
* ```tsx
|
|
390
|
-
* // In app/qr/[code]/page.tsx:
|
|
391
|
-
* import { QRLandingPage } from '@neowhale/storefront/react'
|
|
392
|
-
* export default function Page({ params }: { params: { code: string } }) {
|
|
393
|
-
* return <QRLandingPage code={params.code} />
|
|
394
|
-
* }
|
|
395
|
-
* ```
|
|
396
|
-
*/
|
|
397
377
|
declare function QRLandingPage({ code, gatewayUrl, renderProduct, renderCOA, renderPage, onDataLoaded, onError, }: QRLandingPageProps): react_jsx_runtime.JSX.Element | null;
|
|
398
378
|
|
|
399
379
|
interface LandingPageProps {
|
package/dist/react/index.d.ts
CHANGED
|
@@ -366,34 +366,14 @@ declare function useCoupons(): {
|
|
|
366
366
|
};
|
|
367
367
|
|
|
368
368
|
interface QRLandingPageProps {
|
|
369
|
-
/** QR code string (from URL param) */
|
|
370
369
|
code: string;
|
|
371
|
-
/** Gateway URL — defaults to https://whale-gateway.fly.dev */
|
|
372
370
|
gatewayUrl?: string;
|
|
373
|
-
/** Custom component for product display */
|
|
374
371
|
renderProduct?: (data: QRLandingData) => React.ReactNode;
|
|
375
|
-
/** Custom component for COA/lab results */
|
|
376
372
|
renderCOA?: (data: QRLandingData) => React.ReactNode;
|
|
377
|
-
/** Custom component for the full page (overrides default layout) */
|
|
378
373
|
renderPage?: (data: QRLandingData) => React.ReactNode;
|
|
379
|
-
/** Called when data is loaded */
|
|
380
374
|
onDataLoaded?: (data: QRLandingData) => void;
|
|
381
|
-
/** Called on error */
|
|
382
375
|
onError?: (error: Error) => void;
|
|
383
376
|
}
|
|
384
|
-
/**
|
|
385
|
-
* QR Landing Page — drop-in component that renders a branded landing page
|
|
386
|
-
* for any QR code. Fetches data from the gateway's public endpoint.
|
|
387
|
-
*
|
|
388
|
-
* Usage:
|
|
389
|
-
* ```tsx
|
|
390
|
-
* // In app/qr/[code]/page.tsx:
|
|
391
|
-
* import { QRLandingPage } from '@neowhale/storefront/react'
|
|
392
|
-
* export default function Page({ params }: { params: { code: string } }) {
|
|
393
|
-
* return <QRLandingPage code={params.code} />
|
|
394
|
-
* }
|
|
395
|
-
* ```
|
|
396
|
-
*/
|
|
397
377
|
declare function QRLandingPage({ code, gatewayUrl, renderProduct, renderCOA, renderPage, onDataLoaded, onError, }: QRLandingPageProps): react_jsx_runtime.JSX.Element | null;
|
|
398
378
|
|
|
399
379
|
interface LandingPageProps {
|
package/dist/react/index.js
CHANGED
|
@@ -1262,174 +1262,414 @@ function QRLandingPage({
|
|
|
1262
1262
|
if (state === "error") return /* @__PURE__ */ jsx(DefaultError, { message: errorMsg });
|
|
1263
1263
|
if (!data) return null;
|
|
1264
1264
|
if (renderPage) return /* @__PURE__ */ jsx(Fragment, { children: renderPage(data) });
|
|
1265
|
-
return /* @__PURE__ */ jsx(
|
|
1265
|
+
return /* @__PURE__ */ jsx(ProductLanding, { data, renderProduct, renderCOA });
|
|
1266
1266
|
}
|
|
1267
|
-
function
|
|
1267
|
+
function extractTheme(data) {
|
|
1268
|
+
const lp = data.qr_code.landing_page;
|
|
1269
|
+
const t = data.store?.theme;
|
|
1270
|
+
return {
|
|
1271
|
+
bg: lp.background_color || t?.background || "#050505",
|
|
1272
|
+
fg: lp.text_color || t?.foreground || "#fafafa",
|
|
1273
|
+
accent: t?.accent || "#E8E2D9",
|
|
1274
|
+
accentDark: t?.accentDark || "#C8BFB2",
|
|
1275
|
+
surface: t?.surface || "#0C0C0C",
|
|
1276
|
+
surfaceLight: t?.surfaceLight || "#141414",
|
|
1277
|
+
muted: t?.muted || "#8A8A8A",
|
|
1278
|
+
border: t?.border || "#1C1C1C",
|
|
1279
|
+
fontDisplay: t?.fontDisplay || "system-ui, -apple-system, sans-serif",
|
|
1280
|
+
fontBody: t?.fontBody || "system-ui, -apple-system, sans-serif",
|
|
1281
|
+
radius: t?.radius || "0px"
|
|
1282
|
+
};
|
|
1283
|
+
}
|
|
1284
|
+
function ProductLanding({
|
|
1268
1285
|
data,
|
|
1269
1286
|
renderProduct,
|
|
1270
1287
|
renderCOA
|
|
1271
1288
|
}) {
|
|
1272
1289
|
const { qr_code: qr, store, product, coa } = data;
|
|
1273
1290
|
const lp = qr.landing_page;
|
|
1291
|
+
const theme = extractTheme(data);
|
|
1274
1292
|
const [showCOA, setShowCOA] = useState(false);
|
|
1275
|
-
const bg = lp.background_color || store?.theme?.background || "#050505";
|
|
1276
|
-
const fg = lp.text_color || store?.theme?.foreground || "#fafafa";
|
|
1277
|
-
const accent = store?.theme?.accent || qr.brand_color || "#E8E2D9";
|
|
1278
|
-
const surface = store?.theme?.surface || "#111";
|
|
1279
|
-
const muted = store?.theme?.muted || "#888";
|
|
1280
1293
|
const logoUrl = qr.logo_url || store?.logo_url;
|
|
1281
|
-
const productImage = lp.image_url || product?.featured_image ||
|
|
1294
|
+
const productImage = lp.image_url || product?.featured_image || null;
|
|
1282
1295
|
const productName = lp.title || product?.name || qr.name;
|
|
1283
1296
|
const description = lp.description || product?.description || "";
|
|
1284
|
-
const ctaText = lp.cta_text || (coa ? "View Lab Results" : "Learn More");
|
|
1285
1297
|
const ctaUrl = lp.cta_url || qr.destination_url;
|
|
1298
|
+
const categoryName = product?.category_name ?? null;
|
|
1286
1299
|
const cf = product?.custom_fields;
|
|
1287
1300
|
const thca = cf?.thca_percentage ?? null;
|
|
1288
1301
|
const thc = cf?.d9_percentage ?? null;
|
|
1289
1302
|
const cbd = cf?.cbd_total ?? null;
|
|
1290
1303
|
const strainType = cf?.strain_type ?? null;
|
|
1291
|
-
const
|
|
1304
|
+
const batchNumber = cf?.batch_number ?? null;
|
|
1305
|
+
const dateTested = cf?.date_tested ?? null;
|
|
1306
|
+
const terpenes = cf?.terpenes ?? null;
|
|
1307
|
+
const effects = cf?.effects ?? null;
|
|
1308
|
+
const genetics = cf?.genetics ?? null;
|
|
1309
|
+
const tagline = cf?.tagline ?? null;
|
|
1310
|
+
const pricingData = product?.pricing_data;
|
|
1311
|
+
const tiers = pricingData?.tiers?.sort((a, b) => (a.sort_order ?? 0) - (b.sort_order ?? 0)) ?? [];
|
|
1312
|
+
const lowestPrice = tiers.length > 0 ? Math.min(...tiers.map((t) => t.default_price)) : null;
|
|
1292
1313
|
const handleCOAClick = useCallback(() => {
|
|
1293
1314
|
if (coa) setShowCOA(true);
|
|
1294
1315
|
}, [coa]);
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1316
|
+
const labelStyle = {
|
|
1317
|
+
fontSize: 11,
|
|
1318
|
+
fontWeight: 500,
|
|
1319
|
+
textTransform: "uppercase",
|
|
1320
|
+
letterSpacing: "0.25em",
|
|
1321
|
+
color: `${theme.fg}40`
|
|
1322
|
+
// 40 = 25% opacity
|
|
1323
|
+
};
|
|
1324
|
+
const dividerStyle = {
|
|
1325
|
+
border: "none",
|
|
1326
|
+
borderTop: `1px solid ${theme.fg}0A`,
|
|
1327
|
+
// 0A = 4% opacity
|
|
1328
|
+
margin: 0
|
|
1329
|
+
};
|
|
1330
|
+
return /* @__PURE__ */ jsxs("div", { style: {
|
|
1331
|
+
minHeight: "100dvh",
|
|
1332
|
+
background: theme.bg,
|
|
1333
|
+
color: theme.fg,
|
|
1334
|
+
fontFamily: theme.fontBody
|
|
1335
|
+
}, children: [
|
|
1336
|
+
/* @__PURE__ */ jsxs("nav", { style: {
|
|
1337
|
+
padding: "1.25rem 1.5rem",
|
|
1338
|
+
display: "flex",
|
|
1339
|
+
alignItems: "center",
|
|
1340
|
+
justifyContent: "space-between",
|
|
1341
|
+
maxWidth: 1280,
|
|
1342
|
+
margin: "0 auto"
|
|
1343
|
+
}, children: [
|
|
1344
|
+
logoUrl ? /* @__PURE__ */ jsx("img", { src: logoUrl, alt: store?.name || "", style: { height: 28, objectFit: "contain" } }) : store?.name ? /* @__PURE__ */ jsx("span", { style: { fontFamily: theme.fontDisplay, fontWeight: 600, fontSize: "1rem", letterSpacing: "-0.02em" }, children: store.name }) : /* @__PURE__ */ jsx("span", {}),
|
|
1345
|
+
/* @__PURE__ */ jsx(
|
|
1346
|
+
"a",
|
|
1347
|
+
{
|
|
1348
|
+
href: ctaUrl,
|
|
1349
|
+
style: {
|
|
1350
|
+
fontSize: 11,
|
|
1351
|
+
fontWeight: 500,
|
|
1352
|
+
textTransform: "uppercase",
|
|
1353
|
+
letterSpacing: "0.15em",
|
|
1354
|
+
color: theme.fg,
|
|
1355
|
+
textDecoration: "none",
|
|
1356
|
+
padding: "0.5rem 1.25rem",
|
|
1357
|
+
border: `1px solid ${theme.fg}10`,
|
|
1358
|
+
transition: "border-color 0.3s"
|
|
1359
|
+
},
|
|
1360
|
+
children: "Shop"
|
|
1361
|
+
}
|
|
1362
|
+
)
|
|
1363
|
+
] }),
|
|
1364
|
+
/* @__PURE__ */ jsxs("main", { style: { maxWidth: 560, margin: "0 auto", padding: "0 1.5rem 3rem" }, children: [
|
|
1365
|
+
productImage && /* @__PURE__ */ jsx("div", { style: {
|
|
1366
|
+
width: "100%",
|
|
1367
|
+
aspectRatio: "1",
|
|
1368
|
+
overflow: "hidden",
|
|
1369
|
+
background: theme.surfaceLight,
|
|
1370
|
+
display: "flex",
|
|
1371
|
+
alignItems: "center",
|
|
1372
|
+
justifyContent: "center",
|
|
1373
|
+
padding: "2rem"
|
|
1374
|
+
}, children: /* @__PURE__ */ jsx(
|
|
1375
|
+
"img",
|
|
1376
|
+
{
|
|
1377
|
+
src: productImage,
|
|
1378
|
+
alt: productName,
|
|
1379
|
+
style: { maxWidth: "100%", maxHeight: "100%", objectFit: "contain" }
|
|
1380
|
+
}
|
|
1381
|
+
) }),
|
|
1382
|
+
/* @__PURE__ */ jsxs("div", { style: { marginTop: "1.5rem", display: "flex", gap: "0.75rem", alignItems: "center" }, children: [
|
|
1383
|
+
categoryName && /* @__PURE__ */ jsx("span", { style: {
|
|
1384
|
+
fontSize: 11,
|
|
1385
|
+
fontWeight: 500,
|
|
1386
|
+
color: theme.accent,
|
|
1387
|
+
textTransform: "uppercase",
|
|
1388
|
+
letterSpacing: "0.15em"
|
|
1389
|
+
}, children: categoryName }),
|
|
1314
1390
|
strainType && /* @__PURE__ */ jsx("span", { style: {
|
|
1315
|
-
|
|
1316
|
-
borderRadius: 999,
|
|
1317
|
-
fontSize: "0.75rem",
|
|
1391
|
+
fontSize: 10,
|
|
1318
1392
|
fontWeight: 600,
|
|
1319
1393
|
textTransform: "uppercase",
|
|
1320
|
-
letterSpacing: "0.
|
|
1394
|
+
letterSpacing: "0.08em",
|
|
1395
|
+
padding: "0.2rem 0.5rem",
|
|
1321
1396
|
background: strainBadgeColor(strainType),
|
|
1322
1397
|
color: "#fff"
|
|
1323
|
-
}, children: strainType })
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1398
|
+
}, children: strainType })
|
|
1399
|
+
] }),
|
|
1400
|
+
/* @__PURE__ */ jsx("h1", { style: {
|
|
1401
|
+
fontFamily: theme.fontDisplay,
|
|
1402
|
+
fontSize: "clamp(2rem, 8vw, 3rem)",
|
|
1403
|
+
fontWeight: 300,
|
|
1404
|
+
margin: "0.5rem 0 0",
|
|
1405
|
+
lineHeight: 1.1,
|
|
1406
|
+
letterSpacing: "-0.02em"
|
|
1407
|
+
}, children: productName }),
|
|
1408
|
+
tagline && /* @__PURE__ */ jsx("p", { style: {
|
|
1409
|
+
fontFamily: theme.fontDisplay,
|
|
1410
|
+
fontSize: "1.1rem",
|
|
1411
|
+
fontWeight: 300,
|
|
1412
|
+
color: `${theme.fg}80`,
|
|
1413
|
+
margin: "0.5rem 0 0"
|
|
1414
|
+
}, children: tagline }),
|
|
1415
|
+
lowestPrice != null && /* @__PURE__ */ jsxs("p", { style: {
|
|
1416
|
+
fontFamily: theme.fontDisplay,
|
|
1417
|
+
fontSize: "1.5rem",
|
|
1418
|
+
fontWeight: 300,
|
|
1419
|
+
margin: "0.75rem 0 0"
|
|
1420
|
+
}, children: [
|
|
1421
|
+
"$",
|
|
1422
|
+
lowestPrice.toFixed(2),
|
|
1423
|
+
tiers.length > 1 && /* @__PURE__ */ jsx("span", { style: { fontSize: "0.8rem", color: `${theme.fg}50`, marginLeft: "0.5rem", fontWeight: 400 }, children: "and up" })
|
|
1331
1424
|
] }),
|
|
1332
|
-
/* @__PURE__ */ jsx("h1", { style: { fontSize: "1.75rem", fontWeight: 600, margin: "0 0 0.5rem", lineHeight: 1.2 }, children: productName }),
|
|
1333
|
-
description && /* @__PURE__ */ jsx("p", { style: { color: muted, lineHeight: 1.6, margin: "0 0 1.5rem", fontSize: "0.95rem" }, children: description }),
|
|
1334
1425
|
renderProduct ? renderProduct(data) : null,
|
|
1335
|
-
(thca || thc || cbd) && /* @__PURE__ */ jsxs("div", { style: {
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1426
|
+
(thca != null || thc != null || cbd != null) && /* @__PURE__ */ jsxs("div", { style: {
|
|
1427
|
+
marginTop: "1.75rem",
|
|
1428
|
+
border: `1px solid ${theme.fg}0F`,
|
|
1429
|
+
display: "grid",
|
|
1430
|
+
gridTemplateColumns: `repeat(${[thca, thc, cbd].filter((v) => v != null).length}, 1fr)`
|
|
1431
|
+
}, children: [
|
|
1432
|
+
thca != null && /* @__PURE__ */ jsx(CannabinoidStat, { label: "THCa", value: thca, theme, showBorder: thc != null || cbd != null }),
|
|
1433
|
+
thc != null && /* @__PURE__ */ jsx(CannabinoidStat, { label: "\u03949 THC", value: thc, theme, showBorder: cbd != null }),
|
|
1434
|
+
cbd != null && /* @__PURE__ */ jsx(CannabinoidStat, { label: "CBD", value: cbd, theme, showBorder: false })
|
|
1435
|
+
] }),
|
|
1436
|
+
tiers.length > 1 && /* @__PURE__ */ jsxs("div", { style: { marginTop: "1.5rem" }, children: [
|
|
1437
|
+
/* @__PURE__ */ jsx("p", { style: { ...labelStyle, marginBottom: "0.75rem" }, children: "Pricing" }),
|
|
1438
|
+
/* @__PURE__ */ jsx("div", { style: { display: "flex", flexWrap: "wrap", gap: 1 }, children: tiers.map((tier) => /* @__PURE__ */ jsxs("div", { style: {
|
|
1439
|
+
flex: "1 1 0",
|
|
1440
|
+
minWidth: 90,
|
|
1441
|
+
padding: "0.75rem 0.5rem",
|
|
1442
|
+
background: theme.surfaceLight,
|
|
1443
|
+
textAlign: "center"
|
|
1444
|
+
}, children: [
|
|
1445
|
+
/* @__PURE__ */ jsxs("div", { style: { fontSize: "1rem", fontWeight: 300, fontFamily: theme.fontDisplay }, children: [
|
|
1446
|
+
"$",
|
|
1447
|
+
tier.default_price.toFixed(2)
|
|
1448
|
+
] }),
|
|
1449
|
+
/* @__PURE__ */ jsx("div", { style: { fontSize: 10, color: `${theme.fg}60`, letterSpacing: "0.1em", textTransform: "uppercase", marginTop: 2 }, children: tier.label })
|
|
1450
|
+
] }, tier.id)) })
|
|
1339
1451
|
] }),
|
|
1340
|
-
/* @__PURE__ */ jsxs("div", { style: {
|
|
1341
|
-
|
|
1452
|
+
(genetics || terpenes || effects || batchNumber || dateTested) && /* @__PURE__ */ jsxs("div", { style: { marginTop: "1.75rem" }, children: [
|
|
1453
|
+
/* @__PURE__ */ jsx("p", { style: { ...labelStyle, marginBottom: "0.75rem" }, children: "Details" }),
|
|
1454
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
1455
|
+
genetics && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1456
|
+
/* @__PURE__ */ jsx(DetailRow, { label: "Genetics", value: genetics, theme }),
|
|
1457
|
+
/* @__PURE__ */ jsx("hr", { style: dividerStyle })
|
|
1458
|
+
] }),
|
|
1459
|
+
terpenes && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1460
|
+
/* @__PURE__ */ jsx(DetailRow, { label: "Terpenes", value: terpenes, theme }),
|
|
1461
|
+
/* @__PURE__ */ jsx("hr", { style: dividerStyle })
|
|
1462
|
+
] }),
|
|
1463
|
+
effects && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1464
|
+
/* @__PURE__ */ jsx(DetailRow, { label: "Effects", value: effects, theme }),
|
|
1465
|
+
/* @__PURE__ */ jsx("hr", { style: dividerStyle })
|
|
1466
|
+
] }),
|
|
1467
|
+
batchNumber && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1468
|
+
/* @__PURE__ */ jsx(DetailRow, { label: "Batch", value: batchNumber, theme }),
|
|
1469
|
+
/* @__PURE__ */ jsx("hr", { style: dividerStyle })
|
|
1470
|
+
] }),
|
|
1471
|
+
dateTested && /* @__PURE__ */ jsx(DetailRow, { label: "Tested", value: formatDate(dateTested), theme })
|
|
1472
|
+
] })
|
|
1473
|
+
] }),
|
|
1474
|
+
description && /* @__PURE__ */ jsxs("div", { style: { marginTop: "1.75rem" }, children: [
|
|
1475
|
+
/* @__PURE__ */ jsx("p", { style: { ...labelStyle, marginBottom: "0.75rem" }, children: "About" }),
|
|
1476
|
+
/* @__PURE__ */ jsx("p", { style: {
|
|
1477
|
+
fontSize: "0.9rem",
|
|
1478
|
+
fontWeight: 300,
|
|
1479
|
+
color: `${theme.fg}99`,
|
|
1480
|
+
lineHeight: 1.7,
|
|
1481
|
+
margin: 0
|
|
1482
|
+
}, children: description })
|
|
1483
|
+
] }),
|
|
1484
|
+
coa && /* @__PURE__ */ jsxs("div", { style: { marginTop: "1.75rem" }, children: [
|
|
1485
|
+
/* @__PURE__ */ jsx("p", { style: { ...labelStyle, marginBottom: "0.75rem" }, children: "Lab Results" }),
|
|
1486
|
+
batchNumber && /* @__PURE__ */ jsxs("p", { style: { fontSize: 12, color: `${theme.fg}60`, margin: "0 0 0.5rem", letterSpacing: "0.05em" }, children: [
|
|
1487
|
+
"Batch ",
|
|
1488
|
+
batchNumber,
|
|
1489
|
+
dateTested ? ` \xB7 ${formatDate(dateTested)}` : ""
|
|
1490
|
+
] }),
|
|
1491
|
+
/* @__PURE__ */ jsxs(
|
|
1342
1492
|
"button",
|
|
1343
1493
|
{
|
|
1344
1494
|
onClick: handleCOAClick,
|
|
1345
1495
|
style: {
|
|
1346
1496
|
width: "100%",
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
fontSize: "0.95rem",
|
|
1352
|
-
fontWeight: 600,
|
|
1497
|
+
position: "relative",
|
|
1498
|
+
height: 180,
|
|
1499
|
+
border: `1px solid ${theme.fg}0F`,
|
|
1500
|
+
background: theme.surface,
|
|
1353
1501
|
cursor: "pointer",
|
|
1354
|
-
|
|
1502
|
+
overflow: "hidden",
|
|
1503
|
+
padding: 0,
|
|
1504
|
+
display: "block"
|
|
1355
1505
|
},
|
|
1356
|
-
children:
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1506
|
+
children: [
|
|
1507
|
+
/* @__PURE__ */ jsx(
|
|
1508
|
+
"iframe",
|
|
1509
|
+
{
|
|
1510
|
+
src: coa.url,
|
|
1511
|
+
style: {
|
|
1512
|
+
width: "200%",
|
|
1513
|
+
height: "200%",
|
|
1514
|
+
border: "none",
|
|
1515
|
+
transform: "scale(0.5)",
|
|
1516
|
+
transformOrigin: "top left",
|
|
1517
|
+
pointerEvents: "none"
|
|
1518
|
+
},
|
|
1519
|
+
title: "COA Preview",
|
|
1520
|
+
tabIndex: -1
|
|
1521
|
+
}
|
|
1522
|
+
),
|
|
1523
|
+
/* @__PURE__ */ jsx("div", { style: {
|
|
1524
|
+
position: "absolute",
|
|
1525
|
+
inset: 0,
|
|
1526
|
+
background: `linear-gradient(to top, ${theme.bg}E6 0%, transparent 60%)`,
|
|
1527
|
+
display: "flex",
|
|
1528
|
+
alignItems: "flex-end",
|
|
1529
|
+
justifyContent: "center",
|
|
1530
|
+
paddingBottom: "1.25rem"
|
|
1531
|
+
}, children: /* @__PURE__ */ jsx("span", { style: {
|
|
1532
|
+
fontSize: 11,
|
|
1533
|
+
fontWeight: 500,
|
|
1534
|
+
textTransform: "uppercase",
|
|
1535
|
+
letterSpacing: "0.2em",
|
|
1536
|
+
color: theme.accent
|
|
1537
|
+
}, children: "View Certificate of Analysis" }) })
|
|
1538
|
+
]
|
|
1379
1539
|
}
|
|
1380
1540
|
)
|
|
1381
1541
|
] }),
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1542
|
+
renderCOA ? renderCOA(data) : null,
|
|
1543
|
+
/* @__PURE__ */ jsx(
|
|
1544
|
+
"a",
|
|
1545
|
+
{
|
|
1546
|
+
href: ctaUrl,
|
|
1547
|
+
style: {
|
|
1548
|
+
display: "block",
|
|
1549
|
+
width: "100%",
|
|
1550
|
+
marginTop: "2rem",
|
|
1551
|
+
padding: "1rem",
|
|
1552
|
+
background: theme.fg,
|
|
1553
|
+
color: theme.bg,
|
|
1554
|
+
fontFamily: theme.fontDisplay,
|
|
1555
|
+
fontSize: "0.85rem",
|
|
1556
|
+
fontWeight: 500,
|
|
1557
|
+
textAlign: "center",
|
|
1558
|
+
textDecoration: "none",
|
|
1559
|
+
letterSpacing: "0.08em",
|
|
1560
|
+
textTransform: "uppercase",
|
|
1561
|
+
boxSizing: "border-box"
|
|
1562
|
+
},
|
|
1563
|
+
children: "Shop Online"
|
|
1564
|
+
}
|
|
1565
|
+
),
|
|
1566
|
+
/* @__PURE__ */ jsxs("footer", { style: {
|
|
1567
|
+
marginTop: "3rem",
|
|
1568
|
+
paddingTop: "1.5rem",
|
|
1569
|
+
borderTop: `1px solid ${theme.fg}0A`,
|
|
1570
|
+
textAlign: "center"
|
|
1571
|
+
}, children: [
|
|
1572
|
+
/* @__PURE__ */ jsxs("div", { style: { display: "flex", justifyContent: "center", gap: "2rem", marginBottom: "0.75rem" }, children: [
|
|
1573
|
+
coa && /* @__PURE__ */ jsx(FooterLabel, { text: "Lab Verified" }),
|
|
1574
|
+
/* @__PURE__ */ jsx(FooterLabel, { text: "Authentic Product" })
|
|
1386
1575
|
] }),
|
|
1387
|
-
store?.name && /* @__PURE__ */ jsxs("p", { style: { fontSize:
|
|
1388
|
-
|
|
1389
|
-
store.
|
|
1576
|
+
store?.name && /* @__PURE__ */ jsxs("p", { style: { fontSize: 11, color: `${theme.fg}40`, margin: "0.5rem 0 0", letterSpacing: "0.1em" }, children: [
|
|
1577
|
+
store.name,
|
|
1578
|
+
store?.tagline ? ` \u2014 ${store.tagline}` : ""
|
|
1390
1579
|
] })
|
|
1391
1580
|
] })
|
|
1392
1581
|
] }),
|
|
1393
|
-
showCOA && coa && /* @__PURE__ */
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
style: { flex: 1, border: "none", background: "#fff" },
|
|
1421
|
-
title: "Lab Results"
|
|
1422
|
-
}
|
|
1423
|
-
)
|
|
1424
|
-
]
|
|
1425
|
-
}
|
|
1426
|
-
)
|
|
1582
|
+
showCOA && coa && /* @__PURE__ */ jsx(COAModal, { coa, theme, onClose: () => setShowCOA(false) })
|
|
1583
|
+
] });
|
|
1584
|
+
}
|
|
1585
|
+
function CannabinoidStat({ label, value, theme, showBorder }) {
|
|
1586
|
+
return /* @__PURE__ */ jsxs("div", { style: {
|
|
1587
|
+
padding: "1.25rem 0.5rem",
|
|
1588
|
+
textAlign: "center",
|
|
1589
|
+
borderRight: showBorder ? `1px solid ${theme.fg}0F` : void 0
|
|
1590
|
+
}, children: [
|
|
1591
|
+
/* @__PURE__ */ jsxs("div", { style: {
|
|
1592
|
+
fontFamily: theme.fontDisplay,
|
|
1593
|
+
fontSize: "clamp(1.5rem, 5vw, 2rem)",
|
|
1594
|
+
fontWeight: 300,
|
|
1595
|
+
lineHeight: 1,
|
|
1596
|
+
color: theme.fg
|
|
1597
|
+
}, children: [
|
|
1598
|
+
value.toFixed(value >= 1 ? 1 : 2),
|
|
1599
|
+
/* @__PURE__ */ jsx("span", { style: { fontSize: "0.6em", marginLeft: 1 }, children: "%" })
|
|
1600
|
+
] }),
|
|
1601
|
+
/* @__PURE__ */ jsx("div", { style: {
|
|
1602
|
+
fontSize: 11,
|
|
1603
|
+
fontWeight: 500,
|
|
1604
|
+
textTransform: "uppercase",
|
|
1605
|
+
letterSpacing: "0.25em",
|
|
1606
|
+
color: theme.accent,
|
|
1607
|
+
marginTop: "0.5rem"
|
|
1608
|
+
}, children: label })
|
|
1427
1609
|
] });
|
|
1428
1610
|
}
|
|
1429
|
-
function
|
|
1430
|
-
return /* @__PURE__ */ jsxs("div", { style: {
|
|
1431
|
-
|
|
1432
|
-
|
|
1611
|
+
function DetailRow({ label, value, theme }) {
|
|
1612
|
+
return /* @__PURE__ */ jsxs("div", { style: {
|
|
1613
|
+
display: "flex",
|
|
1614
|
+
justifyContent: "space-between",
|
|
1615
|
+
alignItems: "baseline",
|
|
1616
|
+
padding: "0.625rem 0"
|
|
1617
|
+
}, children: [
|
|
1618
|
+
/* @__PURE__ */ jsx("span", { style: {
|
|
1619
|
+
fontSize: 12,
|
|
1620
|
+
textTransform: "uppercase",
|
|
1621
|
+
letterSpacing: "0.15em",
|
|
1622
|
+
color: `${theme.fg}66`
|
|
1623
|
+
}, children: label }),
|
|
1624
|
+
/* @__PURE__ */ jsx("span", { style: {
|
|
1625
|
+
fontSize: 14,
|
|
1626
|
+
fontWeight: 300,
|
|
1627
|
+
color: `${theme.fg}CC`
|
|
1628
|
+
}, children: value })
|
|
1629
|
+
] });
|
|
1630
|
+
}
|
|
1631
|
+
function FooterLabel({ text }) {
|
|
1632
|
+
return /* @__PURE__ */ jsx("span", { style: {
|
|
1633
|
+
fontSize: 10,
|
|
1634
|
+
textTransform: "uppercase",
|
|
1635
|
+
letterSpacing: "0.15em",
|
|
1636
|
+
color: "rgba(255,255,255,0.25)"
|
|
1637
|
+
}, children: text });
|
|
1638
|
+
}
|
|
1639
|
+
function COAModal({ coa, theme, onClose }) {
|
|
1640
|
+
return /* @__PURE__ */ jsxs("div", { style: { position: "fixed", inset: 0, zIndex: 9999, background: "rgba(0,0,0,0.95)", display: "flex", flexDirection: "column" }, children: [
|
|
1641
|
+
/* @__PURE__ */ jsxs("div", { style: {
|
|
1642
|
+
display: "flex",
|
|
1643
|
+
justifyContent: "space-between",
|
|
1644
|
+
alignItems: "center",
|
|
1645
|
+
padding: "0.75rem 1rem",
|
|
1646
|
+
borderBottom: `1px solid ${theme.fg}10`
|
|
1647
|
+
}, children: [
|
|
1648
|
+
/* @__PURE__ */ jsx("span", { style: {
|
|
1649
|
+
color: "#fff",
|
|
1650
|
+
fontFamily: theme.fontDisplay,
|
|
1651
|
+
fontWeight: 500,
|
|
1652
|
+
fontSize: "0.85rem",
|
|
1653
|
+
letterSpacing: "-0.01em"
|
|
1654
|
+
}, children: coa.document_name || "Lab Results" }),
|
|
1655
|
+
/* @__PURE__ */ jsx(
|
|
1656
|
+
"button",
|
|
1657
|
+
{
|
|
1658
|
+
onClick: onClose,
|
|
1659
|
+
style: {
|
|
1660
|
+
background: `${theme.fg}10`,
|
|
1661
|
+
border: "none",
|
|
1662
|
+
color: "#fff",
|
|
1663
|
+
fontSize: "0.85rem",
|
|
1664
|
+
cursor: "pointer",
|
|
1665
|
+
padding: "0.375rem 0.75rem",
|
|
1666
|
+
letterSpacing: "0.05em"
|
|
1667
|
+
},
|
|
1668
|
+
children: "Close"
|
|
1669
|
+
}
|
|
1670
|
+
)
|
|
1671
|
+
] }),
|
|
1672
|
+
/* @__PURE__ */ jsx("iframe", { src: coa.url, style: { flex: 1, border: "none", background: "#fff" }, title: "Lab Results" })
|
|
1433
1673
|
] });
|
|
1434
1674
|
}
|
|
1435
1675
|
function strainBadgeColor(strain) {
|
|
@@ -1439,6 +1679,14 @@ function strainBadgeColor(strain) {
|
|
|
1439
1679
|
if (s === "hybrid") return "#f59e0b";
|
|
1440
1680
|
return "#6b7280";
|
|
1441
1681
|
}
|
|
1682
|
+
function formatDate(dateStr) {
|
|
1683
|
+
try {
|
|
1684
|
+
const d = /* @__PURE__ */ new Date(dateStr + "T00:00:00");
|
|
1685
|
+
return d.toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric" });
|
|
1686
|
+
} catch {
|
|
1687
|
+
return dateStr;
|
|
1688
|
+
}
|
|
1689
|
+
}
|
|
1442
1690
|
var containerStyle = {
|
|
1443
1691
|
minHeight: "100dvh",
|
|
1444
1692
|
display: "flex",
|