@xiwebdesign/xicms-core 7.17.1 → 7.18.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/settings/MailSettings.js +41 -0
- package/dist/admin/settings/MailSettings.js.map +1 -1
- package/dist/admin/shop/CategoriesTab.js +27 -5
- package/dist/admin/shop/CategoriesTab.js.map +1 -1
- package/dist/admin/shop/DiscountsTab.js +9 -1
- package/dist/admin/shop/DiscountsTab.js.map +1 -1
- package/dist/admin/shop/OrdersTab.js +155 -9
- package/dist/admin/shop/OrdersTab.js.map +1 -1
- package/dist/admin/shop/ProductsTab.js +10 -1
- package/dist/admin/shop/ProductsTab.js.map +1 -1
- package/dist/admin.css +1 -1
- package/dist/schema/shop.d.ts +11 -0
- package/dist/schema/shop.js +27 -2
- package/dist/schema/shop.js.map +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/admin/shop/ProductsTab.tsx"],"sourcesContent":["\"use client\";\n\nimport { useEffect, useMemo, useState } from \"react\";\nimport { collection, doc, onSnapshot } from \"firebase/firestore\";\nimport { removeProduct, saveProductDoc } from \"./productClient\";\nimport { getDb } from \"../../firebase/client\";\nimport {\n ProductSchema,\n formatCents,\n saneerProductDoc,\n type PersonalizationField,\n type Product,\n type ProductOption,\n type ProductVariant,\n type ShopCategory,\n} from \"../../schema/shop\";\nimport { Badge, Button, Card, Field, Input, Textarea } from \"../ui/primitives\";\nimport { Select } from \"../ui/select\";\nimport { Switch } from \"../ui/switch\";\nimport { useToast } from \"../ui/toast\";\nimport { useConfirm, type ConfirmOptions } from \"../ui/ConfirmDialog\";\nimport { MediaPickerButton } from \"../media/MediaPicker\";\nimport { slugify } from \"../lib/slugify\";\nimport { allVariantKeys, centsToEuroInput, eurosToCents } from \"./lib\";\n\ntype StoredProduct = Product & { id: string };\ntype StoredCategory = ShopCategory & { id: string };\n\nconst STATUS_OPTIONS = [\n { value: \"draft\", label: \"Concept\" },\n { value: \"active\", label: \"Actief\" },\n { value: \"archived\", label: \"Gearchiveerd\" },\n];\n\nexport function ProductsTab({ siteId }: { siteId: string }) {\n const { toast } = useToast();\n const confirm = useConfirm();\n const [products, setProducts] = useState<StoredProduct[]>([]);\n const [cats, setCats] = useState<StoredCategory[]>([]);\n const [selected, setSelected] = useState<string | null>(null);\n const [q, setQ] = useState(\"\");\n\n useEffect(() => {\n const db = getDb();\n if (!db) return;\n const un1 = onSnapshot(collection(db, \"sites\", siteId, \"products\"), (snap) =>\n setProducts(\n snap.docs\n // saneerProductDoc: historische docs dragen null-velden (zie schema/shop.ts) — schoon\n // ze bij het lezen, zodat de form-state ze niet via {...def} opnieuw naar de server lekt.\n .map((d) => ({ id: d.id, ...saneerProductDoc(d.data() as Product) }))\n .sort((a, b) => (a.order ?? 0) - (b.order ?? 0) || a.name.localeCompare(b.name, \"nl\")),\n ),\n );\n const un2 = onSnapshot(collection(db, \"sites\", siteId, \"shopCategories\"), (snap) =>\n setCats(snap.docs.map((d) => ({ id: d.id, ...(d.data() as ShopCategory) }))),\n );\n return () => {\n un1();\n un2();\n };\n }, [siteId]);\n\n async function createProduct() {\n // Ook aanmaken loopt via de callable: een vers product is een draft (onzichtbaar voor de\n // cache), maar zo staat élke productmutatie in de audit-log en is er één pad i.p.v. twee.\n const nieuw = {\n name: \"Nieuw product\",\n slug: `product-${products.length + 1}`,\n description: \"\",\n categoryIds: [],\n images: [],\n priceCents: 0,\n vatRate: 21,\n options: [],\n variants: [],\n trackStock: false,\n status: \"draft\",\n order: products.length,\n featured: false,\n } satisfies Product;\n try {\n const id = await saveProductDoc(siteId, undefined, nieuw);\n setSelected(id);\n } catch (e) {\n // Zichtbaar falen: dit gooide een tijdlang stil (unhandled) terwijl de knop gewoon niets\n // leek te doen — precies de melding \"bij aanmaken product krijg ik iets met permissions\".\n toast({ title: \"Product aanmaken mislukt\", description: e instanceof Error ? e.message : undefined, tone: \"error\" });\n }\n }\n\n const filtered = useMemo(() => {\n const needle = q.trim().toLowerCase();\n if (!needle) return products;\n return products.filter((p) => p.name.toLowerCase().includes(needle) || p.slug.includes(needle));\n }, [products, q]);\n\n const current = products.find((p) => p.id === selected) ?? null;\n\n return (\n <div className=\"grid gap-4 md:grid-cols-[300px_1fr]\">\n <Card className=\"h-fit\">\n <div className=\"mb-3 flex items-center justify-between\">\n <h3 className=\"text-sm font-semibold\">Producten</h3>\n <Button size=\"sm\" onClick={createProduct}>+ Nieuw</Button>\n </div>\n <Input placeholder=\"Zoeken…\" value={q} onChange={(e) => setQ(e.target.value)} className=\"mb-2\" />\n <ul className=\"grid max-h-[60vh] gap-1 overflow-y-auto\">\n {filtered.map((p) => (\n <li key={p.id}>\n <button\n onClick={() => setSelected(p.id)}\n className={`flex w-full items-center gap-2 rounded-lg px-3 py-2 text-left text-sm ${\n p.id === selected ? \"bg-brand-50 text-brand-700\" : \"hover:bg-slate-100\"\n }`}\n >\n {p.images?.[0] && <img src={p.images[0]} alt=\"\" className=\"h-8 w-8 rounded object-cover\" />}\n <span className=\"min-w-0 flex-1 truncate\">{p.name}</span>\n <Badge tone={p.status === \"active\" ? \"green\" : p.status === \"draft\" ? \"amber\" : \"slate\"}>\n {STATUS_OPTIONS.find((s) => s.value === p.status)?.label ?? p.status}\n </Badge>\n </button>\n </li>\n ))}\n {filtered.length === 0 && <li className=\"px-3 py-2 text-sm text-slate-500\">Geen producten.</li>}\n </ul>\n </Card>\n {current ? (\n <ProductForm\n key={current.id}\n siteId={siteId}\n product={current}\n categories={cats}\n onDeleted={() => setSelected(null)}\n onToast={toast}\n confirm={confirm}\n />\n ) : (\n <Card>\n <p className=\"text-sm text-slate-500\">Selecteer of maak een product.</p>\n </Card>\n )}\n </div>\n );\n}\n\n/**\n * Het volledige productformulier. Sinds 7.8 ook buiten dit tabblad in gebruik: het tandwiel bij een\n * product op de pagina opent hetzelfde formulier in een zijpaneel. Bewust hergebruik en geen tweede,\n * uitgeklede versie — twee formulieren voor één product lopen gegarandeerd uit elkaar, en dan hangt\n * het van je route af welke velden je kunt bereiken.\n */\nexport function ProductForm({\n siteId,\n product,\n categories,\n onDeleted,\n onToast,\n confirm,\n}: {\n siteId: string;\n product: StoredProduct;\n categories: StoredCategory[];\n onDeleted: () => void;\n onToast: (t: { title: string; description?: string; tone?: \"default\" | \"success\" | \"error\" }) => void;\n confirm: (opts: ConfirmOptions) => Promise<boolean>;\n}) {\n const [def, setDef] = useState<Product>(product);\n const [priceInput, setPriceInput] = useState(centsToEuroInput(product.priceCents));\n const [compareInput, setCompareInput] = useState(centsToEuroInput(product.compareAtCents));\n\n const variantKeys = useMemo(() => allVariantKeys(def.options ?? []), [def.options]);\n\n function setOption(i: number, patch: Partial<ProductOption>) {\n setDef((p) => {\n const options = [...(p.options ?? [])];\n options[i] = { ...options[i], ...patch };\n return { ...p, options };\n });\n }\n\n function variantFor(key: string): ProductVariant | undefined {\n return (def.variants ?? []).find((v) => v.key === key);\n }\n\n function patchVariant(key: string, patch: Partial<ProductVariant>) {\n setDef((p) => {\n const variants = [...(p.variants ?? [])];\n const idx = variants.findIndex((v) => v.key === key);\n if (idx >= 0) variants[idx] = { ...variants[idx], ...patch };\n else variants.push({ key, active: true, ...patch });\n return { ...p, variants };\n });\n }\n\n async function save() {\n const db = getDb();\n if (!db) return;\n // Alleen varianten bewaren die iets toevoegen (afwijkende prijs/voorraad/sku of inactief),\n // en geen wees-varianten van verwijderde optiewaarden.\n const validKeys = new Set(variantKeys);\n const cleanVariants = (def.variants ?? []).filter(\n (v) =>\n validKeys.has(v.key) &&\n (v.priceCents !== undefined || v.stock !== undefined || v.sku !== undefined || v.active === false),\n );\n // Onvolledige personalisatie-regels (zonder label) niet opslaan.\n const cleanPersonalization = (def.personalization ?? []).filter((f) => f.label.trim().length > 0);\n const parsed = ProductSchema.safeParse({\n ...def,\n variants: cleanVariants,\n personalization: cleanPersonalization.length > 0 ? cleanPersonalization : undefined,\n });\n if (!parsed.success) {\n onToast({ title: \"Ongeldig product\", description: parsed.error.issues[0]?.message, tone: \"error\" });\n return;\n }\n // Via de callable (7.9): de server valideert, auditeert en — belangrijker — pingt de\n // products-cachetag, zodat de wijziging ook op de LIVE shoppagina's staat en niet alleen in\n // Firestore. Zie productClient.ts, ook voor de terugval op een oudere backend.\n await saveProductDoc(siteId, product.id, parsed.data);\n onToast({ title: \"Product opgeslagen\", tone: \"success\" });\n }\n\n async function remove() {\n const ok = await confirm({\n title: `\"${product.name}\" verwijderen?`,\n description: \"Bestaande bestellingen behouden hun eigen kopie van naam en prijs.\",\n tone: \"danger\",\n });\n if (!ok) return;\n await removeProduct(siteId, product.id);\n onDeleted();\n }\n\n return (\n <Card>\n <div className=\"grid gap-4\">\n <div className=\"grid gap-4 md:grid-cols-2\">\n <Field label=\"Naam\">\n <Input\n value={def.name}\n onChange={(e) => setDef((p) => ({ ...p, name: e.target.value, slug: slugify(e.target.value) || p.slug }))}\n />\n </Field>\n <Field label=\"Slug\" hint=\"Product-URL: /product?p=<slug>\">\n <Input value={def.slug} onChange={(e) => setDef((p) => ({ ...p, slug: slugify(e.target.value) }))} />\n </Field>\n </div>\n\n <div className=\"grid gap-4 md:grid-cols-3\">\n <Field label=\"Prijs (€, incl. btw)\">\n <Input\n value={priceInput}\n onChange={(e) => {\n setPriceInput(e.target.value);\n const c = eurosToCents(e.target.value);\n if (c !== null) setDef((p) => ({ ...p, priceCents: c }));\n }}\n />\n </Field>\n <Field label=\"Van-prijs (€, optioneel)\" hint=\"Doorgestreept naast de prijs\">\n <Input\n value={compareInput}\n onChange={(e) => {\n setCompareInput(e.target.value);\n const c = eurosToCents(e.target.value);\n setDef((p) => ({ ...p, compareAtCents: c ?? undefined }));\n }}\n />\n </Field>\n <Field label=\"Btw-tarief\">\n <Select\n value={String(def.vatRate ?? 21)}\n onValueChange={(v) => setDef((p) => ({ ...p, vatRate: Number(v) as 21 | 9 | 0 }))}\n options={[\n { value: \"21\", label: \"21%\" },\n { value: \"9\", label: \"9%\" },\n { value: \"0\", label: \"0%\" },\n ]}\n />\n </Field>\n </div>\n\n <Field label=\"Omschrijving\">\n <Textarea rows={4} value={def.description ?? \"\"} onChange={(e) => setDef((p) => ({ ...p, description: e.target.value }))} />\n </Field>\n\n <Field label=\"Afbeeldingen\" hint=\"Eerste afbeelding is het hoofdbeeld\">\n <div className=\"flex flex-wrap items-center gap-3\">\n {(def.images ?? []).map((url, i) => (\n <div key={url + i} className=\"relative\">\n <img src={url} alt=\"\" className=\"h-16 w-16 rounded-lg object-cover\" />\n <button\n type=\"button\"\n aria-label=\"Verwijder afbeelding\"\n className=\"absolute -right-2 -top-2 grid h-5 w-5 place-items-center rounded-full bg-slate-700 text-xs text-white\"\n onClick={() => setDef((p) => ({ ...p, images: (p.images ?? []).filter((_, j) => j !== i) }))}\n >\n ✕\n </button>\n </div>\n ))}\n <MediaPickerButton\n siteId={siteId}\n label=\"+ Afbeelding\"\n onSelect={(m) => setDef((p) => ({ ...p, images: [...(p.images ?? []), m.url] }))}\n />\n </div>\n </Field>\n\n <Field label=\"Categorieën\">\n <div className=\"flex flex-wrap gap-2\">\n {categories.map((c) => {\n const on = (def.categoryIds ?? []).includes(c.id);\n return (\n <button\n key={c.id}\n type=\"button\"\n onClick={() =>\n setDef((p) => ({\n ...p,\n categoryIds: on ? (p.categoryIds ?? []).filter((x) => x !== c.id) : [...(p.categoryIds ?? []), c.id],\n }))\n }\n className={`rounded-full border px-3 py-1 text-sm ${\n on ? \"border-brand-500 bg-brand-50 text-brand-700\" : \"border-slate-200 hover:bg-slate-50\"\n }`}\n >\n {on ? \"✓ \" : \"\"}\n {c.name}\n </button>\n );\n })}\n {categories.length === 0 && <span className=\"text-sm text-slate-500\">Maak eerst een categorie aan.</span>}\n </div>\n </Field>\n\n {/* Opties & varianten */}\n <Field\n label=\"Opties (bv. Maat, Kleur)\"\n hint=\"Max. 3. Waarden gescheiden door komma's. Combinaties kun je hieronder per stuk aanpassen.\"\n >\n <div className=\"grid gap-2\">\n {(def.options ?? []).map((opt, i) => (\n <div key={i} className=\"grid gap-2 rounded-lg border border-slate-100 p-2\">\n <div className=\"flex items-center gap-2\">\n <Input\n className=\"w-36\"\n placeholder=\"Naam (bv. Maat)\"\n value={opt.name}\n onChange={(e) => setOption(i, { name: e.target.value })}\n />\n <Input\n placeholder=\"Waarden, bv: 56, 62, 68, 74\"\n value={opt.values.join(\", \")}\n onChange={(e) =>\n setOption(i, { values: e.target.value.split(\",\").map((s) => s.trim()).filter(Boolean) })\n }\n />\n <Button\n size=\"sm\"\n variant=\"ghost\"\n onClick={() => setDef((p) => ({ ...p, options: (p.options ?? []).filter((_, j) => j !== i) }))}\n >\n ✕\n </Button>\n </div>\n {/* Afbeelding per waarde (5.6) — vooral voor de kleur-as: de detailpagina\n wisselt het hoofdbeeld mee met de gekozen kleur. */}\n {/^kleur|^colou?r/i.test(opt.name) && opt.values.length > 0 && (\n <div className=\"flex flex-wrap items-center gap-2\">\n {opt.values.map((value) => {\n const url = opt.images?.[value];\n return (\n <span key={value} className=\"flex items-center gap-1 rounded-full border border-slate-200 px-2 py-1 text-xs\">\n {url && <img src={url} alt=\"\" className=\"h-6 w-6 rounded object-cover\" />}\n {value}\n <MediaPickerButton\n siteId={siteId}\n label={url ? \"↻\" : \"+ foto\"}\n onSelect={(m) =>\n setOption(i, { images: { ...(opt.images ?? {}), [value]: m.url } })\n }\n />\n {url && (\n <button\n type=\"button\"\n aria-label={`Verwijder foto van ${value}`}\n className=\"text-slate-400 hover:text-slate-700\"\n onClick={() => {\n const images = { ...(opt.images ?? {}) };\n delete images[value];\n setOption(i, { images });\n }}\n >\n ✕\n </button>\n )}\n </span>\n );\n })}\n </div>\n )}\n </div>\n ))}\n {(def.options ?? []).length < 3 && (\n <Button\n size=\"sm\"\n variant=\"secondary\"\n className=\"w-fit\"\n onClick={() => setDef((p) => ({ ...p, options: [...(p.options ?? []), { name: \"\", values: [] }] }))}\n >\n + Optie\n </Button>\n )}\n </div>\n </Field>\n\n {variantKeys.length > 0 && variantKeys.length <= 60 && (\n <Field label=\"Varianten\" hint=\"Leeg = standaardprijs / onbeperkte voorraad\">\n <div className=\"overflow-x-auto\">\n <table className=\"w-full text-sm\">\n <thead>\n <tr className=\"text-left text-xs text-slate-500\">\n <th className=\"py-1 pr-2\">Combinatie</th>\n <th className=\"py-1 pr-2\">Prijs (€)</th>\n {def.trackStock && <th className=\"py-1 pr-2\">Voorraad</th>}\n <th className=\"py-1 pr-2\">Leverbaar</th>\n </tr>\n </thead>\n <tbody>\n {variantKeys.map((key) => {\n const v = variantFor(key);\n return (\n <tr key={key} className=\"border-t border-slate-100\">\n <td className=\"py-1 pr-2\">{key.split(\"|\").join(\" · \")}</td>\n <td className=\"py-1 pr-2\">\n {/* Native uncontrolled inputs: geen cursor-gedoe bij \"13,\" tijdens typen. */}\n <input\n className=\"w-24 rounded-lg border border-slate-200 px-2 py-1\"\n placeholder={centsToEuroInput(def.priceCents)}\n defaultValue={centsToEuroInput(v?.priceCents)}\n onChange={(e) => {\n const c = eurosToCents(e.target.value);\n patchVariant(key, { priceCents: c ?? undefined });\n }}\n />\n </td>\n {def.trackStock && (\n <td className=\"py-1 pr-2\">\n <input\n className=\"w-20 rounded-lg border border-slate-200 px-2 py-1\"\n type=\"number\"\n min={0}\n placeholder=\"∞\"\n defaultValue={v?.stock ?? \"\"}\n onChange={(e) => {\n const n = e.target.value === \"\" ? undefined : Math.max(0, Number(e.target.value) || 0);\n patchVariant(key, { stock: n });\n }}\n />\n </td>\n )}\n <td className=\"py-1 pr-2\">\n <Switch checked={v?.active !== false} onCheckedChange={(on) => patchVariant(key, { active: on })} />\n </td>\n </tr>\n );\n })}\n </tbody>\n </table>\n </div>\n </Field>\n )}\n {variantKeys.length > 60 && (\n <p className=\"text-sm text-amber-600\">\n {variantKeys.length} combinaties — te veel om per stuk te tonen. Beperk het aantal optie-waarden.\n </p>\n )}\n\n {/* Personalisatie (5.6): eigen naam/logo van de klant, met optionele meerprijs. */}\n <Field\n label=\"Personalisatie (eigen tekst of afbeelding)\"\n hint=\"Max. 3 velden. De klant vult dit in op de productpagina; de meerprijs telt automatisch mee.\"\n >\n <div className=\"grid gap-2\">\n {(def.personalization ?? []).map((f, i) => {\n const setPers = (patch: Partial<PersonalizationField>) =>\n setDef((p) => {\n const personalization = [...(p.personalization ?? [])];\n personalization[i] = { ...personalization[i], ...patch };\n return { ...p, personalization };\n });\n return (\n <div key={i} className=\"grid gap-2 rounded-lg border border-slate-100 p-2 md:grid-cols-[1fr_130px_90px_110px_auto]\">\n <Input\n placeholder='Label (bv. \"Naam van het kindje\")'\n value={f.label}\n onChange={(e) =>\n setPers({\n label: e.target.value,\n key: f.key || e.target.value.toLowerCase().replace(/[^a-z0-9]+/g, \"_\").replace(/^_+|_+$/g, \"\").slice(0, 40) || `veld${i + 1}`,\n })\n }\n />\n <Select\n value={f.type}\n onValueChange={(v) => setPers({ type: v as \"text\" | \"image\" })}\n options={[\n { value: \"text\", label: \"Tekst\" },\n { value: \"image\", label: \"Afbeelding\" },\n ]}\n />\n {f.type === \"text\" ? (\n <Input\n type=\"number\"\n title=\"Max. tekens\"\n min={1}\n max={200}\n value={f.maxLength ?? 40}\n onChange={(e) => setPers({ maxLength: Math.max(1, Math.min(200, Number(e.target.value) || 40)) })}\n />\n ) : (\n <span className=\"self-center text-xs text-slate-400\">jpg/png/webp</span>\n )}\n <input\n className=\"rounded-lg border border-slate-200 px-2 py-1\"\n placeholder=\"Meerprijs €\"\n defaultValue={f.priceCents ? centsToEuroInput(f.priceCents) : \"\"}\n onChange={(e) => setPers({ priceCents: eurosToCents(e.target.value) ?? 0 })}\n />\n <div className=\"flex items-center gap-2\">\n <Switch checked={f.required === true} onCheckedChange={(v) => setPers({ required: v })} label=\"Verplicht\" />\n <Button\n size=\"sm\"\n variant=\"ghost\"\n onClick={() => setDef((p) => ({ ...p, personalization: (p.personalization ?? []).filter((_, j) => j !== i) }))}\n >\n ✕\n </Button>\n </div>\n </div>\n );\n })}\n {(def.personalization ?? []).length < 3 && (\n <Button\n size=\"sm\"\n variant=\"secondary\"\n className=\"w-fit\"\n onClick={() =>\n setDef((p) => ({\n ...p,\n personalization: [\n ...(p.personalization ?? []),\n { key: `veld${(p.personalization ?? []).length + 1}`, label: \"\", type: \"text\", required: false, maxLength: 40, priceCents: 0 },\n ],\n }))\n }\n >\n + Personalisatie-veld\n </Button>\n )}\n </div>\n </Field>\n\n <div className=\"flex flex-wrap items-center gap-6\">\n <Field label=\"Status\">\n <Select\n value={def.status ?? \"draft\"}\n onValueChange={(v) => setDef((p) => ({ ...p, status: v as Product[\"status\"] }))}\n options={STATUS_OPTIONS}\n />\n </Field>\n <Field label=\"Volgorde\">\n <Input\n type=\"number\"\n className=\"w-24\"\n value={def.order ?? 0}\n onChange={(e) => setDef((p) => ({ ...p, order: Math.max(0, Number(e.target.value) || 0) }))}\n />\n </Field>\n <Switch checked={def.featured === true} onCheckedChange={(v) => setDef((p) => ({ ...p, featured: v }))} label=\"Uitgelicht\" />\n <Switch checked={def.trackStock === true} onCheckedChange={(v) => setDef((p) => ({ ...p, trackStock: v }))} label=\"Voorraad bijhouden\" />\n {def.trackStock && (def.options ?? []).length === 0 && (\n <Field label=\"Voorraad\">\n <Input\n type=\"number\"\n className=\"w-24\"\n placeholder=\"∞\"\n value={def.stock ?? \"\"}\n onChange={(e) =>\n setDef((p) => ({ ...p, stock: e.target.value === \"\" ? undefined : Math.max(0, Number(e.target.value) || 0) }))\n }\n />\n </Field>\n )}\n </div>\n\n <div className=\"flex items-center justify-between border-t border-slate-100 pt-4\">\n <div className=\"flex items-center gap-3\">\n <Button onClick={save}>Opslaan</Button>\n <span className=\"text-sm text-slate-500\">{formatCents(def.priceCents)}</span>\n </div>\n <Button variant=\"danger\" onClick={remove}>Verwijderen</Button>\n </div>\n </div>\n </Card>\n );\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../src/admin/shop/ProductsTab.tsx"],"sourcesContent":["\"use client\";\n\nimport { useEffect, useMemo, useState } from \"react\";\nimport { collection, doc, onSnapshot } from \"firebase/firestore\";\nimport { removeProduct, saveProductDoc } from \"./productClient\";\nimport { getDb } from \"../../firebase/client\";\nimport {\n ProductSchema,\n formatCents,\n saneerProductDoc,\n type PersonalizationField,\n type Product,\n type ProductOption,\n type ProductVariant,\n type ShopCategory,\n} from \"../../schema/shop\";\nimport { Badge, Button, Card, Field, Input, Textarea } from \"../ui/primitives\";\nimport { Select } from \"../ui/select\";\nimport { Switch } from \"../ui/switch\";\nimport { useToast } from \"../ui/toast\";\nimport { useConfirm, type ConfirmOptions } from \"../ui/ConfirmDialog\";\nimport { MediaPickerButton } from \"../media/MediaPicker\";\nimport { slugify } from \"../lib/slugify\";\nimport { allVariantKeys, centsToEuroInput, eurosToCents } from \"./lib\";\n\ntype StoredProduct = Product & { id: string };\ntype StoredCategory = ShopCategory & { id: string };\n\nconst STATUS_OPTIONS = [\n { value: \"draft\", label: \"Concept\" },\n { value: \"active\", label: \"Actief\" },\n { value: \"archived\", label: \"Gearchiveerd\" },\n];\n\nexport function ProductsTab({ siteId }: { siteId: string }) {\n const { toast } = useToast();\n const confirm = useConfirm();\n const [products, setProducts] = useState<StoredProduct[]>([]);\n const [cats, setCats] = useState<StoredCategory[]>([]);\n const [selected, setSelected] = useState<string | null>(null);\n const [q, setQ] = useState(\"\");\n\n useEffect(() => {\n const db = getDb();\n if (!db) return;\n const un1 = onSnapshot(collection(db, \"sites\", siteId, \"products\"), (snap) =>\n setProducts(\n snap.docs\n // saneerProductDoc: historische docs dragen null-velden (zie schema/shop.ts) — schoon\n // ze bij het lezen, zodat de form-state ze niet via {...def} opnieuw naar de server lekt.\n .map((d) => ({ id: d.id, ...saneerProductDoc(d.data() as Product) }))\n .sort((a, b) => (a.order ?? 0) - (b.order ?? 0) || a.name.localeCompare(b.name, \"nl\")),\n ),\n );\n const un2 = onSnapshot(collection(db, \"sites\", siteId, \"shopCategories\"), (snap) =>\n setCats(snap.docs.map((d) => ({ id: d.id, ...(d.data() as ShopCategory) }))),\n );\n return () => {\n un1();\n un2();\n };\n }, [siteId]);\n\n async function createProduct() {\n // Ook aanmaken loopt via de callable: een vers product is een draft (onzichtbaar voor de\n // cache), maar zo staat élke productmutatie in de audit-log en is er één pad i.p.v. twee.\n const nieuw = {\n name: \"Nieuw product\",\n slug: `product-${products.length + 1}`,\n description: \"\",\n categoryIds: [],\n images: [],\n priceCents: 0,\n vatRate: 21,\n options: [],\n variants: [],\n trackStock: false,\n status: \"draft\",\n order: products.length,\n featured: false,\n } satisfies Product;\n try {\n const id = await saveProductDoc(siteId, undefined, nieuw);\n setSelected(id);\n } catch (e) {\n // Zichtbaar falen: dit gooide een tijdlang stil (unhandled) terwijl de knop gewoon niets\n // leek te doen — precies de melding \"bij aanmaken product krijg ik iets met permissions\".\n toast({ title: \"Product aanmaken mislukt\", description: e instanceof Error ? e.message : undefined, tone: \"error\" });\n }\n }\n\n const filtered = useMemo(() => {\n const needle = q.trim().toLowerCase();\n if (!needle) return products;\n return products.filter((p) => p.name.toLowerCase().includes(needle) || p.slug.includes(needle));\n }, [products, q]);\n\n const current = products.find((p) => p.id === selected) ?? null;\n\n return (\n <div className=\"grid gap-4 md:grid-cols-[300px_1fr]\">\n <Card className=\"h-fit\">\n <div className=\"mb-3 flex items-center justify-between\">\n <h3 className=\"text-sm font-semibold\">Producten</h3>\n <Button size=\"sm\" onClick={createProduct}>+ Nieuw</Button>\n </div>\n <Input placeholder=\"Zoeken…\" value={q} onChange={(e) => setQ(e.target.value)} className=\"mb-2\" />\n <ul className=\"grid max-h-[60vh] gap-1 overflow-y-auto\">\n {filtered.map((p) => (\n <li key={p.id}>\n <button\n onClick={() => setSelected(p.id)}\n className={`flex w-full items-center gap-2 rounded-lg px-3 py-2 text-left text-sm ${\n p.id === selected ? \"bg-brand-50 text-brand-700\" : \"hover:bg-slate-100\"\n }`}\n >\n {p.images?.[0] && <img src={p.images[0]} alt=\"\" className=\"h-8 w-8 rounded object-cover\" />}\n <span className=\"min-w-0 flex-1 truncate\">{p.name}</span>\n <Badge tone={p.status === \"active\" ? \"green\" : p.status === \"draft\" ? \"amber\" : \"slate\"}>\n {STATUS_OPTIONS.find((s) => s.value === p.status)?.label ?? p.status}\n </Badge>\n </button>\n </li>\n ))}\n {filtered.length === 0 && <li className=\"px-3 py-2 text-sm text-slate-500\">Geen producten.</li>}\n </ul>\n </Card>\n {current ? (\n <ProductForm\n key={current.id}\n siteId={siteId}\n product={current}\n categories={cats}\n onDeleted={() => setSelected(null)}\n onToast={toast}\n confirm={confirm}\n />\n ) : (\n <Card>\n <p className=\"text-sm text-slate-500\">Selecteer of maak een product.</p>\n </Card>\n )}\n </div>\n );\n}\n\n/**\n * Het volledige productformulier. Sinds 7.8 ook buiten dit tabblad in gebruik: het tandwiel bij een\n * product op de pagina opent hetzelfde formulier in een zijpaneel. Bewust hergebruik en geen tweede,\n * uitgeklede versie — twee formulieren voor één product lopen gegarandeerd uit elkaar, en dan hangt\n * het van je route af welke velden je kunt bereiken.\n */\nexport function ProductForm({\n siteId,\n product,\n categories,\n onDeleted,\n onToast,\n confirm,\n}: {\n siteId: string;\n product: StoredProduct;\n categories: StoredCategory[];\n onDeleted: () => void;\n onToast: (t: { title: string; description?: string; tone?: \"default\" | \"success\" | \"error\" }) => void;\n confirm: (opts: ConfirmOptions) => Promise<boolean>;\n}) {\n const [def, setDef] = useState<Product>(product);\n const [priceInput, setPriceInput] = useState(centsToEuroInput(product.priceCents));\n const [compareInput, setCompareInput] = useState(centsToEuroInput(product.compareAtCents));\n\n const variantKeys = useMemo(() => allVariantKeys(def.options ?? []), [def.options]);\n\n function setOption(i: number, patch: Partial<ProductOption>) {\n setDef((p) => {\n const options = [...(p.options ?? [])];\n options[i] = { ...options[i], ...patch };\n return { ...p, options };\n });\n }\n\n function variantFor(key: string): ProductVariant | undefined {\n return (def.variants ?? []).find((v) => v.key === key);\n }\n\n function patchVariant(key: string, patch: Partial<ProductVariant>) {\n setDef((p) => {\n const variants = [...(p.variants ?? [])];\n const idx = variants.findIndex((v) => v.key === key);\n if (idx >= 0) variants[idx] = { ...variants[idx], ...patch };\n else variants.push({ key, active: true, ...patch });\n return { ...p, variants };\n });\n }\n\n async function save() {\n const db = getDb();\n if (!db) return;\n // Alleen varianten bewaren die iets toevoegen (afwijkende prijs/voorraad/sku of inactief),\n // en geen wees-varianten van verwijderde optiewaarden.\n const validKeys = new Set(variantKeys);\n const cleanVariants = (def.variants ?? []).filter(\n (v) =>\n validKeys.has(v.key) &&\n (v.priceCents !== undefined || v.stock !== undefined || v.sku !== undefined || v.active === false),\n );\n // Onvolledige personalisatie-regels (zonder label) niet opslaan.\n const cleanPersonalization = (def.personalization ?? []).filter((f) => f.label.trim().length > 0);\n const parsed = ProductSchema.safeParse({\n ...def,\n variants: cleanVariants,\n personalization: cleanPersonalization.length > 0 ? cleanPersonalization : undefined,\n });\n if (!parsed.success) {\n onToast({ title: \"Ongeldig product\", description: parsed.error.issues[0]?.message, tone: \"error\" });\n return;\n }\n // Via de callable (7.9): de server valideert, auditeert en — belangrijker — pingt de\n // products-cachetag, zodat de wijziging ook op de LIVE shoppagina's staat en niet alleen in\n // Firestore. Zie productClient.ts, ook voor de terugval op een oudere backend.\n await saveProductDoc(siteId, product.id, parsed.data);\n onToast({ title: \"Product opgeslagen\", tone: \"success\" });\n }\n\n async function remove() {\n const ok = await confirm({\n title: `\"${product.name}\" verwijderen?`,\n description: \"Bestaande bestellingen behouden hun eigen kopie van naam en prijs.\",\n tone: \"danger\",\n });\n if (!ok) return;\n await removeProduct(siteId, product.id);\n onDeleted();\n }\n\n return (\n <Card>\n <div className=\"grid gap-4\">\n <div className=\"grid gap-4 md:grid-cols-2\">\n <Field label=\"Naam\">\n <Input\n value={def.name}\n onChange={(e) => setDef((p) => ({ ...p, name: e.target.value, slug: slugify(e.target.value) || p.slug }))}\n />\n </Field>\n <Field label=\"Slug\" hint=\"Product-URL: /product?p=<slug>\">\n <Input value={def.slug} onChange={(e) => setDef((p) => ({ ...p, slug: slugify(e.target.value) }))} />\n </Field>\n </div>\n\n <div className=\"grid gap-4 md:grid-cols-3\">\n <Field label=\"Prijs (€, incl. btw)\">\n <Input\n value={priceInput}\n onChange={(e) => {\n setPriceInput(e.target.value);\n const c = eurosToCents(e.target.value);\n if (c !== null) setDef((p) => ({ ...p, priceCents: c }));\n }}\n />\n </Field>\n <Field label=\"Van-prijs (€, optioneel)\" hint=\"Doorgestreept naast de prijs\">\n <Input\n value={compareInput}\n onChange={(e) => {\n setCompareInput(e.target.value);\n const c = eurosToCents(e.target.value);\n setDef((p) => ({ ...p, compareAtCents: c ?? undefined }));\n }}\n />\n </Field>\n <Field label=\"Btw-tarief\">\n <Select\n value={String(def.vatRate ?? 21)}\n onValueChange={(v) => setDef((p) => ({ ...p, vatRate: Number(v) as 21 | 9 | 0 }))}\n options={[\n { value: \"21\", label: \"21%\" },\n { value: \"9\", label: \"9%\" },\n { value: \"0\", label: \"0%\" },\n ]}\n />\n </Field>\n </div>\n\n <Field label=\"Omschrijving\">\n <Textarea rows={4} value={def.description ?? \"\"} onChange={(e) => setDef((p) => ({ ...p, description: e.target.value }))} />\n </Field>\n\n <Field label=\"Afbeeldingen\" hint=\"Eerste afbeelding is het hoofdbeeld\">\n <div className=\"flex flex-wrap items-center gap-3\">\n {(def.images ?? []).map((url, i) => (\n <div key={url + i} className=\"relative\">\n <img src={url} alt=\"\" className=\"h-16 w-16 rounded-lg object-cover\" />\n <button\n type=\"button\"\n aria-label=\"Verwijder afbeelding\"\n className=\"absolute -right-2 -top-2 grid h-5 w-5 place-items-center rounded-full bg-slate-700 text-xs text-white\"\n onClick={() => setDef((p) => ({ ...p, images: (p.images ?? []).filter((_, j) => j !== i) }))}\n >\n ✕\n </button>\n </div>\n ))}\n <MediaPickerButton\n siteId={siteId}\n label=\"+ Afbeelding\"\n onSelect={(m) => setDef((p) => ({ ...p, images: [...(p.images ?? []), m.url] }))}\n />\n </div>\n </Field>\n\n <Field label=\"Categorieën\">\n <div className=\"flex flex-wrap gap-2\">\n {categories.map((c) => {\n const on = (def.categoryIds ?? []).includes(c.id);\n return (\n <button\n key={c.id}\n type=\"button\"\n onClick={() =>\n setDef((p) => ({\n ...p,\n categoryIds: on ? (p.categoryIds ?? []).filter((x) => x !== c.id) : [...(p.categoryIds ?? []), c.id],\n }))\n }\n className={`rounded-full border px-3 py-1 text-sm ${\n on ? \"border-brand-500 bg-brand-50 text-brand-700\" : \"border-slate-200 hover:bg-slate-50\"\n }`}\n >\n {on ? \"✓ \" : \"\"}\n {c.name}\n </button>\n );\n })}\n {categories.length === 0 && <span className=\"text-sm text-slate-500\">Maak eerst een categorie aan.</span>}\n </div>\n </Field>\n\n {/* Opties & varianten */}\n <Field\n label=\"Opties (bv. Maat, Kleur)\"\n hint=\"Max. 3. Waarden gescheiden door komma's. Combinaties kun je hieronder per stuk aanpassen.\"\n >\n <div className=\"grid gap-2\">\n {(def.options ?? []).map((opt, i) => (\n <div key={i} className=\"grid gap-2 rounded-lg border border-slate-100 p-2\">\n <div className=\"flex items-center gap-2\">\n <Input\n className=\"w-36\"\n placeholder=\"Naam (bv. Maat)\"\n value={opt.name}\n onChange={(e) => setOption(i, { name: e.target.value })}\n />\n <Input\n placeholder=\"Waarden, bv: 56, 62, 68, 74\"\n value={opt.values.join(\", \")}\n onChange={(e) =>\n setOption(i, { values: e.target.value.split(\",\").map((s) => s.trim()).filter(Boolean) })\n }\n />\n <Button\n size=\"sm\"\n variant=\"ghost\"\n onClick={() => setDef((p) => ({ ...p, options: (p.options ?? []).filter((_, j) => j !== i) }))}\n >\n ✕\n </Button>\n </div>\n {/* Afbeelding per waarde (5.6) — vooral voor de kleur-as: de detailpagina\n wisselt het hoofdbeeld mee met de gekozen kleur. */}\n {/^kleur|^colou?r/i.test(opt.name) && opt.values.length > 0 && (\n <div className=\"flex flex-wrap items-center gap-2\">\n {opt.values.map((value) => {\n const url = opt.images?.[value];\n return (\n <span key={value} className=\"flex items-center gap-1 rounded-full border border-slate-200 px-2 py-1 text-xs\">\n {url && <img src={url} alt=\"\" className=\"h-6 w-6 rounded object-cover\" />}\n {value}\n <MediaPickerButton\n siteId={siteId}\n label={url ? \"↻\" : \"+ foto\"}\n onSelect={(m) =>\n setOption(i, { images: { ...(opt.images ?? {}), [value]: m.url } })\n }\n />\n {url && (\n <button\n type=\"button\"\n aria-label={`Verwijder foto van ${value}`}\n className=\"text-slate-400 hover:text-slate-700\"\n onClick={() => {\n const images = { ...(opt.images ?? {}) };\n delete images[value];\n setOption(i, { images });\n }}\n >\n ✕\n </button>\n )}\n </span>\n );\n })}\n </div>\n )}\n </div>\n ))}\n {(def.options ?? []).length < 3 && (\n <Button\n size=\"sm\"\n variant=\"secondary\"\n className=\"w-fit\"\n onClick={() => setDef((p) => ({ ...p, options: [...(p.options ?? []), { name: \"\", values: [] }] }))}\n >\n + Optie\n </Button>\n )}\n </div>\n </Field>\n\n {variantKeys.length > 0 && variantKeys.length <= 60 && (\n <Field label=\"Varianten\" hint=\"Leeg = standaardprijs / onbeperkte voorraad\">\n <div className=\"overflow-x-auto\">\n <table className=\"w-full text-sm\">\n <thead>\n <tr className=\"text-left text-xs text-slate-500\">\n <th className=\"py-1 pr-2\">Combinatie</th>\n <th className=\"py-1 pr-2\">Prijs (€)</th>\n {def.trackStock && <th className=\"py-1 pr-2\">Voorraad</th>}\n <th className=\"py-1 pr-2\">Leverbaar</th>\n </tr>\n </thead>\n <tbody>\n {variantKeys.map((key) => {\n const v = variantFor(key);\n return (\n <tr key={key} className=\"border-t border-slate-100\">\n <td className=\"py-1 pr-2\">{key.split(\"|\").join(\" · \")}</td>\n <td className=\"py-1 pr-2\">\n {/* Native uncontrolled inputs: geen cursor-gedoe bij \"13,\" tijdens typen. */}\n <input\n className=\"w-24 rounded-lg border border-slate-200 px-2 py-1\"\n placeholder={centsToEuroInput(def.priceCents)}\n defaultValue={centsToEuroInput(v?.priceCents)}\n onChange={(e) => {\n const c = eurosToCents(e.target.value);\n patchVariant(key, { priceCents: c ?? undefined });\n }}\n />\n </td>\n {def.trackStock && (\n <td className=\"py-1 pr-2\">\n <input\n className=\"w-20 rounded-lg border border-slate-200 px-2 py-1\"\n type=\"number\"\n min={0}\n placeholder=\"∞\"\n defaultValue={v?.stock ?? \"\"}\n onChange={(e) => {\n const n = e.target.value === \"\" ? undefined : Math.max(0, Number(e.target.value) || 0);\n patchVariant(key, { stock: n });\n }}\n />\n </td>\n )}\n <td className=\"py-1 pr-2\">\n <Switch checked={v?.active !== false} onCheckedChange={(on) => patchVariant(key, { active: on })} />\n </td>\n </tr>\n );\n })}\n </tbody>\n </table>\n </div>\n </Field>\n )}\n {variantKeys.length > 60 && (\n <p className=\"text-sm text-amber-600\">\n {variantKeys.length} combinaties — te veel om per stuk te tonen. Beperk het aantal optie-waarden.\n </p>\n )}\n\n {/* Personalisatie (5.6): eigen naam/logo van de klant, met optionele meerprijs. */}\n <Field\n label=\"Personalisatie (eigen tekst of afbeelding)\"\n hint=\"Max. 3 velden. De klant vult dit in op de productpagina; de meerprijs telt automatisch mee.\"\n >\n <div className=\"grid gap-2\">\n {(def.personalization ?? []).map((f, i) => {\n const setPers = (patch: Partial<PersonalizationField>) =>\n setDef((p) => {\n const personalization = [...(p.personalization ?? [])];\n personalization[i] = { ...personalization[i], ...patch };\n return { ...p, personalization };\n });\n return (\n <div key={i} className=\"grid gap-2 rounded-lg border border-slate-100 p-2 md:grid-cols-[1fr_130px_90px_110px_auto]\">\n <Input\n placeholder='Label (bv. \"Naam van het kindje\")'\n value={f.label}\n onChange={(e) =>\n setPers({\n label: e.target.value,\n key: f.key || e.target.value.toLowerCase().replace(/[^a-z0-9]+/g, \"_\").replace(/^_+|_+$/g, \"\").slice(0, 40) || `veld${i + 1}`,\n })\n }\n />\n <Select\n value={f.type}\n onValueChange={(v) => setPers({ type: v as \"text\" | \"image\" })}\n options={[\n { value: \"text\", label: \"Tekst\" },\n { value: \"image\", label: \"Afbeelding\" },\n ]}\n />\n {f.type === \"text\" ? (\n <Input\n type=\"number\"\n title=\"Max. tekens\"\n min={1}\n max={200}\n value={f.maxLength ?? 40}\n onChange={(e) => setPers({ maxLength: Math.max(1, Math.min(200, Number(e.target.value) || 40)) })}\n />\n ) : (\n <span className=\"self-center text-xs text-slate-400\">jpg/png/webp</span>\n )}\n <input\n className=\"rounded-lg border border-slate-200 px-2 py-1\"\n placeholder=\"Meerprijs €\"\n defaultValue={f.priceCents ? centsToEuroInput(f.priceCents) : \"\"}\n onChange={(e) => setPers({ priceCents: eurosToCents(e.target.value) ?? 0 })}\n />\n <div className=\"flex items-center gap-2\">\n <Switch checked={f.required === true} onCheckedChange={(v) => setPers({ required: v })} label=\"Verplicht\" />\n <Button\n size=\"sm\"\n variant=\"ghost\"\n onClick={() => setDef((p) => ({ ...p, personalization: (p.personalization ?? []).filter((_, j) => j !== i) }))}\n >\n ✕\n </Button>\n </div>\n {/* Uitleg onder het veld in de winkel. Bestond al in het schema en werd al\n gerenderd op de productpagina, maar was hier nooit in te vullen. */}\n <input\n className=\"col-span-full rounded-lg border border-slate-200 px-2 py-1 text-sm\"\n placeholder=\"Uitleg voor de klant (optioneel), bv. 'Max. 12 tekens — komt op de voorkant'\"\n value={f.helpText ?? \"\"}\n onChange={(e) => setPers({ helpText: e.target.value })}\n />\n </div>\n );\n })}\n {(def.personalization ?? []).length < 3 && (\n <Button\n size=\"sm\"\n variant=\"secondary\"\n className=\"w-fit\"\n onClick={() =>\n setDef((p) => ({\n ...p,\n personalization: [\n ...(p.personalization ?? []),\n { key: `veld${(p.personalization ?? []).length + 1}`, label: \"\", type: \"text\", required: false, maxLength: 40, priceCents: 0 },\n ],\n }))\n }\n >\n + Personalisatie-veld\n </Button>\n )}\n </div>\n </Field>\n\n <div className=\"flex flex-wrap items-center gap-6\">\n <Field label=\"Status\">\n <Select\n value={def.status ?? \"draft\"}\n onValueChange={(v) => setDef((p) => ({ ...p, status: v as Product[\"status\"] }))}\n options={STATUS_OPTIONS}\n />\n </Field>\n <Field label=\"Volgorde\">\n <Input\n type=\"number\"\n className=\"w-24\"\n value={def.order ?? 0}\n onChange={(e) => setDef((p) => ({ ...p, order: Math.max(0, Number(e.target.value) || 0) }))}\n />\n </Field>\n <Switch checked={def.featured === true} onCheckedChange={(v) => setDef((p) => ({ ...p, featured: v }))} label=\"Uitgelicht\" />\n <Switch checked={def.trackStock === true} onCheckedChange={(v) => setDef((p) => ({ ...p, trackStock: v }))} label=\"Voorraad bijhouden\" />\n {def.trackStock && (def.options ?? []).length === 0 && (\n <Field label=\"Voorraad\">\n <Input\n type=\"number\"\n className=\"w-24\"\n placeholder=\"∞\"\n value={def.stock ?? \"\"}\n onChange={(e) =>\n setDef((p) => ({ ...p, stock: e.target.value === \"\" ? undefined : Math.max(0, Number(e.target.value) || 0) }))\n }\n />\n </Field>\n )}\n </div>\n\n <div className=\"flex items-center justify-between border-t border-slate-100 pt-4\">\n <div className=\"flex items-center gap-3\">\n <Button onClick={save}>Opslaan</Button>\n <span className=\"text-sm text-slate-500\">{formatCents(def.priceCents)}</span>\n </div>\n <Button variant=\"danger\" onClick={remove}>Verwijderen</Button>\n </div>\n </div>\n </Card>\n );\n}\n"],"mappings":";AAsGQ,SACE,KADF;AApGR,SAAS,WAAW,SAAS,gBAAgB;AAC7C,SAAS,YAAiB,kBAAkB;AAC5C,SAAS,eAAe,sBAAsB;AAC9C,SAAS,aAAa;AACtB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OAMK;AACP,SAAS,OAAO,QAAQ,MAAM,OAAO,OAAO,gBAAgB;AAC5D,SAAS,cAAc;AACvB,SAAS,cAAc;AACvB,SAAS,gBAAgB;AACzB,SAAS,kBAAuC;AAChD,SAAS,yBAAyB;AAClC,SAAS,eAAe;AACxB,SAAS,gBAAgB,kBAAkB,oBAAoB;AAK/D,MAAM,iBAAiB;AAAA,EACrB,EAAE,OAAO,SAAS,OAAO,UAAU;AAAA,EACnC,EAAE,OAAO,UAAU,OAAO,SAAS;AAAA,EACnC,EAAE,OAAO,YAAY,OAAO,eAAe;AAC7C;AAEO,SAAS,YAAY,EAAE,OAAO,GAAuB;AAC1D,QAAM,EAAE,MAAM,IAAI,SAAS;AAC3B,QAAM,UAAU,WAAW;AAC3B,QAAM,CAAC,UAAU,WAAW,IAAI,SAA0B,CAAC,CAAC;AAC5D,QAAM,CAAC,MAAM,OAAO,IAAI,SAA2B,CAAC,CAAC;AACrD,QAAM,CAAC,UAAU,WAAW,IAAI,SAAwB,IAAI;AAC5D,QAAM,CAAC,GAAG,IAAI,IAAI,SAAS,EAAE;AAE7B,YAAU,MAAM;AACd,UAAM,KAAK,MAAM;AACjB,QAAI,CAAC,GAAI;AACT,UAAM,MAAM;AAAA,MAAW,WAAW,IAAI,SAAS,QAAQ,UAAU;AAAA,MAAG,CAAC,SACnE;AAAA,QACE,KAAK,KAGF,IAAI,CAAC,OAAO,EAAE,IAAI,EAAE,IAAI,GAAG,iBAAiB,EAAE,KAAK,CAAY,EAAE,EAAE,EACnE,KAAK,CAAC,GAAG,OAAO,EAAE,SAAS,MAAM,EAAE,SAAS,MAAM,EAAE,KAAK,cAAc,EAAE,MAAM,IAAI,CAAC;AAAA,MACzF;AAAA,IACF;AACA,UAAM,MAAM;AAAA,MAAW,WAAW,IAAI,SAAS,QAAQ,gBAAgB;AAAA,MAAG,CAAC,SACzE,QAAQ,KAAK,KAAK,IAAI,CAAC,OAAO,EAAE,IAAI,EAAE,IAAI,GAAI,EAAE,KAAK,EAAmB,EAAE,CAAC;AAAA,IAC7E;AACA,WAAO,MAAM;AACX,UAAI;AACJ,UAAI;AAAA,IACN;AAAA,EACF,GAAG,CAAC,MAAM,CAAC;AAEX,iBAAe,gBAAgB;AAG7B,UAAM,QAAQ;AAAA,MACZ,MAAM;AAAA,MACN,MAAM,WAAW,SAAS,SAAS,CAAC;AAAA,MACpC,aAAa;AAAA,MACb,aAAa,CAAC;AAAA,MACd,QAAQ,CAAC;AAAA,MACT,YAAY;AAAA,MACZ,SAAS;AAAA,MACT,SAAS,CAAC;AAAA,MACV,UAAU,CAAC;AAAA,MACX,YAAY;AAAA,MACZ,QAAQ;AAAA,MACR,OAAO,SAAS;AAAA,MAChB,UAAU;AAAA,IACZ;AACA,QAAI;AACF,YAAM,KAAK,MAAM,eAAe,QAAQ,QAAW,KAAK;AACxD,kBAAY,EAAE;AAAA,IAChB,SAAS,GAAG;AAGV,YAAM,EAAE,OAAO,4BAA4B,aAAa,aAAa,QAAQ,EAAE,UAAU,QAAW,MAAM,QAAQ,CAAC;AAAA,IACrH;AAAA,EACF;AAEA,QAAM,WAAW,QAAQ,MAAM;AAC7B,UAAM,SAAS,EAAE,KAAK,EAAE,YAAY;AACpC,QAAI,CAAC,OAAQ,QAAO;AACpB,WAAO,SAAS,OAAO,CAAC,MAAM,EAAE,KAAK,YAAY,EAAE,SAAS,MAAM,KAAK,EAAE,KAAK,SAAS,MAAM,CAAC;AAAA,EAChG,GAAG,CAAC,UAAU,CAAC,CAAC;AAEhB,QAAM,UAAU,SAAS,KAAK,CAAC,MAAM,EAAE,OAAO,QAAQ,KAAK;AAE3D,SACE,qBAAC,SAAI,WAAU,uCACb;AAAA,yBAAC,QAAK,WAAU,SACd;AAAA,2BAAC,SAAI,WAAU,0CACb;AAAA,4BAAC,QAAG,WAAU,yBAAwB,uBAAS;AAAA,QAC/C,oBAAC,UAAO,MAAK,MAAK,SAAS,eAAe,qBAAO;AAAA,SACnD;AAAA,MACA,oBAAC,SAAM,aAAY,gBAAU,OAAO,GAAG,UAAU,CAAC,MAAM,KAAK,EAAE,OAAO,KAAK,GAAG,WAAU,QAAO;AAAA,MAC/F,qBAAC,QAAG,WAAU,2CACX;AAAA,iBAAS,IAAI,CAAC,MACb,oBAAC,QACC;AAAA,UAAC;AAAA;AAAA,YACC,SAAS,MAAM,YAAY,EAAE,EAAE;AAAA,YAC/B,WAAW,yEACT,EAAE,OAAO,WAAW,+BAA+B,oBACrD;AAAA,YAEC;AAAA,gBAAE,SAAS,CAAC,KAAK,oBAAC,SAAI,KAAK,EAAE,OAAO,CAAC,GAAG,KAAI,IAAG,WAAU,gCAA+B;AAAA,cACzF,oBAAC,UAAK,WAAU,2BAA2B,YAAE,MAAK;AAAA,cAClD,oBAAC,SAAM,MAAM,EAAE,WAAW,WAAW,UAAU,EAAE,WAAW,UAAU,UAAU,SAC7E,yBAAe,KAAK,CAAC,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,SAAS,EAAE,QAChE;AAAA;AAAA;AAAA,QACF,KAZO,EAAE,EAaX,CACD;AAAA,QACA,SAAS,WAAW,KAAK,oBAAC,QAAG,WAAU,oCAAmC,6BAAe;AAAA,SAC5F;AAAA,OACF;AAAA,IACC,UACC;AAAA,MAAC;AAAA;AAAA,QAEC;AAAA,QACA,SAAS;AAAA,QACT,YAAY;AAAA,QACZ,WAAW,MAAM,YAAY,IAAI;AAAA,QACjC,SAAS;AAAA,QACT;AAAA;AAAA,MANK,QAAQ;AAAA,IAOf,IAEA,oBAAC,QACC,8BAAC,OAAE,WAAU,0BAAyB,4CAA8B,GACtE;AAAA,KAEJ;AAEJ;AAQO,SAAS,YAAY;AAAA,EAC1B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAOG;AACD,QAAM,CAAC,KAAK,MAAM,IAAI,SAAkB,OAAO;AAC/C,QAAM,CAAC,YAAY,aAAa,IAAI,SAAS,iBAAiB,QAAQ,UAAU,CAAC;AACjF,QAAM,CAAC,cAAc,eAAe,IAAI,SAAS,iBAAiB,QAAQ,cAAc,CAAC;AAEzF,QAAM,cAAc,QAAQ,MAAM,eAAe,IAAI,WAAW,CAAC,CAAC,GAAG,CAAC,IAAI,OAAO,CAAC;AAElF,WAAS,UAAU,GAAW,OAA+B;AAC3D,WAAO,CAAC,MAAM;AACZ,YAAM,UAAU,CAAC,GAAI,EAAE,WAAW,CAAC,CAAE;AACrC,cAAQ,CAAC,IAAI,EAAE,GAAG,QAAQ,CAAC,GAAG,GAAG,MAAM;AACvC,aAAO,EAAE,GAAG,GAAG,QAAQ;AAAA,IACzB,CAAC;AAAA,EACH;AAEA,WAAS,WAAW,KAAyC;AAC3D,YAAQ,IAAI,YAAY,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,QAAQ,GAAG;AAAA,EACvD;AAEA,WAAS,aAAa,KAAa,OAAgC;AACjE,WAAO,CAAC,MAAM;AACZ,YAAM,WAAW,CAAC,GAAI,EAAE,YAAY,CAAC,CAAE;AACvC,YAAM,MAAM,SAAS,UAAU,CAAC,MAAM,EAAE,QAAQ,GAAG;AACnD,UAAI,OAAO,EAAG,UAAS,GAAG,IAAI,EAAE,GAAG,SAAS,GAAG,GAAG,GAAG,MAAM;AAAA,UACtD,UAAS,KAAK,EAAE,KAAK,QAAQ,MAAM,GAAG,MAAM,CAAC;AAClD,aAAO,EAAE,GAAG,GAAG,SAAS;AAAA,IAC1B,CAAC;AAAA,EACH;AAEA,iBAAe,OAAO;AACpB,UAAM,KAAK,MAAM;AACjB,QAAI,CAAC,GAAI;AAGT,UAAM,YAAY,IAAI,IAAI,WAAW;AACrC,UAAM,iBAAiB,IAAI,YAAY,CAAC,GAAG;AAAA,MACzC,CAAC,MACC,UAAU,IAAI,EAAE,GAAG,MAClB,EAAE,eAAe,UAAa,EAAE,UAAU,UAAa,EAAE,QAAQ,UAAa,EAAE,WAAW;AAAA,IAChG;AAEA,UAAM,wBAAwB,IAAI,mBAAmB,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,MAAM,KAAK,EAAE,SAAS,CAAC;AAChG,UAAM,SAAS,cAAc,UAAU;AAAA,MACrC,GAAG;AAAA,MACH,UAAU;AAAA,MACV,iBAAiB,qBAAqB,SAAS,IAAI,uBAAuB;AAAA,IAC5E,CAAC;AACD,QAAI,CAAC,OAAO,SAAS;AACnB,cAAQ,EAAE,OAAO,oBAAoB,aAAa,OAAO,MAAM,OAAO,CAAC,GAAG,SAAS,MAAM,QAAQ,CAAC;AAClG;AAAA,IACF;AAIA,UAAM,eAAe,QAAQ,QAAQ,IAAI,OAAO,IAAI;AACpD,YAAQ,EAAE,OAAO,sBAAsB,MAAM,UAAU,CAAC;AAAA,EAC1D;AAEA,iBAAe,SAAS;AACtB,UAAM,KAAK,MAAM,QAAQ;AAAA,MACvB,OAAO,IAAI,QAAQ,IAAI;AAAA,MACvB,aAAa;AAAA,MACb,MAAM;AAAA,IACR,CAAC;AACD,QAAI,CAAC,GAAI;AACT,UAAM,cAAc,QAAQ,QAAQ,EAAE;AACtC,cAAU;AAAA,EACZ;AAEA,SACE,oBAAC,QACC,+BAAC,SAAI,WAAU,cACb;AAAA,yBAAC,SAAI,WAAU,6BACb;AAAA,0BAAC,SAAM,OAAM,QACX;AAAA,QAAC;AAAA;AAAA,UACC,OAAO,IAAI;AAAA,UACX,UAAU,CAAC,MAAM,OAAO,CAAC,OAAO,EAAE,GAAG,GAAG,MAAM,EAAE,OAAO,OAAO,MAAM,QAAQ,EAAE,OAAO,KAAK,KAAK,EAAE,KAAK,EAAE;AAAA;AAAA,MAC1G,GACF;AAAA,MACA,oBAAC,SAAM,OAAM,QAAO,MAAK,kCACvB,8BAAC,SAAM,OAAO,IAAI,MAAM,UAAU,CAAC,MAAM,OAAO,CAAC,OAAO,EAAE,GAAG,GAAG,MAAM,QAAQ,EAAE,OAAO,KAAK,EAAE,EAAE,GAAG,GACrG;AAAA,OACF;AAAA,IAEA,qBAAC,SAAI,WAAU,6BACb;AAAA,0BAAC,SAAM,OAAM,6BACX;AAAA,QAAC;AAAA;AAAA,UACC,OAAO;AAAA,UACP,UAAU,CAAC,MAAM;AACf,0BAAc,EAAE,OAAO,KAAK;AAC5B,kBAAM,IAAI,aAAa,EAAE,OAAO,KAAK;AACrC,gBAAI,MAAM,KAAM,QAAO,CAAC,OAAO,EAAE,GAAG,GAAG,YAAY,EAAE,EAAE;AAAA,UACzD;AAAA;AAAA,MACF,GACF;AAAA,MACA,oBAAC,SAAM,OAAM,iCAA2B,MAAK,gCAC3C;AAAA,QAAC;AAAA;AAAA,UACC,OAAO;AAAA,UACP,UAAU,CAAC,MAAM;AACf,4BAAgB,EAAE,OAAO,KAAK;AAC9B,kBAAM,IAAI,aAAa,EAAE,OAAO,KAAK;AACrC,mBAAO,CAAC,OAAO,EAAE,GAAG,GAAG,gBAAgB,KAAK,OAAU,EAAE;AAAA,UAC1D;AAAA;AAAA,MACF,GACF;AAAA,MACA,oBAAC,SAAM,OAAM,cACX;AAAA,QAAC;AAAA;AAAA,UACC,OAAO,OAAO,IAAI,WAAW,EAAE;AAAA,UAC/B,eAAe,CAAC,MAAM,OAAO,CAAC,OAAO,EAAE,GAAG,GAAG,SAAS,OAAO,CAAC,EAAgB,EAAE;AAAA,UAChF,SAAS;AAAA,YACP,EAAE,OAAO,MAAM,OAAO,MAAM;AAAA,YAC5B,EAAE,OAAO,KAAK,OAAO,KAAK;AAAA,YAC1B,EAAE,OAAO,KAAK,OAAO,KAAK;AAAA,UAC5B;AAAA;AAAA,MACF,GACF;AAAA,OACF;AAAA,IAEA,oBAAC,SAAM,OAAM,gBACX,8BAAC,YAAS,MAAM,GAAG,OAAO,IAAI,eAAe,IAAI,UAAU,CAAC,MAAM,OAAO,CAAC,OAAO,EAAE,GAAG,GAAG,aAAa,EAAE,OAAO,MAAM,EAAE,GAAG,GAC5H;AAAA,IAEA,oBAAC,SAAM,OAAM,gBAAe,MAAK,uCAC/B,+BAAC,SAAI,WAAU,qCACX;AAAA,WAAI,UAAU,CAAC,GAAG,IAAI,CAAC,KAAK,MAC5B,qBAAC,SAAkB,WAAU,YAC3B;AAAA,4BAAC,SAAI,KAAK,KAAK,KAAI,IAAG,WAAU,qCAAoC;AAAA,QACpE;AAAA,UAAC;AAAA;AAAA,YACC,MAAK;AAAA,YACL,cAAW;AAAA,YACX,WAAU;AAAA,YACV,SAAS,MAAM,OAAO,CAAC,OAAO,EAAE,GAAG,GAAG,SAAS,EAAE,UAAU,CAAC,GAAG,OAAO,CAAC,GAAG,MAAM,MAAM,CAAC,EAAE,EAAE;AAAA,YAC5F;AAAA;AAAA,QAED;AAAA,WATQ,MAAM,CAUhB,CACD;AAAA,MACD;AAAA,QAAC;AAAA;AAAA,UACC;AAAA,UACA,OAAM;AAAA,UACN,UAAU,CAAC,MAAM,OAAO,CAAC,OAAO,EAAE,GAAG,GAAG,QAAQ,CAAC,GAAI,EAAE,UAAU,CAAC,GAAI,EAAE,GAAG,EAAE,EAAE;AAAA;AAAA,MACjF;AAAA,OACF,GACF;AAAA,IAEA,oBAAC,SAAM,OAAM,kBACX,+BAAC,SAAI,WAAU,wBACZ;AAAA,iBAAW,IAAI,CAAC,MAAM;AACrB,cAAM,MAAM,IAAI,eAAe,CAAC,GAAG,SAAS,EAAE,EAAE;AAChD,eACE;AAAA,UAAC;AAAA;AAAA,YAEC,MAAK;AAAA,YACL,SAAS,MACP,OAAO,CAAC,OAAO;AAAA,cACb,GAAG;AAAA,cACH,aAAa,MAAM,EAAE,eAAe,CAAC,GAAG,OAAO,CAAC,MAAM,MAAM,EAAE,EAAE,IAAI,CAAC,GAAI,EAAE,eAAe,CAAC,GAAI,EAAE,EAAE;AAAA,YACrG,EAAE;AAAA,YAEJ,WAAW,yCACT,KAAK,gDAAgD,oCACvD;AAAA,YAEC;AAAA,mBAAK,YAAO;AAAA,cACZ,EAAE;AAAA;AAAA;AAAA,UAbE,EAAE;AAAA,QAcT;AAAA,MAEJ,CAAC;AAAA,MACA,WAAW,WAAW,KAAK,oBAAC,UAAK,WAAU,0BAAyB,2CAA6B;AAAA,OACpG,GACF;AAAA,IAGA;AAAA,MAAC;AAAA;AAAA,QACC,OAAM;AAAA,QACN,MAAK;AAAA,QAEL,+BAAC,SAAI,WAAU,cACX;AAAA,eAAI,WAAW,CAAC,GAAG,IAAI,CAAC,KAAK,MAC7B,qBAAC,SAAY,WAAU,qDACrB;AAAA,iCAAC,SAAI,WAAU,2BACb;AAAA;AAAA,gBAAC;AAAA;AAAA,kBACC,WAAU;AAAA,kBACV,aAAY;AAAA,kBACZ,OAAO,IAAI;AAAA,kBACX,UAAU,CAAC,MAAM,UAAU,GAAG,EAAE,MAAM,EAAE,OAAO,MAAM,CAAC;AAAA;AAAA,cACxD;AAAA,cACA;AAAA,gBAAC;AAAA;AAAA,kBACC,aAAY;AAAA,kBACZ,OAAO,IAAI,OAAO,KAAK,IAAI;AAAA,kBAC3B,UAAU,CAAC,MACT,UAAU,GAAG,EAAE,QAAQ,EAAE,OAAO,MAAM,MAAM,GAAG,EAAE,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO,OAAO,EAAE,CAAC;AAAA;AAAA,cAE3F;AAAA,cACA;AAAA,gBAAC;AAAA;AAAA,kBACC,MAAK;AAAA,kBACL,SAAQ;AAAA,kBACR,SAAS,MAAM,OAAO,CAAC,OAAO,EAAE,GAAG,GAAG,UAAU,EAAE,WAAW,CAAC,GAAG,OAAO,CAAC,GAAG,MAAM,MAAM,CAAC,EAAE,EAAE;AAAA,kBAC9F;AAAA;AAAA,cAED;AAAA,eACF;AAAA,YAGC,mBAAmB,KAAK,IAAI,IAAI,KAAK,IAAI,OAAO,SAAS,KACxD,oBAAC,SAAI,WAAU,qCACZ,cAAI,OAAO,IAAI,CAAC,UAAU;AACzB,oBAAM,MAAM,IAAI,SAAS,KAAK;AAC9B,qBACE,qBAAC,UAAiB,WAAU,kFACzB;AAAA,uBAAO,oBAAC,SAAI,KAAK,KAAK,KAAI,IAAG,WAAU,gCAA+B;AAAA,gBACtE;AAAA,gBACD;AAAA,kBAAC;AAAA;AAAA,oBACC;AAAA,oBACA,OAAO,MAAM,WAAM;AAAA,oBACnB,UAAU,CAAC,MACT,UAAU,GAAG,EAAE,QAAQ,EAAE,GAAI,IAAI,UAAU,CAAC,GAAI,CAAC,KAAK,GAAG,EAAE,IAAI,EAAE,CAAC;AAAA;AAAA,gBAEtE;AAAA,gBACC,OACC;AAAA,kBAAC;AAAA;AAAA,oBACC,MAAK;AAAA,oBACL,cAAY,sBAAsB,KAAK;AAAA,oBACvC,WAAU;AAAA,oBACV,SAAS,MAAM;AACb,4BAAM,SAAS,EAAE,GAAI,IAAI,UAAU,CAAC,EAAG;AACvC,6BAAO,OAAO,KAAK;AACnB,gCAAU,GAAG,EAAE,OAAO,CAAC;AAAA,oBACzB;AAAA,oBACD;AAAA;AAAA,gBAED;AAAA,mBAtBO,KAwBX;AAAA,YAEJ,CAAC,GACH;AAAA,eAzDM,CA2DV,CACD;AAAA,WACC,IAAI,WAAW,CAAC,GAAG,SAAS,KAC5B;AAAA,YAAC;AAAA;AAAA,cACC,MAAK;AAAA,cACL,SAAQ;AAAA,cACR,WAAU;AAAA,cACV,SAAS,MAAM,OAAO,CAAC,OAAO,EAAE,GAAG,GAAG,SAAS,CAAC,GAAI,EAAE,WAAW,CAAC,GAAI,EAAE,MAAM,IAAI,QAAQ,CAAC,EAAE,CAAC,EAAE,EAAE;AAAA,cACnG;AAAA;AAAA,UAED;AAAA,WAEJ;AAAA;AAAA,IACF;AAAA,IAEC,YAAY,SAAS,KAAK,YAAY,UAAU,MAC/C,oBAAC,SAAM,OAAM,aAAY,MAAK,+CAC5B,8BAAC,SAAI,WAAU,mBACb,+BAAC,WAAM,WAAU,kBACf;AAAA,0BAAC,WACC,+BAAC,QAAG,WAAU,oCACZ;AAAA,4BAAC,QAAG,WAAU,aAAY,wBAAU;AAAA,QACpC,oBAAC,QAAG,WAAU,aAAY,4BAAS;AAAA,QAClC,IAAI,cAAc,oBAAC,QAAG,WAAU,aAAY,sBAAQ;AAAA,QACrD,oBAAC,QAAG,WAAU,aAAY,uBAAS;AAAA,SACrC,GACF;AAAA,MACA,oBAAC,WACE,sBAAY,IAAI,CAAC,QAAQ;AACxB,cAAM,IAAI,WAAW,GAAG;AACxB,eACE,qBAAC,QAAa,WAAU,6BACtB;AAAA,8BAAC,QAAG,WAAU,aAAa,cAAI,MAAM,GAAG,EAAE,KAAK,QAAK,GAAE;AAAA,UACtD,oBAAC,QAAG,WAAU,aAEZ;AAAA,YAAC;AAAA;AAAA,cACC,WAAU;AAAA,cACV,aAAa,iBAAiB,IAAI,UAAU;AAAA,cAC5C,cAAc,iBAAiB,GAAG,UAAU;AAAA,cAC5C,UAAU,CAAC,MAAM;AACf,sBAAM,IAAI,aAAa,EAAE,OAAO,KAAK;AACrC,6BAAa,KAAK,EAAE,YAAY,KAAK,OAAU,CAAC;AAAA,cAClD;AAAA;AAAA,UACF,GACF;AAAA,UACC,IAAI,cACH,oBAAC,QAAG,WAAU,aACZ;AAAA,YAAC;AAAA;AAAA,cACC,WAAU;AAAA,cACV,MAAK;AAAA,cACL,KAAK;AAAA,cACL,aAAY;AAAA,cACZ,cAAc,GAAG,SAAS;AAAA,cAC1B,UAAU,CAAC,MAAM;AACf,sBAAM,IAAI,EAAE,OAAO,UAAU,KAAK,SAAY,KAAK,IAAI,GAAG,OAAO,EAAE,OAAO,KAAK,KAAK,CAAC;AACrF,6BAAa,KAAK,EAAE,OAAO,EAAE,CAAC;AAAA,cAChC;AAAA;AAAA,UACF,GACF;AAAA,UAEF,oBAAC,QAAG,WAAU,aACZ,8BAAC,UAAO,SAAS,GAAG,WAAW,OAAO,iBAAiB,CAAC,OAAO,aAAa,KAAK,EAAE,QAAQ,GAAG,CAAC,GAAG,GACpG;AAAA,aA/BO,GAgCT;AAAA,MAEJ,CAAC,GACH;AAAA,OACF,GACF,GACF;AAAA,IAED,YAAY,SAAS,MACpB,qBAAC,OAAE,WAAU,0BACV;AAAA,kBAAY;AAAA,MAAO;AAAA,OACtB;AAAA,IAIF;AAAA,MAAC;AAAA;AAAA,QACC,OAAM;AAAA,QACN,MAAK;AAAA,QAEL,+BAAC,SAAI,WAAU,cACX;AAAA,eAAI,mBAAmB,CAAC,GAAG,IAAI,CAAC,GAAG,MAAM;AACzC,kBAAM,UAAU,CAAC,UACf,OAAO,CAAC,MAAM;AACZ,oBAAM,kBAAkB,CAAC,GAAI,EAAE,mBAAmB,CAAC,CAAE;AACrD,8BAAgB,CAAC,IAAI,EAAE,GAAG,gBAAgB,CAAC,GAAG,GAAG,MAAM;AACvD,qBAAO,EAAE,GAAG,GAAG,gBAAgB;AAAA,YACjC,CAAC;AACH,mBACE,qBAAC,SAAY,WAAU,8FACrB;AAAA;AAAA,gBAAC;AAAA;AAAA,kBACC,aAAY;AAAA,kBACZ,OAAO,EAAE;AAAA,kBACT,UAAU,CAAC,MACT,QAAQ;AAAA,oBACN,OAAO,EAAE,OAAO;AAAA,oBAChB,KAAK,EAAE,OAAO,EAAE,OAAO,MAAM,YAAY,EAAE,QAAQ,eAAe,GAAG,EAAE,QAAQ,YAAY,EAAE,EAAE,MAAM,GAAG,EAAE,KAAK,OAAO,IAAI,CAAC;AAAA,kBAC7H,CAAC;AAAA;AAAA,cAEL;AAAA,cACA;AAAA,gBAAC;AAAA;AAAA,kBACC,OAAO,EAAE;AAAA,kBACT,eAAe,CAAC,MAAM,QAAQ,EAAE,MAAM,EAAsB,CAAC;AAAA,kBAC7D,SAAS;AAAA,oBACP,EAAE,OAAO,QAAQ,OAAO,QAAQ;AAAA,oBAChC,EAAE,OAAO,SAAS,OAAO,aAAa;AAAA,kBACxC;AAAA;AAAA,cACF;AAAA,cACC,EAAE,SAAS,SACV;AAAA,gBAAC;AAAA;AAAA,kBACC,MAAK;AAAA,kBACL,OAAM;AAAA,kBACN,KAAK;AAAA,kBACL,KAAK;AAAA,kBACL,OAAO,EAAE,aAAa;AAAA,kBACtB,UAAU,CAAC,MAAM,QAAQ,EAAE,WAAW,KAAK,IAAI,GAAG,KAAK,IAAI,KAAK,OAAO,EAAE,OAAO,KAAK,KAAK,EAAE,CAAC,EAAE,CAAC;AAAA;AAAA,cAClG,IAEA,oBAAC,UAAK,WAAU,sCAAqC,0BAAY;AAAA,cAEnE;AAAA,gBAAC;AAAA;AAAA,kBACC,WAAU;AAAA,kBACV,aAAY;AAAA,kBACZ,cAAc,EAAE,aAAa,iBAAiB,EAAE,UAAU,IAAI;AAAA,kBAC9D,UAAU,CAAC,MAAM,QAAQ,EAAE,YAAY,aAAa,EAAE,OAAO,KAAK,KAAK,EAAE,CAAC;AAAA;AAAA,cAC5E;AAAA,cACA,qBAAC,SAAI,WAAU,2BACb;AAAA,oCAAC,UAAO,SAAS,EAAE,aAAa,MAAM,iBAAiB,CAAC,MAAM,QAAQ,EAAE,UAAU,EAAE,CAAC,GAAG,OAAM,aAAY;AAAA,gBAC1G;AAAA,kBAAC;AAAA;AAAA,oBACC,MAAK;AAAA,oBACL,SAAQ;AAAA,oBACR,SAAS,MAAM,OAAO,CAAC,OAAO,EAAE,GAAG,GAAG,kBAAkB,EAAE,mBAAmB,CAAC,GAAG,OAAO,CAAC,GAAG,MAAM,MAAM,CAAC,EAAE,EAAE;AAAA,oBAC9G;AAAA;AAAA,gBAED;AAAA,iBACF;AAAA,cAGA;AAAA,gBAAC;AAAA;AAAA,kBACC,WAAU;AAAA,kBACV,aAAY;AAAA,kBACZ,OAAO,EAAE,YAAY;AAAA,kBACrB,UAAU,CAAC,MAAM,QAAQ,EAAE,UAAU,EAAE,OAAO,MAAM,CAAC;AAAA;AAAA,cACvD;AAAA,iBAtDQ,CAuDV;AAAA,UAEJ,CAAC;AAAA,WACC,IAAI,mBAAmB,CAAC,GAAG,SAAS,KACpC;AAAA,YAAC;AAAA;AAAA,cACC,MAAK;AAAA,cACL,SAAQ;AAAA,cACR,WAAU;AAAA,cACV,SAAS,MACP,OAAO,CAAC,OAAO;AAAA,gBACb,GAAG;AAAA,gBACH,iBAAiB;AAAA,kBACf,GAAI,EAAE,mBAAmB,CAAC;AAAA,kBAC1B,EAAE,KAAK,QAAQ,EAAE,mBAAmB,CAAC,GAAG,SAAS,CAAC,IAAI,OAAO,IAAI,MAAM,QAAQ,UAAU,OAAO,WAAW,IAAI,YAAY,EAAE;AAAA,gBAC/H;AAAA,cACF,EAAE;AAAA,cAEL;AAAA;AAAA,UAED;AAAA,WAEJ;AAAA;AAAA,IACF;AAAA,IAEA,qBAAC,SAAI,WAAU,qCACb;AAAA,0BAAC,SAAM,OAAM,UACX;AAAA,QAAC;AAAA;AAAA,UACC,OAAO,IAAI,UAAU;AAAA,UACrB,eAAe,CAAC,MAAM,OAAO,CAAC,OAAO,EAAE,GAAG,GAAG,QAAQ,EAAuB,EAAE;AAAA,UAC9E,SAAS;AAAA;AAAA,MACX,GACF;AAAA,MACA,oBAAC,SAAM,OAAM,YACX;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,WAAU;AAAA,UACV,OAAO,IAAI,SAAS;AAAA,UACpB,UAAU,CAAC,MAAM,OAAO,CAAC,OAAO,EAAE,GAAG,GAAG,OAAO,KAAK,IAAI,GAAG,OAAO,EAAE,OAAO,KAAK,KAAK,CAAC,EAAE,EAAE;AAAA;AAAA,MAC5F,GACF;AAAA,MACA,oBAAC,UAAO,SAAS,IAAI,aAAa,MAAM,iBAAiB,CAAC,MAAM,OAAO,CAAC,OAAO,EAAE,GAAG,GAAG,UAAU,EAAE,EAAE,GAAG,OAAM,cAAa;AAAA,MAC3H,oBAAC,UAAO,SAAS,IAAI,eAAe,MAAM,iBAAiB,CAAC,MAAM,OAAO,CAAC,OAAO,EAAE,GAAG,GAAG,YAAY,EAAE,EAAE,GAAG,OAAM,sBAAqB;AAAA,MACtI,IAAI,eAAe,IAAI,WAAW,CAAC,GAAG,WAAW,KAChD,oBAAC,SAAM,OAAM,YACX;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,WAAU;AAAA,UACV,aAAY;AAAA,UACZ,OAAO,IAAI,SAAS;AAAA,UACpB,UAAU,CAAC,MACT,OAAO,CAAC,OAAO,EAAE,GAAG,GAAG,OAAO,EAAE,OAAO,UAAU,KAAK,SAAY,KAAK,IAAI,GAAG,OAAO,EAAE,OAAO,KAAK,KAAK,CAAC,EAAE,EAAE;AAAA;AAAA,MAEjH,GACF;AAAA,OAEJ;AAAA,IAEA,qBAAC,SAAI,WAAU,oEACb;AAAA,2BAAC,SAAI,WAAU,2BACb;AAAA,4BAAC,UAAO,SAAS,MAAM,qBAAO;AAAA,QAC9B,oBAAC,UAAK,WAAU,0BAA0B,sBAAY,IAAI,UAAU,GAAE;AAAA,SACxE;AAAA,MACA,oBAAC,UAAO,SAAQ,UAAS,SAAS,QAAQ,yBAAW;AAAA,OACvD;AAAA,KACF,GACF;AAEJ;","names":[]}
|
package/dist/admin.css
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */
|
|
2
|
-
@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-border-style:solid;--tw-font-weight:initial;--tw-tracking:initial;--tw-ordinal:initial;--tw-slashed-zero:initial;--tw-numeric-figure:initial;--tw-numeric-spacing:initial;--tw-numeric-fraction:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-outline-style:solid;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial;--tw-ease:initial}}}@layer theme{:root,:host{--font-mono:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--color-red-50:oklch(97.1% .013 17.38);--color-red-200:oklch(88.5% .062 18.334);--color-red-600:oklch(57.7% .245 27.325);--color-red-700:oklch(50.5% .213 27.518);--color-amber-50:oklch(98.7% .022 95.277);--color-amber-300:oklch(87.9% .169 91.605);--color-amber-600:oklch(66.6% .179 58.318);--color-amber-700:oklch(55.5% .163 48.998);--color-amber-800:oklch(47.3% .137 46.201);--color-amber-900:oklch(41.4% .112 45.904);--color-emerald-700:oklch(50.8% .118 165.612);--color-blue-600:oklch(54.6% .245 262.881);--color-indigo-500:oklch(58.5% .233 277.117);--color-slate-50:oklch(98.4% .003 247.858);--color-slate-100:oklch(96.8% .007 247.896);--color-slate-200:oklch(92.9% .013 255.508);--color-slate-300:oklch(86.9% .022 252.894);--color-slate-400:oklch(70.4% .04 256.788);--color-slate-500:oklch(55.4% .046 257.417);--color-slate-600:oklch(44.6% .043 257.281);--color-slate-700:oklch(37.2% .044 257.287);--color-slate-800:oklch(27.9% .041 260.031);--color-slate-900:oklch(20.8% .042 265.755);--color-white:#fff;--spacing:.25rem;--container-xs:20rem;--container-sm:24rem;--container-md:28rem;--container-3xl:48rem;--container-4xl:56rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-base:1rem;--text-base--line-height:calc(1.5 / 1);--text-lg:1.125rem;--text-lg--line-height:calc(1.75 / 1.125);--text-xl:1.25rem;--text-xl--line-height:calc(1.75 / 1.25);--text-3xl:1.875rem;--text-3xl--line-height:calc(2.25 / 1.875);--font-weight-normal:400;--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--tracking-wide:.025em;--radius-md:.375rem;--radius-lg:.5rem;--radius-xl:.75rem;--ease-out:cubic-bezier(0, 0, .2, 1);--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--color-brand:#4f46e5;--color-brand-50:#eef2ff;--color-brand-500:#4f46e5;--color-brand-600:#4f46e5;--color-brand-700:#3730a3}}@scope(.xicms-admin){@layer utilities{.pointer-events-none{pointer-events:none!important}.visible{visibility:visible!important}.sr-only{clip-path:inset(50%)!important;white-space:nowrap!important;border-width:0!important;width:1px!important;height:1px!important;margin:-1px!important;padding:0!important;position:absolute!important;overflow:hidden!important}.absolute{position:absolute!important}.fixed{position:fixed!important}.relative{position:relative!important}.static{position:static!important}.sticky{position:sticky!important}.inset-x-0{inset-inline:0!important}.-top-0\.5{top:calc(var(--spacing) * -.5)!important}.-top-2{top:calc(var(--spacing) * -2)!important}.top-0{top:0!important}.-right-2{right:calc(var(--spacing) * -2)!important}.right-0{right:0!important}.bottom-0{bottom:0!important}.z-10{z-index:10!important}.col-span-2{grid-column:span 2/span 2!important}.container{width:100%!important}@media (min-width:40rem){.container{max-width:40rem!important}}@media (min-width:48rem){.container{max-width:48rem!important}}@media (min-width:64rem){.container{max-width:64rem!important}}@media (min-width:80rem){.container{max-width:80rem!important}}@media (min-width:96rem){.container{max-width:96rem!important}}.-mx-1{margin-inline:calc(var(--spacing) * -1)!important}.-mx-4{margin-inline:calc(var(--spacing) * -4)!important}.mt-0\.5{margin-top:calc(var(--spacing) * .5)!important}.mt-1{margin-top:var(--spacing)!important}.mt-2{margin-top:calc(var(--spacing) * 2)!important}.mt-3{margin-top:calc(var(--spacing) * 3)!important}.mt-4{margin-top:calc(var(--spacing) * 4)!important}.mt-5{margin-top:calc(var(--spacing) * 5)!important}.mt-6{margin-top:calc(var(--spacing) * 6)!important}.mt-auto{margin-top:auto!important}.mr-1{margin-right:var(--spacing)!important}.mr-2{margin-right:calc(var(--spacing) * 2)!important}.mb-1{margin-bottom:var(--spacing)!important}.mb-2{margin-bottom:calc(var(--spacing) * 2)!important}.mb-3{margin-bottom:calc(var(--spacing) * 3)!important}.mb-4{margin-bottom:calc(var(--spacing) * 4)!important}.ml-1{margin-left:var(--spacing)!important}.ml-1\.5{margin-left:calc(var(--spacing) * 1.5)!important}.ml-auto{margin-left:auto!important}.block{display:block!important}.contents{display:contents!important}.flex{display:flex!important}.grid{display:grid!important}.hidden{display:none!important}.inline{display:inline!important}.inline-block{display:inline-block!important}.inline-flex{display:inline-flex!important}.table{display:table!important}.aspect-square{aspect-ratio:1!important}.h-1\.5{height:calc(var(--spacing) * 1.5)!important}.h-2{height:calc(var(--spacing) * 2)!important}.h-5{height:calc(var(--spacing) * 5)!important}.h-6{height:calc(var(--spacing) * 6)!important}.h-8{height:calc(var(--spacing) * 8)!important}.h-9{height:calc(var(--spacing) * 9)!important}.h-10{height:calc(var(--spacing) * 10)!important}.h-14{height:calc(var(--spacing) * 14)!important}.h-16{height:calc(var(--spacing) * 16)!important}.h-28{height:calc(var(--spacing) * 28)!important}.h-32{height:calc(var(--spacing) * 32)!important}.h-\[70vh\]{height:70vh!important}.h-fit{height:fit-content!important}.h-full{height:100%!important}.max-h-\[55vh\]{max-height:55vh!important}.max-h-\[60vh\]{max-height:60vh!important}.min-h-\[8rem\]{min-height:8rem!important}.min-h-screen{min-height:100vh!important}.w-2{width:calc(var(--spacing) * 2)!important}.w-5{width:calc(var(--spacing) * 5)!important}.w-6{width:calc(var(--spacing) * 6)!important}.w-8{width:calc(var(--spacing) * 8)!important}.w-14{width:calc(var(--spacing) * 14)!important}.w-16{width:calc(var(--spacing) * 16)!important}.w-20{width:calc(var(--spacing) * 20)!important}.w-24{width:calc(var(--spacing) * 24)!important}.w-28{width:calc(var(--spacing) * 28)!important}.w-32{width:calc(var(--spacing) * 32)!important}.w-36{width:calc(var(--spacing) * 36)!important}.w-44{width:calc(var(--spacing) * 44)!important}.w-56{width:calc(var(--spacing) * 56)!important}.w-fit{width:fit-content!important}.w-full{width:100%!important}.max-w-3xl{max-width:var(--container-3xl)!important}.max-w-4xl{max-width:var(--container-4xl)!important}.max-w-md{max-width:var(--container-md)!important}.max-w-sm{max-width:var(--container-sm)!important}.max-w-xs{max-width:var(--container-xs)!important}.min-w-0{min-width:0!important}.min-w-8{min-width:calc(var(--spacing) * 8)!important}.flex-1{flex:1!important}.shrink-0{flex-shrink:0!important}.-translate-x-1\/2{--tw-translate-x:calc(calc(1 / 2 * 100%) * -1)!important;translate:var(--tw-translate-x) var(--tw-translate-y)!important}.-translate-y-1\/2{--tw-translate-y:calc(calc(1 / 2 * 100%) * -1)!important;translate:var(--tw-translate-x) var(--tw-translate-y)!important}.transform{transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)!important}.cursor-default{cursor:default!important}.cursor-pointer{cursor:pointer!important}.resize{resize:both!important}.list-decimal{list-style-type:decimal!important}.list-disc{list-style-type:disc!important}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))!important}.grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))!important}.grid-cols-\[1fr_1\.4fr_auto\]{grid-template-columns:1fr 1.4fr auto!important}.grid-cols-\[120px_1fr\]{grid-template-columns:120px 1fr!important}.grid-cols-\[minmax\(0\,1fr\)_auto\]{grid-template-columns:minmax(0,1fr) auto!important}.flex-col{flex-direction:column!important}.flex-wrap{flex-wrap:wrap!important}.place-items-center{place-items:center!important}.items-baseline{align-items:baseline!important}.items-center{align-items:center!important}.items-end{align-items:flex-end!important}.items-start{align-items:flex-start!important}.justify-between{justify-content:space-between!important}.justify-center{justify-content:center!important}.justify-end{justify-content:flex-end!important}.gap-0\.5{gap:calc(var(--spacing) * .5)!important}.gap-1{gap:var(--spacing)!important}.gap-1\.5{gap:calc(var(--spacing) * 1.5)!important}.gap-2{gap:calc(var(--spacing) * 2)!important}.gap-2\.5{gap:calc(var(--spacing) * 2.5)!important}.gap-3{gap:calc(var(--spacing) * 3)!important}.gap-4{gap:calc(var(--spacing) * 4)!important}.gap-6{gap:calc(var(--spacing) * 6)!important}.gap-x-3{column-gap:calc(var(--spacing) * 3)!important}.gap-x-5{column-gap:calc(var(--spacing) * 5)!important}.gap-y-1{row-gap:var(--spacing)!important}.self-center{align-self:center!important}.self-end{align-self:flex-end!important}.truncate{text-overflow:ellipsis!important;white-space:nowrap!important;overflow:hidden!important}.overflow-auto{overflow:auto!important}.overflow-hidden{overflow:hidden!important}.overflow-x-auto{overflow-x:auto!important}.overflow-y-auto{overflow-y:auto!important}.rounded{border-radius:.25rem!important}.rounded-full{border-radius:3.40282e38px!important}.rounded-lg{border-radius:var(--radius-lg)!important}.rounded-md{border-radius:var(--radius-md)!important}.rounded-xl{border-radius:var(--radius-xl)!important}.rounded-t-lg{border-top-left-radius:var(--radius-lg)!important;border-top-right-radius:var(--radius-lg)!important}.rounded-b-lg{border-bottom-right-radius:var(--radius-lg)!important;border-bottom-left-radius:var(--radius-lg)!important}.border{border-style:var(--tw-border-style)!important;border-width:1px!important}.border-t{border-top-style:var(--tw-border-style)!important;border-top-width:1px!important}.border-t-0{border-top-style:var(--tw-border-style)!important;border-top-width:0!important}.border-b{border-bottom-style:var(--tw-border-style)!important;border-bottom-width:1px!important}.border-amber-300{border-color:var(--color-amber-300)!important}.border-brand-500{border-color:var(--color-brand-500)!important}.border-red-200{border-color:var(--color-red-200)!important}.border-slate-100{border-color:var(--color-slate-100)!important}.border-slate-200{border-color:var(--color-slate-200)!important}.border-slate-300{border-color:var(--color-slate-300)!important}.bg-amber-50{background-color:var(--color-amber-50)!important}.bg-brand-50{background-color:var(--color-brand-50)!important}.bg-red-50{background-color:var(--color-red-50)!important}.bg-slate-50{background-color:var(--color-slate-50)!important}.bg-slate-100{background-color:var(--color-slate-100)!important}.bg-slate-200{background-color:var(--color-slate-200)!important}.bg-slate-700{background-color:var(--color-slate-700)!important}.bg-white{background-color:var(--color-white)!important}.object-cover{object-fit:cover!important}.p-0\.5{padding:calc(var(--spacing) * .5)!important}.p-1{padding:var(--spacing)!important}.p-2{padding:calc(var(--spacing) * 2)!important}.p-3{padding:calc(var(--spacing) * 3)!important}.p-4{padding:calc(var(--spacing) * 4)!important}.p-5{padding:calc(var(--spacing) * 5)!important}.p-6{padding:calc(var(--spacing) * 6)!important}.px-1{padding-inline:var(--spacing)!important}.px-1\.5{padding-inline:calc(var(--spacing) * 1.5)!important}.px-2{padding-inline:calc(var(--spacing) * 2)!important}.px-2\.5{padding-inline:calc(var(--spacing) * 2.5)!important}.px-3{padding-inline:calc(var(--spacing) * 3)!important}.px-4{padding-inline:calc(var(--spacing) * 4)!important}.py-0\.5{padding-block:calc(var(--spacing) * .5)!important}.py-1{padding-block:var(--spacing)!important}.py-1\.5{padding-block:calc(var(--spacing) * 1.5)!important}.py-2{padding-block:calc(var(--spacing) * 2)!important}.py-3{padding-block:calc(var(--spacing) * 3)!important}.py-8{padding-block:calc(var(--spacing) * 8)!important}.py-16{padding-block:calc(var(--spacing) * 16)!important}.pt-0{padding-top:0!important}.pt-1{padding-top:var(--spacing)!important}.pt-3{padding-top:calc(var(--spacing) * 3)!important}.pt-4{padding-top:calc(var(--spacing) * 4)!important}.pt-5{padding-top:calc(var(--spacing) * 5)!important}.pr-2{padding-right:calc(var(--spacing) * 2)!important}.pl-1{padding-left:var(--spacing)!important}.pl-5{padding-left:calc(var(--spacing) * 5)!important}.text-center{text-align:center!important}.text-left{text-align:left!important}.text-right{text-align:right!important}.align-top{vertical-align:top!important}.font-mono{font-family:var(--font-mono)!important}.text-3xl{font-size:var(--text-3xl)!important;line-height:var(--tw-leading,var(--text-3xl--line-height))!important}.text-base{font-size:var(--text-base)!important;line-height:var(--tw-leading,var(--text-base--line-height))!important}.text-lg{font-size:var(--text-lg)!important;line-height:var(--tw-leading,var(--text-lg--line-height))!important}.text-sm{font-size:var(--text-sm)!important;line-height:var(--tw-leading,var(--text-sm--line-height))!important}.text-xl{font-size:var(--text-xl)!important;line-height:var(--tw-leading,var(--text-xl--line-height))!important}.text-xs{font-size:var(--text-xs)!important;line-height:var(--tw-leading,var(--text-xs--line-height))!important}.text-\[10px\]{font-size:10px!important}.text-\[11px\]{font-size:11px!important}.font-bold{--tw-font-weight:var(--font-weight-bold)!important;font-weight:var(--font-weight-bold)!important}.font-medium{--tw-font-weight:var(--font-weight-medium)!important;font-weight:var(--font-weight-medium)!important}.font-normal{--tw-font-weight:var(--font-weight-normal)!important;font-weight:var(--font-weight-normal)!important}.font-semibold{--tw-font-weight:var(--font-weight-semibold)!important;font-weight:var(--font-weight-semibold)!important}.tracking-wide{--tw-tracking:var(--tracking-wide)!important;letter-spacing:var(--tracking-wide)!important}.whitespace-nowrap{white-space:nowrap!important}.whitespace-pre-wrap{white-space:pre-wrap!important}.text-amber-600{color:var(--color-amber-600)!important}.text-amber-700{color:var(--color-amber-700)!important}.text-amber-800{color:var(--color-amber-800)!important}.text-amber-900{color:var(--color-amber-900)!important}.text-brand-700{color:var(--color-brand-700)!important}.text-brand-700\/70{color:#3730a3b3!important}@supports (color:color-mix(in lab, red, red)){.text-brand-700\/70{color:color-mix(in oklab, var(--color-brand-700) 70%, transparent)!important}}.text-emerald-700{color:var(--color-emerald-700)!important}.text-red-600{color:var(--color-red-600)!important}.text-red-700{color:var(--color-red-700)!important}.text-slate-400{color:var(--color-slate-400)!important}.text-slate-500{color:var(--color-slate-500)!important}.text-slate-600{color:var(--color-slate-600)!important}.text-slate-700{color:var(--color-slate-700)!important}.text-slate-800{color:var(--color-slate-800)!important}.text-slate-900{color:var(--color-slate-900)!important}.text-white{color:var(--color-white)!important}.uppercase{text-transform:uppercase!important}.italic{font-style:italic!important}.tabular-nums{--tw-numeric-spacing:tabular-nums!important;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)!important}.line-through{text-decoration-line:line-through!important}.underline{text-decoration-line:underline!important}.opacity-70{opacity:.7!important}.shadow-md{--tw-shadow:0 4px 6px -1px var(--tw-shadow-color,#0000001a), 0 2px 4px -2px var(--tw-shadow-color,#0000001a)!important;box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)!important}.shadow-sm{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a)!important;box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)!important}.outline{outline-style:var(--tw-outline-style)!important;outline-width:1px!important}.blur{--tw-blur:blur(8px)!important;filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)!important}.grayscale{--tw-grayscale:grayscale(100%)!important;filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)!important}.filter{filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)!important}.transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events!important;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function))!important;transition-duration:var(--tw-duration,var(--default-transition-duration))!important}.transition-\[filter\]{transition-property:filter!important;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function))!important;transition-duration:var(--tw-duration,var(--default-transition-duration))!important}.ease-out{--tw-ease:var(--ease-out)!important;transition-timing-function:var(--ease-out)!important}.outline-none{--tw-outline-style:none!important;outline-style:none!important}@media (hover:hover){.group-hover\:opacity-80:is(:where(.group):hover *){opacity:.8!important}}.last\:border-0:last-child{border-style:var(--tw-border-style)!important;border-width:0!important}@media (hover:hover){.hover\:border-brand-500:hover{border-color:var(--color-brand-500)!important}.hover\:bg-slate-50:hover{background-color:var(--color-slate-50)!important}.hover\:bg-slate-100:hover{background-color:var(--color-slate-100)!important}.hover\:bg-slate-200:hover{background-color:var(--color-slate-200)!important}.hover\:text-slate-600:hover{color:var(--color-slate-600)!important}.hover\:text-slate-700:hover{color:var(--color-slate-700)!important}.hover\:text-slate-900:hover{color:var(--color-slate-900)!important}.hover\:underline:hover{text-decoration-line:underline!important}.hover\:brightness-95:hover{--tw-brightness:brightness(95%)!important;filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)!important}}.focus\:ring-2:focus{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor)!important;box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)!important}.focus\:ring-indigo-500:focus{--tw-ring-color:var(--color-indigo-500)!important}.focus\:outline-none:focus{--tw-outline-style:none!important;outline-style:none!important}.focus-visible\:ring-2:focus-visible{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor)!important;box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)!important}.focus-visible\:ring-brand-500:focus-visible{--tw-ring-color:var(--color-brand-500)!important}.focus-visible\:outline-2:focus-visible{outline-style:var(--tw-outline-style)!important;outline-width:2px!important}.focus-visible\:outline-offset-2:focus-visible{outline-offset:2px!important}.focus-visible\:outline-blue-600:focus-visible{outline-color:var(--color-blue-600)!important}.focus-visible\:outline-none:focus-visible{--tw-outline-style:none!important;outline-style:none!important}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed!important}.disabled\:opacity-50:disabled{opacity:.5!important}.aria-\[current\=page\]\:bg-slate-200[aria-current=page]{background-color:var(--color-slate-200)!important}.aria-\[current\=page\]\:text-slate-900[aria-current=page]{color:var(--color-slate-900)!important}.data-\[state\=active\]\:border-b-2[data-state=active]{border-bottom-style:var(--tw-border-style)!important;border-bottom-width:2px!important}.data-\[state\=active\]\:border-brand-600[data-state=active]{border-color:var(--color-brand-600)!important}.data-\[state\=active\]\:bg-slate-100[data-state=active]{background-color:var(--color-slate-100)!important}.data-\[state\=active\]\:text-brand-700[data-state=active]{color:var(--color-brand-700)!important}.data-\[state\=active\]\:text-slate-900[data-state=active]{color:var(--color-slate-900)!important}.data-\[state\=active\]\:shadow-\[inset_2px_0_0_0_var\(--color-brand-500\)\][data-state=active]{--tw-shadow:inset 2px 0 0 0 var(--tw-shadow-color,var(--color-brand-500))!important;box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)!important}@media (min-width:40rem){.sm\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))!important}.sm\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))!important}.sm\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))!important}}@media (min-width:48rem){.md\:sticky{position:sticky!important}.md\:top-0{top:0!important}.md\:col-span-5{grid-column:span 5/span 5!important}.md\:flex{display:flex!important}.md\:grid{display:grid!important}.md\:h-screen{height:100vh!important}.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))!important}.md\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))!important}.md\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))!important}.md\:grid-cols-\[1fr_1fr_140px_auto_auto\]{grid-template-columns:1fr 1fr 140px auto auto!important}.md\:grid-cols-\[1fr_110px\]{grid-template-columns:1fr 110px!important}.md\:grid-cols-\[1fr_130px_90px_110px_auto\]{grid-template-columns:1fr 130px 90px 110px auto!important}.md\:grid-cols-\[1fr_140px_1fr_150px\]{grid-template-columns:1fr 140px 1fr 150px!important}.md\:grid-cols-\[248px_1fr\]{grid-template-columns:248px 1fr!important}.md\:grid-cols-\[260px_1fr\]{grid-template-columns:260px 1fr!important}.md\:grid-cols-\[280px_1fr\]{grid-template-columns:280px 1fr!important}.md\:grid-cols-\[300px_1fr\]{grid-template-columns:300px 1fr!important}.md\:grid-cols-\[340px_1fr\]{grid-template-columns:340px 1fr!important}.md\:flex-col{flex-direction:column!important}.md\:overflow-visible{overflow:visible!important}.md\:border-r{border-right-style:var(--tw-border-style)!important;border-right-width:1px!important}.md\:border-b-0{border-bottom-style:var(--tw-border-style)!important;border-bottom-width:0!important}.md\:p-8{padding:calc(var(--spacing) * 8)!important}.md\:px-8{padding-inline:calc(var(--spacing) * 8)!important}}@media (min-width:64rem){.lg\:col-span-2{grid-column:span 2/span 2!important}.lg\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))!important}.lg\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))!important}}}}.xicms-admin,.xicms-admin *,.xicms-admin :before,.xicms-admin :after{box-sizing:border-box}.xicms-admin{-webkit-font-smoothing:antialiased;--color-white:#fff;--color-slate-50:#faf9f6;--color-slate-100:#f3f1ec;--color-slate-200:#e5e2da;--color-slate-300:#d5d1c7;--color-slate-400:#a8a399;--color-slate-500:#6e6a62;--color-slate-600:#57534b;--color-slate-700:#403c36;--color-slate-900:#1b1a17;--color-brand:var(--xicms-primary,#4f46e5);--color-brand-500:var(--xicms-primary,#4f46e5);--color-brand-600:var(--xicms-primary,#4f46e5);--color-brand-700:var(--xicms-primary,#4f46e5);font-family:system-ui,-apple-system,Segoe UI,Roboto,sans-serif;line-height:1.5}@supports (color:color-mix(in lab, red, red)){.xicms-admin{--color-brand-700:color-mix(in srgb, var(--xicms-primary,#4f46e5) 78%, #1b1a17)}}.xicms-admin{--color-brand-50:var(--xicms-primary,#4f46e5)}@supports (color:color-mix(in lab, red, red)){.xicms-admin{--color-brand-50:color-mix(in srgb, var(--xicms-primary,#4f46e5) 10%, #fff)}}.xicms-admin{--admin-canvas:#f6f5f2;--admin-sidebar:#f0eee9;--color-background-body:var(--color-white);--color-background-surface:var(--color-white);--color-background-card:var(--color-white);--color-background-muted:var(--color-slate-100);--color-text-primary:var(--color-slate-900);--color-text-secondary:var(--color-slate-500);--color-border:var(--color-slate-200);--color-accent:var(--color-brand-500);--color-on-accent:#fff;--color-error:#dc2626;color-scheme:light;color:var(--color-text-primary);background:var(--admin-canvas)}.xicms-admin h1,.xicms-admin h2,.xicms-admin h3,.xicms-admin p,.xicms-admin figure{margin:0}.xicms-admin button{font:inherit;cursor:pointer}.xicms-admin input,.xicms-admin textarea,.xicms-admin select{font:inherit;color:inherit}.xicms-admin .ProseMirror{outline:none;min-height:8rem}.xicms-admin .ProseMirror p{margin:0 0 .6rem}.xicms-admin .ProseMirror:focus{outline:none}@media (prefers-reduced-motion:reduce){.xicms-admin *,.xicms-admin :before,.xicms-admin :after,.xicms-ve *,.xicms-ve :before,.xicms-ve :after{transition-duration:.001ms!important;animation-duration:.001ms!important;animation-iteration-count:1!important}}.xicms-admin__midden,.xicms-admin__raster{min-height:100vh}.xicms-admin--ingebed .xicms-admin__midden{min-height:50vh}.xicms-admin--ingebed .xicms-admin__raster{min-height:60vh;display:block}.xicms-admin--ingebed .xicms-admin__rail{border-bottom-width:1px;border-right:0;flex-flow:wrap;align-items:center;gap:1rem;height:auto;padding:.5rem 1rem;position:static}.xicms-admin--ingebed .xicms-admin__lijst{flex-direction:row;overflow-x:auto}.xicms-admin--ingebed .xicms-admin__voet{border-top:0;flex-direction:row;align-items:center;gap:.75rem;margin-top:0;margin-left:auto;padding-top:0;display:flex}.xicms-admin--ingebed .xicms-admin__kop{position:static}.xicms-admin--ingebed{width:100vw;max-width:100vw;margin-left:calc(50% - 50vw);margin-right:calc(50% - 50vw)}@property --tw-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-y{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-z{syntax:"*";inherits:false;initial-value:0}@property --tw-rotate-x{syntax:"*";inherits:false}@property --tw-rotate-y{syntax:"*";inherits:false}@property --tw-rotate-z{syntax:"*";inherits:false}@property --tw-skew-x{syntax:"*";inherits:false}@property --tw-skew-y{syntax:"*";inherits:false}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-ordinal{syntax:"*";inherits:false}@property --tw-slashed-zero{syntax:"*";inherits:false}@property --tw-numeric-figure{syntax:"*";inherits:false}@property --tw-numeric-spacing{syntax:"*";inherits:false}@property --tw-numeric-fraction{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"<length>";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-outline-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false}@property --tw-ease{syntax:"*";inherits:false}
|
|
2
|
+
@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-border-style:solid;--tw-font-weight:initial;--tw-tracking:initial;--tw-ordinal:initial;--tw-slashed-zero:initial;--tw-numeric-figure:initial;--tw-numeric-spacing:initial;--tw-numeric-fraction:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-outline-style:solid;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial;--tw-ease:initial}}}@layer theme{:root,:host{--font-mono:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--color-red-50:oklch(97.1% .013 17.38);--color-red-200:oklch(88.5% .062 18.334);--color-red-600:oklch(57.7% .245 27.325);--color-red-700:oklch(50.5% .213 27.518);--color-amber-50:oklch(98.7% .022 95.277);--color-amber-300:oklch(87.9% .169 91.605);--color-amber-600:oklch(66.6% .179 58.318);--color-amber-700:oklch(55.5% .163 48.998);--color-amber-800:oklch(47.3% .137 46.201);--color-amber-900:oklch(41.4% .112 45.904);--color-emerald-700:oklch(50.8% .118 165.612);--color-blue-600:oklch(54.6% .245 262.881);--color-indigo-500:oklch(58.5% .233 277.117);--color-slate-50:oklch(98.4% .003 247.858);--color-slate-100:oklch(96.8% .007 247.896);--color-slate-200:oklch(92.9% .013 255.508);--color-slate-300:oklch(86.9% .022 252.894);--color-slate-400:oklch(70.4% .04 256.788);--color-slate-500:oklch(55.4% .046 257.417);--color-slate-600:oklch(44.6% .043 257.281);--color-slate-700:oklch(37.2% .044 257.287);--color-slate-800:oklch(27.9% .041 260.031);--color-slate-900:oklch(20.8% .042 265.755);--color-white:#fff;--spacing:.25rem;--container-xs:20rem;--container-sm:24rem;--container-md:28rem;--container-3xl:48rem;--container-4xl:56rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-base:1rem;--text-base--line-height:calc(1.5 / 1);--text-lg:1.125rem;--text-lg--line-height:calc(1.75 / 1.125);--text-xl:1.25rem;--text-xl--line-height:calc(1.75 / 1.25);--text-3xl:1.875rem;--text-3xl--line-height:calc(2.25 / 1.875);--font-weight-normal:400;--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--tracking-wide:.025em;--radius-md:.375rem;--radius-lg:.5rem;--radius-xl:.75rem;--ease-out:cubic-bezier(0, 0, .2, 1);--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--color-brand:#4f46e5;--color-brand-50:#eef2ff;--color-brand-500:#4f46e5;--color-brand-600:#4f46e5;--color-brand-700:#3730a3}}@scope(.xicms-admin){@layer utilities{.pointer-events-none{pointer-events:none!important}.visible{visibility:visible!important}.sr-only{clip-path:inset(50%)!important;white-space:nowrap!important;border-width:0!important;width:1px!important;height:1px!important;margin:-1px!important;padding:0!important;position:absolute!important;overflow:hidden!important}.absolute{position:absolute!important}.fixed{position:fixed!important}.relative{position:relative!important}.static{position:static!important}.sticky{position:sticky!important}.inset-x-0{inset-inline:0!important}.-top-0\.5{top:calc(var(--spacing) * -.5)!important}.-top-2{top:calc(var(--spacing) * -2)!important}.top-0{top:0!important}.-right-2{right:calc(var(--spacing) * -2)!important}.right-0{right:0!important}.bottom-0{bottom:0!important}.z-10{z-index:10!important}.col-span-2{grid-column:span 2/span 2!important}.col-span-full{grid-column:1/-1!important}.container{width:100%!important}@media (min-width:40rem){.container{max-width:40rem!important}}@media (min-width:48rem){.container{max-width:48rem!important}}@media (min-width:64rem){.container{max-width:64rem!important}}@media (min-width:80rem){.container{max-width:80rem!important}}@media (min-width:96rem){.container{max-width:96rem!important}}.-mx-1{margin-inline:calc(var(--spacing) * -1)!important}.-mx-4{margin-inline:calc(var(--spacing) * -4)!important}.mt-0\.5{margin-top:calc(var(--spacing) * .5)!important}.mt-1{margin-top:var(--spacing)!important}.mt-2{margin-top:calc(var(--spacing) * 2)!important}.mt-3{margin-top:calc(var(--spacing) * 3)!important}.mt-4{margin-top:calc(var(--spacing) * 4)!important}.mt-5{margin-top:calc(var(--spacing) * 5)!important}.mt-6{margin-top:calc(var(--spacing) * 6)!important}.mt-auto{margin-top:auto!important}.mr-1{margin-right:var(--spacing)!important}.mr-2{margin-right:calc(var(--spacing) * 2)!important}.mb-1{margin-bottom:var(--spacing)!important}.mb-2{margin-bottom:calc(var(--spacing) * 2)!important}.mb-3{margin-bottom:calc(var(--spacing) * 3)!important}.mb-4{margin-bottom:calc(var(--spacing) * 4)!important}.ml-1{margin-left:var(--spacing)!important}.ml-1\.5{margin-left:calc(var(--spacing) * 1.5)!important}.ml-auto{margin-left:auto!important}.block{display:block!important}.contents{display:contents!important}.flex{display:flex!important}.grid{display:grid!important}.hidden{display:none!important}.inline{display:inline!important}.inline-block{display:inline-block!important}.inline-flex{display:inline-flex!important}.table{display:table!important}.aspect-square{aspect-ratio:1!important}.h-1\.5{height:calc(var(--spacing) * 1.5)!important}.h-2{height:calc(var(--spacing) * 2)!important}.h-5{height:calc(var(--spacing) * 5)!important}.h-6{height:calc(var(--spacing) * 6)!important}.h-8{height:calc(var(--spacing) * 8)!important}.h-9{height:calc(var(--spacing) * 9)!important}.h-10{height:calc(var(--spacing) * 10)!important}.h-14{height:calc(var(--spacing) * 14)!important}.h-16{height:calc(var(--spacing) * 16)!important}.h-28{height:calc(var(--spacing) * 28)!important}.h-32{height:calc(var(--spacing) * 32)!important}.h-\[70vh\]{height:70vh!important}.h-fit{height:fit-content!important}.h-full{height:100%!important}.max-h-\[55vh\]{max-height:55vh!important}.max-h-\[60vh\]{max-height:60vh!important}.min-h-\[8rem\]{min-height:8rem!important}.min-h-screen{min-height:100vh!important}.w-2{width:calc(var(--spacing) * 2)!important}.w-5{width:calc(var(--spacing) * 5)!important}.w-6{width:calc(var(--spacing) * 6)!important}.w-8{width:calc(var(--spacing) * 8)!important}.w-14{width:calc(var(--spacing) * 14)!important}.w-16{width:calc(var(--spacing) * 16)!important}.w-20{width:calc(var(--spacing) * 20)!important}.w-24{width:calc(var(--spacing) * 24)!important}.w-28{width:calc(var(--spacing) * 28)!important}.w-32{width:calc(var(--spacing) * 32)!important}.w-36{width:calc(var(--spacing) * 36)!important}.w-44{width:calc(var(--spacing) * 44)!important}.w-56{width:calc(var(--spacing) * 56)!important}.w-fit{width:fit-content!important}.w-full{width:100%!important}.max-w-3xl{max-width:var(--container-3xl)!important}.max-w-4xl{max-width:var(--container-4xl)!important}.max-w-md{max-width:var(--container-md)!important}.max-w-sm{max-width:var(--container-sm)!important}.max-w-xs{max-width:var(--container-xs)!important}.min-w-0{min-width:0!important}.min-w-8{min-width:calc(var(--spacing) * 8)!important}.flex-1{flex:1!important}.shrink-0{flex-shrink:0!important}.-translate-x-1\/2{--tw-translate-x:calc(calc(1 / 2 * 100%) * -1)!important;translate:var(--tw-translate-x) var(--tw-translate-y)!important}.-translate-y-1\/2{--tw-translate-y:calc(calc(1 / 2 * 100%) * -1)!important;translate:var(--tw-translate-x) var(--tw-translate-y)!important}.transform{transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)!important}.cursor-default{cursor:default!important}.cursor-pointer{cursor:pointer!important}.resize{resize:both!important}.list-decimal{list-style-type:decimal!important}.list-disc{list-style-type:disc!important}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))!important}.grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))!important}.grid-cols-\[1fr_1\.4fr_auto\]{grid-template-columns:1fr 1.4fr auto!important}.grid-cols-\[120px_1fr\]{grid-template-columns:120px 1fr!important}.grid-cols-\[minmax\(0\,1fr\)_auto\]{grid-template-columns:minmax(0,1fr) auto!important}.flex-col{flex-direction:column!important}.flex-wrap{flex-wrap:wrap!important}.place-items-center{place-items:center!important}.items-baseline{align-items:baseline!important}.items-center{align-items:center!important}.items-end{align-items:flex-end!important}.items-start{align-items:flex-start!important}.justify-between{justify-content:space-between!important}.justify-center{justify-content:center!important}.justify-end{justify-content:flex-end!important}.gap-0\.5{gap:calc(var(--spacing) * .5)!important}.gap-1{gap:var(--spacing)!important}.gap-1\.5{gap:calc(var(--spacing) * 1.5)!important}.gap-2{gap:calc(var(--spacing) * 2)!important}.gap-2\.5{gap:calc(var(--spacing) * 2.5)!important}.gap-3{gap:calc(var(--spacing) * 3)!important}.gap-4{gap:calc(var(--spacing) * 4)!important}.gap-6{gap:calc(var(--spacing) * 6)!important}.gap-x-3{column-gap:calc(var(--spacing) * 3)!important}.gap-x-5{column-gap:calc(var(--spacing) * 5)!important}.gap-y-1{row-gap:var(--spacing)!important}.self-center{align-self:center!important}.self-end{align-self:flex-end!important}.truncate{text-overflow:ellipsis!important;white-space:nowrap!important;overflow:hidden!important}.overflow-auto{overflow:auto!important}.overflow-hidden{overflow:hidden!important}.overflow-x-auto{overflow-x:auto!important}.overflow-y-auto{overflow-y:auto!important}.rounded{border-radius:.25rem!important}.rounded-full{border-radius:3.40282e38px!important}.rounded-lg{border-radius:var(--radius-lg)!important}.rounded-md{border-radius:var(--radius-md)!important}.rounded-xl{border-radius:var(--radius-xl)!important}.rounded-t-lg{border-top-left-radius:var(--radius-lg)!important;border-top-right-radius:var(--radius-lg)!important}.rounded-b-lg{border-bottom-right-radius:var(--radius-lg)!important;border-bottom-left-radius:var(--radius-lg)!important}.border{border-style:var(--tw-border-style)!important;border-width:1px!important}.border-t{border-top-style:var(--tw-border-style)!important;border-top-width:1px!important}.border-t-0{border-top-style:var(--tw-border-style)!important;border-top-width:0!important}.border-b{border-bottom-style:var(--tw-border-style)!important;border-bottom-width:1px!important}.border-amber-300{border-color:var(--color-amber-300)!important}.border-brand-500{border-color:var(--color-brand-500)!important}.border-red-200{border-color:var(--color-red-200)!important}.border-slate-100{border-color:var(--color-slate-100)!important}.border-slate-200{border-color:var(--color-slate-200)!important}.border-slate-300{border-color:var(--color-slate-300)!important}.bg-amber-50{background-color:var(--color-amber-50)!important}.bg-brand-50{background-color:var(--color-brand-50)!important}.bg-red-50{background-color:var(--color-red-50)!important}.bg-slate-50{background-color:var(--color-slate-50)!important}.bg-slate-100{background-color:var(--color-slate-100)!important}.bg-slate-200{background-color:var(--color-slate-200)!important}.bg-slate-700{background-color:var(--color-slate-700)!important}.bg-white{background-color:var(--color-white)!important}.object-cover{object-fit:cover!important}.p-0\.5{padding:calc(var(--spacing) * .5)!important}.p-1{padding:var(--spacing)!important}.p-2{padding:calc(var(--spacing) * 2)!important}.p-3{padding:calc(var(--spacing) * 3)!important}.p-4{padding:calc(var(--spacing) * 4)!important}.p-5{padding:calc(var(--spacing) * 5)!important}.p-6{padding:calc(var(--spacing) * 6)!important}.px-1{padding-inline:var(--spacing)!important}.px-1\.5{padding-inline:calc(var(--spacing) * 1.5)!important}.px-2{padding-inline:calc(var(--spacing) * 2)!important}.px-2\.5{padding-inline:calc(var(--spacing) * 2.5)!important}.px-3{padding-inline:calc(var(--spacing) * 3)!important}.px-4{padding-inline:calc(var(--spacing) * 4)!important}.py-0\.5{padding-block:calc(var(--spacing) * .5)!important}.py-1{padding-block:var(--spacing)!important}.py-1\.5{padding-block:calc(var(--spacing) * 1.5)!important}.py-2{padding-block:calc(var(--spacing) * 2)!important}.py-3{padding-block:calc(var(--spacing) * 3)!important}.py-8{padding-block:calc(var(--spacing) * 8)!important}.py-16{padding-block:calc(var(--spacing) * 16)!important}.pt-0{padding-top:0!important}.pt-1{padding-top:var(--spacing)!important}.pt-3{padding-top:calc(var(--spacing) * 3)!important}.pt-4{padding-top:calc(var(--spacing) * 4)!important}.pt-5{padding-top:calc(var(--spacing) * 5)!important}.pr-2{padding-right:calc(var(--spacing) * 2)!important}.pl-1{padding-left:var(--spacing)!important}.pl-5{padding-left:calc(var(--spacing) * 5)!important}.text-center{text-align:center!important}.text-left{text-align:left!important}.text-right{text-align:right!important}.align-top{vertical-align:top!important}.font-mono{font-family:var(--font-mono)!important}.text-3xl{font-size:var(--text-3xl)!important;line-height:var(--tw-leading,var(--text-3xl--line-height))!important}.text-base{font-size:var(--text-base)!important;line-height:var(--tw-leading,var(--text-base--line-height))!important}.text-lg{font-size:var(--text-lg)!important;line-height:var(--tw-leading,var(--text-lg--line-height))!important}.text-sm{font-size:var(--text-sm)!important;line-height:var(--tw-leading,var(--text-sm--line-height))!important}.text-xl{font-size:var(--text-xl)!important;line-height:var(--tw-leading,var(--text-xl--line-height))!important}.text-xs{font-size:var(--text-xs)!important;line-height:var(--tw-leading,var(--text-xs--line-height))!important}.text-\[10px\]{font-size:10px!important}.text-\[11px\]{font-size:11px!important}.font-bold{--tw-font-weight:var(--font-weight-bold)!important;font-weight:var(--font-weight-bold)!important}.font-medium{--tw-font-weight:var(--font-weight-medium)!important;font-weight:var(--font-weight-medium)!important}.font-normal{--tw-font-weight:var(--font-weight-normal)!important;font-weight:var(--font-weight-normal)!important}.font-semibold{--tw-font-weight:var(--font-weight-semibold)!important;font-weight:var(--font-weight-semibold)!important}.tracking-wide{--tw-tracking:var(--tracking-wide)!important;letter-spacing:var(--tracking-wide)!important}.whitespace-nowrap{white-space:nowrap!important}.whitespace-pre-wrap{white-space:pre-wrap!important}.text-amber-600{color:var(--color-amber-600)!important}.text-amber-700{color:var(--color-amber-700)!important}.text-amber-800{color:var(--color-amber-800)!important}.text-amber-900{color:var(--color-amber-900)!important}.text-brand-700{color:var(--color-brand-700)!important}.text-brand-700\/70{color:#3730a3b3!important}@supports (color:color-mix(in lab, red, red)){.text-brand-700\/70{color:color-mix(in oklab, var(--color-brand-700) 70%, transparent)!important}}.text-emerald-700{color:var(--color-emerald-700)!important}.text-red-600{color:var(--color-red-600)!important}.text-red-700{color:var(--color-red-700)!important}.text-slate-400{color:var(--color-slate-400)!important}.text-slate-500{color:var(--color-slate-500)!important}.text-slate-600{color:var(--color-slate-600)!important}.text-slate-700{color:var(--color-slate-700)!important}.text-slate-800{color:var(--color-slate-800)!important}.text-slate-900{color:var(--color-slate-900)!important}.text-white{color:var(--color-white)!important}.uppercase{text-transform:uppercase!important}.italic{font-style:italic!important}.tabular-nums{--tw-numeric-spacing:tabular-nums!important;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)!important}.line-through{text-decoration-line:line-through!important}.underline{text-decoration-line:underline!important}.opacity-70{opacity:.7!important}.shadow-md{--tw-shadow:0 4px 6px -1px var(--tw-shadow-color,#0000001a), 0 2px 4px -2px var(--tw-shadow-color,#0000001a)!important;box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)!important}.shadow-sm{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a)!important;box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)!important}.outline{outline-style:var(--tw-outline-style)!important;outline-width:1px!important}.blur{--tw-blur:blur(8px)!important;filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)!important}.grayscale{--tw-grayscale:grayscale(100%)!important;filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)!important}.filter{filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)!important}.transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events!important;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function))!important;transition-duration:var(--tw-duration,var(--default-transition-duration))!important}.transition-\[filter\]{transition-property:filter!important;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function))!important;transition-duration:var(--tw-duration,var(--default-transition-duration))!important}.ease-out{--tw-ease:var(--ease-out)!important;transition-timing-function:var(--ease-out)!important}.outline-none{--tw-outline-style:none!important;outline-style:none!important}@media (hover:hover){.group-hover\:opacity-80:is(:where(.group):hover *){opacity:.8!important}}.last\:border-0:last-child{border-style:var(--tw-border-style)!important;border-width:0!important}@media (hover:hover){.hover\:border-brand-500:hover{border-color:var(--color-brand-500)!important}.hover\:bg-slate-50:hover{background-color:var(--color-slate-50)!important}.hover\:bg-slate-100:hover{background-color:var(--color-slate-100)!important}.hover\:bg-slate-200:hover{background-color:var(--color-slate-200)!important}.hover\:text-slate-600:hover{color:var(--color-slate-600)!important}.hover\:text-slate-700:hover{color:var(--color-slate-700)!important}.hover\:text-slate-900:hover{color:var(--color-slate-900)!important}.hover\:underline:hover{text-decoration-line:underline!important}.hover\:brightness-95:hover{--tw-brightness:brightness(95%)!important;filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)!important}}.focus\:ring-2:focus{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor)!important;box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)!important}.focus\:ring-indigo-500:focus{--tw-ring-color:var(--color-indigo-500)!important}.focus\:outline-none:focus{--tw-outline-style:none!important;outline-style:none!important}.focus-visible\:ring-2:focus-visible{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor)!important;box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)!important}.focus-visible\:ring-brand-500:focus-visible{--tw-ring-color:var(--color-brand-500)!important}.focus-visible\:outline-2:focus-visible{outline-style:var(--tw-outline-style)!important;outline-width:2px!important}.focus-visible\:outline-offset-2:focus-visible{outline-offset:2px!important}.focus-visible\:outline-blue-600:focus-visible{outline-color:var(--color-blue-600)!important}.focus-visible\:outline-none:focus-visible{--tw-outline-style:none!important;outline-style:none!important}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed!important}.disabled\:opacity-50:disabled{opacity:.5!important}.aria-\[current\=page\]\:bg-slate-200[aria-current=page]{background-color:var(--color-slate-200)!important}.aria-\[current\=page\]\:text-slate-900[aria-current=page]{color:var(--color-slate-900)!important}.data-\[state\=active\]\:border-b-2[data-state=active]{border-bottom-style:var(--tw-border-style)!important;border-bottom-width:2px!important}.data-\[state\=active\]\:border-brand-600[data-state=active]{border-color:var(--color-brand-600)!important}.data-\[state\=active\]\:bg-slate-100[data-state=active]{background-color:var(--color-slate-100)!important}.data-\[state\=active\]\:text-brand-700[data-state=active]{color:var(--color-brand-700)!important}.data-\[state\=active\]\:text-slate-900[data-state=active]{color:var(--color-slate-900)!important}.data-\[state\=active\]\:shadow-\[inset_2px_0_0_0_var\(--color-brand-500\)\][data-state=active]{--tw-shadow:inset 2px 0 0 0 var(--tw-shadow-color,var(--color-brand-500))!important;box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)!important}@media (min-width:40rem){.sm\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))!important}.sm\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))!important}.sm\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))!important}}@media (min-width:48rem){.md\:sticky{position:sticky!important}.md\:top-0{top:0!important}.md\:col-span-5{grid-column:span 5/span 5!important}.md\:flex{display:flex!important}.md\:grid{display:grid!important}.md\:h-screen{height:100vh!important}.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))!important}.md\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))!important}.md\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))!important}.md\:grid-cols-\[1fr_1fr_140px_auto_auto\]{grid-template-columns:1fr 1fr 140px auto auto!important}.md\:grid-cols-\[1fr_110px\]{grid-template-columns:1fr 110px!important}.md\:grid-cols-\[1fr_130px_90px_110px_auto\]{grid-template-columns:1fr 130px 90px 110px auto!important}.md\:grid-cols-\[1fr_140px_1fr_150px\]{grid-template-columns:1fr 140px 1fr 150px!important}.md\:grid-cols-\[248px_1fr\]{grid-template-columns:248px 1fr!important}.md\:grid-cols-\[260px_1fr\]{grid-template-columns:260px 1fr!important}.md\:grid-cols-\[280px_1fr\]{grid-template-columns:280px 1fr!important}.md\:grid-cols-\[300px_1fr\]{grid-template-columns:300px 1fr!important}.md\:grid-cols-\[340px_1fr\]{grid-template-columns:340px 1fr!important}.md\:flex-col{flex-direction:column!important}.md\:overflow-visible{overflow:visible!important}.md\:border-r{border-right-style:var(--tw-border-style)!important;border-right-width:1px!important}.md\:border-b-0{border-bottom-style:var(--tw-border-style)!important;border-bottom-width:0!important}.md\:p-8{padding:calc(var(--spacing) * 8)!important}.md\:px-8{padding-inline:calc(var(--spacing) * 8)!important}}@media (min-width:64rem){.lg\:col-span-2{grid-column:span 2/span 2!important}.lg\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))!important}.lg\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))!important}}}}.xicms-admin,.xicms-admin *,.xicms-admin :before,.xicms-admin :after{box-sizing:border-box}.xicms-admin{-webkit-font-smoothing:antialiased;--color-white:#fff;--color-slate-50:#faf9f6;--color-slate-100:#f3f1ec;--color-slate-200:#e5e2da;--color-slate-300:#d5d1c7;--color-slate-400:#a8a399;--color-slate-500:#6e6a62;--color-slate-600:#57534b;--color-slate-700:#403c36;--color-slate-900:#1b1a17;--color-brand:var(--xicms-primary,#4f46e5);--color-brand-500:var(--xicms-primary,#4f46e5);--color-brand-600:var(--xicms-primary,#4f46e5);--color-brand-700:var(--xicms-primary,#4f46e5);font-family:system-ui,-apple-system,Segoe UI,Roboto,sans-serif;line-height:1.5}@supports (color:color-mix(in lab, red, red)){.xicms-admin{--color-brand-700:color-mix(in srgb, var(--xicms-primary,#4f46e5) 78%, #1b1a17)}}.xicms-admin{--color-brand-50:var(--xicms-primary,#4f46e5)}@supports (color:color-mix(in lab, red, red)){.xicms-admin{--color-brand-50:color-mix(in srgb, var(--xicms-primary,#4f46e5) 10%, #fff)}}.xicms-admin{--admin-canvas:#f6f5f2;--admin-sidebar:#f0eee9;--color-background-body:var(--color-white);--color-background-surface:var(--color-white);--color-background-card:var(--color-white);--color-background-muted:var(--color-slate-100);--color-text-primary:var(--color-slate-900);--color-text-secondary:var(--color-slate-500);--color-border:var(--color-slate-200);--color-accent:var(--color-brand-500);--color-on-accent:#fff;--color-error:#dc2626;color-scheme:light;color:var(--color-text-primary);background:var(--admin-canvas)}.xicms-admin h1,.xicms-admin h2,.xicms-admin h3,.xicms-admin p,.xicms-admin figure{margin:0}.xicms-admin button{font:inherit;cursor:pointer}.xicms-admin input,.xicms-admin textarea,.xicms-admin select{font:inherit;color:inherit}.xicms-admin .ProseMirror{outline:none;min-height:8rem}.xicms-admin .ProseMirror p{margin:0 0 .6rem}.xicms-admin .ProseMirror:focus{outline:none}@media (prefers-reduced-motion:reduce){.xicms-admin *,.xicms-admin :before,.xicms-admin :after,.xicms-ve *,.xicms-ve :before,.xicms-ve :after{transition-duration:.001ms!important;animation-duration:.001ms!important;animation-iteration-count:1!important}}.xicms-admin__midden,.xicms-admin__raster{min-height:100vh}.xicms-admin--ingebed .xicms-admin__midden{min-height:50vh}.xicms-admin--ingebed .xicms-admin__raster{min-height:60vh;display:block}.xicms-admin--ingebed .xicms-admin__rail{border-bottom-width:1px;border-right:0;flex-flow:wrap;align-items:center;gap:1rem;height:auto;padding:.5rem 1rem;position:static}.xicms-admin--ingebed .xicms-admin__lijst{flex-direction:row;overflow-x:auto}.xicms-admin--ingebed .xicms-admin__voet{border-top:0;flex-direction:row;align-items:center;gap:.75rem;margin-top:0;margin-left:auto;padding-top:0;display:flex}.xicms-admin--ingebed .xicms-admin__kop{position:static}.xicms-admin--ingebed{width:100vw;max-width:100vw;margin-left:calc(50% - 50vw);margin-right:calc(50% - 50vw)}@property --tw-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-y{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-z{syntax:"*";inherits:false;initial-value:0}@property --tw-rotate-x{syntax:"*";inherits:false}@property --tw-rotate-y{syntax:"*";inherits:false}@property --tw-rotate-z{syntax:"*";inherits:false}@property --tw-skew-x{syntax:"*";inherits:false}@property --tw-skew-y{syntax:"*";inherits:false}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-ordinal{syntax:"*";inherits:false}@property --tw-slashed-zero{syntax:"*";inherits:false}@property --tw-numeric-figure{syntax:"*";inherits:false}@property --tw-numeric-spacing{syntax:"*";inherits:false}@property --tw-numeric-fraction{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"<length>";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-outline-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false}@property --tw-ease{syntax:"*";inherits:false}
|
package/dist/schema/shop.d.ts
CHANGED
|
@@ -143,6 +143,7 @@ export declare const DiscountCodeSchema: z.ZodMiniObject<{
|
|
|
143
143
|
endsAt: z.ZodMiniOptional<z.ZodMiniString<string>>;
|
|
144
144
|
maxUses: z.ZodMiniDefault<z.ZodMiniNumberFormat>;
|
|
145
145
|
usedCount: z.ZodMiniDefault<z.ZodMiniNumberFormat>;
|
|
146
|
+
oncePerCustomer: z.ZodMiniDefault<z.ZodMiniBoolean<boolean>>;
|
|
146
147
|
}, z.core.$strip>;
|
|
147
148
|
export type DiscountCode = z.infer<typeof DiscountCodeSchema>;
|
|
148
149
|
export { MAIL_TEMPLATE_KEYS, MAIL_TEMPLATE_DEFS, MailTemplateSchema, MailTemplatesSchema, type MailTemplateKey, type MailTemplates, } from "./mail.js";
|
|
@@ -171,6 +172,7 @@ export declare const OrderItemSchema: z.ZodMiniObject<{
|
|
|
171
172
|
imagePath: z.ZodMiniOptional<z.ZodMiniString<string>>;
|
|
172
173
|
fileName: z.ZodMiniOptional<z.ZodMiniString<string>>;
|
|
173
174
|
}, z.core.$strip>>>;
|
|
175
|
+
personalizationCents: z.ZodMiniDefault<z.ZodMiniNumberFormat>;
|
|
174
176
|
}, z.core.$strip>;
|
|
175
177
|
export type OrderItem = z.infer<typeof OrderItemSchema>;
|
|
176
178
|
export declare const OrderAddressSchema: z.ZodMiniObject<{
|
|
@@ -212,6 +214,7 @@ export declare const OrderSchema: z.ZodMiniObject<{
|
|
|
212
214
|
imagePath: z.ZodMiniOptional<z.ZodMiniString<string>>;
|
|
213
215
|
fileName: z.ZodMiniOptional<z.ZodMiniString<string>>;
|
|
214
216
|
}, z.core.$strip>>>;
|
|
217
|
+
personalizationCents: z.ZodMiniDefault<z.ZodMiniNumberFormat>;
|
|
215
218
|
}, z.core.$strip>>;
|
|
216
219
|
amount: z.ZodMiniObject<{
|
|
217
220
|
itemsCents: z.ZodMiniNumberFormat;
|
|
@@ -240,6 +243,14 @@ export declare const OrderSchema: z.ZodMiniObject<{
|
|
|
240
243
|
country: z.ZodMiniDefault<z.ZodMiniString<string>>;
|
|
241
244
|
}, z.core.$strip>>;
|
|
242
245
|
note: z.ZodMiniOptional<z.ZodMiniString<string>>;
|
|
246
|
+
adminNote: z.ZodMiniOptional<z.ZodMiniString<string>>;
|
|
247
|
+
refundedCents: z.ZodMiniOptional<z.ZodMiniNumberFormat>;
|
|
248
|
+
messages: z.ZodMiniOptional<z.ZodMiniArray<z.ZodMiniObject<{
|
|
249
|
+
at: z.ZodMiniString<string>;
|
|
250
|
+
by: z.ZodMiniOptional<z.ZodMiniString<string>>;
|
|
251
|
+
subject: z.ZodMiniString<string>;
|
|
252
|
+
body: z.ZodMiniString<string>;
|
|
253
|
+
}, z.core.$strip>>>;
|
|
243
254
|
trackingUrl: z.ZodMiniOptional<z.ZodMiniString<string>>;
|
|
244
255
|
invoicePath: z.ZodMiniOptional<z.ZodMiniString<string>>;
|
|
245
256
|
stripe: z.ZodMiniDefault<z.ZodMiniObject<{
|
package/dist/schema/shop.js
CHANGED
|
@@ -97,7 +97,12 @@ const DiscountCodeSchema = z.object({
|
|
|
97
97
|
endsAt: z.optional(z.string().check(z.maxLength(10))),
|
|
98
98
|
/** 0 = onbeperkt. `usedCount` wordt door de webhook transactioneel opgehoogd. */
|
|
99
99
|
maxUses: z._default(z.int().check(z.minimum(0)).check(z.maximum(1e6)), 0),
|
|
100
|
-
usedCount: z._default(z.int().check(z.minimum(0)), 0)
|
|
100
|
+
usedCount: z._default(z.int().check(z.minimum(0)), 0),
|
|
101
|
+
/**
|
|
102
|
+
* Eén keer per e-mailadres (7.18) — de klassieke welkomstkorting. De server telt de eerder
|
|
103
|
+
* BETAALDE orders van dat adres met deze code; een afgebroken checkout brandt hem dus niet op.
|
|
104
|
+
*/
|
|
105
|
+
oncePerCustomer: z._default(z.boolean(), false)
|
|
101
106
|
});
|
|
102
107
|
import {
|
|
103
108
|
MAIL_TEMPLATE_KEYS,
|
|
@@ -116,7 +121,16 @@ const OrderItemSchema = z.object({
|
|
|
116
121
|
unitPriceCents: z.int().check(z.minimum(0)).check(z.maximum(1e8)),
|
|
117
122
|
vatRate: z._default(z.union([z.literal(21), z.literal(9), z.literal(0)]), 21),
|
|
118
123
|
/** 5.6: ingevulde personalisatie ("Naam: Boaz", upload-pad). */
|
|
119
|
-
personalization: z.optional(PersonalizationValueSchema)
|
|
124
|
+
personalization: z.optional(PersonalizationValueSchema),
|
|
125
|
+
/**
|
|
126
|
+
* 7.18: het deel van `unitPriceCents` dat personalisatie-meerprijs is.
|
|
127
|
+
*
|
|
128
|
+
* De meerprijs werkte al end-to-end, maar zat onzichtbaar in de stukprijs: een klant die
|
|
129
|
+
* € 16,95 betaalde voor een product van € 13,95 zag nergens waar die € 3,00 vandaan kwam.
|
|
130
|
+
* Apart opslaan (i.p.v. bij het tonen terugrekenen) houdt de historie kloppend als de
|
|
131
|
+
* meerprijs van het product later verandert.
|
|
132
|
+
*/
|
|
133
|
+
personalizationCents: z._default(z.int().check(z.minimum(0)), 0)
|
|
120
134
|
});
|
|
121
135
|
const OrderAddressSchema = z.object({
|
|
122
136
|
address: z.string().check(z.minLength(1)).check(z.maxLength(200)),
|
|
@@ -150,6 +164,17 @@ const OrderSchema = z.object({
|
|
|
150
164
|
customer: OrderCustomerSchema,
|
|
151
165
|
shipping: z.optional(OrderAddressSchema),
|
|
152
166
|
note: z.optional(z.string().check(z.maxLength(2e3))),
|
|
167
|
+
/** 7.18: interne notitie van de winkelier — de klant ziet hem nooit. */
|
|
168
|
+
adminNote: z.optional(z.string().check(z.maxLength(2e3))),
|
|
169
|
+
/** 7.18: som van alle (gedeeltelijke) terugbetalingen in centen. */
|
|
170
|
+
refundedCents: z.optional(z.int().check(z.minimum(0))),
|
|
171
|
+
/** 7.18: berichten die de winkelier vanuit /beheer aan de klant stuurde. */
|
|
172
|
+
messages: z.optional(z.array(z.object({
|
|
173
|
+
at: z.string().check(z.maxLength(40)),
|
|
174
|
+
by: z.optional(z.string().check(z.maxLength(200))),
|
|
175
|
+
subject: z.string().check(z.maxLength(200)),
|
|
176
|
+
body: z.string().check(z.maxLength(2e3))
|
|
177
|
+
})).check(z.maxLength(50))),
|
|
153
178
|
/** 5.6: track & trace-link (gezet bij "verzonden"). */
|
|
154
179
|
trackingUrl: z.optional(z.string().check(z.maxLength(500))),
|
|
155
180
|
/** 5.6: factuur-pad in Storage (gezet door de webhook). */
|
package/dist/schema/shop.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/schema/shop.ts"],"sourcesContent":["import * as z from \"zod/mini\";\nimport { blockBase } from \"./blockBase\";\n\n/**\n * Shop-schema's (5.5) — webwinkel + donaties. Pure Zod: gedeeld door core, admin en Functions.\n *\n * Datamodel per tenant:\n * sites/{siteId}/shopCategories/{id} — categorieën (editor-write, publiek leesbaar)\n * sites/{siteId}/products/{id} — producten (editor-write, publiek leesbaar als actief)\n * sites/{siteId}/orders/{id} — bestellingen (ALLEEN via Functions; editor leest,\n * en mag alleen status/verzendvelden muteren)\n * sites/{siteId}/private/shop — geheimen (Stripe-key, SMTP) — deny-all, alleen Functions\n *\n * Geld is ALTIJD een integer in centen (nooit floats). Publieke shopconfig (verzendkosten,\n * paden, donatie-grenzen) leeft in de module-settings van de `shop`-module\n * (settings/public.modules.shop.config) — zie modules/shop/module.ts.\n */\n\n/* ── Categorieën ─────────────────────────────────────────────────────────── */\n\nexport const ShopCategorySchema = z.object({\n name: z.string().check(z.minLength(1)).check(z.maxLength(120)),\n slug: z.string().check(z.minLength(1)).check(z.maxLength(120)).check(z.regex(/^[a-z0-9-]+$/, \"slug: alleen kleine letters, cijfers en streepjes\")),\n description: z.optional(z.string().check(z.maxLength(2000))),\n imageUrl: z.optional(z.string().check(z.maxLength(2000))),\n order: z._default(z.int().check(z.minimum(0)).check(z.maximum(9999)), 0),\n visible: z._default(z.boolean(), true),\n});\nexport type ShopCategory = z.infer<typeof ShopCategorySchema>;\n\n/* ── Producten ───────────────────────────────────────────────────────────── */\n\n/** Optie-as, bv. { name: \"Maat\", values: [\"56\",\"62\",\"68\",\"74\"] }. Max 3 assen per product. */\nexport const ProductOptionSchema = z.object({\n name: z.string().check(z.minLength(1)).check(z.maxLength(40)),\n values: z.array(z.string().check(z.minLength(1)).check(z.maxLength(60))).check(z.minLength(1)).check(z.maxLength(30)),\n /**\n * 5.6: afbeelding per optieWAARDE (praktijk: de kleur-as) — `{ \"Roze\": \"https://…\" }`.\n * De detailpagina wisselt het hoofdbeeld mee met de keuze; bewust per as en niet per\n * variant-combinatie (maat × kleur zou het aantal afbeeldingen laten exploderen).\n */\n images: z.optional(z.record(z.string(), z.string().check(z.maxLength(2000)))),\n});\nexport type ProductOption = z.infer<typeof ProductOptionSchema>;\n\n/**\n * Personalisatie-veld (5.6): de klant geeft eigen tekst of een eigen afbeelding op\n * (bv. \"rompertje met naam\"). Meerprijs telt server-side mee in `createOrder`.\n */\nexport const PersonalizationFieldSchema = z.object({\n key: z.string().check(z.minLength(1)).check(z.maxLength(40)).check(z.regex(/^[a-zA-Z][a-zA-Z0-9_]*$/, \"sleutel: letters/cijfers/underscore, beginnend met een letter\")),\n label: z.string().check(z.minLength(1)).check(z.maxLength(120)),\n type: z.enum([\"text\", \"image\"]),\n required: z._default(z.boolean(), false),\n /** Alleen voor type \"text\". */\n maxLength: z._default(z.int().check(z.minimum(1)).check(z.maximum(200)), 40),\n /** Meerprijs in centen (per stuk). */\n priceCents: z._default(z.int().check(z.minimum(0)).check(z.maximum(1_000_000_00)), 0),\n helpText: z.optional(z.string().check(z.maxLength(200))),\n});\nexport type PersonalizationField = z.infer<typeof PersonalizationFieldSchema>;\n\n/** Ingevulde personalisatie op een orderregel/cartregel: per veld-key tekst of upload-pad. */\nexport const PersonalizationValueSchema = z.record(\n z.string(),\n z.object({\n text: z.optional(z.string().check(z.maxLength(200))),\n /** Storage-pad uit de uploadPersonalization-callable (géén vrije URL). */\n imagePath: z.optional(z.string().check(z.maxLength(400))),\n /** Oorspronkelijke bestandsnaam (weergave in beheer/mail). */\n fileName: z.optional(z.string().check(z.maxLength(200))),\n }),\n);\nexport type PersonalizationValue = z.infer<typeof PersonalizationValueSchema>;\n\n/**\n * Variant = één combinatie van optiewaarden. `key` is de waarden gejoined met \"|\" in\n * optie-volgorde (bv. \"68|Roze\"). Alleen aanwezig waar hij afwijkt van de product-defaults.\n */\nexport const ProductVariantSchema = z.object({\n key: z.string().check(z.minLength(1)).check(z.maxLength(200)),\n priceCents: z.optional(z.int().check(z.minimum(0)).check(z.maximum(1_000_000_00))),\n stock: z.optional(z.int().check(z.minimum(0)).check(z.maximum(1_000_000))),\n sku: z.optional(z.string().check(z.maxLength(60))),\n active: z._default(z.boolean(), true),\n});\nexport type ProductVariant = z.infer<typeof ProductVariantSchema>;\n\nexport const ProductStatusSchema = z.enum([\"draft\", \"active\", \"archived\"]);\nexport type ProductStatus = z.infer<typeof ProductStatusSchema>;\n\n/**\n * Verwijdert top-level `null`-velden uit een productdoc vóór validatie (7.17.1).\n *\n * Er liggen productdocs met `personalization: null` (e.d.) in Firestore: de functions-SDK\n * encodeerde een client-side `undefined` als `null` over de draad (dezelfde klasse als de\n * \"+ Nieuw product\"-bug), en de callable schreef dat door. `z.optional()` accepteert een\n * ONTBREKEND veld maar geen `null`, en `parseProduct` op de leeskant slaat een ongeldig doc\n * stil over — waardoor een product dat gewoon \"actief\" in het beheer staat van de live winkel\n * verdween (gemeten op christelijkkraamcadeau: 2 van de 4 \"Maak een vrolijk geluid\"-varianten,\n * precies de twee die vóór de 7.16.2-fix nog eens waren opgeslagen). In dit model betekent\n * `null` nergens iets anders dan \"afwezig\", dus strippen is verliesvrij.\n */\nexport function saneerProductDoc<T>(data: T): T {\n if (typeof data !== \"object\" || data === null || Array.isArray(data)) return data;\n return Object.fromEntries(\n Object.entries(data as Record<string, unknown>).filter(([, v]) => v !== null),\n ) as T;\n}\n\nexport const ProductSchema = z.object({\n name: z.string().check(z.minLength(1)).check(z.maxLength(200)),\n slug: z.string().check(z.minLength(1)).check(z.maxLength(120)).check(z.regex(/^[a-z0-9-]+$/, \"slug: alleen kleine letters, cijfers en streepjes\")),\n /**\n * Beschrijving als platte tekst (regeleinden blijven behouden). Bewust géén HTML:\n * producten worden client-side door editors geschreven (rules-pad, niet via de\n * publish-sanitizer), dus HTML zou hier een XSS-route zijn.\n */\n description: z._default(z.string().check(z.maxLength(20_000)), \"\"),\n categoryIds: z._default(z.array(z.string().check(z.maxLength(80))).check(z.maxLength(20)), []),\n /** Afbeeldings-URL's (via de mediabibliotheek). Eerste = hoofdbeeld. */\n images: z._default(z.array(z.string().check(z.maxLength(2000))).check(z.maxLength(12)), []),\n /** Prijs incl. btw, in centen. */\n priceCents: z._default(z.int().check(z.minimum(0)).check(z.maximum(1_000_000_00)), 0),\n /** Doorgestreepte \"van\"-prijs (optioneel). */\n compareAtCents: z.optional(z.int().check(z.minimum(0)).check(z.maximum(1_000_000_00))),\n /** Btw-tarief (NL): 21, 9 of 0. Prijzen zijn inclusief. */\n vatRate: z._default(z.union([z.literal(21), z.literal(9), z.literal(0)]), 21),\n options: z._default(z.array(ProductOptionSchema).check(z.maxLength(3)), []),\n variants: z._default(z.array(ProductVariantSchema).check(z.maxLength(200)), []),\n /** Voorraad bijhouden? false = altijd bestelbaar (op-maat/print-on-demand). */\n trackStock: z._default(z.boolean(), false),\n /** Voorraad voor producten zónder varianten (met varianten telt de variant-stock). */\n stock: z.optional(z.int().check(z.minimum(0)).check(z.maximum(1_000_000))),\n status: z._default(ProductStatusSchema, \"draft\"),\n /** Sorteervolgorde binnen een categorie (laag = eerst). */\n order: z._default(z.int().check(z.minimum(0)).check(z.maximum(9999)), 0),\n featured: z._default(z.boolean(), false),\n /** 5.6: personalisatie-velden (eigen naam/logo) — max 3 per product. */\n personalization: z.optional(z.array(PersonalizationFieldSchema).check(z.maxLength(3))),\n});\nexport type Product = z.infer<typeof ProductSchema>;\n\n/* ── Kortingscodes ───────────────────────────────────────────────────────── */\n\n/** `sites/{siteId}/discountCodes/{CODE}` (doc-id = code in hoofdletters; editor beheert). */\nexport const DiscountCodeSchema = z.object({\n code: z.string().check(z.minLength(3)).check(z.maxLength(30)).check(z.regex(/^[A-Z0-9-]+$/, \"code: hoofdletters, cijfers en streepjes\")),\n type: z.enum([\"percent\", \"fixed\"]),\n /** percent: 1–100; fixed: bedrag in centen. */\n value: z.int().check(z.minimum(1)).check(z.maximum(1_000_000_00)),\n minOrderCents: z._default(z.int().check(z.minimum(0)).check(z.maximum(1_000_000_00)), 0),\n freeShipping: z._default(z.boolean(), false),\n active: z._default(z.boolean(), true),\n /** ISO-datums (yyyy-mm-dd); leeg = geen grens. */\n startsAt: z.optional(z.string().check(z.maxLength(10))),\n endsAt: z.optional(z.string().check(z.maxLength(10))),\n /** 0 = onbeperkt. `usedCount` wordt door de webhook transactioneel opgehoogd. */\n maxUses: z._default(z.int().check(z.minimum(0)).check(z.maximum(1_000_000)), 0),\n usedCount: z._default(z.int().check(z.minimum(0)), 0),\n});\nexport type DiscountCode = z.infer<typeof DiscountCodeSchema>;\n\n/* ── Mail-templates ──────────────────────────────────────────────────────── */\n\n// Mailtemplates zijn site-breed (ook zonder shop) — de canonieke definitie staat in `./mail`.\n// Hier her-geëxporteerd voor backward-compat van bestaande shop-importeurs.\nexport {\n MAIL_TEMPLATE_KEYS,\n MAIL_TEMPLATE_DEFS,\n MailTemplateSchema,\n MailTemplatesSchema,\n type MailTemplateKey,\n type MailTemplates,\n} from \"./mail\";\n\n/* ── Bestellingen ────────────────────────────────────────────────────────── */\n\nexport const OrderStatusSchema = z.enum([\"pending\", \"paid\", \"shipped\", \"cancelled\", \"refunded\"]);\nexport type OrderStatus = z.infer<typeof OrderStatusSchema>;\n\n/** Orderregel: snapshot van naam/prijs op besteldatum — blijft kloppen als het product wijzigt. */\nexport const OrderItemSchema = z.object({\n kind: z._default(z.enum([\"product\", \"donation\"]), \"product\"),\n productId: z.optional(z.string().check(z.maxLength(80))),\n name: z.string().check(z.minLength(1)).check(z.maxLength(240)),\n variantKey: z.optional(z.string().check(z.maxLength(200))),\n qty: z.int().check(z.minimum(1)).check(z.maximum(999)),\n /** Incl. eventuele personalisatie-meerprijs. */\n unitPriceCents: z.int().check(z.minimum(0)).check(z.maximum(1_000_000_00)),\n vatRate: z._default(z.union([z.literal(21), z.literal(9), z.literal(0)]), 21),\n /** 5.6: ingevulde personalisatie (\"Naam: Boaz\", upload-pad). */\n personalization: z.optional(PersonalizationValueSchema),\n});\nexport type OrderItem = z.infer<typeof OrderItemSchema>;\n\nexport const OrderAddressSchema = z.object({\n address: z.string().check(z.minLength(1)).check(z.maxLength(200)),\n zip: z.string().check(z.minLength(1)).check(z.maxLength(20)),\n city: z.string().check(z.minLength(1)).check(z.maxLength(120)),\n country: z._default(z.string().check(z.minLength(2)).check(z.maxLength(2)), \"NL\"),\n});\nexport type OrderAddress = z.infer<typeof OrderAddressSchema>;\n\nexport const OrderCustomerSchema = z.object({\n email: z.email().check(z.maxLength(200)),\n name: z.string().check(z.minLength(1)).check(z.maxLength(200)),\n phone: z.optional(z.string().check(z.maxLength(40))),\n});\nexport type OrderCustomer = z.infer<typeof OrderCustomerSchema>;\n\n/** Volledige order zoals door Functions geschreven (timestamps komen daarbovenop). */\nexport const OrderSchema = z.object({\n number: z.string().check(z.minLength(1)).check(z.maxLength(40)),\n status: OrderStatusSchema,\n items: z.array(OrderItemSchema).check(z.minLength(1)).check(z.maxLength(50)),\n amount: z.object({\n itemsCents: z.int().check(z.minimum(0)),\n shippingCents: z.int().check(z.minimum(0)),\n /** 5.6: toegepaste korting (0 = geen). */\n discountCents: z._default(z.int().check(z.minimum(0)), 0),\n totalCents: z.int().check(z.minimum(0)),\n }),\n /** 5.6: toegepaste kortingscode (snapshot). */\n discount: z.optional(z.object({\n code: z.string().check(z.maxLength(30)),\n type: z.enum([\"percent\", \"fixed\"]),\n value: z.int(),\n freeShipping: z._default(z.boolean(), false),\n })),\n customer: OrderCustomerSchema,\n shipping: z.optional(OrderAddressSchema),\n note: z.optional(z.string().check(z.maxLength(2000))),\n /** 5.6: track & trace-link (gezet bij \"verzonden\"). */\n trackingUrl: z.optional(z.string().check(z.maxLength(500))),\n /** 5.6: factuur-pad in Storage (gezet door de webhook). */\n invoicePath: z.optional(z.string().check(z.maxLength(400))),\n stripe: z._default(z.object({\n sessionId: z.optional(z.string().check(z.maxLength(200))),\n paymentIntentId: z.optional(z.string().check(z.maxLength(200))),\n }), {}),\n});\nexport type Order = z.infer<typeof OrderSchema>;\n\n/* ── Callable-input (checkout) ───────────────────────────────────────────── */\n\n/**\n * Input van de `createOrder`-callable. Client-prijzen worden NOOIT vertrouwd: de functie\n * herrekent alles tegen de opgeslagen product-docs. Alleen `donationCents` komt van de\n * client (binnen de per-site ingestelde min/max van het donatie-block).\n */\nexport const CreateOrderItemInputSchema = z.object({\n productId: z.optional(z.string().check(z.minLength(1)).check(z.maxLength(80))),\n variantKey: z.optional(z.string().check(z.maxLength(200))),\n qty: z._default(z.int().check(z.minimum(1)).check(z.maximum(999)), 1),\n /** Alleen voor donaties (zonder productId). */\n donationCents: z.optional(z.int().check(z.minimum(50)).check(z.maximum(1_000_000_00))),\n /** 5.6: personalisatie-invoer (gevalideerd tegen product.personalization). */\n personalization: z.optional(PersonalizationValueSchema),\n});\n\nexport const CreateOrderInputSchema = z.object({\n siteId: z.string().check(z.minLength(1)).check(z.maxLength(80)),\n items: z.array(CreateOrderItemInputSchema).check(z.minLength(1)).check(z.maxLength(50)),\n customer: OrderCustomerSchema,\n shipping: z.optional(OrderAddressSchema),\n note: z.optional(z.string().check(z.maxLength(2000))),\n /** 5.6: kortingscode (server-side gevalideerd en verrekend). */\n discountCode: z.optional(z.string().check(z.maxLength(30))),\n /** Paden op de klantsite waarheen Stripe terug redirect. */\n successPath: z._default(z.string().check(z.maxLength(400)), \"/bedankt\"),\n cancelPath: z._default(z.string().check(z.maxLength(400)), \"/winkelwagen\"),\n /** Honeypot — moet leeg zijn (bot-detectie). */\n _hp: z.optional(z.string().check(z.maxLength(0))),\n});\nexport type CreateOrderInput = z.infer<typeof CreateOrderInputSchema>;\n\n/* ── Block-schema's ──────────────────────────────────────────────────────── */\n\nexport const ShopCategoriesBlockSchema = z.object({\n ...blockBase,\n _type: z.literal(\"shopCategories\"),\n heading: z.optional(z.string().check(z.maxLength(200))),\n columns: z._default(z.int().check(z.minimum(2)).check(z.maximum(4)), 3),\n});\n\nexport const ProductGridBlockSchema = z.object({\n ...blockBase,\n _type: z.literal(\"productGrid\"),\n heading: z.optional(z.string().check(z.maxLength(200))),\n /** Vast categorie-filter (slug). Leeg + useQueryCategory → filter via `?cat=` in de URL. */\n categorySlug: z.optional(z.string().check(z.maxLength(120))),\n useQueryCategory: z._default(z.boolean(), true),\n featuredOnly: z._default(z.boolean(), false),\n columns: z._default(z.int().check(z.minimum(2)).check(z.maximum(4)), 3),\n limit: z._default(z.int().check(z.minimum(1)).check(z.maximum(200)), 60),\n sort: z._default(z.enum([\"order\", \"name\", \"priceAsc\", \"priceDesc\"]), \"order\"),\n /** 5.6: filterchips (kleur/maat) + sorteerkeuze boven het grid. */\n showFilters: z._default(z.boolean(), false),\n});\n\nexport const ProductDetailBlockSchema = z.object({\n ...blockBase,\n _type: z.literal(\"productDetail\"),\n /** Vast product (slug); leeg → product uit `?p=` in de URL (één generieke detailpagina). */\n slug: z.optional(z.string().check(z.maxLength(120))),\n /** 5.6: \"Ook leuk\"-rij met producten uit dezelfde categorie. */\n showRelated: z._default(z.boolean(), true),\n});\n\nexport const CartBlockSchema = z.object({\n ...blockBase,\n _type: z.literal(\"cart\"),\n heading: z.optional(z.string().check(z.maxLength(200))),\n});\n\nexport const CheckoutBlockSchema = z.object({\n ...blockBase,\n _type: z.literal(\"checkout\"),\n heading: z.optional(z.string().check(z.maxLength(200))),\n introText: z.optional(z.string().check(z.maxLength(2000))),\n});\n\nexport const DonationBlockSchema = z.object({\n ...blockBase,\n _type: z.literal(\"donation\"),\n heading: z.optional(z.string().check(z.maxLength(200))),\n introText: z.optional(z.string().check(z.maxLength(2000))),\n /** Voorgestelde bedragen in centen. */\n amountsCents: z._default(z.array(z.int().check(z.minimum(50)).check(z.maximum(1_000_000_00))).check(z.maxLength(8)), [500, 1000, 2500]),\n allowCustom: z._default(z.boolean(), true),\n customMinCents: z._default(z.int().check(z.minimum(50)), 100),\n customMaxCents: z._default(z.int().check(z.maximum(1_000_000_00)), 100_000),\n buttonLabel: z._default(z.string().check(z.maxLength(80)), \"Doneer\"),\n});\n\nexport const OrderStatusBlockSchema = z.object({\n ...blockBase,\n _type: z.literal(\"orderStatus\"),\n successTitle: z._default(z.string().check(z.maxLength(200)), \"Bedankt voor je bestelling!\"),\n successText: z._default(z.string().check(z.maxLength(2000)), \"Je ontvangt zo een bevestiging per e-mail.\"),\n cancelledTitle: z._default(z.string().check(z.maxLength(200)), \"Betaling niet afgerond\"),\n cancelledText: z._default(z.string().check(z.maxLength(2000)), \"Je bestelling is niet betaald. Probeer het gerust opnieuw.\"),\n});\n\n/* ── Helpers (gedeeld client/server) ─────────────────────────────────────── */\n\nexport { variantKeyFor, productPriceCents, shippingCentsFor, formatCents } from \"./shopGeld\";\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/schema/shop.ts"],"sourcesContent":["import * as z from \"zod/mini\";\nimport { blockBase } from \"./blockBase\";\n\n/**\n * Shop-schema's (5.5) — webwinkel + donaties. Pure Zod: gedeeld door core, admin en Functions.\n *\n * Datamodel per tenant:\n * sites/{siteId}/shopCategories/{id} — categorieën (editor-write, publiek leesbaar)\n * sites/{siteId}/products/{id} — producten (editor-write, publiek leesbaar als actief)\n * sites/{siteId}/orders/{id} — bestellingen (ALLEEN via Functions; editor leest,\n * en mag alleen status/verzendvelden muteren)\n * sites/{siteId}/private/shop — geheimen (Stripe-key, SMTP) — deny-all, alleen Functions\n *\n * Geld is ALTIJD een integer in centen (nooit floats). Publieke shopconfig (verzendkosten,\n * paden, donatie-grenzen) leeft in de module-settings van de `shop`-module\n * (settings/public.modules.shop.config) — zie modules/shop/module.ts.\n */\n\n/* ── Categorieën ─────────────────────────────────────────────────────────── */\n\nexport const ShopCategorySchema = z.object({\n name: z.string().check(z.minLength(1)).check(z.maxLength(120)),\n slug: z.string().check(z.minLength(1)).check(z.maxLength(120)).check(z.regex(/^[a-z0-9-]+$/, \"slug: alleen kleine letters, cijfers en streepjes\")),\n description: z.optional(z.string().check(z.maxLength(2000))),\n imageUrl: z.optional(z.string().check(z.maxLength(2000))),\n order: z._default(z.int().check(z.minimum(0)).check(z.maximum(9999)), 0),\n visible: z._default(z.boolean(), true),\n});\nexport type ShopCategory = z.infer<typeof ShopCategorySchema>;\n\n/* ── Producten ───────────────────────────────────────────────────────────── */\n\n/** Optie-as, bv. { name: \"Maat\", values: [\"56\",\"62\",\"68\",\"74\"] }. Max 3 assen per product. */\nexport const ProductOptionSchema = z.object({\n name: z.string().check(z.minLength(1)).check(z.maxLength(40)),\n values: z.array(z.string().check(z.minLength(1)).check(z.maxLength(60))).check(z.minLength(1)).check(z.maxLength(30)),\n /**\n * 5.6: afbeelding per optieWAARDE (praktijk: de kleur-as) — `{ \"Roze\": \"https://…\" }`.\n * De detailpagina wisselt het hoofdbeeld mee met de keuze; bewust per as en niet per\n * variant-combinatie (maat × kleur zou het aantal afbeeldingen laten exploderen).\n */\n images: z.optional(z.record(z.string(), z.string().check(z.maxLength(2000)))),\n});\nexport type ProductOption = z.infer<typeof ProductOptionSchema>;\n\n/**\n * Personalisatie-veld (5.6): de klant geeft eigen tekst of een eigen afbeelding op\n * (bv. \"rompertje met naam\"). Meerprijs telt server-side mee in `createOrder`.\n */\nexport const PersonalizationFieldSchema = z.object({\n key: z.string().check(z.minLength(1)).check(z.maxLength(40)).check(z.regex(/^[a-zA-Z][a-zA-Z0-9_]*$/, \"sleutel: letters/cijfers/underscore, beginnend met een letter\")),\n label: z.string().check(z.minLength(1)).check(z.maxLength(120)),\n type: z.enum([\"text\", \"image\"]),\n required: z._default(z.boolean(), false),\n /** Alleen voor type \"text\". */\n maxLength: z._default(z.int().check(z.minimum(1)).check(z.maximum(200)), 40),\n /** Meerprijs in centen (per stuk). */\n priceCents: z._default(z.int().check(z.minimum(0)).check(z.maximum(1_000_000_00)), 0),\n helpText: z.optional(z.string().check(z.maxLength(200))),\n});\nexport type PersonalizationField = z.infer<typeof PersonalizationFieldSchema>;\n\n/** Ingevulde personalisatie op een orderregel/cartregel: per veld-key tekst of upload-pad. */\nexport const PersonalizationValueSchema = z.record(\n z.string(),\n z.object({\n text: z.optional(z.string().check(z.maxLength(200))),\n /** Storage-pad uit de uploadPersonalization-callable (géén vrije URL). */\n imagePath: z.optional(z.string().check(z.maxLength(400))),\n /** Oorspronkelijke bestandsnaam (weergave in beheer/mail). */\n fileName: z.optional(z.string().check(z.maxLength(200))),\n }),\n);\nexport type PersonalizationValue = z.infer<typeof PersonalizationValueSchema>;\n\n/**\n * Variant = één combinatie van optiewaarden. `key` is de waarden gejoined met \"|\" in\n * optie-volgorde (bv. \"68|Roze\"). Alleen aanwezig waar hij afwijkt van de product-defaults.\n */\nexport const ProductVariantSchema = z.object({\n key: z.string().check(z.minLength(1)).check(z.maxLength(200)),\n priceCents: z.optional(z.int().check(z.minimum(0)).check(z.maximum(1_000_000_00))),\n stock: z.optional(z.int().check(z.minimum(0)).check(z.maximum(1_000_000))),\n sku: z.optional(z.string().check(z.maxLength(60))),\n active: z._default(z.boolean(), true),\n});\nexport type ProductVariant = z.infer<typeof ProductVariantSchema>;\n\nexport const ProductStatusSchema = z.enum([\"draft\", \"active\", \"archived\"]);\nexport type ProductStatus = z.infer<typeof ProductStatusSchema>;\n\n/**\n * Verwijdert top-level `null`-velden uit een productdoc vóór validatie (7.17.1).\n *\n * Er liggen productdocs met `personalization: null` (e.d.) in Firestore: de functions-SDK\n * encodeerde een client-side `undefined` als `null` over de draad (dezelfde klasse als de\n * \"+ Nieuw product\"-bug), en de callable schreef dat door. `z.optional()` accepteert een\n * ONTBREKEND veld maar geen `null`, en `parseProduct` op de leeskant slaat een ongeldig doc\n * stil over — waardoor een product dat gewoon \"actief\" in het beheer staat van de live winkel\n * verdween (gemeten op christelijkkraamcadeau: 2 van de 4 \"Maak een vrolijk geluid\"-varianten,\n * precies de twee die vóór de 7.16.2-fix nog eens waren opgeslagen). In dit model betekent\n * `null` nergens iets anders dan \"afwezig\", dus strippen is verliesvrij.\n */\nexport function saneerProductDoc<T>(data: T): T {\n if (typeof data !== \"object\" || data === null || Array.isArray(data)) return data;\n return Object.fromEntries(\n Object.entries(data as Record<string, unknown>).filter(([, v]) => v !== null),\n ) as T;\n}\n\nexport const ProductSchema = z.object({\n name: z.string().check(z.minLength(1)).check(z.maxLength(200)),\n slug: z.string().check(z.minLength(1)).check(z.maxLength(120)).check(z.regex(/^[a-z0-9-]+$/, \"slug: alleen kleine letters, cijfers en streepjes\")),\n /**\n * Beschrijving als platte tekst (regeleinden blijven behouden). Bewust géén HTML:\n * producten worden client-side door editors geschreven (rules-pad, niet via de\n * publish-sanitizer), dus HTML zou hier een XSS-route zijn.\n */\n description: z._default(z.string().check(z.maxLength(20_000)), \"\"),\n categoryIds: z._default(z.array(z.string().check(z.maxLength(80))).check(z.maxLength(20)), []),\n /** Afbeeldings-URL's (via de mediabibliotheek). Eerste = hoofdbeeld. */\n images: z._default(z.array(z.string().check(z.maxLength(2000))).check(z.maxLength(12)), []),\n /** Prijs incl. btw, in centen. */\n priceCents: z._default(z.int().check(z.minimum(0)).check(z.maximum(1_000_000_00)), 0),\n /** Doorgestreepte \"van\"-prijs (optioneel). */\n compareAtCents: z.optional(z.int().check(z.minimum(0)).check(z.maximum(1_000_000_00))),\n /** Btw-tarief (NL): 21, 9 of 0. Prijzen zijn inclusief. */\n vatRate: z._default(z.union([z.literal(21), z.literal(9), z.literal(0)]), 21),\n options: z._default(z.array(ProductOptionSchema).check(z.maxLength(3)), []),\n variants: z._default(z.array(ProductVariantSchema).check(z.maxLength(200)), []),\n /** Voorraad bijhouden? false = altijd bestelbaar (op-maat/print-on-demand). */\n trackStock: z._default(z.boolean(), false),\n /** Voorraad voor producten zónder varianten (met varianten telt de variant-stock). */\n stock: z.optional(z.int().check(z.minimum(0)).check(z.maximum(1_000_000))),\n status: z._default(ProductStatusSchema, \"draft\"),\n /** Sorteervolgorde binnen een categorie (laag = eerst). */\n order: z._default(z.int().check(z.minimum(0)).check(z.maximum(9999)), 0),\n featured: z._default(z.boolean(), false),\n /** 5.6: personalisatie-velden (eigen naam/logo) — max 3 per product. */\n personalization: z.optional(z.array(PersonalizationFieldSchema).check(z.maxLength(3))),\n});\nexport type Product = z.infer<typeof ProductSchema>;\n\n/* ── Kortingscodes ───────────────────────────────────────────────────────── */\n\n/** `sites/{siteId}/discountCodes/{CODE}` (doc-id = code in hoofdletters; editor beheert). */\nexport const DiscountCodeSchema = z.object({\n code: z.string().check(z.minLength(3)).check(z.maxLength(30)).check(z.regex(/^[A-Z0-9-]+$/, \"code: hoofdletters, cijfers en streepjes\")),\n type: z.enum([\"percent\", \"fixed\"]),\n /** percent: 1–100; fixed: bedrag in centen. */\n value: z.int().check(z.minimum(1)).check(z.maximum(1_000_000_00)),\n minOrderCents: z._default(z.int().check(z.minimum(0)).check(z.maximum(1_000_000_00)), 0),\n freeShipping: z._default(z.boolean(), false),\n active: z._default(z.boolean(), true),\n /** ISO-datums (yyyy-mm-dd); leeg = geen grens. */\n startsAt: z.optional(z.string().check(z.maxLength(10))),\n endsAt: z.optional(z.string().check(z.maxLength(10))),\n /** 0 = onbeperkt. `usedCount` wordt door de webhook transactioneel opgehoogd. */\n maxUses: z._default(z.int().check(z.minimum(0)).check(z.maximum(1_000_000)), 0),\n usedCount: z._default(z.int().check(z.minimum(0)), 0),\n /**\n * Eén keer per e-mailadres (7.18) — de klassieke welkomstkorting. De server telt de eerder\n * BETAALDE orders van dat adres met deze code; een afgebroken checkout brandt hem dus niet op.\n */\n oncePerCustomer: z._default(z.boolean(), false),\n});\nexport type DiscountCode = z.infer<typeof DiscountCodeSchema>;\n\n/* ── Mail-templates ──────────────────────────────────────────────────────── */\n\n// Mailtemplates zijn site-breed (ook zonder shop) — de canonieke definitie staat in `./mail`.\n// Hier her-geëxporteerd voor backward-compat van bestaande shop-importeurs.\nexport {\n MAIL_TEMPLATE_KEYS,\n MAIL_TEMPLATE_DEFS,\n MailTemplateSchema,\n MailTemplatesSchema,\n type MailTemplateKey,\n type MailTemplates,\n} from \"./mail\";\n\n/* ── Bestellingen ────────────────────────────────────────────────────────── */\n\nexport const OrderStatusSchema = z.enum([\"pending\", \"paid\", \"shipped\", \"cancelled\", \"refunded\"]);\nexport type OrderStatus = z.infer<typeof OrderStatusSchema>;\n\n/** Orderregel: snapshot van naam/prijs op besteldatum — blijft kloppen als het product wijzigt. */\nexport const OrderItemSchema = z.object({\n kind: z._default(z.enum([\"product\", \"donation\"]), \"product\"),\n productId: z.optional(z.string().check(z.maxLength(80))),\n name: z.string().check(z.minLength(1)).check(z.maxLength(240)),\n variantKey: z.optional(z.string().check(z.maxLength(200))),\n qty: z.int().check(z.minimum(1)).check(z.maximum(999)),\n /** Incl. eventuele personalisatie-meerprijs. */\n unitPriceCents: z.int().check(z.minimum(0)).check(z.maximum(1_000_000_00)),\n vatRate: z._default(z.union([z.literal(21), z.literal(9), z.literal(0)]), 21),\n /** 5.6: ingevulde personalisatie (\"Naam: Boaz\", upload-pad). */\n personalization: z.optional(PersonalizationValueSchema),\n /**\n * 7.18: het deel van `unitPriceCents` dat personalisatie-meerprijs is.\n *\n * De meerprijs werkte al end-to-end, maar zat onzichtbaar in de stukprijs: een klant die\n * € 16,95 betaalde voor een product van € 13,95 zag nergens waar die € 3,00 vandaan kwam.\n * Apart opslaan (i.p.v. bij het tonen terugrekenen) houdt de historie kloppend als de\n * meerprijs van het product later verandert.\n */\n personalizationCents: z._default(z.int().check(z.minimum(0)), 0),\n});\nexport type OrderItem = z.infer<typeof OrderItemSchema>;\n\nexport const OrderAddressSchema = z.object({\n address: z.string().check(z.minLength(1)).check(z.maxLength(200)),\n zip: z.string().check(z.minLength(1)).check(z.maxLength(20)),\n city: z.string().check(z.minLength(1)).check(z.maxLength(120)),\n country: z._default(z.string().check(z.minLength(2)).check(z.maxLength(2)), \"NL\"),\n});\nexport type OrderAddress = z.infer<typeof OrderAddressSchema>;\n\nexport const OrderCustomerSchema = z.object({\n email: z.email().check(z.maxLength(200)),\n name: z.string().check(z.minLength(1)).check(z.maxLength(200)),\n phone: z.optional(z.string().check(z.maxLength(40))),\n});\nexport type OrderCustomer = z.infer<typeof OrderCustomerSchema>;\n\n/** Volledige order zoals door Functions geschreven (timestamps komen daarbovenop). */\nexport const OrderSchema = z.object({\n number: z.string().check(z.minLength(1)).check(z.maxLength(40)),\n status: OrderStatusSchema,\n items: z.array(OrderItemSchema).check(z.minLength(1)).check(z.maxLength(50)),\n amount: z.object({\n itemsCents: z.int().check(z.minimum(0)),\n shippingCents: z.int().check(z.minimum(0)),\n /** 5.6: toegepaste korting (0 = geen). */\n discountCents: z._default(z.int().check(z.minimum(0)), 0),\n totalCents: z.int().check(z.minimum(0)),\n }),\n /** 5.6: toegepaste kortingscode (snapshot). */\n discount: z.optional(z.object({\n code: z.string().check(z.maxLength(30)),\n type: z.enum([\"percent\", \"fixed\"]),\n value: z.int(),\n freeShipping: z._default(z.boolean(), false),\n })),\n customer: OrderCustomerSchema,\n shipping: z.optional(OrderAddressSchema),\n note: z.optional(z.string().check(z.maxLength(2000))),\n /** 7.18: interne notitie van de winkelier — de klant ziet hem nooit. */\n adminNote: z.optional(z.string().check(z.maxLength(2000))),\n /** 7.18: som van alle (gedeeltelijke) terugbetalingen in centen. */\n refundedCents: z.optional(z.int().check(z.minimum(0))),\n /** 7.18: berichten die de winkelier vanuit /beheer aan de klant stuurde. */\n messages: z.optional(z.array(z.object({\n at: z.string().check(z.maxLength(40)),\n by: z.optional(z.string().check(z.maxLength(200))),\n subject: z.string().check(z.maxLength(200)),\n body: z.string().check(z.maxLength(2000)),\n })).check(z.maxLength(50))),\n /** 5.6: track & trace-link (gezet bij \"verzonden\"). */\n trackingUrl: z.optional(z.string().check(z.maxLength(500))),\n /** 5.6: factuur-pad in Storage (gezet door de webhook). */\n invoicePath: z.optional(z.string().check(z.maxLength(400))),\n stripe: z._default(z.object({\n sessionId: z.optional(z.string().check(z.maxLength(200))),\n paymentIntentId: z.optional(z.string().check(z.maxLength(200))),\n }), {}),\n});\nexport type Order = z.infer<typeof OrderSchema>;\n\n/* ── Callable-input (checkout) ───────────────────────────────────────────── */\n\n/**\n * Input van de `createOrder`-callable. Client-prijzen worden NOOIT vertrouwd: de functie\n * herrekent alles tegen de opgeslagen product-docs. Alleen `donationCents` komt van de\n * client (binnen de per-site ingestelde min/max van het donatie-block).\n */\nexport const CreateOrderItemInputSchema = z.object({\n productId: z.optional(z.string().check(z.minLength(1)).check(z.maxLength(80))),\n variantKey: z.optional(z.string().check(z.maxLength(200))),\n qty: z._default(z.int().check(z.minimum(1)).check(z.maximum(999)), 1),\n /** Alleen voor donaties (zonder productId). */\n donationCents: z.optional(z.int().check(z.minimum(50)).check(z.maximum(1_000_000_00))),\n /** 5.6: personalisatie-invoer (gevalideerd tegen product.personalization). */\n personalization: z.optional(PersonalizationValueSchema),\n});\n\nexport const CreateOrderInputSchema = z.object({\n siteId: z.string().check(z.minLength(1)).check(z.maxLength(80)),\n items: z.array(CreateOrderItemInputSchema).check(z.minLength(1)).check(z.maxLength(50)),\n customer: OrderCustomerSchema,\n shipping: z.optional(OrderAddressSchema),\n note: z.optional(z.string().check(z.maxLength(2000))),\n /** 5.6: kortingscode (server-side gevalideerd en verrekend). */\n discountCode: z.optional(z.string().check(z.maxLength(30))),\n /** Paden op de klantsite waarheen Stripe terug redirect. */\n successPath: z._default(z.string().check(z.maxLength(400)), \"/bedankt\"),\n cancelPath: z._default(z.string().check(z.maxLength(400)), \"/winkelwagen\"),\n /** Honeypot — moet leeg zijn (bot-detectie). */\n _hp: z.optional(z.string().check(z.maxLength(0))),\n});\nexport type CreateOrderInput = z.infer<typeof CreateOrderInputSchema>;\n\n/* ── Block-schema's ──────────────────────────────────────────────────────── */\n\nexport const ShopCategoriesBlockSchema = z.object({\n ...blockBase,\n _type: z.literal(\"shopCategories\"),\n heading: z.optional(z.string().check(z.maxLength(200))),\n columns: z._default(z.int().check(z.minimum(2)).check(z.maximum(4)), 3),\n});\n\nexport const ProductGridBlockSchema = z.object({\n ...blockBase,\n _type: z.literal(\"productGrid\"),\n heading: z.optional(z.string().check(z.maxLength(200))),\n /** Vast categorie-filter (slug). Leeg + useQueryCategory → filter via `?cat=` in de URL. */\n categorySlug: z.optional(z.string().check(z.maxLength(120))),\n useQueryCategory: z._default(z.boolean(), true),\n featuredOnly: z._default(z.boolean(), false),\n columns: z._default(z.int().check(z.minimum(2)).check(z.maximum(4)), 3),\n limit: z._default(z.int().check(z.minimum(1)).check(z.maximum(200)), 60),\n sort: z._default(z.enum([\"order\", \"name\", \"priceAsc\", \"priceDesc\"]), \"order\"),\n /** 5.6: filterchips (kleur/maat) + sorteerkeuze boven het grid. */\n showFilters: z._default(z.boolean(), false),\n});\n\nexport const ProductDetailBlockSchema = z.object({\n ...blockBase,\n _type: z.literal(\"productDetail\"),\n /** Vast product (slug); leeg → product uit `?p=` in de URL (één generieke detailpagina). */\n slug: z.optional(z.string().check(z.maxLength(120))),\n /** 5.6: \"Ook leuk\"-rij met producten uit dezelfde categorie. */\n showRelated: z._default(z.boolean(), true),\n});\n\nexport const CartBlockSchema = z.object({\n ...blockBase,\n _type: z.literal(\"cart\"),\n heading: z.optional(z.string().check(z.maxLength(200))),\n});\n\nexport const CheckoutBlockSchema = z.object({\n ...blockBase,\n _type: z.literal(\"checkout\"),\n heading: z.optional(z.string().check(z.maxLength(200))),\n introText: z.optional(z.string().check(z.maxLength(2000))),\n});\n\nexport const DonationBlockSchema = z.object({\n ...blockBase,\n _type: z.literal(\"donation\"),\n heading: z.optional(z.string().check(z.maxLength(200))),\n introText: z.optional(z.string().check(z.maxLength(2000))),\n /** Voorgestelde bedragen in centen. */\n amountsCents: z._default(z.array(z.int().check(z.minimum(50)).check(z.maximum(1_000_000_00))).check(z.maxLength(8)), [500, 1000, 2500]),\n allowCustom: z._default(z.boolean(), true),\n customMinCents: z._default(z.int().check(z.minimum(50)), 100),\n customMaxCents: z._default(z.int().check(z.maximum(1_000_000_00)), 100_000),\n buttonLabel: z._default(z.string().check(z.maxLength(80)), \"Doneer\"),\n});\n\nexport const OrderStatusBlockSchema = z.object({\n ...blockBase,\n _type: z.literal(\"orderStatus\"),\n successTitle: z._default(z.string().check(z.maxLength(200)), \"Bedankt voor je bestelling!\"),\n successText: z._default(z.string().check(z.maxLength(2000)), \"Je ontvangt zo een bevestiging per e-mail.\"),\n cancelledTitle: z._default(z.string().check(z.maxLength(200)), \"Betaling niet afgerond\"),\n cancelledText: z._default(z.string().check(z.maxLength(2000)), \"Je bestelling is niet betaald. Probeer het gerust opnieuw.\"),\n});\n\n/* ── Helpers (gedeeld client/server) ─────────────────────────────────────── */\n\nexport { variantKeyFor, productPriceCents, shippingCentsFor, formatCents } from \"./shopGeld\";\n"],"mappings":"AAAA,YAAY,OAAO;AACnB,SAAS,iBAAiB;AAmBnB,MAAM,qBAAqB,EAAE,OAAO;AAAA,EACzC,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,CAAC,EAAE,MAAM,EAAE,UAAU,GAAG,CAAC;AAAA,EAC7D,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,CAAC,EAAE,MAAM,EAAE,UAAU,GAAG,CAAC,EAAE,MAAM,EAAE,MAAM,gBAAgB,mDAAmD,CAAC;AAAA,EACjJ,aAAa,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,GAAI,CAAC,CAAC;AAAA,EAC3D,UAAU,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,GAAI,CAAC,CAAC;AAAA,EACxD,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,QAAQ,IAAI,CAAC,GAAG,CAAC;AAAA,EACvE,SAAS,EAAE,SAAS,EAAE,QAAQ,GAAG,IAAI;AACvC,CAAC;AAMM,MAAM,sBAAsB,EAAE,OAAO;AAAA,EAC1C,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC;AAAA,EAC5D,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,UAAU,CAAC,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMpH,QAAQ,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,GAAI,CAAC,CAAC,CAAC;AAC9E,CAAC;AAOM,MAAM,6BAA6B,EAAE,OAAO;AAAA,EACjD,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM,2BAA2B,+DAA+D,CAAC;AAAA,EACtK,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,CAAC,EAAE,MAAM,EAAE,UAAU,GAAG,CAAC;AAAA,EAC9D,MAAM,EAAE,KAAK,CAAC,QAAQ,OAAO,CAAC;AAAA,EAC9B,UAAU,EAAE,SAAS,EAAE,QAAQ,GAAG,KAAK;AAAA;AAAA,EAEvC,WAAW,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,QAAQ,GAAG,CAAC,GAAG,EAAE;AAAA;AAAA,EAE3E,YAAY,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,QAAQ,GAAY,CAAC,GAAG,CAAC;AAAA,EACpF,UAAU,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,GAAG,CAAC,CAAC;AACzD,CAAC;AAIM,MAAM,6BAA6B,EAAE;AAAA,EAC1C,EAAE,OAAO;AAAA,EACT,EAAE,OAAO;AAAA,IACP,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,GAAG,CAAC,CAAC;AAAA;AAAA,IAEnD,WAAW,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,GAAG,CAAC,CAAC;AAAA;AAAA,IAExD,UAAU,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,GAAG,CAAC,CAAC;AAAA,EACzD,CAAC;AACH;AAOO,MAAM,uBAAuB,EAAE,OAAO;AAAA,EAC3C,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,CAAC,EAAE,MAAM,EAAE,UAAU,GAAG,CAAC;AAAA,EAC5D,YAAY,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,QAAQ,GAAY,CAAC,CAAC;AAAA,EACjF,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,QAAQ,GAAS,CAAC,CAAC;AAAA,EACzE,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC,CAAC;AAAA,EACjD,QAAQ,EAAE,SAAS,EAAE,QAAQ,GAAG,IAAI;AACtC,CAAC;AAGM,MAAM,sBAAsB,EAAE,KAAK,CAAC,SAAS,UAAU,UAAU,CAAC;AAelE,SAAS,iBAAoB,MAAY;AAC9C,MAAI,OAAO,SAAS,YAAY,SAAS,QAAQ,MAAM,QAAQ,IAAI,EAAG,QAAO;AAC7E,SAAO,OAAO;AAAA,IACZ,OAAO,QAAQ,IAA+B,EAAE,OAAO,CAAC,CAAC,EAAE,CAAC,MAAM,MAAM,IAAI;AAAA,EAC9E;AACF;AAEO,MAAM,gBAAgB,EAAE,OAAO;AAAA,EACpC,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,CAAC,EAAE,MAAM,EAAE,UAAU,GAAG,CAAC;AAAA,EAC7D,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,CAAC,EAAE,MAAM,EAAE,UAAU,GAAG,CAAC,EAAE,MAAM,EAAE,MAAM,gBAAgB,mDAAmD,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMjJ,aAAa,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,GAAM,CAAC,GAAG,EAAE;AAAA,EACjE,aAAa,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC,GAAG,CAAC,CAAC;AAAA;AAAA,EAE7F,QAAQ,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,GAAI,CAAC,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC,GAAG,CAAC,CAAC;AAAA;AAAA,EAE1F,YAAY,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,QAAQ,GAAY,CAAC,GAAG,CAAC;AAAA;AAAA,EAEpF,gBAAgB,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,QAAQ,GAAY,CAAC,CAAC;AAAA;AAAA,EAErF,SAAS,EAAE,SAAS,EAAE,MAAM,CAAC,EAAE,QAAQ,EAAE,GAAG,EAAE,QAAQ,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC,CAAC,GAAG,EAAE;AAAA,EAC5E,SAAS,EAAE,SAAS,EAAE,MAAM,mBAAmB,EAAE,MAAM,EAAE,UAAU,CAAC,CAAC,GAAG,CAAC,CAAC;AAAA,EAC1E,UAAU,EAAE,SAAS,EAAE,MAAM,oBAAoB,EAAE,MAAM,EAAE,UAAU,GAAG,CAAC,GAAG,CAAC,CAAC;AAAA;AAAA,EAE9E,YAAY,EAAE,SAAS,EAAE,QAAQ,GAAG,KAAK;AAAA;AAAA,EAEzC,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,QAAQ,GAAS,CAAC,CAAC;AAAA,EACzE,QAAQ,EAAE,SAAS,qBAAqB,OAAO;AAAA;AAAA,EAE/C,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,QAAQ,IAAI,CAAC,GAAG,CAAC;AAAA,EACvE,UAAU,EAAE,SAAS,EAAE,QAAQ,GAAG,KAAK;AAAA;AAAA,EAEvC,iBAAiB,EAAE,SAAS,EAAE,MAAM,0BAA0B,EAAE,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC;AACvF,CAAC;AAMM,MAAM,qBAAqB,EAAE,OAAO;AAAA,EACzC,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM,gBAAgB,0CAA0C,CAAC;AAAA,EACvI,MAAM,EAAE,KAAK,CAAC,WAAW,OAAO,CAAC;AAAA;AAAA,EAEjC,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,QAAQ,GAAY,CAAC;AAAA,EAChE,eAAe,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,QAAQ,GAAY,CAAC,GAAG,CAAC;AAAA,EACvF,cAAc,EAAE,SAAS,EAAE,QAAQ,GAAG,KAAK;AAAA,EAC3C,QAAQ,EAAE,SAAS,EAAE,QAAQ,GAAG,IAAI;AAAA;AAAA,EAEpC,UAAU,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC,CAAC;AAAA,EACtD,QAAQ,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC,CAAC;AAAA;AAAA,EAEpD,SAAS,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,QAAQ,GAAS,CAAC,GAAG,CAAC;AAAA,EAC9E,WAAW,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC,GAAG,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA,EAKpD,iBAAiB,EAAE,SAAS,EAAE,QAAQ,GAAG,KAAK;AAChD,CAAC;AAOD;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAGK;AAIA,MAAM,oBAAoB,EAAE,KAAK,CAAC,WAAW,QAAQ,WAAW,aAAa,UAAU,CAAC;AAIxF,MAAM,kBAAkB,EAAE,OAAO;AAAA,EACtC,MAAM,EAAE,SAAS,EAAE,KAAK,CAAC,WAAW,UAAU,CAAC,GAAG,SAAS;AAAA,EAC3D,WAAW,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC,CAAC;AAAA,EACvD,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,CAAC,EAAE,MAAM,EAAE,UAAU,GAAG,CAAC;AAAA,EAC7D,YAAY,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,GAAG,CAAC,CAAC;AAAA,EACzD,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,QAAQ,GAAG,CAAC;AAAA;AAAA,EAErD,gBAAgB,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,QAAQ,GAAY,CAAC;AAAA,EACzE,SAAS,EAAE,SAAS,EAAE,MAAM,CAAC,EAAE,QAAQ,EAAE,GAAG,EAAE,QAAQ,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC,CAAC,GAAG,EAAE;AAAA;AAAA,EAE5E,iBAAiB,EAAE,SAAS,0BAA0B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAStD,sBAAsB,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC,GAAG,CAAC;AACjE,CAAC;AAGM,MAAM,qBAAqB,EAAE,OAAO;AAAA,EACzC,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,CAAC,EAAE,MAAM,EAAE,UAAU,GAAG,CAAC;AAAA,EAChE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC;AAAA,EAC3D,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,CAAC,EAAE,MAAM,EAAE,UAAU,GAAG,CAAC;AAAA,EAC7D,SAAS,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,CAAC,EAAE,MAAM,EAAE,UAAU,CAAC,CAAC,GAAG,IAAI;AAClF,CAAC;AAGM,MAAM,sBAAsB,EAAE,OAAO;AAAA,EAC1C,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,GAAG,CAAC;AAAA,EACvC,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,CAAC,EAAE,MAAM,EAAE,UAAU,GAAG,CAAC;AAAA,EAC7D,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC,CAAC;AACrD,CAAC;AAIM,MAAM,cAAc,EAAE,OAAO;AAAA,EAClC,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC;AAAA,EAC9D,QAAQ;AAAA,EACR,OAAO,EAAE,MAAM,eAAe,EAAE,MAAM,EAAE,UAAU,CAAC,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC;AAAA,EAC3E,QAAQ,EAAE,OAAO;AAAA,IACf,YAAY,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC;AAAA,IACtC,eAAe,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC;AAAA;AAAA,IAEzC,eAAe,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC,GAAG,CAAC;AAAA,IACxD,YAAY,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC;AAAA,EACxC,CAAC;AAAA;AAAA,EAED,UAAU,EAAE,SAAS,EAAE,OAAO;AAAA,IAC1B,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC;AAAA,IACtC,MAAM,EAAE,KAAK,CAAC,WAAW,OAAO,CAAC;AAAA,IACjC,OAAO,EAAE,IAAI;AAAA,IACb,cAAc,EAAE,SAAS,EAAE,QAAQ,GAAG,KAAK;AAAA,EAC7C,CAAC,CAAC;AAAA,EACJ,UAAU;AAAA,EACV,UAAU,EAAE,SAAS,kBAAkB;AAAA,EACvC,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,GAAI,CAAC,CAAC;AAAA;AAAA,EAEpD,WAAW,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,GAAI,CAAC,CAAC;AAAA;AAAA,EAEzD,eAAe,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC;AAAA;AAAA,EAErD,UAAU,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO;AAAA,IAClC,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC;AAAA,IACpC,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,GAAG,CAAC,CAAC;AAAA,IACjD,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,GAAG,CAAC;AAAA,IAC1C,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,GAAI,CAAC;AAAA,EAC1C,CAAC,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC,CAAC;AAAA;AAAA,EAE5B,aAAa,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,GAAG,CAAC,CAAC;AAAA;AAAA,EAE1D,aAAa,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,GAAG,CAAC,CAAC;AAAA,EAC1D,QAAQ,EAAE,SAAS,EAAE,OAAO;AAAA,IACxB,WAAW,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,GAAG,CAAC,CAAC;AAAA,IACxD,iBAAiB,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,GAAG,CAAC,CAAC;AAAA,EAChE,CAAC,GAAG,CAAC,CAAC;AACV,CAAC;AAUM,MAAM,6BAA6B,EAAE,OAAO;AAAA,EACjD,WAAW,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC,CAAC;AAAA,EAC7E,YAAY,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,GAAG,CAAC,CAAC;AAAA,EACzD,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,QAAQ,GAAG,CAAC,GAAG,CAAC;AAAA;AAAA,EAEpE,eAAe,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,EAAE,MAAM,EAAE,QAAQ,GAAY,CAAC,CAAC;AAAA;AAAA,EAErF,iBAAiB,EAAE,SAAS,0BAA0B;AACxD,CAAC;AAEM,MAAM,yBAAyB,EAAE,OAAO;AAAA,EAC7C,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC;AAAA,EAC9D,OAAO,EAAE,MAAM,0BAA0B,EAAE,MAAM,EAAE,UAAU,CAAC,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC;AAAA,EACtF,UAAU;AAAA,EACV,UAAU,EAAE,SAAS,kBAAkB;AAAA,EACvC,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,GAAI,CAAC,CAAC;AAAA;AAAA,EAEpD,cAAc,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC,CAAC;AAAA;AAAA,EAE1D,aAAa,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,GAAG,CAAC,GAAG,UAAU;AAAA,EACtE,YAAY,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,GAAG,CAAC,GAAG,cAAc;AAAA;AAAA,EAEzE,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC;AAClD,CAAC;AAKM,MAAM,4BAA4B,EAAE,OAAO;AAAA,EAChD,GAAG;AAAA,EACH,OAAO,EAAE,QAAQ,gBAAgB;AAAA,EACjC,SAAS,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,GAAG,CAAC,CAAC;AAAA,EACtD,SAAS,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC,GAAG,CAAC;AACxE,CAAC;AAEM,MAAM,yBAAyB,EAAE,OAAO;AAAA,EAC7C,GAAG;AAAA,EACH,OAAO,EAAE,QAAQ,aAAa;AAAA,EAC9B,SAAS,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,GAAG,CAAC,CAAC;AAAA;AAAA,EAEtD,cAAc,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,GAAG,CAAC,CAAC;AAAA,EAC3D,kBAAkB,EAAE,SAAS,EAAE,QAAQ,GAAG,IAAI;AAAA,EAC9C,cAAc,EAAE,SAAS,EAAE,QAAQ,GAAG,KAAK;AAAA,EAC3C,SAAS,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC,GAAG,CAAC;AAAA,EACtE,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,QAAQ,GAAG,CAAC,GAAG,EAAE;AAAA,EACvE,MAAM,EAAE,SAAS,EAAE,KAAK,CAAC,SAAS,QAAQ,YAAY,WAAW,CAAC,GAAG,OAAO;AAAA;AAAA,EAE5E,aAAa,EAAE,SAAS,EAAE,QAAQ,GAAG,KAAK;AAC5C,CAAC;AAEM,MAAM,2BAA2B,EAAE,OAAO;AAAA,EAC/C,GAAG;AAAA,EACH,OAAO,EAAE,QAAQ,eAAe;AAAA;AAAA,EAEhC,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,GAAG,CAAC,CAAC;AAAA;AAAA,EAEnD,aAAa,EAAE,SAAS,EAAE,QAAQ,GAAG,IAAI;AAC3C,CAAC;AAEM,MAAM,kBAAkB,EAAE,OAAO;AAAA,EACtC,GAAG;AAAA,EACH,OAAO,EAAE,QAAQ,MAAM;AAAA,EACvB,SAAS,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,GAAG,CAAC,CAAC;AACxD,CAAC;AAEM,MAAM,sBAAsB,EAAE,OAAO;AAAA,EAC1C,GAAG;AAAA,EACH,OAAO,EAAE,QAAQ,UAAU;AAAA,EAC3B,SAAS,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,GAAG,CAAC,CAAC;AAAA,EACtD,WAAW,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,GAAI,CAAC,CAAC;AAC3D,CAAC;AAEM,MAAM,sBAAsB,EAAE,OAAO;AAAA,EAC1C,GAAG;AAAA,EACH,OAAO,EAAE,QAAQ,UAAU;AAAA,EAC3B,SAAS,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,GAAG,CAAC,CAAC;AAAA,EACtD,WAAW,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,GAAI,CAAC,CAAC;AAAA;AAAA,EAEzD,cAAc,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,EAAE,MAAM,EAAE,QAAQ,GAAY,CAAC,CAAC,EAAE,MAAM,EAAE,UAAU,CAAC,CAAC,GAAG,CAAC,KAAK,KAAM,IAAI,CAAC;AAAA,EACtI,aAAa,EAAE,SAAS,EAAE,QAAQ,GAAG,IAAI;AAAA,EACzC,gBAAgB,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,GAAG,GAAG;AAAA,EAC5D,gBAAgB,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,GAAY,CAAC,GAAG,GAAO;AAAA,EAC1E,aAAa,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC,GAAG,QAAQ;AACrE,CAAC;AAEM,MAAM,yBAAyB,EAAE,OAAO;AAAA,EAC7C,GAAG;AAAA,EACH,OAAO,EAAE,QAAQ,aAAa;AAAA,EAC9B,cAAc,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,GAAG,CAAC,GAAG,6BAA6B;AAAA,EAC1F,aAAa,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,GAAI,CAAC,GAAG,4CAA4C;AAAA,EACzG,gBAAgB,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,GAAG,CAAC,GAAG,wBAAwB;AAAA,EACvF,eAAe,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,GAAI,CAAC,GAAG,4DAA4D;AAC7H,CAAC;AAID,SAAS,eAAe,mBAAmB,kBAAkB,mBAAmB;","names":[]}
|