@xiwebdesign/xicms-core 5.5.2 → 5.6.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.
- package/dist/admin/shop/ConnectionsTab.js +1 -1
- package/dist/admin/shop/ConnectionsTab.js.map +1 -1
- package/dist/admin/shop/DiscountsTab.d.ts +4 -0
- package/dist/admin/shop/DiscountsTab.js +168 -0
- package/dist/admin/shop/DiscountsTab.js.map +1 -0
- package/dist/admin/shop/EmailsTab.d.ts +8 -0
- package/dist/admin/shop/EmailsTab.js +88 -0
- package/dist/admin/shop/EmailsTab.js.map +1 -0
- package/dist/admin/shop/OrdersTab.js +271 -139
- package/dist/admin/shop/OrdersTab.js.map +1 -1
- package/dist/admin/shop/ProductsTab.js +152 -28
- package/dist/admin/shop/ProductsTab.js.map +1 -1
- package/dist/admin/shop/ShopPanel.d.ts +4 -1
- package/dist/admin/shop/ShopPanel.js +9 -0
- package/dist/admin/shop/ShopPanel.js.map +1 -1
- package/dist/admin/shop/ShopSettingsTab.d.ts +3 -0
- package/dist/admin/shop/ShopSettingsTab.js +86 -0
- package/dist/admin/shop/ShopSettingsTab.js.map +1 -0
- package/dist/admin.css +1 -1
- package/dist/blocks/context.d.ts +6 -0
- package/dist/blocks/context.js +15 -1
- package/dist/blocks/context.js.map +1 -1
- package/dist/modules/shop/blocks/Cart.js +22 -14
- package/dist/modules/shop/blocks/Cart.js.map +1 -1
- package/dist/modules/shop/blocks/Checkout.js +75 -23
- package/dist/modules/shop/blocks/Checkout.js.map +1 -1
- package/dist/modules/shop/blocks/Donation.js +8 -7
- package/dist/modules/shop/blocks/Donation.js.map +1 -1
- package/dist/modules/shop/blocks/ProductDetail.js +296 -102
- package/dist/modules/shop/blocks/ProductDetail.js.map +1 -1
- package/dist/modules/shop/blocks/ProductGrid.d.ts +9 -0
- package/dist/modules/shop/blocks/ProductGrid.js +151 -29
- package/dist/modules/shop/blocks/ProductGrid.js.map +1 -1
- package/dist/modules/shop/blocks/ShopCategories.js +7 -2
- package/dist/modules/shop/blocks/ShopCategories.js.map +1 -1
- package/dist/modules/shop/cartStore.d.ts +13 -2
- package/dist/modules/shop/cartStore.js +10 -5
- package/dist/modules/shop/cartStore.js.map +1 -1
- package/dist/modules/shop/lib.d.ts +24 -1
- package/dist/modules/shop/lib.js +49 -0
- package/dist/modules/shop/lib.js.map +1 -1
- package/dist/modules/shop/module.js +6 -1
- package/dist/modules/shop/module.js.map +1 -1
- package/dist/modules/shop/settings.d.ts +18 -0
- package/dist/modules/shop/settings.js +8 -1
- package/dist/modules/shop/settings.js.map +1 -1
- package/dist/modules/shop/strings.d.ts +65 -0
- package/dist/modules/shop/strings.js +136 -0
- package/dist/modules/shop/strings.js.map +1 -0
- package/dist/modules/shop/types.d.ts +18 -0
- package/dist/modules/shop/types.js +1 -0
- package/dist/modules/shop/types.js.map +1 -0
- package/dist/render/PublishedPage.d.ts +6 -1
- package/dist/render/PublishedPage.js +4 -3
- package/dist/render/PublishedPage.js.map +1 -1
- package/dist/schema/shop.d.ts +413 -2
- package/dist/schema/shop.js +95 -6
- package/dist/schema/shop.js.map +1 -1
- package/dist/server/index.d.ts +1 -0
- package/dist/server/index.js +14 -0
- package/dist/server/index.js.map +1 -1
- package/dist/server/shop.d.ts +46 -0
- package/dist/server/shop.js +181 -0
- package/dist/server/shop.js.map +1 -0
- package/dist/styles.css +76 -0
- package/package.json +1 -1
package/dist/modules/shop/lib.js
CHANGED
|
@@ -45,6 +45,27 @@ async function createOrderCall(input) {
|
|
|
45
45
|
const res = await fn(input);
|
|
46
46
|
return res.data;
|
|
47
47
|
}
|
|
48
|
+
async function validateDiscountCall(siteId, code, itemsCents) {
|
|
49
|
+
const app = getFirebaseApp();
|
|
50
|
+
if (!app) throw new Error("Webwinkel niet beschikbaar.");
|
|
51
|
+
const fn = httpsCallable(getFunctions(app, functionsRegion()), "validateDiscountCode");
|
|
52
|
+
const res = await fn({ siteId, code, itemsCents });
|
|
53
|
+
return res.data;
|
|
54
|
+
}
|
|
55
|
+
async function uploadPersonalizationCall(siteId, productId, file) {
|
|
56
|
+
const app = getFirebaseApp();
|
|
57
|
+
if (!app) throw new Error("Webwinkel niet beschikbaar.");
|
|
58
|
+
if (file.size > 5 * 1024 * 1024) throw new Error("TOO_BIG");
|
|
59
|
+
const bytesBase64 = await new Promise((resolve, reject) => {
|
|
60
|
+
const reader = new FileReader();
|
|
61
|
+
reader.onload = () => resolve(String(reader.result).split(",")[1] ?? "");
|
|
62
|
+
reader.onerror = () => reject(new Error("Bestand lezen mislukt."));
|
|
63
|
+
reader.readAsDataURL(file);
|
|
64
|
+
});
|
|
65
|
+
const fn = httpsCallable(getFunctions(app, functionsRegion()), "uploadPersonalization");
|
|
66
|
+
const res = await fn({ siteId, productId, fileName: file.name, contentType: file.type, bytesBase64 });
|
|
67
|
+
return res.data;
|
|
68
|
+
}
|
|
48
69
|
function variantLabelFor(options, key) {
|
|
49
70
|
const parts = key.split("|");
|
|
50
71
|
return options.map((o, i) => `${o.name} ${parts[i] ?? "?"}`).join(" \xB7 ");
|
|
@@ -53,13 +74,41 @@ function readQueryParam(name) {
|
|
|
53
74
|
if (typeof window === "undefined") return null;
|
|
54
75
|
return new URLSearchParams(window.location.search).get(name);
|
|
55
76
|
}
|
|
77
|
+
const KLEUR_CSS = [
|
|
78
|
+
[/fuchsia|fushia/, "#c2588c"],
|
|
79
|
+
[/roze|pink/, "#f2b9cd"],
|
|
80
|
+
[/blauw|blue/, "#a9c8e6"],
|
|
81
|
+
[/mint/, "#abd7c6"],
|
|
82
|
+
[/oker|geel|yellow/, "#eac57e"],
|
|
83
|
+
[/wit|white/, "#ffffff"],
|
|
84
|
+
[/creme|crème|ecru|beige|zand/, "#f1e6d2"],
|
|
85
|
+
[/grijs|gray|grey/, "#b9bec7"],
|
|
86
|
+
[/groen|green/, "#9fc79f"],
|
|
87
|
+
[/rood|red|bordeaux/, "#d97a7a"],
|
|
88
|
+
[/paars|purple|lila/, "#c3aede"],
|
|
89
|
+
[/oranje|orange/, "#f0a868"],
|
|
90
|
+
[/bruin|brown/, "#b08968"],
|
|
91
|
+
[/zwart|black|navy|donkerblauw/, "#3a4356"]
|
|
92
|
+
];
|
|
93
|
+
function kleurNaarCss(naam) {
|
|
94
|
+
const l = naam.toLowerCase();
|
|
95
|
+
for (const [re, css] of KLEUR_CSS) if (re.test(l)) return css;
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
function kleurOptie(product) {
|
|
99
|
+
return (product.options ?? []).find((o) => /^kleur|^colou?r/i.test(o.name)) ?? null;
|
|
100
|
+
}
|
|
56
101
|
export {
|
|
57
102
|
createOrderCall,
|
|
58
103
|
fetchActiveProducts,
|
|
59
104
|
fetchProductBySlug,
|
|
60
105
|
fetchShopCategories,
|
|
106
|
+
kleurNaarCss,
|
|
107
|
+
kleurOptie,
|
|
61
108
|
readQueryParam,
|
|
109
|
+
uploadPersonalizationCall,
|
|
62
110
|
useShopSettings,
|
|
111
|
+
validateDiscountCall,
|
|
63
112
|
variantLabelFor
|
|
64
113
|
};
|
|
65
114
|
//# sourceMappingURL=lib.js.map
|
|
@@ -1 +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 // Het status-filter is verplicht: de rules staan publieke reads alleen toe voor actieve\n // producten, en Firestore weigert query's die niet aantoonbaar binnen die regel blijven.\n // Twee equality-filters → geen composite index nodig (index-merging).\n const snap = await getDocs(\n query(\n collection(db, \"sites\", siteId, \"products\"),\n where(\"slug\", \"==\", slug),\n where(\"status\", \"==\", \"active\"),\n fsLimit(1),\n ),\n );\n const doc = snap.docs[0];\n if (!doc) return null;\n return { id: doc.id, ...(doc.data() as Product) };\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;AAIzB,QAAM,OAAO,MAAM;AAAA,IACjB;AAAA,MACE,WAAW,IAAI,SAAS,QAAQ,UAAU;AAAA,MAC1C,MAAM,QAAQ,MAAM,IAAI;AAAA,MACxB,MAAM,UAAU,MAAM,QAAQ;AAAA,MAC9B,QAAQ,CAAC;AAAA,IACX;AAAA,EACF;AACA,QAAM,MAAM,KAAK,KAAK,CAAC;AACvB,MAAI,CAAC,IAAK,QAAO;AACjB,SAAO,EAAE,IAAI,IAAI,IAAI,GAAI,IAAI,KAAK,EAAc;AAClD;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":[]}
|
|
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, ProductOption, 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 // Het status-filter is verplicht: de rules staan publieke reads alleen toe voor actieve\n // producten, en Firestore weigert query's die niet aantoonbaar binnen die regel blijven.\n // Twee equality-filters → geen composite index nodig (index-merging).\n const snap = await getDocs(\n query(\n collection(db, \"sites\", siteId, \"products\"),\n where(\"slug\", \"==\", slug),\n where(\"status\", \"==\", \"active\"),\n fsLimit(1),\n ),\n );\n const doc = snap.docs[0];\n if (!doc) return null;\n return { id: doc.id, ...(doc.data() as Product) };\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/** Valideert een kortingscode server-side; geeft de verwachte korting terug (preview). */\nexport interface DiscountPreview {\n valid: boolean;\n code?: string;\n discountCents?: number;\n freeShipping?: boolean;\n reason?: string;\n}\nexport async function validateDiscountCall(siteId: string, code: string, itemsCents: number): Promise<DiscountPreview> {\n const app = getFirebaseApp();\n if (!app) throw new Error(\"Webwinkel niet beschikbaar.\");\n const fn = httpsCallable(getFunctions(app, functionsRegion()), \"validateDiscountCode\");\n const res = await fn({ siteId, code, itemsCents });\n return res.data as DiscountPreview;\n}\n\n/**\n * Upload een personalisatie-afbeelding (eigen logo e.d.) via de begrensde callable —\n * geeft het Storage-pad terug dat op de orderregel meegaat. Max 5 MB, jpeg/png/webp.\n */\nexport async function uploadPersonalizationCall(\n siteId: string,\n productId: string,\n file: File,\n): Promise<{ path: string }> {\n const app = getFirebaseApp();\n if (!app) throw new Error(\"Webwinkel niet beschikbaar.\");\n if (file.size > 5 * 1024 * 1024) throw new Error(\"TOO_BIG\");\n const bytesBase64 = await new Promise<string>((resolve, reject) => {\n const reader = new FileReader();\n reader.onload = () => resolve(String(reader.result).split(\",\")[1] ?? \"\");\n reader.onerror = () => reject(new Error(\"Bestand lezen mislukt.\"));\n reader.readAsDataURL(file);\n });\n const fn = httpsCallable(getFunctions(app, functionsRegion()), \"uploadPersonalization\");\n const res = await fn({ siteId, productId, fileName: file.name, contentType: file.type, bytesBase64 });\n return res.data as { path: 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\n/* ── Kleur-swatches ──────────────────────────────────────────────────────── */\n\nconst KLEUR_CSS: [RegExp, string][] = [\n [/fuchsia|fushia/, \"#c2588c\"],\n [/roze|pink/, \"#f2b9cd\"],\n [/blauw|blue/, \"#a9c8e6\"],\n [/mint/, \"#abd7c6\"],\n [/oker|geel|yellow/, \"#eac57e\"],\n [/wit|white/, \"#ffffff\"],\n [/creme|crème|ecru|beige|zand/, \"#f1e6d2\"],\n [/grijs|gray|grey/, \"#b9bec7\"],\n [/groen|green/, \"#9fc79f\"],\n [/rood|red|bordeaux/, \"#d97a7a\"],\n [/paars|purple|lila/, \"#c3aede\"],\n [/oranje|orange/, \"#f0a868\"],\n [/bruin|brown/, \"#b08968\"],\n [/zwart|black|navy|donkerblauw/, \"#3a4356\"],\n];\n\n/**\n * Zet een kleurnaam (\"Roze\", \"Korte mouw tshirt (oker)geel\") om naar een CSS-kleur voor de\n * swatch-stipjes op productkaarten. Onbekende namen geven null (dan geen stipje).\n */\nexport function kleurNaarCss(naam: string): string | null {\n const l = naam.toLowerCase();\n for (const [re, css] of KLEUR_CSS) if (re.test(l)) return css;\n return null;\n}\n\n/** De kleur-optie van een product (naam \"Kleur\"/\"Colour\"/\"Color\"), of null. */\nexport function kleurOptie(product: Pick<Product, \"options\">): ProductOption | null {\n return (product.options ?? []).find((o) => /^kleur|^colou?r/i.test(o.name)) ?? null;\n}\n"],"mappings":"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","names":[]}
|
|
@@ -28,9 +28,14 @@ const shopModule = defineModule({
|
|
|
28
28
|
{ kind: "function", name: "createOrder" },
|
|
29
29
|
{ kind: "function", name: "shopStripeWebhook" },
|
|
30
30
|
{ kind: "function", name: "saveShopSecrets" },
|
|
31
|
+
{ kind: "function", name: "updateOrderStatus" },
|
|
32
|
+
{ kind: "function", name: "validateDiscountCode" },
|
|
33
|
+
{ kind: "function", name: "uploadPersonalization" },
|
|
31
34
|
{ kind: "firestore", collection: "products" },
|
|
32
35
|
{ kind: "firestore", collection: "shopCategories" },
|
|
33
|
-
{ kind: "firestore", collection: "orders" }
|
|
36
|
+
{ kind: "firestore", collection: "orders" },
|
|
37
|
+
{ kind: "firestore", collection: "discountCodes" },
|
|
38
|
+
{ kind: "storage", prefix: "sites/" }
|
|
34
39
|
]
|
|
35
40
|
});
|
|
36
41
|
export {
|
|
@@ -1 +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,
|
|
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: \"function\", name: \"updateOrderStatus\" },\n { kind: \"function\", name: \"validateDiscountCode\" },\n { kind: \"function\", name: \"uploadPersonalization\" },\n { kind: \"firestore\", collection: \"products\" },\n { kind: \"firestore\", collection: \"shopCategories\" },\n { kind: \"firestore\", collection: \"orders\" },\n { kind: \"firestore\", collection: \"discountCodes\" },\n { kind: \"storage\", prefix: \"sites/\" },\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,YAAY,MAAM,oBAAoB;AAAA,IAC9C,EAAE,MAAM,YAAY,MAAM,uBAAuB;AAAA,IACjD,EAAE,MAAM,YAAY,MAAM,wBAAwB;AAAA,IAClD,EAAE,MAAM,aAAa,YAAY,WAAW;AAAA,IAC5C,EAAE,MAAM,aAAa,YAAY,iBAAiB;AAAA,IAClD,EAAE,MAAM,aAAa,YAAY,SAAS;AAAA,IAC1C,EAAE,MAAM,aAAa,YAAY,gBAAgB;AAAA,IACjD,EAAE,MAAM,WAAW,QAAQ,SAAS;AAAA,EACtC;AACF,CAAC;","names":[]}
|
|
@@ -16,6 +16,12 @@ export declare const ShopSettingsSchema: z.ZodObject<{
|
|
|
16
16
|
shippingFlatCents: z.ZodDefault<z.ZodNumber>;
|
|
17
17
|
shippingFreeFromCents: z.ZodDefault<z.ZodNumber>;
|
|
18
18
|
orderPrefix: z.ZodDefault<z.ZodString>;
|
|
19
|
+
businessName: z.ZodDefault<z.ZodString>;
|
|
20
|
+
businessAddress: z.ZodDefault<z.ZodString>;
|
|
21
|
+
businessZip: z.ZodDefault<z.ZodString>;
|
|
22
|
+
businessCity: z.ZodDefault<z.ZodString>;
|
|
23
|
+
businessKvk: z.ZodDefault<z.ZodString>;
|
|
24
|
+
businessBtw: z.ZodDefault<z.ZodString>;
|
|
19
25
|
}, "strip", z.ZodTypeAny, {
|
|
20
26
|
currency: string;
|
|
21
27
|
shopPath: string;
|
|
@@ -26,6 +32,12 @@ export declare const ShopSettingsSchema: z.ZodObject<{
|
|
|
26
32
|
shippingFlatCents: number;
|
|
27
33
|
shippingFreeFromCents: number;
|
|
28
34
|
orderPrefix: string;
|
|
35
|
+
businessName: string;
|
|
36
|
+
businessAddress: string;
|
|
37
|
+
businessZip: string;
|
|
38
|
+
businessCity: string;
|
|
39
|
+
businessKvk: string;
|
|
40
|
+
businessBtw: string;
|
|
29
41
|
}, {
|
|
30
42
|
currency?: string | undefined;
|
|
31
43
|
shopPath?: string | undefined;
|
|
@@ -36,6 +48,12 @@ export declare const ShopSettingsSchema: z.ZodObject<{
|
|
|
36
48
|
shippingFlatCents?: number | undefined;
|
|
37
49
|
shippingFreeFromCents?: number | undefined;
|
|
38
50
|
orderPrefix?: string | undefined;
|
|
51
|
+
businessName?: string | undefined;
|
|
52
|
+
businessAddress?: string | undefined;
|
|
53
|
+
businessZip?: string | undefined;
|
|
54
|
+
businessCity?: string | undefined;
|
|
55
|
+
businessKvk?: string | undefined;
|
|
56
|
+
businessBtw?: string | undefined;
|
|
39
57
|
}>;
|
|
40
58
|
export type ShopSettings = z.infer<typeof ShopSettingsSchema>;
|
|
41
59
|
export declare const defaultShopSettings: ShopSettings;
|
|
@@ -8,7 +8,14 @@ const ShopSettingsSchema = z.object({
|
|
|
8
8
|
currency: z.string().min(3).max(3).default("EUR").describe("Valuta (ISO-code)"),
|
|
9
9
|
shippingFlatCents: z.number().int().min(0).max(1e5).default(495).describe("Verzendkosten (centen)"),
|
|
10
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")
|
|
11
|
+
orderPrefix: z.string().max(12).default("ORDER").describe("Ordernummer-prefix"),
|
|
12
|
+
/* Bedrijfsgegevens — komen op de factuur-PDF (5.6). */
|
|
13
|
+
businessName: z.string().max(120).default("").describe("Bedrijfsnaam (factuur)"),
|
|
14
|
+
businessAddress: z.string().max(200).default("").describe("Adres (factuur)"),
|
|
15
|
+
businessZip: z.string().max(20).default("").describe("Postcode (factuur)"),
|
|
16
|
+
businessCity: z.string().max(120).default("").describe("Plaats (factuur)"),
|
|
17
|
+
businessKvk: z.string().max(20).default("").describe("KVK-nummer (factuur)"),
|
|
18
|
+
businessBtw: z.string().max(30).default("").describe("Btw-nummer (factuur)")
|
|
12
19
|
});
|
|
13
20
|
const defaultShopSettings = ShopSettingsSchema.parse({});
|
|
14
21
|
export {
|
|
@@ -1 +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;
|
|
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 /* Bedrijfsgegevens — komen op de factuur-PDF (5.6). */\n businessName: z.string().max(120).default(\"\").describe(\"Bedrijfsnaam (factuur)\"),\n businessAddress: z.string().max(200).default(\"\").describe(\"Adres (factuur)\"),\n businessZip: z.string().max(20).default(\"\").describe(\"Postcode (factuur)\"),\n businessCity: z.string().max(120).default(\"\").describe(\"Plaats (factuur)\"),\n businessKvk: z.string().max(20).default(\"\").describe(\"KVK-nummer (factuur)\"),\n businessBtw: z.string().max(30).default(\"\").describe(\"Btw-nummer (factuur)\"),\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;AAAA;AAAA,EAE9E,cAAc,EAAE,OAAO,EAAE,IAAI,GAAG,EAAE,QAAQ,EAAE,EAAE,SAAS,wBAAwB;AAAA,EAC/E,iBAAiB,EAAE,OAAO,EAAE,IAAI,GAAG,EAAE,QAAQ,EAAE,EAAE,SAAS,iBAAiB;AAAA,EAC3E,aAAa,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE,QAAQ,EAAE,EAAE,SAAS,oBAAoB;AAAA,EACzE,cAAc,EAAE,OAAO,EAAE,IAAI,GAAG,EAAE,QAAQ,EAAE,EAAE,SAAS,kBAAkB;AAAA,EACzE,aAAa,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE,QAAQ,EAAE,EAAE,SAAS,sBAAsB;AAAA,EAC3E,aAAa,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE,QAAQ,EAAE,EAAE,SAAS,sBAAsB;AAC7E,CAAC;AAGM,MAAM,sBAAoC,mBAAmB,MAAM,CAAC,CAAC;","names":[]}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
declare const NL: {
|
|
2
|
+
addToCart: string;
|
|
3
|
+
added: string;
|
|
4
|
+
addedGoToCart: string;
|
|
5
|
+
view: string;
|
|
6
|
+
toCart: string;
|
|
7
|
+
continueShopping: string;
|
|
8
|
+
checkout: string;
|
|
9
|
+
cartEmpty: string;
|
|
10
|
+
toShop: string;
|
|
11
|
+
subtotal: string;
|
|
12
|
+
shippingCosts: string;
|
|
13
|
+
free: string;
|
|
14
|
+
total: string;
|
|
15
|
+
perPiece: string;
|
|
16
|
+
qty: string;
|
|
17
|
+
outOfStock: string;
|
|
18
|
+
variantUnavailable: string;
|
|
19
|
+
stockLeft: string;
|
|
20
|
+
soldOut: string;
|
|
21
|
+
productNotFound: string;
|
|
22
|
+
backToShop: string;
|
|
23
|
+
noProducts: string;
|
|
24
|
+
noCategories: string;
|
|
25
|
+
allCategories: string;
|
|
26
|
+
pay: string;
|
|
27
|
+
busy: string;
|
|
28
|
+
checkoutFailed: string;
|
|
29
|
+
secureNote: string;
|
|
30
|
+
yourOrder: string;
|
|
31
|
+
fieldName: string;
|
|
32
|
+
fieldEmail: string;
|
|
33
|
+
fieldPhone: string;
|
|
34
|
+
fieldAddress: string;
|
|
35
|
+
fieldZip: string;
|
|
36
|
+
fieldCity: string;
|
|
37
|
+
fieldNote: string;
|
|
38
|
+
choose: string;
|
|
39
|
+
discountCode: string;
|
|
40
|
+
discountApply: string;
|
|
41
|
+
discount: string;
|
|
42
|
+
discountInvalid: string;
|
|
43
|
+
discountApplied: string;
|
|
44
|
+
related: string;
|
|
45
|
+
filterAll: string;
|
|
46
|
+
sortLabel: string;
|
|
47
|
+
sortDefault: string;
|
|
48
|
+
sortName: string;
|
|
49
|
+
sortPriceAsc: string;
|
|
50
|
+
sortPriceDesc: string;
|
|
51
|
+
personalizationUpload: string;
|
|
52
|
+
personalizationUploading: string;
|
|
53
|
+
personalizationUploaded: string;
|
|
54
|
+
personalizationTooBig: string;
|
|
55
|
+
personalizationFailed: string;
|
|
56
|
+
personalizationSurcharge: string;
|
|
57
|
+
donationFailed: string;
|
|
58
|
+
donationCustomAmount: string;
|
|
59
|
+
donationAmountLabel: string;
|
|
60
|
+
};
|
|
61
|
+
export type ShopStringKey = keyof typeof NL;
|
|
62
|
+
export type ShopT = (key: ShopStringKey, vars?: Record<string, string | number>) => string;
|
|
63
|
+
/** Vertaalfunctie voor shop-blocks: `t("addToCart")`, `t("stockLeft", { n: 3 })`. */
|
|
64
|
+
export declare function useShopStrings(): ShopT;
|
|
65
|
+
export {};
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useSiteConfig } from "../../config";
|
|
3
|
+
const NL = {
|
|
4
|
+
addToCart: "In winkelwagen",
|
|
5
|
+
added: "\u2713 Toegevoegd aan je winkelwagen.",
|
|
6
|
+
addedGoToCart: "\u2713 In winkelwagen \u2014 bekijk",
|
|
7
|
+
view: "Bekijk",
|
|
8
|
+
toCart: "Naar winkelwagen",
|
|
9
|
+
continueShopping: "Verder winkelen",
|
|
10
|
+
checkout: "Afrekenen",
|
|
11
|
+
cartEmpty: "Je winkelwagen is leeg.",
|
|
12
|
+
toShop: "Naar de winkel",
|
|
13
|
+
subtotal: "Subtotaal",
|
|
14
|
+
shippingCosts: "Verzendkosten",
|
|
15
|
+
free: "gratis",
|
|
16
|
+
total: "Totaal",
|
|
17
|
+
perPiece: "p/st",
|
|
18
|
+
qty: "Aantal",
|
|
19
|
+
outOfStock: "Uitverkocht in deze uitvoering.",
|
|
20
|
+
variantUnavailable: "Deze combinatie is niet leverbaar.",
|
|
21
|
+
stockLeft: "Nog {n} beschikbaar",
|
|
22
|
+
soldOut: "Uitverkocht",
|
|
23
|
+
productNotFound: "Product niet gevonden.",
|
|
24
|
+
backToShop: "Terug naar de winkel",
|
|
25
|
+
noProducts: "Geen producten gevonden.",
|
|
26
|
+
noCategories: "Nog geen categorie\xEBn.",
|
|
27
|
+
allCategories: "Alle categorie\xEBn",
|
|
28
|
+
pay: "Betalen \u2014 {bedrag}",
|
|
29
|
+
busy: "Bezig\u2026",
|
|
30
|
+
checkoutFailed: "Afrekenen is niet gelukt. Controleer je gegevens en probeer het opnieuw.",
|
|
31
|
+
secureNote: "Je rekent veilig af via Stripe (o.a. iDEAL). Na betaling ontvang je een bevestiging per e-mail.",
|
|
32
|
+
yourOrder: "Je bestelling",
|
|
33
|
+
fieldName: "Naam",
|
|
34
|
+
fieldEmail: "E-mail",
|
|
35
|
+
fieldPhone: "Telefoon (optioneel)",
|
|
36
|
+
fieldAddress: "Straat + huisnummer",
|
|
37
|
+
fieldZip: "Postcode",
|
|
38
|
+
fieldCity: "Plaats",
|
|
39
|
+
fieldNote: "Opmerking (optioneel)",
|
|
40
|
+
choose: "Kies\u2026",
|
|
41
|
+
discountCode: "Kortingscode",
|
|
42
|
+
discountApply: "Toepassen",
|
|
43
|
+
discount: "Korting",
|
|
44
|
+
discountInvalid: "Deze code is niet (meer) geldig.",
|
|
45
|
+
discountApplied: "Korting toegepast: {code}",
|
|
46
|
+
related: "Ook leuk",
|
|
47
|
+
filterAll: "Alles",
|
|
48
|
+
sortLabel: "Sorteren",
|
|
49
|
+
sortDefault: "Aanbevolen",
|
|
50
|
+
sortName: "Naam",
|
|
51
|
+
sortPriceAsc: "Prijs (laag-hoog)",
|
|
52
|
+
sortPriceDesc: "Prijs (hoog-laag)",
|
|
53
|
+
personalizationUpload: "Kies bestand\u2026",
|
|
54
|
+
personalizationUploading: "Uploaden\u2026",
|
|
55
|
+
personalizationUploaded: "\u2713 Ge\xFCpload",
|
|
56
|
+
personalizationTooBig: "Bestand te groot (max 5 MB).",
|
|
57
|
+
personalizationFailed: "Uploaden mislukt \u2014 probeer het opnieuw.",
|
|
58
|
+
personalizationSurcharge: "+ {bedrag}",
|
|
59
|
+
donationFailed: "Doneren is niet gelukt. Probeer het later opnieuw.",
|
|
60
|
+
donationCustomAmount: "Ander bedrag",
|
|
61
|
+
donationAmountLabel: "Bedrag in euro's (min. {min})"
|
|
62
|
+
};
|
|
63
|
+
const EN = {
|
|
64
|
+
addToCart: "Add to cart",
|
|
65
|
+
added: "\u2713 Added to your cart.",
|
|
66
|
+
addedGoToCart: "\u2713 In cart \u2014 view",
|
|
67
|
+
view: "View",
|
|
68
|
+
toCart: "Go to cart",
|
|
69
|
+
continueShopping: "Continue shopping",
|
|
70
|
+
checkout: "Checkout",
|
|
71
|
+
cartEmpty: "Your cart is empty.",
|
|
72
|
+
toShop: "Go to the shop",
|
|
73
|
+
subtotal: "Subtotal",
|
|
74
|
+
shippingCosts: "Shipping",
|
|
75
|
+
free: "free",
|
|
76
|
+
total: "Total",
|
|
77
|
+
perPiece: "each",
|
|
78
|
+
qty: "Quantity",
|
|
79
|
+
outOfStock: "Sold out in this variant.",
|
|
80
|
+
variantUnavailable: "This combination is unavailable.",
|
|
81
|
+
stockLeft: "Only {n} left",
|
|
82
|
+
soldOut: "Sold out",
|
|
83
|
+
productNotFound: "Product not found.",
|
|
84
|
+
backToShop: "Back to the shop",
|
|
85
|
+
noProducts: "No products found.",
|
|
86
|
+
noCategories: "No categories yet.",
|
|
87
|
+
allCategories: "All categories",
|
|
88
|
+
pay: "Pay \u2014 {bedrag}",
|
|
89
|
+
busy: "Working\u2026",
|
|
90
|
+
checkoutFailed: "Checkout failed. Check your details and try again.",
|
|
91
|
+
secureNote: "You pay securely via Stripe. You will receive a confirmation email after payment.",
|
|
92
|
+
yourOrder: "Your order",
|
|
93
|
+
fieldName: "Name",
|
|
94
|
+
fieldEmail: "Email",
|
|
95
|
+
fieldPhone: "Phone (optional)",
|
|
96
|
+
fieldAddress: "Street + number",
|
|
97
|
+
fieldZip: "Postal code",
|
|
98
|
+
fieldCity: "City",
|
|
99
|
+
fieldNote: "Note (optional)",
|
|
100
|
+
choose: "Choose\u2026",
|
|
101
|
+
discountCode: "Discount code",
|
|
102
|
+
discountApply: "Apply",
|
|
103
|
+
discount: "Discount",
|
|
104
|
+
discountInvalid: "This code is not (or no longer) valid.",
|
|
105
|
+
discountApplied: "Discount applied: {code}",
|
|
106
|
+
related: "You may also like",
|
|
107
|
+
filterAll: "All",
|
|
108
|
+
sortLabel: "Sort",
|
|
109
|
+
sortDefault: "Recommended",
|
|
110
|
+
sortName: "Name",
|
|
111
|
+
sortPriceAsc: "Price (low-high)",
|
|
112
|
+
sortPriceDesc: "Price (high-low)",
|
|
113
|
+
personalizationUpload: "Choose file\u2026",
|
|
114
|
+
personalizationUploading: "Uploading\u2026",
|
|
115
|
+
personalizationUploaded: "\u2713 Uploaded",
|
|
116
|
+
personalizationTooBig: "File too large (max 5 MB).",
|
|
117
|
+
personalizationFailed: "Upload failed \u2014 please try again.",
|
|
118
|
+
personalizationSurcharge: "+ {bedrag}",
|
|
119
|
+
donationFailed: "Donation failed. Please try again later.",
|
|
120
|
+
donationCustomAmount: "Custom amount",
|
|
121
|
+
donationAmountLabel: "Amount in euros (min. {min})"
|
|
122
|
+
};
|
|
123
|
+
const TABLES = { nl: NL, en: EN };
|
|
124
|
+
function useShopStrings() {
|
|
125
|
+
const lang = (useSiteConfig().lang ?? "nl").slice(0, 2).toLowerCase();
|
|
126
|
+
const table = TABLES[lang] ?? NL;
|
|
127
|
+
return (key, vars) => {
|
|
128
|
+
let s = table[key] ?? NL[key] ?? key;
|
|
129
|
+
if (vars) for (const [k, v] of Object.entries(vars)) s = s.replaceAll(`{${k}}`, String(v));
|
|
130
|
+
return s;
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
export {
|
|
134
|
+
useShopStrings
|
|
135
|
+
};
|
|
136
|
+
//# sourceMappingURL=strings.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/modules/shop/strings.ts"],"sourcesContent":["\"use client\";\n\n/**\n * UI-teksten van de shop-blocks, gelokaliseerd op `SiteConfig.lang` (nl = default, en).\n * Interpolatie via `{naam}`-placeholders. Bewust een platte tabel — geen i18n-framework;\n * de shop-strings zijn de enige plek in de blocks met vaste UI-tekst.\n */\nimport { useSiteConfig } from \"../../config\";\n\nconst NL = {\n addToCart: \"In winkelwagen\",\n added: \"✓ Toegevoegd aan je winkelwagen.\",\n addedGoToCart: \"✓ In winkelwagen — bekijk\",\n view: \"Bekijk\",\n toCart: \"Naar winkelwagen\",\n continueShopping: \"Verder winkelen\",\n checkout: \"Afrekenen\",\n cartEmpty: \"Je winkelwagen is leeg.\",\n toShop: \"Naar de winkel\",\n subtotal: \"Subtotaal\",\n shippingCosts: \"Verzendkosten\",\n free: \"gratis\",\n total: \"Totaal\",\n perPiece: \"p/st\",\n qty: \"Aantal\",\n outOfStock: \"Uitverkocht in deze uitvoering.\",\n variantUnavailable: \"Deze combinatie is niet leverbaar.\",\n stockLeft: \"Nog {n} beschikbaar\",\n soldOut: \"Uitverkocht\",\n productNotFound: \"Product niet gevonden.\",\n backToShop: \"Terug naar de winkel\",\n noProducts: \"Geen producten gevonden.\",\n noCategories: \"Nog geen categorieën.\",\n allCategories: \"Alle categorieën\",\n pay: \"Betalen — {bedrag}\",\n busy: \"Bezig…\",\n checkoutFailed: \"Afrekenen is niet gelukt. Controleer je gegevens en probeer het opnieuw.\",\n secureNote: \"Je rekent veilig af via Stripe (o.a. iDEAL). Na betaling ontvang je een bevestiging per e-mail.\",\n yourOrder: \"Je bestelling\",\n fieldName: \"Naam\",\n fieldEmail: \"E-mail\",\n fieldPhone: \"Telefoon (optioneel)\",\n fieldAddress: \"Straat + huisnummer\",\n fieldZip: \"Postcode\",\n fieldCity: \"Plaats\",\n fieldNote: \"Opmerking (optioneel)\",\n choose: \"Kies…\",\n discountCode: \"Kortingscode\",\n discountApply: \"Toepassen\",\n discount: \"Korting\",\n discountInvalid: \"Deze code is niet (meer) geldig.\",\n discountApplied: \"Korting toegepast: {code}\",\n related: \"Ook leuk\",\n filterAll: \"Alles\",\n sortLabel: \"Sorteren\",\n sortDefault: \"Aanbevolen\",\n sortName: \"Naam\",\n sortPriceAsc: \"Prijs (laag-hoog)\",\n sortPriceDesc: \"Prijs (hoog-laag)\",\n personalizationUpload: \"Kies bestand…\",\n personalizationUploading: \"Uploaden…\",\n personalizationUploaded: \"✓ Geüpload\",\n personalizationTooBig: \"Bestand te groot (max 5 MB).\",\n personalizationFailed: \"Uploaden mislukt — probeer het opnieuw.\",\n personalizationSurcharge: \"+ {bedrag}\",\n donationFailed: \"Doneren is niet gelukt. Probeer het later opnieuw.\",\n donationCustomAmount: \"Ander bedrag\",\n donationAmountLabel: \"Bedrag in euro's (min. {min})\",\n};\n\nconst EN: typeof NL = {\n addToCart: \"Add to cart\",\n added: \"✓ Added to your cart.\",\n addedGoToCart: \"✓ In cart — view\",\n view: \"View\",\n toCart: \"Go to cart\",\n continueShopping: \"Continue shopping\",\n checkout: \"Checkout\",\n cartEmpty: \"Your cart is empty.\",\n toShop: \"Go to the shop\",\n subtotal: \"Subtotal\",\n shippingCosts: \"Shipping\",\n free: \"free\",\n total: \"Total\",\n perPiece: \"each\",\n qty: \"Quantity\",\n outOfStock: \"Sold out in this variant.\",\n variantUnavailable: \"This combination is unavailable.\",\n stockLeft: \"Only {n} left\",\n soldOut: \"Sold out\",\n productNotFound: \"Product not found.\",\n backToShop: \"Back to the shop\",\n noProducts: \"No products found.\",\n noCategories: \"No categories yet.\",\n allCategories: \"All categories\",\n pay: \"Pay — {bedrag}\",\n busy: \"Working…\",\n checkoutFailed: \"Checkout failed. Check your details and try again.\",\n secureNote: \"You pay securely via Stripe. You will receive a confirmation email after payment.\",\n yourOrder: \"Your order\",\n fieldName: \"Name\",\n fieldEmail: \"Email\",\n fieldPhone: \"Phone (optional)\",\n fieldAddress: \"Street + number\",\n fieldZip: \"Postal code\",\n fieldCity: \"City\",\n fieldNote: \"Note (optional)\",\n choose: \"Choose…\",\n discountCode: \"Discount code\",\n discountApply: \"Apply\",\n discount: \"Discount\",\n discountInvalid: \"This code is not (or no longer) valid.\",\n discountApplied: \"Discount applied: {code}\",\n related: \"You may also like\",\n filterAll: \"All\",\n sortLabel: \"Sort\",\n sortDefault: \"Recommended\",\n sortName: \"Name\",\n sortPriceAsc: \"Price (low-high)\",\n sortPriceDesc: \"Price (high-low)\",\n personalizationUpload: \"Choose file…\",\n personalizationUploading: \"Uploading…\",\n personalizationUploaded: \"✓ Uploaded\",\n personalizationTooBig: \"File too large (max 5 MB).\",\n personalizationFailed: \"Upload failed — please try again.\",\n personalizationSurcharge: \"+ {bedrag}\",\n donationFailed: \"Donation failed. Please try again later.\",\n donationCustomAmount: \"Custom amount\",\n donationAmountLabel: \"Amount in euros (min. {min})\",\n};\n\nexport type ShopStringKey = keyof typeof NL;\nconst TABLES: Record<string, typeof NL> = { nl: NL, en: EN };\n\nexport type ShopT = (key: ShopStringKey, vars?: Record<string, string | number>) => string;\n\n/** Vertaalfunctie voor shop-blocks: `t(\"addToCart\")`, `t(\"stockLeft\", { n: 3 })`. */\nexport function useShopStrings(): ShopT {\n const lang = (useSiteConfig().lang ?? \"nl\").slice(0, 2).toLowerCase();\n const table = TABLES[lang] ?? NL;\n return (key, vars) => {\n let s = table[key] ?? NL[key] ?? key;\n if (vars) for (const [k, v] of Object.entries(vars)) s = s.replaceAll(`{${k}}`, String(v));\n return s;\n };\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Product, ShopCategory } from "../../schema/shop";
|
|
2
|
+
/**
|
|
3
|
+
* Vorm van de server-gefetchte shop-startdata (blockData-namespace "shop") — gedeeld door
|
|
4
|
+
* `server/shop.ts` (producent) en de blocks (consument via `useBlockData("shop")`).
|
|
5
|
+
* Pure types: client- én server-importeerbaar.
|
|
6
|
+
*/
|
|
7
|
+
export interface ShopPageData {
|
|
8
|
+
products?: (Product & {
|
|
9
|
+
id: string;
|
|
10
|
+
})[];
|
|
11
|
+
categories?: (ShopCategory & {
|
|
12
|
+
id: string;
|
|
13
|
+
})[];
|
|
14
|
+
/** Product voor de detailpagina (o.b.v. ?p=); null = expliciet niet gevonden. */
|
|
15
|
+
product?: (Product & {
|
|
16
|
+
id: string;
|
|
17
|
+
}) | null;
|
|
18
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -5,9 +5,14 @@ import type { Page } from "../schema/pages";
|
|
|
5
5
|
* sinds de blokken op Astryx draaien zijn de meeste `"use client"`, en een server-opgebouwde registry
|
|
6
6
|
* krijgt daar alleen opake client-referenties van → die blokken worden stil overgeslagen. Deze client-
|
|
7
7
|
* component zit binnen de provider en heeft dus de volledige registry.
|
|
8
|
+
*
|
|
9
|
+
* `blockData` (5.6, optioneel): server-gefetchte startdata per domein (bv. `{ shop: … }` uit
|
|
10
|
+
* `getShopPageData`). Data-blokken renderen dan direct met inhoud (SSR/ISR) i.p.v. eerst
|
|
11
|
+
* client-side te fetchen. Moet serialiseerbare plain data zijn (server → client-prop).
|
|
8
12
|
*/
|
|
9
|
-
export declare function PublishedPage({ page, siteId, mode, }: {
|
|
13
|
+
export declare function PublishedPage({ page, siteId, mode, blockData, }: {
|
|
10
14
|
page: Pick<Page, "blocks">;
|
|
11
15
|
siteId: string;
|
|
12
16
|
mode?: "view" | "preview";
|
|
17
|
+
blockData?: Record<string, unknown>;
|
|
13
18
|
}): import("react").JSX.Element;
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx } from "react/jsx-runtime";
|
|
3
3
|
import { PageRenderer } from "./PageRenderer";
|
|
4
|
-
import { useRegistry } from "../blocks/context";
|
|
4
|
+
import { useRegistry, BlockDataProvider } from "../blocks/context";
|
|
5
5
|
function PublishedPage({
|
|
6
6
|
page,
|
|
7
7
|
siteId,
|
|
8
|
-
mode = "view"
|
|
8
|
+
mode = "view",
|
|
9
|
+
blockData
|
|
9
10
|
}) {
|
|
10
11
|
const registry = useRegistry();
|
|
11
|
-
return /* @__PURE__ */ jsx(PageRenderer, { page, registry, siteId, mode });
|
|
12
|
+
return /* @__PURE__ */ jsx(BlockDataProvider, { data: blockData, children: /* @__PURE__ */ jsx(PageRenderer, { page, registry, siteId, mode }) });
|
|
12
13
|
}
|
|
13
14
|
export {
|
|
14
15
|
PublishedPage
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/render/PublishedPage.tsx"],"sourcesContent":["\"use client\";\n\nimport { PageRenderer } from \"./PageRenderer\";\nimport { useRegistry } from \"../blocks/context\";\nimport type { Page } from \"../schema/pages\";\n\n/**\n * Rendert een (gepubliceerde) pagina via de **context-registry** (module-resolved, opgebouwd in\n * XiCMSProvider). Gebruik dit i.p.v. zelf `createRegistry([...builtinBlocks])` server-side te bouwen:\n * sinds de blokken op Astryx draaien zijn de meeste `\"use client\"`, en een server-opgebouwde registry\n * krijgt daar alleen opake client-referenties van → die blokken worden stil overgeslagen. Deze client-\n * component zit binnen de provider en heeft dus de volledige registry.\n */\nexport function PublishedPage({\n page,\n siteId,\n mode = \"view\",\n}: {\n page: Pick<Page, \"blocks\">;\n siteId: string;\n mode?: \"view\" | \"preview\";\n}) {\n const registry = useRegistry();\n return <PageRenderer page={page} registry={registry} siteId={siteId} mode={mode}
|
|
1
|
+
{"version":3,"sources":["../../src/render/PublishedPage.tsx"],"sourcesContent":["\"use client\";\n\nimport { PageRenderer } from \"./PageRenderer\";\nimport { useRegistry, BlockDataProvider } from \"../blocks/context\";\nimport type { Page } from \"../schema/pages\";\n\n/**\n * Rendert een (gepubliceerde) pagina via de **context-registry** (module-resolved, opgebouwd in\n * XiCMSProvider). Gebruik dit i.p.v. zelf `createRegistry([...builtinBlocks])` server-side te bouwen:\n * sinds de blokken op Astryx draaien zijn de meeste `\"use client\"`, en een server-opgebouwde registry\n * krijgt daar alleen opake client-referenties van → die blokken worden stil overgeslagen. Deze client-\n * component zit binnen de provider en heeft dus de volledige registry.\n *\n * `blockData` (5.6, optioneel): server-gefetchte startdata per domein (bv. `{ shop: … }` uit\n * `getShopPageData`). Data-blokken renderen dan direct met inhoud (SSR/ISR) i.p.v. eerst\n * client-side te fetchen. Moet serialiseerbare plain data zijn (server → client-prop).\n */\nexport function PublishedPage({\n page,\n siteId,\n mode = \"view\",\n blockData,\n}: {\n page: Pick<Page, \"blocks\">;\n siteId: string;\n mode?: \"view\" | \"preview\";\n blockData?: Record<string, unknown>;\n}) {\n const registry = useRegistry();\n return (\n <BlockDataProvider data={blockData}>\n <PageRenderer page={page} registry={registry} siteId={siteId} mode={mode} />\n </BlockDataProvider>\n );\n}\n"],"mappings":";AA+BM;AA7BN,SAAS,oBAAoB;AAC7B,SAAS,aAAa,yBAAyB;AAcxC,SAAS,cAAc;AAAA,EAC5B;AAAA,EACA;AAAA,EACA,OAAO;AAAA,EACP;AACF,GAKG;AACD,QAAM,WAAW,YAAY;AAC7B,SACE,oBAAC,qBAAkB,MAAM,WACvB,8BAAC,gBAAa,MAAY,UAAoB,QAAgB,MAAY,GAC5E;AAEJ;","names":[]}
|