@flowselections/layout-maker 1.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (145) hide show
  1. package/README.md +24 -0
  2. package/dist-lib/_core-safelist.d.ts +2 -0
  3. package/dist-lib/_core-safelist.d.ts.map +1 -0
  4. package/dist-lib/_core-safelist.js +18 -0
  5. package/dist-lib/components/AfdrukKnop.d.ts +13 -0
  6. package/dist-lib/components/AfdrukKnop.d.ts.map +1 -0
  7. package/dist-lib/components/AfdrukKnop.js +38 -0
  8. package/dist-lib/components/AiLayoutImportDialog.d.ts +6 -0
  9. package/dist-lib/components/AiLayoutImportDialog.d.ts.map +1 -0
  10. package/dist-lib/components/AiLayoutImportDialog.js +133 -0
  11. package/dist-lib/components/BlokOntwerperPage.d.ts +5 -0
  12. package/dist-lib/components/BlokOntwerperPage.d.ts.map +1 -0
  13. package/dist-lib/components/BlokOntwerperPage.js +67 -0
  14. package/dist-lib/components/DocumentSettingsPanel.d.ts +13 -0
  15. package/dist-lib/components/DocumentSettingsPanel.d.ts.map +1 -0
  16. package/dist-lib/components/DocumentSettingsPanel.js +65 -0
  17. package/dist-lib/components/FormaatOrientatieBar.d.ts +10 -0
  18. package/dist-lib/components/FormaatOrientatieBar.d.ts.map +1 -0
  19. package/dist-lib/components/FormaatOrientatieBar.js +51 -0
  20. package/dist-lib/components/LayoutsPage.d.ts +2 -0
  21. package/dist-lib/components/LayoutsPage.d.ts.map +1 -0
  22. package/dist-lib/components/LayoutsPage.js +196 -0
  23. package/dist-lib/components/LogoBibliotheekDialog.d.ts +7 -0
  24. package/dist-lib/components/LogoBibliotheekDialog.d.ts.map +1 -0
  25. package/dist-lib/components/LogoBibliotheekDialog.js +27 -0
  26. package/dist-lib/components/LogosPage.d.ts +7 -0
  27. package/dist-lib/components/LogosPage.d.ts.map +1 -0
  28. package/dist-lib/components/LogosPage.js +131 -0
  29. package/dist-lib/components/PaginaDocument.d.ts +22 -0
  30. package/dist-lib/components/PaginaDocument.d.ts.map +1 -0
  31. package/dist-lib/components/PaginaDocument.js +80 -0
  32. package/dist-lib/components/PakbonBuilderPage.d.ts +5 -0
  33. package/dist-lib/components/PakbonBuilderPage.d.ts.map +1 -0
  34. package/dist-lib/components/PakbonBuilderPage.js +69 -0
  35. package/dist-lib/components/PakbonPreview.d.ts +13 -0
  36. package/dist-lib/components/PakbonPreview.d.ts.map +1 -0
  37. package/dist-lib/components/PakbonPreview.js +423 -0
  38. package/dist-lib/components/PakbonSectiesPanel.d.ts +18 -0
  39. package/dist-lib/components/PakbonSectiesPanel.d.ts.map +1 -0
  40. package/dist-lib/components/PakbonSectiesPanel.js +156 -0
  41. package/dist-lib/components/PreviewFit.d.ts +9 -0
  42. package/dist-lib/components/PreviewFit.d.ts.map +1 -0
  43. package/dist-lib/components/PreviewFit.js +38 -0
  44. package/dist-lib/components/PrintQueuePage.d.ts +2 -0
  45. package/dist-lib/components/PrintQueuePage.d.ts.map +1 -0
  46. package/dist-lib/components/PrintQueuePage.js +59 -0
  47. package/dist-lib/components/PrinterSetupWizard.d.ts +6 -0
  48. package/dist-lib/components/PrinterSetupWizard.d.ts.map +1 -0
  49. package/dist-lib/components/PrinterSetupWizard.js +119 -0
  50. package/dist-lib/components/PrintersPage.d.ts +2 -0
  51. package/dist-lib/components/PrintersPage.d.ts.map +1 -0
  52. package/dist-lib/components/PrintersPage.js +60 -0
  53. package/dist-lib/components/ProductiebonBlokkenPanel.d.ts +7 -0
  54. package/dist-lib/components/ProductiebonBlokkenPanel.d.ts.map +1 -0
  55. package/dist-lib/components/ProductiebonBlokkenPanel.js +85 -0
  56. package/dist-lib/components/ProductiebonBuilderPage.d.ts +5 -0
  57. package/dist-lib/components/ProductiebonBuilderPage.d.ts.map +1 -0
  58. package/dist-lib/components/ProductiebonBuilderPage.js +48 -0
  59. package/dist-lib/components/ProductiebonPreview.d.ts +12 -0
  60. package/dist-lib/components/ProductiebonPreview.d.ts.map +1 -0
  61. package/dist-lib/components/ProductiebonPreview.js +237 -0
  62. package/dist-lib/components/TemplateModuleLogo.d.ts +6 -0
  63. package/dist-lib/components/TemplateModuleLogo.d.ts.map +1 -0
  64. package/dist-lib/components/TemplateModuleLogo.js +4 -0
  65. package/dist-lib/components/settings/MijnModuleSettingsCard.d.ts +2 -0
  66. package/dist-lib/components/settings/MijnModuleSettingsCard.d.ts.map +1 -0
  67. package/dist-lib/components/settings/MijnModuleSettingsCard.js +42 -0
  68. package/dist-lib/hooks/useMijnModuleData.d.ts +13 -0
  69. package/dist-lib/hooks/useMijnModuleData.d.ts.map +1 -0
  70. package/dist-lib/hooks/useMijnModuleData.js +49 -0
  71. package/dist-lib/index.d.ts +38 -0
  72. package/dist-lib/index.d.ts.map +1 -0
  73. package/dist-lib/index.js +56 -0
  74. package/dist-lib/integrations/supabase/auth-attacher.d.ts +2 -0
  75. package/dist-lib/integrations/supabase/auth-attacher.d.ts.map +1 -0
  76. package/dist-lib/integrations/supabase/auth-attacher.js +12 -0
  77. package/dist-lib/integrations/supabase/auth-middleware.d.ts +19847 -0
  78. package/dist-lib/integrations/supabase/auth-middleware.d.ts.map +1 -0
  79. package/dist-lib/integrations/supabase/auth-middleware.js +52 -0
  80. package/dist-lib/integrations/supabase/client.d.ts +2 -0
  81. package/dist-lib/integrations/supabase/client.d.ts.map +1 -0
  82. package/dist-lib/integrations/supabase/client.js +3 -0
  83. package/dist-lib/integrations/supabase/client.server.d.ts +19843 -0
  84. package/dist-lib/integrations/supabase/client.server.d.ts.map +1 -0
  85. package/dist-lib/integrations/supabase/client.server.js +30 -0
  86. package/dist-lib/integrations/supabase/types.d.ts +20423 -0
  87. package/dist-lib/integrations/supabase/types.d.ts.map +1 -0
  88. package/dist-lib/integrations/supabase/types.js +78 -0
  89. package/dist-lib/lib/aiLayout.functions.d.ts +19864 -0
  90. package/dist-lib/lib/aiLayout.functions.d.ts.map +1 -0
  91. package/dist-lib/lib/aiLayout.functions.js +28 -0
  92. package/dist-lib/lib/aiLayout.server.d.ts +14 -0
  93. package/dist-lib/lib/aiLayout.server.d.ts.map +1 -0
  94. package/dist-lib/lib/aiLayout.server.js +197 -0
  95. package/dist-lib/lib/browserPrint.d.ts +38 -0
  96. package/dist-lib/lib/browserPrint.d.ts.map +1 -0
  97. package/dist-lib/lib/browserPrint.js +104 -0
  98. package/dist-lib/lib/documentProfiles.d.ts +50 -0
  99. package/dist-lib/lib/documentProfiles.d.ts.map +1 -0
  100. package/dist-lib/lib/documentProfiles.js +458 -0
  101. package/dist-lib/lib/documentTypes.d.ts +123 -0
  102. package/dist-lib/lib/documentTypes.d.ts.map +1 -0
  103. package/dist-lib/lib/documentTypes.js +74 -0
  104. package/dist-lib/lib/fieldRegistry.d.ts +20 -0
  105. package/dist-lib/lib/fieldRegistry.d.ts.map +1 -0
  106. package/dist-lib/lib/fieldRegistry.js +123 -0
  107. package/dist-lib/lib/formaatPrinters.d.ts +14 -0
  108. package/dist-lib/lib/formaatPrinters.d.ts.map +1 -0
  109. package/dist-lib/lib/formaatPrinters.js +39 -0
  110. package/dist-lib/lib/layoutPreview.d.ts +18 -0
  111. package/dist-lib/lib/layoutPreview.d.ts.map +1 -0
  112. package/dist-lib/lib/layoutPreview.js +134 -0
  113. package/dist-lib/lib/logoLibrary.d.ts +80 -0
  114. package/dist-lib/lib/logoLibrary.d.ts.map +1 -0
  115. package/dist-lib/lib/logoLibrary.js +264 -0
  116. package/dist-lib/lib/pakbonBuilder.d.ts +210 -0
  117. package/dist-lib/lib/pakbonBuilder.d.ts.map +1 -0
  118. package/dist-lib/lib/pakbonBuilder.js +910 -0
  119. package/dist-lib/lib/pakbonHtml.d.ts +48 -0
  120. package/dist-lib/lib/pakbonHtml.d.ts.map +1 -0
  121. package/dist-lib/lib/pakbonHtml.js +754 -0
  122. package/dist-lib/lib/printApi.d.ts +45 -0
  123. package/dist-lib/lib/printApi.d.ts.map +1 -0
  124. package/dist-lib/lib/printApi.js +98 -0
  125. package/dist-lib/lib/productiebonBuilder.d.ts +137 -0
  126. package/dist-lib/lib/productiebonBuilder.d.ts.map +1 -0
  127. package/dist-lib/lib/productiebonBuilder.js +661 -0
  128. package/dist-lib/lib/supabaseClient.d.ts +4 -0
  129. package/dist-lib/lib/supabaseClient.d.ts.map +1 -0
  130. package/dist-lib/lib/supabaseClient.js +10 -0
  131. package/dist-lib/lib/useDocumentData.d.ts +97 -0
  132. package/dist-lib/lib/useDocumentData.d.ts.map +1 -0
  133. package/dist-lib/lib/useDocumentData.js +286 -0
  134. package/dist-lib/lib/useLayoutIdParam.d.ts +7 -0
  135. package/dist-lib/lib/useLayoutIdParam.d.ts.map +1 -0
  136. package/dist-lib/lib/useLayoutIdParam.js +10 -0
  137. package/dist-lib/lib/utils.d.ts +3 -0
  138. package/dist-lib/lib/utils.d.ts.map +1 -0
  139. package/dist-lib/lib/utils.js +5 -0
  140. package/dist-lib/lib/validationSchemas.d.ts +15 -0
  141. package/dist-lib/lib/validationSchemas.d.ts.map +1 -0
  142. package/dist-lib/lib/validationSchemas.js +25 -0
  143. package/dist-lib/styles.css +1 -0
  144. package/package.json +117 -0
  145. package/public/logo.png +0 -0
