@xiwebdesign/xicms-core 5.4.0 → 5.5.0

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.
Files changed (67) hide show
  1. package/dist/admin/panelRegistry.js +3 -0
  2. package/dist/admin/panelRegistry.js.map +1 -1
  3. package/dist/admin/shop/CategoriesTab.d.ts +3 -0
  4. package/dist/admin/shop/CategoriesTab.js +140 -0
  5. package/dist/admin/shop/CategoriesTab.js.map +1 -0
  6. package/dist/admin/shop/ConnectionsTab.d.ts +9 -0
  7. package/dist/admin/shop/ConnectionsTab.js +158 -0
  8. package/dist/admin/shop/ConnectionsTab.js.map +1 -0
  9. package/dist/admin/shop/OrdersTab.d.ts +3 -0
  10. package/dist/admin/shop/OrdersTab.js +236 -0
  11. package/dist/admin/shop/OrdersTab.js.map +1 -0
  12. package/dist/admin/shop/ProductsTab.d.ts +3 -0
  13. package/dist/admin/shop/ProductsTab.js +397 -0
  14. package/dist/admin/shop/ProductsTab.js.map +1 -0
  15. package/dist/admin/shop/ShopPanel.d.ts +4 -0
  16. package/dist/admin/shop/ShopPanel.js +25 -0
  17. package/dist/admin/shop/ShopPanel.js.map +1 -0
  18. package/dist/admin/shop/lib.d.ts +16 -0
  19. package/dist/admin/shop/lib.js +46 -0
  20. package/dist/admin/shop/lib.js.map +1 -0
  21. package/dist/admin.css +1 -1
  22. package/dist/modules/builtins.d.ts +1 -0
  23. package/dist/modules/builtins.js +4 -0
  24. package/dist/modules/builtins.js.map +1 -1
  25. package/dist/modules/index.d.ts +4 -0
  26. package/dist/modules/index.js +12 -0
  27. package/dist/modules/index.js.map +1 -1
  28. package/dist/modules/shop/blocks/Cart.d.ts +2 -0
  29. package/dist/modules/shop/blocks/Cart.js +91 -0
  30. package/dist/modules/shop/blocks/Cart.js.map +1 -0
  31. package/dist/modules/shop/blocks/Checkout.d.ts +2 -0
  32. package/dist/modules/shop/blocks/Checkout.js +126 -0
  33. package/dist/modules/shop/blocks/Checkout.js.map +1 -0
  34. package/dist/modules/shop/blocks/Donation.d.ts +2 -0
  35. package/dist/modules/shop/blocks/Donation.js +145 -0
  36. package/dist/modules/shop/blocks/Donation.js.map +1 -0
  37. package/dist/modules/shop/blocks/OrderStatus.d.ts +2 -0
  38. package/dist/modules/shop/blocks/OrderStatus.js +40 -0
  39. package/dist/modules/shop/blocks/OrderStatus.js.map +1 -0
  40. package/dist/modules/shop/blocks/ProductDetail.d.ts +2 -0
  41. package/dist/modules/shop/blocks/ProductDetail.js +168 -0
  42. package/dist/modules/shop/blocks/ProductDetail.js.map +1 -0
  43. package/dist/modules/shop/blocks/ProductGrid.d.ts +8 -0
  44. package/dist/modules/shop/blocks/ProductGrid.js +136 -0
  45. package/dist/modules/shop/blocks/ProductGrid.js.map +1 -0
  46. package/dist/modules/shop/blocks/ShopCategories.d.ts +2 -0
  47. package/dist/modules/shop/blocks/ShopCategories.js +69 -0
  48. package/dist/modules/shop/blocks/ShopCategories.js.map +1 -0
  49. package/dist/modules/shop/cartStore.d.ts +22 -0
  50. package/dist/modules/shop/cartStore.js +85 -0
  51. package/dist/modules/shop/cartStore.js.map +1 -0
  52. package/dist/modules/shop/lib.d.ts +25 -0
  53. package/dist/modules/shop/lib.js +61 -0
  54. package/dist/modules/shop/lib.js.map +1 -0
  55. package/dist/modules/shop/module.d.ts +1 -0
  56. package/dist/modules/shop/module.js +39 -0
  57. package/dist/modules/shop/module.js.map +1 -0
  58. package/dist/modules/shop/settings.d.ts +41 -0
  59. package/dist/modules/shop/settings.js +18 -0
  60. package/dist/modules/shop/settings.js.map +1 -0
  61. package/dist/schema/index.d.ts +1 -0
  62. package/dist/schema/index.js +1 -0
  63. package/dist/schema/index.js.map +1 -1
  64. package/dist/schema/shop.d.ts +939 -0
  65. package/dist/schema/shop.js +209 -0
  66. package/dist/schema/shop.js.map +1 -0
  67. package/package.json +1 -1
