@flowselections/layout-maker 1.31.0 → 1.36.7

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 (75) hide show
  1. package/dist-lib/components/ComputersLijst.d.ts.map +1 -1
  2. package/dist-lib/components/ComputersLijst.js +5 -5
  3. package/dist-lib/components/DocumentenPage.d.ts.map +1 -1
  4. package/dist-lib/components/DocumentenPage.js +3 -2
  5. package/dist-lib/components/LayoutPrinterSelect.d.ts +8 -0
  6. package/dist-lib/components/LayoutPrinterSelect.d.ts.map +1 -0
  7. package/dist-lib/components/LayoutPrinterSelect.js +23 -0
  8. package/dist-lib/components/LayoutsPage.d.ts.map +1 -1
  9. package/dist-lib/components/LayoutsPage.js +16 -3
  10. package/dist-lib/components/PakbonBuilderPage.d.ts.map +1 -1
  11. package/dist-lib/components/PakbonBuilderPage.js +3 -1
  12. package/dist-lib/components/PakbonSectiesPanel.d.ts.map +1 -1
  13. package/dist-lib/components/PakbonSectiesPanel.js +6 -2
  14. package/dist-lib/components/PdfMapAutoSaver.d.ts +6 -0
  15. package/dist-lib/components/PdfMapAutoSaver.d.ts.map +1 -0
  16. package/dist-lib/components/PdfMapAutoSaver.js +12 -0
  17. package/dist-lib/components/PdfMapGlobaleBanner.d.ts +12 -0
  18. package/dist-lib/components/PdfMapGlobaleBanner.d.ts.map +1 -0
  19. package/dist-lib/components/PdfMapGlobaleBanner.js +30 -0
  20. package/dist-lib/components/PdfMapToestemmingBanner.d.ts +7 -0
  21. package/dist-lib/components/PdfMapToestemmingBanner.d.ts.map +1 -0
  22. package/dist-lib/components/PdfMapToestemmingBanner.js +139 -0
  23. package/dist-lib/components/PrintQueuePage.d.ts.map +1 -1
  24. package/dist-lib/components/PrintQueuePage.js +42 -3
  25. package/dist-lib/components/PrintenPage.d.ts.map +1 -1
  26. package/dist-lib/components/PrintenPage.js +3 -2
  27. package/dist-lib/components/PrinterSetupWizard.d.ts.map +1 -1
  28. package/dist-lib/components/PrinterSetupWizard.js +25 -2
  29. package/dist-lib/components/PrintplekDetailPage.d.ts.map +1 -1
  30. package/dist-lib/components/PrintplekDetailPage.js +30 -4
  31. package/dist-lib/components/PrintplekWizard.d.ts.map +1 -1
  32. package/dist-lib/components/PrintplekWizard.js +81 -3
  33. package/dist-lib/components/ProductiebonBuilderPage.d.ts.map +1 -1
  34. package/dist-lib/components/ProductiebonBuilderPage.js +3 -1
  35. package/dist-lib/index.d.ts +6 -0
  36. package/dist-lib/index.d.ts.map +1 -1
  37. package/dist-lib/index.js +11 -1
  38. package/dist-lib/integrations/supabase/auth-middleware.d.ts +293 -8
  39. package/dist-lib/integrations/supabase/auth-middleware.d.ts.map +1 -1
  40. package/dist-lib/integrations/supabase/client.server.d.ts +293 -8
  41. package/dist-lib/integrations/supabase/client.server.d.ts.map +1 -1
  42. package/dist-lib/integrations/supabase/types.d.ts +295 -8
  43. package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
  44. package/dist-lib/lib/aiLayout.functions.d.ts +293 -8
  45. package/dist-lib/lib/aiLayout.functions.d.ts.map +1 -1
  46. package/dist-lib/lib/htmlPdf.server.d.ts.map +1 -1
  47. package/dist-lib/lib/htmlPdf.server.js +43 -6
  48. package/dist-lib/lib/orderData.server.d.ts.map +1 -1
  49. package/dist-lib/lib/orderData.server.js +117 -10
  50. package/dist-lib/lib/pdfJobSaver.d.ts +20 -0
  51. package/dist-lib/lib/pdfJobSaver.d.ts.map +1 -0
  52. package/dist-lib/lib/pdfJobSaver.js +173 -0
  53. package/dist-lib/lib/pdfMap.d.ts +48 -0
  54. package/dist-lib/lib/pdfMap.d.ts.map +1 -0
  55. package/dist-lib/lib/pdfMap.js +219 -0
  56. package/dist-lib/lib/pdfPrinter.d.ts +9 -0
  57. package/dist-lib/lib/pdfPrinter.d.ts.map +1 -0
  58. package/dist-lib/lib/pdfPrinter.js +34 -0
  59. package/dist-lib/lib/previewPdf.d.ts +10 -0
  60. package/dist-lib/lib/previewPdf.d.ts.map +1 -0
  61. package/dist-lib/lib/previewPdf.js +202 -0
  62. package/dist-lib/lib/printApi.d.ts.map +1 -1
  63. package/dist-lib/lib/printApi.js +9 -1
  64. package/dist-lib/lib/printQueue.server.d.ts +14 -1
  65. package/dist-lib/lib/printQueue.server.d.ts.map +1 -1
  66. package/dist-lib/lib/printQueue.server.js +125 -27
  67. package/dist-lib/lib/printService.functions.d.ts +1172 -32
  68. package/dist-lib/lib/printService.functions.d.ts.map +1 -1
  69. package/dist-lib/lib/useDocumentData.d.ts +10 -0
  70. package/dist-lib/lib/useDocumentData.d.ts.map +1 -1
  71. package/dist-lib/lib/useDocumentData.js +19 -0
  72. package/dist-lib/lib/voorbeeldPrint.d.ts +4 -0
  73. package/dist-lib/lib/voorbeeldPrint.d.ts.map +1 -1
  74. package/dist-lib/lib/voorbeeldPrint.js +13 -2
  75. package/package.json +14 -2
