@flowselections/layout-maker 1.38.4 → 1.45.8

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 (124) hide show
  1. package/README.md +139 -139
  2. package/dist-lib/components/AfdrukKnop.d.ts +19 -19
  3. package/dist-lib/components/AfleverbriefBuilderPage.d.ts +10 -0
  4. package/dist-lib/components/AfleverbriefBuilderPage.d.ts.map +1 -0
  5. package/dist-lib/components/AfleverbriefBuilderPage.js +101 -0
  6. package/dist-lib/components/BlokOntwerperPage.d.ts.map +1 -1
  7. package/dist-lib/components/BlokOntwerperPage.js +8 -3
  8. package/dist-lib/components/ComputersLijst.d.ts.map +1 -1
  9. package/dist-lib/components/ComputersLijst.js +1 -1
  10. package/dist-lib/components/FustKolomVolgorde.d.ts +11 -0
  11. package/dist-lib/components/FustKolomVolgorde.d.ts.map +1 -0
  12. package/dist-lib/components/FustKolomVolgorde.js +52 -0
  13. package/dist-lib/components/LayoutsPage.d.ts.map +1 -1
  14. package/dist-lib/components/LayoutsPage.js +10 -9
  15. package/dist-lib/components/NetwerkPrintersDialog.d.ts +7 -7
  16. package/dist-lib/components/PakbonBuilderPage.d.ts.map +1 -1
  17. package/dist-lib/components/PakbonBuilderPage.js +32 -14
  18. package/dist-lib/components/PakbonPreview.d.ts.map +1 -1
  19. package/dist-lib/components/PakbonPreview.js +21 -14
  20. package/dist-lib/components/PakbonSectiesPanel.d.ts +4 -2
  21. package/dist-lib/components/PakbonSectiesPanel.d.ts.map +1 -1
  22. package/dist-lib/components/PakbonSectiesPanel.js +5 -3
  23. package/dist-lib/components/PdfMapToestemmingBanner.d.ts +2 -1
  24. package/dist-lib/components/PdfMapToestemmingBanner.d.ts.map +1 -1
  25. package/dist-lib/components/PdfMapToestemmingBanner.js +73 -14
  26. package/dist-lib/components/PdfVoorbeeld.d.ts +20 -0
  27. package/dist-lib/components/PdfVoorbeeld.d.ts.map +1 -0
  28. package/dist-lib/components/PdfVoorbeeld.js +95 -0
  29. package/dist-lib/components/PrintProgrammaUitleg.d.ts.map +1 -1
  30. package/dist-lib/components/PrintProgrammaUitleg.js +1 -1
  31. package/dist-lib/components/PrintQueuePage.d.ts.map +1 -1
  32. package/dist-lib/components/PrintQueuePage.js +81 -43
  33. package/dist-lib/components/PrinterSetupWizard.js +3 -3
  34. package/dist-lib/components/PrintersRedirectPage.d.ts +1 -1
  35. package/dist-lib/components/PrintersRedirectPage.js +14 -14
  36. package/dist-lib/components/PrintplekDetailPage.d.ts.map +1 -1
  37. package/dist-lib/components/PrintplekDetailPage.js +2 -2
  38. package/dist-lib/components/PrintplekWizard.d.ts.map +1 -1
  39. package/dist-lib/components/PrintplekWizard.js +6 -2
  40. package/dist-lib/components/ProductiebonBuilderPage.d.ts.map +1 -1
  41. package/dist-lib/components/ProductiebonBuilderPage.js +13 -7
  42. package/dist-lib/index.d.ts +13 -5
  43. package/dist-lib/index.d.ts.map +1 -1
  44. package/dist-lib/index.js +10 -5
  45. package/dist-lib/integrations/supabase/auth-middleware.d.ts +2008 -118
  46. package/dist-lib/integrations/supabase/auth-middleware.d.ts.map +1 -1
  47. package/dist-lib/integrations/supabase/client.server.d.ts +2008 -118
  48. package/dist-lib/integrations/supabase/client.server.d.ts.map +1 -1
  49. package/dist-lib/integrations/supabase/client.server.js +1 -1
  50. package/dist-lib/integrations/supabase/moduleClient.d.ts +2008 -118
  51. package/dist-lib/integrations/supabase/moduleClient.d.ts.map +1 -1
  52. package/dist-lib/integrations/supabase/types.d.ts +2031 -99
  53. package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
  54. package/dist-lib/integrations/supabase/types.js +1 -0
  55. package/dist-lib/lib/aiLayout.functions.d.ts +2008 -118
  56. package/dist-lib/lib/aiLayout.functions.d.ts.map +1 -1
  57. package/dist-lib/lib/aiLayout.server.js +9 -9
  58. package/dist-lib/lib/browserPrint.js +1 -1
  59. package/dist-lib/lib/documentVoorbeeld.functions.d.ts +23 -0
  60. package/dist-lib/lib/documentVoorbeeld.functions.d.ts.map +1 -0
  61. package/dist-lib/lib/documentVoorbeeld.functions.js +37 -0
  62. package/dist-lib/lib/fontMetrics.d.ts +15 -0
  63. package/dist-lib/lib/fontMetrics.d.ts.map +1 -0
  64. package/dist-lib/lib/fontMetrics.js +118 -0
  65. package/dist-lib/lib/htmlNaarPdf.server.d.ts +2 -2
  66. package/dist-lib/lib/htmlNaarPdf.server.d.ts.map +1 -1
  67. package/dist-lib/lib/htmlNaarPdf.server.js +2 -1
  68. package/dist-lib/lib/htmlPdf.server.d.ts +11 -0
  69. package/dist-lib/lib/htmlPdf.server.d.ts.map +1 -1
  70. package/dist-lib/lib/htmlPdf.server.js +118 -15
  71. package/dist-lib/lib/layoutPreview.js +7 -7
  72. package/dist-lib/lib/orderData.server.d.ts.map +1 -1
  73. package/dist-lib/lib/orderData.server.js +43 -16
  74. package/dist-lib/lib/pakbonBarcodeVerzoek.d.ts +39 -0
  75. package/dist-lib/lib/pakbonBarcodeVerzoek.d.ts.map +1 -0
  76. package/dist-lib/lib/pakbonBarcodeVerzoek.js +80 -0
  77. package/dist-lib/lib/pakbonBuilder.d.ts +62 -3
  78. package/dist-lib/lib/pakbonBuilder.d.ts.map +1 -1
  79. package/dist-lib/lib/pakbonBuilder.js +386 -50
  80. package/dist-lib/lib/pakbonContent.d.ts +41 -0
  81. package/dist-lib/lib/pakbonContent.d.ts.map +1 -0
  82. package/dist-lib/lib/pakbonContent.js +668 -0
  83. package/dist-lib/lib/pakbonDocument.d.ts +199 -0
  84. package/dist-lib/lib/pakbonDocument.d.ts.map +1 -0
  85. package/dist-lib/lib/pakbonDocument.js +735 -0
  86. package/dist-lib/lib/pakbonHtml.d.ts +7 -0
  87. package/dist-lib/lib/pakbonHtml.d.ts.map +1 -1
  88. package/dist-lib/lib/pakbonHtml.js +142 -126
  89. package/dist-lib/lib/pakbonPresentation.d.ts +8 -0
  90. package/dist-lib/lib/pakbonPresentation.d.ts.map +1 -0
  91. package/dist-lib/lib/pakbonPresentation.js +31 -0
  92. package/dist-lib/lib/pakbonScan.d.ts.map +1 -1
  93. package/dist-lib/lib/pakbonScan.js +20 -38
  94. package/dist-lib/lib/pdfJobSaver.d.ts +10 -6
  95. package/dist-lib/lib/pdfJobSaver.d.ts.map +1 -1
  96. package/dist-lib/lib/pdfJobSaver.js +302 -99
  97. package/dist-lib/lib/pdfMap.d.ts +37 -2
  98. package/dist-lib/lib/pdfMap.d.ts.map +1 -1
  99. package/dist-lib/lib/pdfMap.js +120 -7
  100. package/dist-lib/lib/pdfOpslagStatus.d.ts +23 -0
  101. package/dist-lib/lib/pdfOpslagStatus.d.ts.map +1 -0
  102. package/dist-lib/lib/pdfOpslagStatus.js +74 -0
  103. package/dist-lib/lib/printApi.d.ts +31 -0
  104. package/dist-lib/lib/printApi.d.ts.map +1 -1
  105. package/dist-lib/lib/printApi.js +62 -1
  106. package/dist-lib/lib/printQueue.server.d.ts +6 -0
  107. package/dist-lib/lib/printQueue.server.d.ts.map +1 -1
  108. package/dist-lib/lib/printQueue.server.js +450 -94
  109. package/dist-lib/lib/printService.functions.d.ts +7916 -356
  110. package/dist-lib/lib/printService.functions.d.ts.map +1 -1
  111. package/dist-lib/lib/productiebonHtml.js +15 -15
  112. package/dist-lib/lib/profielHtml.d.ts.map +1 -1
  113. package/dist-lib/lib/profielHtml.js +26 -19
  114. package/dist-lib/lib/supabaseAdmin.server.d.ts +5 -5
  115. package/dist-lib/lib/supabaseAdmin.server.js +43 -43
  116. package/dist-lib/lib/testpagina.js +3 -3
  117. package/dist-lib/lib/testprint.js +4 -4
  118. package/dist-lib/lib/useDocumentData.d.ts +8 -0
  119. package/dist-lib/lib/useDocumentData.d.ts.map +1 -1
  120. package/dist-lib/lib/useDocumentData.js +1 -1
  121. package/dist-lib/lib/voorbeeldPrint.d.ts +3 -0
  122. package/dist-lib/lib/voorbeeldPrint.d.ts.map +1 -1
  123. package/dist-lib/lib/voorbeeldPrint.js +65 -17
  124. package/package.json +158 -151
