@flowselections/layout-maker 1.38.4 → 1.45.9

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 (110) hide show
  1. package/dist-lib/components/AfleverbriefBuilderPage.d.ts +10 -0
  2. package/dist-lib/components/AfleverbriefBuilderPage.d.ts.map +1 -0
  3. package/dist-lib/components/AfleverbriefBuilderPage.js +101 -0
  4. package/dist-lib/components/BlokOntwerperPage.d.ts.map +1 -1
  5. package/dist-lib/components/BlokOntwerperPage.js +8 -3
  6. package/dist-lib/components/ComputersLijst.d.ts.map +1 -1
  7. package/dist-lib/components/ComputersLijst.js +1 -1
  8. package/dist-lib/components/FustKolomVolgorde.d.ts +11 -0
  9. package/dist-lib/components/FustKolomVolgorde.d.ts.map +1 -0
  10. package/dist-lib/components/FustKolomVolgorde.js +52 -0
  11. package/dist-lib/components/LayoutsPage.d.ts.map +1 -1
  12. package/dist-lib/components/LayoutsPage.js +10 -9
  13. package/dist-lib/components/PakbonBuilderPage.d.ts.map +1 -1
  14. package/dist-lib/components/PakbonBuilderPage.js +32 -14
  15. package/dist-lib/components/PakbonPreview.d.ts.map +1 -1
  16. package/dist-lib/components/PakbonPreview.js +21 -14
  17. package/dist-lib/components/PakbonSectiesPanel.d.ts +4 -2
  18. package/dist-lib/components/PakbonSectiesPanel.d.ts.map +1 -1
  19. package/dist-lib/components/PakbonSectiesPanel.js +5 -3
  20. package/dist-lib/components/PdfMapToestemmingBanner.d.ts +2 -1
  21. package/dist-lib/components/PdfMapToestemmingBanner.d.ts.map +1 -1
  22. package/dist-lib/components/PdfMapToestemmingBanner.js +124 -18
  23. package/dist-lib/components/PdfVoorbeeld.d.ts +20 -0
  24. package/dist-lib/components/PdfVoorbeeld.d.ts.map +1 -0
  25. package/dist-lib/components/PdfVoorbeeld.js +95 -0
  26. package/dist-lib/components/PrintProgrammaUitleg.d.ts.map +1 -1
  27. package/dist-lib/components/PrintProgrammaUitleg.js +1 -1
  28. package/dist-lib/components/PrintQueuePage.d.ts.map +1 -1
  29. package/dist-lib/components/PrintQueuePage.js +81 -43
  30. package/dist-lib/components/PrintplekDetailPage.d.ts.map +1 -1
  31. package/dist-lib/components/PrintplekDetailPage.js +2 -2
  32. package/dist-lib/components/PrintplekWizard.d.ts.map +1 -1
  33. package/dist-lib/components/PrintplekWizard.js +6 -2
  34. package/dist-lib/components/ProductiebonBuilderPage.d.ts.map +1 -1
  35. package/dist-lib/components/ProductiebonBuilderPage.js +13 -7
  36. package/dist-lib/index.d.ts +13 -5
  37. package/dist-lib/index.d.ts.map +1 -1
  38. package/dist-lib/index.js +10 -5
  39. package/dist-lib/integrations/supabase/auth-middleware.d.ts +2280 -198
  40. package/dist-lib/integrations/supabase/auth-middleware.d.ts.map +1 -1
  41. package/dist-lib/integrations/supabase/client.server.d.ts +2280 -198
  42. package/dist-lib/integrations/supabase/client.server.d.ts.map +1 -1
  43. package/dist-lib/integrations/supabase/client.server.js +1 -1
  44. package/dist-lib/integrations/supabase/moduleClient.d.ts +2280 -198
  45. package/dist-lib/integrations/supabase/moduleClient.d.ts.map +1 -1
  46. package/dist-lib/integrations/supabase/types.d.ts +2306 -182
  47. package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
  48. package/dist-lib/integrations/supabase/types.js +1 -0
  49. package/dist-lib/lib/aiLayout.functions.d.ts +2280 -198
  50. package/dist-lib/lib/aiLayout.functions.d.ts.map +1 -1
  51. package/dist-lib/lib/documentVoorbeeld.functions.d.ts +23 -0
  52. package/dist-lib/lib/documentVoorbeeld.functions.d.ts.map +1 -0
  53. package/dist-lib/lib/documentVoorbeeld.functions.js +37 -0
  54. package/dist-lib/lib/fontMetrics.d.ts +15 -0
  55. package/dist-lib/lib/fontMetrics.d.ts.map +1 -0
  56. package/dist-lib/lib/fontMetrics.js +118 -0
  57. package/dist-lib/lib/htmlNaarPdf.server.d.ts +2 -2
  58. package/dist-lib/lib/htmlNaarPdf.server.d.ts.map +1 -1
  59. package/dist-lib/lib/htmlNaarPdf.server.js +2 -1
  60. package/dist-lib/lib/htmlPdf.server.d.ts +11 -0
  61. package/dist-lib/lib/htmlPdf.server.d.ts.map +1 -1
  62. package/dist-lib/lib/htmlPdf.server.js +118 -15
  63. package/dist-lib/lib/orderData.server.d.ts.map +1 -1
  64. package/dist-lib/lib/orderData.server.js +43 -16
  65. package/dist-lib/lib/pakbonBarcodeVerzoek.d.ts +39 -0
  66. package/dist-lib/lib/pakbonBarcodeVerzoek.d.ts.map +1 -0
  67. package/dist-lib/lib/pakbonBarcodeVerzoek.js +80 -0
  68. package/dist-lib/lib/pakbonBuilder.d.ts +62 -3
  69. package/dist-lib/lib/pakbonBuilder.d.ts.map +1 -1
  70. package/dist-lib/lib/pakbonBuilder.js +386 -50
  71. package/dist-lib/lib/pakbonContent.d.ts +41 -0
  72. package/dist-lib/lib/pakbonContent.d.ts.map +1 -0
  73. package/dist-lib/lib/pakbonContent.js +668 -0
  74. package/dist-lib/lib/pakbonDocument.d.ts +199 -0
  75. package/dist-lib/lib/pakbonDocument.d.ts.map +1 -0
  76. package/dist-lib/lib/pakbonDocument.js +735 -0
  77. package/dist-lib/lib/pakbonHtml.d.ts +7 -0
  78. package/dist-lib/lib/pakbonHtml.d.ts.map +1 -1
  79. package/dist-lib/lib/pakbonHtml.js +89 -73
  80. package/dist-lib/lib/pakbonPresentation.d.ts +8 -0
  81. package/dist-lib/lib/pakbonPresentation.d.ts.map +1 -0
  82. package/dist-lib/lib/pakbonPresentation.js +31 -0
  83. package/dist-lib/lib/pakbonScan.d.ts.map +1 -1
  84. package/dist-lib/lib/pakbonScan.js +20 -38
  85. package/dist-lib/lib/pdfJobSaver.d.ts +10 -6
  86. package/dist-lib/lib/pdfJobSaver.d.ts.map +1 -1
  87. package/dist-lib/lib/pdfJobSaver.js +302 -99
  88. package/dist-lib/lib/pdfMap.d.ts +37 -2
  89. package/dist-lib/lib/pdfMap.d.ts.map +1 -1
  90. package/dist-lib/lib/pdfMap.js +120 -7
  91. package/dist-lib/lib/pdfOpslagStatus.d.ts +23 -0
  92. package/dist-lib/lib/pdfOpslagStatus.d.ts.map +1 -0
  93. package/dist-lib/lib/pdfOpslagStatus.js +74 -0
  94. package/dist-lib/lib/printApi.d.ts +31 -0
  95. package/dist-lib/lib/printApi.d.ts.map +1 -1
  96. package/dist-lib/lib/printApi.js +62 -1
  97. package/dist-lib/lib/printQueue.server.d.ts +6 -0
  98. package/dist-lib/lib/printQueue.server.d.ts.map +1 -1
  99. package/dist-lib/lib/printQueue.server.js +450 -94
  100. package/dist-lib/lib/printService.functions.d.ts +8857 -529
  101. package/dist-lib/lib/printService.functions.d.ts.map +1 -1
  102. package/dist-lib/lib/profielHtml.d.ts.map +1 -1
  103. package/dist-lib/lib/profielHtml.js +8 -1
  104. package/dist-lib/lib/useDocumentData.d.ts +8 -0
  105. package/dist-lib/lib/useDocumentData.d.ts.map +1 -1
  106. package/dist-lib/lib/useDocumentData.js +1 -1
  107. package/dist-lib/lib/voorbeeldPrint.d.ts +3 -0
  108. package/dist-lib/lib/voorbeeldPrint.d.ts.map +1 -1
  109. package/dist-lib/lib/voorbeeldPrint.js +65 -17
  110. package/package.json +9 -2