@@ -0,0 +1,69 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import { useEffect, useState } from "react";
4
+ import { ShopCategoriesBlockSchema } from "../../../schema/shop";
5
+ import { defineBlock } from "../../../blocks/registry";
6
+ import { Card, Heading, Text } from "../../../blocks/ui";
7
+ import { fetchShopCategories, useShopSettings } from "../lib";
8
+ function ShopCategoriesRender({ block, siteId }) {
9
+ const settings = useShopSettings();
10
+ const [cats, setCats] = useState(null);
11
+ useEffect(() => {
12
+ void fetchShopCategories(siteId).then(setCats).catch(() => setCats([]));
13
+ }, [siteId]);
14
+ if (cats === null) return null;
15
+ if (cats.length === 0) {
16
+ return /* @__PURE__ */ jsx(Card, { padding: 4, variant: "muted", children: /* @__PURE__ */ jsx(Text, { children: "Nog geen categorie\xEBn." }) });
17
+ }
18
+ return /* @__PURE__ */ jsxs("div", { className: "xicms-shop-categories", children: [
19
+ block.heading && /* @__PURE__ */ jsx(Heading, { level: 2, "data-xicms-edit": "heading", children: block.heading }),
20
+ /* @__PURE__ */ jsx(
21
+ "div",
22
+ {
23
+ className: "xicms-shop-categories__grid",
24
+ style: {
25
+ display: "grid",
26
+ gridTemplateColumns: `repeat(auto-fill, minmax(min(260px, calc(100%/${block.columns} - 1rem)), 1fr))`,
27
+ gap: "1rem",
28
+ marginTop: block.heading ? "1rem" : 0
29
+ },
30
+ children: cats.map((c) => /* @__PURE__ */ jsx(
31
+ "a",
32
+ {
33
+ className: "xicms-shop-categories__item",
34
+ href: `${settings.shopPath}?cat=${encodeURIComponent(c.slug)}`,
35
+ style: { textDecoration: "none", color: "inherit" },
36
+ children: /* @__PURE__ */ jsxs(Card, { padding: 0, children: [
37
+ c.imageUrl && /* @__PURE__ */ jsx(
38
+ "img",
39
+ {
40
+ src: c.imageUrl,
41
+ alt: c.name,
42
+ loading: "lazy",
43
+ style: { width: "100%", aspectRatio: "4/3", objectFit: "cover", display: "block", borderRadius: "inherit" }
44
+ }
45
+ ),
46
+ /* @__PURE__ */ jsxs("div", { style: { padding: "0.9rem 1rem" }, children: [
47
+ /* @__PURE__ */ jsx(Text, { weight: "bold", children: c.name }),
48
+ c.description && /* @__PURE__ */ jsx(Text, { size: "sm", style: { opacity: 0.75, marginTop: 4 }, children: c.description })
49
+ ] })
50
+ ] })
51
+ },
52
+ c.id
53
+ ))
54
+ }
55
+ )
56
+ ] });
57
+ }
58
+ const shopCategoriesBlock = defineBlock({
59
+ type: "shopCategories",
60
+ schema: ShopCategoriesBlockSchema,
61
+ Render: ShopCategoriesRender,
62
+ label: "Winkel-categorie\xEBn",
63
+ icon: "\u{1F5C2}\uFE0F",
64
+ defaults: () => ({ columns: 3 })
65
+ });
66
+ export {
67
+ shopCategoriesBlock
68
+ };
69
+ //# sourceMappingURL=ShopCategories.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../src/modules/shop/blocks/ShopCategories.tsx"],"sourcesContent":["\"use client\";\n\nimport { useEffect, useState } from \"react\";\nimport type { z } from \"zod\";\nimport { ShopCategoriesBlockSchema } from \"../../../schema/shop\";\nimport { defineBlock, type BlockRenderProps } from \"../../../blocks/registry\";\nimport { Card, Heading, Text } from \"../../../blocks/ui\";\nimport { fetchShopCategories, useShopSettings, type StoredShopCategory } from \"../lib\";\n\ntype Blk = z.infer<typeof ShopCategoriesBlockSchema>;\n\n/** Categorie-tegels — linkt naar de winkelpagina met `?cat=<slug>` (productGrid filtert daarop). */\nfunction ShopCategoriesRender({ block, siteId }: BlockRenderProps<Blk>) {\n const settings = useShopSettings();\n const [cats, setCats] = useState<StoredShopCategory[] | null>(null);\n\n useEffect(() => {\n void fetchShopCategories(siteId).then(setCats).catch(() => setCats([]));\n }, [siteId]);\n\n if (cats === null) return null;\n if (cats.length === 0) {\n return (\n <Card padding={4} variant=\"muted\">\n <Text>Nog geen categorieën.</Text>\n </Card>\n );\n }\n\n return (\n <div className=\"xicms-shop-categories\">\n {block.heading && (\n <Heading level={2} data-xicms-edit=\"heading\">{block.heading}</Heading>\n )}\n <div\n className=\"xicms-shop-categories__grid\"\n style={{\n display: \"grid\",\n gridTemplateColumns: `repeat(auto-fill, minmax(min(260px, calc(100%/${block.columns} - 1rem)), 1fr))`,\n gap: \"1rem\",\n marginTop: block.heading ? \"1rem\" : 0,\n }}\n >\n {cats.map((c) => (\n <a\n key={c.id}\n className=\"xicms-shop-categories__item\"\n href={`${settings.shopPath}?cat=${encodeURIComponent(c.slug)}`}\n style={{ textDecoration: \"none\", color: \"inherit\" }}\n >\n <Card padding={0}>\n {c.imageUrl && (\n <img\n src={c.imageUrl}\n alt={c.name}\n loading=\"lazy\"\n style={{ width: \"100%\", aspectRatio: \"4/3\", objectFit: \"cover\", display: \"block\", borderRadius: \"inherit\" }}\n />\n )}\n <div style={{ padding: \"0.9rem 1rem\" }}>\n <Text weight=\"bold\">{c.name}</Text>\n {c.description && (\n <Text size=\"sm\" style={{ opacity: 0.75, marginTop: 4 }}>{c.description}</Text>\n )}\n </div>\n </Card>\n </a>\n ))}\n </div>\n </div>\n );\n}\n\nexport const shopCategoriesBlock = defineBlock({\n type: \"shopCategories\",\n schema: ShopCategoriesBlockSchema,\n Render: ShopCategoriesRender,\n label: \"Winkel-categorieën\",\n icon: \"🗂️\",\n defaults: () => ({ columns: 3 }),\n});\n"],"mappings":";AAwBQ,cAmCM,YAnCN;AAtBR,SAAS,WAAW,gBAAgB;AAEpC,SAAS,iCAAiC;AAC1C,SAAS,mBAA0C;AACnD,SAAS,MAAM,SAAS,YAAY;AACpC,SAAS,qBAAqB,uBAAgD;AAK9E,SAAS,qBAAqB,EAAE,OAAO,OAAO,GAA0B;AACtE,QAAM,WAAW,gBAAgB;AACjC,QAAM,CAAC,MAAM,OAAO,IAAI,SAAsC,IAAI;AAElE,YAAU,MAAM;AACd,SAAK,oBAAoB,MAAM,EAAE,KAAK,OAAO,EAAE,MAAM,MAAM,QAAQ,CAAC,CAAC,CAAC;AAAA,EACxE,GAAG,CAAC,MAAM,CAAC;AAEX,MAAI,SAAS,KAAM,QAAO;AAC1B,MAAI,KAAK,WAAW,GAAG;AACrB,WACE,oBAAC,QAAK,SAAS,GAAG,SAAQ,SACxB,8BAAC,QAAK,sCAAqB,GAC7B;AAAA,EAEJ;AAEA,SACE,qBAAC,SAAI,WAAU,yBACZ;AAAA,UAAM,WACL,oBAAC,WAAQ,OAAO,GAAG,mBAAgB,WAAW,gBAAM,SAAQ;AAAA,IAE9D;AAAA,MAAC;AAAA;AAAA,QACC,WAAU;AAAA,QACV,OAAO;AAAA,UACL,SAAS;AAAA,UACT,qBAAqB,iDAAiD,MAAM,OAAO;AAAA,UACnF,KAAK;AAAA,UACL,WAAW,MAAM,UAAU,SAAS;AAAA,QACtC;AAAA,QAEC,eAAK,IAAI,CAAC,MACT;AAAA,UAAC;AAAA;AAAA,YAEC,WAAU;AAAA,YACV,MAAM,GAAG,SAAS,QAAQ,QAAQ,mBAAmB,EAAE,IAAI,CAAC;AAAA,YAC5D,OAAO,EAAE,gBAAgB,QAAQ,OAAO,UAAU;AAAA,YAElD,+BAAC,QAAK,SAAS,GACZ;AAAA,gBAAE,YACD;AAAA,gBAAC;AAAA;AAAA,kBACC,KAAK,EAAE;AAAA,kBACP,KAAK,EAAE;AAAA,kBACP,SAAQ;AAAA,kBACR,OAAO,EAAE,OAAO,QAAQ,aAAa,OAAO,WAAW,SAAS,SAAS,SAAS,cAAc,UAAU;AAAA;AAAA,cAC5G;AAAA,cAEF,qBAAC,SAAI,OAAO,EAAE,SAAS,cAAc,GACnC;AAAA,oCAAC,QAAK,QAAO,QAAQ,YAAE,MAAK;AAAA,gBAC3B,EAAE,eACD,oBAAC,QAAK,MAAK,MAAK,OAAO,EAAE,SAAS,MAAM,WAAW,EAAE,GAAI,YAAE,aAAY;AAAA,iBAE3E;AAAA,eACF;AAAA;AAAA,UApBK,EAAE;AAAA,QAqBT,CACD;AAAA;AAAA,IACH;AAAA,KACF;AAEJ;AAEO,MAAM,sBAAsB,YAAY;AAAA,EAC7C,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,MAAM;AAAA,EACN,UAAU,OAAO,EAAE,SAAS,EAAE;AAChC,CAAC;","names":[]}
@@ -0,0 +1,22 @@
1
+ export interface CartLine {
2
+ productId: string;
3
+ slug: string;
4
+ name: string;
5
+ /** Variant-key ("68|Roze") + leesbaar label ("Maat 68, Roze"). */
6
+ variantKey?: string;
7
+ variantLabel?: string;
8
+ unitPriceCents: number;
9
+ qty: number;
10
+ imageUrl?: string;
11
+ }
12
+ export declare function cartAdd(siteId: string, line: Omit<CartLine, "qty">, qty?: number): void;
13
+ export declare function cartSetQty(siteId: string, productId: string, variantKey: string | undefined, qty: number): void;
14
+ export declare function cartRemove(siteId: string, productId: string, variantKey: string | undefined): void;
15
+ export declare function cartClear(siteId: string): void;
16
+ export interface CartState {
17
+ lines: CartLine[];
18
+ count: number;
19
+ itemsCents: number;
20
+ }
21
+ /** Reactieve cart-state voor blocks/klantcomponenten (bv. een cart-badge in de header). */
22
+ export declare function useCart(siteId: string): CartState;
@@ -0,0 +1,85 @@
1
+ "use client";
2
+ import { useSyncExternalStore } from "react";
3
+ const listeners = /* @__PURE__ */ new Set();
4
+ const cache = /* @__PURE__ */ new Map();
5
+ function storageKey(siteId) {
6
+ return `xicms:cart:${siteId}`;
7
+ }
8
+ function readLines(siteId) {
9
+ if (typeof window === "undefined") return [];
10
+ let raw = "";
11
+ try {
12
+ raw = window.localStorage.getItem(storageKey(siteId)) ?? "[]";
13
+ } catch {
14
+ raw = "[]";
15
+ }
16
+ const cached = cache.get(siteId);
17
+ if (cached && cached.raw === raw) return cached.lines;
18
+ let lines = [];
19
+ try {
20
+ const parsed = JSON.parse(raw);
21
+ if (Array.isArray(parsed)) lines = parsed.filter((l) => l && typeof l === "object" && typeof l.productId === "string");
22
+ } catch {
23
+ lines = [];
24
+ }
25
+ cache.set(siteId, { raw, lines });
26
+ return lines;
27
+ }
28
+ function writeLines(siteId, lines) {
29
+ try {
30
+ window.localStorage.setItem(storageKey(siteId), JSON.stringify(lines));
31
+ } catch {
32
+ cache.set(siteId, { raw: `mem:${Date.now()}`, lines });
33
+ }
34
+ emit();
35
+ }
36
+ function emit() {
37
+ for (const l of listeners) l();
38
+ }
39
+ function subscribe(cb) {
40
+ listeners.add(cb);
41
+ const onStorage = (e) => {
42
+ if (e.key?.startsWith("xicms:cart:")) cb();
43
+ };
44
+ window.addEventListener("storage", onStorage);
45
+ return () => {
46
+ listeners.delete(cb);
47
+ window.removeEventListener("storage", onStorage);
48
+ };
49
+ }
50
+ function lineId(l) {
51
+ return `${l.productId}::${l.variantKey ?? ""}`;
52
+ }
53
+ function cartAdd(siteId, line, qty = 1) {
54
+ const lines = [...readLines(siteId)];
55
+ const idx = lines.findIndex((l) => lineId(l) === lineId(line));
56
+ if (idx >= 0) lines[idx] = { ...lines[idx], qty: Math.min(999, lines[idx].qty + qty) };
57
+ else lines.push({ ...line, qty: Math.min(999, Math.max(1, qty)) });
58
+ writeLines(siteId, lines);
59
+ }
60
+ function cartSetQty(siteId, productId, variantKey, qty) {
61
+ const lines = readLines(siteId).map((l) => lineId(l) === lineId({ productId, variantKey }) ? { ...l, qty: Math.min(999, qty) } : l).filter((l) => l.qty > 0);
62
+ writeLines(siteId, lines);
63
+ }
64
+ function cartRemove(siteId, productId, variantKey) {
65
+ writeLines(siteId, readLines(siteId).filter((l) => lineId(l) !== lineId({ productId, variantKey })));
66
+ }
67
+ function cartClear(siteId) {
68
+ writeLines(siteId, []);
69
+ }
70
+ function useCart(siteId) {
71
+ const lines = useSyncExternalStore(subscribe, () => readLines(siteId), () => []);
72
+ return {
73
+ lines,
74
+ count: lines.reduce((n, l) => n + l.qty, 0),
75
+ itemsCents: lines.reduce((n, l) => n + l.unitPriceCents * l.qty, 0)
76
+ };
77
+ }
78
+ export {
79
+ cartAdd,
80
+ cartClear,
81
+ cartRemove,
82
+ cartSetQty,
83
+ useCart
84
+ };
85
+ //# sourceMappingURL=cartStore.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/modules/shop/cartStore.ts"],"sourcesContent":["\"use client\";\n\n/**\n * Winkelwagen-store — puur client-side (localStorage), geen Firestore-writes vóór checkout.\n * Extern store + `useSyncExternalStore` zodat élk shop-block (grid, cart-badge, checkout) dezelfde\n * state ziet zonder dat er een provider gemount hoeft te worden. Cross-tab sync via `storage`.\n *\n * Prijzen in de cart zijn indicatief voor weergave; bij `createOrder` herrekent de server alles\n * tegen de opgeslagen product-docs (client wordt nooit vertrouwd).\n */\nimport { useSyncExternalStore } from \"react\";\n\nexport interface CartLine {\n productId: string;\n slug: string;\n name: string;\n /** Variant-key (\"68|Roze\") + leesbaar label (\"Maat 68, Roze\"). */\n variantKey?: string;\n variantLabel?: string;\n unitPriceCents: number;\n qty: number;\n imageUrl?: string;\n}\n\nconst listeners = new Set<() => void>();\n/** Cache per siteId zodat getSnapshot een stabiele referentie teruggeeft (useSyncExternalStore-eis). */\nconst cache = new Map<string, { raw: string; lines: CartLine[] }>();\n\nfunction storageKey(siteId: string): string {\n return `xicms:cart:${siteId}`;\n}\n\nfunction readLines(siteId: string): CartLine[] {\n if (typeof window === \"undefined\") return [];\n let raw = \"\";\n try {\n raw = window.localStorage.getItem(storageKey(siteId)) ?? \"[]\";\n } catch {\n raw = \"[]\";\n }\n const cached = cache.get(siteId);\n if (cached && cached.raw === raw) return cached.lines;\n let lines: CartLine[] = [];\n try {\n const parsed = JSON.parse(raw);\n if (Array.isArray(parsed)) lines = parsed.filter((l) => l && typeof l === \"object\" && typeof l.productId === \"string\");\n } catch {\n lines = [];\n }\n cache.set(siteId, { raw, lines });\n return lines;\n}\n\nfunction writeLines(siteId: string, lines: CartLine[]): void {\n try {\n window.localStorage.setItem(storageKey(siteId), JSON.stringify(lines));\n } catch {\n // localStorage vol/geblokkeerd: cart werkt dan alleen in-memory via de cache.\n cache.set(siteId, { raw: `mem:${Date.now()}`, lines });\n }\n emit();\n}\n\nfunction emit(): void {\n for (const l of listeners) l();\n}\n\nfunction subscribe(cb: () => void): () => void {\n listeners.add(cb);\n // Cross-tab: een andere tab die de cart wijzigt triggert hier een re-render.\n const onStorage = (e: StorageEvent) => {\n if (e.key?.startsWith(\"xicms:cart:\")) cb();\n };\n window.addEventListener(\"storage\", onStorage);\n return () => {\n listeners.delete(cb);\n window.removeEventListener(\"storage\", onStorage);\n };\n}\n\nfunction lineId(l: Pick<CartLine, \"productId\" | \"variantKey\">): string {\n return `${l.productId}::${l.variantKey ?? \"\"}`;\n}\n\nexport function cartAdd(siteId: string, line: Omit<CartLine, \"qty\">, qty = 1): void {\n const lines = [...readLines(siteId)];\n const idx = lines.findIndex((l) => lineId(l) === lineId(line));\n if (idx >= 0) lines[idx] = { ...lines[idx], qty: Math.min(999, lines[idx].qty + qty) };\n else lines.push({ ...line, qty: Math.min(999, Math.max(1, qty)) });\n writeLines(siteId, lines);\n}\n\nexport function cartSetQty(siteId: string, productId: string, variantKey: string | undefined, qty: number): void {\n const lines = readLines(siteId)\n .map((l) => (lineId(l) === lineId({ productId, variantKey }) ? { ...l, qty: Math.min(999, qty) } : l))\n .filter((l) => l.qty > 0);\n writeLines(siteId, lines);\n}\n\nexport function cartRemove(siteId: string, productId: string, variantKey: string | undefined): void {\n writeLines(siteId, readLines(siteId).filter((l) => lineId(l) !== lineId({ productId, variantKey })));\n}\n\nexport function cartClear(siteId: string): void {\n writeLines(siteId, []);\n}\n\nexport interface CartState {\n lines: CartLine[];\n count: number;\n itemsCents: number;\n}\n\n/** Reactieve cart-state voor blocks/klantcomponenten (bv. een cart-badge in de header). */\nexport function useCart(siteId: string): CartState {\n const lines = useSyncExternalStore(subscribe, () => readLines(siteId), () => [] as CartLine[]);\n return {\n lines,\n count: lines.reduce((n, l) => n + l.qty, 0),\n itemsCents: lines.reduce((n, l) => n + l.unitPriceCents * l.qty, 0),\n };\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,25 @@
1
+ import type { CreateOrderInput, Product, ShopCategory } from "../../schema/shop";
2
+ import { type ShopSettings } from "./settings";
3
+ /** Publieke shop-instellingen met defaults (paden, verzendkosten, valuta). */
4
+ export declare function useShopSettings(): ShopSettings;
5
+ export type StoredProduct = Product & {
6
+ id: string;
7
+ };
8
+ export type StoredShopCategory = ShopCategory & {
9
+ id: string;
10
+ };
11
+ export declare function fetchActiveProducts(siteId: string): Promise<StoredProduct[]>;
12
+ export declare function fetchProductBySlug(siteId: string, slug: string): Promise<StoredProduct | null>;
13
+ export declare function fetchShopCategories(siteId: string): Promise<StoredShopCategory[]>;
14
+ /** Start de checkout: order + Stripe-sessie server-side; geeft de redirect-URL terug. */
15
+ export declare function createOrderCall(input: CreateOrderInput): Promise<{
16
+ url: string;
17
+ orderNumber: string;
18
+ }>;
19
+ /** Leesbaar variantlabel: opties ["Maat","Kleur"] + key "68|Roze" → "Maat 68 · Roze". */
20
+ export declare function variantLabelFor(options: {
21
+ name: string;
22
+ values: string[];
23
+ }[], key: string): string;
24
+ /** Querystring-parameter client-side lezen (blocks zijn client components). */
25
+ export declare function readQueryParam(name: string): string | null;
@@ -0,0 +1,61 @@
1
+ "use client";
2
+ import { collection, getDocs, limit as fsLimit, query, where } from "firebase/firestore";
3
+ import { getFunctions, httpsCallable } from "firebase/functions";
4
+ import { getDb, getFirebaseApp } from "../../firebase/client";
5
+ import { functionsRegion } from "../../firebase/region";
6
+ import { useModuleSettings } from "../context";
7
+ import { defaultShopSettings } from "./settings";
8
+ function useShopSettings() {
9
+ const raw = useModuleSettings("shop");
10
+ return { ...defaultShopSettings, ...raw };
11
+ }
12
+ async function fetchActiveProducts(siteId) {
13
+ const db = getDb();
14
+ if (!db) return [];
15
+ const snap = await getDocs(
16
+ query(collection(db, "sites", siteId, "products"), where("status", "==", "active"), fsLimit(500))
17
+ );
18
+ return snap.docs.map((d) => ({ id: d.id, ...d.data() }));
19
+ }
20
+ async function fetchProductBySlug(siteId, slug) {
21
+ const db = getDb();
22
+ if (!db || !slug) return null;
23
+ const snap = await getDocs(
24
+ query(collection(db, "sites", siteId, "products"), where("slug", "==", slug), fsLimit(1))
25
+ );
26
+ const doc = snap.docs[0];
27
+ if (!doc) return null;
28
+ const product = { id: doc.id, ...doc.data() };
29
+ return product.status === "active" ? product : null;
30
+ }
31
+ async function fetchShopCategories(siteId) {
32
+ const db = getDb();
33
+ if (!db) return [];
34
+ const snap = await getDocs(query(collection(db, "sites", siteId, "shopCategories"), fsLimit(100)));
35
+ return snap.docs.map((d) => ({ id: d.id, ...d.data() })).filter((c) => c.visible !== false).sort((a, b) => (a.order ?? 0) - (b.order ?? 0));
36
+ }
37
+ async function createOrderCall(input) {
38
+ const app = getFirebaseApp();
39
+ if (!app) throw new Error("Webwinkel niet beschikbaar.");
40
+ const fn = httpsCallable(getFunctions(app, functionsRegion()), "createOrder");
41
+ const res = await fn(input);
42
+ return res.data;
43
+ }
44
+ function variantLabelFor(options, key) {
45
+ const parts = key.split("|");
46
+ return options.map((o, i) => `${o.name} ${parts[i] ?? "?"}`).join(" \xB7 ");
47
+ }
48
+ function readQueryParam(name) {
49
+ if (typeof window === "undefined") return null;
50
+ return new URLSearchParams(window.location.search).get(name);
51
+ }
52
+ export {
53
+ createOrderCall,
54
+ fetchActiveProducts,
55
+ fetchProductBySlug,
56
+ fetchShopCategories,
57
+ readQueryParam,
58
+ useShopSettings,
59
+ variantLabelFor
60
+ };
61
+ //# sourceMappingURL=lib.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/modules/shop/lib.ts"],"sourcesContent":["\"use client\";\n\n/**\n * Client-helpers voor de shop-blocks: producten/categorieën lezen (publieke rules) en de\n * `createOrder`-callable aanroepen. Queries bewust simpel gehouden (één `where` op status,\n * filteren/sorteren client-side) zodat er géén composite-indexes nodig zijn — assortimenten\n * van XiCMS-sites zijn klein (tientallen tot honderden producten).\n */\nimport { collection, getDocs, limit as fsLimit, query, where } from \"firebase/firestore\";\nimport { getFunctions, httpsCallable } from \"firebase/functions\";\nimport { getDb, getFirebaseApp } from \"../../firebase/client\";\nimport { functionsRegion } from \"../../firebase/region\";\nimport type { CreateOrderInput, Product, ShopCategory } from \"../../schema/shop\";\nimport { useModuleSettings } from \"../context\";\nimport { defaultShopSettings, type ShopSettings } from \"./settings\";\n\n/** Publieke shop-instellingen met defaults (paden, verzendkosten, valuta). */\nexport function useShopSettings(): ShopSettings {\n const raw = useModuleSettings<Partial<ShopSettings>>(\"shop\");\n return { ...defaultShopSettings, ...raw };\n}\n\nexport type StoredProduct = Product & { id: string };\nexport type StoredShopCategory = ShopCategory & { id: string };\n\nexport async function fetchActiveProducts(siteId: string): Promise<StoredProduct[]> {\n const db = getDb();\n if (!db) return [];\n const snap = await getDocs(\n query(collection(db, \"sites\", siteId, \"products\"), where(\"status\", \"==\", \"active\"), fsLimit(500)),\n );\n return snap.docs.map((d) => ({ id: d.id, ...(d.data() as Product) }));\n}\n\nexport async function fetchProductBySlug(siteId: string, slug: string): Promise<StoredProduct | null> {\n const db = getDb();\n if (!db || !slug) return null;\n const snap = await getDocs(\n query(collection(db, \"sites\", siteId, \"products\"), where(\"slug\", \"==\", slug), fsLimit(1)),\n );\n const doc = snap.docs[0];\n if (!doc) return null;\n const product = { id: doc.id, ...(doc.data() as Product) };\n return product.status === \"active\" ? product : null;\n}\n\nexport async function fetchShopCategories(siteId: string): Promise<StoredShopCategory[]> {\n const db = getDb();\n if (!db) return [];\n const snap = await getDocs(query(collection(db, \"sites\", siteId, \"shopCategories\"), fsLimit(100)));\n return snap.docs\n .map((d) => ({ id: d.id, ...(d.data() as ShopCategory) }))\n .filter((c) => c.visible !== false)\n .sort((a, b) => (a.order ?? 0) - (b.order ?? 0));\n}\n\n/** Start de checkout: order + Stripe-sessie server-side; geeft de redirect-URL terug. */\nexport async function createOrderCall(input: CreateOrderInput): Promise<{ url: string; orderNumber: string }> {\n const app = getFirebaseApp();\n if (!app) throw new Error(\"Webwinkel niet beschikbaar.\");\n const fn = httpsCallable(getFunctions(app, functionsRegion()), \"createOrder\");\n const res = await fn(input);\n return res.data as { url: string; orderNumber: string };\n}\n\n/** Leesbaar variantlabel: opties [\"Maat\",\"Kleur\"] + key \"68|Roze\" → \"Maat 68 · Roze\". */\nexport function variantLabelFor(options: { name: string; values: string[] }[], key: string): string {\n const parts = key.split(\"|\");\n return options.map((o, i) => `${o.name} ${parts[i] ?? \"?\"}`).join(\" · \");\n}\n\n/** Querystring-parameter client-side lezen (blocks zijn client components). */\nexport function readQueryParam(name: string): string | null {\n if (typeof window === \"undefined\") return null;\n return new URLSearchParams(window.location.search).get(name);\n}\n"],"mappings":";AAQA,SAAS,YAAY,SAAS,SAAS,SAAS,OAAO,aAAa;AACpE,SAAS,cAAc,qBAAqB;AAC5C,SAAS,OAAO,sBAAsB;AACtC,SAAS,uBAAuB;AAEhC,SAAS,yBAAyB;AAClC,SAAS,2BAA8C;AAGhD,SAAS,kBAAgC;AAC9C,QAAM,MAAM,kBAAyC,MAAM;AAC3D,SAAO,EAAE,GAAG,qBAAqB,GAAG,IAAI;AAC1C;AAKA,eAAsB,oBAAoB,QAA0C;AAClF,QAAM,KAAK,MAAM;AACjB,MAAI,CAAC,GAAI,QAAO,CAAC;AACjB,QAAM,OAAO,MAAM;AAAA,IACjB,MAAM,WAAW,IAAI,SAAS,QAAQ,UAAU,GAAG,MAAM,UAAU,MAAM,QAAQ,GAAG,QAAQ,GAAG,CAAC;AAAA,EAClG;AACA,SAAO,KAAK,KAAK,IAAI,CAAC,OAAO,EAAE,IAAI,EAAE,IAAI,GAAI,EAAE,KAAK,EAAc,EAAE;AACtE;AAEA,eAAsB,mBAAmB,QAAgB,MAA6C;AACpG,QAAM,KAAK,MAAM;AACjB,MAAI,CAAC,MAAM,CAAC,KAAM,QAAO;AACzB,QAAM,OAAO,MAAM;AAAA,IACjB,MAAM,WAAW,IAAI,SAAS,QAAQ,UAAU,GAAG,MAAM,QAAQ,MAAM,IAAI,GAAG,QAAQ,CAAC,CAAC;AAAA,EAC1F;AACA,QAAM,MAAM,KAAK,KAAK,CAAC;AACvB,MAAI,CAAC,IAAK,QAAO;AACjB,QAAM,UAAU,EAAE,IAAI,IAAI,IAAI,GAAI,IAAI,KAAK,EAAc;AACzD,SAAO,QAAQ,WAAW,WAAW,UAAU;AACjD;AAEA,eAAsB,oBAAoB,QAA+C;AACvF,QAAM,KAAK,MAAM;AACjB,MAAI,CAAC,GAAI,QAAO,CAAC;AACjB,QAAM,OAAO,MAAM,QAAQ,MAAM,WAAW,IAAI,SAAS,QAAQ,gBAAgB,GAAG,QAAQ,GAAG,CAAC,CAAC;AACjG,SAAO,KAAK,KACT,IAAI,CAAC,OAAO,EAAE,IAAI,EAAE,IAAI,GAAI,EAAE,KAAK,EAAmB,EAAE,EACxD,OAAO,CAAC,MAAM,EAAE,YAAY,KAAK,EACjC,KAAK,CAAC,GAAG,OAAO,EAAE,SAAS,MAAM,EAAE,SAAS,EAAE;AACnD;AAGA,eAAsB,gBAAgB,OAAwE;AAC5G,QAAM,MAAM,eAAe;AAC3B,MAAI,CAAC,IAAK,OAAM,IAAI,MAAM,6BAA6B;AACvD,QAAM,KAAK,cAAc,aAAa,KAAK,gBAAgB,CAAC,GAAG,aAAa;AAC5E,QAAM,MAAM,MAAM,GAAG,KAAK;AAC1B,SAAO,IAAI;AACb;AAGO,SAAS,gBAAgB,SAA+C,KAAqB;AAClG,QAAM,QAAQ,IAAI,MAAM,GAAG;AAC3B,SAAO,QAAQ,IAAI,CAAC,GAAG,MAAM,GAAG,EAAE,IAAI,IAAI,MAAM,CAAC,KAAK,GAAG,EAAE,EAAE,KAAK,QAAK;AACzE;AAGO,SAAS,eAAe,MAA6B;AAC1D,MAAI,OAAO,WAAW,YAAa,QAAO;AAC1C,SAAO,IAAI,gBAAgB,OAAO,SAAS,MAAM,EAAE,IAAI,IAAI;AAC7D;","names":[]}
@@ -0,0 +1 @@
1
+ export declare const shopModule: import("..").ModuleDefinition;
@@ -0,0 +1,39 @@
1
+ import { defineModule } from "../defineModule";
2
+ import { ShopSettingsSchema, defaultShopSettings } from "./settings";
3
+ import { shopCategoriesBlock } from "./blocks/ShopCategories";
4
+ import { productGridBlock } from "./blocks/ProductGrid";
5
+ import { productDetailBlock } from "./blocks/ProductDetail";
6
+ import { cartBlock } from "./blocks/Cart";
7
+ import { checkoutBlock } from "./blocks/Checkout";
8
+ import { donationBlock } from "./blocks/Donation";
9
+ import { orderStatusBlock } from "./blocks/OrderStatus";
10
+ const shopModule = defineModule({
11
+ id: "shop",
12
+ version: "1.0.0",
13
+ label: "Webwinkel",
14
+ description: "Webwinkel + donaties: producten met varianten, winkelwagen, afrekenen via Stripe (eigen account van de klant) en bestelbevestiging per e-mail via de eigen SMTP-mailbox.",
15
+ defaultEnabled: false,
16
+ blocks: [
17
+ shopCategoriesBlock,
18
+ productGridBlock,
19
+ productDetailBlock,
20
+ cartBlock,
21
+ checkoutBlock,
22
+ donationBlock,
23
+ orderStatusBlock
24
+ ],
25
+ settingsSchema: ShopSettingsSchema,
26
+ defaultSettings: defaultShopSettings,
27
+ requires: [
28
+ { kind: "function", name: "createOrder" },
29
+ { kind: "function", name: "shopStripeWebhook" },
30
+ { kind: "function", name: "saveShopSecrets" },
31
+ { kind: "firestore", collection: "products" },
32
+ { kind: "firestore", collection: "shopCategories" },
33
+ { kind: "firestore", collection: "orders" }
34
+ ]
35
+ });
36
+ export {
37
+ shopModule
38
+ };
39
+ //# sourceMappingURL=module.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/modules/shop/module.ts"],"sourcesContent":["/**\n * Shop-module (5.5) — webwinkel + donaties op het eigen Stripe-account van de klant.\n *\n * - Producten/categorieën: `sites/{siteId}/products` + `shopCategories` (editor-write, rules).\n * - Checkout: `createOrder`-callable herrekent bedragen server-side en start een hosted\n * Stripe Checkout-sessie (iDEAL/kaart); de `shopStripeWebhook` zet de order op betaald en\n * verstuurt de bevestigingsmail (eigen SMTP van de klant, bv. Gmail — zie functions/lib/mail).\n * - Donaties: het donatie-block gebruikt dezelfde order-flow (kind:\"donation\", geen verzending).\n * - Beheer: het \"Webwinkel\"-paneel (producten/categorieën/bestellingen/koppelingen) hangt via\n * de paneel-registry aan deze module-id.\n *\n * Server-safe: geen \"use client\" hier — de blocks zelf zijn client components.\n */\nimport { defineModule } from \"../defineModule\";\nimport { ShopSettingsSchema, defaultShopSettings } from \"./settings\";\nimport { shopCategoriesBlock } from \"./blocks/ShopCategories\";\nimport { productGridBlock } from \"./blocks/ProductGrid\";\nimport { productDetailBlock } from \"./blocks/ProductDetail\";\nimport { cartBlock } from \"./blocks/Cart\";\nimport { checkoutBlock } from \"./blocks/Checkout\";\nimport { donationBlock } from \"./blocks/Donation\";\nimport { orderStatusBlock } from \"./blocks/OrderStatus\";\n\nexport const shopModule = defineModule({\n id: \"shop\",\n version: \"1.0.0\",\n label: \"Webwinkel\",\n description:\n \"Webwinkel + donaties: producten met varianten, winkelwagen, afrekenen via Stripe (eigen account van de klant) en bestelbevestiging per e-mail via de eigen SMTP-mailbox.\",\n defaultEnabled: false,\n blocks: [\n shopCategoriesBlock,\n productGridBlock,\n productDetailBlock,\n cartBlock,\n checkoutBlock,\n donationBlock,\n orderStatusBlock,\n ],\n settingsSchema: ShopSettingsSchema,\n defaultSettings: defaultShopSettings,\n requires: [\n { kind: \"function\", name: \"createOrder\" },\n { kind: \"function\", name: \"shopStripeWebhook\" },\n { kind: \"function\", name: \"saveShopSecrets\" },\n { kind: \"firestore\", collection: \"products\" },\n { kind: \"firestore\", collection: \"shopCategories\" },\n { kind: \"firestore\", collection: \"orders\" },\n ],\n});\n"],"mappings":"AAaA,SAAS,oBAAoB;AAC7B,SAAS,oBAAoB,2BAA2B;AACxD,SAAS,2BAA2B;AACpC,SAAS,wBAAwB;AACjC,SAAS,0BAA0B;AACnC,SAAS,iBAAiB;AAC1B,SAAS,qBAAqB;AAC9B,SAAS,qBAAqB;AAC9B,SAAS,wBAAwB;AAE1B,MAAM,aAAa,aAAa;AAAA,EACrC,IAAI;AAAA,EACJ,SAAS;AAAA,EACT,OAAO;AAAA,EACP,aACE;AAAA,EACF,gBAAgB;AAAA,EAChB,QAAQ;AAAA,IACN;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,gBAAgB;AAAA,EAChB,iBAAiB;AAAA,EACjB,UAAU;AAAA,IACR,EAAE,MAAM,YAAY,MAAM,cAAc;AAAA,IACxC,EAAE,MAAM,YAAY,MAAM,oBAAoB;AAAA,IAC9C,EAAE,MAAM,YAAY,MAAM,kBAAkB;AAAA,IAC5C,EAAE,MAAM,aAAa,YAAY,WAAW;AAAA,IAC5C,EAAE,MAAM,aAAa,YAAY,iBAAiB;AAAA,IAClD,EAAE,MAAM,aAAa,YAAY,SAAS;AAAA,EAC5C;AACF,CAAC;","names":[]}
@@ -0,0 +1,41 @@
1
+ import { z } from "zod";
2
+ /**
3
+ * Publieke shop-instellingen (per site, via `settings/public.modules.shop.config` of
4
+ * `SiteConfig.modules.shop.config`). Server-safe: alleen Zod. De SettingsPanel rendert hier
5
+ * automatisch een formulier voor (moduleSettingsForm); de Functions lezen dezelfde config
6
+ * server-side. Geheimen (Stripe-key, SMTP) horen hier NIET — die staan in
7
+ * `sites/{siteId}/private/shop` en lopen via callables.
8
+ */
9
+ export declare const ShopSettingsSchema: z.ZodObject<{
10
+ shopPath: z.ZodDefault<z.ZodString>;
11
+ detailPath: z.ZodDefault<z.ZodString>;
12
+ cartPath: z.ZodDefault<z.ZodString>;
13
+ checkoutPath: z.ZodDefault<z.ZodString>;
14
+ successPath: z.ZodDefault<z.ZodString>;
15
+ currency: z.ZodDefault<z.ZodString>;
16
+ shippingFlatCents: z.ZodDefault<z.ZodNumber>;
17
+ shippingFreeFromCents: z.ZodDefault<z.ZodNumber>;
18
+ orderPrefix: z.ZodDefault<z.ZodString>;
19
+ }, "strip", z.ZodTypeAny, {
20
+ currency: string;
21
+ shopPath: string;
22
+ detailPath: string;
23
+ cartPath: string;
24
+ checkoutPath: string;
25
+ successPath: string;
26
+ shippingFlatCents: number;
27
+ shippingFreeFromCents: number;
28
+ orderPrefix: string;
29
+ }, {
30
+ currency?: string | undefined;
31
+ shopPath?: string | undefined;
32
+ detailPath?: string | undefined;
33
+ cartPath?: string | undefined;
34
+ checkoutPath?: string | undefined;
35
+ successPath?: string | undefined;
36
+ shippingFlatCents?: number | undefined;
37
+ shippingFreeFromCents?: number | undefined;
38
+ orderPrefix?: string | undefined;
39
+ }>;
40
+ export type ShopSettings = z.infer<typeof ShopSettingsSchema>;
41
+ export declare const defaultShopSettings: ShopSettings;
@@ -0,0 +1,18 @@
1
+ import { z } from "zod";
2
+ const ShopSettingsSchema = z.object({
3
+ shopPath: z.string().max(200).default("/webwinkel").describe("Pad van de winkelpagina"),
4
+ detailPath: z.string().max(200).default("/product").describe("Pad van de productpagina"),
5
+ cartPath: z.string().max(200).default("/winkelwagen").describe("Pad van de winkelwagen"),
6
+ checkoutPath: z.string().max(200).default("/afrekenen").describe("Pad van de afrekenpagina"),
7
+ successPath: z.string().max(200).default("/bedankt").describe("Pad van de bedankt-pagina"),
8
+ currency: z.string().min(3).max(3).default("EUR").describe("Valuta (ISO-code)"),
9
+ shippingFlatCents: z.number().int().min(0).max(1e5).default(495).describe("Verzendkosten (centen)"),
10
+ shippingFreeFromCents: z.number().int().min(0).max(1e8).default(0).describe("Gratis verzending vanaf (centen, 0 = nooit)"),
11
+ orderPrefix: z.string().max(12).default("ORDER").describe("Ordernummer-prefix")
12
+ });
13
+ const defaultShopSettings = ShopSettingsSchema.parse({});
14
+ export {
15
+ ShopSettingsSchema,
16
+ defaultShopSettings
17
+ };
18
+ //# sourceMappingURL=settings.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/modules/shop/settings.ts"],"sourcesContent":["import { z } from \"zod\";\n\n/**\n * Publieke shop-instellingen (per site, via `settings/public.modules.shop.config` of\n * `SiteConfig.modules.shop.config`). Server-safe: alleen Zod. De SettingsPanel rendert hier\n * automatisch een formulier voor (moduleSettingsForm); de Functions lezen dezelfde config\n * server-side. Geheimen (Stripe-key, SMTP) horen hier NIET — die staan in\n * `sites/{siteId}/private/shop` en lopen via callables.\n */\nexport const ShopSettingsSchema = z.object({\n shopPath: z.string().max(200).default(\"/webwinkel\").describe(\"Pad van de winkelpagina\"),\n detailPath: z.string().max(200).default(\"/product\").describe(\"Pad van de productpagina\"),\n cartPath: z.string().max(200).default(\"/winkelwagen\").describe(\"Pad van de winkelwagen\"),\n checkoutPath: z.string().max(200).default(\"/afrekenen\").describe(\"Pad van de afrekenpagina\"),\n successPath: z.string().max(200).default(\"/bedankt\").describe(\"Pad van de bedankt-pagina\"),\n currency: z.string().min(3).max(3).default(\"EUR\").describe(\"Valuta (ISO-code)\"),\n shippingFlatCents: z.number().int().min(0).max(100_000).default(495).describe(\"Verzendkosten (centen)\"),\n shippingFreeFromCents: z\n .number()\n .int()\n .min(0)\n .max(1_000_000_00)\n .default(0)\n .describe(\"Gratis verzending vanaf (centen, 0 = nooit)\"),\n orderPrefix: z.string().max(12).default(\"ORDER\").describe(\"Ordernummer-prefix\"),\n});\nexport type ShopSettings = z.infer<typeof ShopSettingsSchema>;\n\nexport const defaultShopSettings: ShopSettings = ShopSettingsSchema.parse({});\n"],"mappings":"AAAA,SAAS,SAAS;AASX,MAAM,qBAAqB,EAAE,OAAO;AAAA,EACzC,UAAU,EAAE,OAAO,EAAE,IAAI,GAAG,EAAE,QAAQ,YAAY,EAAE,SAAS,yBAAyB;AAAA,EACtF,YAAY,EAAE,OAAO,EAAE,IAAI,GAAG,EAAE,QAAQ,UAAU,EAAE,SAAS,0BAA0B;AAAA,EACvF,UAAU,EAAE,OAAO,EAAE,IAAI,GAAG,EAAE,QAAQ,cAAc,EAAE,SAAS,wBAAwB;AAAA,EACvF,cAAc,EAAE,OAAO,EAAE,IAAI,GAAG,EAAE,QAAQ,YAAY,EAAE,SAAS,0BAA0B;AAAA,EAC3F,aAAa,EAAE,OAAO,EAAE,IAAI,GAAG,EAAE,QAAQ,UAAU,EAAE,SAAS,2BAA2B;AAAA,EACzF,UAAU,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,CAAC,EAAE,QAAQ,KAAK,EAAE,SAAS,mBAAmB;AAAA,EAC9E,mBAAmB,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,IAAI,GAAO,EAAE,QAAQ,GAAG,EAAE,SAAS,wBAAwB;AAAA,EACtG,uBAAuB,EACpB,OAAO,EACP,IAAI,EACJ,IAAI,CAAC,EACL,IAAI,GAAY,EAChB,QAAQ,CAAC,EACT,SAAS,6CAA6C;AAAA,EACzD,aAAa,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE,QAAQ,OAAO,EAAE,SAAS,oBAAoB;AAChF,CAAC;AAGM,MAAM,sBAAoC,mBAAmB,MAAM,CAAC,CAAC;","names":[]}
@@ -8,4 +8,5 @@ export * from "./settings";
8
8
  export * from "./blocks";