@@ -1 +1 @@
1
- {"version":3,"file":"ComputersLijst.d.ts","sourceRoot":"","sources":["../../src/components/ComputersLijst.tsx"],"names":[],"mappings":"AAoBA,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,OAAO,CAAC;IAChB,6DAA6D;IAC7D,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,wBAAgB,YAAY;;;;;EA6C3B;AAED,wBAAgB,cAAc,CAAC,EAC7B,SAAS,EACT,OAAO,EACP,IAAI,EACJ,UAAU,EACV,mBAAmB,EACnB,aAAa,EACb,qBAAqB,EACrB,YAAoB,GACrB,EAAE;IACD,SAAS,EAAE,aAAa,EAAE,CAAC;IAC3B,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,UAAU,EAAE,MAAM,IAAI,CAAC;IACvB,mBAAmB,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IAChD,aAAa,EAAE,OAAO,CAAC;IACvB,qBAAqB,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IAC5C,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB,+BAuHA"}
1
+ {"version":3,"file":"ComputersLijst.d.ts","sourceRoot":"","sources":["../../src/components/ComputersLijst.tsx"],"names":[],"mappings":"AAoBA,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,OAAO,CAAC;IAChB,6DAA6D;IAC7D,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,wBAAgB,YAAY;;;;;EA6C3B;AAED,wBAAgB,cAAc,CAAC,EAC7B,SAAS,EACT,OAAO,EACP,IAAI,EACJ,UAAU,EACV,mBAAmB,EACnB,aAAa,EACb,qBAAqB,EACrB,YAAoB,GACrB,EAAE;IACD,SAAS,EAAE,aAAa,EAAE,CAAC;IAC3B,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,UAAU,EAAE,MAAM,IAAI,CAAC;IACvB,mBAAmB,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IAChD,aAAa,EAAE,OAAO,CAAC;IACvB,qBAAqB,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IAC5C,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB,+BAwIA"}
@@ -1,4 +1,4 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  // ============================================================================
3
3
  // src/components/ComputersLijst.tsx — overzicht van computers die het
4
4
  // printprogramma draaien, plus een korte stroom om er een toe te voegen.
@@ -68,8 +68,8 @@ export function ComputersLijst({ computers, loading, fout, onVernieuw, onPrinter
68
68
  if (gevonden)
69
69
  setNieuw(gevonden);
70
70
  }, [computers, toevoegenOpen, wachtOp, nieuw]);
71
- return (_jsxs("div", { className: alleenDialog ? "" : "space-y-3", children: [!alleenDialog && loading && _jsx("p", { className: "text-muted-foreground text-sm", children: "Laden\u2026" }), !alleenDialog && !loading && fout && (_jsx(Card, { children: _jsxs(CardContent, { className: "space-y-3 py-6", children: [_jsx("p", { className: "font-medium", children: "Geen verbinding" }), _jsx("p", { className: "text-muted-foreground text-sm", children: fout }), _jsxs(Button, { variant: "outline", onClick: onVernieuw, children: [_jsx(RefreshCw, { className: "mr-2 h-4 w-4" }), " Opnieuw proberen"] })] }) })), !alleenDialog && !loading && !fout && computers.length === 0 && (_jsx(Card, { children: _jsxs(CardContent, { className: "space-y-4 py-8", children: [_jsx(PrintProgrammaUitleg, {}), _jsxs(Button, { onClick: () => onToevoegenOpenChange(true), children: [_jsx(Plus, { className: "mr-2 h-4 w-4" }), " Computer toevoegen"] })] }) })), !alleenDialog && computers.map((c) => (_jsxs("div", { className: "bg-card flex items-center gap-4 rounded-xl border p-4", children: [_jsx("div", { className: "bg-muted rounded-lg p-2.5", children: _jsx(Monitor, { className: "text-muted-foreground h-5 w-5" }) }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("p", { className: "truncate font-medium", children: c.naam }), _jsxs("p", { className: "text-muted-foreground text-sm", children: [c.aantalGevonden, " ", c.aantalGevonden === 1 ? "printer gevonden" : "printers gevonden"] })] }), _jsx("span", { className: `rounded-full px-2.5 py-1 text-xs font-medium ${c.online ? "bg-primary/10 text-primary" : "bg-muted text-muted-foreground"}`, children: c.online ? "Aan" : "Uit" }), _jsx(Button, { variant: "ghost", size: "sm", onClick: () => onPrintersToevoegen(c.naam), children: "Printers toevoegen" })] }, c.id))), !alleenDialog && computers.length > 0 && _jsx(WerktHetNiet, {}), _jsx(Dialog, { open: toevoegenOpen, onOpenChange: onToevoegenOpenChange, children: _jsxs(DialogContent, { className: "sm:max-w-lg", children: [_jsxs(DialogHeader, { children: [_jsx(DialogTitle, { children: "Computer toevoegen" }), _jsx(DialogDescription, { children: "Zet het printprogramma klaar op de computer waar de printers aan hangen." })] }), _jsx(PrintProgrammaUitleg, { compact: true }), _jsx("div", { className: "rounded-lg border p-4 text-sm", children: nieuw ? (_jsxs("div", { className: "space-y-3", children: [_jsxs("p", { className: "font-medium", children: ["Computer gevonden: ", nieuw.naam] }), _jsx(Button, { onClick: () => {
72
- onToevoegenOpenChange(false);
73
- onPrintersToevoegen(nieuw.naam);
74
- }, children: "Printers van deze computer toevoegen" })] })) : (_jsxs("p", { className: "text-muted-foreground flex items-center gap-2", children: [_jsx(Loader2, { className: "h-4 w-4 animate-spin" }), " We wachten tot de computer zich meldt\u2026"] })) }), _jsx("div", { className: "flex justify-end", children: _jsx(Button, { variant: "outline", onClick: () => onToevoegenOpenChange(false), children: "Sluiten" }) }), _jsx(WerktHetNiet, {})] }) })] }));
71
+ return (_jsxs("div", { className: alleenDialog ? "" : "space-y-3", children: [!alleenDialog && loading && _jsx("p", { className: "text-muted-foreground text-sm", children: "Laden\u2026" }), !alleenDialog && !loading && fout && (_jsx(Card, { children: _jsxs(CardContent, { className: "space-y-3 py-6", children: [_jsx("p", { className: "font-medium", children: "Geen verbinding" }), _jsx("p", { className: "text-muted-foreground text-sm", children: fout }), _jsxs(Button, { variant: "outline", onClick: onVernieuw, children: [_jsx(RefreshCw, { className: "mr-2 h-4 w-4" }), " Opnieuw proberen"] })] }) })), !alleenDialog && !loading && !fout && computers.length === 0 && (_jsx(Card, { children: _jsxs(CardContent, { className: "space-y-4 py-8", children: [_jsx(PrintProgrammaUitleg, {}), _jsxs(Button, { onClick: () => onToevoegenOpenChange(true), children: [_jsx(Plus, { className: "mr-2 h-4 w-4" }), " Computer toevoegen"] })] }) })), !alleenDialog && computers.map((c) => (_jsxs("div", { className: "bg-card flex items-center gap-4 rounded-xl border p-4", children: [_jsx("div", { className: "bg-muted rounded-lg p-2.5", children: _jsx(Monitor, { className: "text-muted-foreground h-5 w-5" }) }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("p", { className: "truncate font-medium", children: c.naam }), _jsxs("p", { className: "text-muted-foreground text-sm", children: [c.aantalGevonden, " ", c.aantalGevonden === 1 ? "printer gevonden" : "printers gevonden"] })] }), _jsx("span", { className: `rounded-full px-2.5 py-1 text-xs font-medium ${c.online ? "bg-primary/10 text-primary" : "bg-muted text-muted-foreground"}`, children: c.online ? "Aan" : "Uit" }), _jsx(Button, { variant: "ghost", size: "sm", onClick: () => onPrintersToevoegen(c.naam), children: "Printers toevoegen" })] }, c.id))), !alleenDialog && computers.length > 0 && _jsx(WerktHetNiet, {}), _jsx(Dialog, { open: toevoegenOpen, onOpenChange: onToevoegenOpenChange, children: _jsxs(DialogContent, { className: "sm:max-w-lg", children: [_jsxs(DialogHeader, { children: [_jsx(DialogTitle, { children: "Computer toevoegen" }), _jsx(DialogDescription, { children: "Zet het printprogramma klaar op de computer waar de printers aan hangen." })] }), _jsx(PrintProgrammaUitleg, { compact: true }), _jsx("div", { className: "space-y-2 rounded-lg border p-4 text-sm", children: computers.length === 0 ? (_jsxs("p", { className: "text-muted-foreground flex items-center gap-2", children: [_jsx(Loader2, { className: "h-4 w-4 animate-spin" }), " We wachten tot de computer zich meldt\u2026"] })) : (_jsxs(_Fragment, { children: [_jsx("p", { className: "font-medium", children: nieuw ? `Nieuwe computer gevonden: ${nieuw.naam}` : "Gevonden computers" }), computers.map((c) => (_jsxs("div", { className: "flex items-center gap-3 rounded-md border p-2", children: [_jsx(Monitor, { className: "text-muted-foreground h-4 w-4" }), _jsx("span", { className: "min-w-0 flex-1 truncate", children: c.naam }), _jsx("span", { className: "text-muted-foreground text-xs", children: c.online ? "Aan" : "Uit" }), _jsx(Button, { size: "sm", onClick: () => {
72
+ onToevoegenOpenChange(false);
73
+ onPrintersToevoegen(c.naam);
74
+ }, children: "Printers toevoegen" })] }, c.id))), _jsx("p", { className: "text-muted-foreground text-xs", children: "Staat jouw Mac er niet bij? Controleer of het printprogramma draait en of je daar met dezelfde beheerdergegevens bent ingelogd." })] })) }), _jsx("div", { className: "flex justify-end", children: _jsx(Button, { variant: "outline", onClick: () => onToevoegenOpenChange(false), children: "Sluiten" }) }), _jsx(WerktHetNiet, {})] }) })] }));
75
75
  }
