@xiwebdesign/xicms-core 7.24.0 → 7.25.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.
@@ -49,8 +49,13 @@ function OrdersTab({ siteId }) {
49
49
  const [manualLines, setManualLines] = useState([]);
50
50
  const [producten, setProducten] = useState([]);
51
51
  const [catNamen, setCatNamen] = useState({});
52
+ const [editOpen, setEditOpen] = useState(false);
53
+ const [editCustomer, setEditCustomer] = useState({ name: "", email: "", phone: "" });
54
+ const [editShipping, setEditShipping] = useState({ address: "", zip: "", city: "" });
55
+ const [editShippingEuro, setEditShippingEuro] = useState("");
56
+ const [editLines, setEditLines] = useState([]);
52
57
  useEffect(() => {
53
- if (!manualOpen || producten.length > 0) return;
58
+ if (!manualOpen && !editOpen || producten.length > 0) return;
54
59
  const db = getDb();
55
60
  if (!db) return;
56
61
  void getDocs(query(collection(db, "sites", siteId, "products"), where("status", "==", "active"))).then(
@@ -245,6 +250,92 @@ function OrdersTab({ siteId }) {
245
250
  setBusy(false);
246
251
  }
247
252
  }
253
+ function openEdit(order) {
254
+ setEditCustomer({ name: order.customer.name, email: order.customer.email, phone: order.customer.phone ?? "" });
255
+ setEditShipping({
256
+ address: order.shipping?.address ?? "",
257
+ zip: order.shipping?.zip ?? "",
258
+ city: order.shipping?.city ?? ""
259
+ });
260
+ setEditShippingEuro(centsToEuroInput(order.amount.shippingCents));
261
+ setEditLines(
262
+ order.status === "pending" ? order.items.filter((i) => i.kind !== "donation").map((i) => ({ productId: i.productId ?? "", variantKey: i.variantKey ?? "", qty: i.qty })) : []
263
+ );
264
+ setEditOpen(true);
265
+ }
266
+ async function saveEdit(order) {
267
+ if (!editCustomer.name.trim() || !editCustomer.email.trim()) {
268
+ toast({ title: "Naam en e-mail zijn verplicht", tone: "error" });
269
+ return;
270
+ }
271
+ const magBedragWijzigen = order.status === "pending";
272
+ const regels = editLines.filter((l) => l.productId && l.qty > 0);
273
+ if (magBedragWijzigen && regels.length === 0) {
274
+ toast({ title: "Er moet minstens \xE9\xE9n product op de bestelling staan", tone: "error" });
275
+ return;
276
+ }
277
+ setBusy(true);
278
+ try {
279
+ await shopCallable("editOrder")({
280
+ siteId,
281
+ orderId: order.id,
282
+ customer: {
283
+ name: editCustomer.name.trim(),
284
+ email: editCustomer.email.trim(),
285
+ phone: editCustomer.phone.trim() || void 0
286
+ },
287
+ // Leeg adresveld = geen wijziging (er is geen route om een adres te wissen — een
288
+ // bestelling met producten heeft er altijd een nodig).
289
+ shipping: editShipping.address.trim() ? { address: editShipping.address.trim(), zip: editShipping.zip.trim(), city: editShipping.city.trim(), country: "NL" } : void 0,
290
+ ...magBedragWijzigen ? {
291
+ items: regels.map((l) => ({ productId: l.productId, variantKey: l.variantKey || void 0, qty: l.qty })),
292
+ shippingCents: eurosToCents(editShippingEuro) ?? 0
293
+ } : {}
294
+ });
295
+ toast({ title: "Bestelling bijgewerkt", tone: "success" });
296
+ setEditOpen(false);
297
+ } catch (e) {
298
+ toast({ title: "Opslaan mislukt", description: e instanceof Error ? e.message : void 0, tone: "error" });
299
+ } finally {
300
+ setBusy(false);
301
+ }
302
+ }
303
+ async function sendPaymentRequest(order) {
304
+ const ok = await confirm({
305
+ title: `Betaalverzoek sturen voor ${order.number}?`,
306
+ description: `De klant ontvangt een e-mail met een betaallink voor ${formatCents(order.amount.totalCents)}.`,
307
+ confirmLabel: "Versturen"
308
+ });
309
+ if (!ok) return;
310
+ setBusy(true);
311
+ try {
312
+ await shopCallable("sendPaymentRequest")({ siteId, orderId: order.id });
313
+ toast({ title: "Betaalverzoek verstuurd", description: order.customer.email, tone: "success" });
314
+ } catch (e) {
315
+ toast({ title: "Versturen mislukt", description: e instanceof Error ? e.message : void 0, tone: "error" });
316
+ } finally {
317
+ setBusy(false);
318
+ }
319
+ }
320
+ async function removeOrder(order) {
321
+ const ok = await confirm({
322
+ title: `Bestelling ${order.number} verwijderen?`,
323
+ description: "Dit kan niet ongedaan worden gemaakt.",
324
+ confirmLabel: "Verwijderen",
325
+ tone: "danger"
326
+ });
327
+ if (!ok) return;
328
+ setBusy(true);
329
+ try {
330
+ await shopCallable("deleteOrder")({ siteId, orderId: order.id });
331
+ toast({ title: "Bestelling verwijderd", tone: "success" });
332
+ setSelected(null);
333
+ } catch (e) {
334
+ toast({ title: "Verwijderen mislukt", description: e instanceof Error ? e.message : void 0, tone: "error" });
335
+ } finally {
336
+ setBusy(false);
337
+ }
338
+ }
248
339
  async function resendMail(order) {
249
340
  setBusy(true);
250
341
  try {
@@ -429,8 +520,11 @@ function OrdersTab({ siteId }) {
429
520
  ] }),
430
521
  (current.messages?.length ?? 0) > 0 && /* @__PURE__ */ jsxs("div", { children: [
431
522
  /* @__PURE__ */ jsx("h4", { className: "mb-1 text-sm font-semibold", children: "Verstuurde berichten" }),
432
- /* @__PURE__ */ jsx("ul", { className: "grid gap-2", children: current.messages.map((m, i) => /* @__PURE__ */ jsxs("li", { className: "rounded-lg border border-slate-200 p-2 text-sm", children: [
433
- /* @__PURE__ */ jsx("p", { className: "font-medium text-slate-800", children: m.subject }),
523
+ /* @__PURE__ */ jsx("ul", { className: "grid gap-2", children: [...current.messages].reverse().map((m, i) => /* @__PURE__ */ jsxs("li", { className: `rounded-lg border p-2 text-sm ${m.mislukt ? "border-red-200 bg-red-50" : "border-slate-200"}`, children: [
524
+ /* @__PURE__ */ jsxs("p", { className: "flex flex-wrap items-center gap-2 font-medium text-slate-800", children: [
525
+ m.subject,
526
+ m.automatisch && /* @__PURE__ */ jsx(Badge, { tone: m.mislukt ? "amber" : "slate", children: m.mislukt ? "mislukt" : "automatisch" })
527
+ ] }),
434
528
  /* @__PURE__ */ jsx("p", { className: "text-xs text-slate-400", children: new Date(m.at).toLocaleString("nl-NL", { dateStyle: "short", timeStyle: "short" }) }),
435
529
  /* @__PURE__ */ jsx("p", { className: "mt-1 whitespace-pre-wrap text-slate-600", children: m.body })
436
530
  ] }, i)) })
@@ -514,6 +608,7 @@ function OrdersTab({ siteId }) {
514
608
  /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-2 border-t border-slate-100 pt-4", children: [
515
609
  current.status === "paid" && !shipOpen && /* @__PURE__ */ jsx(Button, { size: "sm", onClick: () => setShipOpen(true), children: "Markeer als verzonden" }),
516
610
  current.status === "pending" && /* @__PURE__ */ jsx(Button, { size: "sm", disabled: busy, onClick: () => void markPaid(current), children: "Als betaald boeken" }),
611
+ current.status === "pending" && /* @__PURE__ */ jsx(Button, { size: "sm", variant: "secondary", disabled: busy, onClick: () => void sendPaymentRequest(current), children: "Betaalverzoek sturen" }),
517
612
  ["pending", "paid"].includes(current.status) && /* @__PURE__ */ jsx(
518
613
  Button,
519
614
  {
@@ -534,6 +629,7 @@ function OrdersTab({ siteId }) {
534
629
  setMailSubject(`Over je bestelling ${current.number}`);
535
630
  setMailOpen(true);
536
631
  }, children: "\u2709 Klant mailen" }),
632
+ /* @__PURE__ */ jsx(Button, { size: "sm", variant: "secondary", disabled: busy, onClick: () => openEdit(current), children: "Bewerken" }),
537
633
  (current.status === "paid" || current.status === "shipped") && /* @__PURE__ */ jsxs(Fragment, { children: [
538
634
  /* @__PURE__ */ jsx(Button, { size: "sm", variant: "secondary", disabled: busy, onClick: () => resendMail(current), children: "Mail opnieuw versturen" }),
539
635
  /* @__PURE__ */ jsx(Button, { size: "sm", variant: "danger", disabled: busy || openstaand(current) === 0, onClick: () => {
@@ -542,6 +638,16 @@ function OrdersTab({ siteId }) {
542
638
  }, children: "Terugbetalen" })
543
639
  ] }),
544
640
  current.invoicePath && /* @__PURE__ */ jsx(Button, { size: "sm", variant: "secondary", disabled: busy, onClick: () => viewInvoice(current, current.invoicePath), children: "Factuur bekijken" }),
641
+ current.status === "cancelled" && !current.invoiceNumber && /* @__PURE__ */ jsx(
642
+ "button",
643
+ {
644
+ type: "button",
645
+ disabled: busy,
646
+ className: "rounded px-2 py-1 text-sm text-red-600 hover:underline disabled:opacity-50",
647
+ onClick: () => void removeOrder(current),
648
+ children: "Verwijderen"
649
+ }
650
+ ),
545
651
  current.stripe?.paymentIntentId && /* @__PURE__ */ jsx(
546
652
  "a",
547
653
  {
@@ -617,6 +723,68 @@ function OrdersTab({ siteId }) {
617
723
  /* @__PURE__ */ jsx(Button, { disabled: busy, onClick: () => void createManual(), children: busy ? "Bezig\u2026" : "Bestelling aanmaken" })
618
724
  ] })
619
725
  ] }) }) }),
726
+ /* @__PURE__ */ jsx(Dialog, { open: editOpen, onOpenChange: setEditOpen, children: /* @__PURE__ */ jsx(DialogContent, { title: current ? `Bestelling ${current.number} bewerken` : "Bestelling bewerken", width: "min(92vw, 44rem)", children: current && /* @__PURE__ */ jsxs("div", { className: "grid gap-3", children: [
727
+ /* @__PURE__ */ jsxs("div", { className: "grid gap-3 md:grid-cols-3", children: [
728
+ /* @__PURE__ */ jsx(Field, { label: "Naam", children: /* @__PURE__ */ jsx(Input, { value: editCustomer.name, onChange: (e) => setEditCustomer((p) => ({ ...p, name: e.target.value })) }) }),
729
+ /* @__PURE__ */ jsx(Field, { label: "E-mail", children: /* @__PURE__ */ jsx(Input, { value: editCustomer.email, onChange: (e) => setEditCustomer((p) => ({ ...p, email: e.target.value })) }) }),
730
+ /* @__PURE__ */ jsx(Field, { label: "Telefoon (optioneel)", children: /* @__PURE__ */ jsx(Input, { value: editCustomer.phone, onChange: (e) => setEditCustomer((p) => ({ ...p, phone: e.target.value })) }) })
731
+ ] }),
732
+ /* @__PURE__ */ jsxs("div", { className: "grid gap-3 md:grid-cols-3", children: [
733
+ /* @__PURE__ */ jsx(Field, { label: "Adres", children: /* @__PURE__ */ jsx(Input, { value: editShipping.address, onChange: (e) => setEditShipping((p) => ({ ...p, address: e.target.value })) }) }),
734
+ /* @__PURE__ */ jsx(Field, { label: "Postcode", children: /* @__PURE__ */ jsx(Input, { value: editShipping.zip, onChange: (e) => setEditShipping((p) => ({ ...p, zip: e.target.value })) }) }),
735
+ /* @__PURE__ */ jsx(Field, { label: "Plaats", children: /* @__PURE__ */ jsx(Input, { value: editShipping.city, onChange: (e) => setEditShipping((p) => ({ ...p, city: e.target.value })) }) })
736
+ ] }),
737
+ current.status === "pending" ? /* @__PURE__ */ jsxs(Fragment, { children: [
738
+ /* @__PURE__ */ jsxs("div", { children: [
739
+ /* @__PURE__ */ jsx("p", { className: "mb-1 text-sm font-medium", children: "Producten" }),
740
+ /* @__PURE__ */ jsxs("div", { className: "grid gap-2", children: [
741
+ editLines.map((l, i) => {
742
+ const prod = producten.find((p) => p.id === l.productId);
743
+ const varianten = (prod?.options?.length ?? 0) > 0 ? prod?.variants ?? [] : [];
744
+ return /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-[1fr_150px_70px_auto] items-center gap-2", children: [
745
+ /* @__PURE__ */ jsx(
746
+ Select,
747
+ {
748
+ value: l.productId,
749
+ onValueChange: (v) => setEditLines((p) => p.map((x, j) => j === i ? { ...x, productId: v, variantKey: "" } : x)),
750
+ placeholder: "Kies product\u2026",
751
+ options: producten.map((p) => ({ value: p.id, label: productLabel(p) }))
752
+ }
753
+ ),
754
+ varianten.length > 0 ? /* @__PURE__ */ jsx(
755
+ Select,
756
+ {
757
+ value: l.variantKey,
758
+ onValueChange: (v) => setEditLines((p) => p.map((x, j) => j === i ? { ...x, variantKey: v } : x)),
759
+ placeholder: "Uitvoering\u2026",
760
+ options: varianten.map((v) => ({ value: v.key, label: v.key.split("|").join(", ") }))
761
+ }
762
+ ) : /* @__PURE__ */ jsx("span", { className: "text-xs text-slate-400", children: "\u2014" }),
763
+ /* @__PURE__ */ jsx(
764
+ Input,
765
+ {
766
+ type: "number",
767
+ value: String(l.qty),
768
+ onChange: (e) => setEditLines((p) => p.map((x, j) => j === i ? { ...x, qty: Math.max(1, Number(e.target.value) || 1) } : x))
769
+ }
770
+ ),
771
+ /* @__PURE__ */ jsx(Button, { size: "sm", variant: "ghost", onClick: () => setEditLines((p) => p.filter((_, j) => j !== i)), children: "\u2715" })
772
+ ] }, i);
773
+ }),
774
+ /* @__PURE__ */ jsx(Button, { size: "sm", variant: "secondary", onClick: () => setEditLines((p) => [...p, { productId: "", variantKey: "", qty: 1 }]), children: "+ Regel" })
775
+ ] })
776
+ ] }),
777
+ /* @__PURE__ */ jsx(Field, { label: "Verzendkosten (\u20AC)", children: /* @__PURE__ */ jsx(Input, { value: editShippingEuro, onChange: (e) => setEditShippingEuro(e.target.value), placeholder: "0,00", className: "w-36" }) })
778
+ ] }) : /* @__PURE__ */ jsxs("p", { className: "text-xs text-slate-500", children: [
779
+ "Deze bestelling is al ",
780
+ ORDER_STATUS_LABELS[current.status]?.label.toLowerCase() ?? current.status,
781
+ ": de regels en verzendkosten staan vast (er hoort een factuur op dit bedrag). Gebruik Terugbetalen om het bedrag aan te passen."
782
+ ] }),
783
+ /* @__PURE__ */ jsxs("div", { className: "flex justify-end gap-2", children: [
784
+ /* @__PURE__ */ jsx(DialogClose, { asChild: true, children: /* @__PURE__ */ jsx(Button, { variant: "secondary", children: "Annuleren" }) }),
785
+ /* @__PURE__ */ jsx(Button, { disabled: busy, onClick: () => void saveEdit(current), children: busy ? "Bezig\u2026" : "Opslaan" })
786
+ ] })
787
+ ] }) }) }),
620
788
  /* @__PURE__ */ jsx(Dialog, { open: mailOpen, onOpenChange: setMailOpen, children: /* @__PURE__ */ jsx(DialogContent, { title: current ? `Mail ${current.customer.name}` : "Klant mailen", width: "min(92vw, 34rem)", children: /* @__PURE__ */ jsxs("div", { className: "grid gap-3", children: [
621
789
  /* @__PURE__ */ jsxs("p", { className: "text-sm text-slate-500", children: [
622
790
  "Gaat naar ",
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/admin/shop/OrdersTab.tsx"],"sourcesContent":["\"use client\";\n\nimport { useEffect, useMemo, useState } from \"react\";\nimport { collection, getDocs, limit, onSnapshot, orderBy, query, where } from \"firebase/firestore\";\nimport type { Timestamp } from \"firebase/firestore\";\nimport { getDb } from \"../../firebase/client\";\nimport { formatCents, orderRegelNaam, type Order } 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 } from \"../ui/ConfirmDialog\";\nimport { Dialog, DialogContent, DialogClose } from \"../ui/dialog\";\nimport { ORDER_STATUS_LABELS, centsToEuroInput, eurosToCents, shopCallable } from \"./lib\";\n\n/** Minimale productvorm voor de keuzelijst van een handmatige bestelling. */\ntype ProductKeuze = {\n id: string;\n name: string;\n categoryIds?: string[];\n options?: { name: string }[];\n variants?: { key: string }[];\n};\n\ntype StoredOrder = Order & {\n id: string;\n createdAt?: Timestamp;\n paidAt?: Timestamp;\n shippedAt?: Timestamp;\n refundedAt?: Timestamp;\n mail?: { confirmationSentAt?: Timestamp; error?: string };\n /** Runtime-velden die de Functions erbovenop schrijven (niet in OrderSchema). */\n provider?: \"stripe\" | \"mollie\";\n mollie?: { paymentId?: string };\n};\n\nconst FILTERS = [\n { value: \"all\", label: \"Alles\" },\n { value: \"pending\", label: \"Wacht op betaling\" },\n { value: \"paid\", label: \"Betaald\" },\n { value: \"shipped\", label: \"Verzonden\" },\n { value: \"cancelled\", label: \"Geannuleerd\" },\n { value: \"refunded\", label: \"Terugbetaald\" },\n { value: \"donation\", label: \"Donaties\" },\n];\n\nfunction fmtDate(t?: Timestamp): string {\n if (!t?.toDate) return \"—\";\n return t.toDate().toLocaleString(\"nl-NL\", { dateStyle: \"short\", timeStyle: \"short\" });\n}\n\n/** Btw-component uit een incl.-prijs: totaal × tarief / (100 + tarief). */\nfunction vatOf(cents: number, rate: number): number {\n return Math.round((cents * rate) / (100 + rate));\n}\n\nexport function OrdersTab({ siteId }: { siteId: string }) {\n const { toast } = useToast();\n const confirm = useConfirm();\n const [orders, setOrders] = useState<StoredOrder[]>([]);\n const [selected, setSelected] = useState<string | null>(null);\n const [filter, setFilter] = useState(\"all\");\n const [search, setSearch] = useState(\"\");\n const [busy, setBusy] = useState(false);\n const [invoice, setInvoice] = useState<{ url: string; number: string } | null>(null);\n const [shipOpen, setShipOpen] = useState(false);\n const [mailOpen, setMailOpen] = useState(false);\n const [mailSubject, setMailSubject] = useState(\"\");\n const [mailBody, setMailBody] = useState(\"\");\n const [mailCopy, setMailCopy] = useState(true);\n const [refundOpen, setRefundOpen] = useState(false);\n const [refundInput, setRefundInput] = useState(\"\");\n const [noteInput, setNoteInput] = useState(\"\");\n const [manualOpen, setManualOpen] = useState(false);\n const [manual, setManual] = useState({ name: \"\", email: \"\", phone: \"\", address: \"\", zip: \"\", city: \"\", shipping: \"\", note: \"\", paid: true, notify: false });\n const [manualLines, setManualLines] = useState<{ productId: string; variantKey: string; qty: number }[]>([]);\n const [producten, setProducten] = useState<ProductKeuze[]>([]);\n const [catNamen, setCatNamen] = useState<Record<string, string>>({});\n\n // Producten pas ophalen als het handmatige formulier opengaat: de bestellijst zelf heeft ze niet nodig.\n useEffect(() => {\n if (!manualOpen || producten.length > 0) return;\n const db = getDb();\n if (!db) return;\n void getDocs(query(collection(db, \"sites\", siteId, \"products\"), where(\"status\", \"==\", \"active\"))).then((snap) =>\n setProducten(\n snap.docs\n .map((d) => ({ id: d.id, ...(d.data() as Omit<ProductKeuze, \"id\">) }))\n .sort((a, b) => a.name.localeCompare(b.name, \"nl\")),\n ),\n );\n /**\n * Categorieën erbij, want in een winkel waar hetzelfde ontwerp als rompertje, slabbetje én\n * t-shirt bestaat is de productnaam alleen niet genoeg: de lijst toonde vier keer \"Blij blij\"\n * en je kon niet zien welke je aanklikte.\n */\n void getDocs(collection(db, \"sites\", siteId, \"shopCategories\")).then((snap) =>\n setCatNamen(Object.fromEntries(snap.docs.map((d) => [d.id, String((d.data() as { name?: string }).name ?? d.id)]))),\n );\n }, [manualOpen, producten.length, siteId]);\n\n /** Label voor de productkiezer: \"Blij blij — Slabbetje\". */\n const productLabel = (p: ProductKeuze) => {\n const cat = catNamen[p.categoryIds?.[0] ?? \"\"];\n return cat ? `${p.name} — ${cat}` : p.name;\n };\n const [trackingUrl, setTrackingUrl] = useState(\"\");\n const [notify, setNotify] = useState(true);\n\n useEffect(() => {\n const db = getDb();\n if (!db) return;\n return onSnapshot(\n query(collection(db, \"sites\", siteId, \"orders\"), orderBy(\"createdAt\", \"desc\"), limit(300)),\n (snap) => setOrders(snap.docs.map((d) => ({ id: d.id, ...(d.data() as Order) }) as StoredOrder)),\n );\n }, [siteId]);\n\n /** Dashboard-tegels: vandaag / deze week / omzet (betaald + verzonden). */\n const stats = useMemo(() => {\n const now = new Date();\n const startToday = new Date(now.getFullYear(), now.getMonth(), now.getDate()).getTime();\n const startWeek = startToday - ((now.getDay() + 6) % 7) * 86_400_000;\n const paidish = orders.filter((o) => [\"paid\", \"shipped\"].includes(o.status));\n return {\n today: paidish.filter((o) => (o.createdAt?.toMillis?.() ?? 0) >= startToday).length,\n week: paidish.filter((o) => (o.createdAt?.toMillis?.() ?? 0) >= startWeek).length,\n openPaid: orders.filter((o) => o.status === \"paid\").length,\n revenueCents: paidish.reduce((n, o) => n + o.amount.totalCents, 0),\n };\n }, [orders]);\n\n const visible = useMemo(() => {\n let list = orders;\n if (filter === \"donation\") list = list.filter((o) => o.items.some((i) => i.kind === \"donation\"));\n else if (filter !== \"all\") list = list.filter((o) => o.status === filter);\n const needle = search.trim().toLowerCase();\n if (needle) {\n list = list.filter(\n (o) =>\n o.number.toLowerCase().includes(needle) ||\n o.customer.name.toLowerCase().includes(needle) ||\n o.customer.email.toLowerCase().includes(needle),\n );\n }\n return list;\n }, [orders, filter, search]);\n\n const current = orders.find((o) => o.id === selected) ?? null;\n // Notitieveld volgt de geselecteerde order (en niet andersom: typen mag niet worden overschreven).\n useEffect(() => {\n const o = orders.find((x) => x.id === selected);\n setNoteInput(o?.adminNote ?? \"\");\n }, [selected, orders]);\n\n async function setStatus(order: StoredOrder, status: \"shipped\" | \"cancelled\") {\n setBusy(true);\n try {\n await shopCallable<Record<string, unknown>, { ok: boolean }>(\"updateOrderStatus\")({\n siteId,\n orderId: order.id,\n status,\n notify,\n trackingUrl: status === \"shipped\" && trackingUrl.trim() ? trackingUrl.trim() : undefined,\n });\n toast({\n title: status === \"shipped\" ? \"Gemarkeerd als verzonden\" : \"Bestelling geannuleerd\",\n description: notify ? \"De klant heeft een e-mail ontvangen.\" : undefined,\n tone: \"success\",\n });\n setShipOpen(false);\n setTrackingUrl(\"\");\n } catch (e) {\n toast({ title: \"Status wijzigen mislukt\", description: e instanceof Error ? e.message : undefined, tone: \"error\" });\n } finally {\n setBusy(false);\n }\n }\n\n /** Nog openstaand bedrag (totaal minus wat er al terug is). */\n function openstaand(order: StoredOrder): number {\n return Math.max(0, order.amount.totalCents - (order.refundedCents ?? 0));\n }\n\n async function refund(order: StoredOrder, bedragCents?: number) {\n const rest = openstaand(order);\n const bedrag = bedragCents ?? rest;\n // De provider bij naam noemen: dit beloofde altijd \"via Stripe\", ook op een Mollie-winkel.\n const providerNaam = (order.provider ?? (order.mollie?.paymentId ? \"mollie\" : \"stripe\")) === \"mollie\" ? \"Mollie\" : \"Stripe\";\n const volledig = bedrag >= rest;\n const ok = await confirm({\n title: volledig ? `Bestelling ${order.number} terugbetalen?` : `${formatCents(bedrag)} terugbetalen?`,\n description: volledig\n ? `Het openstaande bedrag (${formatCents(rest)}) wordt via ${providerNaam} teruggestort; de klant krijgt een e-mail.`\n : `Er wordt ${formatCents(bedrag)} van ${formatCents(order.amount.totalCents)} via ${providerNaam} teruggestort. De bestelling blijft verder gewoon staan.`,\n tone: \"danger\",\n confirmLabel: \"Terugbetalen\",\n });\n if (!ok) return;\n setBusy(true);\n try {\n await shopCallable<{ siteId: string; orderId: string; amountCents?: number }, { ok: boolean }>(\"refundOrder\")({\n siteId,\n orderId: order.id,\n ...(volledig ? {} : { amountCents: bedrag }),\n });\n toast({ title: volledig ? \"Terugbetaling gestart\" : `${formatCents(bedrag)} teruggestort`, tone: \"success\" });\n setRefundOpen(false);\n setRefundInput(\"\");\n } catch (e) {\n toast({ title: \"Terugbetalen mislukt\", description: e instanceof Error ? e.message : undefined, tone: \"error\" });\n } finally {\n setBusy(false);\n }\n }\n\n /** Klant rechtstreeks mailen (huisstijl + kopie naar de winkel + log bij de order). */\n async function mailKlant(order: StoredOrder) {\n if (!mailSubject.trim() || !mailBody.trim()) return;\n setBusy(true);\n try {\n await shopCallable<Record<string, unknown>, { ok: boolean; to: string }>(\"sendCustomerMail\")({\n siteId,\n orderId: order.id,\n subject: mailSubject.trim(),\n body: mailBody.trim(),\n copyToShop: mailCopy,\n });\n toast({ title: \"Bericht verstuurd\", description: order.customer.email, tone: \"success\" });\n setMailOpen(false);\n setMailSubject(\"\");\n setMailBody(\"\");\n } catch (e) {\n toast({ title: \"Versturen mislukt\", description: e instanceof Error ? e.message : undefined, tone: \"error\" });\n } finally {\n setBusy(false);\n }\n }\n\n /** Handmatige bestelling aanmaken (telefonisch/markt/overschrijving). */\n async function createManual() {\n const regels = manualLines.filter((l) => l.productId && l.qty > 0);\n if (!manual.name.trim() || !manual.email.trim() || regels.length === 0) {\n toast({ title: \"Vul naam, e-mail en minstens één product in\", tone: \"error\" });\n return;\n }\n setBusy(true);\n try {\n const res = await shopCallable<Record<string, unknown>, { id: string }>(\"createManualOrder\")({\n siteId,\n items: regels.map((l) => ({ productId: l.productId, variantKey: l.variantKey || undefined, qty: l.qty })),\n customer: { name: manual.name.trim(), email: manual.email.trim(), phone: manual.phone.trim() || undefined },\n shipping: manual.address.trim()\n ? { address: manual.address.trim(), zip: manual.zip.trim(), city: manual.city.trim(), country: \"NL\" }\n : undefined,\n shippingCents: eurosToCents(manual.shipping) ?? 0,\n note: manual.note.trim() || undefined,\n alBetaald: manual.paid,\n notify: manual.notify,\n });\n toast({ title: \"Bestelling aangemaakt\", description: manual.paid ? \"Geboekt als betaald.\" : \"Staat open.\", tone: \"success\" });\n setManualOpen(false);\n setManualLines([]);\n setManual({ name: \"\", email: \"\", phone: \"\", address: \"\", zip: \"\", city: \"\", shipping: \"\", note: \"\", paid: true, notify: false });\n setSelected(res.id);\n } catch (e) {\n toast({ title: \"Aanmaken mislukt\", description: e instanceof Error ? e.message : undefined, tone: \"error\" });\n } finally {\n setBusy(false);\n }\n }\n\n /** Openstaande bestelling handmatig op betaald zetten (overschrijving ontvangen). */\n async function markPaid(order: StoredOrder) {\n const ok = await confirm({\n title: `Bestelling ${order.number} als betaald boeken?`,\n description: \"Gebruik dit als je het geld buiten de webwinkel om hebt ontvangen (overschrijving, contant, pin). De voorraad wordt afgeboekt en de factuur wordt gemaakt.\",\n confirmLabel: \"Als betaald boeken\",\n });\n if (!ok) return;\n setBusy(true);\n try {\n await shopCallable<{ siteId: string; orderId: string; notify: boolean }, { ok: boolean }>(\"markOrderPaidManually\")({\n siteId,\n orderId: order.id,\n notify,\n });\n toast({ title: \"Geboekt als betaald\", tone: \"success\" });\n } catch (e) {\n toast({ title: \"Mislukt\", description: e instanceof Error ? e.message : undefined, tone: \"error\" });\n } finally {\n setBusy(false);\n }\n }\n\n /** Interne notitie opslaan (debounced via de knop; klant ziet hem nooit). */\n async function saveNote(order: StoredOrder) {\n setBusy(true);\n try {\n await shopCallable<{ siteId: string; orderId: string; note: string }, { ok: boolean }>(\"setOrderNote\")({\n siteId,\n orderId: order.id,\n note: noteInput,\n });\n toast({ title: \"Notitie opgeslagen\", tone: \"success\" });\n } catch (e) {\n toast({ title: \"Opslaan mislukt\", description: e instanceof Error ? e.message : undefined, tone: \"error\" });\n } finally {\n setBusy(false);\n }\n }\n\n async function resendMail(order: StoredOrder) {\n setBusy(true);\n try {\n await shopCallable<{ siteId: string; orderId: string }, { ok: boolean }>(\"resendOrderMail\")({ siteId, orderId: order.id });\n toast({ title: \"Bevestigingsmail opnieuw verstuurd\", tone: \"success\" });\n } catch (e) {\n toast({ title: \"Mail versturen mislukt\", description: e instanceof Error ? e.message : undefined, tone: \"error\" });\n } finally {\n setBusy(false);\n }\n }\n\n async function signedUrl(order: StoredOrder, path: string): Promise<string | null> {\n try {\n const res = await shopCallable<{ siteId: string; orderId: string; path: string }, { url: string }>(\"getOrderFileUrl\")({\n siteId,\n orderId: order.id,\n path,\n });\n return res.url;\n } catch (e) {\n toast({ title: \"Bestand ophalen mislukt\", description: e instanceof Error ? e.message : undefined, tone: \"error\" });\n return null;\n }\n }\n\n async function download(order: StoredOrder, path: string) {\n setBusy(true);\n const url = await signedUrl(order, path);\n setBusy(false);\n if (url) window.open(url, \"_blank\", \"noopener\");\n }\n\n /** Factuur inline tonen in een dialoog (met download/openen-in-tab erin). */\n async function viewInvoice(order: StoredOrder, path: string) {\n setBusy(true);\n const url = await signedUrl(order, path);\n setBusy(false);\n if (url) setInvoice({ url, number: order.number });\n }\n\n function exportCsv() {\n const rows = [\n [\"nummer\", \"datum\", \"status\", \"naam\", \"email\", \"items\", \"subtotaal\", \"korting\", \"verzendkosten\", \"totaal\", \"btw\"],\n ...orders.map((o) => {\n const vat =\n o.items.reduce((n, i) => n + vatOf(i.unitPriceCents * i.qty, i.vatRate ?? 21), 0) +\n (o.amount.shippingCents > 0 ? vatOf(o.amount.shippingCents, 21) : 0);\n return [\n o.number,\n fmtDate(o.createdAt),\n o.status,\n o.customer.name,\n o.customer.email,\n o.items.map((i) => `${i.qty}x ${orderRegelNaam(i)}${i.variantKey ? ` (${i.variantKey})` : \"\"}`).join(\" | \"),\n (o.amount.itemsCents / 100).toFixed(2),\n ((o.amount.discountCents ?? 0) / 100).toFixed(2),\n (o.amount.shippingCents / 100).toFixed(2),\n (o.amount.totalCents / 100).toFixed(2),\n (vat / 100).toFixed(2),\n ];\n }),\n ];\n const csv = rows.map((r) => r.map((c) => `\"${String(c).replaceAll('\"', '\"\"')}\"`).join(\";\")).join(\"\\n\");\n const blob = new Blob([\"\" + csv], { type: \"text/csv;charset=utf-8\" });\n const a = document.createElement(\"a\");\n a.href = URL.createObjectURL(blob);\n a.download = `bestellingen-${siteId}.csv`;\n a.click();\n URL.revokeObjectURL(a.href);\n }\n\n return (\n <div className=\"grid gap-4\">\n {/* Dashboard-tegels */}\n <div className=\"grid grid-cols-2 gap-3 md:grid-cols-4\">\n {[\n { label: \"Vandaag\", value: String(stats.today) },\n { label: \"Deze week\", value: String(stats.week) },\n { label: \"Te verzenden\", value: String(stats.openPaid) },\n { label: \"Omzet (betaald)\", value: formatCents(stats.revenueCents) },\n ].map((s) => (\n <Card key={s.label} className=\"py-3\">\n <p className=\"text-xs text-slate-500\">{s.label}</p>\n <p className=\"text-xl font-bold\">{s.value}</p>\n </Card>\n ))}\n </div>\n\n <div className=\"grid gap-4 md:grid-cols-[340px_1fr]\">\n <Card className=\"h-fit\">\n <div className=\"mb-3 flex items-center justify-between gap-2\">\n <h3 className=\"text-sm font-semibold\">Bestellingen</h3>\n <div className=\"flex gap-2\">\n <Button size=\"sm\" onClick={() => setManualOpen(true)}>+ Handmatig</Button>\n <Button size=\"sm\" variant=\"secondary\" onClick={exportCsv} disabled={orders.length === 0}>\n CSV\n </Button>\n </div>\n </div>\n <Input placeholder=\"Zoek op nummer, naam of e-mail…\" value={search} onChange={(e) => setSearch(e.target.value)} className=\"mb-2\" />\n <Select value={filter} onValueChange={setFilter} options={FILTERS} className=\"mb-2\" />\n <ul className=\"grid max-h-[60vh] gap-1 overflow-y-auto\">\n {visible.map((o) => {\n const st = ORDER_STATUS_LABELS[o.status] ?? { label: o.status, tone: \"slate\" as const };\n return (\n <li key={o.id}>\n <button\n onClick={() => {\n setSelected(o.id);\n setShipOpen(false);\n }}\n className={`grid w-full gap-0.5 rounded-lg px-3 py-2 text-left text-sm ${\n o.id === selected ? \"bg-brand-50 text-brand-700\" : \"hover:bg-slate-100\"\n }`}\n >\n <span className=\"flex items-center justify-between gap-2\">\n <span className=\"font-medium\">{o.number}</span>\n <Badge tone={st.tone}>{st.label}</Badge>\n </span>\n <span className=\"flex items-center justify-between gap-2 text-xs text-slate-500\">\n <span className=\"truncate\">{o.customer.name}</span>\n <span>{formatCents(o.amount.totalCents)}</span>\n </span>\n <span className=\"text-xs text-slate-400\">{fmtDate(o.createdAt)}</span>\n </button>\n </li>\n );\n })}\n {visible.length === 0 && <li className=\"px-3 py-2 text-sm text-slate-500\">Geen bestellingen.</li>}\n </ul>\n </Card>\n\n {current ? (\n <Card>\n <div className=\"grid gap-4\">\n <div className=\"flex flex-wrap items-center justify-between gap-2\">\n <h3 className=\"text-base font-semibold\">\n {current.number}{\" \"}\n <Badge tone={(ORDER_STATUS_LABELS[current.status] ?? { tone: \"slate\" as const }).tone}>\n {(ORDER_STATUS_LABELS[current.status] ?? { label: current.status }).label}\n </Badge>\n </h3>\n <span className=\"text-sm text-slate-500\">Geplaatst: {fmtDate(current.createdAt)}</span>\n </div>\n\n <div className=\"grid gap-4 md:grid-cols-2\">\n <div>\n <h4 className=\"mb-1 text-sm font-semibold\">Klant</h4>\n <p className=\"text-sm\">\n {current.customer.name}\n <br />\n <button\n type=\"button\"\n className=\"text-brand-700 underline\"\n title=\"Mail deze klant vanuit de winkel (in huisstijl, met een kopie bij de bestelling)\"\n onClick={() => { setMailSubject(`Over je bestelling ${current.number}`); setMailOpen(true); }}\n >\n {current.customer.email}\n </button>\n {current.customer.phone && (\n <>\n <br />\n {current.customer.phone}\n </>\n )}\n </p>\n </div>\n <div>\n <h4 className=\"mb-1 text-sm font-semibold\">Verzendadres</h4>\n {current.shipping ? (\n <p className=\"text-sm\">\n {current.shipping.address}\n <br />\n {current.shipping.zip} {current.shipping.city} ({current.shipping.country})\n </p>\n ) : (\n <p className=\"text-sm text-slate-500\">Geen verzending (donatie).</p>\n )}\n {current.trackingUrl && (\n <a className=\"text-sm text-brand-700 underline\" href={current.trackingUrl} target=\"_blank\" rel=\"noreferrer\">\n Track & trace ↗\n </a>\n )}\n </div>\n </div>\n\n {current.note && (\n <div>\n <h4 className=\"mb-1 text-sm font-semibold\">Opmerking van de klant</h4>\n <p className=\"whitespace-pre-wrap text-sm\">{current.note}</p>\n </div>\n )}\n\n {/* Interne notitie: alleen voor de winkelier (\"belt terug over maat\", \"wacht op\n levering leverancier\"). Bewust naast — niet in — de klantopmerking. */}\n <div>\n <h4 className=\"mb-1 text-sm font-semibold\">Interne notitie</h4>\n <Textarea\n rows={2}\n value={noteInput}\n placeholder=\"Alleen zichtbaar in het beheer…\"\n onChange={(e) => setNoteInput(e.target.value)}\n />\n {noteInput !== (current.adminNote ?? \"\") && (\n <Button size=\"sm\" variant=\"secondary\" className=\"mt-2\" disabled={busy} onClick={() => void saveNote(current)}>\n Notitie opslaan\n </Button>\n )}\n </div>\n\n {/* Wat is er al naar deze klant gestuurd? Zonder dit spoor verdwijnt elke afspraak. */}\n {(current.messages?.length ?? 0) > 0 && (\n <div>\n <h4 className=\"mb-1 text-sm font-semibold\">Verstuurde berichten</h4>\n <ul className=\"grid gap-2\">\n {current.messages!.map((m, i) => (\n <li key={i} className=\"rounded-lg border border-slate-200 p-2 text-sm\">\n <p className=\"font-medium text-slate-800\">{m.subject}</p>\n <p className=\"text-xs text-slate-400\">{new Date(m.at).toLocaleString(\"nl-NL\", { dateStyle: \"short\", timeStyle: \"short\" })}</p>\n <p className=\"mt-1 whitespace-pre-wrap text-slate-600\">{m.body}</p>\n </li>\n ))}\n </ul>\n </div>\n )}\n\n <div>\n <h4 className=\"mb-1 text-sm font-semibold\">Regels</h4>\n <table className=\"w-full text-sm\">\n <tbody>\n {current.items.map((i, idx) => (\n <tr key={idx} className=\"border-t border-slate-100 align-top\">\n <td className=\"py-1 pr-2\">\n {i.qty}× {orderRegelNaam(i)}\n {i.variantKey && <span className=\"text-slate-500\"> ({i.variantKey.split(\"|\").join(\" · \")})</span>}\n {i.kind === \"donation\" && <Badge tone=\"amber\">donatie</Badge>}\n {i.personalization &&\n Object.entries(i.personalization).map(([k, v]) => (\n <span key={k} className=\"block text-xs text-slate-600\">\n ↳ {v.text ?? v.fileName ?? \"eigen ontwerp\"}\n {v.imagePath && (\n <button\n type=\"button\"\n className=\"ml-1 text-brand-700 underline\"\n disabled={busy}\n onClick={() => download(current, v.imagePath as string)}\n >\n download\n </button>\n )}\n </span>\n ))}\n </td>\n <td className=\"py-1 text-right\">{formatCents(i.unitPriceCents * i.qty)}</td>\n </tr>\n ))}\n {(current.amount.discountCents ?? 0) > 0 && (\n <tr className=\"border-t border-slate-100 text-slate-500\">\n <td className=\"py-1 pr-2\">Korting{current.discount?.code ? ` (${current.discount.code})` : \"\"}</td>\n <td className=\"py-1 text-right\">−{formatCents(current.amount.discountCents ?? 0)}</td>\n </tr>\n )}\n <tr className=\"border-t border-slate-100 text-slate-500\">\n <td className=\"py-1 pr-2\">Verzendkosten</td>\n <td className=\"py-1 text-right\">\n {current.amount.shippingCents === 0 ? \"gratis\" : formatCents(current.amount.shippingCents)}\n </td>\n </tr>\n <tr className=\"border-t border-slate-200 font-semibold\">\n <td className=\"py-1 pr-2\">Totaal</td>\n <td className=\"py-1 text-right\">{formatCents(current.amount.totalCents)}</td>\n </tr>\n </tbody>\n </table>\n </div>\n\n {current.mail?.error ? (\n <p className=\"rounded-lg bg-amber-50 px-3 py-2 text-sm text-amber-700\">\n Bevestigingsmail mislukt: {current.mail.error}\n </p>\n ) : current.mail?.confirmationSentAt ? (\n /* Geslaagde verzending werd wél vastgelegd maar nergens getoond: je zag alleen\n iets bij een FOUT, en moest dus maar aannemen dat de mail was aangekomen. */\n <p className=\"text-xs text-slate-400\">\n Bevestigingsmail verstuurd op {fmtDate(current.mail.confirmationSentAt)}\n </p>\n ) : null}\n\n {/* Verzenden: track & trace + klant informeren */}\n {shipOpen && current.status === \"paid\" && (\n <div className=\"grid gap-3 rounded-lg border border-slate-200 p-3\">\n <Input\n placeholder=\"Track & trace-link (optioneel, https://…)\"\n value={trackingUrl}\n onChange={(e) => setTrackingUrl(e.target.value)}\n />\n <div className=\"flex items-center gap-4\">\n <Switch checked={notify} onCheckedChange={setNotify} label=\"Klant per e-mail informeren\" />\n <Button size=\"sm\" disabled={busy} onClick={() => setStatus(current, \"shipped\")}>\n Bevestig verzending\n </Button>\n </div>\n </div>\n )}\n\n <div className=\"flex flex-wrap items-center gap-2 border-t border-slate-100 pt-4\">\n {current.status === \"paid\" && !shipOpen && (\n <Button size=\"sm\" onClick={() => setShipOpen(true)}>Markeer als verzonden</Button>\n )}\n {/* Geld buiten de winkel om ontvangen (overschrijving/contant/pin). */}\n {current.status === \"pending\" && (\n <Button size=\"sm\" disabled={busy} onClick={() => void markPaid(current)}>\n Als betaald boeken\n </Button>\n )}\n {[\"pending\", \"paid\"].includes(current.status) && (\n <Button\n size=\"sm\"\n variant=\"secondary\"\n disabled={busy}\n onClick={async () => {\n const ok = await confirm({\n title: `Bestelling ${current.number} annuleren?`,\n description: notify ? \"De klant ontvangt een e-mail.\" : undefined,\n });\n if (ok) void setStatus(current, \"cancelled\");\n }}\n >\n Annuleer\n </Button>\n )}\n <Button size=\"sm\" variant=\"secondary\" disabled={busy} onClick={() => { setMailSubject(`Over je bestelling ${current.number}`); setMailOpen(true); }}>\n ✉ Klant mailen\n </Button>\n {(current.status === \"paid\" || current.status === \"shipped\") && (\n <>\n <Button size=\"sm\" variant=\"secondary\" disabled={busy} onClick={() => resendMail(current)}>\n Mail opnieuw versturen\n </Button>\n <Button size=\"sm\" variant=\"danger\" disabled={busy || openstaand(current) === 0} onClick={() => { setRefundInput(centsToEuroInput(openstaand(current))); setRefundOpen(true); }}>\n Terugbetalen\n </Button>\n </>\n )}\n {current.invoicePath && (\n <Button size=\"sm\" variant=\"secondary\" disabled={busy} onClick={() => viewInvoice(current, current.invoicePath as string)}>\n Factuur bekijken\n </Button>\n )}\n {current.stripe?.paymentIntentId && (\n <a\n className=\"ml-auto text-sm text-brand-700 underline\"\n href={`https://dashboard.stripe.com/payments/${current.stripe.paymentIntentId}`}\n target=\"_blank\"\n rel=\"noreferrer\"\n >\n Bekijk in Stripe ↗\n </a>\n )}\n </div>\n </div>\n </Card>\n ) : (\n <Card>\n <p className=\"text-sm text-slate-500\">Selecteer een bestelling.</p>\n </Card>\n )}\n </div>\n\n {/* Handmatige bestelling: telefonisch, op de markt, of per bankoverschrijving. */}\n <Dialog open={manualOpen} onOpenChange={setManualOpen}>\n <DialogContent title=\"Bestelling handmatig aanmaken\" width=\"min(92vw, 44rem)\">\n <div className=\"grid gap-3\">\n <p className=\"text-sm text-slate-500\">\n Voor een bestelling die buiten de webwinkel om binnenkwam. Prijzen en btw komen uit je\n productenlijst, dus de omzet en de voorraad blijven kloppen.\n </p>\n <div className=\"grid gap-3 md:grid-cols-3\">\n <Field label=\"Naam\"><Input value={manual.name} onChange={(e) => setManual((p) => ({ ...p, name: e.target.value }))} /></Field>\n <Field label=\"E-mail\"><Input value={manual.email} onChange={(e) => setManual((p) => ({ ...p, email: e.target.value }))} /></Field>\n <Field label=\"Telefoon (optioneel)\"><Input value={manual.phone} onChange={(e) => setManual((p) => ({ ...p, phone: e.target.value }))} /></Field>\n </div>\n\n <div>\n <p className=\"mb-1 text-sm font-medium\">Producten</p>\n <div className=\"grid gap-2\">\n {manualLines.map((l, i) => {\n const prod = producten.find((p) => p.id === l.productId);\n const varianten = (prod?.options?.length ?? 0) > 0 ? (prod?.variants ?? []) : [];\n return (\n <div key={i} className=\"grid grid-cols-[1fr_150px_70px_auto] items-center gap-2\">\n <Select\n value={l.productId}\n onValueChange={(v) => setManualLines((p) => p.map((x, j) => (j === i ? { ...x, productId: v, variantKey: \"\" } : x)))}\n placeholder=\"Kies product…\"\n options={producten.map((p) => ({ value: p.id, label: productLabel(p) }))}\n />\n {varianten.length > 0 ? (\n <Select\n value={l.variantKey}\n onValueChange={(v) => setManualLines((p) => p.map((x, j) => (j === i ? { ...x, variantKey: v } : x)))}\n placeholder=\"Uitvoering…\"\n options={varianten.map((v) => ({ value: v.key, label: v.key.split(\"|\").join(\", \") }))}\n />\n ) : (\n <span className=\"text-xs text-slate-400\">—</span>\n )}\n <Input\n type=\"number\"\n value={String(l.qty)}\n onChange={(e) => setManualLines((p) => p.map((x, j) => (j === i ? { ...x, qty: Math.max(1, Number(e.target.value) || 1) } : x)))}\n />\n <Button size=\"sm\" variant=\"ghost\" onClick={() => setManualLines((p) => p.filter((_, j) => j !== i))}>✕</Button>\n </div>\n );\n })}\n <Button size=\"sm\" variant=\"secondary\" onClick={() => setManualLines((p) => [...p, { productId: \"\", variantKey: \"\", qty: 1 }])}>\n + Regel\n </Button>\n </div>\n </div>\n\n <div className=\"grid gap-3 md:grid-cols-4\">\n <Field label=\"Adres\"><Input value={manual.address} onChange={(e) => setManual((p) => ({ ...p, address: e.target.value }))} /></Field>\n <Field label=\"Postcode\"><Input value={manual.zip} onChange={(e) => setManual((p) => ({ ...p, zip: e.target.value }))} /></Field>\n <Field label=\"Plaats\"><Input value={manual.city} onChange={(e) => setManual((p) => ({ ...p, city: e.target.value }))} /></Field>\n <Field label=\"Verzendkosten (€)\"><Input value={manual.shipping} onChange={(e) => setManual((p) => ({ ...p, shipping: e.target.value }))} placeholder=\"0,00\" /></Field>\n </div>\n <Field label=\"Notitie (optioneel)\">\n <Input value={manual.note} onChange={(e) => setManual((p) => ({ ...p, note: e.target.value }))} placeholder=\"bv. telefonisch besteld, contant betaald\" />\n </Field>\n <div className=\"flex flex-wrap gap-4\">\n <Switch checked={manual.paid} onCheckedChange={(v) => setManual((p) => ({ ...p, paid: v }))} label=\"Al betaald (voorraad afboeken + factuur maken)\" />\n <Switch checked={manual.notify} onCheckedChange={(v) => setManual((p) => ({ ...p, notify: v }))} label=\"Bevestigingsmail naar de klant\" />\n </div>\n <div className=\"flex justify-end gap-2\">\n <DialogClose asChild><Button variant=\"secondary\">Annuleren</Button></DialogClose>\n <Button disabled={busy} onClick={() => void createManual()}>{busy ? \"Bezig…\" : \"Bestelling aanmaken\"}</Button>\n </div>\n </div>\n </DialogContent>\n </Dialog>\n\n {/* Klant mailen — huisstijl, kopie naar de winkel, en het bericht komt bij de order te staan. */}\n <Dialog open={mailOpen} onOpenChange={setMailOpen}>\n <DialogContent title={current ? `Mail ${current.customer.name}` : \"Klant mailen\"} width=\"min(92vw, 34rem)\">\n <div className=\"grid gap-3\">\n <p className=\"text-sm text-slate-500\">\n Gaat naar <strong>{current?.customer.email}</strong> in de huisstijl van je site. Antwoorden\n komen binnen op je notificatie-adres.\n </p>\n <Field label=\"Onderwerp\">\n <Input value={mailSubject} onChange={(e) => setMailSubject(e.target.value)} />\n </Field>\n <Field label=\"Bericht\">\n <Textarea rows={7} value={mailBody} onChange={(e) => setMailBody(e.target.value)} placeholder=\"Beste …\" />\n </Field>\n <Switch checked={mailCopy} onCheckedChange={setMailCopy} label=\"Kopie naar mijzelf\" />\n <div className=\"flex justify-end gap-2\">\n <DialogClose asChild>\n <Button variant=\"secondary\">Annuleren</Button>\n </DialogClose>\n <Button disabled={busy || !mailSubject.trim() || !mailBody.trim()} onClick={() => current && void mailKlant(current)}>\n {busy ? \"Bezig…\" : \"Versturen\"}\n </Button>\n </div>\n </div>\n </DialogContent>\n </Dialog>\n\n {/* Terugbetalen — geheel of gedeeltelijk. */}\n <Dialog open={refundOpen} onOpenChange={setRefundOpen}>\n <DialogContent title={current ? `Terugbetalen — ${current.number}` : \"Terugbetalen\"} width=\"min(92vw, 26rem)\">\n <div className=\"grid gap-3\">\n <p className=\"text-sm text-slate-500\">\n Openstaand: <strong>{current ? formatCents(openstaand(current)) : \"—\"}</strong>\n {current && (current.refundedCents ?? 0) > 0 && (\n <> (er is al {formatCents(current.refundedCents as number)} terugbetaald)</>\n )}\n </p>\n <Field label=\"Bedrag (€)\" hint=\"Laat het volledige bedrag staan voor een hele terugbetaling\">\n <Input value={refundInput} onChange={(e) => setRefundInput(e.target.value)} className=\"w-36\" />\n </Field>\n <div className=\"flex justify-end gap-2\">\n <DialogClose asChild>\n <Button variant=\"secondary\">Annuleren</Button>\n </DialogClose>\n <Button\n variant=\"danger\"\n disabled={busy}\n onClick={() => {\n if (!current) return;\n const cents = eurosToCents(refundInput);\n if (cents === null || cents <= 0) {\n toast({ title: \"Vul een geldig bedrag in\", tone: \"error\" });\n return;\n }\n void refund(current, Math.min(cents, openstaand(current)));\n }}\n >\n {busy ? \"Bezig…\" : \"Terugbetalen\"}\n </Button>\n </div>\n </div>\n </DialogContent>\n </Dialog>\n\n <Dialog open={!!invoice} onOpenChange={(o) => !o && setInvoice(null)}>\n {invoice && (\n <DialogContent title={`Factuur ${invoice.number}`} width=\"min(92vw, 48rem)\">\n <iframe\n title={`Factuur ${invoice.number}`}\n src={invoice.url}\n className=\"h-[70vh] w-full rounded-lg border border-slate-200 bg-white\"\n />\n <div className=\"mt-3 flex items-center gap-2\">\n <a href={invoice.url} target=\"_blank\" rel=\"noreferrer\" download={`factuur-${invoice.number}.pdf`}>\n <Button size=\"sm\" variant=\"secondary\">Downloaden</Button>\n </a>\n <a href={invoice.url} target=\"_blank\" rel=\"noreferrer\" className=\"text-sm text-brand-700 underline\">\n Openen in nieuw tabblad ↗\n </a>\n <DialogClose asChild>\n <Button size=\"sm\" variant=\"ghost\" className=\"ml-auto\">Sluiten</Button>\n </DialogClose>\n </div>\n </DialogContent>\n )}\n </Dialog>\n </div>\n );\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../src/admin/shop/OrdersTab.tsx"],"sourcesContent":["\"use client\";\n\nimport { useEffect, useMemo, useState } from \"react\";\nimport { collection, getDocs, limit, onSnapshot, orderBy, query, where } from \"firebase/firestore\";\nimport type { Timestamp } from \"firebase/firestore\";\nimport { getDb } from \"../../firebase/client\";\nimport { formatCents, orderRegelNaam, type Order } 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 } from \"../ui/ConfirmDialog\";\nimport { Dialog, DialogContent, DialogClose } from \"../ui/dialog\";\nimport { ORDER_STATUS_LABELS, centsToEuroInput, eurosToCents, shopCallable } from \"./lib\";\n\n/** Minimale productvorm voor de keuzelijst van een handmatige bestelling. */\ntype ProductKeuze = {\n id: string;\n name: string;\n categoryIds?: string[];\n options?: { name: string }[];\n variants?: { key: string }[];\n};\n\ntype StoredOrder = Order & {\n id: string;\n createdAt?: Timestamp;\n paidAt?: Timestamp;\n shippedAt?: Timestamp;\n refundedAt?: Timestamp;\n mail?: { confirmationSentAt?: Timestamp; error?: string };\n /** Runtime-velden die de Functions erbovenop schrijven (niet in OrderSchema). */\n provider?: \"stripe\" | \"mollie\";\n mollie?: { paymentId?: string };\n};\n\nconst FILTERS = [\n { value: \"all\", label: \"Alles\" },\n { value: \"pending\", label: \"Wacht op betaling\" },\n { value: \"paid\", label: \"Betaald\" },\n { value: \"shipped\", label: \"Verzonden\" },\n { value: \"cancelled\", label: \"Geannuleerd\" },\n { value: \"refunded\", label: \"Terugbetaald\" },\n { value: \"donation\", label: \"Donaties\" },\n];\n\nfunction fmtDate(t?: Timestamp): string {\n if (!t?.toDate) return \"—\";\n return t.toDate().toLocaleString(\"nl-NL\", { dateStyle: \"short\", timeStyle: \"short\" });\n}\n\n/** Btw-component uit een incl.-prijs: totaal × tarief / (100 + tarief). */\nfunction vatOf(cents: number, rate: number): number {\n return Math.round((cents * rate) / (100 + rate));\n}\n\nexport function OrdersTab({ siteId }: { siteId: string }) {\n const { toast } = useToast();\n const confirm = useConfirm();\n const [orders, setOrders] = useState<StoredOrder[]>([]);\n const [selected, setSelected] = useState<string | null>(null);\n const [filter, setFilter] = useState(\"all\");\n const [search, setSearch] = useState(\"\");\n const [busy, setBusy] = useState(false);\n const [invoice, setInvoice] = useState<{ url: string; number: string } | null>(null);\n const [shipOpen, setShipOpen] = useState(false);\n const [mailOpen, setMailOpen] = useState(false);\n const [mailSubject, setMailSubject] = useState(\"\");\n const [mailBody, setMailBody] = useState(\"\");\n const [mailCopy, setMailCopy] = useState(true);\n const [refundOpen, setRefundOpen] = useState(false);\n const [refundInput, setRefundInput] = useState(\"\");\n const [noteInput, setNoteInput] = useState(\"\");\n const [manualOpen, setManualOpen] = useState(false);\n const [manual, setManual] = useState({ name: \"\", email: \"\", phone: \"\", address: \"\", zip: \"\", city: \"\", shipping: \"\", note: \"\", paid: true, notify: false });\n const [manualLines, setManualLines] = useState<{ productId: string; variantKey: string; qty: number }[]>([]);\n const [producten, setProducten] = useState<ProductKeuze[]>([]);\n const [catNamen, setCatNamen] = useState<Record<string, string>>({});\n\n /** Bestelling bewerken (7.25): klantgegevens/adres altijd; regels + verzendkosten alleen zolang\n * de order nog \"pending\" is (zie editOrder in functions — bedrag wijzigen na betaling gaat\n * via Terugbetalen, niet via dit formulier). */\n const [editOpen, setEditOpen] = useState(false);\n const [editCustomer, setEditCustomer] = useState({ name: \"\", email: \"\", phone: \"\" });\n const [editShipping, setEditShipping] = useState({ address: \"\", zip: \"\", city: \"\" });\n const [editShippingEuro, setEditShippingEuro] = useState(\"\");\n const [editLines, setEditLines] = useState<{ productId: string; variantKey: string; qty: number }[]>([]);\n\n // Producten pas ophalen als een formulier dat ze nodig heeft opengaat (handmatig aanmaken of\n // bewerken): de bestellijst zelf heeft ze niet nodig.\n useEffect(() => {\n if ((!manualOpen && !editOpen) || producten.length > 0) return;\n const db = getDb();\n if (!db) return;\n void getDocs(query(collection(db, \"sites\", siteId, \"products\"), where(\"status\", \"==\", \"active\"))).then((snap) =>\n setProducten(\n snap.docs\n .map((d) => ({ id: d.id, ...(d.data() as Omit<ProductKeuze, \"id\">) }))\n .sort((a, b) => a.name.localeCompare(b.name, \"nl\")),\n ),\n );\n /**\n * Categorieën erbij, want in een winkel waar hetzelfde ontwerp als rompertje, slabbetje én\n * t-shirt bestaat is de productnaam alleen niet genoeg: de lijst toonde vier keer \"Blij blij\"\n * en je kon niet zien welke je aanklikte.\n */\n void getDocs(collection(db, \"sites\", siteId, \"shopCategories\")).then((snap) =>\n setCatNamen(Object.fromEntries(snap.docs.map((d) => [d.id, String((d.data() as { name?: string }).name ?? d.id)]))),\n );\n }, [manualOpen, producten.length, siteId]);\n\n /** Label voor de productkiezer: \"Blij blij — Slabbetje\". */\n const productLabel = (p: ProductKeuze) => {\n const cat = catNamen[p.categoryIds?.[0] ?? \"\"];\n return cat ? `${p.name} — ${cat}` : p.name;\n };\n const [trackingUrl, setTrackingUrl] = useState(\"\");\n const [notify, setNotify] = useState(true);\n\n useEffect(() => {\n const db = getDb();\n if (!db) return;\n return onSnapshot(\n query(collection(db, \"sites\", siteId, \"orders\"), orderBy(\"createdAt\", \"desc\"), limit(300)),\n (snap) => setOrders(snap.docs.map((d) => ({ id: d.id, ...(d.data() as Order) }) as StoredOrder)),\n );\n }, [siteId]);\n\n /** Dashboard-tegels: vandaag / deze week / omzet (betaald + verzonden). */\n const stats = useMemo(() => {\n const now = new Date();\n const startToday = new Date(now.getFullYear(), now.getMonth(), now.getDate()).getTime();\n const startWeek = startToday - ((now.getDay() + 6) % 7) * 86_400_000;\n const paidish = orders.filter((o) => [\"paid\", \"shipped\"].includes(o.status));\n return {\n today: paidish.filter((o) => (o.createdAt?.toMillis?.() ?? 0) >= startToday).length,\n week: paidish.filter((o) => (o.createdAt?.toMillis?.() ?? 0) >= startWeek).length,\n openPaid: orders.filter((o) => o.status === \"paid\").length,\n revenueCents: paidish.reduce((n, o) => n + o.amount.totalCents, 0),\n };\n }, [orders]);\n\n const visible = useMemo(() => {\n let list = orders;\n if (filter === \"donation\") list = list.filter((o) => o.items.some((i) => i.kind === \"donation\"));\n else if (filter !== \"all\") list = list.filter((o) => o.status === filter);\n const needle = search.trim().toLowerCase();\n if (needle) {\n list = list.filter(\n (o) =>\n o.number.toLowerCase().includes(needle) ||\n o.customer.name.toLowerCase().includes(needle) ||\n o.customer.email.toLowerCase().includes(needle),\n );\n }\n return list;\n }, [orders, filter, search]);\n\n const current = orders.find((o) => o.id === selected) ?? null;\n // Notitieveld volgt de geselecteerde order (en niet andersom: typen mag niet worden overschreven).\n useEffect(() => {\n const o = orders.find((x) => x.id === selected);\n setNoteInput(o?.adminNote ?? \"\");\n }, [selected, orders]);\n\n async function setStatus(order: StoredOrder, status: \"shipped\" | \"cancelled\") {\n setBusy(true);\n try {\n await shopCallable<Record<string, unknown>, { ok: boolean }>(\"updateOrderStatus\")({\n siteId,\n orderId: order.id,\n status,\n notify,\n trackingUrl: status === \"shipped\" && trackingUrl.trim() ? trackingUrl.trim() : undefined,\n });\n toast({\n title: status === \"shipped\" ? \"Gemarkeerd als verzonden\" : \"Bestelling geannuleerd\",\n description: notify ? \"De klant heeft een e-mail ontvangen.\" : undefined,\n tone: \"success\",\n });\n setShipOpen(false);\n setTrackingUrl(\"\");\n } catch (e) {\n toast({ title: \"Status wijzigen mislukt\", description: e instanceof Error ? e.message : undefined, tone: \"error\" });\n } finally {\n setBusy(false);\n }\n }\n\n /** Nog openstaand bedrag (totaal minus wat er al terug is). */\n function openstaand(order: StoredOrder): number {\n return Math.max(0, order.amount.totalCents - (order.refundedCents ?? 0));\n }\n\n async function refund(order: StoredOrder, bedragCents?: number) {\n const rest = openstaand(order);\n const bedrag = bedragCents ?? rest;\n // De provider bij naam noemen: dit beloofde altijd \"via Stripe\", ook op een Mollie-winkel.\n const providerNaam = (order.provider ?? (order.mollie?.paymentId ? \"mollie\" : \"stripe\")) === \"mollie\" ? \"Mollie\" : \"Stripe\";\n const volledig = bedrag >= rest;\n const ok = await confirm({\n title: volledig ? `Bestelling ${order.number} terugbetalen?` : `${formatCents(bedrag)} terugbetalen?`,\n description: volledig\n ? `Het openstaande bedrag (${formatCents(rest)}) wordt via ${providerNaam} teruggestort; de klant krijgt een e-mail.`\n : `Er wordt ${formatCents(bedrag)} van ${formatCents(order.amount.totalCents)} via ${providerNaam} teruggestort. De bestelling blijft verder gewoon staan.`,\n tone: \"danger\",\n confirmLabel: \"Terugbetalen\",\n });\n if (!ok) return;\n setBusy(true);\n try {\n await shopCallable<{ siteId: string; orderId: string; amountCents?: number }, { ok: boolean }>(\"refundOrder\")({\n siteId,\n orderId: order.id,\n ...(volledig ? {} : { amountCents: bedrag }),\n });\n toast({ title: volledig ? \"Terugbetaling gestart\" : `${formatCents(bedrag)} teruggestort`, tone: \"success\" });\n setRefundOpen(false);\n setRefundInput(\"\");\n } catch (e) {\n toast({ title: \"Terugbetalen mislukt\", description: e instanceof Error ? e.message : undefined, tone: \"error\" });\n } finally {\n setBusy(false);\n }\n }\n\n /** Klant rechtstreeks mailen (huisstijl + kopie naar de winkel + log bij de order). */\n async function mailKlant(order: StoredOrder) {\n if (!mailSubject.trim() || !mailBody.trim()) return;\n setBusy(true);\n try {\n await shopCallable<Record<string, unknown>, { ok: boolean; to: string }>(\"sendCustomerMail\")({\n siteId,\n orderId: order.id,\n subject: mailSubject.trim(),\n body: mailBody.trim(),\n copyToShop: mailCopy,\n });\n toast({ title: \"Bericht verstuurd\", description: order.customer.email, tone: \"success\" });\n setMailOpen(false);\n setMailSubject(\"\");\n setMailBody(\"\");\n } catch (e) {\n toast({ title: \"Versturen mislukt\", description: e instanceof Error ? e.message : undefined, tone: \"error\" });\n } finally {\n setBusy(false);\n }\n }\n\n /** Handmatige bestelling aanmaken (telefonisch/markt/overschrijving). */\n async function createManual() {\n const regels = manualLines.filter((l) => l.productId && l.qty > 0);\n if (!manual.name.trim() || !manual.email.trim() || regels.length === 0) {\n toast({ title: \"Vul naam, e-mail en minstens één product in\", tone: \"error\" });\n return;\n }\n setBusy(true);\n try {\n const res = await shopCallable<Record<string, unknown>, { id: string }>(\"createManualOrder\")({\n siteId,\n items: regels.map((l) => ({ productId: l.productId, variantKey: l.variantKey || undefined, qty: l.qty })),\n customer: { name: manual.name.trim(), email: manual.email.trim(), phone: manual.phone.trim() || undefined },\n shipping: manual.address.trim()\n ? { address: manual.address.trim(), zip: manual.zip.trim(), city: manual.city.trim(), country: \"NL\" }\n : undefined,\n shippingCents: eurosToCents(manual.shipping) ?? 0,\n note: manual.note.trim() || undefined,\n alBetaald: manual.paid,\n notify: manual.notify,\n });\n toast({ title: \"Bestelling aangemaakt\", description: manual.paid ? \"Geboekt als betaald.\" : \"Staat open.\", tone: \"success\" });\n setManualOpen(false);\n setManualLines([]);\n setManual({ name: \"\", email: \"\", phone: \"\", address: \"\", zip: \"\", city: \"\", shipping: \"\", note: \"\", paid: true, notify: false });\n setSelected(res.id);\n } catch (e) {\n toast({ title: \"Aanmaken mislukt\", description: e instanceof Error ? e.message : undefined, tone: \"error\" });\n } finally {\n setBusy(false);\n }\n }\n\n /** Openstaande bestelling handmatig op betaald zetten (overschrijving ontvangen). */\n async function markPaid(order: StoredOrder) {\n const ok = await confirm({\n title: `Bestelling ${order.number} als betaald boeken?`,\n description: \"Gebruik dit als je het geld buiten de webwinkel om hebt ontvangen (overschrijving, contant, pin). De voorraad wordt afgeboekt en de factuur wordt gemaakt.\",\n confirmLabel: \"Als betaald boeken\",\n });\n if (!ok) return;\n setBusy(true);\n try {\n await shopCallable<{ siteId: string; orderId: string; notify: boolean }, { ok: boolean }>(\"markOrderPaidManually\")({\n siteId,\n orderId: order.id,\n notify,\n });\n toast({ title: \"Geboekt als betaald\", tone: \"success\" });\n } catch (e) {\n toast({ title: \"Mislukt\", description: e instanceof Error ? e.message : undefined, tone: \"error\" });\n } finally {\n setBusy(false);\n }\n }\n\n /** Interne notitie opslaan (debounced via de knop; klant ziet hem nooit). */\n async function saveNote(order: StoredOrder) {\n setBusy(true);\n try {\n await shopCallable<{ siteId: string; orderId: string; note: string }, { ok: boolean }>(\"setOrderNote\")({\n siteId,\n orderId: order.id,\n note: noteInput,\n });\n toast({ title: \"Notitie opgeslagen\", tone: \"success\" });\n } catch (e) {\n toast({ title: \"Opslaan mislukt\", description: e instanceof Error ? e.message : undefined, tone: \"error\" });\n } finally {\n setBusy(false);\n }\n }\n\n /** Bewerkformulier vullen met de huidige order — items alleen relevant zolang \"pending\". */\n function openEdit(order: StoredOrder) {\n setEditCustomer({ name: order.customer.name, email: order.customer.email, phone: order.customer.phone ?? \"\" });\n setEditShipping({\n address: order.shipping?.address ?? \"\",\n zip: order.shipping?.zip ?? \"\",\n city: order.shipping?.city ?? \"\",\n });\n setEditShippingEuro(centsToEuroInput(order.amount.shippingCents));\n setEditLines(\n order.status === \"pending\"\n ? order.items.filter((i) => i.kind !== \"donation\").map((i) => ({ productId: i.productId ?? \"\", variantKey: i.variantKey ?? \"\", qty: i.qty }))\n : [],\n );\n setEditOpen(true);\n }\n\n /** Bestelling opslaan — regels/verzendkosten gaan alleen mee als de order nog \"pending\" is. */\n async function saveEdit(order: StoredOrder) {\n if (!editCustomer.name.trim() || !editCustomer.email.trim()) {\n toast({ title: \"Naam en e-mail zijn verplicht\", tone: \"error\" });\n return;\n }\n const magBedragWijzigen = order.status === \"pending\";\n const regels = editLines.filter((l) => l.productId && l.qty > 0);\n if (magBedragWijzigen && regels.length === 0) {\n toast({ title: \"Er moet minstens één product op de bestelling staan\", tone: \"error\" });\n return;\n }\n setBusy(true);\n try {\n await shopCallable<Record<string, unknown>, { ok: boolean }>(\"editOrder\")({\n siteId,\n orderId: order.id,\n customer: {\n name: editCustomer.name.trim(),\n email: editCustomer.email.trim(),\n phone: editCustomer.phone.trim() || undefined,\n },\n // Leeg adresveld = geen wijziging (er is geen route om een adres te wissen — een\n // bestelling met producten heeft er altijd een nodig).\n shipping: editShipping.address.trim()\n ? { address: editShipping.address.trim(), zip: editShipping.zip.trim(), city: editShipping.city.trim(), country: \"NL\" }\n : undefined,\n ...(magBedragWijzigen\n ? {\n items: regels.map((l) => ({ productId: l.productId, variantKey: l.variantKey || undefined, qty: l.qty })),\n shippingCents: eurosToCents(editShippingEuro) ?? 0,\n }\n : {}),\n });\n toast({ title: \"Bestelling bijgewerkt\", tone: \"success\" });\n setEditOpen(false);\n } catch (e) {\n toast({ title: \"Opslaan mislukt\", description: e instanceof Error ? e.message : undefined, tone: \"error\" });\n } finally {\n setBusy(false);\n }\n }\n\n /** Betaalverzoek: betaallink maken en per mail versturen (zichtbaar bij een openstaande order). */\n async function sendPaymentRequest(order: StoredOrder) {\n const ok = await confirm({\n title: `Betaalverzoek sturen voor ${order.number}?`,\n description: `De klant ontvangt een e-mail met een betaallink voor ${formatCents(order.amount.totalCents)}.`,\n confirmLabel: \"Versturen\",\n });\n if (!ok) return;\n setBusy(true);\n try {\n await shopCallable<{ siteId: string; orderId: string }, { url: string }>(\"sendPaymentRequest\")({ siteId, orderId: order.id });\n toast({ title: \"Betaalverzoek verstuurd\", description: order.customer.email, tone: \"success\" });\n } catch (e) {\n toast({ title: \"Versturen mislukt\", description: e instanceof Error ? e.message : undefined, tone: \"error\" });\n } finally {\n setBusy(false);\n }\n }\n\n /** Definitief verwijderen — alleen mogelijk voor een geannuleerde order zonder factuurnummer. */\n async function removeOrder(order: StoredOrder) {\n const ok = await confirm({\n title: `Bestelling ${order.number} verwijderen?`,\n description: \"Dit kan niet ongedaan worden gemaakt.\",\n confirmLabel: \"Verwijderen\",\n tone: \"danger\",\n });\n if (!ok) return;\n setBusy(true);\n try {\n await shopCallable<{ siteId: string; orderId: string }, { ok: boolean }>(\"deleteOrder\")({ siteId, orderId: order.id });\n toast({ title: \"Bestelling verwijderd\", tone: \"success\" });\n setSelected(null);\n } catch (e) {\n toast({ title: \"Verwijderen mislukt\", description: e instanceof Error ? e.message : undefined, tone: \"error\" });\n } finally {\n setBusy(false);\n }\n }\n\n async function resendMail(order: StoredOrder) {\n setBusy(true);\n try {\n await shopCallable<{ siteId: string; orderId: string }, { ok: boolean }>(\"resendOrderMail\")({ siteId, orderId: order.id });\n toast({ title: \"Bevestigingsmail opnieuw verstuurd\", tone: \"success\" });\n } catch (e) {\n toast({ title: \"Mail versturen mislukt\", description: e instanceof Error ? e.message : undefined, tone: \"error\" });\n } finally {\n setBusy(false);\n }\n }\n\n async function signedUrl(order: StoredOrder, path: string): Promise<string | null> {\n try {\n const res = await shopCallable<{ siteId: string; orderId: string; path: string }, { url: string }>(\"getOrderFileUrl\")({\n siteId,\n orderId: order.id,\n path,\n });\n return res.url;\n } catch (e) {\n toast({ title: \"Bestand ophalen mislukt\", description: e instanceof Error ? e.message : undefined, tone: \"error\" });\n return null;\n }\n }\n\n async function download(order: StoredOrder, path: string) {\n setBusy(true);\n const url = await signedUrl(order, path);\n setBusy(false);\n if (url) window.open(url, \"_blank\", \"noopener\");\n }\n\n /** Factuur inline tonen in een dialoog (met download/openen-in-tab erin). */\n async function viewInvoice(order: StoredOrder, path: string) {\n setBusy(true);\n const url = await signedUrl(order, path);\n setBusy(false);\n if (url) setInvoice({ url, number: order.number });\n }\n\n function exportCsv() {\n const rows = [\n [\"nummer\", \"datum\", \"status\", \"naam\", \"email\", \"items\", \"subtotaal\", \"korting\", \"verzendkosten\", \"totaal\", \"btw\"],\n ...orders.map((o) => {\n const vat =\n o.items.reduce((n, i) => n + vatOf(i.unitPriceCents * i.qty, i.vatRate ?? 21), 0) +\n (o.amount.shippingCents > 0 ? vatOf(o.amount.shippingCents, 21) : 0);\n return [\n o.number,\n fmtDate(o.createdAt),\n o.status,\n o.customer.name,\n o.customer.email,\n o.items.map((i) => `${i.qty}x ${orderRegelNaam(i)}${i.variantKey ? ` (${i.variantKey})` : \"\"}`).join(\" | \"),\n (o.amount.itemsCents / 100).toFixed(2),\n ((o.amount.discountCents ?? 0) / 100).toFixed(2),\n (o.amount.shippingCents / 100).toFixed(2),\n (o.amount.totalCents / 100).toFixed(2),\n (vat / 100).toFixed(2),\n ];\n }),\n ];\n const csv = rows.map((r) => r.map((c) => `\"${String(c).replaceAll('\"', '\"\"')}\"`).join(\";\")).join(\"\\n\");\n const blob = new Blob([\"\" + csv], { type: \"text/csv;charset=utf-8\" });\n const a = document.createElement(\"a\");\n a.href = URL.createObjectURL(blob);\n a.download = `bestellingen-${siteId}.csv`;\n a.click();\n URL.revokeObjectURL(a.href);\n }\n\n return (\n <div className=\"grid gap-4\">\n {/* Dashboard-tegels */}\n <div className=\"grid grid-cols-2 gap-3 md:grid-cols-4\">\n {[\n { label: \"Vandaag\", value: String(stats.today) },\n { label: \"Deze week\", value: String(stats.week) },\n { label: \"Te verzenden\", value: String(stats.openPaid) },\n { label: \"Omzet (betaald)\", value: formatCents(stats.revenueCents) },\n ].map((s) => (\n <Card key={s.label} className=\"py-3\">\n <p className=\"text-xs text-slate-500\">{s.label}</p>\n <p className=\"text-xl font-bold\">{s.value}</p>\n </Card>\n ))}\n </div>\n\n <div className=\"grid gap-4 md:grid-cols-[340px_1fr]\">\n <Card className=\"h-fit\">\n <div className=\"mb-3 flex items-center justify-between gap-2\">\n <h3 className=\"text-sm font-semibold\">Bestellingen</h3>\n <div className=\"flex gap-2\">\n <Button size=\"sm\" onClick={() => setManualOpen(true)}>+ Handmatig</Button>\n <Button size=\"sm\" variant=\"secondary\" onClick={exportCsv} disabled={orders.length === 0}>\n CSV\n </Button>\n </div>\n </div>\n <Input placeholder=\"Zoek op nummer, naam of e-mail…\" value={search} onChange={(e) => setSearch(e.target.value)} className=\"mb-2\" />\n <Select value={filter} onValueChange={setFilter} options={FILTERS} className=\"mb-2\" />\n <ul className=\"grid max-h-[60vh] gap-1 overflow-y-auto\">\n {visible.map((o) => {\n const st = ORDER_STATUS_LABELS[o.status] ?? { label: o.status, tone: \"slate\" as const };\n return (\n <li key={o.id}>\n <button\n onClick={() => {\n setSelected(o.id);\n setShipOpen(false);\n }}\n className={`grid w-full gap-0.5 rounded-lg px-3 py-2 text-left text-sm ${\n o.id === selected ? \"bg-brand-50 text-brand-700\" : \"hover:bg-slate-100\"\n }`}\n >\n <span className=\"flex items-center justify-between gap-2\">\n <span className=\"font-medium\">{o.number}</span>\n <Badge tone={st.tone}>{st.label}</Badge>\n </span>\n <span className=\"flex items-center justify-between gap-2 text-xs text-slate-500\">\n <span className=\"truncate\">{o.customer.name}</span>\n <span>{formatCents(o.amount.totalCents)}</span>\n </span>\n <span className=\"text-xs text-slate-400\">{fmtDate(o.createdAt)}</span>\n </button>\n </li>\n );\n })}\n {visible.length === 0 && <li className=\"px-3 py-2 text-sm text-slate-500\">Geen bestellingen.</li>}\n </ul>\n </Card>\n\n {current ? (\n <Card>\n <div className=\"grid gap-4\">\n <div className=\"flex flex-wrap items-center justify-between gap-2\">\n <h3 className=\"text-base font-semibold\">\n {current.number}{\" \"}\n <Badge tone={(ORDER_STATUS_LABELS[current.status] ?? { tone: \"slate\" as const }).tone}>\n {(ORDER_STATUS_LABELS[current.status] ?? { label: current.status }).label}\n </Badge>\n </h3>\n <span className=\"text-sm text-slate-500\">Geplaatst: {fmtDate(current.createdAt)}</span>\n </div>\n\n <div className=\"grid gap-4 md:grid-cols-2\">\n <div>\n <h4 className=\"mb-1 text-sm font-semibold\">Klant</h4>\n <p className=\"text-sm\">\n {current.customer.name}\n <br />\n <button\n type=\"button\"\n className=\"text-brand-700 underline\"\n title=\"Mail deze klant vanuit de winkel (in huisstijl, met een kopie bij de bestelling)\"\n onClick={() => { setMailSubject(`Over je bestelling ${current.number}`); setMailOpen(true); }}\n >\n {current.customer.email}\n </button>\n {current.customer.phone && (\n <>\n <br />\n {current.customer.phone}\n </>\n )}\n </p>\n </div>\n <div>\n <h4 className=\"mb-1 text-sm font-semibold\">Verzendadres</h4>\n {current.shipping ? (\n <p className=\"text-sm\">\n {current.shipping.address}\n <br />\n {current.shipping.zip} {current.shipping.city} ({current.shipping.country})\n </p>\n ) : (\n <p className=\"text-sm text-slate-500\">Geen verzending (donatie).</p>\n )}\n {current.trackingUrl && (\n <a className=\"text-sm text-brand-700 underline\" href={current.trackingUrl} target=\"_blank\" rel=\"noreferrer\">\n Track & trace ↗\n </a>\n )}\n </div>\n </div>\n\n {current.note && (\n <div>\n <h4 className=\"mb-1 text-sm font-semibold\">Opmerking van de klant</h4>\n <p className=\"whitespace-pre-wrap text-sm\">{current.note}</p>\n </div>\n )}\n\n {/* Interne notitie: alleen voor de winkelier (\"belt terug over maat\", \"wacht op\n levering leverancier\"). Bewust naast — niet in — de klantopmerking. */}\n <div>\n <h4 className=\"mb-1 text-sm font-semibold\">Interne notitie</h4>\n <Textarea\n rows={2}\n value={noteInput}\n placeholder=\"Alleen zichtbaar in het beheer…\"\n onChange={(e) => setNoteInput(e.target.value)}\n />\n {noteInput !== (current.adminNote ?? \"\") && (\n <Button size=\"sm\" variant=\"secondary\" className=\"mt-2\" disabled={busy} onClick={() => void saveNote(current)}>\n Notitie opslaan\n </Button>\n )}\n </div>\n\n {/* Wat is er al naar deze klant gestuurd? Zonder dit spoor verdwijnt elke afspraak —\n en sinds 7.25 staan hier ook de AUTOMATISCHE mails (bevestiging, verzonden,\n geannuleerd, terugbetaald, betaalverzoek), inclusief een mislukte poging. */}\n {(current.messages?.length ?? 0) > 0 && (\n <div>\n <h4 className=\"mb-1 text-sm font-semibold\">Verstuurde berichten</h4>\n <ul className=\"grid gap-2\">\n {[...current.messages!].reverse().map((m, i) => (\n <li key={i} className={`rounded-lg border p-2 text-sm ${m.mislukt ? \"border-red-200 bg-red-50\" : \"border-slate-200\"}`}>\n <p className=\"flex flex-wrap items-center gap-2 font-medium text-slate-800\">\n {m.subject}\n {m.automatisch && <Badge tone={m.mislukt ? \"amber\" : \"slate\"}>{m.mislukt ? \"mislukt\" : \"automatisch\"}</Badge>}\n </p>\n <p className=\"text-xs text-slate-400\">{new Date(m.at).toLocaleString(\"nl-NL\", { dateStyle: \"short\", timeStyle: \"short\" })}</p>\n <p className=\"mt-1 whitespace-pre-wrap text-slate-600\">{m.body}</p>\n </li>\n ))}\n </ul>\n </div>\n )}\n\n <div>\n <h4 className=\"mb-1 text-sm font-semibold\">Regels</h4>\n <table className=\"w-full text-sm\">\n <tbody>\n {current.items.map((i, idx) => (\n <tr key={idx} className=\"border-t border-slate-100 align-top\">\n <td className=\"py-1 pr-2\">\n {i.qty}× {orderRegelNaam(i)}\n {i.variantKey && <span className=\"text-slate-500\"> ({i.variantKey.split(\"|\").join(\" · \")})</span>}\n {i.kind === \"donation\" && <Badge tone=\"amber\">donatie</Badge>}\n {i.personalization &&\n Object.entries(i.personalization).map(([k, v]) => (\n <span key={k} className=\"block text-xs text-slate-600\">\n ↳ {v.text ?? v.fileName ?? \"eigen ontwerp\"}\n {v.imagePath && (\n <button\n type=\"button\"\n className=\"ml-1 text-brand-700 underline\"\n disabled={busy}\n onClick={() => download(current, v.imagePath as string)}\n >\n download\n </button>\n )}\n </span>\n ))}\n </td>\n <td className=\"py-1 text-right\">{formatCents(i.unitPriceCents * i.qty)}</td>\n </tr>\n ))}\n {(current.amount.discountCents ?? 0) > 0 && (\n <tr className=\"border-t border-slate-100 text-slate-500\">\n <td className=\"py-1 pr-2\">Korting{current.discount?.code ? ` (${current.discount.code})` : \"\"}</td>\n <td className=\"py-1 text-right\">−{formatCents(current.amount.discountCents ?? 0)}</td>\n </tr>\n )}\n <tr className=\"border-t border-slate-100 text-slate-500\">\n <td className=\"py-1 pr-2\">Verzendkosten</td>\n <td className=\"py-1 text-right\">\n {current.amount.shippingCents === 0 ? \"gratis\" : formatCents(current.amount.shippingCents)}\n </td>\n </tr>\n <tr className=\"border-t border-slate-200 font-semibold\">\n <td className=\"py-1 pr-2\">Totaal</td>\n <td className=\"py-1 text-right\">{formatCents(current.amount.totalCents)}</td>\n </tr>\n </tbody>\n </table>\n </div>\n\n {current.mail?.error ? (\n <p className=\"rounded-lg bg-amber-50 px-3 py-2 text-sm text-amber-700\">\n Bevestigingsmail mislukt: {current.mail.error}\n </p>\n ) : current.mail?.confirmationSentAt ? (\n /* Geslaagde verzending werd wél vastgelegd maar nergens getoond: je zag alleen\n iets bij een FOUT, en moest dus maar aannemen dat de mail was aangekomen. */\n <p className=\"text-xs text-slate-400\">\n Bevestigingsmail verstuurd op {fmtDate(current.mail.confirmationSentAt)}\n </p>\n ) : null}\n\n {/* Verzenden: track & trace + klant informeren */}\n {shipOpen && current.status === \"paid\" && (\n <div className=\"grid gap-3 rounded-lg border border-slate-200 p-3\">\n <Input\n placeholder=\"Track & trace-link (optioneel, https://…)\"\n value={trackingUrl}\n onChange={(e) => setTrackingUrl(e.target.value)}\n />\n <div className=\"flex items-center gap-4\">\n <Switch checked={notify} onCheckedChange={setNotify} label=\"Klant per e-mail informeren\" />\n <Button size=\"sm\" disabled={busy} onClick={() => setStatus(current, \"shipped\")}>\n Bevestig verzending\n </Button>\n </div>\n </div>\n )}\n\n <div className=\"flex flex-wrap items-center gap-2 border-t border-slate-100 pt-4\">\n {current.status === \"paid\" && !shipOpen && (\n <Button size=\"sm\" onClick={() => setShipOpen(true)}>Markeer als verzonden</Button>\n )}\n {/* Geld buiten de winkel om ontvangen (overschrijving/contant/pin). */}\n {current.status === \"pending\" && (\n <Button size=\"sm\" disabled={busy} onClick={() => void markPaid(current)}>\n Als betaald boeken\n </Button>\n )}\n {/* Betaalverzoek (7.25): een betaallink mailen voor een handmatige order die nog\n niet betaald is, of een online afrekening die de klant nooit afmaakte. */}\n {current.status === \"pending\" && (\n <Button size=\"sm\" variant=\"secondary\" disabled={busy} onClick={() => void sendPaymentRequest(current)}>\n Betaalverzoek sturen\n </Button>\n )}\n {[\"pending\", \"paid\"].includes(current.status) && (\n <Button\n size=\"sm\"\n variant=\"secondary\"\n disabled={busy}\n onClick={async () => {\n const ok = await confirm({\n title: `Bestelling ${current.number} annuleren?`,\n description: notify ? \"De klant ontvangt een e-mail.\" : undefined,\n });\n if (ok) void setStatus(current, \"cancelled\");\n }}\n >\n Annuleer\n </Button>\n )}\n <Button size=\"sm\" variant=\"secondary\" disabled={busy} onClick={() => { setMailSubject(`Over je bestelling ${current.number}`); setMailOpen(true); }}>\n ✉ Klant mailen\n </Button>\n {/* Bewerken (7.25): klantgegevens/adres altijd; regels + verzendkosten alleen\n zolang de order nog \"pending\" is (zie openEdit/saveEdit). */}\n <Button size=\"sm\" variant=\"secondary\" disabled={busy} onClick={() => openEdit(current)}>\n Bewerken\n </Button>\n {(current.status === \"paid\" || current.status === \"shipped\") && (\n <>\n <Button size=\"sm\" variant=\"secondary\" disabled={busy} onClick={() => resendMail(current)}>\n Mail opnieuw versturen\n </Button>\n <Button size=\"sm\" variant=\"danger\" disabled={busy || openstaand(current) === 0} onClick={() => { setRefundInput(centsToEuroInput(openstaand(current))); setRefundOpen(true); }}>\n Terugbetalen\n </Button>\n </>\n )}\n {current.invoicePath && (\n <Button size=\"sm\" variant=\"secondary\" disabled={busy} onClick={() => viewInvoice(current, current.invoicePath as string)}>\n Factuur bekijken\n </Button>\n )}\n {/* Verwijderen (7.25): alleen een geannuleerde bestelling die nooit gefactureerd is\n — anders slaat het een gat in de doorlopende factuurreeks (zie deleteOrder). */}\n {current.status === \"cancelled\" && !current.invoiceNumber && (\n <button\n type=\"button\"\n disabled={busy}\n className=\"rounded px-2 py-1 text-sm text-red-600 hover:underline disabled:opacity-50\"\n onClick={() => void removeOrder(current)}\n >\n Verwijderen\n </button>\n )}\n {current.stripe?.paymentIntentId && (\n <a\n className=\"ml-auto text-sm text-brand-700 underline\"\n href={`https://dashboard.stripe.com/payments/${current.stripe.paymentIntentId}`}\n target=\"_blank\"\n rel=\"noreferrer\"\n >\n Bekijk in Stripe ↗\n </a>\n )}\n </div>\n </div>\n </Card>\n ) : (\n <Card>\n <p className=\"text-sm text-slate-500\">Selecteer een bestelling.</p>\n </Card>\n )}\n </div>\n\n {/* Handmatige bestelling: telefonisch, op de markt, of per bankoverschrijving. */}\n <Dialog open={manualOpen} onOpenChange={setManualOpen}>\n <DialogContent title=\"Bestelling handmatig aanmaken\" width=\"min(92vw, 44rem)\">\n <div className=\"grid gap-3\">\n <p className=\"text-sm text-slate-500\">\n Voor een bestelling die buiten de webwinkel om binnenkwam. Prijzen en btw komen uit je\n productenlijst, dus de omzet en de voorraad blijven kloppen.\n </p>\n <div className=\"grid gap-3 md:grid-cols-3\">\n <Field label=\"Naam\"><Input value={manual.name} onChange={(e) => setManual((p) => ({ ...p, name: e.target.value }))} /></Field>\n <Field label=\"E-mail\"><Input value={manual.email} onChange={(e) => setManual((p) => ({ ...p, email: e.target.value }))} /></Field>\n <Field label=\"Telefoon (optioneel)\"><Input value={manual.phone} onChange={(e) => setManual((p) => ({ ...p, phone: e.target.value }))} /></Field>\n </div>\n\n <div>\n <p className=\"mb-1 text-sm font-medium\">Producten</p>\n <div className=\"grid gap-2\">\n {manualLines.map((l, i) => {\n const prod = producten.find((p) => p.id === l.productId);\n const varianten = (prod?.options?.length ?? 0) > 0 ? (prod?.variants ?? []) : [];\n return (\n <div key={i} className=\"grid grid-cols-[1fr_150px_70px_auto] items-center gap-2\">\n <Select\n value={l.productId}\n onValueChange={(v) => setManualLines((p) => p.map((x, j) => (j === i ? { ...x, productId: v, variantKey: \"\" } : x)))}\n placeholder=\"Kies product…\"\n options={producten.map((p) => ({ value: p.id, label: productLabel(p) }))}\n />\n {varianten.length > 0 ? (\n <Select\n value={l.variantKey}\n onValueChange={(v) => setManualLines((p) => p.map((x, j) => (j === i ? { ...x, variantKey: v } : x)))}\n placeholder=\"Uitvoering…\"\n options={varianten.map((v) => ({ value: v.key, label: v.key.split(\"|\").join(\", \") }))}\n />\n ) : (\n <span className=\"text-xs text-slate-400\">—</span>\n )}\n <Input\n type=\"number\"\n value={String(l.qty)}\n onChange={(e) => setManualLines((p) => p.map((x, j) => (j === i ? { ...x, qty: Math.max(1, Number(e.target.value) || 1) } : x)))}\n />\n <Button size=\"sm\" variant=\"ghost\" onClick={() => setManualLines((p) => p.filter((_, j) => j !== i))}>✕</Button>\n </div>\n );\n })}\n <Button size=\"sm\" variant=\"secondary\" onClick={() => setManualLines((p) => [...p, { productId: \"\", variantKey: \"\", qty: 1 }])}>\n + Regel\n </Button>\n </div>\n </div>\n\n <div className=\"grid gap-3 md:grid-cols-4\">\n <Field label=\"Adres\"><Input value={manual.address} onChange={(e) => setManual((p) => ({ ...p, address: e.target.value }))} /></Field>\n <Field label=\"Postcode\"><Input value={manual.zip} onChange={(e) => setManual((p) => ({ ...p, zip: e.target.value }))} /></Field>\n <Field label=\"Plaats\"><Input value={manual.city} onChange={(e) => setManual((p) => ({ ...p, city: e.target.value }))} /></Field>\n <Field label=\"Verzendkosten (€)\"><Input value={manual.shipping} onChange={(e) => setManual((p) => ({ ...p, shipping: e.target.value }))} placeholder=\"0,00\" /></Field>\n </div>\n <Field label=\"Notitie (optioneel)\">\n <Input value={manual.note} onChange={(e) => setManual((p) => ({ ...p, note: e.target.value }))} placeholder=\"bv. telefonisch besteld, contant betaald\" />\n </Field>\n <div className=\"flex flex-wrap gap-4\">\n <Switch checked={manual.paid} onCheckedChange={(v) => setManual((p) => ({ ...p, paid: v }))} label=\"Al betaald (voorraad afboeken + factuur maken)\" />\n <Switch checked={manual.notify} onCheckedChange={(v) => setManual((p) => ({ ...p, notify: v }))} label=\"Bevestigingsmail naar de klant\" />\n </div>\n <div className=\"flex justify-end gap-2\">\n <DialogClose asChild><Button variant=\"secondary\">Annuleren</Button></DialogClose>\n <Button disabled={busy} onClick={() => void createManual()}>{busy ? \"Bezig…\" : \"Bestelling aanmaken\"}</Button>\n </div>\n </div>\n </DialogContent>\n </Dialog>\n\n {/* Bestelling bewerken (7.25). Regels/verzendkosten alleen zichtbaar zolang de bestelling\n nog \"pending\" is — na betaling staat er een factuur op het bedrag, dus dan pas je het\n NIET meer aan met dit formulier (zie de toelichting in editOrder). */}\n <Dialog open={editOpen} onOpenChange={setEditOpen}>\n <DialogContent title={current ? `Bestelling ${current.number} bewerken` : \"Bestelling bewerken\"} width=\"min(92vw, 44rem)\">\n {current && (\n <div className=\"grid gap-3\">\n <div className=\"grid gap-3 md:grid-cols-3\">\n <Field label=\"Naam\"><Input value={editCustomer.name} onChange={(e) => setEditCustomer((p) => ({ ...p, name: e.target.value }))} /></Field>\n <Field label=\"E-mail\"><Input value={editCustomer.email} onChange={(e) => setEditCustomer((p) => ({ ...p, email: e.target.value }))} /></Field>\n <Field label=\"Telefoon (optioneel)\"><Input value={editCustomer.phone} onChange={(e) => setEditCustomer((p) => ({ ...p, phone: e.target.value }))} /></Field>\n </div>\n <div className=\"grid gap-3 md:grid-cols-3\">\n <Field label=\"Adres\"><Input value={editShipping.address} onChange={(e) => setEditShipping((p) => ({ ...p, address: e.target.value }))} /></Field>\n <Field label=\"Postcode\"><Input value={editShipping.zip} onChange={(e) => setEditShipping((p) => ({ ...p, zip: e.target.value }))} /></Field>\n <Field label=\"Plaats\"><Input value={editShipping.city} onChange={(e) => setEditShipping((p) => ({ ...p, city: e.target.value }))} /></Field>\n </div>\n\n {current.status === \"pending\" ? (\n <>\n <div>\n <p className=\"mb-1 text-sm font-medium\">Producten</p>\n <div className=\"grid gap-2\">\n {editLines.map((l, i) => {\n const prod = producten.find((p) => p.id === l.productId);\n const varianten = (prod?.options?.length ?? 0) > 0 ? (prod?.variants ?? []) : [];\n return (\n <div key={i} className=\"grid grid-cols-[1fr_150px_70px_auto] items-center gap-2\">\n <Select\n value={l.productId}\n onValueChange={(v) => setEditLines((p) => p.map((x, j) => (j === i ? { ...x, productId: v, variantKey: \"\" } : x)))}\n placeholder=\"Kies product…\"\n options={producten.map((p) => ({ value: p.id, label: productLabel(p) }))}\n />\n {varianten.length > 0 ? (\n <Select\n value={l.variantKey}\n onValueChange={(v) => setEditLines((p) => p.map((x, j) => (j === i ? { ...x, variantKey: v } : x)))}\n placeholder=\"Uitvoering…\"\n options={varianten.map((v) => ({ value: v.key, label: v.key.split(\"|\").join(\", \") }))}\n />\n ) : (\n <span className=\"text-xs text-slate-400\">—</span>\n )}\n <Input\n type=\"number\"\n value={String(l.qty)}\n onChange={(e) => setEditLines((p) => p.map((x, j) => (j === i ? { ...x, qty: Math.max(1, Number(e.target.value) || 1) } : x)))}\n />\n <Button size=\"sm\" variant=\"ghost\" onClick={() => setEditLines((p) => p.filter((_, j) => j !== i))}>✕</Button>\n </div>\n );\n })}\n <Button size=\"sm\" variant=\"secondary\" onClick={() => setEditLines((p) => [...p, { productId: \"\", variantKey: \"\", qty: 1 }])}>\n + Regel\n </Button>\n </div>\n </div>\n <Field label=\"Verzendkosten (€)\"><Input value={editShippingEuro} onChange={(e) => setEditShippingEuro(e.target.value)} placeholder=\"0,00\" className=\"w-36\" /></Field>\n </>\n ) : (\n <p className=\"text-xs text-slate-500\">\n Deze bestelling is al {ORDER_STATUS_LABELS[current.status]?.label.toLowerCase() ?? current.status}: de regels en verzendkosten\n staan vast (er hoort een factuur op dit bedrag). Gebruik Terugbetalen om het bedrag aan te passen.\n </p>\n )}\n\n <div className=\"flex justify-end gap-2\">\n <DialogClose asChild><Button variant=\"secondary\">Annuleren</Button></DialogClose>\n <Button disabled={busy} onClick={() => void saveEdit(current)}>{busy ? \"Bezig…\" : \"Opslaan\"}</Button>\n </div>\n </div>\n )}\n </DialogContent>\n </Dialog>\n\n {/* Klant mailen — huisstijl, kopie naar de winkel, en het bericht komt bij de order te staan. */}\n <Dialog open={mailOpen} onOpenChange={setMailOpen}>\n <DialogContent title={current ? `Mail ${current.customer.name}` : \"Klant mailen\"} width=\"min(92vw, 34rem)\">\n <div className=\"grid gap-3\">\n <p className=\"text-sm text-slate-500\">\n Gaat naar <strong>{current?.customer.email}</strong> in de huisstijl van je site. Antwoorden\n komen binnen op je notificatie-adres.\n </p>\n <Field label=\"Onderwerp\">\n <Input value={mailSubject} onChange={(e) => setMailSubject(e.target.value)} />\n </Field>\n <Field label=\"Bericht\">\n <Textarea rows={7} value={mailBody} onChange={(e) => setMailBody(e.target.value)} placeholder=\"Beste …\" />\n </Field>\n <Switch checked={mailCopy} onCheckedChange={setMailCopy} label=\"Kopie naar mijzelf\" />\n <div className=\"flex justify-end gap-2\">\n <DialogClose asChild>\n <Button variant=\"secondary\">Annuleren</Button>\n </DialogClose>\n <Button disabled={busy || !mailSubject.trim() || !mailBody.trim()} onClick={() => current && void mailKlant(current)}>\n {busy ? \"Bezig…\" : \"Versturen\"}\n </Button>\n </div>\n </div>\n </DialogContent>\n </Dialog>\n\n {/* Terugbetalen — geheel of gedeeltelijk. */}\n <Dialog open={refundOpen} onOpenChange={setRefundOpen}>\n <DialogContent title={current ? `Terugbetalen — ${current.number}` : \"Terugbetalen\"} width=\"min(92vw, 26rem)\">\n <div className=\"grid gap-3\">\n <p className=\"text-sm text-slate-500\">\n Openstaand: <strong>{current ? formatCents(openstaand(current)) : \"—\"}</strong>\n {current && (current.refundedCents ?? 0) > 0 && (\n <> (er is al {formatCents(current.refundedCents as number)} terugbetaald)</>\n )}\n </p>\n <Field label=\"Bedrag (€)\" hint=\"Laat het volledige bedrag staan voor een hele terugbetaling\">\n <Input value={refundInput} onChange={(e) => setRefundInput(e.target.value)} className=\"w-36\" />\n </Field>\n <div className=\"flex justify-end gap-2\">\n <DialogClose asChild>\n <Button variant=\"secondary\">Annuleren</Button>\n </DialogClose>\n <Button\n variant=\"danger\"\n disabled={busy}\n onClick={() => {\n if (!current) return;\n const cents = eurosToCents(refundInput);\n if (cents === null || cents <= 0) {\n toast({ title: \"Vul een geldig bedrag in\", tone: \"error\" });\n return;\n }\n void refund(current, Math.min(cents, openstaand(current)));\n }}\n >\n {busy ? \"Bezig…\" : \"Terugbetalen\"}\n </Button>\n </div>\n </div>\n </DialogContent>\n </Dialog>\n\n <Dialog open={!!invoice} onOpenChange={(o) => !o && setInvoice(null)}>\n {invoice && (\n <DialogContent title={`Factuur ${invoice.number}`} width=\"min(92vw, 48rem)\">\n <iframe\n title={`Factuur ${invoice.number}`}\n src={invoice.url}\n className=\"h-[70vh] w-full rounded-lg border border-slate-200 bg-white\"\n />\n <div className=\"mt-3 flex items-center gap-2\">\n <a href={invoice.url} target=\"_blank\" rel=\"noreferrer\" download={`factuur-${invoice.number}.pdf`}>\n <Button size=\"sm\" variant=\"secondary\">Downloaden</Button>\n </a>\n <a href={invoice.url} target=\"_blank\" rel=\"noreferrer\" className=\"text-sm text-brand-700 underline\">\n Openen in nieuw tabblad ↗\n </a>\n <DialogClose asChild>\n <Button size=\"sm\" variant=\"ghost\" className=\"ml-auto\">Sluiten</Button>\n </DialogClose>\n </div>\n </DialogContent>\n )}\n </Dialog>\n </div>\n );\n}\n"],"mappings":"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","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-2{bottom:calc(var(--spacing) * -2)!important}.bottom-0{bottom:0!important}.-left-2{left:calc(var(--spacing) * -2)!important}.left-1\/2{left:50%!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-4{height:calc(var(--spacing) * 4)!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-12{height:calc(var(--spacing) * 12)!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-4{width:calc(var(--spacing) * 4)!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-11{width:calc(var(--spacing) * 11)!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-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-40{max-width:calc(var(--spacing) * 40)!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-crosshair{cursor:crosshair!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-\[1fr_1fr_90px_90px_auto\]{grid-template-columns:1fr 1fr 90px 90px auto!important}.grid-cols-\[1fr_150px_70px_auto\]{grid-template-columns:1fr 150px 70px 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-2{border-style:var(--tw-border-style)!important;border-width:2px!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}.border-white{border-color:var(--color-white)!important}.bg-amber-50{background-color:var(--color-amber-50)!important}.bg-amber-50\/40{background-color:#fffbeb66!important}@supports (color:color-mix(in lab, red, red)){.bg-amber-50\/40{background-color:color-mix(in oklab, var(--color-amber-50) 40%, transparent)!important}}.bg-brand-50{background-color:var(--color-brand-50)!important}.bg-brand-500{background-color:var(--color-brand-500)!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-transparent{background-color:#0000!important}.bg-white{background-color:var(--color-white)!important}.object-contain{object-fit:contain!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}.pb-1{padding-bottom:var(--spacing)!important}.pb-2{padding-bottom: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{--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}.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}.ring-2{--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}.ring-brand-500{--tw-ring-color:var(--color-brand-500)!important}.outline{outline-style:var(--tw-outline-style)!important;outline-width:1px!important}.outline-2{outline-style:var(--tw-outline-style)!important;outline-width:2px!important}.outline-offset-4{outline-offset:4px!important}.outline-brand-500{outline-color:var(--color-brand-500)!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-dashed{--tw-outline-style:dashed!important;outline-style:dashed!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\:block{display:block!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-\[1fr_auto_1fr\]{grid-template-columns:1fr auto 1fr!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\:items-end{align-items:flex-end!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-3{grid-template-columns:repeat(3,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-2{bottom:calc(var(--spacing) * -2)!important}.bottom-0{bottom:0!important}.-left-2{left:calc(var(--spacing) * -2)!important}.left-1\/2{left:50%!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-4{height:calc(var(--spacing) * 4)!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-12{height:calc(var(--spacing) * 12)!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-4{width:calc(var(--spacing) * 4)!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-11{width:calc(var(--spacing) * 11)!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-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-40{max-width:calc(var(--spacing) * 40)!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-crosshair{cursor:crosshair!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-\[1fr_1fr_90px_90px_auto\]{grid-template-columns:1fr 1fr 90px 90px auto!important}.grid-cols-\[1fr_150px_70px_auto\]{grid-template-columns:1fr 150px 70px 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-2{border-style:var(--tw-border-style)!important;border-width:2px!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}.border-white{border-color:var(--color-white)!important}.bg-amber-50{background-color:var(--color-amber-50)!important}.bg-amber-50\/40{background-color:#fffbeb66!important}@supports (color:color-mix(in lab, red, red)){.bg-amber-50\/40{background-color:color-mix(in oklab, var(--color-amber-50) 40%, transparent)!important}}.bg-brand-50{background-color:var(--color-brand-50)!important}.bg-brand-500{background-color:var(--color-brand-500)!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-transparent{background-color:#0000!important}.bg-white{background-color:var(--color-white)!important}.object-contain{object-fit:contain!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}.pb-1{padding-bottom:var(--spacing)!important}.pb-2{padding-bottom: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{--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}.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}.ring-2{--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}.ring-brand-500{--tw-ring-color:var(--color-brand-500)!important}.outline{outline-style:var(--tw-outline-style)!important;outline-width:1px!important}.outline-2{outline-style:var(--tw-outline-style)!important;outline-width:2px!important}.outline-offset-4{outline-offset:4px!important}.outline-brand-500{outline-color:var(--color-brand-500)!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-dashed{--tw-outline-style:dashed!important;outline-style:dashed!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\:block{display:block!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-\[1fr_auto_1fr\]{grid-template-columns:1fr auto 1fr!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\:items-end{align-items:flex-end!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-3{grid-template-columns:repeat(3,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{-webkit-overflow-scrolling:touch;flex-direction:row;flex:100%;min-width:0;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}
@@ -51,6 +51,18 @@ export declare const MAIL_TEMPLATE_DEFS: readonly [{
51
51
  readonly hint: "Naar de klant bij annulering.";
52
52
  readonly module: "shop";
53
53
  readonly placeholders: "{{naam}} {{ordernummer}} {{sitenaam}}";
54
+ }, {
55
+ readonly key: "shopkeeperOrderCancelled";
56
+ readonly label: "Bestelling geannuleerd (naar jou)";
57
+ readonly hint: "Naar jou bij elke annulering — ook automatisch (bv. een verlopen betaling).";
58
+ readonly module: "shop";
59
+ readonly placeholders: "{{sitenaam}} {{ordernummer}} {{totaal}} {{klant}} {{regels}}";
60
+ }, {
61
+ readonly key: "paymentRequest";
62
+ readonly label: "Betaalverzoek";
63
+ readonly hint: "Naar de klant als je vanuit een openstaande bestelling een betaallink verstuurt.";
64
+ readonly module: "shop";
65
+ readonly placeholders: "{{naam}} {{ordernummer}} {{betaallink}} {{regels}} {{totaal}} {{verzendadres}} {{sitenaam}}";
54
66
  }, {
55
67
  readonly key: "orderRefunded";
56
68
  readonly label: "Terugbetaald";
@@ -103,9 +115,17 @@ export declare const MailTemplatesSchema: z.ZodMiniObject<{
103
115
  subject: z.ZodMiniOptional<z.ZodMiniString<string>>;
104
116
  bodyText: z.ZodMiniOptional<z.ZodMiniString<string>>;
105
117
  }, z.core.$strip>>;
118
+ paymentRequest: z.ZodMiniOptional<z.ZodMiniObject<{
119
+ subject: z.ZodMiniOptional<z.ZodMiniString<string>>;
120
+ bodyText: z.ZodMiniOptional<z.ZodMiniString<string>>;
121
+ }, z.core.$strip>>;
106
122
  shopkeeperNotification: z.ZodMiniOptional<z.ZodMiniObject<{
107
123
  subject: z.ZodMiniOptional<z.ZodMiniString<string>>;
108
124
  bodyText: z.ZodMiniOptional<z.ZodMiniString<string>>;
109
125
  }, z.core.$strip>>;
126
+ shopkeeperOrderCancelled: z.ZodMiniOptional<z.ZodMiniObject<{
127
+ subject: z.ZodMiniOptional<z.ZodMiniString<string>>;
128
+ bodyText: z.ZodMiniOptional<z.ZodMiniString<string>>;
129
+ }, z.core.$strip>>;
110
130
  }, z.core.$strip>;
111
131
  export type MailTemplates = z.infer<typeof MailTemplatesSchema>;
@@ -42,6 +42,20 @@ const MAIL_TEMPLATE_DEFS = [
42
42
  module: "shop",
43
43
  placeholders: "{{naam}} {{ordernummer}} {{sitenaam}}"
44
44
  },
45
+ {
46
+ key: "shopkeeperOrderCancelled",
47
+ label: "Bestelling geannuleerd (naar jou)",
48
+ hint: "Naar jou bij elke annulering \u2014 ook automatisch (bv. een verlopen betaling).",
49
+ module: "shop",
50
+ placeholders: "{{sitenaam}} {{ordernummer}} {{totaal}} {{klant}} {{regels}}"
51
+ },
52
+ {
53
+ key: "paymentRequest",
54
+ label: "Betaalverzoek",
55
+ hint: "Naar de klant als je vanuit een openstaande bestelling een betaallink verstuurt.",
56
+ module: "shop",
57
+ placeholders: "{{naam}} {{ordernummer}} {{betaallink}} {{regels}} {{totaal}} {{verzendadres}} {{sitenaam}}"
58
+ },
45
59
  {
46
60
  key: "orderRefunded",
47
61
  label: "Terugbetaald",
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/schema/mail.ts"],"sourcesContent":["import * as z from \"zod/mini\";\n\n/**\n * E-mailtemplates (site-breed, 3.7) — `sites/{siteId}/settings/mailTemplates`. Per template een\n * onderwerp + platte-tekst-body met `{{placeholders}}`. Bewust géén HTML van beheerders; de\n * huisstijl-opmaak komt in de mail-pipeline (functions `mailLayout.ts`) om de tekst heen.\n *\n * De keys zijn getagd met de module waartoe ze horen (`core`/`shop`), zodat de editor in /beheer\n * shop-templates alleen toont op sites met de webwinkel. `core`-keys (formulieren) gelden altijd.\n */\n\nexport interface MailTemplateDef {\n key: string;\n label: string;\n hint: string;\n module: \"core\" | \"shop\";\n /** Beschikbare invulvelden, als tonende hint in de editor. */\n placeholders: string;\n}\n\nexport const MAIL_TEMPLATE_DEFS = [\n {\n key: \"formNotification\",\n label: \"Formulier-notificatie (naar jou)\",\n hint: \"Naar jou zodra iemand een formulier invult.\",\n module: \"core\",\n placeholders: \"{{sitenaam}} {{formulier}} {{regels}}\",\n },\n {\n key: \"formConfirmation\",\n label: \"Formulier-bevestiging (naar de inzender)\",\n hint: \"Ontvangstbevestiging naar wie het formulier invulde.\",\n module: \"core\",\n placeholders: \"{{sitenaam}} {{formulier}} {{regels}}\",\n },\n {\n key: \"orderConfirmation\",\n label: \"Bestelbevestiging\",\n hint: \"Naar de klant zodra de betaling binnen is — met factuur-PDF als bijlage.\",\n module: \"shop\",\n placeholders: \"{{naam}} {{ordernummer}} {{regels}} {{totaal}} {{verzendadres}} {{notitie}} {{sitenaam}}\",\n },\n {\n key: \"shopkeeperNotification\",\n label: \"Nieuwe-bestelling-notificatie (naar jou)\",\n hint: \"Naar jou bij elke nieuwe bestelling.\",\n module: \"shop\",\n placeholders: \"{{sitenaam}} {{ordernummer}} {{totaal}} {{klant}} {{regels}} {{verzendadres}} {{notitie}}\",\n },\n {\n key: \"orderShipped\",\n label: \"Bestelling verzonden\",\n hint: \"Naar de klant wanneer je de status op ‘verzonden’ zet.\",\n module: \"shop\",\n placeholders: \"{{naam}} {{ordernummer}} {{regels}} {{trackingUrl}} {{verzendadres}} {{sitenaam}}\",\n },\n {\n key: \"orderCancelled\",\n label: \"Bestelling geannuleerd\",\n hint: \"Naar de klant bij annulering.\",\n module: \"shop\",\n placeholders: \"{{naam}} {{ordernummer}} {{sitenaam}}\",\n },\n {\n key: \"orderRefunded\",\n label: \"Terugbetaald\",\n hint: \"Naar de klant na een terugbetaling.\",\n module: \"shop\",\n placeholders: \"{{naam}} {{ordernummer}} {{totaal}} {{sitenaam}}\",\n },\n {\n key: \"donationThanks\",\n label: \"Donatie-bedankje\",\n hint: \"Naar de gever na een donatie.\",\n module: \"shop\",\n placeholders: \"{{naam}} {{regels}} {{sitenaam}}\",\n },\n] as const satisfies readonly MailTemplateDef[];\n\nexport const MAIL_TEMPLATE_KEYS = MAIL_TEMPLATE_DEFS.map((d) => d.key) as unknown as readonly MailTemplateKey[];\nexport type MailTemplateKey = (typeof MAIL_TEMPLATE_DEFS)[number][\"key\"];\n\n/** Formulier-mails — bewerkbaar in het Formulieren-gedeelte. */\nexport const FORM_TEMPLATE_KEYS = MAIL_TEMPLATE_DEFS.filter((d) => d.module === \"core\").map((d) => d.key) as readonly MailTemplateKey[];\n/** Shop-mails — bewerkbaar in het Webwinkel-gedeelte. */\nexport const SHOP_TEMPLATE_KEYS = MAIL_TEMPLATE_DEFS.filter((d) => d.module === \"shop\").map((d) => d.key) as readonly MailTemplateKey[];\n\nexport const MailTemplateSchema = z.object({\n subject: z.optional(z.string().check(z.maxLength(200))),\n bodyText: z.optional(z.string().check(z.maxLength(10_000))),\n});\nexport const MailTemplatesSchema = z.object(\n Object.fromEntries(MAIL_TEMPLATE_DEFS.map((d) => [d.key, z.optional(MailTemplateSchema)])) as Record<\n MailTemplateKey,\n z.ZodMiniOptional<typeof MailTemplateSchema>\n >,\n);\nexport type MailTemplates = z.infer<typeof MailTemplatesSchema>;\n"],"mappings":"AAAA,YAAY,OAAO;AAoBZ,MAAM,qBAAqB;AAAA,EAChC;AAAA,IACE,KAAK;AAAA,IACL,OAAO;AAAA,IACP,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,cAAc;AAAA,EAChB;AAAA,EACA;AAAA,IACE,KAAK;AAAA,IACL,OAAO;AAAA,IACP,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,cAAc;AAAA,EAChB;AAAA,EACA;AAAA,IACE,KAAK;AAAA,IACL,OAAO;AAAA,IACP,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,cAAc;AAAA,EAChB;AAAA,EACA;AAAA,IACE,KAAK;AAAA,IACL,OAAO;AAAA,IACP,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,cAAc;AAAA,EAChB;AAAA,EACA;AAAA,IACE,KAAK;AAAA,IACL,OAAO;AAAA,IACP,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,cAAc;AAAA,EAChB;AAAA,EACA;AAAA,IACE,KAAK;AAAA,IACL,OAAO;AAAA,IACP,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,cAAc;AAAA,EAChB;AAAA,EACA;AAAA,IACE,KAAK;AAAA,IACL,OAAO;AAAA,IACP,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,cAAc;AAAA,EAChB;AAAA,EACA;AAAA,IACE,KAAK;AAAA,IACL,OAAO;AAAA,IACP,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,cAAc;AAAA,EAChB;AACF;AAEO,MAAM,qBAAqB,mBAAmB,IAAI,CAAC,MAAM,EAAE,GAAG;AAI9D,MAAM,qBAAqB,mBAAmB,OAAO,CAAC,MAAM,EAAE,WAAW,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,GAAG;AAEjG,MAAM,qBAAqB,mBAAmB,OAAO,CAAC,MAAM,EAAE,WAAW,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,GAAG;AAEjG,MAAM,qBAAqB,EAAE,OAAO;AAAA,EACzC,SAAS,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,GAAG,CAAC,CAAC;AAAA,EACtD,UAAU,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,GAAM,CAAC,CAAC;AAC5D,CAAC;AACM,MAAM,sBAAsB,EAAE;AAAA,EACnC,OAAO,YAAY,mBAAmB,IAAI,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,SAAS,kBAAkB,CAAC,CAAC,CAAC;AAI3F;","names":[]}
1
+ {"version":3,"sources":["../../src/schema/mail.ts"],"sourcesContent":["import * as z from \"zod/mini\";\n\n/**\n * E-mailtemplates (site-breed, 3.7) — `sites/{siteId}/settings/mailTemplates`. Per template een\n * onderwerp + platte-tekst-body met `{{placeholders}}`. Bewust géén HTML van beheerders; de\n * huisstijl-opmaak komt in de mail-pipeline (functions `mailLayout.ts`) om de tekst heen.\n *\n * De keys zijn getagd met de module waartoe ze horen (`core`/`shop`), zodat de editor in /beheer\n * shop-templates alleen toont op sites met de webwinkel. `core`-keys (formulieren) gelden altijd.\n */\n\nexport interface MailTemplateDef {\n key: string;\n label: string;\n hint: string;\n module: \"core\" | \"shop\";\n /** Beschikbare invulvelden, als tonende hint in de editor. */\n placeholders: string;\n}\n\nexport const MAIL_TEMPLATE_DEFS = [\n {\n key: \"formNotification\",\n label: \"Formulier-notificatie (naar jou)\",\n hint: \"Naar jou zodra iemand een formulier invult.\",\n module: \"core\",\n placeholders: \"{{sitenaam}} {{formulier}} {{regels}}\",\n },\n {\n key: \"formConfirmation\",\n label: \"Formulier-bevestiging (naar de inzender)\",\n hint: \"Ontvangstbevestiging naar wie het formulier invulde.\",\n module: \"core\",\n placeholders: \"{{sitenaam}} {{formulier}} {{regels}}\",\n },\n {\n key: \"orderConfirmation\",\n label: \"Bestelbevestiging\",\n hint: \"Naar de klant zodra de betaling binnen is — met factuur-PDF als bijlage.\",\n module: \"shop\",\n placeholders: \"{{naam}} {{ordernummer}} {{regels}} {{totaal}} {{verzendadres}} {{notitie}} {{sitenaam}}\",\n },\n {\n key: \"shopkeeperNotification\",\n label: \"Nieuwe-bestelling-notificatie (naar jou)\",\n hint: \"Naar jou bij elke nieuwe bestelling.\",\n module: \"shop\",\n placeholders: \"{{sitenaam}} {{ordernummer}} {{totaal}} {{klant}} {{regels}} {{verzendadres}} {{notitie}}\",\n },\n {\n key: \"orderShipped\",\n label: \"Bestelling verzonden\",\n hint: \"Naar de klant wanneer je de status op ‘verzonden’ zet.\",\n module: \"shop\",\n placeholders: \"{{naam}} {{ordernummer}} {{regels}} {{trackingUrl}} {{verzendadres}} {{sitenaam}}\",\n },\n {\n key: \"orderCancelled\",\n label: \"Bestelling geannuleerd\",\n hint: \"Naar de klant bij annulering.\",\n module: \"shop\",\n placeholders: \"{{naam}} {{ordernummer}} {{sitenaam}}\",\n },\n {\n key: \"shopkeeperOrderCancelled\",\n label: \"Bestelling geannuleerd (naar jou)\",\n hint: \"Naar jou bij elke annulering — ook automatisch (bv. een verlopen betaling).\",\n module: \"shop\",\n placeholders: \"{{sitenaam}} {{ordernummer}} {{totaal}} {{klant}} {{regels}}\",\n },\n {\n key: \"paymentRequest\",\n label: \"Betaalverzoek\",\n hint: \"Naar de klant als je vanuit een openstaande bestelling een betaallink verstuurt.\",\n module: \"shop\",\n placeholders: \"{{naam}} {{ordernummer}} {{betaallink}} {{regels}} {{totaal}} {{verzendadres}} {{sitenaam}}\",\n },\n {\n key: \"orderRefunded\",\n label: \"Terugbetaald\",\n hint: \"Naar de klant na een terugbetaling.\",\n module: \"shop\",\n placeholders: \"{{naam}} {{ordernummer}} {{totaal}} {{sitenaam}}\",\n },\n {\n key: \"donationThanks\",\n label: \"Donatie-bedankje\",\n hint: \"Naar de gever na een donatie.\",\n module: \"shop\",\n placeholders: \"{{naam}} {{regels}} {{sitenaam}}\",\n },\n] as const satisfies readonly MailTemplateDef[];\n\nexport const MAIL_TEMPLATE_KEYS = MAIL_TEMPLATE_DEFS.map((d) => d.key) as unknown as readonly MailTemplateKey[];\nexport type MailTemplateKey = (typeof MAIL_TEMPLATE_DEFS)[number][\"key\"];\n\n/** Formulier-mails — bewerkbaar in het Formulieren-gedeelte. */\nexport const FORM_TEMPLATE_KEYS = MAIL_TEMPLATE_DEFS.filter((d) => d.module === \"core\").map((d) => d.key) as readonly MailTemplateKey[];\n/** Shop-mails — bewerkbaar in het Webwinkel-gedeelte. */\nexport const SHOP_TEMPLATE_KEYS = MAIL_TEMPLATE_DEFS.filter((d) => d.module === \"shop\").map((d) => d.key) as readonly MailTemplateKey[];\n\nexport const MailTemplateSchema = z.object({\n subject: z.optional(z.string().check(z.maxLength(200))),\n bodyText: z.optional(z.string().check(z.maxLength(10_000))),\n});\nexport const MailTemplatesSchema = z.object(\n Object.fromEntries(MAIL_TEMPLATE_DEFS.map((d) => [d.key, z.optional(MailTemplateSchema)])) as Record<\n MailTemplateKey,\n z.ZodMiniOptional<typeof MailTemplateSchema>\n >,\n);\nexport type MailTemplates = z.infer<typeof MailTemplatesSchema>;\n"],"mappings":"AAAA,YAAY,OAAO;AAoBZ,MAAM,qBAAqB;AAAA,EAChC;AAAA,IACE,KAAK;AAAA,IACL,OAAO;AAAA,IACP,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,cAAc;AAAA,EAChB;AAAA,EACA;AAAA,IACE,KAAK;AAAA,IACL,OAAO;AAAA,IACP,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,cAAc;AAAA,EAChB;AAAA,EACA;AAAA,IACE,KAAK;AAAA,IACL,OAAO;AAAA,IACP,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,cAAc;AAAA,EAChB;AAAA,EACA;AAAA,IACE,KAAK;AAAA,IACL,OAAO;AAAA,IACP,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,cAAc;AAAA,EAChB;AAAA,EACA;AAAA,IACE,KAAK;AAAA,IACL,OAAO;AAAA,IACP,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,cAAc;AAAA,EAChB;AAAA,EACA;AAAA,IACE,KAAK;AAAA,IACL,OAAO;AAAA,IACP,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,cAAc;AAAA,EAChB;AAAA,EACA;AAAA,IACE,KAAK;AAAA,IACL,OAAO;AAAA,IACP,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,cAAc;AAAA,EAChB;AAAA,EACA;AAAA,IACE,KAAK;AAAA,IACL,OAAO;AAAA,IACP,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,cAAc;AAAA,EAChB;AAAA,EACA;AAAA,IACE,KAAK;AAAA,IACL,OAAO;AAAA,IACP,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,cAAc;AAAA,EAChB;AAAA,EACA;AAAA,IACE,KAAK;AAAA,IACL,OAAO;AAAA,IACP,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,cAAc;AAAA,EAChB;AACF;AAEO,MAAM,qBAAqB,mBAAmB,IAAI,CAAC,MAAM,EAAE,GAAG;AAI9D,MAAM,qBAAqB,mBAAmB,OAAO,CAAC,MAAM,EAAE,WAAW,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,GAAG;AAEjG,MAAM,qBAAqB,mBAAmB,OAAO,CAAC,MAAM,EAAE,WAAW,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,GAAG;AAEjG,MAAM,qBAAqB,EAAE,OAAO;AAAA,EACzC,SAAS,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,GAAG,CAAC,CAAC;AAAA,EACtD,UAAU,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,GAAM,CAAC,CAAC;AAC5D,CAAC;AACM,MAAM,sBAAsB,EAAE;AAAA,EACnC,OAAO,YAAY,mBAAmB,IAAI,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,SAAS,kBAAkB,CAAC,CAAC,CAAC;AAI3F;","names":[]}
@@ -266,6 +266,8 @@ export declare const OrderSchema: z.ZodMiniObject<{
266
266
  by: z.ZodMiniOptional<z.ZodMiniString<string>>;
267
267
  subject: z.ZodMiniString<string>;
268
268
  body: z.ZodMiniString<string>;
269
+ automatisch: z.ZodMiniOptional<z.ZodMiniBoolean<boolean>>;
270
+ mislukt: z.ZodMiniOptional<z.ZodMiniBoolean<boolean>>;
269
271
  }, z.core.$strip>>>;
270
272
  trackingUrl: z.ZodMiniOptional<z.ZodMiniString<string>>;
271
273
  invoicePath: z.ZodMiniOptional<z.ZodMiniString<string>>;
@@ -223,12 +223,22 @@ const OrderSchema = z.object({
223
223
  manual: z.optional(z.boolean()),
224
224
  /** 7.18: som van alle (gedeeltelijke) terugbetalingen in centen. */
225
225
  refundedCents: z.optional(z.int().check(z.minimum(0))),
226
- /** 7.18: berichten die de winkelier vanuit /beheer aan de klant stuurde. */
226
+ /**
227
+ * 7.18: berichten die naar de klant zijn gestuurd — handmatig vanuit /beheer, en sinds 7.25
228
+ * ook automatisch (bevestiging, verzonden, geannuleerd, terugbetaald, betaalverzoek). Zonder
229
+ * dat spoor was er geen manier om te zien of een klant-mail is aangekomen of dat de order
230
+ * stilletjes zonder bericht is geannuleerd.
231
+ */
227
232
  messages: z.optional(z.array(z.object({
228
233
  at: z.string().check(z.maxLength(40)),
234
+ /** uid van de medewerker, of "systeem" bij een automatische mail. */
229
235
  by: z.optional(z.string().check(z.maxLength(200))),
230
236
  subject: z.string().check(z.maxLength(200)),
231
- body: z.string().check(z.maxLength(2e3))
237
+ body: z.string().check(z.maxLength(2e3)),
238
+ /** 7.25: automatisch verstuurd (order-gebeurtenis) i.p.v. handmatig getypt. */
239
+ automatisch: z.optional(z.boolean()),
240
+ /** 7.25: het versturen is mislukt — blijft zichtbaar i.p.v. stil te verdwijnen. */
241
+ mislukt: z.optional(z.boolean())
232
242
  })).check(z.maxLength(50))),
233
243
  /** 5.6: track & trace-link (gezet bij "verzonden"). */
234
244
  trackingUrl: z.optional(z.string().check(z.maxLength(500))),
@@ -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 — `{ \"Roze\": \"https://…\" }`. De detailpagina wisselt het\n * hoofdbeeld mee met de keuze; bewust per as en niet per variant-combinatie (maat × kleur zou\n * het aantal afbeeldingen laten exploderen).\n */\n images: z.optional(z.record(z.string(), z.string().check(z.maxLength(2000)))),\n /**\n * 7.23: heeft élke waarde van deze as een eigen foto?\n *\n * Tot nu toe was dat geen keuze maar een naamtruc: alleen een as die \"Kleur\"/\"Color\"/\"Colour\"\n * heette kreeg fotoknoppen. Noemde je hem \"Print\", \"Dessin\" of \"Uitvoering\", dan kon je er geen\n * foto's aan hangen — en hernoemde je een bestaande kleur-as, dan verdwenen de knoppen terwijl\n * de foto's onzichtbaar in het document achterbleven. Nu is het een vinkje in het\n * productformulier en staat de naam er los van.\n *\n * Drie toestanden, met opzet: `true` = aan, `false` = uit, en **afwezig** = \"nooit ingesteld\",\n * en dan valt `beeldOptie()` terug op de oude naamcheck. Zo blijven alle bestaande producten\n * werken zonder migratie, terwijl één klik de nieuwe regel vastlegt.\n *\n * Eén as tegelijk: het hoofdbeeld kan maar één keuze volgen. Het formulier zet de vlag daarom\n * bij de andere assen uit zodra je hem hier aanzet.\n */\n imagePerValue: z.optional(z.boolean()),\n});\nexport type ProductOption = z.infer<typeof ProductOptionSchema>;\n\n/** Heeft deze as een eigen foto per waarde? Zonder expliciete keuze: de oude kleur-naamregel. */\nexport function optieHeeftBeeldPerWaarde(optie: Pick<ProductOption, \"name\" | \"imagePerValue\">): boolean {\n return optie.imagePerValue ?? /^kleur|^colou?r/i.test(optie.name);\n}\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 /**\n * Staat \"persoonlijk maken\" aan voor dit product? (7.19)\n *\n * Voorheen was dit impliciet: velden aanwezig = aan. Wie het tijdelijk wilde uitzetten (te\n * druk, vakantie, leverancier levert de bedrukking niet) moest de velden VERWIJDEREN en dus\n * ook zijn labels, meerprijzen en uitleg kwijtraken. Nu blijft de configuratie staan en gaat\n * alleen de functie uit. Default true zodat bestaande producten met velden niets merken.\n */\n personalizationEnabled: z._default(z.boolean(), true),\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 /**\n * 7.22: de categorie waarin dit product verkocht is.\n *\n * Bij een winkel als Christelijk Kraamcadeau is `name` alleen het ontwerp (\"Blij blij\") en zegt\n * de categorie wát het is: rompertje, slabbetje, korte- of lange-mouw t-shirt. Hetzelfde ontwerp\n * bestaat in vijf categorieën, dus zonder dit veld staat er vier keer een identieke regel op de\n * pakbon, de factuur en in de bevestigingsmail — en weet niemand wat er de deur uit moet.\n *\n * Meegeschreven als momentopname, net als `name` en `unitPriceCents`: hernoem je later een\n * categorie, dan blijft een oude bestelling zeggen wat er destijds verkocht is.\n */\n categoryName: z.optional(z.string().check(z.maxLength(120))),\n});\nexport type OrderItem = z.infer<typeof OrderItemSchema>;\n\n/** \"Blij blij — Slabbetje\": de productnaam plus de categorie waarin hij verkocht is. */\nexport function orderRegelNaam(item: { name: string; categoryName?: string }): string {\n return item.categoryName ? `${item.name} — ${item.categoryName}` : item.name;\n}\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 * 7.18: btw-bedrag in de order zelf. Werd eerder alleen op de factuur en in de CSV-export\n * afgeleid — twee losse kopieën van dezelfde som, terwijl dit voor de aangifte hét getal is.\n */\n vatCents: z.optional(z.int().check(z.minimum(0))),\n /** Btw per tarief (\"21\" → centen), voor de aangifte per categorie. */\n vatByRate: z.optional(z.record(z.string(), z.int())),\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: doorlopend factuurnummer (eigen reeks, zonder gaten) — pas gezet bij betaling. */\n invoiceNumber: z.optional(z.string().check(z.maxLength(40))),\n /** 7.18: handmatig aangemaakt in /beheer (telefonisch, markt, overschrijving). */\n manual: z.optional(z.boolean()),\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":"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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 — `{ \"Roze\": \"https://…\" }`. De detailpagina wisselt het\n * hoofdbeeld mee met de keuze; bewust per as en niet per variant-combinatie (maat × kleur zou\n * het aantal afbeeldingen laten exploderen).\n */\n images: z.optional(z.record(z.string(), z.string().check(z.maxLength(2000)))),\n /**\n * 7.23: heeft élke waarde van deze as een eigen foto?\n *\n * Tot nu toe was dat geen keuze maar een naamtruc: alleen een as die \"Kleur\"/\"Color\"/\"Colour\"\n * heette kreeg fotoknoppen. Noemde je hem \"Print\", \"Dessin\" of \"Uitvoering\", dan kon je er geen\n * foto's aan hangen — en hernoemde je een bestaande kleur-as, dan verdwenen de knoppen terwijl\n * de foto's onzichtbaar in het document achterbleven. Nu is het een vinkje in het\n * productformulier en staat de naam er los van.\n *\n * Drie toestanden, met opzet: `true` = aan, `false` = uit, en **afwezig** = \"nooit ingesteld\",\n * en dan valt `beeldOptie()` terug op de oude naamcheck. Zo blijven alle bestaande producten\n * werken zonder migratie, terwijl één klik de nieuwe regel vastlegt.\n *\n * Eén as tegelijk: het hoofdbeeld kan maar één keuze volgen. Het formulier zet de vlag daarom\n * bij de andere assen uit zodra je hem hier aanzet.\n */\n imagePerValue: z.optional(z.boolean()),\n});\nexport type ProductOption = z.infer<typeof ProductOptionSchema>;\n\n/** Heeft deze as een eigen foto per waarde? Zonder expliciete keuze: de oude kleur-naamregel. */\nexport function optieHeeftBeeldPerWaarde(optie: Pick<ProductOption, \"name\" | \"imagePerValue\">): boolean {\n return optie.imagePerValue ?? /^kleur|^colou?r/i.test(optie.name);\n}\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 /**\n * Staat \"persoonlijk maken\" aan voor dit product? (7.19)\n *\n * Voorheen was dit impliciet: velden aanwezig = aan. Wie het tijdelijk wilde uitzetten (te\n * druk, vakantie, leverancier levert de bedrukking niet) moest de velden VERWIJDEREN en dus\n * ook zijn labels, meerprijzen en uitleg kwijtraken. Nu blijft de configuratie staan en gaat\n * alleen de functie uit. Default true zodat bestaande producten met velden niets merken.\n */\n personalizationEnabled: z._default(z.boolean(), true),\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 /**\n * 7.22: de categorie waarin dit product verkocht is.\n *\n * Bij een winkel als Christelijk Kraamcadeau is `name` alleen het ontwerp (\"Blij blij\") en zegt\n * de categorie wát het is: rompertje, slabbetje, korte- of lange-mouw t-shirt. Hetzelfde ontwerp\n * bestaat in vijf categorieën, dus zonder dit veld staat er vier keer een identieke regel op de\n * pakbon, de factuur en in de bevestigingsmail — en weet niemand wat er de deur uit moet.\n *\n * Meegeschreven als momentopname, net als `name` en `unitPriceCents`: hernoem je later een\n * categorie, dan blijft een oude bestelling zeggen wat er destijds verkocht is.\n */\n categoryName: z.optional(z.string().check(z.maxLength(120))),\n});\nexport type OrderItem = z.infer<typeof OrderItemSchema>;\n\n/** \"Blij blij — Slabbetje\": de productnaam plus de categorie waarin hij verkocht is. */\nexport function orderRegelNaam(item: { name: string; categoryName?: string }): string {\n return item.categoryName ? `${item.name} — ${item.categoryName}` : item.name;\n}\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 * 7.18: btw-bedrag in de order zelf. Werd eerder alleen op de factuur en in de CSV-export\n * afgeleid — twee losse kopieën van dezelfde som, terwijl dit voor de aangifte hét getal is.\n */\n vatCents: z.optional(z.int().check(z.minimum(0))),\n /** Btw per tarief (\"21\" → centen), voor de aangifte per categorie. */\n vatByRate: z.optional(z.record(z.string(), z.int())),\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: doorlopend factuurnummer (eigen reeks, zonder gaten) — pas gezet bij betaling. */\n invoiceNumber: z.optional(z.string().check(z.maxLength(40))),\n /** 7.18: handmatig aangemaakt in /beheer (telefonisch, markt, overschrijving). */\n manual: z.optional(z.boolean()),\n /** 7.18: som van alle (gedeeltelijke) terugbetalingen in centen. */\n refundedCents: z.optional(z.int().check(z.minimum(0))),\n /**\n * 7.18: berichten die naar de klant zijn gestuurd — handmatig vanuit /beheer, en sinds 7.25\n * ook automatisch (bevestiging, verzonden, geannuleerd, terugbetaald, betaalverzoek). Zonder\n * dat spoor was er geen manier om te zien of een klant-mail is aangekomen of dat de order\n * stilletjes zonder bericht is geannuleerd.\n */\n messages: z.optional(z.array(z.object({\n at: z.string().check(z.maxLength(40)),\n /** uid van de medewerker, of \"systeem\" bij een automatische mail. */\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 /** 7.25: automatisch verstuurd (order-gebeurtenis) i.p.v. handmatig getypt. */\n automatisch: z.optional(z.boolean()),\n /** 7.25: het versturen is mislukt — blijft zichtbaar i.p.v. stil te verdwijnen. */\n mislukt: z.optional(z.boolean()),\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":"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","names":[]}
package/dist/version.js CHANGED
@@ -1,4 +1,4 @@
1
- const CORE_VERSION = true ? "7.24.0" : "0.0.0-dev";
1
+ const CORE_VERSION = true ? "7.25.0" : "0.0.0-dev";
2
2
  const CORE_PACKAGE = "@xiwebdesign/xicms-core";
3
3
  function isNieuwer(beschikbaar, huidig) {
4
4
  const delen = (v) => v.split("-")[0].split(".").map((n) => Number.parseInt(n, 10) || 0);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xiwebdesign/xicms-core",
3
- "version": "7.24.0",
3
+ "version": "7.25.0",
4
4
  "description": "XiCMS engine: gestructureerde content, pagina's, draft/publish, admin (TipTap+Radix), SEO en beveiliging voor klantsites.",
5
5
  "license": "UNLICENSED",
6
6
  "repository": {