9
9
  export * from "./pages";
10
10
  export * from "./forms";
11
+ export * from "./shop";
11
12
  export { defineSiteConfig, SiteConfigSchema, validateSiteConfig, type SiteConfig, type SiteMode, } from "./siteConfig";
@@ -3,6 +3,7 @@ export * from "./settings";
3
3
  export * from "./blocks";
4
4
  export * from "./pages";
5
5
  export * from "./forms";
6
+ export * from "./shop";
6
7
  import {
7
8
  defineSiteConfig,
8
9
  SiteConfigSchema,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/schema/index.ts"],"sourcesContent":["/**\n * @xiwebdesign/xicms-core/schema — pure Zod-schema's + types.\n * Geen React, geen Firebase: importeerbaar in server-componenten, de sitemap-route én\n * de Cloud Functions, zodat content-validatie overal dezelfde bron gebruikt.\n */\nexport * from \"./seo\";\nexport * from \"./settings\";\nexport * from \"./blocks\";\nexport * from \"./pages\";\nexport * from \"./forms\";\nexport {\n defineSiteConfig,\n SiteConfigSchema,\n validateSiteConfig,\n type SiteConfig,\n type SiteMode,\n} from \"./siteConfig\";\n"],"mappings":"AAKA,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OAGK;","names":[]}
1
+ {"version":3,"sources":["../../src/schema/index.ts"],"sourcesContent":["/**\n * @xiwebdesign/xicms-core/schema — pure Zod-schema's + types.\n * Geen React, geen Firebase: importeerbaar in server-componenten, de sitemap-route én\n * de Cloud Functions, zodat content-validatie overal dezelfde bron gebruikt.\n */\nexport * from \"./seo\";\nexport * from \"./settings\";\nexport * from \"./blocks\";\nexport * from \"./pages\";\nexport * from \"./forms\";\nexport * from \"./shop\";\nexport {\n defineSiteConfig,\n SiteConfigSchema,\n validateSiteConfig,\n type SiteConfig,\n type SiteMode,\n} from \"./siteConfig\";\n"],"mappings":"AAKA,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OAGK;","names":[]}