@@ -1 +1 @@
1
- {"version":3,"file":"DocumentenPage.d.ts","sourceRoot":"","sources":["../../src/components/DocumentenPage.tsx"],"names":[],"mappings":"AAgBA,wBAAgB,cAAc,CAAC,EAAE,GAAe,EAAE,EAAE;IAAE,GAAG,CAAC,EAAE,SAAS,GAAG,OAAO,CAAA;CAAE,+BAchF"}
1
+ {"version":3,"file":"DocumentenPage.d.ts","sourceRoot":"","sources":["../../src/components/DocumentenPage.tsx"],"names":[],"mappings":"AAiBA,wBAAgB,cAAc,CAAC,EAAE,GAAe,EAAE,EAAE;IAAE,GAAG,CAAC,EAAE,SAAS,GAAG,OAAO,CAAA;CAAE,+BAiBhF"}
@@ -1,4 +1,4 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  // ============================================================================
3
3
  // src/components/DocumentenPage.tsx — één menu-item "Documenten" met tabbladen
4
4
  // voor de documentontwerpen en de logobibliotheek.
@@ -8,11 +8,12 @@ import { FileText, Image } from "lucide-react";
8
8
  import { ModuleTabs } from "./ModuleTabs.js";
9
9
  import { LayoutsPage } from "./LayoutsPage.js";
10
10
  import { LogosPage } from "./LogosPage.js";
11
+ import { PdfMapAutoSaver } from "./PdfMapAutoSaver.js";
11
12
  const TABS = [
12
13
  { id: "layouts", label: "Documenten", icon: FileText },
13
14
  { id: "logos", label: "Logo's", icon: Image },
14
15
  ];
15
16
  export function DocumentenPage({ tab = "layouts" }) {
16
17
  const [actiefTab, setActiefTab] = useState(tab);
17
- return (_jsx(ModuleTabs, { titel: "Documenten", omschrijving: "Ontwerp pakbonnen, facturen, stickers en beheer de logo's die daarop staan.", tabs: TABS, actiefTab: actiefTab, onTabWijzig: (nieuwTab) => setActiefTab(nieuwTab === "logos" ? "logos" : "layouts"), children: actiefTab === "logos" ? _jsx(LogosPage, { ingebed: true }) : _jsx(LayoutsPage, { ingebed: true }) }));
18
+ return (_jsxs(_Fragment, { children: [_jsx(PdfMapAutoSaver, {}), _jsx(ModuleTabs, { titel: "Documenten", omschrijving: "Ontwerp pakbonnen, facturen, stickers en beheer de logo's die daarop staan.", tabs: TABS, actiefTab: actiefTab, onTabWijzig: (nieuwTab) => setActiefTab(nieuwTab === "logos" ? "logos" : "layouts"), children: actiefTab === "logos" ? _jsx(LogosPage, { ingebed: true }) : _jsx(LayoutsPage, { ingebed: true }) })] }));
18
19
  }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Printerkeuze voor één layout (zelfde koppeling als de kolom op de
3
+ * Documenten-pagina). Compact, bedoeld voor de header van de builders.
4
+ */
5
+ export declare function LayoutPrinterSelect({ layoutId }: {
6
+ layoutId: string;
7
+ }): import("react").JSX.Element;
8
+ //# sourceMappingURL=LayoutPrinterSelect.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LayoutPrinterSelect.d.ts","sourceRoot":"","sources":["../../src/components/LayoutPrinterSelect.tsx"],"names":[],"mappings":"AAUA;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,EAAE,QAAQ,EAAE,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,+BAgCrE"}
@@ -0,0 +1,23 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, toast, } from "@flowselections/core";
3
+ import { usePrinters, zetLayoutPrinter } from "../lib/useDocumentData.js";
4
+ /**
5
+ * Printerkeuze voor één layout (zelfde koppeling als de kolom op de
6
+ * Documenten-pagina). Compact, bedoeld voor de header van de builders.
7
+ */
8
+ export function LayoutPrinterSelect({ layoutId }) {
9
+ const { printers, koppelingen, reload } = usePrinters();
10
+ const huidige = koppelingen.find((k) => k.layout_id === layoutId)?.printer_id ?? "geen";
11
+ return (_jsxs(Select, { value: huidige, onValueChange: async (printerId) => {
12
+ try {
13
+ await zetLayoutPrinter(layoutId, printerId === "geen" ? null : printerId);
14
+ await reload(true);
15
+ toast.success("Printer bij document opgeslagen");
16
+ }
17
+ catch {
18
+ toast.error("Printerkeuze opslaan mislukt");
19
+ }
20
+ }, children: [_jsx(SelectTrigger, { className: "h-8 w-48 text-sm", "aria-label": "Printer bij dit document", children: _jsx(SelectValue, { placeholder: "Geen printer" }) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "geen", children: "Geen printer" }), printers
21
+ .filter((p) => p.actief)
22
+ .map((printer) => (_jsx(SelectItem, { value: printer.id, children: printer.naam }, printer.id)))] })] }));
23
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"LayoutsPage.d.ts","sourceRoot":"","sources":["../../src/components/LayoutsPage.tsx"],"names":[],"mappings":"AAgEA,wBAAgB,WAAW,CAAC,EAAE,OAAe,EAAE,GAAE;IAAE,OAAO,CAAC,EAAE,OAAO,CAAA;CAAO,+BA+kB1E"}
1
+ {"version":3,"file":"LayoutsPage.d.ts","sourceRoot":"","sources":["../../src/components/LayoutsPage.tsx"],"names":[],"mappings":"AAkEA,wBAAgB,WAAW,CAAC,EAAE,OAAe,EAAE,GAAE;IAAE,OAAO,CAAC,EAAE,OAAO,CAAA;CAAO,+BA8mB1E"}
@@ -4,7 +4,7 @@ import { useAutoRefresh } from "../lib/useAutoRefresh.js";
4
4
  import { useNavigate } from "@tanstack/react-router";