@@ -0,0 +1,85 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useState } from "react";
3
+ import { Badge, Button, Input, Label, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, Separator, Switch, } from "@flowselections/core";
4
+ import { ChevronDown, ChevronUp, Plus, Trash2 } from "lucide-react";
5
+ import { LETTERTYPEN, MATERIAAL_KOLOMMEN, PB_BLOK_REGISTER, PB_CONDITIE_VELDEN, PRODUCTINFO_VELDEN, SAMENSTELLING_KOLOMMEN, nieuwBlok, pbNieuweKolom, nieuwVeld, pbBlokDef, } from "../lib/productiebonBuilder";
6
+ /** Beheer van de blokken waaruit de productiebon is opgebouwd. */
7
+ export function ProductiebonBlokkenPanel({ config, onChange, }) {
8
+ const blokken = Array.isArray(config?.blokken) ? config.blokken : [];
9
+ const [open, setOpen] = useState(blokken[0]?.id ?? null);
10
+ const [toevoegen, setToevoegen] = useState("productinfo");
11
+ const zet = (nieuw) => onChange({ ...config, blokken: nieuw });
12
+ const update = (id, patch) => zet(blokken.map((b) => (b.id === id ? { ...b, ...patch } : b)));
13
+ const verplaats = (index, richting) => {
14
+ const doel = index + richting;
15
+ if (doel < 0 || doel >= blokken.length)
16
+ return;
17
+ const kopie = [...blokken];
18
+ [kopie[index], kopie[doel]] = [kopie[doel], kopie[index]];
19
+ zet(kopie);
20
+ };
21
+ return (_jsxs("div", { className: "space-y-3", children: [_jsxs("div", { className: "flex gap-2", children: [_jsxs(Select, { value: toevoegen, onValueChange: (v) => setToevoegen(v), children: [_jsx(SelectTrigger, { className: "h-9", children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: PB_BLOK_REGISTER.map((d) => (_jsx(SelectItem, { value: d.type, children: d.label }, d.type))) })] }), _jsx(Button, { size: "sm", onClick: () => {
22
+ const b = nieuwBlok(toevoegen);
23
+ zet([...blokken, b]);
24
+ setOpen(b.id);
25
+ }, children: _jsx(Plus, { className: "h-4 w-4" }) })] }), _jsxs("div", { className: "space-y-2", children: [blokken.map((b, i) => {
26
+ const uitgeklapt = open === b.id;
27
+ return (_jsxs("div", { className: "rounded-md border", children: [_jsxs("div", { className: "flex items-center gap-2 px-2 py-1.5", children: [_jsxs("button", { type: "button", className: "flex-1 text-left text-sm font-medium", onClick: () => setOpen(uitgeklapt ? null : b.id), children: [b.titel, !b.zichtbaar && (_jsx(Badge, { variant: "outline", className: "ml-2", children: "verborgen" })), b.condities.length > 0 && (_jsxs(Badge, { variant: "secondary", className: "ml-2", children: [b.condities.length, " voorwaarde", b.condities.length > 1 ? "n" : ""] }))] }), _jsx(Button, { size: "icon", variant: "ghost", className: "h-7 w-7", onClick: () => verplaats(i, -1), children: _jsx(ChevronUp, { className: "h-4 w-4" }) }), _jsx(Button, { size: "icon", variant: "ghost", className: "h-7 w-7", onClick: () => verplaats(i, 1), children: _jsx(ChevronDown, { className: "h-4 w-4" }) }), _jsx(Button, { size: "icon", variant: "ghost", className: "h-7 w-7", onClick: () => zet(blokken.filter((x) => x.id !== b.id)), children: _jsx(Trash2, { className: "h-4 w-4" }) })] }), uitgeklapt && (_jsx("div", { className: "border-t px-3 py-3", children: _jsx(BlokEditor, { blok: b, onChange: (patch) => update(b.id, patch) }) }))] }, b.id));
28
+ }), blokken.length === 0 && (_jsx("p", { className: "text-sm text-muted-foreground", children: "Nog geen blokken toegevoegd." }))] })] }));
29
+ }
30
+ function BlokEditor({ blok, onChange, }) {
31
+ const def = pbBlokDef(blok.type);
32
+ const bibliotheek = def.kolommen === "materiaal" ? MATERIAAL_KOLOMMEN : SAMENSTELLING_KOLOMMEN;
33
+ const zetOptie = (key, waarde) => onChange({ opties: { ...blok.opties, [key]: waarde } });
34
+ return (_jsxs("div", { className: "space-y-3", children: [_jsx("p", { className: "text-xs text-muted-foreground", children: def.omschrijving }), _jsxs("div", { className: "space-y-1", children: [_jsx(Label, { className: "text-xs", children: "Bloktitel" }), _jsx(Input, { value: blok.titel, onChange: (e) => onChange({ titel: e.target.value }) })] }), _jsxs("div", { className: "flex items-center justify-between rounded border px-3 py-2", children: [_jsx("span", { className: "text-sm", children: "Blok tonen" }), _jsx(Switch, { checked: blok.zichtbaar, onCheckedChange: (v) => onChange({ zichtbaar: v }) })] }), def.opties.map((o) => (_jsx(OptieVeld, { def: o, waarde: blok.opties?.[o.key], onChange: (v) => zetOptie(o.key, v) }, o.key))), def.velden && (_jsxs(_Fragment, { children: [_jsx(Separator, {}), _jsx(VeldenEditor, { velden: Array.isArray(blok.velden) ? blok.velden : [], onChange: (velden) => onChange({ velden }) })] })), def.kolommen && (_jsxs(_Fragment, { children: [_jsx(Separator, {}), _jsx(KolommenEditor, { kolommen: Array.isArray(blok.kolommen) ? blok.kolommen : [], bibliotheek: bibliotheek, onChange: (kolommen) => onChange({ kolommen }) })] })), _jsx(Separator, {}), _jsx(CondtiesEditor, { condities: blok.condities ?? [], onChange: (condities) => onChange({ condities }) })] }));
35
+ }
36
+ function OptieVeld({ def, waarde, onChange, }) {
37
+ if (def.type === "schakelaar") {
38
+ return (_jsxs("div", { className: "flex items-center justify-between rounded border px-3 py-2", children: [_jsx("span", { className: "text-sm", children: def.label }), _jsx(Switch, { checked: !!waarde, onCheckedChange: onChange })] }));
39
+ }
40
+ if (def.type === "keuze") {
41
+ return (_jsxs("div", { className: "space-y-1", children: [_jsx(Label, { className: "text-xs", children: def.label }), _jsxs(Select, { value: String(waarde ?? ""), onValueChange: onChange, children: [_jsx(SelectTrigger, { children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: (def.opties ?? []).map((o) => (_jsx(SelectItem, { value: o.value, children: o.label }, o.value))) })] })] }));
42
+ }
43
+ if (def.type === "meerkeuze") {
44
+ const gekozen = Array.isArray(waarde) ? waarde : [];
45
+ return (_jsxs("div", { className: "space-y-1", children: [_jsx(Label, { className: "text-xs", children: def.label }), _jsx("div", { className: "space-y-1", children: (def.opties ?? []).map((o) => (_jsxs("div", { className: "flex items-center justify-between rounded border px-3 py-1.5", children: [_jsx("span", { className: "text-sm", children: o.label }), _jsx(Switch, { checked: gekozen.includes(o.value), onCheckedChange: (aan) => onChange(aan ? [...gekozen, o.value] : gekozen.filter((v) => v !== o.value)) })] }, o.value))) })] }));
46
+ }
47
+ if (def.type === "getal") {
48
+ return (_jsxs("div", { className: "space-y-1", children: [_jsx(Label, { className: "text-xs", children: def.label }), _jsx(Input, { type: "number", value: Number(waarde ?? 0), onChange: (e) => onChange(Number(e.target.value)) })] }));
49
+ }
50
+ if (def.type === "kleur") {
51
+ return (_jsxs("div", { className: "space-y-1", children: [_jsx(Label, { className: "text-xs", children: def.label }), _jsx(Input, { type: "color", value: String(waarde ?? "#111827"), onChange: (e) => onChange(e.target.value) })] }));
52
+ }
53
+ return (_jsxs("div", { className: "space-y-1", children: [_jsx(Label, { className: "text-xs", children: def.label }), _jsx(Input, { value: String(waarde ?? ""), onChange: (e) => onChange(e.target.value) }), def.hint && _jsx("p", { className: "text-xs text-muted-foreground", children: def.hint })] }));
54
+ }
55
+ function VeldenEditor({ velden, onChange, }) {
56
+ const [open, setOpen] = useState(null);
57
+ const lijst = PRODUCTINFO_VELDEN.map((d) => velden.find((v) => v.key === d.key) ?? nieuwVeld(d.key));
58
+ const zet = (key, patch) => onChange(lijst.map((v) => (v.key === key ? { ...v, ...patch } : v)));
59
+ const verplaats = (key, r) => {
60
+ const i = lijst.findIndex((v) => v.key === key);
61
+ const d = i + r;
62
+ if (i < 0 || d < 0 || d >= lijst.length)
63
+ return;
64
+ const kopie = [...lijst];
65
+ [kopie[i], kopie[d]] = [kopie[d], kopie[i]];
66
+ onChange(kopie);
67
+ };
68
+ return (_jsxs("div", { className: "space-y-2", children: [_jsx(Label, { className: "text-xs uppercase text-muted-foreground", children: "Zichtbare gegevens" }), lijst.map((v) => (_jsxs("div", { className: "rounded border", children: [_jsxs("div", { className: "flex items-center gap-2 px-2 py-1.5", children: [_jsx("button", { type: "button", className: "flex-1 text-left text-sm", onClick: () => setOpen(open === v.key ? null : v.key), children: v.label }), _jsx(Button, { size: "icon", variant: "ghost", className: "h-6 w-6", onClick: () => verplaats(v.key, -1), children: _jsx(ChevronUp, { className: "h-3.5 w-3.5" }) }), _jsx(Button, { size: "icon", variant: "ghost", className: "h-6 w-6", onClick: () => verplaats(v.key, 1), children: _jsx(ChevronDown, { className: "h-3.5 w-3.5" }) }), _jsx(Switch, { checked: v.tonen, onCheckedChange: (aan) => zet(v.key, { tonen: aan }) })] }), open === v.key && (_jsxs("div", { className: "grid grid-cols-2 gap-2 border-t p-2", children: [_jsxs("div", { className: "space-y-1", children: [_jsx(Label, { className: "text-[10px]", children: "Positie" }), _jsxs(Select, { value: v.kolom, onValueChange: (w) => zet(v.key, { kolom: w }), children: [_jsx(SelectTrigger, { className: "h-8", children: _jsx(SelectValue, {}) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "links", children: "Linkerkolom" }), _jsx(SelectItem, { value: "rechts", children: "Rechterkolom" }), _jsx(SelectItem, { value: "volledig", children: "Volle breedte" })] })] })] }), _jsxs("div", { className: "space-y-1", children: [_jsx(Label, { className: "text-[10px]", children: "Grootte" }), _jsx(Input, { className: "h-8", type: "number", value: v.grootte, onChange: (e) => zet(v.key, { grootte: Number(e.target.value) }) })] }), _jsxs("div", { className: "space-y-1", children: [_jsx(Label, { className: "text-[10px]", children: "Lettertype" }), _jsxs(Select, { value: v.fontfamilie, onValueChange: (w) => zet(v.key, { fontfamilie: w }), children: [_jsx(SelectTrigger, { className: "h-8", children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: LETTERTYPEN.map((f) => (_jsx(SelectItem, { value: f, children: f }, f))) })] })] }), _jsxs("div", { className: "space-y-1", children: [_jsx(Label, { className: "text-[10px]", children: "Uitlijning" }), _jsxs(Select, { value: v.uitlijning, onValueChange: (w) => zet(v.key, { uitlijning: w }), children: [_jsx(SelectTrigger, { className: "h-8", children: _jsx(SelectValue, {}) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "left", children: "Links" }), _jsx(SelectItem, { value: "center", children: "Midden" }), _jsx(SelectItem, { value: "right", children: "Rechts" })] })] })] }), _jsxs("div", { className: "space-y-1", children: [_jsx(Label, { className: "text-[10px]", children: "Kleur" }), _jsx(Input, { className: "h-8", type: "color", value: v.kleur, onChange: (e) => zet(v.key, { kleur: e.target.value }) })] }), _jsxs("div", { className: "flex items-end justify-between rounded border px-2 py-1", children: [_jsx("span", { className: "text-xs", children: "Vet" }), _jsx(Switch, { checked: v.vet, onCheckedChange: (aan) => zet(v.key, { vet: aan }) })] })] }))] }, v.key)))] }));
69
+ }
70
+ function KolommenEditor({ kolommen, bibliotheek, onChange, }) {
71
+ const [nieuw, setNieuw] = useState(bibliotheek[0].key);
72
+ const zet = (i, patch) => onChange(kolommen.map((k, idx) => (idx === i ? { ...k, ...patch } : k)));
73
+ const verplaats = (i, r) => {
74
+ const d = i + r;
75
+ if (d < 0 || d >= kolommen.length)
76
+ return;
77
+ const kopie = [...kolommen];
78
+ [kopie[i], kopie[d]] = [kopie[d], kopie[i]];
79
+ onChange(kopie);
80
+ };
81
+ return (_jsxs("div", { className: "space-y-2", children: [_jsx(Label, { className: "text-xs uppercase text-muted-foreground", children: "Kolommen" }), kolommen.map((k, i) => (_jsxs("div", { className: "space-y-2 rounded border p-2", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Input, { className: "h-8 flex-1", value: k.label, onChange: (e) => zet(i, { label: e.target.value }) }), _jsx(Button, { size: "icon", variant: "ghost", className: "h-7 w-7", onClick: () => verplaats(i, -1), children: _jsx(ChevronUp, { className: "h-4 w-4" }) }), _jsx(Button, { size: "icon", variant: "ghost", className: "h-7 w-7", onClick: () => verplaats(i, 1), children: _jsx(ChevronDown, { className: "h-4 w-4" }) }), _jsx(Button, { size: "icon", variant: "ghost", className: "h-7 w-7", onClick: () => onChange(kolommen.filter((_, idx) => idx !== i)), children: _jsx(Trash2, { className: "h-4 w-4" }) })] }), _jsxs("div", { className: "grid grid-cols-2 gap-2", children: [_jsxs("div", { className: "space-y-1", children: [_jsx(Label, { className: "text-[10px]", children: "Breedte %" }), _jsx(Input, { className: "h-8", type: "number", value: k.breedte, onChange: (e) => zet(i, { breedte: Number(e.target.value) }) })] }), _jsxs("div", { className: "space-y-1", children: [_jsx(Label, { className: "text-[10px]", children: "Uitlijning" }), _jsxs(Select, { value: k.uitlijning, onValueChange: (v) => zet(i, { uitlijning: v }), children: [_jsx(SelectTrigger, { className: "h-8", children: _jsx(SelectValue, {}) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "left", children: "Links" }), _jsx(SelectItem, { value: "center", children: "Midden" }), _jsx(SelectItem, { value: "right", children: "Rechts" })] })] })] }), _jsxs("div", { className: "space-y-1", children: [_jsx(Label, { className: "text-[10px]", children: "Tekstgrootte" }), _jsx(Input, { className: "h-8", type: "number", value: k.fontgrootte ?? 9, onChange: (e) => zet(i, { fontgrootte: Number(e.target.value) }) })] }), _jsxs("div", { className: "space-y-1", children: [_jsx(Label, { className: "text-[10px]", children: "Kleur" }), _jsx(Input, { className: "h-8", type: "color", value: k.kleur ?? "#111827", onChange: (e) => zet(i, { kleur: e.target.value }) })] })] })] }, `${k.key}-${i}`))), _jsxs("div", { className: "flex gap-2", children: [_jsxs(Select, { value: nieuw, onValueChange: setNieuw, children: [_jsx(SelectTrigger, { className: "h-8", children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: bibliotheek.map((k) => (_jsx(SelectItem, { value: k.key, children: k.label }, k.key))) })] }), _jsx(Button, { size: "sm", variant: "outline", onClick: () => onChange([...kolommen, pbNieuweKolom(nieuw, bibliotheek)]), children: _jsx(Plus, { className: "h-4 w-4" }) })] })] }));
82
+ }
83
+ function CondtiesEditor({ condities, onChange, }) {
84
+ return (_jsxs("div", { className: "space-y-2", children: [_jsx(Label, { className: "text-xs uppercase text-muted-foreground", children: "Alleen tonen wanneer" }), condities.map((c, i) => (_jsxs("div", { className: "grid grid-cols-[1fr_auto] gap-2 rounded border p-2", children: [_jsxs("div", { className: "grid gap-2", children: [_jsxs(Select, { value: c.veld, onValueChange: (v) => onChange(condities.map((x, idx) => (idx === i ? { ...x, veld: v } : x))), children: [_jsx(SelectTrigger, { className: "h-8", children: _jsx(SelectValue, { placeholder: "Veld" }) }), _jsx(SelectContent, { children: PB_CONDITIE_VELDEN.map((v) => (_jsx(SelectItem, { value: v.value, children: v.label }, v.value))) })] }), _jsxs("div", { className: "flex gap-2", children: [_jsxs(Select, { value: c.operator, onValueChange: (v) => onChange(condities.map((x, idx) => idx === i ? { ...x, operator: v } : x)), children: [_jsx(SelectTrigger, { className: "h-8 w-28", children: _jsx(SelectValue, {}) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "=", children: "is gelijk" }), _jsx(SelectItem, { value: "!=", children: "is niet" }), _jsx(SelectItem, { value: ">", children: "groter dan" }), _jsx(SelectItem, { value: "<", children: "kleiner dan" }), _jsx(SelectItem, { value: "bevat", children: "bevat" }), _jsx(SelectItem, { value: "leeg", children: "is leeg" }), _jsx(SelectItem, { value: "niet_leeg", children: "niet leeg" })] })] }), _jsx(Input, { className: "h-8", value: c.waarde, onChange: (e) => onChange(condities.map((x, idx) => (idx === i ? { ...x, waarde: e.target.value } : x))) })] })] }), _jsx(Button, { size: "icon", variant: "ghost", className: "h-7 w-7", onClick: () => onChange(condities.filter((_, idx) => idx !== i)), children: _jsx(Trash2, { className: "h-4 w-4" }) })] }, i))), _jsxs(Button, { size: "sm", variant: "outline", onClick: () => onChange([...condities, { veld: "productgroep", operator: "=", waarde: "" }]), children: [_jsx(Plus, { className: "mr-2 h-4 w-4" }), " Voorwaarde"] })] }));
85
+ }
@@ -0,0 +1,5 @@
1
+ /** Eigen editor voor de productiebon: modulaire blokken met live voorbeeld. */
2
+ export declare function ProductiebonBuilderPage({ layoutId: layoutIdProp }?: {
3
+ layoutId?: string;
4
+ }): import("react").JSX.Element;
5
+ //# sourceMappingURL=ProductiebonBuilderPage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ProductiebonBuilderPage.d.ts","sourceRoot":"","sources":["../../src/components/ProductiebonBuilderPage.tsx"],"names":[],"mappings":"AAoBA,+EAA+E;AAC/E,wBAAgB,uBAAuB,CAAC,EAAE,QAAQ,EAAE,YAAY,EAAE,GAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAO,+BAsG7F"}
@@ -0,0 +1,48 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useMemo, useState } from "react";
3
+ import { Link } from "@tanstack/react-router";
4
+ import { Badge, Button, Card, CardContent, CardDescription, CardHeader, CardTitle, toast, } from "@flowselections/core";
5
+ import { ArrowLeft, Save } from "lucide-react";
6
+ import { ProductiebonBlokkenPanel } from "./ProductiebonBlokkenPanel";
7
+ import { ProductiebonPreview } from "./ProductiebonPreview";
8
+ import { FormaatOrientatieBar } from "./FormaatOrientatieBar";
9
+ import { leesProductiebonConfig } from "../lib/productiebonBuilder";
10
+ import { updateLayout, useLayoutDetail } from "../lib/useDocumentData";
11
+ import { useLayoutIdParam } from "../lib/useLayoutIdParam";
12
+ /** Eigen editor voor de productiebon: modulaire blokken met live voorbeeld. */
13
+ export function ProductiebonBuilderPage({ layoutId: layoutIdProp } = {}) {
14
+ const layoutId = useLayoutIdParam(layoutIdProp);
15
+ const { layout, setLayout, loading } = useLayoutDetail(layoutId ?? "");
16
+ const [config, setConfig] = useState(null);
17
+ const [bezig, setBezig] = useState(false);
18
+ useEffect(() => {
19
+ if (layout)
20
+ setConfig(leesProductiebonConfig(layout.config));
21
+ }, [layout?.id]);
22
+ const breedte = useMemo(() => (layout?.orientatie === "landscape" ? layout?.hoogte_mm : layout?.breedte_mm) ?? 210, [layout]);
23
+ const hoogte = useMemo(() => (layout?.orientatie === "landscape" ? layout?.breedte_mm : layout?.hoogte_mm) ?? 297, [layout]);
24
+ const opslaan = async () => {
25
+ if (!layout || !config)
26
+ return;
27
+ setBezig(true);
28
+ try {
29
+ await updateLayout(layout.id, {
30
+ config: { ...(layout.config ?? {}), productiebon: config },
31
+ });
32
+ toast.success("Productiebon opgeslagen");
33
+ }
34
+ catch {
35
+ toast.error("Opslaan mislukt");
36
+ }
37
+ finally {
38
+ setBezig(false);
39
+ }
40
+ };
41
+ if (!layoutId)
42
+ return (_jsxs("main", { className: "p-6 text-sm text-muted-foreground", children: ["Kies eerst een layout via ", _jsx(Link, { to: "/layouts-printers", className: "underline", children: "Layouts" }), "."] }));
43
+ if (loading)
44
+ return _jsx("main", { className: "p-6 text-sm text-muted-foreground", children: "Layout laden\u2026" });
45
+ if (!layout || !config)
46
+ return _jsx("main", { className: "p-6 text-sm text-muted-foreground", children: "Layout niet gevonden." });
47
+ return (_jsxs("main", { className: "space-y-4 p-6", children: [_jsxs("div", { className: "flex flex-wrap items-center justify-between gap-3", children: [_jsxs("div", { className: "flex items-center gap-3", children: [_jsx(Button, { variant: "ghost", size: "sm", asChild: true, children: _jsxs(Link, { to: "/layouts-printers", children: [_jsx(ArrowLeft, { className: "mr-2 h-4 w-4" }), " Layouts"] }) }), _jsxs("div", { children: [_jsx("h1", { className: "text-lg font-semibold", children: layout.naam }), _jsxs("p", { className: "text-xs text-muted-foreground", children: ["Productiebon opbouwen uit blokken \u00B7 ", config.blokken.length, " blokken"] })] })] }), _jsxs(Button, { size: "sm", onClick: opslaan, disabled: bezig, children: [_jsx(Save, { className: "mr-2 h-4 w-4" }), " Opslaan"] })] }), _jsx(FormaatOrientatieBar, { layout: layout, onChange: (patch) => setLayout((l) => (l ? { ...l, ...patch } : l)) }), _jsxs("div", { className: "grid items-start gap-4 lg:grid-cols-[400px_1fr]", children: [_jsxs(Card, { className: "flex h-[calc(100vh-14rem)] min-h-[420px] flex-col overflow-hidden lg:sticky lg:top-4", children: [_jsxs(CardHeader, { className: "pb-3", children: [_jsx(CardTitle, { className: "text-base", children: "Blokken" }), _jsx(CardDescription, { children: "Voeg blokken toe, wijzig de volgorde en stel elk blok afzonderlijk in." })] }), _jsx(CardContent, { className: "min-h-0 flex-1 overflow-y-auto", children: _jsx(ProductiebonBlokkenPanel, { config: config, onChange: setConfig }) })] }), _jsxs(Card, { className: "flex h-[calc(100vh-14rem)] min-h-[420px] flex-col overflow-hidden", children: [_jsxs(CardHeader, { className: "flex-row items-center justify-between space-y-0 pb-3", children: [_jsxs("div", { children: [_jsx(CardTitle, { className: "text-base", children: "Live voorbeeld" }), _jsx(CardDescription, { children: "Voorbeeldproductieorder met samenstelling en belading." })] }), _jsx(Badge, { variant: "secondary", children: "Direct bijgewerkt" })] }), _jsx(CardContent, { className: "min-h-0 flex-1 overflow-hidden", children: _jsx("div", { className: "h-full overflow-auto rounded-md border bg-muted/30 p-2", children: _jsx(ProductiebonPreview, { config: config, breedteMm: breedte, hoogteMm: hoogte }) }) })] })] })] }));
48
+ }
@@ -0,0 +1,12 @@
1
+ import { type ProductiebonBlok, type ProductiebonConfig } from "../lib/productiebonBuilder";
2
+ /** Live voorbeeld van de volledige productiebon, verdeeld over pagina's. */
3
+ export declare function ProductiebonPreview({ config, breedteMm, hoogteMm, }: {
4
+ config: ProductiebonConfig;
5
+ breedteMm?: number;
6
+ hoogteMm?: number;
7
+ }): import("react").JSX.Element;
8
+ /** Voorbeeld van één los blok, voor de blokontwerper. */
9
+ export declare function ProductiebonBlokPreview({ blok }: {
10
+ blok: ProductiebonBlok;
11
+ }): import("react").JSX.Element;
12
+ //# sourceMappingURL=ProductiebonPreview.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ProductiebonPreview.d.ts","sourceRoot":"","sources":["../../src/components/ProductiebonPreview.tsx"],"names":[],"mappings":"AAEA,OAAO,EAYL,KAAK,gBAAgB,EACrB,KAAK,kBAAkB,EAGxB,MAAM,4BAA4B,CAAC;AAIpC,4EAA4E;AAC5E,wBAAgB,mBAAmB,CAAC,EAClC,MAAM,EACN,SAAe,EACf,QAAc,GACf,EAAE;IACD,MAAM,EAAE,kBAAkB,CAAC;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,+BAaA;AAED,yDAAyD;AACzD,wBAAgB,uBAAuB,CAAC,EAAE,IAAI,EAAE,EAAE;IAAE,IAAI,EAAE,gBAAgB,CAAA;CAAE,+BAM3E"}
@@ -0,0 +1,237 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { PaginaDocument } from "./PaginaDocument";
3
+ import { BELADING_LABELS, DEMO_PRODUCTIEBON, EXTRA_LABELS, FUST_LABELS, ONDERDEEL_LABELS, QR_LABELS, pbBlokZichtbaar, pbConditieContext, splitsLijst, vulPbVeldcodes, } from "../lib/productiebonBuilder";
4
+ const data = DEMO_PRODUCTIEBON;
5
+ /** Live voorbeeld van de volledige productiebon, verdeeld over pagina's. */
6
+ export function ProductiebonPreview({ config, breedteMm = 210, hoogteMm = 297, }) {
7
+ const blokken = Array.isArray(config?.blokken) ? config.blokken : [];
8
+ const ctx = pbConditieContext(data);
9
+ const zichtbaar = blokken.filter((b) => pbBlokZichtbaar(b, ctx));
10
+ return (_jsx(PaginaDocument, { breedteMm: breedteMm, hoogteMm: hoogteMm, leeg: "Nog geen zichtbare blokken.", items: zichtbaar.map((b) => ({ id: b.id, node: _jsx(BlokRender, { blok: b }) })) }));
11
+ }
12
+ /** Voorbeeld van één los blok, voor de blokontwerper. */
13
+ export function ProductiebonBlokPreview({ blok }) {
14
+ return (_jsx("div", { className: "bg-white p-4 text-[11px] text-neutral-900", children: _jsx(BlokRender, { blok: blok }) }));
15
+ }
16
+ function Titel({ blok }) {
17
+ if (blok.opties?.toonTitel === false)
18
+ return null;
19
+ return (_jsx("div", { className: "mb-1 border-b border-neutral-300 pb-0.5 text-[11px] font-semibold uppercase tracking-wide", children: blok.titel }));
20
+ }
21
+ function Kader({ aan, children }) {
22
+ return _jsx("div", { className: aan ? "rounded border border-neutral-300 p-2" : "", children: children });
23
+ }
24
+ function BlokRender({ blok }) {
25
+ switch (blok.type) {
26
+ case "productinfo":
27
+ return _jsx(ProductInfo, { blok: blok });
28
+ case "productfotos":
29
+ return _jsx(Fotos, { blok: blok });
30
+ case "samenstelling":
31
+ return _jsx(Samenstelling, { blok: blok });
32
+ case "instructies":
33
+ return _jsx(Instructies, { blok: blok });
34
+ case "productiestappen":
35
+ return _jsx(Stappen, { blok: blok });
36
+ case "fustinformatie":
37
+ return _jsx(Gegevensblok, { blok: blok, bron: data.fust, labels: FUST_LABELS });
38
+ case "belading":
39
+ return _jsx(Belading, { blok: blok });
40
+ case "visueleopbouw":
41
+ return _jsx(VisueleOpbouw, { blok: blok });
42
+ case "materialen":
43
+ return _jsx(Materialen, { blok: blok });
44
+ case "controlepunten":
45
+ return _jsx(Controlepunten, { blok: blok });
46
+ case "extrainfo":
47
+ return _jsx(Extra, { blok: blok });
48
+ case "qrblok":
49
+ return _jsx(QrBlok, { blok: blok });
50
+ case "tekstblok":
51
+ return _jsx(Tekstblok, { blok: blok });
52
+ default:
53
+ return null;
54
+ }
55
+ }
56
+ // --- algemene productinformatie --------------------------------------------
57
+ function veldWaarde(key) {
58
+ const p = data.product;
59
+ return String(p[key] ?? "");
60
+ }
61
+ function VeldRegel({ veld }) {
62
+ if (veld.key === "productfoto_groot" || veld.key === "productfotos") {
63
+ const aantal = veld.key === "productfotos" ? 3 : 1;
64
+ return (_jsx("div", { className: "flex gap-2", children: Array.from({ length: aantal }).map((_, i) => (_jsx(Fotovlak, { hoogte: veld.grootte, label: i === 0 ? "Productfoto" : `Detail ${i}` }, i))) }));
65
+ }
66
+ if (veld.key === "barcode") {
67
+ return (_jsxs("div", { style: { textAlign: veld.uitlijning }, children: [_jsx("div", { className: "h-6 w-full", style: { background: "repeating-linear-gradient(90deg,#111 0 2px,#fff 2px 4px)" } }), _jsx("div", { className: "text-[8px]", children: veldWaarde("barcode") })] }));
68
+ }
69
+ if (veld.key === "qrcode") {
70
+ return (_jsx("div", { style: { textAlign: veld.uitlijning }, children: _jsx(QrVlak, { grootte: 22 }) }));
71
+ }
72
+ return (_jsxs("div", { className: "flex gap-2", style: {
73
+ fontFamily: veld.fontfamilie,
74
+ fontSize: `${veld.grootte}px`,
75
+ color: veld.kleur,
76
+ textAlign: veld.uitlijning,
77
+ fontWeight: veld.vet ? 700 : 400,
78
+ }, children: [_jsx("span", { className: "min-w-[34%] text-neutral-500", children: veld.label }), _jsx("span", { className: "flex-1", children: veldWaarde(veld.key) || "—" })] }));
79
+ }
80
+ function ProductInfo({ blok }) {
81
+ const velden = (Array.isArray(blok.velden) ? blok.velden : []).filter((v) => v.tonen);
82
+ const links = velden.filter((v) => v.kolom === "links");
83
+ const rechts = velden.filter((v) => v.kolom === "rechts");
84
+ const vol = velden.filter((v) => v.kolom === "volledig");
85
+ const twee = blok.opties?.indeling !== "een";
86
+ return (_jsxs(Kader, { aan: blok.opties?.kader !== false, children: [blok.opties?.toonTitel ? _jsx(Titel, { blok: blok }) : null, _jsxs("div", { className: "space-y-2", children: [vol.map((v) => (_jsx(VeldRegel, { veld: v }, v.key))), _jsxs("div", { className: twee ? "grid grid-cols-2 gap-x-4 gap-y-1" : "space-y-1", children: [_jsx("div", { className: "space-y-1", children: links.map((v) => (_jsx(VeldRegel, { veld: v }, v.key))) }), twee && (_jsx("div", { className: "space-y-1", children: rechts.map((v) => (_jsx(VeldRegel, { veld: v }, v.key))) })), !twee &&
87
+ rechts.map((v) => _jsx(VeldRegel, { veld: v }, v.key))] })] })] }));
88
+ }
89
+ // --- afbeeldingen -----------------------------------------------------------
90
+ function Fotovlak({ hoogte, label }) {
91
+ return (_jsx("div", { className: "flex flex-1 items-center justify-center rounded border border-dashed border-neutral-300 bg-neutral-50 text-[9px] text-neutral-400", style: { height: `${hoogte}mm` }, children: label }));
92
+ }
93
+ function QrVlak({ grootte }) {
94
+ return (_jsx("div", { className: "inline-block", style: {
95
+ width: `${grootte}mm`,
96
+ height: `${grootte}mm`,
97
+ background: "repeating-conic-gradient(#111 0% 25%, #fff 0% 50%) 0 0/22% 22%",
98
+ } }));
99
+ }
100
+ function Fotos({ blok }) {
101
+ const aantal = Math.max(1, Number(blok.opties?.aantal ?? 3));
102
+ const hoogte = Number(blok.opties?.hoogte ?? 35);
103
+ const uitlijning = String(blok.opties?.uitlijning ?? "left");
104
+ return (_jsxs("div", { children: [_jsx(Titel, { blok: blok }), _jsx("div", { className: "flex gap-2", style: { justifyContent: uitlijning === "center" ? "center" : uitlijning === "right" ? "flex-end" : "flex-start" }, children: Array.from({ length: aantal }).map((_, i) => (_jsxs("div", { className: "flex-1 space-y-1", children: [_jsx(Fotovlak, { hoogte: hoogte, label: `Foto ${i + 1}` }), blok.opties?.bijschrift !== false && (_jsxs("div", { className: "text-center text-[8px] text-neutral-400", children: ["Weergave ", i + 1] }))] }, i))) })] }));
105
+ }
106
+ function VisueleOpbouw({ blok }) {
107
+ const weergaven = Array.isArray(blok.opties?.weergaven) ? blok.opties.weergaven : [];
108
+ const labels = {
109
+ eindfoto: "Grote eindfoto",
110
+ detail: "Detailfoto's",
111
+ explosie: "Explosietekening",
112
+ voorbeeld: "Voorbeeld eindresultaat",
113
+ };
114
+ const hoogte = Number(blok.opties?.hoogte ?? 50);
115
+ return (_jsxs("div", { children: [_jsx(Titel, { blok: blok }), _jsxs("div", { className: blok.opties?.naastElkaar === false ? "space-y-2" : "flex gap-2", children: [weergaven.map((w) => (_jsx("div", { className: "flex-1", children: _jsx(Fotovlak, { hoogte: hoogte, label: labels[w] ?? w }) }, w))), weergaven.length === 0 && _jsx("p", { className: "text-neutral-400", children: "Geen afbeeldingen geselecteerd." })] })] }));
116
+ }
117
+ // --- tabellen ---------------------------------------------------------------
118
+ function cel(item, key) {
119
+ if (key === "foto")
120
+ return _jsx(Fotovlak, { hoogte: 10, label: "foto" });
121
+ if (key === "qrcode")
122
+ return _jsx(QrVlak, { grootte: 8 });
123
+ if (key === "barcode")
124
+ return _jsx("div", { className: "h-3 w-full", style: { background: "repeating-linear-gradient(90deg,#111 0 1.5px,#fff 1.5px 3px)" } });
125
+ const v = item[key];
126
+ return v === undefined || v === null || v === "" ? "—" : String(v);
127
+ }
128
+ function Tabel({ kolommen, rijen, toonKoppen, regelhoogte, streepjes, }) {
129
+ return (_jsxs("table", { className: "w-full border-collapse", children: [toonKoppen && (_jsx("thead", { children: _jsx("tr", { children: kolommen.map((k) => (_jsx("th", { className: "border-b border-neutral-400 pb-0.5 font-semibold", style: { width: `${k.breedte}%`, textAlign: k.uitlijning, fontSize: `${k.fontgrootte ?? 9}px` }, children: k.label }, k.key))) }) })), _jsx("tbody", { children: rijen.map((r, i) => (_jsx("tr", { className: streepjes ? "border-b border-neutral-200" : "", children: kolommen.map((k) => (_jsx("td", { style: {
130
+ textAlign: k.uitlijning,
131
+ fontSize: `${k.fontgrootte ?? 9}px`,
132
+ color: k.kleur ?? "#111827",
133
+ fontFamily: k.fontfamilie,
134
+ fontWeight: k.vet ? 700 : 400,
135
+ height: `${regelhoogte}mm`,
136
+ }, children: cel(r, k.key) }, k.key))) }, `${r.artikelnummer}-${i}`))) })] }));
137
+ }
138
+ function Samenstelling({ blok }) {
139
+ const kolommen = Array.isArray(blok.kolommen) ? blok.kolommen : [];
140
+ const soorten = Array.isArray(blok.opties?.onderdelen) ? blok.opties.onderdelen : [];
141
+ const rijen = data.onderdelen.filter((x) => soorten.includes(x.soort));
142
+ const groeperen = blok.opties?.groepeerPerSoort !== false;
143
+ const regelhoogte = Number(blok.opties?.regelhoogte ?? 7);
144
+ const toonKoppen = blok.opties?.toonKoppen !== false;
145
+ const streepjes = blok.opties?.streepjes !== false;
146
+ return (_jsxs("div", { children: [_jsx(Titel, { blok: blok }), rijen.length === 0 && _jsx("p", { className: "text-neutral-400", children: "Geen onderdelen geselecteerd." }), groeperen ? (soorten
147
+ .filter((s) => rijen.some((r) => r.soort === s))
148
+ .map((s) => (_jsxs("div", { className: "mb-1.5", children: [_jsx("div", { className: "bg-neutral-100 px-1 py-0.5 text-[9px] font-semibold uppercase", children: ONDERDEEL_LABELS[s] ?? s }), _jsx(Tabel, { kolommen: kolommen, rijen: rijen.filter((r) => r.soort === s), toonKoppen: toonKoppen, regelhoogte: regelhoogte, streepjes: streepjes })] }, s)))) : (_jsx(Tabel, { kolommen: kolommen, rijen: rijen, toonKoppen: toonKoppen, regelhoogte: regelhoogte, streepjes: streepjes }))] }));
149
+ }
150
+ function Materialen({ blok }) {
151
+ const kolommen = Array.isArray(blok.kolommen) ? blok.kolommen : [];
152
+ const groepering = String(blok.opties?.groepering ?? "geen");
153
+ const regelhoogte = Number(blok.opties?.regelhoogte ?? 7);
154
+ const toonKoppen = blok.opties?.toonKoppen !== false;
155
+ const rijen = data.onderdelen;
156
+ const sleutel = (r) => groepering === "locatie"
157
+ ? r.locatie
158
+ : groepering === "leverancier"
159
+ ? r.leverancier
160
+ : groepering === "stap"
161
+ ? r.stap
162
+ : ONDERDEEL_LABELS[r.productgroep] ?? r.productgroep;
163
+ const groepen = groepering === "geen" ? [] : Array.from(new Set(rijen.map(sleutel)));
164
+ return (_jsxs("div", { children: [_jsx(Titel, { blok: blok }), groepering === "geen" ? (_jsx(Tabel, { kolommen: kolommen, rijen: rijen, toonKoppen: toonKoppen, regelhoogte: regelhoogte, streepjes: true })) : (groepen.map((g) => (_jsxs("div", { className: "mb-1.5", children: [_jsx("div", { className: "bg-neutral-100 px-1 py-0.5 text-[9px] font-semibold uppercase", children: g }), _jsx(Tabel, { kolommen: kolommen, rijen: rijen.filter((r) => sleutel(r) === g), toonKoppen: toonKoppen, regelhoogte: regelhoogte, streepjes: true })] }, g)))), blok.opties?.toonTotaal !== false && (_jsxs("div", { className: "mt-1 border-t border-neutral-400 pt-0.5 text-right font-semibold", children: ["Totaal ", rijen.length, " materialen"] }))] }));
165
+ }
166
+ // --- instructies & stappen --------------------------------------------------
167
+ function Instructies({ blok }) {
168
+ const gekozen = Array.isArray(blok.opties?.onderdelen) ? blok.opties.onderdelen : [];
169
+ const volgorde = String(blok.opties?.volgorde ?? "")
170
+ .split(",")
171
+ .map((s) => s.trim())
172
+ .filter(Boolean);
173
+ const keys = [...volgorde.filter((k) => gekozen.includes(k)), ...gekozen.filter((k) => !volgorde.includes(k))];
174
+ const genummerd = blok.opties?.genummerd !== false;
175
+ const inhoud = {
176
+ stappen: (_jsx("ol", { className: genummerd ? "list-decimal space-y-0.5 pl-4" : "space-y-0.5", children: splitsLijst("Pak de juiste pot;Plaats de plant;Voeg decoratie toe;Plaats de sticker;Controleer de kwaliteit").map((s) => (_jsx("li", { children: s }, s))) })),
177
+ eigentekst: _jsx("p", { children: vulPbVeldcodes(String(blok.opties?.eigenTekst ?? ""), data) || "—" }),
178
+ controlepunten: _jsx("p", { children: data.instructies.controlepunten }),
179
+ veiligheid: _jsx("p", { children: data.instructies.veiligheid }),
180
+ gereedschap: _jsx("p", { children: data.instructies.gereedschap }),
181
+ tijd: _jsx("p", { children: data.instructies.tijd }),
182
+ opmerkingen: _jsx("p", { children: data.instructies.opmerkingen }),
183
+ };
184
+ const labels = {
185
+ stappen: "Stap voor stap",
186
+ eigentekst: "Eigen tekst",
187
+ controlepunten: "Controlepunten",
188
+ veiligheid: "Veiligheid",
189
+ gereedschap: "Gereedschap",
190
+ tijd: "Benodigde tijd",
191
+ opmerkingen: "Opmerkingen",
192
+ };
193
+ return (_jsxs("div", { children: [_jsx(Titel, { blok: blok }), _jsxs("div", { className: "space-y-1.5", children: [keys.map((k) => (_jsxs("div", { children: [_jsx("div", { className: "text-[9px] font-semibold uppercase text-neutral-500", children: labels[k] ?? k }), inhoud[k]] }, k))), keys.length === 0 && _jsx("p", { className: "text-neutral-400", children: "Geen onderdelen geselecteerd." })] })] }));
194
+ }
195
+ function Stappen({ blok }) {
196
+ const stappen = splitsLijst(blok.opties?.stappen);
197
+ const velden = Array.isArray(blok.opties?.velden) ? blok.opties.velden : [];
198
+ const nummeren = blok.opties?.nummeren !== false;
199
+ const regelhoogte = Number(blok.opties?.regelhoogte ?? 9);
200
+ return (_jsxs("div", { children: [_jsx(Titel, { blok: blok }), _jsxs("div", { className: "space-y-1", children: [stappen.map((s, i) => (_jsxs("div", { className: "flex items-start gap-2 border-b border-neutral-200 pb-1", style: { minHeight: `${regelhoogte}mm` }, children: [velden.includes("controlevak") && _jsx("span", { className: "mt-0.5 h-3 w-3 border border-neutral-500" }), _jsxs("div", { className: "flex-1", children: [_jsxs("div", { className: "font-semibold", children: [nummeren ? `${i + 1}. ` : "", s] }), velden.includes("omschrijving") && (_jsx("div", { className: "text-neutral-500", children: "Voer deze stap uit volgens de werkinstructie." })), _jsxs("div", { className: "flex gap-3 text-[9px] text-neutral-500", children: [velden.includes("tijd") && _jsx("span", { children: "\u00B1 30 sec" }), velden.includes("gereedschap") && _jsxs("span", { children: ["Gereedschap: ", data.instructies.gereedschap] }), velden.includes("materialen") && _jsxs("span", { children: ["Materialen: ", data.onderdelen[i % data.onderdelen.length].artikelnaam] })] })] }), velden.includes("foto") && (_jsx("div", { className: "w-16", children: _jsx(Fotovlak, { hoogte: 12, label: "foto" }) }))] }, `${s}-${i}`))), stappen.length === 0 && _jsx("p", { className: "text-neutral-400", children: "Nog geen stappen ingevuld." })] })] }));
201
+ }
202
+ // --- fust, belading, controle, extra ---------------------------------------
203
+ function Gegevensblok({ blok, bron, labels, }) {
204
+ const keys = Array.isArray(blok.opties?.velden) ? blok.opties.velden : [];
205
+ const tabel = String(blok.opties?.weergave ?? "tabel") === "tabel";
206
+ return (_jsxs("div", { children: [_jsx(Titel, { blok: blok }), _jsxs("div", { className: tabel ? "grid grid-cols-2 gap-x-4 gap-y-0.5" : "space-y-0.5", children: [keys.map((k) => k === "afbeelding" ? (_jsx("div", { className: "col-span-2", children: _jsx(Fotovlak, { hoogte: 20, label: "Afbeelding fust" }) }, k)) : k === "barcode" ? (_jsxs("div", { className: "col-span-2", children: [_jsx("div", { className: "h-5 w-32", style: { background: "repeating-linear-gradient(90deg,#111 0 2px,#fff 2px 4px)" } }), _jsx("div", { className: "text-[8px]", children: bron.barcode })] }, k)) : (_jsxs("div", { className: "flex gap-2", children: [_jsx("span", { className: "min-w-[45%] text-neutral-500", children: labels[k] ?? k }), _jsx("span", { children: bron[k] ?? "—" })] }, k))), keys.length === 0 && _jsx("p", { className: "text-neutral-400", children: "Geen gegevens geselecteerd." })] })] }));
207
+ }
208
+ function Belading({ blok }) {
209
+ const keys = Array.isArray(blok.opties?.velden) ? blok.opties.velden : [];
210
+ const karhoogte = Number(blok.opties?.karhoogte ?? 45);
211
+ return (_jsxs("div", { children: [_jsx(Titel, { blok: blok }), _jsx("div", { className: "grid grid-cols-2 gap-x-4 gap-y-0.5", children: keys
212
+ .filter((k) => !["laagoverzicht", "visueel"].includes(k))
213
+ .map((k) => (_jsxs("div", { className: "flex gap-2", children: [_jsx("span", { className: "min-w-[45%] text-neutral-500", children: BELADING_LABELS[k] ?? k }), _jsx("span", { children: data.belading[k] ?? "—" })] }, k))) }), keys.includes("laagoverzicht") && (_jsxs("table", { className: "mt-1.5 w-full border-collapse", children: [_jsx("thead", { children: _jsxs("tr", { children: [_jsx("th", { className: "border-b border-neutral-400 text-left", children: "Laag" }), _jsx("th", { className: "border-b border-neutral-400 text-left", children: "Inhoud" }), _jsx("th", { className: "border-b border-neutral-400 text-left", children: "Opmerking" })] }) }), _jsx("tbody", { children: data.lagen.map((l) => (_jsxs("tr", { className: "border-b border-neutral-200", children: [_jsxs("td", { children: ["Laag ", l.laag] }), _jsx("td", { children: l.inhoud }), _jsx("td", { children: l.opmerking || "—" })] }, l.laag))) })] })), keys.includes("visueel") && (_jsx("div", { className: "mt-1.5 flex flex-col-reverse gap-0.5", style: { height: `${karhoogte}mm` }, children: data.lagen.map((l) => (_jsxs("div", { className: "flex flex-1 items-center justify-between rounded border border-neutral-300 bg-neutral-50 px-1 text-[9px]", children: [_jsxs("span", { children: ["Laag ", l.laag] }), _jsx("span", { children: l.inhoud })] }, l.laag))) }))] }));
214
+ }
215
+ function Controlepunten({ blok }) {
216
+ const punten = splitsLijst(blok.opties?.punten);
217
+ const kolommen = Number(blok.opties?.kolommenAantal ?? 2);
218
+ return (_jsxs("div", { children: [_jsx(Titel, { blok: blok }), _jsxs("div", { className: "grid gap-x-4 gap-y-1", style: { gridTemplateColumns: `repeat(${Math.max(1, kolommen)}, minmax(0, 1fr))` }, children: [punten.map((p) => (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("span", { className: "h-3 w-3 border border-neutral-500" }), _jsx("span", { className: "flex-1", children: p }), blok.opties?.paraaf ? _jsx("span", { className: "h-3 w-10 border-b border-neutral-400" }) : null] }, p))), punten.length === 0 && _jsx("p", { className: "text-neutral-400", children: "Geen controlepunten ingesteld." })] })] }));
219
+ }
220
+ function Extra({ blok }) {
221
+ const keys = Array.isArray(blok.opties?.onderdelen) ? blok.opties.onderdelen : [];
222
+ return (_jsxs(Kader, { aan: blok.opties?.kader !== false, children: [_jsx(Titel, { blok: blok }), _jsxs("div", { className: "space-y-1", children: [keys.map((k) => (_jsxs("div", { children: [_jsx("div", { className: "text-[9px] font-semibold uppercase text-neutral-500", children: EXTRA_LABELS[k] ?? k }), _jsx("p", { children: data.extra[k] ?? "—" })] }, k))), keys.length === 0 && _jsx("p", { className: "text-neutral-400", children: "Geen onderdelen geselecteerd." })] })] }));
223
+ }
224
+ function QrBlok({ blok }) {
225
+ const codes = Array.isArray(blok.opties?.codes) ? blok.opties.codes : [];
226
+ const grootte = Number(blok.opties?.grootte ?? 22);
227
+ const uitlijning = String(blok.opties?.uitlijning ?? "left");
228
+ return (_jsxs("div", { children: [_jsx(Titel, { blok: blok }), _jsxs("div", { className: "flex gap-4", style: { justifyContent: uitlijning === "center" ? "center" : uitlijning === "right" ? "flex-end" : "flex-start" }, children: [codes.map((c) => (_jsxs("div", { className: "text-center", children: [_jsx(QrVlak, { grootte: grootte }), blok.opties?.bijschrift !== false && (_jsx("div", { className: "text-[8px] text-neutral-500", children: QR_LABELS[c] ?? c }))] }, c))), codes.length === 0 && _jsx("p", { className: "text-neutral-400", children: "Geen QR-codes geselecteerd." })] })] }));
229
+ }
230
+ function Tekstblok({ blok }) {
231
+ return (_jsxs(Kader, { aan: !!blok.opties?.kader, children: [_jsx(Titel, { blok: blok }), _jsx("p", { style: {
232
+ fontSize: `${Number(blok.opties?.fontgrootte ?? 10)}px`,
233
+ color: String(blok.opties?.kleur ?? "#111827"),
234
+ textAlign: String(blok.opties?.uitlijning ?? "left"),
235
+ whiteSpace: "pre-wrap",
236
+ }, children: vulPbVeldcodes(String(blok.opties?.tekst ?? ""), data) })] }));
237
+ }
@@ -0,0 +1,6 @@
1
+ interface TemplateModuleLogoProps {
2
+ className?: string;
3
+ }
4
+ export declare function TemplateModuleLogo({ className }: TemplateModuleLogoProps): import("react").JSX.Element;
5
+ export {};
6
+ //# sourceMappingURL=TemplateModuleLogo.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TemplateModuleLogo.d.ts","sourceRoot":"","sources":["../../src/components/TemplateModuleLogo.tsx"],"names":[],"mappings":"AAQA,UAAU,uBAAuB;IAC/B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,kBAAkB,CAAC,EAAE,SAAS,EAAE,EAAE,uBAAuB,+BAwBxE"}
@@ -0,0 +1,4 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ export function TemplateModuleLogo({ className }) {
3
+ return (_jsxs("svg", { viewBox: "0 0 40 40", fill: "none", xmlns: "http://www.w3.org/2000/svg", className: className, "aria-hidden": "true", children: [_jsx("rect", { width: "40", height: "40", rx: "8", fill: "currentColor", opacity: "0.1" }), _jsx("text", { x: "50%", y: "50%", dominantBaseline: "middle", textAnchor: "middle", fontSize: "18", fontWeight: "600", fill: "currentColor", children: "M" })] }));
4
+ }
@@ -0,0 +1,2 @@
1
+ export declare function MijnModuleSettingsCard(): import("react").JSX.Element;
2
+ //# sourceMappingURL=MijnModuleSettingsCard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MijnModuleSettingsCard.d.ts","sourceRoot":"","sources":["../../../src/components/settings/MijnModuleSettingsCard.tsx"],"names":[],"mappings":"AAiCA,wBAAgB,sBAAsB,gCA+CrC"}
@@ -0,0 +1,42 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ // ============================================================================
3
+ // src/components/settings/MijnModuleSettingsCard.tsx — EXAMPLE SETTINGS CARD
4
+ // ============================================================================
5
+ // This is an example of a settings card for your module.
6
+ // It appears on the main settings page when registered in src/index.ts.
7
+ //
8
+ // RULES:
9
+ // - Must return a <Card> as its root element
10
+ // - Handles its own data fetching — no props are passed by the core
11
+ // - Use the same Card primitives from @flowselections/core
12
+ //
13
+ // WHAT TO DO:
14
+ // - Rename this file to match your card's purpose
15
+ // - Add your real settings UI
16
+ // - Register it in src/index.ts under settingsCards
17
+ // - Delete this file entirely if your module has no settings
18
+ // ============================================================================
19
+ import { useState } from "react";
20
+ import { Card, CardContent, CardDescription, CardHeader, CardTitle, Button, Input, Label, toast, } from "@flowselections/core";
21
+ import { Settings2 } from "lucide-react";
22
+ export function MijnModuleSettingsCard() {
23
+ // Replace with your real settings state
24
+ const [setting, setSetting] = useState("");
25
+ const [saving, setSaving] = useState(false);
26
+ const handleSave = async () => {
27
+ setSaving(true);
28
+ try {
29
+ // Example: save to a table — replace with your real query
30
+ // const { error } = await supabase.from("my_settings").upsert({ ... });
31
+ // if (error) throw error;
32
+ toast.success("Instellingen opgeslagen");
33
+ }
34
+ catch {
35
+ toast.error("Fout bij het opslaan");
36
+ }
37
+ finally {
38
+ setSaving(false);
39
+ }
40
+ };
41
+ return (_jsxs(Card, { children: [_jsxs(CardHeader, { children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Settings2, { className: "h-5 w-5 text-primary" }), _jsx(CardTitle, { children: "Module Instellingen" }), " "] }), _jsx(CardDescription, { children: "Beheer de instellingen voor deze module " })] }), _jsxs(CardContent, { className: "space-y-4", children: [_jsxs("div", { className: "space-y-2", children: [_jsx(Label, { htmlFor: "setting", children: "Instelling" }), _jsx(Input, { id: "setting", value: setting, onChange: (e) => setSetting(e.target.value), placeholder: "Waarde..." })] }), _jsx(Button, { onClick: handleSave, disabled: saving, children: saving ? "Opslaan..." : "Opslaan" })] })] }));
42
+ }
@@ -0,0 +1,13 @@
1
+ interface MijnItem {
2
+ id: string;
3
+ created_at: string;
4
+ [key: string]: unknown;
5
+ }
6
+ export declare function useMijnModuleData(): {
7
+ items: MijnItem[];
8
+ loading: boolean;
9
+ error: string | null;
10
+ reload: () => Promise<void>;
11
+ };
12
+ export {};
13
+ //# sourceMappingURL=useMijnModuleData.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useMijnModuleData.d.ts","sourceRoot":"","sources":["../../src/hooks/useMijnModuleData.ts"],"names":[],"mappings":"AAkBA,UAAU,QAAQ;IAChB,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;IACnB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,wBAAgB,iBAAiB;;;;;EAkChC"}
@@ -0,0 +1,49 @@
1
+ // ============================================================================
2
+ // src/hooks/useMijnModuleData.ts — EXAMPLE DATA HOOK
3
+ // ============================================================================
4
+ // Put your data fetching logic in hooks like this, not directly in components.
5
+ // This keeps your pages clean and makes it easy to reuse data across pages.
6
+ //
7
+ // WHAT TO DO:
8
+ // - Rename to match your data (e.g. useProducts, useOrders, useSuppliers)
9
+ // - Replace the example query with your real Supabase query
10
+ // - Replace MijnItem with your actual table's row type
11
+ // - Import and use in your page components
12
+ // - Delete this file if you prefer a different pattern
13
+ // ============================================================================
14
+ import { useState, useEffect, useCallback } from "react";
15
+ import { supabase, toast } from "@flowselections/core";
16
+ export function useMijnModuleData() {
17
+ const [items, setItems] = useState([]);
18
+ const [loading, setLoading] = useState(true);
19
+ const [error, setError] = useState(null);
20
+ const load = useCallback(async () => {
21
+ setLoading(true);
22
+ setError(null);
23
+ try {
24
+ const { data, error } = await supabase
25
+ .from("products") // ← CHANGE THIS to your table
26
+ .select("*")
27
+ .order("created_at", { ascending: false });
28
+ if (error)
29
+ throw error;
30
+ setItems(data ?? []);
31
+ }
32
+ catch {
33
+ setError("Fout bij het laden van gegevens");
34
+ toast.error("Fout bij het laden van gegevens");
35
+ }
36
+ finally {
37
+ setLoading(false);
38
+ }
39
+ }, []);
40
+ useEffect(() => {
41
+ load();
42
+ }, [load]);
43
+ return {
44
+ items,
45
+ loading,
46
+ error,
47
+ reload: load,
48
+ };
49
+ }
@@ -0,0 +1,38 @@
1
+ import type { FlowModule } from "@flowselections/core";
2
+ export * from './_core-safelist';
3
+ export * from './components/LayoutsPage';
4
+ export * from './components/PrintersPage';
5
+ export * from './components/PrintQueuePage';
6
+ export * from './components/AiLayoutImportDialog';
7
+ export * from './components/LogosPage';
8
+ export * from './components/PakbonBuilderPage';
9
+ export * from './components/BlokOntwerperPage';
10
+ export * from './components/PakbonSectiesPanel';
11
+ export * from './components/PakbonPreview';
12
+ export * from './lib/pakbonBuilder';
13
+ export * from './lib/pakbonHtml';
14
+ export * from './components/ProductiebonBuilderPage';
15
+ export * from './components/ProductiebonBlokkenPanel';
16
+ export * from './components/ProductiebonPreview';
17
+ export * from './lib/productiebonBuilder';
18
+ export * from './components/LogoBibliotheekDialog';
19
+ export * from './lib/logoLibrary';
20
+ export * from './lib/documentProfiles';
21
+ export * from './lib/formaatPrinters';
22
+ export * from './components/DocumentSettingsPanel';
23
+ export * from './lib/documentTypes';
24
+ export * from './lib/fieldRegistry';
25
+ export * from './lib/layoutPreview';
26
+ export * from './lib/useDocumentData';
27
+ export * from './lib/printApi';
28
+ export * from './lib/browserPrint';
29
+ export * from './components/AfdrukKnop';
30
+ export * from './lib/supabaseClient';
31
+ export * from './lib/useLayoutIdParam';
32
+ export * from './components/PaginaDocument';
33
+ export * from './components/PreviewFit';
34
+ export * from './components/FormaatOrientatieBar';
35
+ export * from './components/PrinterSetupWizard';
36
+ export { TemplateModuleLogo } from './components/TemplateModuleLogo';
37
+ export declare const layoutmodule: FlowModule;
38
+ //# sourceMappingURL=index.d.ts.map