@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,156 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } 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, ImagePlus, Plus, Trash2 } from "lucide-react";
5
+ import { useLogos } from "../lib/logoLibrary";
6
+ import { LogoBibliotheekDialog } from "./LogoBibliotheekDialog";
7
+ import { LogoThumbnail } from "./LogosPage";
8
+ import { BESCHIKBARE_KOLOMMEN, CONDITIE_VELDEN, SECTIE_REGISTER, nieuweKolom, nieuweSectie, pickMethode, sectieDef, sectieOnderdrukt, } from "../lib/pakbonBuilder";
9
+ /** Beheer van de secties waaruit de pakbon is opgebouwd. */
10
+ export function PakbonSectiesPanel({ config, onChange, }) {
11
+ const alleSecties = Array.isArray(config?.secties) ? config.secties : [];
12
+ const methode = pickMethode(alleSecties);
13
+ const secties = alleSecties.filter((s) => !sectieOnderdrukt(s.type, methode));
14
+ const [open, setOpen] = useState(secties[0]?.id ?? null);
15
+ const [toevoegen, setToevoegen] = useState("orderregels");
16
+ // Onderdrukte blokken blijven bewaard in de configuratie, ook al staan ze niet in de lijst.
17
+ const verborgen = alleSecties.filter((s) => sectieOnderdrukt(s.type, methode));
18
+ const zet = (nieuw) => onChange({ ...config, secties: [...nieuw, ...verborgen] });
19
+ const update = (id, patch) => zet(secties.map((s) => (s.id === id ? { ...s, ...patch } : s)));
20
+ const verplaats = (index, richting) => {
21
+ const doel = index + richting;
22
+ if (doel < 0 || doel >= secties.length)
23
+ return;
24
+ const kopie = [...secties];
25
+ [kopie[index], kopie[doel]] = [kopie[doel], kopie[index]];
26
+ zet(kopie);
27
+ };
28
+ return (_jsxs("div", { className: "space-y-3", children: [_jsxs("div", { className: "space-y-2 rounded-md border bg-muted/30 p-2", children: [_jsxs("div", { children: [_jsx(Label, { className: "text-xs uppercase text-muted-foreground", children: "Documentlogo" }), _jsx("p", { className: "text-xs text-muted-foreground", children: "Dit logo staat bovenaan het document en wordt ook meegeprint." })] }), _jsx(LogoKiezer, { logoId: config.logo?.logoId ?? null, onKies: (logo) => onChange({
29
+ ...config,
30
+ logo: {
31
+ ...(config.logo ?? {}),
32
+ logoId: logo?.id ?? null,
33
+ naam: logo?.naam ?? null,
34
+ hoogte: config.logo?.hoogte ?? 18,
35
+ uitlijning: config.logo?.uitlijning ?? "left",
36
+ },
37
+ }) }), config.logo?.logoId && (_jsxs("div", { className: "grid grid-cols-2 gap-2", children: [_jsxs("div", { className: "space-y-1", children: [_jsx(Label, { className: "text-xs", children: "Hoogte (mm)" }), _jsx(Input, { type: "number", value: config.logo?.hoogte ?? 18, onChange: (e) => onChange({
38
+ ...config,
39
+ logo: { ...(config.logo ?? { logoId: null }), hoogte: Number(e.target.value) || 0 },
40
+ }) })] }), _jsxs("div", { className: "space-y-1", children: [_jsx(Label, { className: "text-xs", children: "Uitlijning" }), _jsxs(Select, { value: config.logo?.uitlijning ?? "left", onValueChange: (v) => onChange({
41
+ ...config,
42
+ logo: {
43
+ ...(config.logo ?? { logoId: null }),
44
+ uitlijning: v,
45
+ },
46
+ }), children: [_jsx(SelectTrigger, { 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: "flex gap-2", children: [_jsxs(Select, { value: toevoegen, onValueChange: (v) => setToevoegen(v), children: [_jsx(SelectTrigger, { className: "h-9", children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: SECTIE_REGISTER.filter((d) => !sectieOnderdrukt(d.type, methode)).map((d) => (_jsx(SelectItem, { value: d.type, children: d.label }, d.type))) })] }), _jsx(Button, { size: "sm", onClick: () => {
47
+ const s = nieuweSectie(toevoegen);
48
+ zet([...secties, s]);
49
+ setOpen(s.id);
50
+ }, children: _jsx(Plus, { className: "h-4 w-4" }) })] }), _jsxs("div", { className: "space-y-2", children: [secties.map((s, i) => {
51
+ const def = sectieDef(s.type);
52
+ const uitgeklapt = open === s.id;
53
+ 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 : s.id), children: [s.titel, !s.zichtbaar && (_jsx(Badge, { variant: "outline", className: "ml-2", children: "verborgen" })), s.condities.length > 0 && (_jsxs(Badge, { variant: "secondary", className: "ml-2", children: [s.condities.length, " voorwaarde", s.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(secties.filter((x) => x.id !== s.id)), children: _jsx(Trash2, { className: "h-4 w-4" }) })] }), uitgeklapt && (_jsx("div", { className: "border-t px-3 py-3", children: _jsx(PakbonSectieEditor, { sectie: s, methode: methode, onChange: (patch) => update(s.id, patch) }) }))] }, s.id));
54
+ }), secties.length === 0 && (_jsx("p", { className: "text-sm text-muted-foreground", children: "Nog geen secties toegevoegd." }))] })] }));
55
+ }
56
+ function OptieVeld({ def, waarde, onChange, }) {
57
+ if (def.type === "schakelaar") {
58
+ 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 })] }));
59
+ }
60
+ if (def.type === "keuze") {
61
+ 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))) })] })] }));
62
+ }
63
+ if (def.type === "meerkeuze") {
64
+ const gekozen = Array.isArray(waarde) ? waarde : [];
65
+ 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))) })] }));
66
+ }
67
+ if (def.type === "getal") {
68
+ 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)) })] }));
69
+ }
70
+ if (def.type === "kleur") {
71
+ return (_jsxs("div", { className: "space-y-1", children: [_jsx(Label, { className: "text-xs", children: def.label }), _jsx(Input, { type: "color", value: String(waarde ?? "#ffffff"), onChange: (e) => onChange(e.target.value) })] }));
72
+ }
73
+ 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 })] }));
74
+ }
75
+ function KolommenEditor({ kolommen, onChange, }) {
76
+ const [nieuw, setNieuw] = useState(BESCHIKBARE_KOLOMMEN[0].key);
77
+ const zet = (i, patch) => onChange(kolommen.map((k, idx) => (idx === i ? { ...k, ...patch } : k)));
78
+ const verplaats = (i, r) => {
79
+ const d = i + r;
80
+ if (d < 0 || d >= kolommen.length)
81
+ return;
82
+ const kopie = [...kolommen];
83
+ [kopie[i], kopie[d]] = [kopie[d], kopie[i]];
84
+ onChange(kopie);
85
+ };
86
+ 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: "Lettertype" }), _jsxs(Select, { value: k.fontfamilie ?? "Helvetica", onValueChange: (v) => zet(i, { fontfamilie: v }), children: [_jsx(SelectTrigger, { className: "h-8", children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: ["Helvetica", "Arial", "Times New Roman", "Courier New", "Verdana"].map((f) => (_jsx(SelectItem, { value: f, children: f }, f))) })] })] }), _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 }) })] }), _jsxs("div", { className: "space-y-1", children: [_jsx(Label, { className: "text-[10px]", children: "Achtergrond" }), _jsx(Input, { className: "h-8", type: "color", value: k.achtergrond ?? "#ffffff", onChange: (e) => zet(i, { achtergrond: e.target.value }) })] })] }), _jsxs("div", { className: "flex gap-4", children: [_jsxs("label", { className: "flex items-center gap-2 text-xs", children: [_jsx(Switch, { checked: !!k.vet, onCheckedChange: (v) => zet(i, { vet: v }) }), " Vet"] }), _jsxs("label", { className: "flex items-center gap-2 text-xs", children: [_jsx(Switch, { checked: !!k.rand, onCheckedChange: (v) => zet(i, { rand: v }) }), " Rand"] })] })] }, `${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: BESCHIKBARE_KOLOMMEN.map((k) => (_jsx(SelectItem, { value: k.key, children: k.label }, k.key))) })] }), _jsx(Button, { size: "sm", variant: "outline", onClick: () => onChange([...kolommen, nieuweKolom(nieuw)]), children: _jsx(Plus, { className: "h-4 w-4" }) })] })] }));
87
+ }
88
+ function CondityEditor({ condities, onChange, }) {
89
+ 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_1fr_auto] items-center gap-1", 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, {}) }), _jsx(SelectContent, { children: CONDITIE_VELDEN.map((v) => (_jsx(SelectItem, { value: v.value, children: v.label }, v.value))) })] }), _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-20", children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: ["=", "!=", ">", "<", "bevat", "leeg", "niet_leeg"].map((o) => (_jsx(SelectItem, { value: o, children: o }, o))) })] }), _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: "aantal_karren", operator: ">", waarde: "1" }]), children: [_jsx(Plus, { className: "mr-2 h-4 w-4" }), " Voorwaarde"] })] }));
90
+ }
91
+ /** Volledige instellingen van één blok — herbruikt in de blokontwerper. */
92
+ export function PakbonSectieEditor({ sectie, onChange, methode = "kar", }) {
93
+ const s = sectie;
94
+ const def = sectieDef(s.type);
95
+ const bundelAan = s.type === "karren" && (s.opties?.bundel_identiek ?? true);
96
+ const aangepasteOpties = def.opties.map((o) => {
97
+ if (s.type === "karren" && o.key === "velden" && bundelAan) {
98
+ return {
99
+ ...o,
100
+ opties: (o.opties ?? []).filter((x) => x.value !== "karnummer"),
101
+ };
102
+ }
103
+ // Zonder karindeling zijn totalen van karren en lagen niet van toepassing.
104
+ if (s.type === "totalen" && o.key === "velden" && methode === "fusten") {
105
+ return {
106
+ ...o,
107
+ opties: (o.opties ?? []).filter((x) => !["karren", "lagen"].includes(x.value)),
108
+ };
109
+ }
110
+ return o;
111
+ });
112
+ 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: "Titel" }), _jsx(Input, { value: s.titel, onChange: (e) => onChange({ titel: e.target.value }) })] }), s.type !== "pickmethode" && (_jsxs("div", { className: "flex items-center justify-between rounded border px-3 py-2", children: [_jsx("span", { className: "text-sm", children: "Sectie tonen" }), _jsx(Switch, { checked: s.zichtbaar, onCheckedChange: (v) => onChange({ zichtbaar: v }) })] })), s.type === "logo" && (_jsx(LogoKiezer, { logoId: typeof s.opties?.logoId === "string" ? s.opties.logoId : null, onKies: (logo) => onChange({
113
+ opties: {
114
+ ...s.opties,
115
+ logoId: logo?.id ?? null,
116
+ logoNaam: logo?.naam ?? null,
117
+ },
118
+ }) })), aangepasteOpties.map((o) => {
119
+ const isVelden = o.key === "velden";
120
+ const basisWaarde = s.opties?.[o.key] ?? o.standaard;
121
+ const waarde = isVelden && bundelAan
122
+ ? (Array.isArray(basisWaarde) ? basisWaarde : []).filter((v) => v !== "karnummer")
123
+ : basisWaarde;
124
+ return (_jsx(OptieVeld, { def: o, waarde: waarde, onChange: (v) => {
125
+ if (o.key === "bundel_identiek" && v === true) {
126
+ const huidigeVelden = Array.isArray(s.opties?.velden)
127
+ ? s.opties.velden
128
+ : [];
129
+ onChange({
130
+ opties: {
131
+ ...s.opties,
132
+ bundel_identiek: true,
133
+ velden: huidigeVelden.filter((x) => x !== "karnummer"),
134
+ },
135
+ });
136
+ }
137
+ else {
138
+ onChange({ opties: { ...s.opties, [o.key]: v } });
139
+ }
140
+ } }, o.key));
141
+ }), def.kolommen && (_jsx(KolommenEditor, { kolommen: Array.isArray(s.kolommen) ? s.kolommen : [], onChange: (k) => onChange({ kolommen: k }) })), _jsx(Separator, {}), _jsx(CondityEditor, { condities: s.condities ?? [], onChange: (c) => onChange({ condities: c }) })] }));
142
+ }
143
+ /** Kaderinstellingen (breedte, uitlijning, marges, opmaak) van één blok. */
144
+ export function PakbonBlokKaderEditor({ sectie, onChange, }) {
145
+ const o = sectie.opties ?? {};
146
+ const zet = (key, waarde) => onChange({ opties: { ...o, [key]: waarde } });
147
+ const num = (key, fallback) => Number(o[key] ?? fallback);
148
+ return (_jsxs("div", { className: "grid grid-cols-2 gap-3", children: [_jsxs("div", { className: "space-y-1", children: [_jsx(Label, { className: "text-[10px]", children: "Breedte %" }), _jsx(Input, { className: "h-8", type: "number", value: num("blok_breedte", 100), onChange: (e) => zet("blok_breedte", Number(e.target.value)) })] }), _jsxs("div", { className: "space-y-1", children: [_jsx(Label, { className: "text-[10px]", children: "Uitlijning blok" }), _jsxs(Select, { value: String(o.blok_uitlijning ?? "left"), onValueChange: (v) => zet("blok_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: "Marge boven/onder (mm)" }), _jsx(Input, { className: "h-8", type: "number", value: num("blok_marge", 0), onChange: (e) => zet("blok_marge", Number(e.target.value)) })] }), _jsxs("div", { className: "space-y-1", children: [_jsx(Label, { className: "text-[10px]", children: "Binnenruimte (mm)" }), _jsx(Input, { className: "h-8", type: "number", value: num("blok_padding", 0), onChange: (e) => zet("blok_padding", Number(e.target.value)) })] }), _jsxs("div", { className: "space-y-1", children: [_jsx(Label, { className: "text-[10px]", children: "Achtergrond" }), _jsx(Input, { className: "h-8", type: "color", value: String(o.blok_achtergrond ?? "#ffffff"), onChange: (e) => zet("blok_achtergrond", e.target.value) })] }), _jsxs("div", { className: "space-y-1", children: [_jsx(Label, { className: "text-[10px]", children: "Randkleur" }), _jsx(Input, { className: "h-8", type: "color", value: String(o.blok_randkleur ?? "#e5e7eb"), onChange: (e) => zet("blok_randkleur", e.target.value) })] }), _jsxs("div", { className: "space-y-1", children: [_jsx(Label, { className: "text-[10px]", children: "Hoekafronding (mm)" }), _jsx(Input, { className: "h-8", type: "number", value: num("blok_radius", 0), onChange: (e) => zet("blok_radius", Number(e.target.value)) })] }), _jsxs("div", { className: "space-y-1", children: [_jsx(Label, { className: "text-[10px]", children: "Tekstschaal %" }), _jsx(Input, { className: "h-8", type: "number", value: num("blok_tekstschaal", 100), onChange: (e) => zet("blok_tekstschaal", Number(e.target.value)) })] }), _jsxs("div", { className: "col-span-2 flex items-center justify-between rounded border px-3 py-2", children: [_jsx("span", { className: "text-sm", children: "Rand om blok" }), _jsx(Switch, { checked: !!o.blok_rand, onCheckedChange: (v) => zet("blok_rand", v) })] })] }));
149
+ }
150
+ /** Kiest een logo uit de centrale Logo Bibliotheek voor een logo-sectie. */
151
+ function LogoKiezer({ logoId, onKies, }) {
152
+ const { logos } = useLogos();
153
+ const [open, setOpen] = useState(false);
154
+ const logo = logos.find((l) => l.id === logoId) ?? null;
155
+ return (_jsxs("div", { className: "space-y-1", children: [_jsx(Label, { className: "text-xs", children: "Logo" }), _jsxs("div", { className: "flex items-center gap-2 rounded border px-3 py-2", children: [logo ? (_jsx(LogoThumbnail, { logo: logo })) : (_jsx("div", { className: "flex h-12 w-16 items-center justify-center rounded border border-dashed", children: _jsx(ImagePlus, { className: "h-4 w-4 text-muted-foreground" }) })), _jsx("div", { className: "flex-1 truncate text-sm", children: logo ? logo.naam : _jsx("span", { className: "text-muted-foreground", children: "Geen logo gekozen" }) }), _jsx(Button, { size: "sm", variant: "outline", onClick: () => setOpen(true), children: "Kiezen" }), logo && (_jsx(Button, { size: "icon", variant: "ghost", className: "h-8 w-8", onClick: () => onKies(null), children: _jsx(Trash2, { className: "h-4 w-4" }) }))] }), _jsx(LogoBibliotheekDialog, { open: open, onOpenChange: setOpen, onKies: (l) => onKies(l) })] }));
156
+ }
@@ -0,0 +1,9 @@
1
+ import { type ReactNode } from "react";
2
+ /**
3
+ * Schaalt het voorbeelddocument zo dat het volledig binnen het beschikbare
4
+ * kader past — zonder scrollbalken.
5
+ */
6
+ export declare function PreviewFit({ children }: {
7
+ children: ReactNode;
8
+ }): import("react").JSX.Element;
9
+ //# sourceMappingURL=PreviewFit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PreviewFit.d.ts","sourceRoot":"","sources":["../../src/components/PreviewFit.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAgD,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAErF;;;GAGG;AACH,wBAAgB,UAAU,CAAC,EAAE,QAAQ,EAAE,EAAE;IAAE,QAAQ,EAAE,SAAS,CAAA;CAAE,+BAwC/D"}
@@ -0,0 +1,38 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useEffect, useLayoutEffect, useRef, useState } from "react";
3
+ /**
4
+ * Schaalt het voorbeelddocument zo dat het volledig binnen het beschikbare
5
+ * kader past — zonder scrollbalken.
6
+ */
7
+ export function PreviewFit({ children }) {
8
+ const buiten = useRef(null);
9
+ const binnen = useRef(null);
10
+ const [schaal, setSchaal] = useState(1);
11
+ const meet = () => {
12
+ const b = buiten.current;
13
+ const i = binnen.current;
14
+ if (!b || !i)
15
+ return;
16
+ const bw = b.clientWidth;
17
+ const bh = b.clientHeight;
18
+ const iw = i.scrollWidth;
19
+ const ih = i.scrollHeight;
20
+ if (!iw || !ih || !bw || !bh)
21
+ return;
22
+ const s = Math.min(bw / iw, bh / ih, 1);
23
+ setSchaal(s > 0 ? s : 1);
24
+ };
25
+ useLayoutEffect(meet);
26
+ useEffect(() => {
27
+ const ro = new ResizeObserver(() => meet());
28
+ if (buiten.current)
29
+ ro.observe(buiten.current);
30
+ if (binnen.current)
31
+ ro.observe(binnen.current);
32
+ return () => ro.disconnect();
33
+ }, []);
34
+ return (_jsx("div", { ref: buiten, className: "flex h-full w-full items-start justify-center overflow-hidden", children: _jsx("div", { ref: binnen, style: {
35
+ transform: `scale(${schaal})`,
36
+ transformOrigin: "top center",
37
+ }, children: children }) }));
38
+ }
@@ -0,0 +1,2 @@
1
+ export declare function PrintQueuePage(): import("react").JSX.Element;
2
+ //# sourceMappingURL=PrintQueuePage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PrintQueuePage.d.ts","sourceRoot":"","sources":["../../src/components/PrintQueuePage.tsx"],"names":[],"mappings":"AAmCA,wBAAgB,cAAc,gCA8K7B"}
@@ -0,0 +1,59 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useMemo, useState } from "react";
3
+ import { Badge, Button, Card, CardContent, CardDescription, CardHeader, CardTitle, Input, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, Table, TableBody, TableCell, TableHead, TableHeader, TableRow, toast, } from "@flowselections/core";
4
+ import { RefreshCw, RotateCcw, Search, XCircle } from "lucide-react";
5
+ import { maakPrintJob, updateJobStatus, useLayouts, usePrinters, usePrintJobs } from "../lib/useDocumentData";
6
+ import { documentTypeLabel } from "../lib/documentTypes";
7
+ const STATUS_VARIANT = {
8
+ wachtend: "outline",
9
+ bezig: "secondary",
10
+ voltooid: "default",
11
+ mislukt: "destructive",
12
+ geannuleerd: "outline",
13
+ };
14
+ export function PrintQueuePage() {
15
+ const { jobs, loading, reload } = usePrintJobs();
16
+ const { printers } = usePrinters();
17
+ const { layouts } = useLayouts();
18
+ const [zoek, setZoek] = useState("");
19
+ const [status, setStatus] = useState("alle");
20
+ const printerNaam = useMemo(() => new Map(printers.map((p) => [p.id, p.naam])), [printers]);
21
+ const layoutNaam = useMemo(() => new Map(layouts.map((l) => [l.id, l.naam])), [layouts]);
22
+ const gefilterd = jobs.filter((j) => {
23
+ const q = zoek.trim().toLowerCase();
24
+ if (q && !`${j.documentnaam ?? ""} ${j.gebruiker_naam ?? ""}`.toLowerCase().includes(q))
25
+ return false;
26
+ if (status !== "alle" && j.status !== status)
27
+ return false;
28
+ return true;
29
+ });
30
+ const tellingen = useMemo(() => {
31
+ const t = { wachtend: 0, bezig: 0, voltooid: 0, mislukt: 0, geannuleerd: 0 };
32
+ for (const j of jobs)
33
+ t[j.status] = (t[j.status] ?? 0) + 1;
34
+ return t;
35
+ }, [jobs]);
36
+ return (_jsxs("main", { className: "space-y-6 p-6", children: [_jsxs("div", { className: "flex flex-wrap items-end justify-between gap-4", children: [_jsxs("div", { children: [_jsx("h1", { className: "text-xl font-semibold", children: "Printwachtrij" }), _jsx("p", { className: "text-sm text-muted-foreground", children: "Volg printopdrachten, herstart mislukte prints en print documenten opnieuw." })] }), _jsxs(Button, { variant: "outline", onClick: () => reload(), children: [_jsx(RefreshCw, { className: "mr-2 h-4 w-4" }), " Vernieuwen"] })] }), _jsx("div", { className: "grid gap-4 md:grid-cols-4", children: ["wachtend", "bezig", "voltooid", "mislukt"].map((s) => (_jsx(Card, { children: _jsxs(CardHeader, { className: "pb-2", children: [_jsx(CardDescription, { className: "capitalize", children: s }), _jsx(CardTitle, { className: "text-2xl", children: tellingen[s] ?? 0 })] }) }, s))) }), _jsxs(Card, { children: [_jsxs(CardHeader, { className: "pb-3", children: [_jsx(CardTitle, { className: "text-base", children: "Opdrachten" }), _jsx(CardDescription, { children: loading ? "Laden…" : `${gefilterd.length} opdrachten` })] }), _jsxs(CardContent, { className: "space-y-4", children: [_jsxs("div", { className: "grid gap-3 md:grid-cols-4", children: [_jsxs("div", { className: "relative md:col-span-2", children: [_jsx(Search, { className: "absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" }), _jsx(Input, { className: "pl-9", placeholder: "Zoek op document of gebruiker", value: zoek, onChange: (e) => setZoek(e.target.value) })] }), _jsxs(Select, { value: status, onValueChange: setStatus, children: [_jsx(SelectTrigger, { children: _jsx(SelectValue, {}) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "alle", children: "Alle statussen" }), ["wachtend", "bezig", "voltooid", "mislukt", "geannuleerd"].map((s) => (_jsx(SelectItem, { value: s, children: s }, s)))] })] })] }), _jsxs(Table, { children: [_jsx(TableHeader, { children: _jsxs(TableRow, { children: [_jsx(TableHead, { children: "Document" }), _jsx(TableHead, { children: "Layout" }), _jsx(TableHead, { children: "Printer" }), _jsx(TableHead, { children: "Status" }), _jsx(TableHead, { children: "Tijdstip" }), _jsx(TableHead, { className: "text-right", children: "Acties" })] }) }), _jsxs(TableBody, { children: [gefilterd.length === 0 && (_jsx(TableRow, { children: _jsx(TableCell, { colSpan: 6, className: "py-10 text-center text-sm text-muted-foreground", children: "Geen printopdrachten." }) })), gefilterd.map((j) => (_jsxs(TableRow, { children: [_jsxs(TableCell, { children: [_jsx("span", { className: "font-medium", children: j.documentnaam ?? documentTypeLabel(j.documenttype) }), j.is_herprint && (_jsx(Badge, { variant: "outline", className: "ml-2", children: "herprint" })), j.foutmelding && (_jsx("span", { className: "block text-xs text-destructive", children: j.foutmelding }))] }), _jsx(TableCell, { className: "text-sm text-muted-foreground", children: j.layout_id ? layoutNaam.get(j.layout_id) ?? "—" : "—" }), _jsx(TableCell, { className: "text-sm text-muted-foreground", children: j.printer_id ? printerNaam.get(j.printer_id) ?? "—" : "—" }), _jsx(TableCell, { children: _jsx(Badge, { variant: STATUS_VARIANT[j.status] ?? "outline", children: j.status }) }), _jsx(TableCell, { className: "text-sm text-muted-foreground", children: new Date(j.created_at).toLocaleString("nl-NL") }), _jsx(TableCell, { className: "text-right", children: _jsxs("div", { className: "flex justify-end gap-1", children: [_jsxs(Button, { variant: "ghost", size: "sm", onClick: async () => {
37
+ if (!j.layout_id)
38
+ return;
39
+ try {
40
+ await maakPrintJob({
41
+ layout_id: j.layout_id,
42
+ printer_id: j.printer_id,
43
+ documenttype: j.documenttype,
44
+ documentnaam: j.documentnaam ?? "Herprint",
45
+ kopieen: j.kopieen ?? 1,
46
+ is_herprint: true,
47
+ });
48
+ toast.success("Opnieuw in de wachtrij gezet");
49
+ void reload();
50
+ }
51
+ catch {
52
+ toast.error("Herprint mislukt");
53
+ }
54
+ }, children: [_jsx(RotateCcw, { className: "mr-2 h-4 w-4" }), " Opnieuw"] }), (j.status === "wachtend" || j.status === "bezig") && (_jsx(Button, { variant: "ghost", size: "sm", onClick: async () => {
55
+ await updateJobStatus(j.id, "geannuleerd");
56
+ toast.success("Opdracht geannuleerd");
57
+ void reload();
58
+ }, children: _jsx(XCircle, { className: "h-4 w-4" }) }))] }) })] }, j.id)))] })] })] })] })] }));
59
+ }
@@ -0,0 +1,6 @@
1
+ export declare function PrinterSetupWizard({ open, onOpenChange, onKlaar, }: {
2
+ open: boolean;
3
+ onOpenChange: (v: boolean) => void;
4
+ onKlaar?: () => void;
5
+ }): import("react").JSX.Element;
6
+ //# sourceMappingURL=PrinterSetupWizard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PrinterSetupWizard.d.ts","sourceRoot":"","sources":["../../src/components/PrinterSetupWizard.tsx"],"names":[],"mappings":"AA6DA,wBAAgB,kBAAkB,CAAC,EACjC,IAAI,EACJ,YAAY,EACZ,OAAO,GACR,EAAE;IACD,IAAI,EAAE,OAAO,CAAC;IACd,YAAY,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IACnC,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;CACtB,+BAuNA"}
@@ -0,0 +1,119 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ // ============================================================================
3
+ // src/components/PrinterSetupWizard.tsx — eenvoudige setupwizard voor printers
4
+ // ============================================================================
5
+ import { useEffect, useState } from "react";
6
+ import { Button, Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, Input, Label, toast, } from "@flowselections/core";
7
+ import { Check, CheckCircle2, FileText, Tag } from "lucide-react";
8
+ import { supabase } from "../lib/supabaseClient";
9
+ import { DOCUMENT_PROFIELEN } from "../lib/documentProfiles";
10
+ import { zetFormaatPrinter } from "../lib/formaatPrinters";
11
+ import { drukAf } from "../lib/browserPrint";
12
+ const STAPPEN = ["Printer", "Documenten", "Klaar"];
13
+ /** Vertaalt een simpele keuze naar de technische printerinstellingen. */
14
+ function instellingenVoor(gebruik) {
15
+ return gebruik === "etiket"
16
+ ? { soort: "labelprinter", papierformaat: "label" }
17
+ : { soort: "laserprinter", papierformaat: "A4" };
18
+ }
19
+ function Stappenbalk({ stap }) {
20
+ return (_jsx("div", { className: "flex items-center gap-2", children: STAPPEN.map((s, i) => (_jsxs("div", { className: "flex flex-1 items-center gap-2", children: [_jsx("div", { className: `flex h-7 w-7 shrink-0 items-center justify-center rounded-full text-xs font-medium ${i < stap
21
+ ? "bg-primary text-primary-foreground"
22
+ : i === stap
23
+ ? "border-primary text-primary border-2"
24
+ : "bg-muted text-muted-foreground"}`, children: i < stap ? _jsx(Check, { className: "h-4 w-4" }) : i + 1 }), _jsx("span", { className: `text-sm ${i === stap ? "text-foreground font-medium" : "text-muted-foreground"}`, children: s }), i < STAPPEN.length - 1 && _jsx("div", { className: "bg-border h-px flex-1" })] }, s))) }));
25
+ }
26
+ export function PrinterSetupWizard({ open, onOpenChange, onKlaar, }) {
27
+ const [stap, setStap] = useState(0);
28
+ const [bezig, setBezig] = useState(false);
29
+ const [printernaam, setPrinternaam] = useState("");
30
+ const [systeemnaam, setSysteemnaam] = useState("");
31
+ const [gebruik, setGebruik] = useState("papier");
32
+ const [printerId, setPrinterId] = useState(null);
33
+ const [keuzes, setKeuzes] = useState({});
34
+ const resetAlles = () => {
35
+ setStap(0);
36
+ setPrinternaam("");
37
+ setSysteemnaam("");
38
+ setGebruik("papier");
39
+ setPrinterId(null);
40
+ setKeuzes({});
41
+ };
42
+ useEffect(() => {
43
+ if (!open)
44
+ resetAlles();
45
+ }, [open]);
46
+ const printerOpslaan = async () => {
47
+ const naam = printernaam.trim() || systeemnaam.trim();
48
+ if (!naam) {
49
+ toast.error("Geef de printer een naam");
50
+ return;
51
+ }
52
+ setBezig(true);
53
+ try {
54
+ const { data, error } = await supabase
55
+ .from("printers")
56
+ .insert({
57
+ naam,
58
+ actief: true,
59
+ systeemnaam: systeemnaam.trim() || naam,
60
+ ...instellingenVoor(gebruik),
61
+ })
62
+ .select("id")
63
+ .single();
64
+ if (error)
65
+ throw error;
66
+ setPrinterId(data?.id ?? null);
67
+ setStap(1);
68
+ onKlaar?.();
69
+ }
70
+ catch {
71
+ toast.error("Opslaan mislukt");
72
+ }
73
+ finally {
74
+ setBezig(false);
75
+ }
76
+ };
77
+ const koppelingenOpslaan = async () => {
78
+ if (!printerId)
79
+ return;
80
+ setBezig(true);
81
+ try {
82
+ for (const [documenttype, formaatCode] of Object.entries(keuzes)) {
83
+ await zetFormaatPrinter(documenttype, formaatCode, printerId);
84
+ }
85
+ setStap(2);
86
+ }
87
+ catch {
88
+ toast.error("Opslaan van de documentkeuzes mislukt");
89
+ }
90
+ finally {
91
+ setBezig(false);
92
+ }
93
+ };
94
+ const testpaginaAfdrukken = async () => {
95
+ const naam = printernaam || systeemnaam;
96
+ await drukAf({
97
+ documenttype: "pakbon",
98
+ formaatCode: gebruik === "etiket" ? "label" : "A4",
99
+ orientatie: "portrait",
100
+ documentnaam: "Testpagina",
101
+ html: `<div style="padding:20mm;font-size:14pt"><h1 style="font-size:20pt">Testpagina</h1>
102
+ <p>Deze pagina hoort uit: <strong>${naam}</strong></p></div>`,
103
+ });
104
+ };
105
+ return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { className: "max-h-[85vh] overflow-y-auto sm:max-w-lg", children: [_jsxs(DialogHeader, { children: [_jsx(DialogTitle, { children: "Printer instellen" }), _jsx(DialogDescription, { children: "Twee korte stappen, daarna staat alles goed." })] }), _jsx(Stappenbalk, { stap: stap }), stap === 0 && (_jsxs("div", { className: "space-y-4", children: [_jsxs("div", { className: "space-y-2", children: [_jsx(Label, { children: "Naam van de printer" }), _jsx(Input, { value: printernaam, placeholder: "Bijvoorbeeld: printer magazijn", onChange: (e) => setPrinternaam(e.target.value) }), _jsx("p", { className: "text-muted-foreground text-xs", children: "Gebruik dezelfde naam als in het printvenster van deze computer, dan weet u meteen welke u moet kiezen." })] }), _jsxs("div", { className: "space-y-2", children: [_jsx(Label, { children: "Wat gaat deze printer afdrukken?" }), _jsx("div", { className: "grid grid-cols-2 gap-3", children: [
106
+ { code: "papier", label: "Papier", sub: "Gewone A4-documenten", Icon: FileText },
107
+ { code: "etiket", label: "Etiketten", sub: "Stickers en labels", Icon: Tag },
108
+ ].map((o) => (_jsxs("button", { type: "button", onClick: () => setGebruik(o.code), className: `hover:bg-accent rounded-lg border p-4 text-left transition ${gebruik === o.code ? "border-primary bg-accent" : ""}`, children: [_jsx(o.Icon, { className: "text-muted-foreground mb-2 h-5 w-5" }), _jsx("p", { className: "text-sm font-medium", children: o.label }), _jsx("p", { className: "text-muted-foreground text-xs", children: o.sub })] }, o.code))) })] }), _jsx("div", { className: "flex justify-end", children: _jsx(Button, { disabled: bezig, onClick: () => void printerOpslaan(), children: "Opslaan en verder" }) })] })), stap === 1 && (_jsxs("div", { className: "space-y-4", children: [_jsxs("div", { children: [_jsx("p", { className: "text-sm font-medium", children: "Wat drukt deze printer af?" }), _jsx("p", { className: "text-muted-foreground text-sm", children: "Kies per soort document het formaat. Daarna staan papierformaat en stand bij het afdrukken automatisch goed." })] }), _jsx("div", { className: "space-y-3", children: DOCUMENT_PROFIELEN.map((profiel) => {
109
+ const gekozen = keuzes[profiel.id];
110
+ return (_jsxs("div", { className: "rounded-lg border p-3", children: [_jsx("p", { className: "text-sm font-medium", children: profiel.label }), _jsx("div", { className: "mt-2 flex flex-wrap gap-2", children: profiel.formaten.map((f) => (_jsx("button", { type: "button", onClick: () => setKeuzes((k) => {
111
+ const n = { ...k };
112
+ if (n[profiel.id] === f.code)
113
+ delete n[profiel.id];
114
+ else
115
+ n[profiel.id] = f.code;
116
+ return n;
117
+ }), className: `hover:bg-accent rounded-md border px-3 py-1.5 text-xs transition ${gekozen === f.code ? "border-primary bg-accent" : ""}`, children: f.label }, f.code))) })] }, profiel.id));
118
+ }) }), _jsxs("div", { className: "flex justify-between", children: [_jsx(Button, { variant: "ghost", onClick: () => setStap(2), children: "Overslaan" }), _jsx(Button, { disabled: bezig, onClick: () => void koppelingenOpslaan(), children: "Opslaan en verder" })] })] })), stap === 2 && (_jsxs("div", { className: "space-y-4 text-center", children: [_jsx(CheckCircle2, { className: "text-primary mx-auto h-10 w-10" }), _jsxs("div", { children: [_jsx("p", { className: "font-medium", children: "De printer is klaar voor gebruik" }), _jsxs("p", { className: "text-muted-foreground text-sm", children: [printernaam || systeemnaam, " staat klaar. Papierformaat en stand worden voortaan automatisch ingesteld bij het afdrukken."] })] }), _jsxs("div", { className: "flex flex-wrap justify-center gap-2", children: [_jsx(Button, { variant: "outline", onClick: () => void testpaginaAfdrukken(), children: "Testpagina afdrukken" }), _jsx(Button, { variant: "ghost", onClick: resetAlles, children: "Nog een printer instellen" }), _jsx(Button, { onClick: () => onOpenChange(false), children: "Klaar" })] })] }))] }) }));
119
+ }
@@ -0,0 +1,2 @@
1
+ export declare function PrintersPage(): import("react").JSX.Element;
2
+ //# sourceMappingURL=PrintersPage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PrintersPage.d.ts","sourceRoot":"","sources":["../../src/components/PrintersPage.tsx"],"names":[],"mappings":"AAiBA,wBAAgB,YAAY,gCAoK3B"}
@@ -0,0 +1,60 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useMemo, useState } from "react";
3
+ import { Button, Card, CardContent, CardDescription, CardHeader, CardTitle, Checkbox, toast, } from "@flowselections/core";
4
+ import { Link } from "@tanstack/react-router";
5
+ import { ListChecks, Plus, Printer, Trash2 } from "lucide-react";
6
+ import { toggleKoppeling, useLayouts, usePrinters, verwijderPrinter } from "../lib/useDocumentData";
7
+ import { drukAf } from "../lib/browserPrint";
8
+ import { PrinterSetupWizard } from "./PrinterSetupWizard";
9
+ export function PrintersPage() {
10
+ const { printers, koppelingen, loading, reload } = usePrinters();
11
+ const { layouts } = useLayouts();
12
+ const [wizard, setWizard] = useState(false);
13
+ const [gekozenPrinter, setGekozenPrinter] = useState(null);
14
+ const gekoppeld = useMemo(() => {
15
+ const map = new Map();
16
+ for (const k of koppelingen) {
17
+ if (!map.has(k.printer_id))
18
+ map.set(k.printer_id, new Set());
19
+ map.get(k.printer_id).add(k.layout_id);
20
+ }
21
+ return map;
22
+ }, [koppelingen]);
23
+ const actievePrinter = printers.find((p) => p.id === gekozenPrinter) ?? null;
24
+ const testpagina = async (naam) => {
25
+ try {
26
+ await drukAf({
27
+ documenttype: "pakbon",
28
+ formaatCode: "A4",
29
+ orientatie: "portrait",
30
+ documentnaam: "Testpagina",
31
+ html: `<div style="padding:20mm;font-size:14pt"><h1 style="font-size:20pt">Testpagina</h1>
32
+ <p>Deze pagina hoort uit: <strong>${naam}</strong></p></div>`,
33
+ });
34
+ }
35
+ catch {
36
+ toast.error("De testpagina kon niet worden geopend");
37
+ }
38
+ };
39
+ return (_jsxs("main", { className: "space-y-6 p-6", children: [_jsxs("div", { className: "flex flex-wrap items-end justify-between gap-4", children: [_jsxs("div", { children: [_jsx("h1", { className: "text-xl font-semibold", children: "Printers" }), _jsx("p", { className: "text-muted-foreground text-sm", children: "Stel een printer in en kies wat erop wordt afgedrukt." })] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Button, { variant: "outline", asChild: true, children: _jsxs(Link, { to: "/layouts-printers/wachtrij", children: [_jsx(ListChecks, { className: "mr-2 h-4 w-4" }), " Printwachtrij"] }) }), _jsxs(Button, { onClick: () => setWizard(true), children: [_jsx(Plus, { className: "mr-2 h-4 w-4" }), " Printer instellen"] })] })] }), _jsxs("div", { className: "grid gap-6 lg:grid-cols-[1fr_340px]", children: [_jsxs("div", { className: "space-y-3", children: [loading && _jsx("p", { className: "text-muted-foreground text-sm", children: "Laden\u2026" }), !loading && printers.length === 0 && (_jsx(Card, { children: _jsxs(CardContent, { className: "py-12 text-center", children: [_jsx(Printer, { className: "text-muted-foreground mx-auto mb-3 h-8 w-8" }), _jsx("p", { className: "text-sm font-medium", children: "Nog geen printers" }), _jsx("p", { className: "text-muted-foreground mb-4 text-sm", children: "In drie korte stappen is uw printer klaar voor gebruik." }), _jsx(Button, { onClick: () => setWizard(true), children: "Printer instellen" })] }) })), printers.map((p) => (_jsxs("button", { type: "button", onClick: () => setGekozenPrinter(p.id), className: `hover:bg-accent/50 flex w-full items-center gap-3 rounded-lg border p-4 text-left transition ${p.id === gekozenPrinter ? "border-primary bg-accent/40" : ""}`, children: [_jsx(Printer, { className: "text-muted-foreground h-5 w-5" }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("p", { className: "truncate text-sm font-medium", children: p.naam }), _jsx("p", { className: "text-muted-foreground text-xs", children: p.locatie ? `Bij ${p.locatie}` : "Geen computer gekozen" })] }), _jsx("span", { className: `h-2.5 w-2.5 rounded-full ${p.actief ? "bg-primary" : "bg-muted-foreground/40"}` }), _jsx(Button, { variant: "ghost", size: "sm", onClick: (e) => {
40
+ e.stopPropagation();
41
+ void testpagina(p.naam);
42
+ }, children: "Testpagina" }), _jsx(Button, { variant: "ghost", size: "sm", title: "Verwijderen", onClick: async (e) => {
43
+ e.stopPropagation();
44
+ try {
45
+ await verwijderPrinter(p.id);
46
+ toast.success("Printer verwijderd");
47
+ void reload();
48
+ }
49
+ catch {
50
+ toast.error("Verwijderen mislukt");
51
+ }
52
+ }, children: _jsx(Trash2, { className: "h-4 w-4" }) })] }, p.id)))] }), _jsx("div", { className: "space-y-6", children: _jsxs(Card, { className: "h-fit", children: [_jsxs(CardHeader, { className: "pb-3", children: [_jsx(CardTitle, { className: "text-base", children: "Wat wordt hier afgedrukt?" }), _jsx(CardDescription, { children: actievePrinter ? actievePrinter.naam : "Kies eerst een printer" })] }), _jsxs(CardContent, { className: "space-y-3", children: [!actievePrinter && (_jsx("p", { className: "text-muted-foreground text-sm", children: "Nog geen printer gekozen." })), actievePrinter &&
53
+ layouts.map((l) => {
54
+ const aan = gekoppeld.get(actievePrinter.id)?.has(l.id) ?? false;
55
+ return (_jsxs("div", { className: "flex items-center gap-3", children: [_jsx(Checkbox, { checked: aan, onCheckedChange: async (v) => {
56
+ await toggleKoppeling(l.id, actievePrinter.id, !!v);
57
+ void reload();
58
+ } }), _jsxs("div", { className: "min-w-0", children: [_jsx("span", { className: "block truncate text-sm", children: l.naam }), _jsx("span", { className: "text-muted-foreground text-xs", children: l.papierformaat })] })] }, l.id));
59
+ }), actievePrinter && layouts.length === 0 && (_jsx("p", { className: "text-muted-foreground text-sm", children: "Er zijn nog geen ontwerpen." }))] })] }) })] }), _jsx(PrinterSetupWizard, { open: wizard, onOpenChange: setWizard, onKlaar: () => void reload() })] }));
60
+ }
@@ -0,0 +1,7 @@
1
+ import { type ProductiebonConfig } from "../lib/productiebonBuilder";
2
+ /** Beheer van de blokken waaruit de productiebon is opgebouwd. */
3
+ export declare function ProductiebonBlokkenPanel({ config, onChange, }: {
4
+ config: ProductiebonConfig;
5
+ onChange: (c: ProductiebonConfig) => void;
6
+ }): import("react").JSX.Element;
7
+ //# sourceMappingURL=ProductiebonBlokkenPanel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ProductiebonBlokkenPanel.d.ts","sourceRoot":"","sources":["../../src/components/ProductiebonBlokkenPanel.tsx"],"names":[],"mappings":"AAgBA,OAAO,EAaL,KAAK,kBAAkB,EAGxB,MAAM,4BAA4B,CAAC;AAEpC,kEAAkE;AAClE,wBAAgB,wBAAwB,CAAC,EACvC,MAAM,EACN,QAAQ,GACT,EAAE;IACD,MAAM,EAAE,kBAAkB,CAAC;IAC3B,QAAQ,EAAE,CAAC,CAAC,EAAE,kBAAkB,KAAK,IAAI,CAAC;CAC3C,+BAkGA"}