@@ -1,8 +1,8 @@
1
- export declare function NetwerkPrintersDialog({ open, onOpenChange, huidigePrinterId, huidigePrinterNaam, onKlaar, }: {
2
- open: boolean;
3
- onOpenChange: (v: boolean) => void;
4
- huidigePrinterId?: string | null;
5
- huidigePrinterNaam?: string | null;
6
- onKlaar?: () => void;
7
- }): import("react/jsx-runtime").JSX.Element;
1
+ export declare function NetwerkPrintersDialog({ open, onOpenChange, huidigePrinterId, huidigePrinterNaam, onKlaar, }: {
2
+ open: boolean;
3
+ onOpenChange: (v: boolean) => void;
4
+ huidigePrinterId?: string | null;
5
+ huidigePrinterNaam?: string | null;
6
+ onKlaar?: () => void;
7
+ }): import("react/jsx-runtime").JSX.Element;
8
8
  //# sourceMappingURL=NetwerkPrintersDialog.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"PakbonBuilderPage.d.ts","sourceRoot":"","sources":["../../src/components/PakbonBuilderPage.tsx"],"names":[],"mappings":"AA+BA,iFAAiF;AACjF,wBAAgB,iBAAiB,CAAC,EAAE,QAAQ,EAAE,YAAY,EAAE,GAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAO,+BAqKvF"}
1
+ {"version":3,"file":"PakbonBuilderPage.d.ts","sourceRoot":"","sources":["../../src/components/PakbonBuilderPage.tsx"],"names":[],"mappings":"AA+BA,iFAAiF;AACjF,wBAAgB,iBAAiB,CAAC,EAAE,QAAQ,EAAE,YAAY,EAAE,GAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAO,+BA8LvF"}
@@ -2,11 +2,11 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useEffect, useMemo, useState } from "react";
3
3
  import { Link } from "@tanstack/react-router";
4
4
  import { Badge, Button, Card, CardContent, CardDescription, CardHeader, CardTitle, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, toast, } from "@flowselections/core";
5
- import { ArrowLeft, Loader2, Printer, Save } from "lucide-react";
5
+ import { AlertTriangle, ArrowLeft, CheckCircle2, Loader2, Printer, Save } from "lucide-react";
6
6
  import { PakbonSectiesPanel } from "./PakbonSectiesPanel.js";
7
- import { PakbonPreview } from "./PakbonPreview.js";
7
+ import { PdfVoorbeeld } from "./PdfVoorbeeld.js";
8
8
  import { FormaatOrientatieBar } from "./FormaatOrientatieBar.js";
9
- import { leesPakbonConfig } from "../lib/pakbonBuilder.js";
9
+ import { leesPakbonConfig, pickMethode } from "../lib/pakbonBuilder.js";
10
10
  import { renderPakbonDocument, voorbeeldPakbonOrder } from "../lib/pakbonHtml.js";
11
11
  import { voorbeeldPrint } from "../lib/voorbeeldPrint.js";
12
12
  import { haalPrintLogos } from "../lib/logoLibrary.js";