@@ -10,16 +10,63 @@ import { Button, toast } from "@flowselections/core";
10
10
  import { FolderCheck, FolderOpen, ShieldAlert, ShieldCheck } from "lucide-react";
11
11
  import { usePrinters } from "../lib/useDocumentData.js";
12
12
  import { zorgVoorPdfPrinter } from "../lib/pdfPrinter.js";
13
- import { draaitInIframe, herstelPdfMapToestemming, kiesPdfMap, mapKiezenMogelijk, openInEigenTabblad, pdfMapNaam, pdfMapToestemming, } from "../lib/pdfMap.js";
14
- export function PdfMapToestemmingBanner({ alleenActieNodig = false, zwevend = false, } = {}) {
13
+ import { supabase } from "../lib/supabaseClient.js";
14
+ import { draaitInIframe, herstelPdfMapToestemming, kiesPdfMap, mapKiezenMogelijk, openInEigenTabblad, lokalePdfPrintplek, pdfMapKoppeling, pdfMapNaam, pdfMapToestemming, } from "../lib/pdfMap.js";
15
+ export function PdfMapToestemmingBanner({ alleenActieNodig = false, zwevend = false, printplekId, } = {}) {
15
16
  const { printers, reload } = usePrinters();
16
17
  const pdfPrinter = printers.find((p) => p.afdrukmethode === "pdf");
18
+ // De balk is alleen nuttig als "Opslaan als PDF" nog ergens als
19
+ // afdrukmethode is ingesteld (printplek-regel, layoutkoppeling of
20
+ // formaatregel). Zodra nergens meer een PDF-printer is gekozen, verdwijnt
21
+ // de balk en wordt de virtuele printer ook niet meer vanzelf aangemaakt.
22
+ const [inGebruik, setInGebruik] = useState(null);
23
+ const meetGebruik = useCallback(async (printerId) => {
24
+ const tabellen = [
25
+ supabase.from("document_printplek_printers").select("print_plek_id").eq("printer_id", printerId).limit(1),
26
+ supabase.from("printer_mappings").select("layout_id").eq("printer_id", printerId).limit(1),
27
+ supabase.from("document_formaat_printers").select("documenttype").eq("printer_id", printerId).limit(1),
28
+ ];
29
+ try {
30
+ const resultaten = await Promise.all(tabellen);
31
+ return resultaten.some((r) => Array.isArray(r.data) && r.data.length > 0);
32
+ }
33
+ catch {
34
+ // Bij een leesfout liever tonen dan stil verbergen.
35
+ return true;
36
+ }
37
+ }, []);
38
+ useEffect(() => {
39
+ if (!pdfPrinter) {
40
+ setInGebruik(null);
41
+ return;
42
+ }
43
+ let actief = true;
44
+ void meetGebruik(pdfPrinter.id).then((gebruikt) => {
45
+ if (actief)
46
+ setInGebruik(gebruikt);
47
+ });
48
+ return () => {
49
+ actief = false;
50
+ };
51
+ }, [pdfPrinter?.id, meetGebruik]); // eslint-disable-line react-hooks/exhaustive-deps
17
52
  const [naam, setNaam] = useState(null);
18
53
  const [toestand, setToestand] = useState("laden");
19
- // Zorg dat de virtuele PDF-printer bestaat, ook als de gebruiker nog geen
20
- // wizard heeft doorlopen. Daarna herladen zodat de banner direct zichtbaar is.
54
+ const [gekoppeldePrintplek, setGekoppeldePrintplek] = useState(null);
55
+ const [persistent, setPersistent] = useState(null);
56
+ const [instellingOpen, setInstellingOpen] = useState(false);
57
+ const [aangevraagdePrintplek, setAangevraagdePrintplek] = useState(null);
58
+ useEffect(() => {
59
+ const params = new URLSearchParams(window.location.search);
60
+ const gevraagd = params.get("pdf-printplek")?.trim() || null;
61
+ setInstellingOpen(params.get("pdf-map-instellen") === "1");
62
+ setAangevraagdePrintplek(gevraagd);
63
+ void lokalePdfPrintplek().then((lokaal) => setGekoppeldePrintplek(gevraagd ?? lokaal));
64
+ }, []);
65
+ // Maak de virtuele PDF-printer alleen aan wanneer expliciet om het instellen
66
+ // van de map gevraagd wordt (?pdf-map-instellen=1). Zonder die vraag én
67
+ // zonder bestaande PDF-printer is er niets om toestemming voor te geven.
21
68
  useEffect(() => {
22
- if (pdfPrinter)
69
+ if (pdfPrinter || !instellingOpen)
23
70
  return;
24
71
  let actief = true;
25
72
  void (async () => {
@@ -35,7 +82,7 @@ export function PdfMapToestemmingBanner({ alleenActieNodig = false, zwevend = fa
35
82
  return () => {
36
83
  actief = false;
37
84
  };
38
- }, [pdfPrinter, reload]);
85
+ }, [pdfPrinter, instellingOpen, reload]);
39
86
  const laad = useCallback(async () => {
40
87
  if (!pdfPrinter) {
41
88
  setNaam(null);
@@ -44,7 +91,26 @@ export function PdfMapToestemmingBanner({ alleenActieNodig = false, zwevend = fa
44
91
  }
45
92
  setNaam(await pdfMapNaam(pdfPrinter.id));
46
93
  setToestand(await pdfMapToestemming(pdfPrinter.id));
47
- }, [pdfPrinter]);
94
+ const koppeling = await pdfMapKoppeling();
95
+ let doelPrintplek = printplekId ?? aangevraagdePrintplek ?? koppeling?.printPlekId ?? null;
96
+ if (!doelPrintplek) {
97
+ const { data } = await supabase
98
+ .from("document_printplek_printers")
99
+ .select("print_plek_id")
100
+ .eq("printer_id", pdfPrinter.id);
101
+ const ids = [
102
+ ...new Set((Array.isArray(data) ? data : [])
103
+ .map((rij) => String(rij.print_plek_id ?? "").trim())
104
+ .filter(Boolean)),
105
+ ];
106
+ doelPrintplek = ids.length === 1 ? ids[0] ?? null : null;
107
+ }
108
+ setGekoppeldePrintplek(doelPrintplek);
109
+ setPersistent(koppeling?.persistent ?? null);
110
+ // Opnieuw meten of PDF nog ergens is ingesteld: kiest de gebruiker bij
111
+ // alle documenten een echte printer, dan verdwijnt de balk direct.
112
+ setInGebruik(await meetGebruik(pdfPrinter.id));
113
+ }, [aangevraagdePrintplek, meetGebruik, pdfPrinter, printplekId]);
48
114
  useEffect(() => {
49
115
  void laad();
50
116
  }, [laad]);
@@ -58,14 +124,33 @@ export function PdfMapToestemmingBanner({ alleenActieNodig = false, zwevend = fa
58
124
  document.removeEventListener("visibilitychange", h);
59
125
  };
60
126
  }, [laad]);
61
- if (!pdfPrinter)
127
+ // In een iframe (o.a. de Lovable-preview) heeft de balk geen nut: de
128
+ // mappenverkenner werkt daar niet en de gebruiker kan alleen via "Open in
129
+ // eigen tabblad" verder. De balk daar niet tonen houdt de preview schoon;
130
+ // in een eigen tabblad blijft hij volledig zichtbaar.
131
+ const inIframe = draaitInIframe();
132
+ if (inIframe)
62
133
  return null;
63
- // Banner blijft altijd zichtbaar zolang er een PDF-printer is, zodat de
64
- // maptoestemming ook proactief te vernieuwen is (groen = in orde).
65
- const inOrde = toestand === "granted" && !!naam;
66
- if (alleenActieNodig && (toestand === "laden" || inOrde))
134
+ // Is "Opslaan als PDF" nergens meer ingesteld (geen printplek-, layout- of
135
+ // formaatregel met deze printer), dan is de balk overbodig en verbergen we
136
+ // hem volledig. Alleen bij een expliciete instelvraag blijft hij zichtbaar.
137
+ if (!instellingOpen) {
138
+ if (!pdfPrinter)
139
+ return null;
140
+ if (inGebruik === false)
141
+ return null;
142
+ }
143
+ // De balk is op dit punt alleen nog zichtbaar als "Opslaan als PDF"
144
+ // daadwerkelijk ergens is ingesteld (of de gebruiker expliciet aan het
145
+ // instellen is); anders is hierboven al `null` teruggegeven.
146
+ const vereistePrintplek = printplekId ?? aangevraagdePrintplek ?? gekoppeldePrintplek;
147
+ const inOrde = !!pdfPrinter &&
148
+ toestand === "granted" &&
149
+ !!naam &&
150
+ !!gekoppeldePrintplek &&
151
+ (!vereistePrintplek || gekoppeldePrintplek === vereistePrintplek);
152
+ if (alleenActieNodig && !instellingOpen && (!pdfPrinter ? false : toestand === "laden" || inOrde))
67
153
  return null;
68
- const inIframe = draaitInIframe();
69
154
  const ondersteund = mapKiezenMogelijk();
70
155
  const geefToestemming = async () => {
71
156
  if (!pdfPrinter)
@@ -94,13 +179,14 @@ export function PdfMapToestemmingBanner({ alleenActieNodig = false, zwevend = fa
94
179
  description: "Open de app in een eigen tabblad om de mappenverkenner te gebruiken.",
95
180
  action: {
96
181
  label: "Open in eigen tabblad",
97
- onClick: openInEigenTabblad,
182
+ onClick: () => openInEigenTabblad(printplekId ?? gekoppeldePrintplek ?? undefined),
98
183
  },
99
184
  duration: 15000,
100
185
  });
101
186
  return;
102
187
  }
103
- const uitkomst = await kiesPdfMap(pdfPrinter.id);
188
+ const doelPrintplek = printplekId ?? gekoppeldePrintplek ?? undefined;
189
+ const uitkomst = await kiesPdfMap(pdfPrinter.id, doelPrintplek);
104
190
  if (uitkomst.status === "gekozen") {
105
191
  toast.success("Map gekozen", { description: `PDF's worden opgeslagen in “${uitkomst.naam}”.` });
106
192
  await laad();
@@ -113,7 +199,10 @@ export function PdfMapToestemmingBanner({ alleenActieNodig = false, zwevend = fa
113
199
  else if (uitkomst.status === "iframe-geblokkeerd") {
114
200
  toast.error("Map kiezen werkt niet in een iframe", {
115
201
  description: "Open de app in een eigen tabblad.",
116
- action: { label: "Open in eigen tabblad", onClick: openInEigenTabblad },
202
+ action: {
203
+ label: "Open in eigen tabblad",
204
+ onClick: () => openInEigenTabblad(printplekId ?? gekoppeldePrintplek ?? undefined),
205
+ },
117
206
  duration: 15000,
118
207
  });
119
208
  }
@@ -121,12 +210,27 @@ export function PdfMapToestemmingBanner({ alleenActieNodig = false, zwevend = fa
121
210
  toast.error("Map kiezen is mislukt", { description: uitkomst.melding });
122
211
  }
123
212
  };
124
- const icoon = toestand === "granted"
213
+ const icoon = inOrde
125
214
  ? ShieldCheck
126
215
  : toestand === "denied"
127
216
  ? ShieldAlert
128
217
  : FolderOpen;
129
218
  const Icoon = icoon;
219
+ if (!pdfPrinter) {
220
+ return (_jsxs("div", { role: "status", "aria-live": "polite", className: `${zwevend ? "fixed inset-x-4 top-4 z-[100] mx-auto max-w-4xl shadow-lg" : ""} flex flex-wrap items-center justify-between gap-3 rounded-lg border border-amber-300/60 bg-amber-50 p-3 dark:border-amber-700/60 dark:bg-amber-950/30`, children: [_jsxs("div", { className: "flex items-center gap-2 text-sm", children: [_jsx(ShieldAlert, { className: "h-4 w-4 text-amber-600 dark:text-amber-400" }), _jsx("span", { className: "font-medium", children: "PDF-maptoestemming" }), _jsx("span", { className: "text-muted-foreground", children: "De virtuele \u201COpslaan als PDF\u201D-printer wordt aangemaakt of kon nog niet worden geladen." })] }), _jsxs(Button, { size: "sm", variant: "outline", onClick: () => {
221
+ void (async () => {
222
+ try {
223
+ await zorgVoorPdfPrinter();
224
+ await reload(true);
225
+ }
226
+ catch (fout) {
227
+ toast.error("PDF-printer kon niet worden aangemaakt", {
228
+ description: fout instanceof Error ? fout.message : undefined,
229
+ });
230
+ }
231
+ })();
232
+ }, children: [_jsx(FolderOpen, { className: "mr-2 h-4 w-4" }), " PDF-printer aanmaken"] })] }));
233
+ }
130
234
  return (_jsxs("div", { role: "status", "aria-live": "polite", className: `${inOrde
131
235
  ? "border-emerald-300/60 bg-emerald-50 dark:border-emerald-700/60 dark:bg-emerald-950/30"
132
236
  : "border-amber-300/60 bg-amber-50 dark:border-amber-700/60 dark:bg-amber-950/30"} ${zwevend
@@ -135,5 +239,7 @@ export function PdfMapToestemmingBanner({ alleenActieNodig = false, zwevend = fa
135
239
  ? "h-4 w-4 text-emerald-600 dark:text-emerald-400"
136
240
  : "h-4 w-4 text-amber-600 dark:text-amber-400" }), _jsx("span", { className: "font-medium", children: "PDF-maptoestemming" }), _jsxs("span", { className: "text-muted-foreground", children: [naam
137
241
  ? `Map “${naam}” — toestemming ${toestand === "granted" ? "verleend" : "vereist vernieuwen"}`
138
- : "Nog geen map gekozen op deze computer", !ondersteund ? " (browser ondersteunt geen mapkeuze)" : ""] })] }), _jsxs("div", { className: "flex gap-2", children: [naam ? (_jsxs(Button, { size: "sm", variant: inOrde ? "outline" : "default", onClick: () => void geefToestemming(), children: [_jsx(FolderCheck, { className: "mr-2 h-4 w-4" }), inOrde ? "Toestemming controleren" : "Maptoestemming geven"] })) : null, _jsxs(Button, { size: "sm", variant: "outline", onClick: () => void kiesNieuweMap(), children: [_jsx(FolderOpen, { className: "mr-2 h-4 w-4" }), " ", naam ? "Andere map kiezen" : "Map kiezen"] })] })] }));
242
+ : "Nog geen map gekozen op deze computer", !ondersteund ? " (browser ondersteunt geen mapkeuze)" : "", inIframe
243
+ ? " — de preview draait in een iframe; de mapkeuze geldt per webadres en moet hier opnieuw worden gezet"
244
+ : "", !inIframe && gekoppeldePrintplek ? " — gekoppeld aan deze printplek" : "", !inIframe && persistent === false ? " — browseropslag is niet permanent gegarandeerd" : ""] })] }), _jsxs("div", { className: "flex gap-2", children: [naam ? (_jsxs(Button, { size: "sm", variant: inOrde ? "outline" : "default", onClick: () => void geefToestemming(), children: [_jsx(FolderCheck, { className: "mr-2 h-4 w-4" }), inOrde ? "Toestemming controleren" : "Maptoestemming geven"] })) : null, _jsxs(Button, { size: "sm", variant: "outline", onClick: () => void kiesNieuweMap(), children: [_jsx(FolderOpen, { className: "mr-2 h-4 w-4" }), " ", naam ? "Andere map kiezen" : "Map kiezen"] }), inIframe ? (_jsx(Button, { size: "sm", variant: "outline", onClick: () => openInEigenTabblad(printplekId ?? gekoppeldePrintplek ?? undefined), children: "Open in eigen tabblad" })) : null] })] }));
139
245
  }
@@ -0,0 +1,20 @@
1
+ interface Props {
2
+ /** Print-HTML, exact zoals die ook naar de printwachtrij gaat. */
3
+ html: string;
4
+ breedteMm: number;
5
+ hoogteMm: number;
6
+ margeMm: number;
7
+ onControle?: (status: "controleren" | "gereed" | "afwijking", fout?: string) => void;
8
+ }
9
+ /**
10
+ * Toont het voorbeeld altijd: de print-HTML wordt direct getoond op ware
11
+ * papiergrootte (werkt ook binnen een shell-iframe, waar Chrome ingesloten
12
+ * PDF's blokkeert). De echte print-PDF blijft opvraagbaar in een eigen tabblad.
13
+ *
14
+ * Pagina's stapelen verticaal; het hele kader wordt met een CSS-schaling op
15
+ * de beschikbare breedte passend gemaakt, zodat het document altijd volledig
16
+ * zichtbaar is (met verticaal scrollen bij meerdere pagina's).
17
+ */
18
+ export declare function PdfVoorbeeld({ html, breedteMm, hoogteMm, margeMm, onControle }: Props): import("react").JSX.Element;
19
+ export {};
20
+ //# sourceMappingURL=PdfVoorbeeld.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PdfVoorbeeld.d.ts","sourceRoot":"","sources":["../../src/components/PdfVoorbeeld.tsx"],"names":[],"mappings":"AAKA,UAAU,KAAK;IACb,kEAAkE;IAClE,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,aAAa,GAAG,QAAQ,GAAG,WAAW,EAAE,IAAI,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;CACtF;AAED;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,OAAO,EAAE,UAAU,EAAE,EAAE,KAAK,+BAyGrF"}
@@ -0,0 +1,95 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useRef, useState } from "react";
3
+ import { Button } from "@flowselections/core";
4
+ import { AlertTriangle, ExternalLink, Loader2 } from "lucide-react";
5
+ import { maakVoorbeeldPdf } from "../lib/documentVoorbeeld.functions.js";
6
+ /**
7
+ * Toont het voorbeeld altijd: de print-HTML wordt direct getoond op ware
8
+ * papiergrootte (werkt ook binnen een shell-iframe, waar Chrome ingesloten
9
+ * PDF's blokkeert). De echte print-PDF blijft opvraagbaar in een eigen tabblad.
10
+ *
11
+ * Pagina's stapelen verticaal; het hele kader wordt met een CSS-schaling op
12
+ * de beschikbare breedte passend gemaakt, zodat het document altijd volledig
13
+ * zichtbaar is (met verticaal scrollen bij meerdere pagina's).
14
+ */
15
+ export function PdfVoorbeeld({ html, breedteMm, hoogteMm, margeMm, onControle }) {
16
+ const renderVolgorde = useRef(0);
17
+ const controleCallback = useRef(onControle);
18
+ const [paginas, setPaginas] = useState([]);
19
+ const [bezigPdf, setBezigPdf] = useState(false);
20
+ const [fout, setFout] = useState(null);
21
+ const [pdfBase64, setPdfBase64] = useState(null);
22
+ useEffect(() => {
23
+ controleCallback.current = onControle;
24
+ }, [onControle]);
25
+ useEffect(() => {
26
+ const volgorde = ++renderVolgorde.current;
27
+ const timer = window.setTimeout(() => {
28
+ setBezigPdf(true);
29
+ setFout(null);
30
+ controleCallback.current?.("controleren");
31
+ void maakVoorbeeldPdf({ data: { html, breedteMm, hoogteMm, margeMm } })
32
+ .then(async (res) => {
33
+ if (volgorde !== renderVolgorde.current)
34
+ return;
35
+ const bin = atob(res.pdfBase64);
36
+ const bytes = new Uint8Array(bin.length);
37
+ for (let i = 0; i < bin.length; i += 1)
38
+ bytes[i] = bin.charCodeAt(i);
39
+ const pdfjs = await import("pdfjs-dist");
40
+ pdfjs.GlobalWorkerOptions.workerSrc = new URL("pdfjs-dist/build/pdf.worker.min.mjs", import.meta.url).toString();
41
+ const taak = pdfjs.getDocument({ data: bytes });
42
+ const pdf = await taak.promise;
43
+ const nieuw = [];
44
+ for (let nummer = 1; nummer <= pdf.numPages; nummer += 1) {
45
+ const pagina = await pdf.getPage(nummer);
46
+ const viewport = pagina.getViewport({ scale: 1.5 });
47
+ const canvas = document.createElement("canvas");
48
+ canvas.width = viewport.width;
49
+ canvas.height = viewport.height;
50
+ const context = canvas.getContext("2d");
51
+ if (!context)
52
+ throw new Error("De afdrukpagina kon niet worden getoond.");
53
+ await pagina.render({ canvas, canvasContext: context, viewport }).promise;
54
+ nieuw.push(canvas.toDataURL("image/png"));
55
+ }
56
+ if (volgorde !== renderVolgorde.current)
57
+ return;
58
+ setPaginas(nieuw);
59
+ setPdfBase64(res.pdfBase64);
60
+ controleCallback.current?.("gereed");
61
+ })
62
+ .catch((e) => {
63
+ if (volgorde !== renderVolgorde.current)
64
+ return;
65
+ const melding = e instanceof Error ? e.message : "Afdrukcontrole mislukt";
66
+ setPaginas([]);
67
+ setPdfBase64(null);
68
+ setFout(melding);
69
+ controleCallback.current?.("afwijking", melding);
70
+ })
71
+ .finally(() => {
72
+ if (volgorde === renderVolgorde.current)
73
+ setBezigPdf(false);
74
+ });
75
+ }, 350);
76
+ return () => window.clearTimeout(timer);
77
+ }, [html, breedteMm, hoogteMm, margeMm]);
78
+ const openPdf = async () => {
79
+ if (!pdfBase64)
80
+ return;
81
+ try {
82
+ const bin = atob(pdfBase64);
83
+ const bytes = new Uint8Array(bin.length);
84
+ for (let i = 0; i < bin.length; i += 1)
85
+ bytes[i] = bin.charCodeAt(i);
86
+ const url = URL.createObjectURL(new Blob([bytes], { type: "application/pdf" }));
87
+ window.open(url, "_blank", "noopener,noreferrer");
88
+ setTimeout(() => URL.revokeObjectURL(url), 60000);
89
+ }
90
+ catch (e) {
91
+ setFout(e instanceof Error ? e.message : "PDF kon niet worden geopend");
92
+ }
93
+ };
94
+ return (_jsxs("div", { className: "relative h-full w-full overflow-hidden", children: [_jsx("div", { className: "h-full w-full overflow-y-auto overflow-x-hidden rounded-md bg-muted p-3", children: _jsxs("div", { className: "mx-auto flex max-w-full flex-col items-center gap-4", children: [paginas.map((src, index) => (_jsx("img", { src: src, alt: `Afdrukpagina ${index + 1}`, className: "h-auto max-w-full border bg-background shadow-sm" }, `${index}-${src.length}`))), bezigPdf ? _jsx(Loader2, { className: "mt-8 h-6 w-6 animate-spin text-muted-foreground" }) : null, fout ? _jsxs("div", { className: "mt-8 flex max-w-md items-start gap-2 text-sm text-destructive", children: [_jsx(AlertTriangle, { className: "mt-0.5 h-4 w-4 shrink-0" }), fout] }) : null] }) }), _jsx("div", { className: "pointer-events-none absolute right-3 top-3 flex flex-col items-end gap-2", children: _jsxs(Button, { size: "sm", variant: "outline", className: "pointer-events-auto", onClick: () => void openPdf(), disabled: bezigPdf || !pdfBase64, children: [bezigPdf ? (_jsx(Loader2, { className: "mr-2 h-3 w-3 animate-spin" })) : (_jsx(ExternalLink, { className: "mr-2 h-3 w-3" })), "Print-PDF openen"] }) })] }));
95
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"PrintProgrammaUitleg.d.ts","sourceRoot":"","sources":["../../src/components/PrintProgrammaUitleg.tsx"],"names":[],"mappings":"AAgBA,wBAAgB,oBAAoB,CAAC,EAAE,OAAe,EAAE,EAAE;IAAE,OAAO,CAAC,EAAE,OAAO,CAAA;CAAE,+BAmC9E;AAED,wBAAgB,YAAY,gCAa3B"}
1
+ {"version":3,"file":"PrintProgrammaUitleg.d.ts","sourceRoot":"","sources":["../../src/components/PrintProgrammaUitleg.tsx"],"names":[],"mappings":"AAgBA,wBAAgB,oBAAoB,CAAC,EAAE,OAAe,EAAE,EAAE;IAAE,OAAO,CAAC,EAAE,OAAO,CAAA;CAAE,+BAmC9E;AAED,wBAAgB,YAAY,gCAc3B"}
@@ -15,5 +15,5 @@ export function PrintProgrammaUitleg({ compact = false }) {
15
15
  return (_jsxs("div", { className: "bg-muted/40 space-y-4 rounded-xl border p-5", children: [!compact && (_jsxs("div", { className: "flex items-center gap-3", children: [_jsx("div", { className: "bg-background rounded-lg border p-2", children: _jsx(MonitorSmartphone, { className: "text-muted-foreground h-5 w-5" }) }), _jsxs("div", { children: [_jsx("p", { className: "text-sm font-medium", children: "We zien nog geen printers" }), _jsx("p", { className: "text-muted-foreground text-sm", children: "Zet de computer klaar in drie korte stappen." })] })] })), _jsx("ol", { className: "space-y-3", children: STAPPEN.map((tekst, i) => (_jsxs("li", { className: "flex gap-3", children: [_jsx("span", { className: "bg-background text-foreground flex h-6 w-6 shrink-0 items-center justify-center rounded-full border text-xs font-medium", children: i + 1 }), _jsx("span", { className: "text-sm", children: tekst })] }, tekst))) }), _jsx(Button, { asChild: true, variant: "outline", children: _jsxs("a", { href: DOWNLOAD_URL, target: "_blank", rel: "noreferrer", children: [_jsx(Download, { className: "mr-2 h-4 w-4" }), " Printprogramma downloaden"] }) })] }));
16
16
  }
17
17
  export function WerktHetNiet() {
18
- return (_jsxs("details", { className: "group rounded-lg border p-3", children: [_jsxs("summary", { className: "flex cursor-pointer items-center gap-2 text-sm font-medium", children: [_jsx(HelpCircle, { className: "text-muted-foreground h-4 w-4" }), " Werkt het niet?"] }), _jsxs("ul", { className: "text-muted-foreground mt-3 space-y-2 text-sm", children: [_jsx("li", { children: "De computer staat uit of is in slaapstand." }), _jsx("li", { children: "Het printprogramma is nog niet gestart op die computer." }), _jsx("li", { children: "Er is ingelogd met andere gegevens dan die u van uw beheerder kreeg." })] })] }));
18
+ return (_jsxs("details", { className: "group rounded-lg border p-3", children: [_jsxs("summary", { className: "flex cursor-pointer items-center gap-2 text-sm font-medium", children: [_jsx(HelpCircle, { className: "text-muted-foreground h-4 w-4" }), " Werkt het niet?"] }), _jsxs("ul", { className: "text-muted-foreground mt-3 space-y-2 text-sm", children: [_jsx("li", { children: "De computer staat uit, is in slaapstand of heeft geen internet." }), _jsx("li", { children: "Het printprogramma is nog niet gestart op die computer." }), _jsx("li", { children: "Er is ingelogd met een eigen/ander PrintNode-account in plaats van de beheerdergegevens \u2014 alleen computers onder het gekoppelde account verschijnen hier." }), _jsx("li", { children: "De computer heet anders dan je verwacht: de naam in de lijst is de naam zoals macOS die aan PrintNode doorgeeft." })] })] }));
19
19
  }
@@ -1 +1 @@
1
- {"version":3,"file":"PrintQueuePage.d.ts","sourceRoot":"","sources":["../../src/components/PrintQueuePage.tsx"],"names":[],"mappings":"AAoFA,wBAAgB,cAAc,gCAsT7B"}
1
+ {"version":3,"file":"PrintQueuePage.d.ts","sourceRoot":"","sources":["../../src/components/PrintQueuePage.tsx"],"names":[],"mappings":"AAuFA,wBAAgB,cAAc,gCA2X7B"}
@@ -1,18 +1,19 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useMemo, useRef, useState } from "react";
3
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 { FileDown, RotateCcw, Search, XCircle } from "lucide-react";
4
+ import { FileDown, FolderOpen, RotateCcw, Save, Search, XCircle } from "lucide-react";
5
5
  import { bijgewerktTekst, useAutoRefresh } from "../lib/useAutoRefresh.js";
6
6
  import { PrintStatusBanner } from "./PrintStatusBanner.js";
7
7
  import { maakPrintJob, updateJobStatus, useLayouts, usePrinters, usePrintJobs, usePrintPlekNamen, } from "../lib/useDocumentData.js";
8
8
  import { printStatus } from "../lib/printService.functions.js";
9
9
  import { vraagWachtrijRonde } from "../lib/queueTickClient.js";
10
10
  import { documentTypeLabel } from "../lib/documentTypes.js";
11
- import { jobPlekLabel } from "../lib/printQueue.js";
12
- import { bewaarInPdfMap } from "../lib/pdfMap.js";
11
+ import { jobPlekLabel, jobPrintPlekId } from "../lib/printQueue.js";
12
+ import { kiesPdfMap, openInEigenTabblad } from "../lib/pdfMap.js";
13
+ import { bewaarPdfJobOpnieuw, downloadPdfJob } from "../lib/pdfJobSaver.js";
14
+ import { pdfOpslagLabel, isOpslagFout } from "../lib/pdfOpslagStatus.js";
13
15
  import { PdfMapAutoSaver } from "./PdfMapAutoSaver.js";
14
16
  import { PdfMapToestemmingBanner } from "./PdfMapToestemmingBanner.js";
15
- import { supabase } from "../lib/supabaseClient.js";
16
17
  const STATUS_VARIANT = {
17
18
  wachtend: "outline",
18
19
  bezig: "secondary",
@@ -27,14 +28,6 @@ const BRON_LABEL = {
27
28
  render_html: "meegestuurde opmaak",
28
29
  klantlayout: "klantlayout",
29
30
  };
30
- const LOKALE_PDF_LABEL = {
31
- gereed: "PDF gereed — lokaal opslaan",
32
- opgeslagen: "opgeslagen op Mac",
33
- map_ontbreekt: "PDF-map ontbreekt",
34
- toestemming_nodig: "maptoestemming nodig",
35
- schrijffout: "opslaan mislukt",
36
- timeout: "wachten op PDF verlopen",
37
- };
38
31
  /**
39
32
  * Heeft deze opdracht een kenmerk waarmee hij van andere orders te
40
33
  * onderscheiden is? Zo niet, dan valt de dubbelbescherming terug op het
@@ -44,10 +37,15 @@ function heeftOrderKenmerk(job) {
44
37
  const p = (job.payload ?? {});
45
38
  if (typeof p !== "object" || p === null)
46
39
  return false;
47
- return ["queue_item_id", "order_id", "ordernummer", "order_number", "orderId"].some((k) => {
48
- const w = p[k];
49
- return (typeof w === "string" && w.trim() !== "") || typeof w === "number";
50
- });
40
+ const gevuld = (w) => (typeof w === "string" && w.trim() !== "") || typeof w === "number";
41
+ if (["queue_item_id", "order_id", "ordernummer", "order_number", "orderId"].some((k) => gevuld(p[k]))) {
42
+ return true;
43
+ }
44
+ // Modules kunnen de order ook genest meesturen als payload.order / payload.data.order.
45
+ const genest = [p.order, p.data?.order];
46
+ return genest.some((o) => o !== null &&
47
+ typeof o === "object" &&
48
+ ["id", "order_id", "orderNumber", "ordernummer", "nummer"].some((k) => gevuld(o[k])));
51
49
  }
52
50
  export function PrintQueuePage() {
53
51
  const { jobs, loading, reload } = usePrintJobs();
@@ -56,9 +54,43 @@ export function PrintQueuePage() {
56
54
  const [zoek, setZoek] = useState("");
57
55
  const [status, setStatus] = useState("alle");
58
56
  const [plekFilter, setPlekFilter] = useState("alle");
57
+ const [opslagFilter, setOpslagFilter] = useState("alle");
59
58
  const [tickFout, setTickFout] = useState(null);
60
59
  const plekNamen = usePrintPlekNamen();
61
60
  const printerNaam = useMemo(() => new Map(printers.map((p) => [p.id, p.naam])), [printers]);
61
+ const pdfPrinter = printers.find((p) => p.afdrukmethode === "pdf");
62
+ /** Kies direct vanuit een foute opdracht de PDF-map op deze werkplek. */
63
+ const stelMapIn = async (printplekId) => {
64
+ if (!pdfPrinter) {
65
+ toast.error("Er is nog geen “Opslaan als PDF”-printer aangemaakt");
66
+ return;
67
+ }
68
+ const uitkomst = await kiesPdfMap(pdfPrinter.id, printplekId);
69
+ if (uitkomst.status === "gekozen") {
70
+ toast.success("Map gekozen", {
71
+ description: `PDF's worden opgeslagen in “${uitkomst.naam}”. De opdracht wordt opnieuw opgeslagen.`,
72
+ });
73
+ void reload(true);
74
+ }
75
+ else if (uitkomst.status === "iframe-geblokkeerd") {
76
+ toast.error("Map kiezen werkt niet in een iframe", {
77
+ description: "Open de app in een eigen tabblad.",
78
+ action: {
79
+ label: "Open in eigen tabblad",
80
+ onClick: () => openInEigenTabblad(printplekId),
81
+ },
82
+ duration: 15000,
83
+ });
84
+ }
85
+ else if (uitkomst.status === "niet-ondersteund") {
86
+ toast.error("Deze browser ondersteunt geen mapkeuze", {
87
+ description: "Gebruik Chrome, Edge of Safari op desktop.",
88
+ });
89
+ }
90
+ else if (uitkomst.status === "fout") {
91
+ toast.error("Map kiezen is mislukt", { description: uitkomst.melding });
92
+ }
93
+ };
62
94
  const layoutNaam = useMemo(() => new Map(layouts.map((l) => [l.id, l.naam])), [layouts]);
63
95
  /** Herkomstlabel per opdracht: printplek, anders pickstation of werkplek. */
64
96
  const plekPerJob = useMemo(() => {
@@ -76,6 +108,13 @@ export function PrintQueuePage() {
76
108
  return false;
77
109
  if (plekFilter !== "alle" && (plekPerJob.get(j.id) || "—") !== plekFilter)
78
110
  return false;
111
+ if (opslagFilter === "fout" && !isOpslagFout(j.lokale_pdf_status))
112
+ return false;
113
+ if (opslagFilter === "opgeslagen" && j.lokale_pdf_status !== "opgeslagen")
114
+ return false;
115
+ if (opslagFilter === "wacht" &&
116
+ !["wachtend", "genereren", "gereed", "bezig"].includes(j.lokale_pdf_status ?? ""))
117
+ return false;
79
118
  return true;
80
119
  });
81
120
  const tellingen = useMemo(() => {
@@ -115,38 +154,31 @@ export function PrintQueuePage() {
115
154
  await reload(true);
116
155
  };
117
156
  const bijgewerkt = useAutoRefresh(ronde, lopende ? 5000 : 20000);
118
- return (_jsxs("main", { className: "space-y-6 p-6", children: [_jsx(PdfMapAutoSaver, {}), _jsx(PdfMapToestemmingBanner, {}), _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." })] }), _jsx("span", { className: "text-muted-foreground text-xs", children: bijgewerktTekst(bijgewerkt) })] }), _jsx(PrintStatusBanner, { fout: tickFout }), _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(Select, { value: plekFilter, onValueChange: setPlekFilter, children: [_jsx(SelectTrigger, { children: _jsx(SelectValue, {}) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "alle", children: "Alle printplekken" }), plekOpties.map((p) => (_jsx(SelectItem, { value: p, children: p }, p))), _jsx(SelectItem, { value: "\u2014", children: "Zonder printplek" })] })] })] }), _jsxs(Table, { children: [_jsx(TableHeader, { children: _jsxs(TableRow, { children: [_jsx(TableHead, { children: "Document" }), _jsx(TableHead, { children: "Printplek" }), _jsx(TableHead, { children: "Layout (aangeleverd \u2192 gebruikt)" }), _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: 7, 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" })), !heeftOrderKenmerk(j) && (_jsx(Badge, { variant: "outline", className: "ml-2 text-amber-600", children: "geen orderkenmerk" })), j.foutmelding && (_jsx("span", { className: "block text-xs text-destructive", children: j.foutmelding })), j.lokale_pdf_fout && (_jsx("span", { className: "block text-xs text-destructive", children: j.lokale_pdf_fout }))] }), _jsx(TableCell, { className: "text-sm text-muted-foreground", children: plekPerJob.get(j.id) || "—" }), _jsxs(TableCell, { className: "text-sm text-muted-foreground", children: [_jsx("span", { title: "Aangeleverd door de module", children: j.layout_id ? layoutNaam.get(j.layout_id) ?? "Onbekende layout" : "geen" }), _jsxs("span", { className: "block text-xs", children: ["\u2192", " ", _jsx("span", { title: "Layout die de wachtrij gebruikte", children: j.gebruikte_layout_id
157
+ return (_jsxs("main", { className: "space-y-6 p-6", children: [_jsx(PdfMapAutoSaver, {}), _jsx(PdfMapToestemmingBanner, {}), _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." })] }), _jsx("span", { className: "text-muted-foreground text-xs", children: bijgewerktTekst(bijgewerkt) })] }), _jsx(PrintStatusBanner, { fout: tickFout }), _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-5", 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(Select, { value: plekFilter, onValueChange: setPlekFilter, children: [_jsx(SelectTrigger, { children: _jsx(SelectValue, {}) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "alle", children: "Alle printplekken" }), plekOpties.map((p) => (_jsx(SelectItem, { value: p, children: p }, p))), _jsx(SelectItem, { value: "\u2014", children: "Zonder printplek" })] })] }), _jsxs(Select, { value: opslagFilter, onValueChange: setOpslagFilter, children: [_jsx(SelectTrigger, { children: _jsx(SelectValue, {}) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "alle", children: "Alle PDF-opslag" }), _jsx(SelectItem, { value: "fout", children: "Opslag mislukt" }), _jsx(SelectItem, { value: "wacht", children: "Wacht op werkplek" }), _jsx(SelectItem, { value: "opgeslagen", children: "Opgeslagen in map" })] })] })] }), _jsxs(Table, { children: [_jsx(TableHeader, { children: _jsxs(TableRow, { children: [_jsx(TableHead, { children: "Document" }), _jsx(TableHead, { children: "Printplek" }), _jsx(TableHead, { children: "Layout (aangeleverd \u2192 gebruikt)" }), _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: 7, 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" })), !heeftOrderKenmerk(j) && (_jsx(Badge, { variant: "outline", className: "ml-2 text-amber-600", children: "geen orderkenmerk" })), j.foutmelding && (_jsx("span", { className: "block text-xs text-destructive", children: j.foutmelding })), j.lokale_pdf_fout && (_jsx("span", { className: "block text-xs text-destructive", children: j.lokale_pdf_fout })), (["map_ontbreekt", "geen_rechten", "toestemming_nodig"].includes(j.lokale_pdf_status ?? "") ||
158
+ (j.lokale_pdf_status === "gereed" && !j.lokale_pdf_print_plek_id)) && (_jsxs(Button, { variant: "outline", size: "sm", className: "mt-1", onClick: () => void stelMapIn(jobPrintPlekId(j) || undefined), children: [_jsx(FolderOpen, { className: "mr-2 h-4 w-4" }), " Map instellen"] }))] }), _jsx(TableCell, { className: "text-sm text-muted-foreground", children: plekPerJob.get(j.id) || "—" }), _jsxs(TableCell, { className: "text-sm text-muted-foreground", children: [_jsx("span", { title: "Aangeleverd door de module", children: j.layout_id ? layoutNaam.get(j.layout_id) ?? "Onbekende layout" : "geen" }), _jsxs("span", { className: "block text-xs", children: ["\u2192", " ", _jsx("span", { title: "Layout die de wachtrij gebruikte", children: j.gebruikte_layout_id
119
159
  ? layoutNaam.get(j.gebruikte_layout_id) ?? "Onbekende layout"
120
160
  : j.gebruikte_bron
121
161
  ? "geen layout"
122
- : "nog niet verwerkt" }), j.gebruikte_bron && (_jsx(Badge, { variant: "outline", className: "ml-2", children: BRON_LABEL[j.gebruikte_bron] ?? j.gebruikte_bron }))] })] }), _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.lokale_pdf_status && LOKALE_PDF_LABEL[j.lokale_pdf_status]) ?? 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: [j.resultaat_pdf_base64 && (_jsxs(Button, { variant: "ghost", size: "sm", title: "Bewaar de opgemaakte PDF", onClick: async () => {
123
- const bytes = Uint8Array.from(atob(j.resultaat_pdf_base64), (c) => c.charCodeAt(0));
124
- const blob = new Blob([bytes], { type: "application/pdf" });
125
- const bestandsnaam = `${j.documentnaam ?? "document"}.pdf`;
126
- const inMap = j.printer_id
127
- ? await bewaarInPdfMap(j.printer_id, bestandsnaam, blob)
128
- : false;
129
- if (inMap) {
130
- await supabase
131
- .from("print_jobs")
132
- .update({
133
- status: "voltooid",
134
- lokale_pdf_status: "opgeslagen",
135
- lokaal_opgeslagen_op: new Date().toISOString(),
136
- lokale_pdf_fout: null,
137
- })
138
- .eq("id", j.id);
139
- toast.success("PDF opgeslagen in de ingestelde map");
162
+ : "nog niet verwerkt" }), j.gebruikte_bron && (_jsx(Badge, { variant: "outline", className: "ml-2", children: BRON_LABEL[j.gebruikte_bron] ?? j.gebruikte_bron }))] })] }), _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: pdfOpslagLabel(j.lokale_pdf_status, Boolean(j.lokale_pdf_print_plek_id)) ?? 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: [(j.artefact_pad || j.resultaat_pdf_base64) && (_jsxs(Button, { variant: "ghost", size: "sm", title: "Bewaar de opgemaakte PDF", onClick: async () => {
163
+ try {
164
+ await downloadPdfJob(j.id, j.documentnaam ?? "document");
165
+ }
166
+ catch (fout) {
167
+ toast.error(fout instanceof Error ? fout.message : "PDF downloaden mislukt");
168
+ }
169
+ }, children: [_jsx(FileDown, { className: "mr-2 h-4 w-4" }), " PDF"] })), j.lokale_pdf_status !== "opgeslagen" &&
170
+ (j.artefact_pad || j.resultaat_pdf_base64) && (_jsxs(Button, { variant: "ghost", size: "sm", title: "Bewaar de bestaande PDF opnieuw in de ingestelde map", onClick: async () => {
171
+ try {
172
+ await bewaarPdfJobOpnieuw(j.id);
173
+ toast.success("De bestaande PDF wordt opnieuw in de ingestelde map opgeslagen");
174
+ }
175
+ catch (fout) {
176
+ toast.error(fout instanceof Error ? fout.message : "Opnieuw opslaan mislukt");
177
+ }
178
+ finally {
140
179
  void reload(true);
141
- return;
142
180
  }
143
- const url = URL.createObjectURL(blob);
144
- const a = document.createElement("a");
145
- a.href = url;
146
- a.download = bestandsnaam;
147
- a.click();
148
- URL.revokeObjectURL(url);
149
- }, children: [_jsx(FileDown, { className: "mr-2 h-4 w-4" }), " PDF"] })), _jsxs(Button, { variant: "ghost", size: "sm", onClick: async () => {
181
+ }, children: [_jsx(Save, { className: "mr-2 h-4 w-4" }), " Opslag opnieuw"] })), _jsxs(Button, { variant: "ghost", size: "sm", onClick: async () => {
150
182
  if (!j.layout_id)
151
183
  return;
152
184
  try {
@@ -157,6 +189,12 @@ export function PrintQueuePage() {
157
189
  documentnaam: j.documentnaam ?? "Herprint",
158
190
  kopieen: j.kopieen ?? 1,
159
191
  is_herprint: true,
192
+ bron_job_id: j.id,
193
+ payload: {
194
+ ...(j.payload && typeof j.payload === "object" ? j.payload : {}),
195
+ is_herprint: true,
196
+ bron_job_id: j.id,
197
+ },
160
198
  });
161
199
  toast.success("Opnieuw in de wachtrij gezet");
162
200
  void reload();
@@ -1 +1 @@
1
- {"version":3,"file":"PrintplekDetailPage.d.ts","sourceRoot":"","sources":["../../src/components/PrintplekDetailPage.tsx"],"names":[],"mappings":"AAiDA,wBAAgB,mBAAmB,gCAsYlC"}
1
+ {"version":3,"file":"PrintplekDetailPage.d.ts","sourceRoot":"","sources":["../../src/components/PrintplekDetailPage.tsx"],"names":[],"mappings":"AAiDA,wBAAgB,mBAAmB,gCA0YlC"}
@@ -86,7 +86,7 @@ export function PrintplekDetailPage() {
86
86
  if (!plek) {
87
87
  return (_jsxs("main", { className: "space-y-4 p-6", children: [_jsx(Button, { variant: "ghost", asChild: true, children: _jsxs(Link, { to: "/layouts-printers/printplekken", children: [_jsx(ArrowLeft, { className: "mr-2 h-4 w-4" }), " Terug"] }) }), _jsx("p", { className: "text-muted-foreground text-sm", children: "Deze printplek bestaat niet (meer)." })] }));
88
88
  }
89
- return (_jsxs("main", { className: "space-y-6 p-6", children: [_jsx(PdfMapAutoSaver, {}), _jsx(PdfMapToestemmingBanner, {}), _jsxs("div", { className: "flex flex-wrap items-start justify-between gap-4", children: [_jsxs("div", { className: "space-y-1", children: [_jsx(Button, { variant: "ghost", size: "sm", asChild: true, className: "-ml-2", children: _jsxs(Link, { to: "/layouts-printers/printplekken", children: [_jsx(ArrowLeft, { className: "mr-2 h-4 w-4" }), " Alle printplekken"] }) }), naamBewerken ? (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Input, { value: naam, onChange: (e) => setNaam(e.target.value), className: "w-64" }), _jsx(Button, { size: "icon", onClick: async () => {
89
+ return (_jsxs("main", { className: "space-y-6 p-6", children: [_jsx(PdfMapAutoSaver, {}), _jsx(PdfMapToestemmingBanner, { printplekId: plekId }), _jsxs("div", { className: "flex flex-wrap items-start justify-between gap-4", children: [_jsxs("div", { className: "space-y-1", children: [_jsx(Button, { variant: "ghost", size: "sm", asChild: true, className: "-ml-2", children: _jsxs(Link, { to: "/layouts-printers/printplekken", children: [_jsx(ArrowLeft, { className: "mr-2 h-4 w-4" }), " Alle printplekken"] }) }), naamBewerken ? (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Input, { value: naam, onChange: (e) => setNaam(e.target.value), className: "w-64" }), _jsx(Button, { size: "icon", onClick: async () => {
90
90
  await bewerkPrintplek(plek.id, { naam: naam.trim() });
91
91
  setNaamBewerken(false);
92
92
  await reload(true);
@@ -116,6 +116,6 @@ export function PrintplekDetailPage() {
116
116
  printers.find((p) => p.id === regel?.printer_id) ??
117
117
  null;
118
118
  const isPdf = printer?.afdrukmethode === "pdf";
119
- return (_jsxs("div", { className: "flex flex-wrap items-center justify-between gap-3 rounded-lg border p-2", children: [_jsx("div", { className: "text-sm font-medium", children: documentTypeLabel(d.value) }), printer ? (_jsxs("span", { className: "text-muted-foreground text-sm", children: [printer.naam, isPdf && pdfMap ? (_jsxs("span", { className: "ml-2 text-xs", children: ["\u2192 map \u201C", pdfMap, "\u201D"] })) : null] })) : (_jsxs("span", { className: "text-destructive flex items-center gap-1 text-xs", children: [_jsx(AlertTriangle, { className: "h-3.5 w-3.5" }), " Geen printer"] }))] }, d.value));
119
+ return (_jsxs("div", { className: "flex flex-wrap items-center justify-between gap-3 rounded-lg border p-2", children: [_jsx("div", { className: "text-sm font-medium", children: documentTypeLabel(d.value) }), printer ? (_jsxs("span", { className: "text-muted-foreground text-sm", children: [printer.naam, isPdf ? (_jsxs("span", { className: "ml-2 inline-flex items-center gap-1 rounded-full bg-amber-100 px-2 py-0.5 text-xs font-medium text-amber-900", children: [_jsx(AlertTriangle, { className: "h-3 w-3" }), "Wordt niet geprint, maar opgeslagen als PDF", pdfMap ? ` in “${pdfMap}”` : ""] })) : null] })) : (_jsxs("span", { className: "text-destructive flex items-center gap-1 text-xs", children: [_jsx(AlertTriangle, { className: "h-3.5 w-3.5" }), " Geen printer"] }))] }, d.value));
120
120
  }), !alleDocumenten && overige.length > 0 ? (_jsxs("p", { className: "text-muted-foreground text-xs", children: [overige.length, " documentsoort", overige.length === 1 ? "" : "en", " nog niet ingesteld."] })) : null] })] }), _jsxs(Card, { children: [_jsxs(CardHeader, { children: [_jsxs(CardTitle, { className: "flex items-center gap-2 text-base", children: [_jsx(Link2, { className: "h-4 w-4" }), " Gekoppeld aan"] }), _jsx(CardDescription, { children: "Andere modules (bijvoorbeeld pickstations) kunnen zich aan deze printplek koppelen." })] }), _jsxs(CardContent, { className: "space-y-4", children: [bronnen.length === 0 ? (_jsx("p", { className: "text-muted-foreground text-sm", children: "Nog niets gekoppeld. Kies deze printplek in de andere module (bijvoorbeeld bij een pickstation); de koppeling verschijnt hier vanzelf." })) : (_jsx("div", { className: "space-y-2", children: bronnen.map((b) => (_jsxs("div", { className: "flex flex-wrap items-center gap-3 rounded-lg border px-3 py-2 text-sm", children: [_jsx(Badge, { variant: "secondary", children: b.bron }), _jsxs("span", { className: "min-w-0 flex-1 truncate", children: [b.label ?? b.bron_id, _jsx("span", { className: "text-muted-foreground ml-2 text-xs", children: b.bron_id })] }), _jsxs(Button, { variant: "ghost", size: "sm", onClick: () => void ontkoppel(b.bron, b.bron_id), children: [_jsx(X, { className: "mr-1 h-3.5 w-3.5" }), " Ontkoppelen"] })] }, b.id))) })), weeskoppelingen.length > 0 ? (_jsxs("div", { className: "space-y-2 rounded-lg border border-dashed p-3", children: [_jsxs("p", { className: "text-muted-foreground flex items-center gap-2 text-xs", children: [_jsx(AlertTriangle, { className: "h-3.5 w-3.5" }), " Koppelingen zonder bestaande printplek:"] }), weeskoppelingen.map((b) => (_jsxs("div", { className: "flex flex-wrap items-center gap-3 text-sm", children: [_jsx("span", { className: "min-w-0 flex-1 truncate", children: b.label ?? `${b.bron} ${b.bron_id}` }), _jsxs(Button, { variant: "ghost", size: "sm", onClick: () => void ontkoppel(b.bron, b.bron_id), children: [_jsx(Trash2, { className: "mr-1 h-3.5 w-3.5" }), " Opruimen"] })] }, b.id)))] })) : null] })] })] }));
121
121
  }
@@ -1 +1 @@
1
- {"version":3,"file":"PrintplekWizard.d.ts","sourceRoot":"","sources":["../../src/components/PrintplekWizard.tsx"],"names":[],"mappings":"AAiEA,wBAAgB,eAAe,CAAC,EAC9B,IAAI,EACJ,YAAY,EACZ,WAAW,EACX,OAAO,GACR,EAAE;IACD,IAAI,EAAE,OAAO,CAAC;IACd,YAAY,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IACnC,sEAAsE;IACtE,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,OAAO,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;CACzC,+BAsjBA"}
1
+ {"version":3,"file":"PrintplekWizard.d.ts","sourceRoot":"","sources":["../../src/components/PrintplekWizard.tsx"],"names":[],"mappings":"AAiEA,wBAAgB,eAAe,CAAC,EAC9B,IAAI,EACJ,YAAY,EACZ,WAAW,EACX,OAAO,GACR,EAAE;IACD,IAAI,EAAE,OAAO,CAAC;IACd,YAAY,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IACnC,sEAAsE;IACtE,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,OAAO,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;CACzC,+BA0jBA"}