5
5
  import { Badge, Button, Card, CardContent, CardDescription, CardHeader, CardTitle, Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, Input, Label, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, Separator, Switch, Table, TableBody, TableCell, TableHead, TableHeader, TableRow, Textarea, toast, } from "@flowselections/core";
6
6
  import { Copy, FileText, Pencil, Plus, Search, Settings2, Trash2 } from "lucide-react";
7
- import { dupliceerLayout, maakLayout, updateLayout, useLayouts, verwijderLayout, } from "../lib/useDocumentData.js";
7
+ import { dupliceerLayout, maakLayout, updateLayout, useLayouts, usePrinters, zetLayoutPrinter, verwijderLayout, } from "../lib/useDocumentData.js";
8
8
  import { DOCUMENT_TYPES, FLORIDAY_LAYOUT_TOELICHTING, PAPIERFORMATEN, documentTypeLabel, isFloridayLayout, } from "../lib/documentTypes.js";
9
9
  import { DOCUMENT_PROFIELEN, documentProfiel, primaireProfielen, standaardConfig, } from "../lib/documentProfiles.js";
10
10
  function klantOntwerpLayout(l) {
@@ -15,7 +15,11 @@ function klantOntwerpLayout(l) {
15
15
  }
16
16
  export function LayoutsPage({ ingebed = false } = {}) {
17
17
  const { layouts, loading, reload } = useLayouts();
18
- useAutoRefresh(() => reload(true), 30000);
18
+ const { printers, koppelingen, reload: reloadPrinters } = usePrinters();
19
+ useAutoRefresh(() => {
20
+ void reload(true);
21
+ void reloadPrinters(true);
22
+ }, 30000);
19
23
  const navigate = useNavigate();
20
24
  const [zoek, setZoek] = useState("");
21
25
  const [typeFilter, setTypeFilter] = useState("alle");
@@ -145,7 +149,16 @@ export function LayoutsPage({ ingebed = false } = {}) {
145
149
  }
146
150
  };
147
151
  const Wrapper = ingebed ? "div" : "main";
148
- return (_jsxs(Wrapper, { className: ingebed ? "space-y-6" : "p-6 space-y-6", children: [_jsxs("div", { className: "flex flex-wrap items-end justify-between gap-4", children: [!ingebed && (_jsxs("div", { children: [_jsx("h1", { className: "text-xl font-semibold", children: "Documentlayouts" }), _jsx("p", { className: "text-sm text-muted-foreground", children: "Ontwerp pakbonnen, facturen, etiketten en alle andere documenten." })] })), _jsx("div", { className: "ml-auto flex gap-2", children: _jsxs(Button, { onClick: () => setWizardOpen(true), children: [_jsx(Plus, { className: "mr-2 h-4 w-4" }), " Nieuwe layout"] }) })] }), _jsxs(Card, { children: [_jsxs(CardHeader, { className: "pb-3", children: [_jsx(CardTitle, { className: "text-base", children: "Overzicht" }), _jsx(CardDescription, { children: loading ? "Laden…" : `${gefilterd.length} van ${layouts.length} layouts` })] }), _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 naam of omschrijving", value: zoek, onChange: (e) => setZoek(e.target.value) })] }), _jsxs(Select, { value: typeFilter, onValueChange: setTypeFilter, children: [_jsx(SelectTrigger, { children: _jsx(SelectValue, { placeholder: "Documenttype" }) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "alle", children: "Alle documenttypes" }), DOCUMENT_TYPES.map((d) => (_jsx(SelectItem, { value: d.value, children: d.label }, d.value)))] })] }), _jsxs(Select, { value: statusFilter, onValueChange: setStatusFilter, children: [_jsx(SelectTrigger, { children: _jsx(SelectValue, { placeholder: "Status" }) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "alle", children: "Alle statussen" }), _jsx(SelectItem, { value: "actief", children: "Actief" }), _jsx(SelectItem, { value: "inactief", children: "Inactief" })] })] }), _jsxs(Select, { value: bronFilter, onValueChange: setBronFilter, children: [_jsx(SelectTrigger, { children: _jsx(SelectValue, { placeholder: "Herkomst" }) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "alle", children: "Alle herkomsten" }), _jsx(SelectItem, { value: "eigen", children: "Eigen ontwerp" }), _jsx(SelectItem, { value: "floriday", children: "Uit Floriday" })] })] })] }), _jsxs(Table, { children: [_jsx(TableHeader, { children: _jsxs(TableRow, { children: [_jsx(TableHead, { children: "Naam" }), _jsx(TableHead, { children: "Documenttype" }), _jsx(TableHead, { children: "Formaat" }), _jsx(TableHead, { children: "Actief" }), _jsx(TableHead, { className: "text-right", children: "Acties" })] }) }), _jsxs(TableBody, { children: [gefilterd.length === 0 && (_jsx(TableRow, { children: _jsx(TableCell, { colSpan: 5, className: "py-10 text-center text-sm text-muted-foreground", children: "Geen layouts gevonden." }) })), gefilterd.map((l) => (_jsxs(TableRow, { children: [_jsxs(TableCell, { children: [_jsxs("div", { className: "flex items-center gap-2 font-medium", children: [_jsx(FileText, { className: "h-4 w-4 text-muted-foreground" }), l.naam, l.is_standaard && _jsx(Badge, { variant: "secondary", children: "Standaard" }), isFloridayLayout(l) && _jsx(Badge, { variant: "outline", children: "Uit Floriday" })] }), (l.omschrijving || isFloridayLayout(l)) && (_jsx("span", { className: "text-xs text-muted-foreground", children: isFloridayLayout(l) ? FLORIDAY_LAYOUT_TOELICHTING : l.omschrijving }))] }), _jsx(TableCell, { children: documentTypeLabel(l.documenttype) }), _jsxs(TableCell, { className: "text-sm text-muted-foreground", children: [l.papierformaat, " \u00B7 ", Number(l.breedte_mm), " \u00D7 ", Number(l.hoogte_mm), " mm"] }), _jsx(TableCell, { children: _jsx(Switch, { checked: l.actief, onCheckedChange: async (v) => {
152
+ return (_jsxs(Wrapper, { className: ingebed ? "space-y-6" : "p-6 space-y-6", children: [_jsxs("div", { className: "flex flex-wrap items-end justify-between gap-4", children: [!ingebed && (_jsxs("div", { children: [_jsx("h1", { className: "text-xl font-semibold", children: "Documentlayouts" }), _jsx("p", { className: "text-sm text-muted-foreground", children: "Ontwerp pakbonnen, facturen, etiketten en alle andere documenten." })] })), _jsx("div", { className: "ml-auto flex gap-2", children: _jsxs(Button, { onClick: () => setWizardOpen(true), children: [_jsx(Plus, { className: "mr-2 h-4 w-4" }), " Nieuwe layout"] }) })] }), _jsxs(Card, { children: [_jsxs(CardHeader, { className: "pb-3", children: [_jsx(CardTitle, { className: "text-base", children: "Overzicht" }), _jsx(CardDescription, { children: loading ? "Laden…" : `${gefilterd.length} van ${layouts.length} layouts` })] }), _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 naam of omschrijving", value: zoek, onChange: (e) => setZoek(e.target.value) })] }), _jsxs(Select, { value: typeFilter, onValueChange: setTypeFilter, children: [_jsx(SelectTrigger, { children: _jsx(SelectValue, { placeholder: "Documenttype" }) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "alle", children: "Alle documenttypes" }), DOCUMENT_TYPES.map((d) => (_jsx(SelectItem, { value: d.value, children: d.label }, d.value)))] })] }), _jsxs(Select, { value: statusFilter, onValueChange: setStatusFilter, children: [_jsx(SelectTrigger, { children: _jsx(SelectValue, { placeholder: "Status" }) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "alle", children: "Alle statussen" }), _jsx(SelectItem, { value: "actief", children: "Actief" }), _jsx(SelectItem, { value: "inactief", children: "Inactief" })] })] }), _jsxs(Select, { value: bronFilter, onValueChange: setBronFilter, children: [_jsx(SelectTrigger, { children: _jsx(SelectValue, { placeholder: "Herkomst" }) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "alle", children: "Alle herkomsten" }), _jsx(SelectItem, { value: "eigen", children: "Eigen ontwerp" }), _jsx(SelectItem, { value: "floriday", children: "Uit Floriday" })] })] })] }), _jsxs(Table, { children: [_jsx(TableHeader, { children: _jsxs(TableRow, { children: [_jsx(TableHead, { children: "Naam" }), _jsx(TableHead, { children: "Documenttype" }), _jsx(TableHead, { children: "Formaat" }), _jsx(TableHead, { children: "Printer bij document" }), _jsx(TableHead, { children: "Actief" }), _jsx(TableHead, { className: "text-right", children: "Acties" })] }) }), _jsxs(TableBody, { children: [gefilterd.length === 0 && (_jsx(TableRow, { children: _jsx(TableCell, { colSpan: 6, className: "py-10 text-center text-sm text-muted-foreground", children: "Geen layouts gevonden." }) })), gefilterd.map((l) => (_jsxs(TableRow, { children: [_jsxs(TableCell, { children: [_jsxs("div", { className: "flex items-center gap-2 font-medium", children: [_jsx(FileText, { className: "h-4 w-4 text-muted-foreground" }), l.naam, l.is_standaard && _jsx(Badge, { variant: "secondary", children: "Standaard" }), isFloridayLayout(l) && _jsx(Badge, { variant: "outline", children: "Uit Floriday" })] }), (l.omschrijving || isFloridayLayout(l)) && (_jsx("span", { className: "text-xs text-muted-foreground", children: isFloridayLayout(l) ? FLORIDAY_LAYOUT_TOELICHTING : l.omschrijving }))] }), _jsx(TableCell, { children: documentTypeLabel(l.documenttype) }), _jsxs(TableCell, { className: "text-sm text-muted-foreground", children: [l.papierformaat, " \u00B7 ", Number(l.breedte_mm), " \u00D7 ", Number(l.hoogte_mm), " mm"] }), _jsx(TableCell, { className: "min-w-52", children: _jsxs(Select, { value: koppelingen.find((k) => k.layout_id === l.id)?.printer_id ?? "geen", onValueChange: async (printerId) => {
153
+ try {
154
+ await zetLayoutPrinter(l.id, printerId === "geen" ? null : printerId);
155
+ await reloadPrinters(true);
156
+ toast.success("Printer bij document opgeslagen");
157
+ }
158
+ catch {
159
+ toast.error("Printerkeuze opslaan mislukt");
160
+ }
161
+ }, children: [_jsx(SelectTrigger, { "aria-label": `Printer voor ${l.naam}`, children: _jsx(SelectValue, { placeholder: "Geen printer" }) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "geen", children: "Geen printer" }), printers.filter((p) => p.actief).map((printer) => (_jsx(SelectItem, { value: printer.id, children: printer.naam }, printer.id)))] })] }) }), _jsx(TableCell, { children: _jsx(Switch, { checked: l.actief, onCheckedChange: async (v) => {
149
162
  await updateLayout(l.id, { actief: v });
150
163
  void reload();
151
164
  } }) }), _jsx(TableCell, { className: "text-right", children: _jsxs("div", { className: "flex justify-end gap-1", children: [isFloridayLayout(l) ? (_jsx("span", { className: "text-xs text-muted-foreground", children: "Niet aanpasbaar \u2014 komt uit Floriday" })) : klantOntwerpLayout(l) ? (_jsx(Badge, { variant: "outline", children: "Ontwerp van klant" })) : (_jsxs(Button, { variant: "ghost", size: "sm", onClick: () => navigate(l.documenttype === "productiebon"
@@ -1 +1 @@
1
- {"version":3,"file":"PakbonBuilderPage.d.ts","sourceRoot":"","sources":["../../src/components/PakbonBuilderPage.tsx"],"names":[],"mappings":"AAyBA,iFAAiF;AACjF,wBAAgB,iBAAiB,CAAC,EAAE,QAAQ,EAAE,YAAY,EAAE,GAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAO,+BAqJvF"}
1
+ {"version":3,"file":"PakbonBuilderPage.d.ts","sourceRoot":"","sources":["../../src/components/PakbonBuilderPage.tsx"],"names":[],"mappings":"AA0BA,iFAAiF;AACjF,wBAAgB,iBAAiB,CAAC,EAAE,QAAQ,EAAE,YAAY,EAAE,GAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAO,+BAwJvF"}
@@ -11,6 +11,7 @@ import { renderPakbonDocument, voorbeeldPakbonOrder } from "../lib/pakbonHtml.js
11
11
  import { voorbeeldPrint } from "../lib/voorbeeldPrint.js";
12
12
  import { haalPrintLogos } from "../lib/logoLibrary.js";
13
13
  import { updateLayout, useLayoutDetail } from "../lib/useDocumentData.js";
14
+ import { LayoutPrinterSelect } from "./LayoutPrinterSelect.js";
14
15
  import { useLayoutIdParam } from "../lib/useLayoutIdParam.js";
15
16
  /** Sectiegebaseerde pakbon-builder met live voorbeeld op echte ordergegevens. */
16
17
  export function PakbonBuilderPage({ layoutId: layoutIdProp } = {}) {
@@ -53,6 +54,7 @@ export function PakbonBuilderPage({ layoutId: layoutIdProp } = {}) {
53
54
  titel: layout?.naam ?? "Pakbon",
54
55
  });
55
56
  await voorbeeldPrint({
57
+ ...(layout ?? {}),
56
58
  id: layout?.id ?? layoutId,
57
59
  naam: layout?.naam,
58
60
  documenttype: layout?.documenttype ?? "pakbon",
@@ -72,5 +74,5 @@ export function PakbonBuilderPage({ layoutId: layoutIdProp } = {}) {
72
74
  return _jsx("main", { className: "p-6 text-sm text-muted-foreground", children: "Layout laden\u2026" });
73
75
  if (!layout || !config)
74
76
  return _jsx("main", { className: "p-6 text-sm text-muted-foreground", children: "Layout niet gevonden." });
75
- 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 gap-2", children: [_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: "Voorbeeldorder met 2 karren, 3 lagen en fusten." })] }), _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 }) }) })] })] })] }));
77
+ 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: "Voorbeeldorder met 2 karren, 3 lagen en fusten." })] }), _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 }) }) })] })] })] }));
76
78
  }