@@ -21,14 +21,22 @@ export function PakbonBuilderPage({ layoutId: layoutIdProp } = {}) {
21
21
  const [bezig, setBezig] = useState(false);
22
22
  const [printen, setPrinten] = useState(false);
23
23
  const [voorbeeldVariant, setVoorbeeldVariant] = useState("full_cart");
24
+ const [logos, setLogos] = useState([]);
25
+ const [controle, setControle] = useState("controleren");
26
+ useEffect(() => {
27
+ void haalPrintLogos()
28
+ .then((l) => setLogos(Array.isArray(l) ? l : []))
29
+ .catch(() => setLogos([]));
30
+ }, []);
24
31
  useEffect(() => {
25
32
  if (layout)
26
33
  setConfig(leesPakbonConfig(layout.config));
27
34
  }, [layout?.id]);
28
35
  const breedte = useMemo(() => (layout?.orientatie === "landscape" ? layout?.hoogte_mm : layout?.breedte_mm) ?? 210, [layout]);
29
36
  const hoogte = useMemo(() => (layout?.orientatie === "landscape" ? layout?.breedte_mm : layout?.hoogte_mm) ?? 297, [layout]);
37
+ const marge = Number(layout?.marge_boven ?? 10);
30
38
  const opslaan = async () => {
31
- if (!layout || !config)
39
+ if (!layout || !config || controle !== "gereed")
32
40
  return;
33
41
  setBezig(true);
34
42
  try {
@@ -42,19 +50,29 @@ export function PakbonBuilderPage({ layoutId: layoutIdProp } = {}) {
42
50
  setBezig(false);
43
51
  }
44
52
  };
45
- const voorbeeldPrinten = async () => {
53
+ // Eén bron voor scherm én print: exact dezelfde HTML gaat naar de
54
+ // server-PDF van het voorbeeld en naar de printopdracht.
55
+ const voorbeeldHtml = useMemo(() => {
46
56
  if (!config)
57
+ return "";
58
+ const beideVarianten = voorbeeldVariant === "full_cart" &&
59
+ pickMethode(Array.isArray(config?.secties) ? config.secties : []) === "beide";
60
+ return renderPakbonDocument(config, voorbeeldPakbonOrder(), {
61
+ logos: logos,
62
+ breedteMm: breedte,
63
+ hoogteMm: hoogte,
64
+ margeMm: marge,
65
+ layoutVariant: voorbeeldVariant,
66
+ voorbeeldBeideVarianten: beideVarianten,
67
+ titel: layout?.naam ?? "Pakbon",
68
+ });
69
+ }, [config, logos, breedte, hoogte, marge, voorbeeldVariant, layout?.naam]);
70
+ const voorbeeldPrinten = async () => {
71
+ if (!config || controle !== "gereed")
47
72
  return;
48
73
  setPrinten(true);
49
74
  try {
50
- const logos = await haalPrintLogos();
51
- const html = renderPakbonDocument(config, voorbeeldPakbonOrder(), {
52
- logos,
53
- breedteMm: breedte,
54
- hoogteMm: hoogte,
55
- layoutVariant: voorbeeldVariant,
56
- titel: layout?.naam ?? "Pakbon",
57
- });
75
+ const html = voorbeeldHtml;
58
76
  await voorbeeldPrint({
59
77
  ...(layout ?? {}),
60
78
  id: layout?.id ?? layoutId,
@@ -76,5 +94,5 @@ export function PakbonBuilderPage({ layoutId: layoutIdProp } = {}) {
76
94
  return _jsx("main", { className: "p-6 text-sm text-muted-foreground", children: "Layout laden\u2026" });
77
95
  if (!layout || !config)
78
96
  return _jsx("main", { className: "p-6 text-sm text-muted-foreground", children: "Layout niet gevonden." });
79
- return (_jsxs("main", { className: "space-y-4 p-6", children: [_jsxs("div", { className: "flex flex-wrap items-center justify-between gap-3", children: [_jsxs("div", { className: "flex items-center gap-3", children: [_jsx(Button, { variant: "ghost", size: "sm", asChild: true, children: _jsxs(Link, { to: "/layouts-printers", children: [_jsx(ArrowLeft, { className: "mr-2 h-4 w-4" }), " Layouts"] }) }), _jsxs("div", { children: [_jsx("h1", { className: "text-lg font-semibold", children: layout.naam }), _jsxs("p", { className: "text-xs text-muted-foreground", children: ["Pakbon opbouwen uit secties \u00B7 ", config.secties.length, " secties"] })] })] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(LayoutPrinterSelect, { layoutId: layoutId }), _jsxs(Button, { size: "sm", variant: "outline", onClick: () => void voorbeeldPrinten(), disabled: printen, children: [printen ? (_jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" })) : (_jsx(Printer, { className: "mr-2 h-4 w-4" })), "Voorbeeld printen"] }), _jsx(Button, { size: "sm", variant: "outline", asChild: true, children: _jsx(Link, { to: "/layouts-printers/blokken/$layoutId", params: { layoutId }, children: "Blokken ontwerpen" }) }), _jsxs(Button, { size: "sm", onClick: opslaan, disabled: bezig, children: [_jsx(Save, { className: "mr-2 h-4 w-4" }), " Opslaan"] })] })] }), _jsx(FormaatOrientatieBar, { layout: layout, onChange: (patch) => setLayout((l) => (l ? { ...l, ...patch } : l)) }), _jsxs("div", { className: "grid items-start gap-4 lg:grid-cols-[400px_1fr]", children: [_jsxs(Card, { className: "flex h-[calc(100vh-14rem)] min-h-[420px] flex-col overflow-hidden lg:sticky lg:top-4", children: [_jsxs(CardHeader, { className: "pb-3", children: [_jsx(CardTitle, { className: "text-base", children: "Secties" }), _jsx(CardDescription, { children: "Voeg blokken toe, wijzig de volgorde en stel elk blok afzonderlijk in." })] }), _jsx(CardContent, { className: "min-h-0 flex-1 overflow-y-auto", children: _jsx(PakbonSectiesPanel, { config: config, onChange: setConfig }) })] }), _jsxs(Card, { className: "flex h-[calc(100vh-14rem)] min-h-[420px] flex-col overflow-hidden", children: [_jsxs(CardHeader, { className: "flex-row items-center justify-between space-y-0 pb-3", children: [_jsxs("div", { children: [_jsx(CardTitle, { className: "text-base", children: "Live voorbeeld" }), _jsx(CardDescription, { children: "Bekijk een volle kar of een deelkar met alleen de te verzamelen fusten." })] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsxs(Select, { value: voorbeeldVariant, onValueChange: (value) => setVoorbeeldVariant(value), children: [_jsx(SelectTrigger, { className: "w-48", "aria-label": "Voorbeeldvariant", children: _jsx(SelectValue, {}) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "full_cart", children: "Volle kar" }), _jsx(SelectItem, { value: "partial_order", children: "Deelkar" })] })] }), _jsx(Badge, { variant: "secondary", children: "Direct bijgewerkt" })] })] }), _jsx(CardContent, { className: "min-h-0 flex-1 overflow-hidden", children: _jsx("div", { className: "h-full overflow-auto rounded-md border bg-muted/30 p-2", children: _jsx(PakbonPreview, { config: config, breedteMm: breedte, hoogteMm: hoogte, variant: voorbeeldVariant }) }) })] })] })] }));
97
+ return (_jsxs("main", { className: "space-y-4 p-6", children: [_jsxs("div", { className: "flex flex-wrap items-center justify-between gap-3", children: [_jsxs("div", { className: "flex items-center gap-3", children: [_jsx(Button, { variant: "ghost", size: "sm", asChild: true, children: _jsxs(Link, { to: "/layouts-printers", children: [_jsx(ArrowLeft, { className: "mr-2 h-4 w-4" }), " Layouts"] }) }), _jsxs("div", { children: [_jsx("h1", { className: "text-lg font-semibold", children: layout.naam }), _jsxs("p", { className: "text-xs text-muted-foreground", children: ["Pakbon opbouwen uit secties \u00B7 ", config.secties.length, " secties"] })] })] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(LayoutPrinterSelect, { layoutId: layoutId }), _jsxs(Button, { size: "sm", variant: "outline", onClick: () => void voorbeeldPrinten(), disabled: printen || controle !== "gereed", children: [printen ? (_jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" })) : (_jsx(Printer, { className: "mr-2 h-4 w-4" })), "Voorbeeld printen"] }), _jsx(Button, { size: "sm", variant: "outline", asChild: true, children: _jsx(Link, { to: "/layouts-printers/blokken/$layoutId", params: { layoutId }, children: "Blokken ontwerpen" }) }), _jsxs(Button, { size: "sm", onClick: opslaan, disabled: bezig || controle !== "gereed", children: [_jsx(Save, { className: "mr-2 h-4 w-4" }), " Opslaan"] })] })] }), _jsx(FormaatOrientatieBar, { layout: layout, onChange: (patch) => setLayout((l) => (l ? { ...l, ...patch } : l)) }), _jsxs("div", { className: "grid items-start gap-4 lg:grid-cols-[400px_1fr]", children: [_jsxs(Card, { className: "flex h-[calc(100vh-14rem)] min-h-[420px] flex-col overflow-hidden lg:sticky lg:top-4", children: [_jsxs(CardHeader, { className: "pb-3", children: [_jsx(CardTitle, { className: "text-base", children: "Secties" }), _jsx(CardDescription, { children: "Voeg blokken toe, wijzig de volgorde en stel elk blok afzonderlijk in." })] }), _jsx(CardContent, { className: "min-h-0 flex-1 overflow-y-auto", children: _jsx(PakbonSectiesPanel, { config: config, onChange: setConfig }) })] }), _jsxs(Card, { className: "flex h-[calc(100vh-14rem)] min-h-[420px] flex-col overflow-hidden", children: [_jsxs(CardHeader, { className: "flex-row items-center justify-between space-y-0 pb-3", children: [_jsxs("div", { children: [_jsx(CardTitle, { className: "text-base", children: "Live voorbeeld" }), _jsx(CardDescription, { children: "Bekijk een volle kar of een deelkar met alleen de te verzamelen fusten." })] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsxs(Select, { value: voorbeeldVariant, onValueChange: (value) => setVoorbeeldVariant(value), children: [_jsx(SelectTrigger, { className: "w-48", "aria-label": "Voorbeeldvariant", children: _jsx(SelectValue, {}) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "full_cart", children: "Volle kar" }), _jsx(SelectItem, { value: "partial_order", children: "Deelkar" })] })] }), _jsxs(Badge, { variant: controle === "afwijking" ? "destructive" : "secondary", children: [controle === "controleren" ? _jsx(Loader2, { className: "mr-1 h-3 w-3 animate-spin" }) : controle === "gereed" ? _jsx(CheckCircle2, { className: "mr-1 h-3 w-3" }) : _jsx(AlertTriangle, { className: "mr-1 h-3 w-3" }), controle === "controleren" ? "Controleren" : controle === "gereed" ? "Afdruk gereed" : "Afwijking gevonden"] })] })] }), _jsx(CardContent, { className: "min-h-0 flex-1 overflow-hidden", children: _jsx("div", { className: "h-full rounded-md border bg-muted/30 p-2", children: voorbeeldHtml ? (_jsx(PdfVoorbeeld, { html: voorbeeldHtml, breedteMm: breedte, hoogteMm: hoogte, margeMm: marge, onControle: (status) => setControle(status) })) : null }) })] })] })] }));
80
98
  }
@@ -1 +1 @@
1
- {"version":3,"file":"PakbonPreview.d.ts","sourceRoot":"","sources":["../../src/components/PakbonPreview.tsx"],"names":[],"mappings":"AAIA,OAAO,EAsBL,KAAK,YAAY,EAEjB,KAAK,YAAY,EACjB,KAAK,mBAAmB,EACzB,MAAM,sBAAsB,CAAC;AA6B9B,wBAAgB,aAAa,CAAC,EAC5B,MAAM,EACN,SAAe,EACf,QAAc,EACd,OAAc,GACf,EAAE;IACD,MAAM,EAAE,YAAY,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,oFAAoF;IACpF,OAAO,CAAC,EAAE,mBAAmB,GAAG,IAAI,CAAC;CACtC,+BAiJA;AAiLD,oEAAoE;AACpE,wBAAgB,mBAAmB,CAAC,EAClC,MAAM,EACN,MAAM,EACN,SAAS,GACV,EAAE;IACD,MAAM,EAAE,YAAY,CAAC;IACrB,MAAM,EAAE,YAAY,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,+BAmCA"}
1
+ {"version":3,"file":"PakbonPreview.d.ts","sourceRoot":"","sources":["../../src/components/PakbonPreview.tsx"],"names":[],"mappings":"AAKA,OAAO,EAyBL,KAAK,YAAY,EAEjB,KAAK,YAAY,EACjB,KAAK,mBAAmB,EACzB,MAAM,sBAAsB,CAAC;AA6B9B,wBAAgB,aAAa,CAAC,EAC5B,MAAM,EACN,SAAe,EACf,QAAc,EACd,OAAc,GACf,EAAE;IACD,MAAM,EAAE,YAAY,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,oFAAoF;IACpF,OAAO,CAAC,EAAE,mBAAmB,GAAG,IAAI,CAAC;CACtC,+BAiJA;AAiLD,oEAAoE;AACpE,wBAAgB,mBAAmB,CAAC,EAClC,MAAM,EACN,MAAM,EACN,SAAS,GACV,EAAE;IACD,MAAM,EAAE,YAAY,CAAC;IACrB,MAAM,EAAE,YAAY,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,+BAmCA"}
@@ -3,7 +3,8 @@ import { useMemo } from "react";
3
3
  import { PaginaDocument } from "./PaginaDocument.js";
4
4
  import { useLogos } from "../lib/logoLibrary.js";
5
5
  import { code128Bars } from "../lib/code128.js";
6
- import { DEMO_ORDER, deelkarVoorbeeldOrder, conditieContext, groepeerIdentiekeKarren, groepeerIdentiekeLagen, karBundelTekst, fustVerzamellijst, pickMethode, pakbonLayoutVariant, productVeldBreedtes, sectieOnderdruktVoorVariant, kolomLabel, laagBundelTekst, sectieZichtbaar, sorteerProducten, totalen, vulVeldcodes, } from "../lib/pakbonBuilder.js";
6
+ import { karMetadata } from "../lib/pakbonPresentation.js";
7
+ import { DEMO_ORDER, deelkarVoorbeeldOrder, conditieContext, groepeerIdentiekeKarren, groepeerIdentiekeLagen, karBundelTekst, fustVerzamellijst, fustRegels, FUSTREGEL_LABELS, FUSTREGEL_RECHTS, pickMethode, pakbonLayoutVariant, productVeldBreedtes, sectieOnderdruktVoorVariant, kolomLabel, laagBundelTekst, sectieZichtbaar, sorteerProducten, totalen, vulVeldcodes, } from "../lib/pakbonBuilder.js";
7
8
  /** Voorbeeldbarcode: alleen bedoeld om de plek en hoogte te tonen. */
8
9
  function BarcodeStrepen({ waarde, hoogteMm = 15 }) {
9
10
  const bars = code128Bars(waarde);
@@ -146,7 +147,7 @@ function BundelMelding({ tekst }) {
146
147
  return (_jsx("div", { className: "rounded border border-dashed px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wide", children: tekst }));
147
148
  }
148
149
  function KarBlok({ sectie, kar, velden, lagen, lagenSectie, productSectie, fustSectie, omkaderd, aantal = 1, }) {
149
- return (_jsxs("div", { className: `space-y-2 ${omkaderd ? "rounded border p-2" : ""}`, children: [_jsx(KarKop, { sectie: sectie, kar: kar, velden: velden, bundelTekst: karBundelTekst(aantal) }), lagen.length > 0 && lagenSectie && (_jsx(LagenView, { sectie: lagenSectie, lagen: lagen, productSectie: productSectie, fustSectie: fustSectie })), fustSectie && optS(fustSectie, "modus", "totaal") === "kar" && (_jsx(FustTabel, { velden: lijst(fustSectie, "velden"), fusten: lagen.flatMap((l) => l.fusten), titel: `Fusten kar ${kar.karnummer}` })), kar.barcode ? (_jsxs("div", { className: "space-y-1 pt-2", children: [_jsx(BarcodeStrepen, { waarde: kar.barcode, hoogteMm: Number(opt(sectie, "barcodeHoogte", 15)) }), _jsx("div", { className: "text-[8px] text-muted-foreground", children: "Voorbeeld \u2014 iedere kar krijgt de eigen barcode van de Engine." })] })) : null] }));
150
+ return (_jsxs("div", { className: `space-y-2 ${omkaderd ? "rounded border p-2" : ""}`, children: [_jsx(KarKop, { sectie: sectie, kar: kar, velden: velden, bundelTekst: karBundelTekst(aantal) }), kar.barcode ? (_jsxs("div", { className: "space-y-1 py-1", children: [_jsx(BarcodeStrepen, { waarde: kar.barcode, hoogteMm: Number(opt(sectie, "barcodeHoogte", 15)) }), _jsx("div", { className: "text-[8px] text-muted-foreground", children: "Voorbeeld \u2014 iedere kar krijgt de eigen barcode van de Engine." })] })) : null, lagen.length > 0 && lagenSectie && (_jsx(LagenView, { sectie: lagenSectie, lagen: lagen, productSectie: productSectie, fustSectie: fustSectie })), fustSectie && optS(fustSectie, "modus", "totaal") === "kar" && (_jsx(FustTabel, { velden: lijst(fustSectie, "velden"), fusten: lagen.flatMap((l) => l.fusten), titel: `Fusten kar ${kar.karnummer}` }))] }));
150
151
  }
151
152
  /** Losse weergave van één blok — gebruikt door de blokontwerper. */
152
153
  export function PakbonSectiePreview({ sectie, config, breedteMm, }) {
@@ -214,6 +215,21 @@ function SectieView({ sectie, order, zoek, }) {
214
215
  const groepen = groepeer(order, groepering, optS(s, "sortering", "eigen"));
215
216
  return (_jsx("div", { className: "space-y-2", children: groepen.map((g) => (_jsxs("div", { className: "space-y-1", children: [g.titel !== "—" && _jsx("div", { className: "font-semibold", children: g.titel }), _jsx(RegelWeergave, { producten: g.producten, kolommen: kolommen, weergave: optS(s, "weergave", "tabel"), toonKoppen: opt(s, "toonKoppen", true), zebra: opt(s, "zebra", false), randen: opt(s, "randen", true) })] }, g.titel))) }));
216
217
  }
218
+ case "fustregels": {
219
+ const velden = lijst(s, "velden").filter((v) => FUSTREGEL_LABELS[v]);
220
+ const regels = fustRegels(order.karren, {
221
+ comprimeren: opt(s, "comprimeren", true) !== false,
222
+ sortering: optS(s, "sortering", "fustsoort"),
223
+ });
224
+ if (!velden.length || !regels.length)
225
+ return null;
226
+ const randen = opt(s, "randen", true) !== false;
227
+ const zebra = opt(s, "zebra", false);
228
+ const cel = randen ? "border-b border-neutral-300 px-1 py-0.5" : "px-1 py-0.5";
229
+ const totaalFusten = regels.reduce((n, r) => n + r.fust_aantal, 0);
230
+ const totaalPlanten = regels.reduce((n, r) => n + r.aantal, 0);
231
+ return (_jsxs("div", { className: "space-y-1", children: [_jsxs("table", { className: "w-full border-collapse", children: [opt(s, "toonKoppen", true) !== false && (_jsx("thead", { children: _jsx("tr", { children: velden.map((v) => (_jsx("th", { className: `${cel} font-semibold ${FUSTREGEL_RECHTS.has(v) ? "text-right" : "text-left"}`, children: FUSTREGEL_LABELS[v] }, v))) }) })), _jsx("tbody", { children: regels.map((r, i) => (_jsx("tr", { className: zebra && i % 2 === 1 ? "bg-neutral-100" : undefined, children: velden.map((v) => (_jsx("td", { className: `${cel} ${FUSTREGEL_RECHTS.has(v) ? "text-right" : "text-left"}`, children: String(r[v] ?? "") }, v))) }, i))) })] }), opt(s, "toonTotaal", true) !== false && (_jsxs("div", { className: "flex justify-end gap-3 font-semibold", children: [_jsxs("span", { children: [totaalFusten, " fusten"] }), _jsxs("span", { children: [totaalPlanten, " planten"] })] }))] }));
232
+ }
217
233
  case "pickmethode":
218
234
  if (optS(s, "methode", "kar") === "kar")
219
235
  return null;
@@ -279,17 +295,7 @@ function KarKop({ sectie, kar, velden, bundelTekst = "" }) {
279
295
  .replace(/{chauffeur}/g, kar.chauffeur)
280
296
  .replace(/{lagen}/g, String(kar.lagen.length))
281
297
  .replace(/{producten}/g, String(kar.lagen.reduce((n, l) => n + l.producten.length, 0)));
282
- const info = [];
283
- if (velden.includes("lagen"))
284
- info.push(`${kar.lagen.length} lagen`);
285
- if (velden.includes("producten"))
286
- info.push(`${kar.lagen.reduce((n, l) => n + l.producten.reduce((m, p) => m + p.aantal, 0), 0)} producten`);
287
- if (velden.includes("gewicht"))
288
- info.push(kar.gewicht);
289
- if (velden.includes("chauffeur"))
290
- info.push(kar.chauffeur);
291
- if (velden.includes("route"))
292
- info.push(kar.route);
298
+ const info = karMetadata(kar, velden);
293
299
  const groot = scheiding === "kop";
294
300
  const gekleurd = scheiding === "kleur" || groot;
295
301
  const toonNummer = velden.includes("karnummer");
@@ -372,9 +378,10 @@ const FUST_KOLOM_LABELS = {
372
378
  locatie: "Locatie",
373
379
  fustsoort: "Tray/fust",
374
380
  planten_per_fust: "Pl./fust",
381
+ fust_aantal: "Fusten",
375
382
  aantal: "Planten",
376
383
  };
377
- const FUST_RECHTS = new Set(["planten_per_fust", "aantal"]);
384
+ const FUST_RECHTS = new Set(["planten_per_fust", "fust_aantal", "aantal"]);
378
385
  /** Verzamellijst met alle te picken fusten per fusttype, incl. producten. */
379
386
  function FustVerzamellijst({ order, kop = "Te verzamelen fusten — karindeling zelf bepalen", velden, toonFusthoogte = true, toonTotaal = true, }) {
380
387
  const groepen = fustVerzamellijst(order?.karren);
@@ -1,8 +1,10 @@
1
- import { type PakbonConfig, type PakbonSectie, type PickMethode } from "../lib/pakbonBuilder.js";
1
+ import { type PakbonConfig, type PakbonSectie, type PakbonSectieType, type PickMethode } from "../lib/pakbonBuilder.js";
2
2
  /** Beheer van de secties waaruit de pakbon is opgebouwd. */
3
- export declare function PakbonSectiesPanel({ config, onChange, }: {
3
+ export declare function PakbonSectiesPanel({ config, onChange, beschikbareTypes, }: {
4
4
  config: PakbonConfig;
5
5
  onChange: (c: PakbonConfig) => void;
6
+ /** Beperkt de keuzelijst met blokken (bijvoorbeeld voor de afleverbrief). */
7
+ beschikbareTypes?: PakbonSectieType[];
6
8
  }): import("react").JSX.Element;
7
9
  /** Volledige instellingen van één blok — herbruikt in de blokontwerper. */
8
10
  export declare function PakbonSectieEditor({ sectie, onChange, methode, }: {
@@ -1 +1 @@
1
- {"version":3,"file":"PakbonSectiesPanel.d.ts","sourceRoot":"","sources":["../../src/components/PakbonSectiesPanel.tsx"],"names":[],"mappings":"AAmBA,OAAO,EASL,KAAK,YAAY,EAEjB,KAAK,YAAY,EAEjB,KAAK,WAAW,EACjB,MAAM,sBAAsB,CAAC;AAE9B,4DAA4D;AAC5D,wBAAgB,kBAAkB,CAAC,EACjC,MAAM,EACN,QAAQ,GACT,EAAE;IACD,MAAM,EAAE,YAAY,CAAC;IACrB,QAAQ,EAAE,CAAC,CAAC,EAAE,YAAY,KAAK,IAAI,CAAC;CACrC,+BA6KA;AA0TD,2EAA2E;AAC3E,wBAAgB,kBAAkB,CAAC,EACjC,MAAM,EACN,QAAQ,EACR,OAAe,GAChB,EAAE;IACD,MAAM,EAAE,YAAY,CAAC;IACrB,QAAQ,EAAE,CAAC,KAAK,EAAE,OAAO,CAAC,YAAY,CAAC,KAAK,IAAI,CAAC;IACjD,OAAO,CAAC,EAAE,WAAW,CAAC;CACvB,+BAuHA;AAED,4EAA4E;AAC5E,wBAAgB,qBAAqB,CAAC,EACpC,MAAM,EACN,QAAQ,GACT,EAAE;IACD,MAAM,EAAE,YAAY,CAAC;IACrB,QAAQ,EAAE,CAAC,KAAK,EAAE,OAAO,CAAC,YAAY,CAAC,KAAK,IAAI,CAAC;CAClD,+BA4FA"}
1
+ {"version":3,"file":"PakbonSectiesPanel.d.ts","sourceRoot":"","sources":["../../src/components/PakbonSectiesPanel.tsx"],"names":[],"mappings":"AAmBA,OAAO,EASL,KAAK,YAAY,EAEjB,KAAK,YAAY,EACjB,KAAK,gBAAgB,EACrB,KAAK,WAAW,EACjB,MAAM,sBAAsB,CAAC;AAE9B,4DAA4D;AAC5D,wBAAgB,kBAAkB,CAAC,EACjC,MAAM,EACN,QAAQ,EACR,gBAAgB,GACjB,EAAE;IACD,MAAM,EAAE,YAAY,CAAC;IACrB,QAAQ,EAAE,CAAC,CAAC,EAAE,YAAY,KAAK,IAAI,CAAC;IACpC,6EAA6E;IAC7E,gBAAgB,CAAC,EAAE,gBAAgB,EAAE,CAAC;CACvC,+BAkLA;AA0TD,2EAA2E;AAC3E,wBAAgB,kBAAkB,CAAC,EACjC,MAAM,EACN,QAAQ,EACR,OAAe,GAChB,EAAE;IACD,MAAM,EAAE,YAAY,CAAC;IACrB,QAAQ,EAAE,CAAC,KAAK,EAAE,OAAO,CAAC,YAAY,CAAC,KAAK,IAAI,CAAC;IACjD,OAAO,CAAC,EAAE,WAAW,CAAC;CACvB,+BAuHA;AAED,4EAA4E;AAC5E,wBAAgB,qBAAqB,CAAC,EACpC,MAAM,EACN,QAAQ,GACT,EAAE;IACD,MAAM,EAAE,YAAY,CAAC;IACrB,QAAQ,EAAE,CAAC,KAAK,EAAE,OAAO,CAAC,YAAY,CAAC,KAAK,IAAI,CAAC;CAClD,+BA4FA"}
@@ -7,12 +7,14 @@ import { LogoBibliotheekDialog } from "./LogoBibliotheekDialog.js";
7
7
  import { LogoThumbnail } from "./LogosPage.js";
8
8
  import { BESCHIKBARE_KOLOMMEN, CONDITIE_VELDEN, SECTIE_REGISTER, nieuweKolom, nieuweSectie, pickMethode, sectieDef, sectieOnderdrukt, } from "../lib/pakbonBuilder.js";
9
9
  /** Beheer van de secties waaruit de pakbon is opgebouwd. */
10
- export function PakbonSectiesPanel({ config, onChange, }) {
10
+ export function PakbonSectiesPanel({ config, onChange, beschikbareTypes, }) {
11
11
  const alleSecties = Array.isArray(config?.secties) ? config.secties : [];
12
12
  const methode = pickMethode(alleSecties);
13
+ const toegestaan = (t) => !beschikbareTypes || beschikbareTypes.includes(t);
13
14
  const secties = alleSecties.filter((s) => !sectieOnderdrukt(s.type, methode));
14
15
  const [open, setOpen] = useState(secties[0]?.id ?? null);
15
- const [toevoegen, setToevoegen] = useState("orderregels");
16
+ const keuzes = SECTIE_REGISTER.filter((d) => !sectieOnderdrukt(d.type, methode) && toegestaan(d.type));
17
+ const [toevoegen, setToevoegen] = useState(keuzes.find((d) => d.type === "orderregels")?.type ?? keuzes[0]?.type ?? "tekstblok");
16
18
  // Onderdrukte blokken blijven bewaard in de configuratie, ook al staan ze niet in de lijst.
17
19
  const verborgen = alleSecties.filter((s) => sectieOnderdrukt(s.type, methode));
18
20
  const zet = (nieuw) => onChange({ ...config, secties: [...nieuw, ...verborgen] });
@@ -43,7 +45,7 @@ export function PakbonSectiesPanel({ config, onChange, }) {
43
45
  ...(config.logo ?? { logoId: null }),
44
46
  uitlijning: v,
45
47
  },
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: () => {
48
+ }), 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: keuzes.map((d) => (_jsx(SelectItem, { value: d.type, children: d.label }, d.type))) })] }), _jsx(Button, { size: "sm", onClick: () => {
47
49
  const s = nieuweSectie(toevoegen);
48
50
  zet([...secties, s]);
49
51
  setOpen(s.id);
@@ -1,7 +1,8 @@
1
1
  type PdfMapToestemmingBannerProps = {
2
2
  alleenActieNodig?: boolean;
3
3
  zwevend?: boolean;
4
+ printplekId?: string;
4
5
  };
5
- export declare function PdfMapToestemmingBanner({ alleenActieNodig, zwevend, }?: PdfMapToestemmingBannerProps): import("react").JSX.Element | null;
6
+ export declare function PdfMapToestemmingBanner({ alleenActieNodig, zwevend, printplekId, }?: PdfMapToestemmingBannerProps): import("react").JSX.Element | null;
6
7
  export {};
7
8
  //# sourceMappingURL=PdfMapToestemmingBanner.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"PdfMapToestemmingBanner.d.ts","sourceRoot":"","sources":["../../src/components/PdfMapToestemmingBanner.tsx"],"names":[],"mappings":"AAuBA,KAAK,4BAA4B,GAAG;IAClC,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,CAAC;AAEF,wBAAgB,uBAAuB,CAAC,EACtC,gBAAwB,EACxB,OAAe,GAChB,GAAE,4BAAiC,sCAwKnC"}
1
+ {"version":3,"file":"PdfMapToestemmingBanner.d.ts","sourceRoot":"","sources":["../../src/components/PdfMapToestemmingBanner.tsx"],"names":[],"mappings":"AA0BA,KAAK,4BAA4B,GAAG;IAClC,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,wBAAgB,uBAAuB,CAAC,EACtC,gBAAwB,EACxB,OAAe,EACf,WAAW,GACZ,GAAE,4BAAiC,sCAuQnC"}
@@ -10,12 +10,24 @@ 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");
17
18
  const [naam, setNaam] = useState(null);
18
19
  const [toestand, setToestand] = useState("laden");
20
+ const [gekoppeldePrintplek, setGekoppeldePrintplek] = useState(null);
21
+ const [persistent, setPersistent] = useState(null);
22
+ const [instellingOpen, setInstellingOpen] = useState(false);
23
+ const [aangevraagdePrintplek, setAangevraagdePrintplek] = useState(null);
24
+ useEffect(() => {
25
+ const params = new URLSearchParams(window.location.search);
26
+ const gevraagd = params.get("pdf-printplek")?.trim() || null;
27
+ setInstellingOpen(params.get("pdf-map-instellen") === "1");
28
+ setAangevraagdePrintplek(gevraagd);
29
+ void lokalePdfPrintplek().then((lokaal) => setGekoppeldePrintplek(gevraagd ?? lokaal));
30
+ }, []);
19
31
  // Zorg dat de virtuele PDF-printer bestaat, ook als de gebruiker nog geen
20
32
  // wizard heeft doorlopen. Daarna herladen zodat de banner direct zichtbaar is.
21
33
  useEffect(() => {
@@ -44,7 +56,23 @@ export function PdfMapToestemmingBanner({ alleenActieNodig = false, zwevend = fa
44
56
  }
45
57
  setNaam(await pdfMapNaam(pdfPrinter.id));
46
58
  setToestand(await pdfMapToestemming(pdfPrinter.id));
47
- }, [pdfPrinter]);
59
+ const koppeling = await pdfMapKoppeling();
60
+ let doelPrintplek = printplekId ?? aangevraagdePrintplek ?? koppeling?.printPlekId ?? null;
61
+ if (!doelPrintplek) {
62
+ const { data } = await supabase
63
+ .from("document_printplek_printers")
64
+ .select("print_plek_id")
65
+ .eq("printer_id", pdfPrinter.id);
66
+ const ids = [
67
+ ...new Set((Array.isArray(data) ? data : [])
68
+ .map((rij) => String(rij.print_plek_id ?? "").trim())
69
+ .filter(Boolean)),
70
+ ];
71
+ doelPrintplek = ids.length === 1 ? ids[0] ?? null : null;
72
+ }
73
+ setGekoppeldePrintplek(doelPrintplek);
74
+ setPersistent(koppeling?.persistent ?? null);
75
+ }, [aangevraagdePrintplek, pdfPrinter, printplekId]);
48
76
  useEffect(() => {
49
77
  void laad();
50
78
  }, [laad]);
@@ -58,14 +86,24 @@ export function PdfMapToestemmingBanner({ alleenActieNodig = false, zwevend = fa
58
86
  document.removeEventListener("visibilitychange", h);
59
87
  };
60
88
  }, [laad]);
61
- if (!pdfPrinter)
89
+ // In een iframe (o.a. de Lovable-preview) heeft de balk geen nut: de
90
+ // mappenverkenner werkt daar niet en de gebruiker kan alleen via "Open in
91
+ // eigen tabblad" verder. De balk daar niet tonen houdt de preview schoon;
92
+ // in een eigen tabblad blijft hij volledig zichtbaar.
93
+ const inIframe = draaitInIframe();
94
+ if (inIframe)
62
95
  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))
96
+ // De banner mag nooit onzichtbaar zijn: zonder PDF-printer is de mapkeuze
97
+ // onmogelijk en zonder mapkeuze mislukt elke lokale PDF-opslag. Toon daarom
98
+ // ook een waarschuwing terwijl de printer nog (aan)ge(maakt) wordt.
99
+ const vereistePrintplek = printplekId ?? aangevraagdePrintplek ?? gekoppeldePrintplek;
100
+ const inOrde = !!pdfPrinter &&
101
+ toestand === "granted" &&
102
+ !!naam &&
103
+ !!gekoppeldePrintplek &&
104
+ (!vereistePrintplek || gekoppeldePrintplek === vereistePrintplek);
105
+ if (alleenActieNodig && !instellingOpen && (!pdfPrinter ? false : toestand === "laden" || inOrde))
67
106
  return null;
68
- const inIframe = draaitInIframe();
69
107
  const ondersteund = mapKiezenMogelijk();
70
108
  const geefToestemming = async () => {
71
109
  if (!pdfPrinter)
@@ -94,13 +132,14 @@ export function PdfMapToestemmingBanner({ alleenActieNodig = false, zwevend = fa
94
132
  description: "Open de app in een eigen tabblad om de mappenverkenner te gebruiken.",
95
133
  action: {
96
134
  label: "Open in eigen tabblad",
97
- onClick: openInEigenTabblad,
135
+ onClick: () => openInEigenTabblad(printplekId ?? gekoppeldePrintplek ?? undefined),
98
136
  },
99
137
  duration: 15000,
100
138
  });
101
139
  return;
102
140
  }
103
- const uitkomst = await kiesPdfMap(pdfPrinter.id);
141
+ const doelPrintplek = printplekId ?? gekoppeldePrintplek ?? undefined;
142
+ const uitkomst = await kiesPdfMap(pdfPrinter.id, doelPrintplek);
104
143
  if (uitkomst.status === "gekozen") {
105
144
  toast.success("Map gekozen", { description: `PDF's worden opgeslagen in “${uitkomst.naam}”.` });
106
145
  await laad();
@@ -113,7 +152,10 @@ export function PdfMapToestemmingBanner({ alleenActieNodig = false, zwevend = fa
113
152
  else if (uitkomst.status === "iframe-geblokkeerd") {
114
153
  toast.error("Map kiezen werkt niet in een iframe", {
115
154
  description: "Open de app in een eigen tabblad.",
116
- action: { label: "Open in eigen tabblad", onClick: openInEigenTabblad },
155
+ action: {
156
+ label: "Open in eigen tabblad",
157
+ onClick: () => openInEigenTabblad(printplekId ?? gekoppeldePrintplek ?? undefined),
158
+ },
117
159
  duration: 15000,
118
160
  });
119
161
  }
@@ -121,12 +163,27 @@ export function PdfMapToestemmingBanner({ alleenActieNodig = false, zwevend = fa
121
163
  toast.error("Map kiezen is mislukt", { description: uitkomst.melding });
122
164
  }
123
165
  };
124
- const icoon = toestand === "granted"
166
+ const icoon = inOrde
125
167
  ? ShieldCheck
126
168
  : toestand === "denied"
127
169
  ? ShieldAlert
128
170
  : FolderOpen;
129
171
  const Icoon = icoon;
172
+ if (!pdfPrinter) {
173
+ 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: () => {
174
+ void (async () => {
175
+ try {
176
+ await zorgVoorPdfPrinter();
177
+ await reload(true);
178
+ }
179
+ catch (fout) {
180
+ toast.error("PDF-printer kon niet worden aangemaakt", {
181
+ description: fout instanceof Error ? fout.message : undefined,
182
+ });
183
+ }
184
+ })();
185
+ }, children: [_jsx(FolderOpen, { className: "mr-2 h-4 w-4" }), " PDF-printer aanmaken"] })] }));
186
+ }
130
187
  return (_jsxs("div", { role: "status", "aria-live": "polite", className: `${inOrde
131
188
  ? "border-emerald-300/60 bg-emerald-50 dark:border-emerald-700/60 dark:bg-emerald-950/30"
132
189
  : "border-amber-300/60 bg-amber-50 dark:border-amber-700/60 dark:bg-amber-950/30"} ${zwevend
@@ -135,5 +192,7 @@ export function PdfMapToestemmingBanner({ alleenActieNodig = false, zwevend = fa
135
192
  ? "h-4 w-4 text-emerald-600 dark:text-emerald-400"
136
193
  : "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
194
  ? `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"] })] })] }));
195
+ : "Nog geen map gekozen op deze computer", !ondersteund ? " (browser ondersteunt geen mapkeuze)" : "", inIframe
196
+ ? " — de preview draait in een iframe; de mapkeuze geldt per webadres en moet hier opnieuw worden gezet"
197
+ : "", !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
198
  }
@@ -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"}