@aurora-studio/starter-core 0.1.3 → 0.1.4
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/components/AddToCartButton.d.ts +0 -1
- package/dist/components/AddToCartButton.js +1 -46
- package/dist/components/AddToCartFly.d.ts +0 -1
- package/dist/components/AddToCartFly.js +1 -33
- package/dist/components/AuthProvider.d.ts +0 -1
- package/dist/components/AuthProvider.js +1 -79
- package/dist/components/CartLink.d.ts +0 -1
- package/dist/components/CartLink.js +1 -30
- package/dist/components/CartProvider.d.ts +0 -1
- package/dist/components/CartProvider.js +1 -125
- package/dist/components/CatalogueEmptyState.d.ts +0 -1
- package/dist/components/CatalogueEmptyState.js +1 -12
- package/dist/components/CatalogueFilters.d.ts +0 -1
- package/dist/components/CatalogueFilters.js +1 -88
- package/dist/components/CheckoutButton.d.ts +0 -1
- package/dist/components/CheckoutButton.js +1 -70
- package/dist/components/ConditionalHolmesScript.d.ts +0 -1
- package/dist/components/ConditionalHolmesScript.js +1 -17
- package/dist/components/FloatingLabelInput.d.ts +0 -1
- package/dist/components/FloatingLabelInput.js +1 -13
- package/dist/components/HolmesHomeRefresher.d.ts +0 -1
- package/dist/components/HolmesHomeRefresher.js +1 -19
- package/dist/components/HolmesProductViewTracker.d.ts +0 -1
- package/dist/components/HolmesProductViewTracker.js +1 -39
- package/dist/components/HolmesSprinkleIcon.d.ts +0 -1
- package/dist/components/HolmesSprinkleIcon.js +1 -9
- package/dist/components/HolmesTidbits.d.ts +0 -1
- package/dist/components/HolmesTidbits.js +1 -33
- package/dist/components/ProductCardSkeleton.d.ts +0 -1
- package/dist/components/ProductCardSkeleton.js +1 -5
- package/dist/components/ProductDetailTabs.d.ts +0 -1
- package/dist/components/ProductDetailTabs.js +1 -29
- package/dist/components/ProductImage.d.ts +0 -1
- package/dist/components/ProductImage.js +1 -33
- package/dist/components/ProductImageGallery.d.ts +0 -1
- package/dist/components/ProductImageGallery.js +1 -25
- package/dist/components/SearchDropdown.d.ts +7 -2
- package/dist/components/SearchDropdown.js +1 -145
- package/dist/components/SmartCartPanel.d.ts +0 -1
- package/dist/components/SmartCartPanel.js +1 -19
- package/dist/components/SortDropdown.d.ts +0 -1
- package/dist/components/SortDropdown.js +1 -25
- package/dist/components/StoreConfigContext.d.ts +0 -1
- package/dist/components/StoreConfigContext.js +1 -26
- package/dist/components/StoreContext.d.ts +0 -1
- package/dist/components/StoreContext.js +1 -73
- package/dist/components/StoreContextBar.d.ts +0 -1
- package/dist/components/StoreContextBar.js +1 -12
- package/dist/index.d.ts +0 -1
- package/dist/index.js +1 -31
- package/dist/lib/aurora.d.ts +0 -1
- package/dist/lib/aurora.js +1 -235
- package/dist/lib/format-price.d.ts +0 -1
- package/dist/lib/format-price.js +1 -18
- package/dist/lib/holmes-events.d.ts +0 -1
- package/dist/lib/holmes-events.js +1 -73
- package/dist/lib/image-url.d.ts +0 -1
- package/dist/lib/image-url.js +1 -70
- package/dist/lib/utils.d.ts +0 -1
- package/dist/lib/utils.js +1 -9
- package/package.json +14 -9
- package/dist/components/AddToCartButton.d.ts.map +0 -1
- package/dist/components/AddToCartFly.d.ts.map +0 -1
- package/dist/components/AuthProvider.d.ts.map +0 -1
- package/dist/components/CartLink.d.ts.map +0 -1
- package/dist/components/CartProvider.d.ts.map +0 -1
- package/dist/components/CatalogueEmptyState.d.ts.map +0 -1
- package/dist/components/CatalogueFilters.d.ts.map +0 -1
- package/dist/components/CheckoutButton.d.ts.map +0 -1
- package/dist/components/ConditionalHolmesScript.d.ts.map +0 -1
- package/dist/components/FloatingLabelInput.d.ts.map +0 -1
- package/dist/components/HolmesHomeRefresher.d.ts.map +0 -1
- package/dist/components/HolmesProductViewTracker.d.ts.map +0 -1
- package/dist/components/HolmesSprinkleIcon.d.ts.map +0 -1
- package/dist/components/HolmesTidbits.d.ts.map +0 -1
- package/dist/components/ProductCardSkeleton.d.ts.map +0 -1
- package/dist/components/ProductDetailTabs.d.ts.map +0 -1
- package/dist/components/ProductImage.d.ts.map +0 -1
- package/dist/components/ProductImageGallery.d.ts.map +0 -1
- package/dist/components/SearchDropdown.d.ts.map +0 -1
- package/dist/components/SmartCartPanel.d.ts.map +0 -1
- package/dist/components/SortDropdown.d.ts.map +0 -1
- package/dist/components/StoreConfigContext.d.ts.map +0 -1
- package/dist/components/StoreContext.d.ts.map +0 -1
- package/dist/components/StoreContextBar.d.ts.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/lib/aurora.d.ts.map +0 -1
- package/dist/lib/format-price.d.ts.map +0 -1
- package/dist/lib/holmes-events.d.ts.map +0 -1
- package/dist/lib/image-url.d.ts.map +0 -1
- package/dist/lib/utils.d.ts.map +0 -1
|
@@ -12,4 +12,3 @@ interface AddToCartButtonProps {
|
|
|
12
12
|
}
|
|
13
13
|
export declare function AddToCartButton({ recordId, tableSlug, name, unitAmount, sellByWeight, unit, imageUrl, className, }: AddToCartButtonProps): import("react/jsx-runtime").JSX.Element;
|
|
14
14
|
export {};
|
|
15
|
-
//# sourceMappingURL=AddToCartButton.d.ts.map
|
|
@@ -1,46 +1 @@
|
|
|
1
|
-
"
|
|
2
|
-
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
-
import { useState } from "react";
|
|
4
|
-
import { Check } from "lucide-react";
|
|
5
|
-
import { useCart } from "./CartProvider";
|
|
6
|
-
import { useAddToCartFly } from "./AddToCartFly";
|
|
7
|
-
export function AddToCartButton({ recordId, tableSlug, name, unitAmount, sellByWeight, unit = "kg", imageUrl, className, }) {
|
|
8
|
-
const { items, addItem } = useCart();
|
|
9
|
-
const flyCtx = useAddToCartFly();
|
|
10
|
-
const [weight, setWeight] = useState("1");
|
|
11
|
-
const cartId = `${tableSlug}:${recordId}`;
|
|
12
|
-
const inCart = items.some((i) => i.id === cartId);
|
|
13
|
-
if (sellByWeight) {
|
|
14
|
-
const handleAdd = (e) => {
|
|
15
|
-
const w = parseFloat(weight);
|
|
16
|
-
if (!Number.isFinite(w) || w <= 0)
|
|
17
|
-
return;
|
|
18
|
-
const rect = e.currentTarget.getBoundingClientRect();
|
|
19
|
-
flyCtx?.triggerFly(imageUrl ?? null, rect);
|
|
20
|
-
addItem({
|
|
21
|
-
recordId,
|
|
22
|
-
tableSlug,
|
|
23
|
-
name,
|
|
24
|
-
unitAmount,
|
|
25
|
-
quantity: w,
|
|
26
|
-
sellByWeight: true,
|
|
27
|
-
unit,
|
|
28
|
-
imageUrl,
|
|
29
|
-
});
|
|
30
|
-
};
|
|
31
|
-
return (_jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("input", { type: "number", step: "0.1", min: "0.1", value: weight, onChange: (e) => setWeight(e.target.value), className: "w-20 px-2 py-2 rounded-lg border border-aurora-border bg-aurora-surface text-aurora-text" }), _jsx("span", { className: "text-aurora-muted", children: unit })] }), _jsx("button", { type: "button", onClick: handleAdd, disabled: inCart, className: inCart
|
|
32
|
-
? `inline-flex items-center gap-1.5 text-aurora-primary font-medium cursor-default ${className ?? ""}`.trim()
|
|
33
|
-
: className ??
|
|
34
|
-
"h-12 px-4 rounded-xl bg-aurora-primary text-white font-semibold hover:bg-aurora-primary-dark transition-colors", children: inCart ? (_jsxs(_Fragment, { children: [_jsx(Check, { className: "w-4 h-4 shrink-0", "aria-hidden": true }), "Added"] })) : ("Add to cart") })] }));
|
|
35
|
-
}
|
|
36
|
-
const handleAdd = (e) => {
|
|
37
|
-
const rect = e.currentTarget.getBoundingClientRect();
|
|
38
|
-
flyCtx?.triggerFly(imageUrl ?? null, rect);
|
|
39
|
-
addItem({ recordId, tableSlug, name, unitAmount, imageUrl });
|
|
40
|
-
};
|
|
41
|
-
const baseClass = className ??
|
|
42
|
-
"h-12 px-4 rounded-xl bg-aurora-primary text-white font-semibold hover:bg-aurora-primary-dark transition-colors";
|
|
43
|
-
return (_jsx("button", { type: "button", onClick: handleAdd, disabled: inCart, className: inCart
|
|
44
|
-
? `inline-flex items-center gap-1.5 text-aurora-primary font-medium cursor-default ${className ?? ""}`.trim()
|
|
45
|
-
: baseClass, children: inCart ? (_jsxs(_Fragment, { children: [_jsx(Check, { className: "w-4 h-4 shrink-0", "aria-hidden": true }), "Added"] })) : ("Add to cart") }));
|
|
46
|
-
}
|
|
1
|
+
import{jsx as r,jsxs as e,Fragment as t}from"react/jsx-runtime";import{useState as a}from"react";import{Check as i}from"lucide-react";import{useCart as n}from"./CartProvider";import{useAddToCartFly as o}from"./AddToCartFly";export function AddToCartButton({recordId:d,tableSlug:l,name:s,unitAmount:m,sellByWeight:u,unit:c="kg",imageUrl:g,className:p}){const{items:h,addItem:b}=n(),x=o(),[f,y]=a("1"),N=`${l}:${d}`,A=h.some(r=>r.id===N);if(u){const a=r=>{const e=parseFloat(f);if(!Number.isFinite(e)||e<=0)return;const t=r.currentTarget.getBoundingClientRect();x?.triggerFly(g??null,t),b({recordId:d,tableSlug:l,name:s,unitAmount:m,quantity:e,sellByWeight:!0,unit:c,imageUrl:g})};return e("div",{className:"flex flex-wrap items-center gap-2",children:[e("div",{className:"flex items-center gap-2",children:[r("input",{type:"number",step:"0.1",min:"0.1",value:f,onChange:r=>y(r.target.value),className:"w-20 px-2 py-2 rounded-lg border border-aurora-border bg-aurora-surface text-aurora-text"}),r("span",{className:"text-aurora-muted",children:c})]}),r("button",{type:"button",onClick:a,disabled:A,className:A?`inline-flex items-center gap-1.5 text-aurora-primary font-medium cursor-default ${p??""}`.trim():p??"h-12 px-4 rounded-xl bg-aurora-primary text-white font-semibold hover:bg-aurora-primary-dark transition-colors",children:A?e(t,{children:[r(i,{className:"w-4 h-4 shrink-0","aria-hidden":!0}),"Added"]}):"Add to cart"})]})}const C=p??"h-12 px-4 rounded-xl bg-aurora-primary text-white font-semibold hover:bg-aurora-primary-dark transition-colors";return r("button",{type:"button",onClick:r=>{const e=r.currentTarget.getBoundingClientRect();x?.triggerFly(g??null,e),b({recordId:d,tableSlug:l,name:s,unitAmount:m,imageUrl:g})},disabled:A,className:A?`inline-flex items-center gap-1.5 text-aurora-primary font-medium cursor-default ${p??""}`.trim():C,children:A?e(t,{children:[r(i,{className:"w-4 h-4 shrink-0","aria-hidden":!0}),"Added"]}):"Add to cart"})}
|
|
@@ -1,33 +1 @@
|
|
|
1
|
-
"
|
|
2
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { createContext, useContext, useCallback, useState, useRef, useEffect } from "react";
|
|
4
|
-
import { createPortal } from "react-dom";
|
|
5
|
-
const AddToCartFlyContext = createContext(null);
|
|
6
|
-
export function useAddToCartFly() {
|
|
7
|
-
const ctx = useContext(AddToCartFlyContext);
|
|
8
|
-
return ctx;
|
|
9
|
-
}
|
|
10
|
-
export function AddToCartFlyProvider({ children }) {
|
|
11
|
-
const [fly, setFly] = useState(null);
|
|
12
|
-
const cartRef = useRef(null);
|
|
13
|
-
const setCartRef = useCallback((el) => {
|
|
14
|
-
cartRef.current = el;
|
|
15
|
-
}, []);
|
|
16
|
-
const triggerFly = useCallback((imageUrl, fromRect) => {
|
|
17
|
-
const toRect = cartRef.current?.getBoundingClientRect() ?? new DOMRect(0, 0, 0, 0);
|
|
18
|
-
setFly({ imageUrl, fromRect, toRect, key: Date.now() });
|
|
19
|
-
}, []);
|
|
20
|
-
useEffect(() => {
|
|
21
|
-
if (!fly)
|
|
22
|
-
return;
|
|
23
|
-
const t = setTimeout(() => setFly(null), 500);
|
|
24
|
-
return () => clearTimeout(t);
|
|
25
|
-
}, [fly]);
|
|
26
|
-
return (_jsxs(AddToCartFlyContext.Provider, { value: { triggerFly, setCartRef }, children: [children, fly && typeof window !== "undefined" && createPortal(_jsx("div", { className: "add-to-cart-fly fixed inset-0 pointer-events-none z-[99999]", "aria-hidden": true, children: _jsx("div", { className: "add-to-cart-fly-img absolute w-12 h-12 rounded-xl overflow-hidden bg-aurora-surface border-2 border-aurora-primary shadow-lg", style: {
|
|
27
|
-
["--fly-from-x"]: `${(fly.fromRect?.left ?? 0) + (fly.fromRect?.width ?? 0) / 2 - 24}px`,
|
|
28
|
-
["--fly-from-y"]: `${(fly.fromRect?.top ?? 0) + (fly.fromRect?.height ?? 0) / 2 - 24}px`,
|
|
29
|
-
["--fly-to-x"]: `${(fly.toRect?.left ?? 0) + (fly.toRect?.width ?? 0) / 2 - 24}px`,
|
|
30
|
-
["--fly-to-y"]: `${(fly.toRect?.top ?? 0) + (fly.toRect?.height ?? 0) / 2 - 24}px`,
|
|
31
|
-
animation: "add-to-cart-fly 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94) forwards",
|
|
32
|
-
}, children: fly.imageUrl ? (_jsx("img", { src: fly.imageUrl, alt: "", className: "w-full h-full object-cover" })) : (_jsx("div", { className: "w-full h-full flex items-center justify-center text-aurora-primary text-lg", children: "+" })) }) }, fly.key), document.body)] }));
|
|
33
|
-
}
|
|
1
|
+
import{jsx as e,jsxs as t}from"react/jsx-runtime";import{createContext as r,useContext as o,useCallback as l,useState as i,useRef as a,useEffect as c}from"react";import{createPortal as n}from"react-dom";const d=r(null);export function useAddToCartFly(){return o(d)}export function AddToCartFlyProvider({children:r}){const[o,f]=i(null),s=a(null),u=l(e=>{s.current=e},[]),m=l((e,t)=>{const r=s.current?.getBoundingClientRect()??new DOMRect(0,0,0,0);f({imageUrl:e,fromRect:t,toRect:r,key:Date.now()})},[]);return c(()=>{if(!o)return;const e=setTimeout(()=>f(null),500);return()=>clearTimeout(e)},[o]),t(d.Provider,{value:{triggerFly:m,setCartRef:u},children:[r,o&&"undefined"!=typeof window&&n(e("div",{className:"add-to-cart-fly fixed inset-0 pointer-events-none z-[99999]","aria-hidden":!0,children:e("div",{className:"add-to-cart-fly-img absolute w-12 h-12 rounded-xl overflow-hidden bg-aurora-surface border-2 border-aurora-primary shadow-lg",style:{"--fly-from-x":(o.fromRect?.left??0)+(o.fromRect?.width??0)/2-24+"px","--fly-from-y":(o.fromRect?.top??0)+(o.fromRect?.height??0)/2-24+"px","--fly-to-x":(o.toRect?.left??0)+(o.toRect?.width??0)/2-24+"px","--fly-to-y":(o.toRect?.top??0)+(o.toRect?.height??0)/2-24+"px",animation:"add-to-cart-fly 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94) forwards"},children:o.imageUrl?e("img",{src:o.imageUrl,alt:"",className:"w-full h-full object-cover"}):e("div",{className:"w-full h-full flex items-center justify-center text-aurora-primary text-lg",children:"+"})})},o.key),document.body)]})}
|
|
@@ -1,79 +1 @@
|
|
|
1
|
-
"
|
|
2
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
-
import { createContext, useCallback, useContext, useEffect, useState, } from "react";
|
|
4
|
-
const AUTH_TOKEN_KEY = "aurora_app_token";
|
|
5
|
-
const AuthContext = createContext(null);
|
|
6
|
-
export function AuthProvider({ children }) {
|
|
7
|
-
const [state, setState] = useState({
|
|
8
|
-
user: null,
|
|
9
|
-
token: null,
|
|
10
|
-
loading: true,
|
|
11
|
-
});
|
|
12
|
-
const fetchSession = useCallback(async () => {
|
|
13
|
-
if (typeof window === "undefined")
|
|
14
|
-
return;
|
|
15
|
-
const token = localStorage.getItem(AUTH_TOKEN_KEY);
|
|
16
|
-
if (!token) {
|
|
17
|
-
setState((s) => ({ ...s, user: null, token: null, loading: false }));
|
|
18
|
-
return;
|
|
19
|
-
}
|
|
20
|
-
try {
|
|
21
|
-
const res = await fetch("/api/auth/session", {
|
|
22
|
-
headers: { Authorization: `Bearer ${token}` },
|
|
23
|
-
});
|
|
24
|
-
if (res.ok) {
|
|
25
|
-
const data = (await res.json());
|
|
26
|
-
setState((s) => ({ ...s, user: data.user, token, loading: false }));
|
|
27
|
-
}
|
|
28
|
-
else {
|
|
29
|
-
localStorage.removeItem(AUTH_TOKEN_KEY);
|
|
30
|
-
setState((s) => ({ ...s, user: null, token: null, loading: false }));
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
catch {
|
|
34
|
-
localStorage.removeItem(AUTH_TOKEN_KEY);
|
|
35
|
-
setState((s) => ({ ...s, user: null, token: null, loading: false }));
|
|
36
|
-
}
|
|
37
|
-
}, []);
|
|
38
|
-
useEffect(() => {
|
|
39
|
-
fetchSession();
|
|
40
|
-
}, [fetchSession]);
|
|
41
|
-
const setSession = useCallback((token, user) => {
|
|
42
|
-
if (typeof window !== "undefined") {
|
|
43
|
-
localStorage.setItem(AUTH_TOKEN_KEY, token);
|
|
44
|
-
}
|
|
45
|
-
setState((s) => ({ ...s, user, token, loading: false }));
|
|
46
|
-
}, []);
|
|
47
|
-
const signOut = useCallback(async () => {
|
|
48
|
-
const token = state.token ?? (typeof window !== "undefined" ? localStorage.getItem(AUTH_TOKEN_KEY) : null);
|
|
49
|
-
if (token) {
|
|
50
|
-
try {
|
|
51
|
-
await fetch("/api/auth/signout", {
|
|
52
|
-
method: "POST",
|
|
53
|
-
headers: { Authorization: `Bearer ${token}` },
|
|
54
|
-
});
|
|
55
|
-
}
|
|
56
|
-
catch {
|
|
57
|
-
// ignore
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
if (typeof window !== "undefined") {
|
|
61
|
-
localStorage.removeItem(AUTH_TOKEN_KEY);
|
|
62
|
-
}
|
|
63
|
-
setState((s) => ({ ...s, user: null, token: null }));
|
|
64
|
-
}, [state.token]);
|
|
65
|
-
const value = {
|
|
66
|
-
...state,
|
|
67
|
-
setSession,
|
|
68
|
-
signOut,
|
|
69
|
-
fetchSession,
|
|
70
|
-
};
|
|
71
|
-
return _jsx(AuthContext.Provider, { value: value, children: children });
|
|
72
|
-
}
|
|
73
|
-
export function useAuth() {
|
|
74
|
-
const ctx = useContext(AuthContext);
|
|
75
|
-
if (!ctx) {
|
|
76
|
-
throw new Error("useAuth must be used within AuthProvider");
|
|
77
|
-
}
|
|
78
|
-
return ctx;
|
|
79
|
-
}
|
|
1
|
+
import{jsx as e}from"react/jsx-runtime";import{createContext as t,useCallback as o,useContext as n,useEffect as r,useState as l}from"react";const u="aurora_app_token",a=t(null);export function AuthProvider({children:t}){const[n,i]=l({user:null,token:null,loading:!0}),s=o(async()=>{if("undefined"==typeof window)return;const e=localStorage.getItem(u);if(e)try{const t=await fetch("/api/auth/session",{headers:{Authorization:`Bearer ${e}`}});if(t.ok){const o=await t.json();i(t=>({...t,user:o.user,token:e,loading:!1}))}else localStorage.removeItem(u),i(e=>({...e,user:null,token:null,loading:!1}))}catch{localStorage.removeItem(u),i(e=>({...e,user:null,token:null,loading:!1}))}else i(e=>({...e,user:null,token:null,loading:!1}))},[]);r(()=>{s()},[s]);const c=o((e,t)=>{"undefined"!=typeof window&&localStorage.setItem(u,e),i(o=>({...o,user:t,token:e,loading:!1}))},[]),d=o(async()=>{const e=n.token??("undefined"!=typeof window?localStorage.getItem(u):null);if(e)try{await fetch("/api/auth/signout",{method:"POST",headers:{Authorization:`Bearer ${e}`}})}catch{}"undefined"!=typeof window&&localStorage.removeItem(u),i(e=>({...e,user:null,token:null}))},[n.token]),f={...n,setSession:c,signOut:d,fetchSession:s};return e(a.Provider,{value:f,children:t})}export function useAuth(){const e=n(a);if(!e)throw new Error("useAuth must be used within AuthProvider");return e}
|
|
@@ -1,30 +1 @@
|
|
|
1
|
-
"
|
|
2
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import Link from "next/link";
|
|
4
|
-
import { ShoppingCart } from "lucide-react";
|
|
5
|
-
import { useCart } from "./CartProvider";
|
|
6
|
-
import { useAddToCartFly } from "./AddToCartFly";
|
|
7
|
-
import { formatPrice } from "../lib/format-price";
|
|
8
|
-
import { useState, useRef, useEffect } from "react";
|
|
9
|
-
export function CartLink() {
|
|
10
|
-
const { items, total } = useCart();
|
|
11
|
-
const flyCtx = useAddToCartFly();
|
|
12
|
-
const [open, setOpen] = useState(false);
|
|
13
|
-
const ref = useRef(null);
|
|
14
|
-
const linkRef = useRef(null);
|
|
15
|
-
const count = items.reduce((sum, i) => sum + i.quantity, 0);
|
|
16
|
-
useEffect(() => {
|
|
17
|
-
flyCtx?.setCartRef(linkRef.current);
|
|
18
|
-
return () => flyCtx?.setCartRef(null);
|
|
19
|
-
}, [flyCtx]);
|
|
20
|
-
useEffect(() => {
|
|
21
|
-
function handleClickOutside(e) {
|
|
22
|
-
if (ref.current && !ref.current.contains(e.target)) {
|
|
23
|
-
setOpen(false);
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
document.addEventListener("mousedown", handleClickOutside);
|
|
27
|
-
return () => document.removeEventListener("mousedown", handleClickOutside);
|
|
28
|
-
}, []);
|
|
29
|
-
return (_jsxs("div", { ref: ref, className: "relative", onMouseEnter: () => setOpen(true), onMouseLeave: () => setOpen(false), children: [_jsxs(Link, { ref: linkRef, href: "/cart", className: "flex items-center gap-2 text-sm text-aurora-muted hover:text-aurora-text transition-colors font-medium", children: [_jsx(ShoppingCart, { className: "w-5 h-5 shrink-0" }), _jsx("span", { children: "Cart" }), count > 0 && (_jsx("span", { className: "inline-flex items-center justify-center min-w-[1.25rem] h-5 px-2 rounded-full bg-aurora-primary text-white text-xs font-semibold cart-badge", children: count }, count))] }), open && count > 0 && (_jsx("div", { className: "absolute top-full right-0 mt-1 pt-1 z-[200]", children: _jsxs("div", { className: "rounded-xl bg-aurora-surface border border-aurora-border shadow-xl w-80 max-h-96 overflow-hidden", children: [_jsxs("div", { className: "p-4 border-b border-aurora-border", children: [_jsxs("p", { className: "font-semibold text-sm", children: [count, " ", count === 1 ? "item" : "items"] }), _jsx("p", { className: "text-lg font-bold text-aurora-primary mt-0.5", children: formatPrice(total) })] }), _jsxs("div", { className: "max-h-48 overflow-y-auto", children: [items.slice(0, 5).map((item) => (_jsxs("div", { className: "flex gap-3 p-3 border-b border-aurora-border last:border-0", children: [_jsx("div", { className: "w-12 h-12 rounded-lg bg-aurora-surface-hover shrink-0 overflow-hidden", children: item.imageUrl ? (_jsx("img", { src: item.imageUrl, alt: "", className: "w-full h-full object-cover" })) : (_jsx("div", { className: "w-full h-full flex items-center justify-center text-aurora-muted text-lg", children: "-" })) }), _jsxs("div", { className: "flex-1 min-w-0", children: [_jsx("p", { className: "text-sm font-medium truncate", children: item.name }), _jsxs("p", { className: "text-xs text-aurora-muted", children: [formatPrice(item.unitAmount), " \u00D7 ", item.quantity, item.sellByWeight ? ` ${item.unit || "kg"}` : ""] })] })] }, item.id))), items.length > 5 && (_jsxs("p", { className: "text-xs text-aurora-muted p-3 text-center", children: ["+", items.length - 5, " more"] }))] }), _jsx("div", { className: "p-4", children: _jsx(Link, { href: "/cart", onClick: () => setOpen(false), className: "block w-full py-3 rounded-xl bg-aurora-primary text-white text-center font-semibold hover:bg-aurora-primary-dark transition-colors", children: "View cart \u00B7 Checkout" }) })] }) }))] }));
|
|
30
|
-
}
|
|
1
|
+
import{jsx as e,jsxs as r}from"react/jsx-runtime";import t from"next/link";import{ShoppingCart as a}from"lucide-react";import{useCart as l}from"./CartProvider";import{useAddToCartFly as o}from"./AddToCartFly";import{formatPrice as i}from"../lib/format-price";import{useState as n,useRef as s,useEffect as m}from"react";export function CartLink(){const{items:c,total:d}=l(),u=o(),[h,f]=n(!1),x=s(null),p=s(null),b=c.reduce((e,r)=>e+r.quantity,0);return m(()=>(u?.setCartRef(p.current),()=>u?.setCartRef(null)),[u]),m(()=>{function e(e){x.current&&!x.current.contains(e.target)&&f(!1)}return document.addEventListener("mousedown",e),()=>document.removeEventListener("mousedown",e)},[]),r("div",{ref:x,className:"relative",onMouseEnter:()=>f(!0),onMouseLeave:()=>f(!1),children:[r(t,{ref:p,href:"/cart",className:"flex items-center gap-2 text-sm text-aurora-muted hover:text-aurora-text transition-colors font-medium",children:[e(a,{className:"w-5 h-5 shrink-0"}),e("span",{children:"Cart"}),b>0&&e("span",{className:"inline-flex items-center justify-center min-w-[1.25rem] h-5 px-2 rounded-full bg-aurora-primary text-white text-xs font-semibold cart-badge",children:b},b)]}),h&&b>0&&e("div",{className:"absolute top-full right-0 mt-1 pt-1 z-[200]",children:r("div",{className:"rounded-xl bg-aurora-surface border border-aurora-border shadow-xl w-80 max-h-96 overflow-hidden",children:[r("div",{className:"p-4 border-b border-aurora-border",children:[r("p",{className:"font-semibold text-sm",children:[b," ",1===b?"item":"items"]}),e("p",{className:"text-lg font-bold text-aurora-primary mt-0.5",children:i(d)})]}),r("div",{className:"max-h-48 overflow-y-auto",children:[c.slice(0,5).map(t=>r("div",{className:"flex gap-3 p-3 border-b border-aurora-border last:border-0",children:[e("div",{className:"w-12 h-12 rounded-lg bg-aurora-surface-hover shrink-0 overflow-hidden",children:t.imageUrl?e("img",{src:t.imageUrl,alt:"",className:"w-full h-full object-cover"}):e("div",{className:"w-full h-full flex items-center justify-center text-aurora-muted text-lg",children:"-"})}),r("div",{className:"flex-1 min-w-0",children:[e("p",{className:"text-sm font-medium truncate",children:t.name}),r("p",{className:"text-xs text-aurora-muted",children:[i(t.unitAmount)," × ",t.quantity,t.sellByWeight?` ${t.unit||"kg"}`:""]})]})]},t.id)),c.length>5&&r("p",{className:"text-xs text-aurora-muted p-3 text-center",children:["+",c.length-5," more"]})]}),e("div",{className:"p-4",children:e(t,{href:"/cart",onClick:()=>f(!1),className:"block w-full py-3 rounded-xl bg-aurora-primary text-white text-center font-semibold hover:bg-aurora-primary-dark transition-colors",children:"View cart · Checkout"})})]})})]})}
|
|
@@ -1,125 +1 @@
|
|
|
1
|
-
"
|
|
2
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
-
import { createContext, useContext, useCallback, useState, useEffect, useRef, } from "react";
|
|
4
|
-
import { holmesCartUpdate } from "../lib/holmes-events";
|
|
5
|
-
const CART_KEY = "aurora-cart";
|
|
6
|
-
function loadCart() {
|
|
7
|
-
if (typeof window === "undefined")
|
|
8
|
-
return [];
|
|
9
|
-
try {
|
|
10
|
-
const stored = localStorage.getItem(CART_KEY);
|
|
11
|
-
return stored ? JSON.parse(stored) : [];
|
|
12
|
-
}
|
|
13
|
-
catch {
|
|
14
|
-
return [];
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
function saveCart(items) {
|
|
18
|
-
if (typeof window === "undefined")
|
|
19
|
-
return;
|
|
20
|
-
localStorage.setItem(CART_KEY, JSON.stringify(items));
|
|
21
|
-
}
|
|
22
|
-
const CartContext = createContext(null);
|
|
23
|
-
export function CartProvider({ children }) {
|
|
24
|
-
const [items, setItems] = useState([]);
|
|
25
|
-
const [mounted, setMounted] = useState(false);
|
|
26
|
-
const itemsRef = useRef(items);
|
|
27
|
-
const bootstrapFiredRef = useRef(false);
|
|
28
|
-
itemsRef.current = items;
|
|
29
|
-
useEffect(() => {
|
|
30
|
-
setItems(loadCart());
|
|
31
|
-
setMounted(true);
|
|
32
|
-
}, []);
|
|
33
|
-
useEffect(() => {
|
|
34
|
-
if (mounted)
|
|
35
|
-
saveCart(items);
|
|
36
|
-
}, [items, mounted]);
|
|
37
|
-
useEffect(() => {
|
|
38
|
-
if (!mounted)
|
|
39
|
-
return;
|
|
40
|
-
const count = items.reduce((s, i) => s + i.quantity, 0);
|
|
41
|
-
const holmesItems = items.map((i) => ({
|
|
42
|
-
id: i.recordId,
|
|
43
|
-
name: i.name,
|
|
44
|
-
price: i.unitAmount,
|
|
45
|
-
}));
|
|
46
|
-
holmesCartUpdate(count, holmesItems);
|
|
47
|
-
}, [items, mounted]);
|
|
48
|
-
useEffect(() => {
|
|
49
|
-
if (!mounted)
|
|
50
|
-
return;
|
|
51
|
-
const fireBootstrap = () => {
|
|
52
|
-
if (bootstrapFiredRef.current)
|
|
53
|
-
return;
|
|
54
|
-
bootstrapFiredRef.current = true;
|
|
55
|
-
const currentItems = itemsRef.current;
|
|
56
|
-
const count = currentItems.reduce((s, i) => s + i.quantity, 0);
|
|
57
|
-
const holmesItems = currentItems.map((i) => ({
|
|
58
|
-
id: i.recordId,
|
|
59
|
-
name: i.name,
|
|
60
|
-
price: i.unitAmount,
|
|
61
|
-
}));
|
|
62
|
-
holmesCartUpdate(count, holmesItems, true);
|
|
63
|
-
};
|
|
64
|
-
const onReady = () => fireBootstrap();
|
|
65
|
-
if (window.holmes?.getSessionId && !bootstrapFiredRef.current) {
|
|
66
|
-
fireBootstrap();
|
|
67
|
-
}
|
|
68
|
-
document.addEventListener("holmes:ready", onReady);
|
|
69
|
-
return () => document.removeEventListener("holmes:ready", onReady);
|
|
70
|
-
}, [mounted]);
|
|
71
|
-
const addItem = useCallback((item) => {
|
|
72
|
-
const qty = item.quantity ?? 1;
|
|
73
|
-
const cartId = `${item.tableSlug}:${item.recordId}`;
|
|
74
|
-
const isNew = !itemsRef.current.some((i) => i.id === cartId);
|
|
75
|
-
setItems((prev) => {
|
|
76
|
-
const existing = prev.find((i) => i.id === cartId);
|
|
77
|
-
if (existing && existing.sellByWeight === item.sellByWeight) {
|
|
78
|
-
return prev.map((i) => i.id === cartId ? { ...i, quantity: i.quantity + qty } : i);
|
|
79
|
-
}
|
|
80
|
-
return [...prev, { ...item, id: cartId, recordId: item.recordId, quantity: qty }];
|
|
81
|
-
});
|
|
82
|
-
if (isNew && typeof window !== "undefined") {
|
|
83
|
-
window.dispatchEvent(new CustomEvent("cart:itemAdded", { detail: { name: item.name } }));
|
|
84
|
-
}
|
|
85
|
-
}, []);
|
|
86
|
-
useEffect(() => {
|
|
87
|
-
if (!mounted)
|
|
88
|
-
return;
|
|
89
|
-
const handler = (e) => {
|
|
90
|
-
const d = e.detail;
|
|
91
|
-
if (!d?.products?.length || !d.tableSlug)
|
|
92
|
-
return;
|
|
93
|
-
for (const p of d.products) {
|
|
94
|
-
addItem({
|
|
95
|
-
recordId: p.id,
|
|
96
|
-
tableSlug: d.tableSlug,
|
|
97
|
-
name: p.name,
|
|
98
|
-
unitAmount: p.price,
|
|
99
|
-
imageUrl: p.image,
|
|
100
|
-
});
|
|
101
|
-
}
|
|
102
|
-
};
|
|
103
|
-
document.addEventListener("holmes:addBundle", handler);
|
|
104
|
-
return () => document.removeEventListener("holmes:addBundle", handler);
|
|
105
|
-
}, [addItem, mounted]);
|
|
106
|
-
const removeItem = useCallback((id) => {
|
|
107
|
-
setItems((prev) => prev.filter((i) => i.id !== id));
|
|
108
|
-
}, []);
|
|
109
|
-
const updateQuantity = useCallback((id, quantity) => {
|
|
110
|
-
if (quantity <= 0) {
|
|
111
|
-
setItems((prev) => prev.filter((i) => i.id !== id));
|
|
112
|
-
return;
|
|
113
|
-
}
|
|
114
|
-
setItems((prev) => prev.map((i) => (i.id === id ? { ...i, quantity } : i)));
|
|
115
|
-
}, []);
|
|
116
|
-
const clearCart = useCallback(() => setItems([]), []);
|
|
117
|
-
const total = items.reduce((sum, i) => sum + i.unitAmount * i.quantity, 0);
|
|
118
|
-
return (_jsx(CartContext.Provider, { value: { items, addItem, removeItem, updateQuantity, clearCart, total }, children: children }));
|
|
119
|
-
}
|
|
120
|
-
export function useCart() {
|
|
121
|
-
const ctx = useContext(CartContext);
|
|
122
|
-
if (!ctx)
|
|
123
|
-
throw new Error("useCart must be used within CartProvider");
|
|
124
|
-
return ctx;
|
|
125
|
-
}
|
|
1
|
+
import{jsx as e}from"react/jsx-runtime";import{createContext as t,useContext as r,useCallback as n,useState as i,useEffect as o,useRef as d}from"react";import{holmesCartUpdate as u}from"../lib/holmes-events";const a="aurora-cart",c=t(null);export function CartProvider({children:t}){const[r,m]=i([]),[s,l]=i(!1),f=d(r),p=d(!1);f.current=r,o(()=>{m(function(){if("undefined"==typeof window)return[];try{const e=localStorage.getItem(a);return e?JSON.parse(e):[]}catch{return[]}}()),l(!0)},[]),o(()=>{s&&function(e){"undefined"!=typeof window&&localStorage.setItem(a,JSON.stringify(e))}(r)},[r,s]),o(()=>{if(!s)return;const e=r.reduce((e,t)=>e+t.quantity,0),t=r.map(e=>({id:e.recordId,name:e.name,price:e.unitAmount}));u(e,t)},[r,s]),o(()=>{if(!s)return;const e=()=>{if(p.current)return;p.current=!0;const e=f.current,t=e.reduce((e,t)=>e+t.quantity,0),r=e.map(e=>({id:e.recordId,name:e.name,price:e.unitAmount}));u(t,r,!0)},t=()=>e();return window.holmes?.getSessionId&&!p.current&&e(),document.addEventListener("holmes:ready",t),()=>document.removeEventListener("holmes:ready",t)},[s]);const y=n(e=>{const t=e.quantity??1,r=`${e.tableSlug}:${e.recordId}`,n=!f.current.some(e=>e.id===r);m(n=>{const i=n.find(e=>e.id===r);return i&&i.sellByWeight===e.sellByWeight?n.map(e=>e.id===r?{...e,quantity:e.quantity+t}:e):[...n,{...e,id:r,recordId:e.recordId,quantity:t}]}),n&&"undefined"!=typeof window&&window.dispatchEvent(new CustomEvent("cart:itemAdded",{detail:{name:e.name}}))},[]);o(()=>{if(!s)return;const e=e=>{const t=e.detail;if(t?.products?.length&&t.tableSlug)for(const e of t.products)y({recordId:e.id,tableSlug:t.tableSlug,name:e.name,unitAmount:e.price,imageUrl:e.image})};return document.addEventListener("holmes:addBundle",e),()=>document.removeEventListener("holmes:addBundle",e)},[y,s]);const h=n(e=>{m(t=>t.filter(t=>t.id!==e))},[]),g=n((e,t)=>{m(t<=0?t=>t.filter(t=>t.id!==e):r=>r.map(r=>r.id===e?{...r,quantity:t}:r))},[]),v=n(()=>m([]),[]),w=r.reduce((e,t)=>e+t.unitAmount*t.quantity,0);return e(c.Provider,{value:{items:r,addItem:y,removeItem:h,updateQuantity:g,clearCart:v,total:w},children:t})}export function useCart(){const e=r(c);if(!e)throw new Error("useCart must be used within CartProvider");return e}
|
|
@@ -1,12 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
import Link from "next/link";
|
|
3
|
-
import { ShoppingBag, Store, LayoutGrid } from "lucide-react";
|
|
4
|
-
export function CatalogueEmptyState({ hasCategory, hasStore, categories = [], }) {
|
|
5
|
-
if (!hasStore) {
|
|
6
|
-
return (_jsxs("div", { className: "flex flex-col items-center justify-center py-16 px-6 text-center max-w-md mx-auto", children: [_jsx("div", { className: "w-20 h-20 rounded-full bg-aurora-surface-hover flex items-center justify-center mb-6 ring-4 ring-aurora-primary/10", children: _jsx(Store, { className: "w-10 h-10 text-aurora-primary" }) }), _jsx("h2", { className: "text-xl font-bold text-aurora-text mb-2", children: "Select a store to browse" }), _jsx("p", { className: "text-aurora-muted mb-8", children: "Choose your local store to see products and start shopping." }), _jsxs(Link, { href: "/stores", className: "inline-flex items-center justify-center gap-2 h-12 px-6 rounded-xl bg-aurora-primary text-white font-semibold hover:bg-aurora-primary-dark transition-colors", children: [_jsx(Store, { className: "w-5 h-5" }), "Find your store"] })] }));
|
|
7
|
-
}
|
|
8
|
-
if (hasCategory) {
|
|
9
|
-
return (_jsxs("div", { className: "flex flex-col items-center justify-center py-16 px-6 text-center max-w-md mx-auto", children: [_jsx("div", { className: "w-20 h-20 rounded-full bg-aurora-surface-hover flex items-center justify-center mb-6 ring-4 ring-aurora-primary/10", children: _jsx(LayoutGrid, { className: "w-10 h-10 text-aurora-primary" }) }), _jsx("h2", { className: "text-xl font-bold text-aurora-text mb-2", children: "No products in this category yet" }), _jsx("p", { className: "text-aurora-muted mb-8", children: "This category is empty. Try another category or browse all products." }), _jsxs("div", { className: "flex flex-wrap justify-center gap-3", children: [_jsxs(Link, { href: "/catalogue", className: "inline-flex items-center justify-center gap-2 h-12 px-6 rounded-xl bg-aurora-primary text-white font-semibold hover:bg-aurora-primary-dark transition-colors", children: [_jsx(LayoutGrid, { className: "w-5 h-5" }), "View all products"] }), categories.slice(0, 3).map((cat) => (_jsx(Link, { href: `/catalogue?category=${cat.slug}`, className: "inline-flex items-center h-12 px-5 rounded-xl border-2 border-aurora-border text-aurora-text font-medium hover:border-aurora-primary hover:text-aurora-primary transition-colors", children: cat.name }, cat.slug)))] })] }));
|
|
10
|
-
}
|
|
11
|
-
return (_jsxs("div", { className: "flex flex-col items-center justify-center py-16 px-6 text-center max-w-md mx-auto", children: [_jsx("div", { className: "w-20 h-20 rounded-full bg-aurora-surface-hover flex items-center justify-center mb-6 ring-4 ring-aurora-primary/10", children: _jsx(ShoppingBag, { className: "w-10 h-10 text-aurora-primary" }) }), _jsx("h2", { className: "text-xl font-bold text-aurora-text mb-2", children: "No products yet" }), _jsx("p", { className: "text-aurora-muted mb-8", children: "Products will appear here once they're added. Check back soon or browse our categories." }), _jsxs(Link, { href: "/catalogue", className: "inline-flex items-center justify-center gap-2 h-12 px-6 rounded-xl bg-aurora-primary text-white font-semibold hover:bg-aurora-primary-dark transition-colors", children: [_jsx(ShoppingBag, { className: "w-5 h-5" }), "Browse catalogue"] })] }));
|
|
12
|
-
}
|
|
1
|
+
import{jsx as e,jsxs as r}from"react/jsx-runtime";import a from"next/link";import{ShoppingBag as t,Store as o,LayoutGrid as s}from"lucide-react";export function CatalogueEmptyState({hasCategory:l,hasStore:i,categories:c=[]}){return r("div",i?l?{className:"flex flex-col items-center justify-center py-16 px-6 text-center max-w-md mx-auto",children:[e("div",{className:"w-20 h-20 rounded-full bg-aurora-surface-hover flex items-center justify-center mb-6 ring-4 ring-aurora-primary/10",children:e(s,{className:"w-10 h-10 text-aurora-primary"})}),e("h2",{className:"text-xl font-bold text-aurora-text mb-2",children:"No products in this category yet"}),e("p",{className:"text-aurora-muted mb-8",children:"This category is empty. Try another category or browse all products."}),r("div",{className:"flex flex-wrap justify-center gap-3",children:[r(a,{href:"/catalogue",className:"inline-flex items-center justify-center gap-2 h-12 px-6 rounded-xl bg-aurora-primary text-white font-semibold hover:bg-aurora-primary-dark transition-colors",children:[e(s,{className:"w-5 h-5"}),"View all products"]}),c.slice(0,3).map(r=>e(a,{href:`/catalogue?category=${r.slug}`,className:"inline-flex items-center h-12 px-5 rounded-xl border-2 border-aurora-border text-aurora-text font-medium hover:border-aurora-primary hover:text-aurora-primary transition-colors",children:r.name},r.slug))]})]}:{className:"flex flex-col items-center justify-center py-16 px-6 text-center max-w-md mx-auto",children:[e("div",{className:"w-20 h-20 rounded-full bg-aurora-surface-hover flex items-center justify-center mb-6 ring-4 ring-aurora-primary/10",children:e(t,{className:"w-10 h-10 text-aurora-primary"})}),e("h2",{className:"text-xl font-bold text-aurora-text mb-2",children:"No products yet"}),e("p",{className:"text-aurora-muted mb-8",children:"Products will appear here once they're added. Check back soon or browse our categories."}),r(a,{href:"/catalogue",className:"inline-flex items-center justify-center gap-2 h-12 px-6 rounded-xl bg-aurora-primary text-white font-semibold hover:bg-aurora-primary-dark transition-colors",children:[e(t,{className:"w-5 h-5"}),"Browse catalogue"]})]}:{className:"flex flex-col items-center justify-center py-16 px-6 text-center max-w-md mx-auto",children:[e("div",{className:"w-20 h-20 rounded-full bg-aurora-surface-hover flex items-center justify-center mb-6 ring-4 ring-aurora-primary/10",children:e(o,{className:"w-10 h-10 text-aurora-primary"})}),e("h2",{className:"text-xl font-bold text-aurora-text mb-2",children:"Select a store to browse"}),e("p",{className:"text-aurora-muted mb-8",children:"Choose your local store to see products and start shopping."}),r(a,{href:"/stores",className:"inline-flex items-center justify-center gap-2 h-12 px-6 rounded-xl bg-aurora-primary text-white font-semibold hover:bg-aurora-primary-dark transition-colors",children:[e(o,{className:"w-5 h-5"}),"Find your store"]})]})}
|
|
@@ -21,4 +21,3 @@ type CatalogueFiltersProps = {
|
|
|
21
21
|
};
|
|
22
22
|
export declare function CatalogueFilters({ categories, currentCategory, currentSort, onSortChange, storeName, onClose, variant, suggestedSlugs, missionPrioritySlugs, }: CatalogueFiltersProps): import("react/jsx-runtime").JSX.Element;
|
|
23
23
|
export {};
|
|
24
|
-
//# sourceMappingURL=CatalogueFilters.d.ts.map
|
|
@@ -1,88 +1 @@
|
|
|
1
|
-
"
|
|
2
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import Link from "next/link";
|
|
4
|
-
import { useState } from "react";
|
|
5
|
-
import { ChevronDown, Baby, Beer, Candy, Cat, Wheat, Sparkles, CupSoda, Droplets, HeartPulse, Apple, Drumstick, Shirt, } from "lucide-react";
|
|
6
|
-
import { HolmesSprinkleIcon } from "./HolmesSprinkleIcon";
|
|
7
|
-
/** Map category slug (or partial name) to icon for sidebar. */
|
|
8
|
-
function getCategoryIcon(slug) {
|
|
9
|
-
const s = slug.toLowerCase();
|
|
10
|
-
const map = {
|
|
11
|
-
"baby-food": Baby,
|
|
12
|
-
baby: Baby,
|
|
13
|
-
beer: Beer,
|
|
14
|
-
candy: Candy,
|
|
15
|
-
"cat-food": Cat,
|
|
16
|
-
cat: Cat,
|
|
17
|
-
cereal: Wheat,
|
|
18
|
-
cleaning: Sparkles,
|
|
19
|
-
dishwashing: Sparkles,
|
|
20
|
-
tea: CupSoda,
|
|
21
|
-
water: Droplets,
|
|
22
|
-
"health-care": HeartPulse,
|
|
23
|
-
healthcare: HeartPulse,
|
|
24
|
-
juices: Apple,
|
|
25
|
-
juice: Apple,
|
|
26
|
-
poultry: Drumstick,
|
|
27
|
-
"skin-care": Shirt,
|
|
28
|
-
skincare: Shirt,
|
|
29
|
-
vegetables: Apple,
|
|
30
|
-
fruits: Apple,
|
|
31
|
-
dairy: Droplets,
|
|
32
|
-
bakery: Wheat,
|
|
33
|
-
snacks: Candy,
|
|
34
|
-
beverages: CupSoda,
|
|
35
|
-
};
|
|
36
|
-
return map[s] ?? Wheat;
|
|
37
|
-
}
|
|
38
|
-
export const SORT_OPTIONS = [
|
|
39
|
-
{ id: "featured", label: "Featured" },
|
|
40
|
-
{ id: "bestsellers", label: "Bestsellers" },
|
|
41
|
-
{ id: "new", label: "New Arrivals" },
|
|
42
|
-
{ id: "sale", label: "On Sale" },
|
|
43
|
-
];
|
|
44
|
-
function FilterSection({ title, children, defaultOpen = true, }) {
|
|
45
|
-
const [open, setOpen] = useState(defaultOpen);
|
|
46
|
-
return (_jsxs("section", { className: "border-b border-aurora-border last:border-0", children: [_jsxs("button", { type: "button", onClick: () => setOpen((o) => !o), className: "flex items-center justify-between w-full py-3 text-left", children: [_jsx("h3", { className: "text-xs font-semibold uppercase tracking-wider text-aurora-muted", children: title }), _jsx(ChevronDown, { className: `w-4 h-4 text-aurora-muted transition-transform ${open ? "rotate-180" : ""}` })] }), open && _jsx("div", { className: "pb-3", children: children })] }));
|
|
47
|
-
}
|
|
48
|
-
export function CatalogueFilters({ categories, currentCategory, currentSort, onSortChange, storeName, onClose, variant = "sidebar", suggestedSlugs = [], missionPrioritySlugs = [], }) {
|
|
49
|
-
const sortedCategories = [...categories].sort((a, b) => {
|
|
50
|
-
const aSuggested = suggestedSlugs.includes(a.slug);
|
|
51
|
-
const bSuggested = suggestedSlugs.includes(b.slug);
|
|
52
|
-
if (aSuggested && !bSuggested)
|
|
53
|
-
return -1;
|
|
54
|
-
if (!aSuggested && bSuggested)
|
|
55
|
-
return 1;
|
|
56
|
-
if (missionPrioritySlugs.length > 0) {
|
|
57
|
-
const aIdx = missionPrioritySlugs.indexOf(a.slug);
|
|
58
|
-
const bIdx = missionPrioritySlugs.indexOf(b.slug);
|
|
59
|
-
if (aIdx >= 0 && bIdx < 0)
|
|
60
|
-
return -1;
|
|
61
|
-
if (aIdx < 0 && bIdx >= 0)
|
|
62
|
-
return 1;
|
|
63
|
-
if (aIdx >= 0 && bIdx >= 0)
|
|
64
|
-
return aIdx - bIdx;
|
|
65
|
-
}
|
|
66
|
-
return 0;
|
|
67
|
-
});
|
|
68
|
-
const content = (_jsxs("div", { className: "space-y-0", children: [_jsx(FilterSection, { title: "Categories", children: _jsxs("nav", { className: "space-y-1", children: [_jsx(Link, { href: "/catalogue", onClick: onClose, className: `block px-3 py-2 rounded-component text-sm font-medium transition-colors ${!currentCategory
|
|
69
|
-
? "bg-aurora-accent/20 text-aurora-accent border border-aurora-accent/40"
|
|
70
|
-
: "text-aurora-muted hover:text-aurora-text hover:bg-aurora-surface-hover border border-transparent"}`, children: "All categories" }), sortedCategories.map((cat) => {
|
|
71
|
-
const isSuggested = suggestedSlugs.includes(cat.slug);
|
|
72
|
-
return (_jsxs(Link, { href: `/catalogue?category=${encodeURIComponent(cat.slug)}`, onClick: onClose, className: `flex items-center gap-2 px-3 py-2 rounded-component text-sm font-medium transition-colors ${currentCategory === cat.slug
|
|
73
|
-
? "bg-aurora-accent/20 text-aurora-accent border border-aurora-accent/40"
|
|
74
|
-
: "text-aurora-muted hover:text-aurora-text hover:bg-aurora-surface-hover border border-transparent"}`, children: [(() => {
|
|
75
|
-
const Icon = getCategoryIcon(cat.slug);
|
|
76
|
-
return _jsx(Icon, { className: "w-4 h-4 shrink-0 text-aurora-muted", "aria-hidden": true });
|
|
77
|
-
})(), isSuggested ? _jsx(HolmesSprinkleIcon, { className: "shrink-0" }) : null, cat.name] }, cat.slug));
|
|
78
|
-
})] }) }), _jsx(FilterSection, { title: "Sort by", defaultOpen: true, children: _jsx("div", { className: "space-y-1", children: SORT_OPTIONS.map((opt) => (_jsx("button", { type: "button", onClick: () => {
|
|
79
|
-
onSortChange(opt.id);
|
|
80
|
-
onClose?.();
|
|
81
|
-
}, className: `w-full text-left px-3 py-2 rounded-component text-sm font-medium transition-colors ${currentSort === opt.id
|
|
82
|
-
? "bg-aurora-accent/20 text-aurora-accent border border-aurora-accent/40"
|
|
83
|
-
: "text-aurora-muted hover:text-aurora-text hover:bg-aurora-surface-hover border border-transparent"}`, children: opt.label }, opt.id))) }) }), storeName && (_jsxs("p", { className: "text-xs text-aurora-muted pt-2 border-t border-aurora-border", children: ["Showing products from ", storeName] }))] }));
|
|
84
|
-
if (variant === "drawer") {
|
|
85
|
-
return (_jsx("div", { className: "bg-aurora-surface border-t border-aurora-border p-6", children: content }));
|
|
86
|
-
}
|
|
87
|
-
return (_jsx("aside", { className: "w-56 shrink-0 order-first block", children: _jsx("div", { className: "pattern-well sticky top-24 space-y-6 rounded-component border border-aurora-border p-4", children: content }) }));
|
|
88
|
-
}
|
|
1
|
+
import{jsx as e,jsxs as r}from"react/jsx-runtime";import t from"next/link";import{useState as a}from"react";import{ChevronDown as o,Baby as n,Beer as s,Candy as c,Cat as l,Wheat as i,Sparkles as d,CupSoda as u,Droplets as m,HeartPulse as b,Apple as h,Drumstick as p,Shirt as f}from"lucide-react";import{HolmesSprinkleIcon as x}from"./HolmesSprinkleIcon";export const SORT_OPTIONS=[{id:"featured",label:"Featured"},{id:"bestsellers",label:"Bestsellers"},{id:"new",label:"New Arrivals"},{id:"sale",label:"On Sale"}];function g({title:t,children:n,defaultOpen:s=!0}){const[c,l]=a(s);return r("section",{className:"border-b border-aurora-border last:border-0",children:[r("button",{type:"button",onClick:()=>l(e=>!e),className:"flex items-center justify-between w-full py-3 text-left",children:[e("h3",{className:"text-xs font-semibold uppercase tracking-wider text-aurora-muted",children:t}),e(o,{className:"w-4 h-4 text-aurora-muted transition-transform "+(c?"rotate-180":"")})]}),c&&e("div",{className:"pb-3",children:n})]})}export function CatalogueFilters({categories:a,currentCategory:o,currentSort:y,onSortChange:N,storeName:v,onClose:k,variant:w="sidebar",suggestedSlugs:S=[],missionPrioritySlugs:C=[]}){const O=[...a].sort((e,r)=>{const t=S.includes(e.slug),a=S.includes(r.slug);if(t&&!a)return-1;if(!t&&a)return 1;if(C.length>0){const t=C.indexOf(e.slug),a=C.indexOf(r.slug);if(t>=0&&a<0)return-1;if(t<0&&a>=0)return 1;if(t>=0&&a>=0)return t-a}return 0}),j=r("div",{className:"space-y-0",children:[e(g,{title:"Categories",children:r("nav",{className:"space-y-1",children:[e(t,{href:"/catalogue",onClick:k,className:"flex min-h-10 items-center px-3 py-2 rounded-component text-sm font-medium transition-colors "+(o?"text-aurora-muted hover:text-aurora-text hover:bg-aurora-surface-hover border border-transparent":"bg-aurora-accent/20 text-aurora-accent border border-aurora-accent/40"),children:"All categories"}),O.map(a=>{const g=S.includes(a.slug);return r(t,{href:`/catalogue?category=${encodeURIComponent(a.slug)}`,onClick:k,className:"flex min-h-10 items-center gap-2 px-3 py-2 rounded-component text-sm font-medium transition-colors "+(o===a.slug?"bg-aurora-accent/20 text-aurora-accent border border-aurora-accent/40":"text-aurora-muted hover:text-aurora-text hover:bg-aurora-surface-hover border border-transparent"),children:[(()=>{const r=function(e){const r=e.toLowerCase();return{"baby-food":n,baby:n,beer:s,candy:c,"cat-food":l,cat:l,cereal:i,cleaning:d,dishwashing:d,tea:u,water:m,"health-care":b,healthcare:b,juices:h,juice:h,poultry:p,"skin-care":f,skincare:f,vegetables:h,fruits:h,dairy:m,bakery:i,snacks:c,beverages:u}[r]??i}(a.slug);return e(r,{className:"h-4 w-4 shrink-0 text-aurora-muted","aria-hidden":!0})})(),g?e(x,{className:"shrink-0"}):null,a.name]},a.slug)})]})}),e(g,{title:"Sort by",defaultOpen:!0,children:e("div",{className:"space-y-1",children:SORT_OPTIONS.map(r=>e("button",{type:"button",onClick:()=>{N(r.id),k?.()},className:"w-full text-left px-3 py-2 rounded-component text-sm font-medium transition-colors "+(y===r.id?"bg-aurora-accent/20 text-aurora-accent border border-aurora-accent/40":"text-aurora-muted hover:text-aurora-text hover:bg-aurora-surface-hover border border-transparent"),children:r.label},r.id))})}),v&&r("p",{className:"text-xs text-aurora-muted pt-2 border-t border-aurora-border",children:["Showing products from ",v]})]});return"drawer"===w?e("div",{className:"bg-aurora-surface border-t border-aurora-border p-6",children:j}):e("aside",{className:"hidden md:block w-full md:w-56 shrink-0 order-first",children:e("div",{className:"pattern-well sticky top-24 space-y-6 rounded-component border border-aurora-border p-4",children:j})})}
|
|
@@ -1,70 +1 @@
|
|
|
1
|
-
"
|
|
2
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
-
import { useState } from "react";
|
|
4
|
-
import { useCart } from "./CartProvider";
|
|
5
|
-
import { getApiBase, getTenantSlug } from "../lib/aurora";
|
|
6
|
-
export function CheckoutButton() {
|
|
7
|
-
const { items, total, clearCart } = useCart();
|
|
8
|
-
const [loading, setLoading] = useState(false);
|
|
9
|
-
const handleCheckout = async () => {
|
|
10
|
-
if (items.length === 0)
|
|
11
|
-
return;
|
|
12
|
-
setLoading(true);
|
|
13
|
-
const apiBase = getApiBase();
|
|
14
|
-
const tenantSlug = getTenantSlug();
|
|
15
|
-
if (!apiBase || !tenantSlug) {
|
|
16
|
-
alert("API not configured. Set NEXT_PUBLIC_AURORA_API_URL and NEXT_PUBLIC_TENANT_SLUG.");
|
|
17
|
-
setLoading(false);
|
|
18
|
-
return;
|
|
19
|
-
}
|
|
20
|
-
const origin = typeof window !== "undefined" ? window.location.origin : "";
|
|
21
|
-
const successUrl = `${origin}/checkout/success`;
|
|
22
|
-
const cancelUrl = `${origin}/cart`;
|
|
23
|
-
const lineItems = items.map((i) => ({
|
|
24
|
-
productId: i.recordId,
|
|
25
|
-
tableSlug: i.tableSlug,
|
|
26
|
-
quantity: i.quantity,
|
|
27
|
-
priceData: {
|
|
28
|
-
unitAmount: i.unitAmount,
|
|
29
|
-
currency: "GBP",
|
|
30
|
-
productData: { name: i.name },
|
|
31
|
-
},
|
|
32
|
-
}));
|
|
33
|
-
const holmes_session_id = typeof window !== "undefined" ? window.holmes?.getSessionId?.() : undefined;
|
|
34
|
-
const holmes_mission_start_timestamp = typeof window !== "undefined" ? window.holmes?.getMissionStartTimestamp?.() : undefined;
|
|
35
|
-
try {
|
|
36
|
-
const res = await fetch("/api/checkout/sessions", {
|
|
37
|
-
method: "POST",
|
|
38
|
-
headers: { "Content-Type": "application/json" },
|
|
39
|
-
body: JSON.stringify({
|
|
40
|
-
successUrl,
|
|
41
|
-
cancelUrl,
|
|
42
|
-
lineItems,
|
|
43
|
-
...(holmes_session_id && { holmes_session_id }),
|
|
44
|
-
...(holmes_mission_start_timestamp != null && {
|
|
45
|
-
holmes_mission_start_timestamp,
|
|
46
|
-
}),
|
|
47
|
-
}),
|
|
48
|
-
});
|
|
49
|
-
if (!res.ok) {
|
|
50
|
-
const err = await res.json().catch(() => ({}));
|
|
51
|
-
throw new Error(err.error ?? "Checkout failed");
|
|
52
|
-
}
|
|
53
|
-
const data = (await res.json());
|
|
54
|
-
if (data.url) {
|
|
55
|
-
clearCart();
|
|
56
|
-
window.location.href = data.url;
|
|
57
|
-
}
|
|
58
|
-
else {
|
|
59
|
-
throw new Error("No checkout URL returned");
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
catch (e) {
|
|
63
|
-
alert(e instanceof Error ? e.message : "Checkout failed");
|
|
64
|
-
}
|
|
65
|
-
finally {
|
|
66
|
-
setLoading(false);
|
|
67
|
-
}
|
|
68
|
-
};
|
|
69
|
-
return (_jsx("button", { type: "button", onClick: handleCheckout, disabled: loading || items.length === 0, className: "w-full sm:w-auto px-6 py-3 rounded-component bg-aurora-accent text-aurora-bg font-semibold hover:opacity-90 disabled:opacity-50 focus:outline-none focus:ring-2 focus:ring-aurora-primary focus:ring-offset-2 focus:ring-offset-aurora-surface transition-opacity", children: loading ? "Processing…" : "Proceed to checkout" }));
|
|
70
|
-
}
|
|
1
|
+
import{jsx as o}from"react/jsx-runtime";import{useState as t}from"react";import{useCart as e}from"./CartProvider";import{getApiBase as n,getTenantSlug as r}from"../lib/aurora";export function CheckoutButton(){const{items:i,clearCart:a}=e(),[s,c]=t(!1);return o("button",{type:"button",onClick:async()=>{if(0===i.length)return;c(!0);const o=n(),t=r();if(!o||!t)return alert("API not configured. Set NEXT_PUBLIC_AURORA_API_URL and NEXT_PUBLIC_TENANT_SLUG."),void c(!1);const e="undefined"!=typeof window?window.location.origin:"",s=`${e}/checkout/success`,u=`${e}/cart`,d=i.map(o=>({productId:o.recordId,tableSlug:o.tableSlug,quantity:o.quantity,priceData:{unitAmount:o.unitAmount,currency:"GBP",productData:{name:o.name}}})),l="undefined"!=typeof window?window.holmes?.getSessionId?.():void 0,f="undefined"!=typeof window?window.holmes?.getMissionStartTimestamp?.():void 0;try{const o=await fetch("/api/checkout/sessions",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({successUrl:s,cancelUrl:u,lineItems:d,...l&&{holmes_session_id:l},...null!=f&&{holmes_mission_start_timestamp:f}})});if(!o.ok){const t=await o.json().catch(()=>({}));throw new Error(t.error??"Checkout failed")}const t=await o.json();if(!t.url)throw new Error("No checkout URL returned");a(),window.location.href=t.url}catch(o){alert(o instanceof Error?o.message:"Checkout failed")}finally{c(!1)}},disabled:s||0===i.length,className:"w-full sm:w-auto px-6 py-3 rounded-component bg-aurora-accent text-aurora-bg font-semibold hover:opacity-90 disabled:opacity-50 focus:outline-none focus:ring-2 focus:ring-aurora-primary focus:ring-offset-2 focus:ring-offset-aurora-surface transition-opacity",children:s?"Processing…":"Proceed to checkout"})}
|
|
@@ -1,17 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
import Script from "next/script";
|
|
3
|
-
import { getHolmesScriptUrl } from "@aurora-studio/sdk";
|
|
4
|
-
/**
|
|
5
|
-
* Loads Holmes script when Aurora API and tenant are configured.
|
|
6
|
-
* Rendered as server component so script is in initial HTML.
|
|
7
|
-
* To disable Holmes: set cookie holmes_holdout=1 (the script checks this).
|
|
8
|
-
*/
|
|
9
|
-
export function ConditionalHolmesScript() {
|
|
10
|
-
const apiUrl = process.env.NEXT_PUBLIC_AURORA_API_URL;
|
|
11
|
-
const tenantSlug = process.env.NEXT_PUBLIC_TENANT_SLUG;
|
|
12
|
-
if (!apiUrl || !tenantSlug) {
|
|
13
|
-
return null;
|
|
14
|
-
}
|
|
15
|
-
const src = getHolmesScriptUrl(apiUrl, tenantSlug);
|
|
16
|
-
return _jsx(Script, { src: src, strategy: "afterInteractive" });
|
|
17
|
-
}
|
|
1
|
+
import{jsx as r}from"react/jsx-runtime";import t from"next/script";import{getHolmesScriptUrl as o}from"@aurora-studio/sdk";export function ConditionalHolmesScript(){const e=process.env.NEXT_PUBLIC_AURORA_API_URL,s=process.env.NEXT_PUBLIC_TENANT_SLUG;if(!e||!s)return null;const n=o(e,s);return r(t,{src:n,strategy:"afterInteractive"})}
|
|
@@ -4,4 +4,3 @@ interface FloatingLabelInputProps extends Omit<React.InputHTMLAttributes<HTMLInp
|
|
|
4
4
|
}
|
|
5
5
|
export declare function FloatingLabelInput({ label, error, id, className, value, ...props }: FloatingLabelInputProps): import("react/jsx-runtime").JSX.Element;
|
|
6
6
|
export {};
|
|
7
|
-
//# sourceMappingURL=FloatingLabelInput.d.ts.map
|