@@ -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,EAElB,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,KAAK,GAAG,QAAQ,CAAC;CAC5B,+BAiGA;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,EAElB,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,KAAK,GAAG,QAAQ,CAAC;CAC5B,+BAgHA;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"}
@@ -93,7 +93,11 @@ export function PakbonSectieEditor({ sectie, onChange, methode = "kar", }) {
93
93
  const s = sectie;
94
94
  const def = sectieDef(s.type);
95
95
  const bundelAan = s.type === "karren" && (s.opties?.bundel_identiek ?? true);
96
- const aangepasteOpties = def.opties.map((o) => {
96
+ // Instellingen van de fustverzamellijst gelden alleen bij picken per fust.
97
+ const verzamellijstUit = s.type === "pickmethode" && methode === "kar";
98
+ const aangepasteOpties = def.opties
99
+ .filter((o) => !(verzamellijstUit && o.key !== "methode"))
100
+ .map((o) => {
97
101
  if (s.type === "karren" && o.key === "velden" && bundelAan) {
98
102
  return {
99
103
  ...o,
@@ -115,7 +119,7 @@ export function PakbonSectieEditor({ sectie, onChange, methode = "kar", }) {
115
119
  logoId: logo?.id ?? null,
116
120
  logoNaam: logo?.naam ?? null,
117
121
  },
118
- }) })), aangepasteOpties.map((o) => {
122
+ }) })), verzamellijstUit && (_jsxs("p", { className: "rounded border border-dashed px-3 py-2 text-xs text-muted-foreground", children: ["De instellingen voor de fustverzamellijst (koptekst, gegevens per fust, fusthoogte en totaal) gelden alleen bij \u201COrder picken per fusten\u201D. Kies die pickmethode om ze in te stellen; bij picken per kar stel je de fustgegevens in bij het blok ", _jsx("span", { className: "font-medium", children: "Fusten" }), "."] })), aangepasteOpties.map((o) => {
119
123
  const isVelden = o.key === "velden";
120
124
  const basisWaarde = s.opties?.[o.key] ?? o.standaard;
121
125
  const waarde = isVelden && bundelAan
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Schrijft voltooide opdrachten van de virtuele PDF-printer naar de map die
3
+ * in deze browser op deze computer is gekozen. Dit component rendert niets.
4
+ */
5
+ export declare function PdfMapAutoSaver(): null;
6
+ //# sourceMappingURL=PdfMapAutoSaver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PdfMapAutoSaver.d.ts","sourceRoot":"","sources":["../../src/components/PdfMapAutoSaver.tsx"],"names":[],"mappings":"AAGA;;;GAGG;AACH,wBAAgB,eAAe,SAM9B"}
@@ -0,0 +1,12 @@
1
+ import { useEffect } from "react";
2
+ import { startPdfMapService } from "../lib/pdfJobSaver.js";
3
+ /**
4
+ * Schrijft voltooide opdrachten van de virtuele PDF-printer naar de map die
5
+ * in deze browser op deze computer is gekozen. Dit component rendert niets.
6
+ */
7
+ export function PdfMapAutoSaver() {
8
+ useEffect(() => {
9
+ startPdfMapService();
10
+ }, []);
11
+ return null;
12
+ }
@@ -0,0 +1,12 @@
1
+ import { type Root } from "react-dom/client";
2
+ declare global {
3
+ interface Window {
4
+ __layoutMakerPdfBannerRoot?: Root;
5
+ }
6
+ }
7
+ /**
8
+ * Monteert de mapmelding één keer boven de volledige shell. Zo is de actie ook
9
+ * zichtbaar in modules die alleen de print-API gebruiken, zoals Pickstation.
10
+ */
11
+ export declare function startPdfMapGlobaleBanner(): void;
12
+ //# sourceMappingURL=PdfMapGlobaleBanner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PdfMapGlobaleBanner.d.ts","sourceRoot":"","sources":["../../src/components/PdfMapGlobaleBanner.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAc,KAAK,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAKzD,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,MAAM;QACd,0BAA0B,CAAC,EAAE,IAAI,CAAC;KACnC;CACF;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,IAAI,IAAI,CAoB/C"}
@@ -0,0 +1,30 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { createRoot } from "react-dom/client";
3
+ import { PdfMapToestemmingBanner } from "./PdfMapToestemmingBanner.js";
4
+ const CONTAINER_ID = "layout-maker-pdf-map-banner";
5
+ /**
6
+ * Monteert de mapmelding één keer boven de volledige shell. Zo is de actie ook
7
+ * zichtbaar in modules die alleen de print-API gebruiken, zoals Pickstation.
8
+ */
9
+ export function startPdfMapGlobaleBanner() {
10
+ if (typeof window === "undefined" || window.__layoutMakerPdfBannerRoot)
11
+ return;
12
+ const monteer = () => {
13
+ if (window.__layoutMakerPdfBannerRoot)
14
+ return;
15
+ const bestaand = document.getElementById(CONTAINER_ID);
16
+ const container = bestaand ?? document.createElement("div");
17
+ container.id = CONTAINER_ID;
18
+ if (!bestaand)
19
+ document.body.appendChild(container);
20
+ const root = createRoot(container);
21
+ window.__layoutMakerPdfBannerRoot = root;
22
+ root.render(_jsx(PdfMapToestemmingBanner, { alleenActieNodig: true, zwevend: true }));
23
+ };
24
+ if (document.readyState === "loading") {
25
+ document.addEventListener("DOMContentLoaded", monteer, { once: true });
26
+ }
27
+ else {
28
+ monteer();
29
+ }
30
+ }
@@ -0,0 +1,7 @@
1
+ type PdfMapToestemmingBannerProps = {
2
+ alleenActieNodig?: boolean;
3
+ zwevend?: boolean;
4
+ };
5
+ export declare function PdfMapToestemmingBanner({ alleenActieNodig, zwevend, }?: PdfMapToestemmingBannerProps): import("react").JSX.Element | null;
6
+ export {};
7
+ //# sourceMappingURL=PdfMapToestemmingBanner.d.ts.map
@@ -0,0 +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"}
@@ -0,0 +1,139 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ // ============================================================================
3
+ // src/components/PdfMapToestemmingBanner.tsx — permanente, zichtbare controle om
4
+ // de maptoestemming voor de virtuele "Opslaan als PDF"-printer te beheren.
5
+ // Getoond op de Printplek-detailpagina en de Printwachtrij, zodat een gebruiker
6
+ // altijd proactief toestemming kan (her)geven zonder op een toast te wachten.
7
+ // ============================================================================
8
+ import { useCallback, useEffect, useState } from "react";
9
+ import { Button, toast } from "@flowselections/core";
10
+ import { FolderCheck, FolderOpen, ShieldAlert, ShieldCheck } from "lucide-react";
11
+ import { usePrinters } from "../lib/useDocumentData.js";
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, } = {}) {
15
+ const { printers, reload } = usePrinters();
16
+ const pdfPrinter = printers.find((p) => p.afdrukmethode === "pdf");
17
+ const [naam, setNaam] = useState(null);
18
+ 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.
21
+ useEffect(() => {
22
+ if (pdfPrinter)
23
+ return;
24
+ let actief = true;
25
+ void (async () => {
26
+ try {
27
+ await zorgVoorPdfPrinter();
28
+ if (actief)
29
+ await reload(true);
30
+ }
31
+ catch {
32
+ // RLS of netwerkfout — stil negeren, banner blijft verborgen.
33
+ }
34
+ })();
35
+ return () => {
36
+ actief = false;
37
+ };
38
+ }, [pdfPrinter, reload]);
39
+ const laad = useCallback(async () => {
40
+ if (!pdfPrinter) {
41
+ setNaam(null);
42
+ setToestand("ontbreekt");
43
+ return;
44
+ }
45
+ setNaam(await pdfMapNaam(pdfPrinter.id));
46
+ setToestand(await pdfMapToestemming(pdfPrinter.id));
47
+ }, [pdfPrinter]);
48
+ useEffect(() => {
49
+ void laad();
50
+ }, [laad]);
51
+ // Herverfris na focus zodat een net verleende toestemming meteen zichtbaar is.
52
+ useEffect(() => {
53
+ const h = () => void laad();
54
+ window.addEventListener("focus", h);
55
+ document.addEventListener("visibilitychange", h);
56
+ return () => {
57
+ window.removeEventListener("focus", h);
58
+ document.removeEventListener("visibilitychange", h);
59
+ };
60
+ }, [laad]);
61
+ if (!pdfPrinter)
62
+ 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))
67
+ return null;
68
+ const inIframe = draaitInIframe();
69
+ const ondersteund = mapKiezenMogelijk();
70
+ const geefToestemming = async () => {
71
+ if (!pdfPrinter)
72
+ return;
73
+ const resultaat = await herstelPdfMapToestemming(pdfPrinter.id);
74
+ if (resultaat === "granted") {
75
+ toast.success("Maptoestemming verleend", {
76
+ description: "Gereedstaande PDF's worden nu naar de map opgeslagen.",
77
+ });
78
+ await laad();
79
+ }
80
+ else if (resultaat === "ontbreekt") {
81
+ // Nog geen map gekozen — open de verkenner.
82
+ await kiesNieuweMap();
83
+ }
84
+ else {
85
+ toast.error("Maptoestemming is niet verleend");
86
+ await laad();
87
+ }
88
+ };
89
+ const kiesNieuweMap = async () => {
90
+ if (!pdfPrinter)
91
+ return;
92
+ if (inIframe) {
93
+ toast.error("Map kiezen werkt niet in een iframe", {
94
+ description: "Open de app in een eigen tabblad om de mappenverkenner te gebruiken.",
95
+ action: {
96
+ label: "Open in eigen tabblad",
97
+ onClick: openInEigenTabblad,
98
+ },
99
+ duration: 15000,
100
+ });
101
+ return;
102
+ }
103
+ const uitkomst = await kiesPdfMap(pdfPrinter.id);
104
+ if (uitkomst.status === "gekozen") {
105
+ toast.success("Map gekozen", { description: `PDF's worden opgeslagen in “${uitkomst.naam}”.` });
106
+ await laad();
107
+ }
108
+ else if (uitkomst.status === "niet-ondersteund") {
109
+ toast.error("Deze browser ondersteunt geen mapkeuze", {
110
+ description: "Gebruik Chrome, Edge of Safari op desktop.",
111
+ });
112
+ }
113
+ else if (uitkomst.status === "iframe-geblokkeerd") {
114
+ toast.error("Map kiezen werkt niet in een iframe", {
115
+ description: "Open de app in een eigen tabblad.",
116
+ action: { label: "Open in eigen tabblad", onClick: openInEigenTabblad },
117
+ duration: 15000,
118
+ });
119
+ }
120
+ else if (uitkomst.status === "fout") {
121
+ toast.error("Map kiezen is mislukt", { description: uitkomst.melding });
122
+ }
123
+ };
124
+ const icoon = toestand === "granted"
125
+ ? ShieldCheck
126
+ : toestand === "denied"
127
+ ? ShieldAlert
128
+ : FolderOpen;
129
+ const Icoon = icoon;
130
+ return (_jsxs("div", { role: "status", "aria-live": "polite", className: `${inOrde
131
+ ? "border-emerald-300/60 bg-emerald-50 dark:border-emerald-700/60 dark:bg-emerald-950/30"
132
+ : "border-amber-300/60 bg-amber-50 dark:border-amber-700/60 dark:bg-amber-950/30"} ${zwevend
133
+ ? "fixed inset-x-4 top-4 z-[100] mx-auto max-w-4xl shadow-lg"
134
+ : ""} flex flex-wrap items-center justify-between gap-3 rounded-lg border p-3`, children: [_jsxs("div", { className: "flex items-center gap-2 text-sm", children: [_jsx(Icoon, { className: inOrde
135
+ ? "h-4 w-4 text-emerald-600 dark:text-emerald-400"
136
+ : "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
+ ? `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"] })] })] }));
139
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"PrintQueuePage.d.ts","sourceRoot":"","sources":["../../src/components/PrintQueuePage.tsx"],"names":[],"mappings":"AAuEA,wBAAgB,cAAc,gCAuQ7B"}
1
+ {"version":3,"file":"PrintQueuePage.d.ts","sourceRoot":"","sources":["../../src/components/PrintQueuePage.tsx"],"names":[],"mappings":"AAoFA,wBAAgB,cAAc,gCAsT7B"}
@@ -1,7 +1,7 @@
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 { RotateCcw, Search, XCircle } from "lucide-react";
4
+ import { FileDown, RotateCcw, 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";
@@ -9,6 +9,10 @@ import { printStatus } from "../lib/printService.functions.js";
9
9
  import { vraagWachtrijRonde } from "../lib/queueTickClient.js";
10
10
  import { documentTypeLabel } from "../lib/documentTypes.js";
11
11
  import { jobPlekLabel } from "../lib/printQueue.js";
12
+ import { bewaarInPdfMap } from "../lib/pdfMap.js";
13
+ import { PdfMapAutoSaver } from "./PdfMapAutoSaver.js";
14
+ import { PdfMapToestemmingBanner } from "./PdfMapToestemmingBanner.js";
15
+ import { supabase } from "../lib/supabaseClient.js";
12
16
  const STATUS_VARIANT = {
13
17
  wachtend: "outline",
14
18
  bezig: "secondary",
@@ -23,6 +27,14 @@ const BRON_LABEL = {
23
27
  render_html: "meegestuurde opmaak",
24
28
  klantlayout: "klantlayout",
25
29
  };
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
+ };
26
38
  /**
27
39
  * Heeft deze opdracht een kenmerk waarmee hij van andere orders te
28
40
  * onderscheiden is? Zo niet, dan valt de dubbelbescherming terug op het
@@ -103,11 +115,38 @@ export function PrintQueuePage() {
103
115
  await reload(true);
104
116
  };
105
117
  const bijgewerkt = useAutoRefresh(ronde, lopende ? 5000 : 20000);
106
- return (_jsxs("main", { className: "space-y-6 p-6", children: [_jsxs("div", { className: "flex flex-wrap items-end justify-between gap-4", children: [_jsxs("div", { children: [_jsx("h1", { className: "text-xl font-semibold", children: "Printwachtrij" }), _jsx("p", { className: "text-sm text-muted-foreground", children: "Volg printopdrachten, herstart mislukte prints en print documenten opnieuw." })] }), _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 }))] }), _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
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
107
119
  ? layoutNaam.get(j.gebruikte_layout_id) ?? "Onbekende layout"
108
120
  : j.gebruikte_bron
109
121
  ? "geen layout"
110
- : "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.status }) }), _jsx(TableCell, { className: "text-sm text-muted-foreground", children: new Date(j.created_at).toLocaleString("nl-NL") }), _jsx(TableCell, { className: "text-right", children: _jsxs("div", { className: "flex justify-end gap-1", children: [_jsxs(Button, { variant: "ghost", size: "sm", onClick: async () => {
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");
140
+ void reload(true);
141
+ return;
142
+ }
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 () => {
111
150
  if (!j.layout_id)
112
151
  return;
113
152
  try {
@@ -1 +1 @@
1
- {"version":3,"file":"PrintenPage.d.ts","sourceRoot":"","sources":["../../src/components/PrintenPage.tsx"],"names":[],"mappings":"AAoBA,wBAAgB,WAAW,CAAC,EAC1B,GAAoB,GACrB,EAAE;IACD,GAAG,CAAC,EAAE,cAAc,GAAG,WAAW,CAAC;CACpC,+BAoBA"}
1
+ {"version":3,"file":"PrintenPage.d.ts","sourceRoot":"","sources":["../../src/components/PrintenPage.tsx"],"names":[],"mappings":"AAqBA,wBAAgB,WAAW,CAAC,EAC1B,GAAoB,GACrB,EAAE;IACD,GAAG,CAAC,EAAE,cAAc,GAAG,WAAW,CAAC;CACpC,+BAuBA"}
@@ -1,4 +1,4 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  // ============================================================================
3
3
  // src/components/PrintenPage.tsx — één menu-item "Printen" met tabbladen voor
4
4
  // printplekken (plekken, computers en printers samen) en standaardregels.
@@ -8,6 +8,7 @@ import { MapPin, Route as RouteIcon } from "lucide-react";
8
8
  import { ModuleTabs } from "./ModuleTabs.js";
9
9
  import { PrintplekkenPage } from "./PrintplekkenPage.js";
10
10
  import { DocumentRouteringPage } from "./DocumentRouteringPage.js";
11
+ import { PdfMapAutoSaver } from "./PdfMapAutoSaver.js";
11
12
  const TABS = [
12
13
  {
13
14
  id: "printplekken",
@@ -18,5 +19,5 @@ const TABS = [
18
19
  ];
19
20
  export function PrintenPage({ tab = "printplekken", }) {
20
21
  const [actiefTab, setActiefTab] = useState(tab);
21
- return (_jsx(ModuleTabs, { titel: "Printen", omschrijving: "Stel per plek in het bedrijf in welke computers, printers en documenten daar horen.", tabs: TABS, actiefTab: actiefTab, onTabWijzig: (nieuwTab) => setActiefTab(nieuwTab === "routering" ? "routering" : "printplekken"), children: actiefTab === "routering" ? (_jsx(DocumentRouteringPage, { ingebed: true })) : (_jsx(PrintplekkenPage, { ingebed: true })) }));
22
+ return (_jsxs(_Fragment, { children: [_jsx(PdfMapAutoSaver, {}), _jsx(ModuleTabs, { titel: "Printen", omschrijving: "Stel per plek in het bedrijf in welke computers, printers en documenten daar horen.", tabs: TABS, actiefTab: actiefTab, onTabWijzig: (nieuwTab) => setActiefTab(nieuwTab === "routering" ? "routering" : "printplekken"), children: actiefTab === "routering" ? (_jsx(DocumentRouteringPage, { ingebed: true })) : (_jsx(PrintplekkenPage, { ingebed: true })) })] }));
22
23
  }