@flowselections/layout-maker 1.46.1 → 1.51.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/dist-lib/components/ComputerVerwijderenDialog.d.ts +16 -0
  2. package/dist-lib/components/ComputerVerwijderenDialog.d.ts.map +1 -0
  3. package/dist-lib/components/ComputerVerwijderenDialog.js +38 -0
  4. package/dist-lib/components/ComputersLijst.d.ts.map +1 -1
  5. package/dist-lib/components/ComputersLijst.js +5 -2
  6. package/dist-lib/components/LayoutPrinterSelect.d.ts +3 -5
  7. package/dist-lib/components/LayoutPrinterSelect.d.ts.map +1 -1
  8. package/dist-lib/components/LayoutPrinterSelect.js +40 -17
  9. package/dist-lib/components/LayoutsPage.d.ts.map +1 -1
  10. package/dist-lib/components/LayoutsPage.js +28 -18
  11. package/dist-lib/components/PrinterSetupWizard.d.ts +3 -1
  12. package/dist-lib/components/PrinterSetupWizard.d.ts.map +1 -1
  13. package/dist-lib/components/PrinterSetupWizard.js +13 -4
  14. package/dist-lib/components/PrinterVervangenDialog.d.ts +14 -0
  15. package/dist-lib/components/PrinterVervangenDialog.d.ts.map +1 -0
  16. package/dist-lib/components/PrinterVervangenDialog.js +44 -0
  17. package/dist-lib/components/PrintersPage.d.ts.map +1 -1
  18. package/dist-lib/components/PrintersPage.js +2 -2
  19. package/dist-lib/components/PrintplekDetailPage.d.ts.map +1 -1
  20. package/dist-lib/components/PrintplekDetailPage.js +175 -48
  21. package/dist-lib/components/PrintplekVerwijderenDialog.d.ts +9 -0
  22. package/dist-lib/components/PrintplekVerwijderenDialog.d.ts.map +1 -0
  23. package/dist-lib/components/PrintplekVerwijderenDialog.js +34 -0
  24. package/dist-lib/components/PrintplekkenPage.d.ts.map +1 -1
  25. package/dist-lib/components/PrintplekkenPage.js +28 -6
  26. package/dist-lib/components/WerkplekConfiguratiePage.d.ts.map +1 -1
  27. package/dist-lib/components/WerkplekConfiguratiePage.js +67 -25
  28. package/dist-lib/index.d.ts +9 -4
  29. package/dist-lib/index.d.ts.map +1 -1
  30. package/dist-lib/index.js +7 -3
  31. package/dist-lib/integrations/supabase/auth-middleware.d.ts +511 -6
  32. package/dist-lib/integrations/supabase/auth-middleware.d.ts.map +1 -1
  33. package/dist-lib/integrations/supabase/client.server.d.ts +511 -6
  34. package/dist-lib/integrations/supabase/client.server.d.ts.map +1 -1
  35. package/dist-lib/integrations/supabase/moduleClient.d.ts +511 -6
  36. package/dist-lib/integrations/supabase/moduleClient.d.ts.map +1 -1
  37. package/dist-lib/integrations/supabase/types.d.ts +527 -6
  38. package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
  39. package/dist-lib/lib/aiLayout.functions.d.ts +511 -6
  40. package/dist-lib/lib/aiLayout.functions.d.ts.map +1 -1
  41. package/dist-lib/lib/documentRoutering.d.ts +2 -1
  42. package/dist-lib/lib/documentRoutering.d.ts.map +1 -1
  43. package/dist-lib/lib/documentRoutering.js +5 -8
  44. package/dist-lib/lib/foutTekst.d.ts +3 -0
  45. package/dist-lib/lib/foutTekst.d.ts.map +1 -0
  46. package/dist-lib/lib/foutTekst.js +25 -0
  47. package/dist-lib/lib/printApi.d.ts.map +1 -1
  48. package/dist-lib/lib/printApi.js +13 -39
  49. package/dist-lib/lib/printQueue.server.d.ts.map +1 -1
  50. package/dist-lib/lib/printQueue.server.js +19 -66
  51. package/dist-lib/lib/printRoutering.d.ts +34 -0
  52. package/dist-lib/lib/printRoutering.d.ts.map +1 -0
  53. package/dist-lib/lib/printRoutering.js +69 -0
  54. package/dist-lib/lib/printService.functions.d.ts +2555 -30
  55. package/dist-lib/lib/printService.functions.d.ts.map +1 -1
  56. package/dist-lib/lib/printplekken.d.ts +67 -2
  57. package/dist-lib/lib/printplekken.d.ts.map +1 -1
  58. package/dist-lib/lib/printplekken.js +141 -10
  59. package/dist-lib/lib/useDocumentData.d.ts +15 -10
  60. package/dist-lib/lib/useDocumentData.d.ts.map +1 -1
  61. package/dist-lib/lib/useDocumentData.js +43 -34
  62. package/package.json +2 -2
@@ -1,57 +1,136 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  // ============================================================================
3
- // src/components/PrintplekDetailPage.tsx — één printplek instellen: welke
4
- // computers erbij horen, welke printers beschikbaar zijn en welk document naar
5
- // welke printer gaat.
3
+ // src/components/PrintplekDetailPage.tsx — de centrale plek voor één printplek:
4
+ // computers, printers (per computer) en welk document naar welke printer gaat.
5
+ // Alles is hier direct bewerkbaar; de wizard is alleen voor een nieuwe plek.
6
6
  // ============================================================================
7
- import { useMemo, useState } from "react";
7
+ import { useEffect, useMemo, useState } from "react";
8
8
  import { Badge, Button, Card, CardContent, CardDescription, CardHeader, CardTitle, Input, toast, } from "@flowselections/core";
9
- import { Link, useParams } from "@tanstack/react-router";
10
- import { AlertTriangle, ArrowLeft, Check, Link2, Monitor, Plus, Printer, Trash2, Wand2, X, } from "lucide-react";
11
- import { bewerkPrintplek, ontkoppelBron, usePrintplekken, verwijderPrintplek, } from "../lib/printplekken.js";
9
+ import { Link, useNavigate, useParams } from "@tanstack/react-router";
10
+ import { PrintplekVerwijderenDialog } from "./PrintplekVerwijderenDialog.js";
11
+ import { AlertTriangle, ArrowLeft, Check, Link2, Monitor, Plus, Printer, Trash2, X, } from "lucide-react";
12
+ import { bewerkPrintplek, koppelComputerAanPrintplek, koppelPrinterLosVanPrintplek, ontkoppelBron, usePrintplekken, verplaatsPrinterNaarPrintplek, wijsPrinterToeAanPrintplek, zetPrintplekPrinter, } from "../lib/printplekken.js";
13
+ import { PrinterVervangenDialog } from "./PrinterVervangenDialog.js";
14
+ import { ComputerVerwijderenDialog } from "./ComputerVerwijderenDialog.js";
12
15
  import { usePrinters } from "../lib/useDocumentData.js";
13
16
  import { useNetwerkPrinters } from "../lib/useNetwerkPrinters.js";
14
17
  import { useComputers } from "./ComputersLijst.js";
15
18
  import { DOCUMENT_TYPES, documentTypeLabel } from "../lib/documentTypes.js";
16
19
  import { drukTestpaginaAf } from "../lib/testpagina.js";
17
- import { PrintplekWizard } from "./PrintplekWizard.js";
18
20
  import { TestprintDialog } from "./TestprintDialog.js";
21
+ import { leesbareFout } from "../lib/foutTekst.js";
22
+ import { useRouteringOverzicht } from "../lib/printRoutering.js";
23
+ const BRON_LABEL = {
24
+ printplek: "Printplek-eigen",
25
+ werkplek: "Werkplek-uitzondering",
26
+ layout: "Standaardprinter (layout)",
27
+ standaard: "Bedrijfsstandaard",
28
+ geen: "Geen regel",
29
+ };
30
+ const selectKlasse = "border-input bg-background rounded-md border px-2 py-1 text-sm";
19
31
  export function PrintplekDetailPage() {
20
32
  const params = useParams({ strict: false });
21
33
  const plekId = params.printplekId ?? "";
22
- const { plekken, computers: plekComputers, regels, koppelingen, reload } = usePrintplekken();
23
- const { computers } = useComputers();
34
+ const navigate = useNavigate();
35
+ const [verwijderOpen, setVerwijderOpen] = useState(false);
36
+ const { plekken, computers: plekComputers, regels, koppelingen, toegewezen, reload } = usePrintplekken();
37
+ const { computers, reload: herlaadComputers } = useComputers();
24
38
  const { printers } = usePrinters();
25
39
  const { gevonden } = useNetwerkPrinters();
26
40
  const [naamBewerken, setNaamBewerken] = useState(false);
27
41
  const [naam, setNaam] = useState("");
28
42
  const [alleDocumenten, setAlleDocumenten] = useState(false);
29
- const [wizardOpen, setWizardOpen] = useState(false);
30
43
  const [testprintOpen, setTestprintOpen] = useState(false);
44
+ const [vervangen, setVervangen] = useState(null);
45
+ const [toevoegen, setToevoegen] = useState("");
46
+ const [computerToevoegen, setComputerToevoegen] = useState("");
47
+ const [teVerwijderen, setTeVerwijderen] = useState(null);
31
48
  const plek = plekken.find((p) => p.id === plekId) ?? null;
32
49
  const eigenComputers = plekComputers.filter((c) => c.print_plek_id === plekId);
33
- const eigenNamen = new Set(eigenComputers.map((c) => c.computer_naam));
34
- // Printers van deze plek: alles wat op één van de gekoppelde computers staat.
35
- const externOpComputer = useMemo(() => {
36
- const map = new Map();
37
- for (const g of gevonden)
38
- if (g.computer)
39
- map.set(g.id, g.computer);
40
- return map;
50
+ const vrijeComputers = computers.filter((c) => !plekComputers.some((pc) => pc.computer_naam === c.naam));
51
+ const andereplekken = plekken.filter((p) => p.id !== plekId);
52
+ // De live scan bepaalt alleen online-status en op welke computer een printer hangt.
53
+ const computerVanExtern = useMemo(() => {
54
+ const m = new Map();
55
+ for (const g of Array.isArray(gevonden) ? gevonden : [])
56
+ if (g.id)
57
+ m.set(String(g.id), g.computer);
58
+ return m;
41
59
  }, [gevonden]);
42
- const eigenPrinters = printers.filter((p) => {
43
- const extern = p.extern_printer_id;
44
- const computer = extern ? externOpComputer.get(extern) : undefined;
45
- return computer ? eigenNamen.has(computer) : false;
60
+ const externVan = (p) => p.extern_printer_id ?? null;
61
+ const isOnline = (p) => {
62
+ const e = externVan(p);
63
+ return e ? computerVanExtern.has(String(e)) : false;
64
+ };
65
+ const computerVan = (p) => {
66
+ const e = externVan(p);
67
+ return e ? computerVanExtern.get(String(e)) ?? null : null;
68
+ };
69
+ const toegewezenIds = new Set((Array.isArray(toegewezen) ? toegewezen : [])
70
+ .filter((t) => t.print_plek_id === plekId)
71
+ .map((t) => t.printer_id));
72
+ const eigenPrinters = printers.filter((p) => toegewezenIds.has(p.id));
73
+ const nietToegewezen = printers.filter((p) => !toegewezenIds.has(p.id));
74
+ const eigenNamen = new Set(eigenComputers.map((c) => c.computer_naam));
75
+ const printersPerComputer = eigenComputers.map((c) => ({
76
+ naam: c.computer_naam,
77
+ printers: eigenPrinters.filter((p) => computerVan(p) === c.computer_naam),
78
+ }));
79
+ const nietGevonden = eigenPrinters.filter((p) => {
80
+ const c = computerVan(p);
81
+ return !c || !eigenNamen.has(c);
46
82
  });
47
- const regelVoor = (documenttype) => regels.find((r) => r.print_plek_id === plekId && r.documenttype === documenttype) ?? null;
48
- const inGebruik = DOCUMENT_TYPES.filter((d) => regelVoor(d.value));
49
- const overige = DOCUMENT_TYPES.filter((d) => !regelVoor(d.value));
50
- const zichtbaar = alleDocumenten ? DOCUMENT_TYPES : inGebruik;
83
+ const pcNamen = eigenComputers.map((c) => c.computer_naam).sort();
84
+ const { uitkomsten, laden: routeringLaden, reload: reloadRoutering } = useRouteringOverzicht(plekId, DOCUMENT_TYPES.map((d) => d.value), pcNamen);
85
+ const uitkomstVoor = (t, pc) => uitkomsten[`${t}::${pc ?? ""}`] ?? null;
86
+ const routeringZichtbaar = DOCUMENT_TYPES.filter((d) => alleDocumenten ||
87
+ (pcNamen.length ? pcNamen : [null]).some((pc) => {
88
+ const u = uitkomstVoor(d.value, pc);
89
+ return !!u && (u.bron !== "geen" || u.overgeslagen.length > 0);
90
+ }));
91
+ useEffect(() => {
92
+ void reloadRoutering();
93
+ // eslint-disable-next-line react-hooks/exhaustive-deps
94
+ }, [regels, toegewezen]);
95
+ const printerNaam = (id) => printers.find((p) => p.id === id)?.naam ?? null;
96
+ const printerOnline = (id) => {
97
+ const p = printers.find((x) => x.id === id);
98
+ return p ? isOnline(p) : false;
99
+ };
100
+ const computerId = (n) => computers.find((k) => k.naam === n)?.id ?? null;
101
+ const eigenRegel = (t) => regels.find((r) => r.print_plek_id === plekId && r.documenttype === t) ?? null;
102
+ const vernieuw = async () => {
103
+ await reload(true);
104
+ await reloadRoutering();
105
+ };
106
+ const zetRegel = async (documenttype, waarde) => {
107
+ try {
108
+ await zetPrintplekPrinter(plekId, documenttype, waarde === "standaard" ? null : waarde);
109
+ await vernieuw();
110
+ toast.success("Printer opgeslagen");
111
+ }
112
+ catch (err) {
113
+ toast.error(leesbareFout(err));
114
+ }
115
+ };
116
+ function BronLink({ u }) {
117
+ const pcId = computerId(u.werkplek);
118
+ if (u.bron === "werkplek" && pcId)
119
+ return (_jsx(Link, { to: "/layouts-printers/printplekken/computers/$computerId", params: { computerId: pcId }, className: "text-primary text-xs underline", children: "Werkplek-uitzondering" }));
120
+ if (u.bron === "standaard")
121
+ return (_jsx(Link, { to: "/layouts-printers/printplekken", className: "text-primary text-xs underline", children: "Standaard bewerken" }));
122
+ return null;
123
+ }
124
+ function RouteringCellen({ u }) {
125
+ if (!u)
126
+ return _jsx("td", { colSpan: 3 });
127
+ const n = printerNaam(u.printer_id);
128
+ const status = !u.printer_id ? "geen" : n ? (printerOnline(u.printer_id) ? "online" : "offline") : "ontbreekt";
129
+ return (_jsxs(_Fragment, { children: [_jsx("td", { className: "py-2 pr-3 text-xs text-muted-foreground", children: u.papierformaat ?? "—" }), _jsxs("td", { className: "py-2 pr-3", children: [_jsxs(Badge, { variant: u.bron === "geen" ? "destructive" : "secondary", "data-bron": u.bron, children: [BRON_LABEL[u.bron], u.bron === "werkplek" && u.werkplek ? ` (${u.werkplek})` : ""] }), u.overgeslagen.map((o) => (_jsxs("div", { className: "text-destructive mt-1 flex items-center gap-1 text-xs", children: [_jsx(AlertTriangle, { className: "h-3.5 w-3.5" }), " ", BRON_LABEL[o.bron], " overgeslagen: printer", " ", o.reden === "ontbreekt" ? "ontbreekt" : o.reden === "inactief" ? "inactief" : "niet (meer) aan deze plek gekoppeld"] }, o.regel_id)))] }), _jsxs("td", { className: "py-2 pr-3", children: [u.printer_id ? (_jsxs("span", { className: "flex items-center gap-1", children: [_jsx("span", { className: `h-2 w-2 rounded-full ${status === "online" ? "bg-emerald-500" : status === "offline" ? "bg-muted-foreground" : "bg-destructive"}` }), n ?? "Onbekende printer", _jsx("span", { className: "text-muted-foreground text-xs", children: status })] })) : (_jsx("span", { className: "text-destructive text-xs", children: "geen printer \u2014 opdrachten blijven wachten" })), _jsx("div", { children: _jsx(BronLink, { u: u }) })] })] }));
130
+ }
51
131
  const bronnen = koppelingen.filter((k) => k.print_plek_id === plekId);
52
132
  const bestaandePlekIds = new Set(plekken.map((p) => p.id));
53
133
  const weeskoppelingen = koppelingen.filter((k) => !bestaandePlekIds.has(k.print_plek_id));
54
- const onvolledig = eigenComputers.length === 0 || !regels.some((r) => r.print_plek_id === plekId && r.printer_id);
55
134
  const ontkoppel = async (bron, bronId) => {
56
135
  try {
57
136
  await ontkoppelBron(bron, bronId);
@@ -62,9 +141,44 @@ export function PrintplekDetailPage() {
62
141
  toast.error("Ontkoppelen mislukt");
63
142
  }
64
143
  };
144
+ const loskoppelen = async (p) => {
145
+ const n = regels.filter((r) => r.print_plek_id === plekId && r.printer_id === p.id).length;
146
+ if (n > 0 && !window.confirm(`${n} regel(s) wijzen naar ${p.naam}. Die worden leeg en gemarkeerd. Doorgaan?`))
147
+ return;
148
+ try {
149
+ await koppelPrinterLosVanPrintplek(plekId, p.id);
150
+ await vernieuw();
151
+ toast.success("Printer verwijderd van deze plek");
152
+ }
153
+ catch {
154
+ toast.error("Verwijderen mislukt");
155
+ }
156
+ };
157
+ const verplaats = async (p, naarPlek) => {
158
+ try {
159
+ await verplaatsPrinterNaarPrintplek(p.id, plekId, naarPlek);
160
+ await vernieuw();
161
+ toast.success(`${p.naam} verplaatst`);
162
+ }
163
+ catch {
164
+ toast.error("Verplaatsen mislukt");
165
+ }
166
+ };
65
167
  if (!plek) {
66
168
  return (_jsxs("main", { className: "space-y-4 p-6", children: [_jsx(Button, { variant: "ghost", asChild: true, children: _jsxs(Link, { to: "/layouts-printers/printplekken", children: [_jsx(ArrowLeft, { className: "mr-2 h-4 w-4" }), " Terug"] }) }), _jsx("p", { className: "text-muted-foreground text-sm", children: "Deze printplek bestaat niet (meer)." })] }));
67
169
  }
170
+ const PrinterRij = ({ p }) => (_jsxs("div", { className: "flex flex-wrap items-center justify-between gap-2 rounded-lg border p-2", children: [_jsxs("div", { className: "flex items-center gap-2 text-sm", children: [_jsx("span", { className: `h-2 w-2 rounded-full ${isOnline(p) ? "bg-emerald-500" : "bg-muted-foreground"}` }), _jsx("span", { className: "font-medium", children: p.naam }), p.papierformaat ? _jsx(Badge, { variant: "secondary", children: p.papierformaat }) : null, !isOnline(p) ? _jsx("span", { className: "text-muted-foreground text-xs", children: "offline" }) : null] }), _jsxs("div", { className: "flex flex-wrap items-center gap-1", children: [_jsx(Button, { size: "sm", variant: "ghost", onClick: async () => {
171
+ try {
172
+ await drukTestpaginaAf(p.id, p.naam);
173
+ toast.success("Testpagina verstuurd");
174
+ }
175
+ catch {
176
+ toast.error("Testpagina versturen is niet gelukt.");
177
+ }
178
+ }, children: "Testpagina" }), _jsx(Button, { size: "sm", variant: "ghost", onClick: () => setVervangen({ id: p.id, naam: p.naam }), children: "Vervangen" }), andereplekken.length > 0 ? (_jsxs("select", { className: selectKlasse, value: "", "aria-label": `${p.naam} verplaatsen`, onChange: (e) => e.target.value && void verplaats(p, e.target.value), children: [_jsx("option", { value: "", children: "Verplaatsen\u2026" }), andereplekken.map((o) => (_jsxs("option", { value: o.id, children: ["naar ", o.naam] }, o.id)))] })) : null, _jsx(Button, { size: "sm", variant: "ghost", className: "text-destructive", onClick: () => void loskoppelen(p), children: "Verwijderen" })] })] }));
179
+ const teVerwijderenPrinters = teVerwijderen
180
+ ? eigenPrinters.filter((p) => computerVan(p) === teVerwijderen).map((p) => ({ id: p.id, naam: p.naam }))
181
+ : [];
68
182
  return (_jsxs("main", { className: "space-y-6 p-6", children: [_jsxs("div", { className: "flex flex-wrap items-start justify-between gap-4", children: [_jsxs("div", { className: "space-y-1", children: [_jsx(Button, { variant: "ghost", size: "sm", asChild: true, className: "-ml-2", children: _jsxs(Link, { to: "/layouts-printers/printplekken", children: [_jsx(ArrowLeft, { className: "mr-2 h-4 w-4" }), " Alle printplekken"] }) }), naamBewerken ? (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Input, { value: naam, onChange: (e) => setNaam(e.target.value), className: "w-64" }), _jsx(Button, { size: "icon", onClick: async () => {
69
183
  await bewerkPrintplek(plek.id, { naam: naam.trim() });
70
184
  setNaamBewerken(false);
@@ -72,28 +186,41 @@ export function PrintplekDetailPage() {
72
186
  }, children: _jsx(Check, { className: "h-4 w-4" }) }), _jsx(Button, { size: "icon", variant: "ghost", onClick: () => setNaamBewerken(false), children: _jsx(X, { className: "h-4 w-4" }) })] })) : (_jsx("h1", { className: "cursor-pointer text-xl font-semibold", onClick: () => {
73
187
  setNaam(plek.naam);
74
188
  setNaamBewerken(true);
75
- }, children: plek.naam })), _jsx("p", { className: "text-muted-foreground text-sm", children: plek.omschrijving ?? "Stel hier in wat er op deze plek wordt afgedrukt." })] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsxs(Button, { variant: "outline", onClick: () => setTestprintOpen(true), children: [_jsx(Printer, { className: "mr-2 h-4 w-4" }), " Testprint controleren"] }), _jsxs(Button, { variant: "outline", onClick: () => setWizardOpen(true), children: [_jsx(Wand2, { className: "mr-2 h-4 w-4" }), onvolledig ? "Afmaken met de wizard" : "Instellen met de wizard"] }), _jsxs(Button, { variant: "ghost", className: "text-destructive", onClick: async () => {
76
- await verwijderPrintplek(plek.id);
77
- toast.success("Printplek verwijderd");
78
- await reload();
79
- }, children: [_jsx(Trash2, { className: "mr-2 h-4 w-4" }), " Verwijderen"] })] })] }), _jsx(PrintplekWizard, { open: wizardOpen, onOpenChange: setWizardOpen, printplekId: plekId, onKlaar: () => void reload() }), _jsx(TestprintDialog, { open: testprintOpen, onOpenChange: setTestprintOpen, printplekId: plekId, printplekNaam: plek.naam }), _jsxs(Card, { children: [_jsxs(CardHeader, { children: [_jsxs(CardTitle, { className: "flex items-center gap-2 text-base", children: [_jsx(Monitor, { className: "h-4 w-4" }), " Computers op deze plek"] }), _jsx(CardDescription, { children: "Computers voeg je toe of haal je weg met de wizard." })] }), _jsx(CardContent, { className: "space-y-3", children: eigenComputers.length === 0 ? (_jsx("p", { className: "text-muted-foreground text-sm", children: "Nog geen computers gekoppeld." })) : (_jsx("div", { className: "flex flex-wrap gap-2", children: eigenComputers.map((c) => {
80
- const aan = computers.find((k) => k.naam === c.computer_naam)?.online ?? false;
81
- return (_jsxs("span", { className: "flex items-center gap-2 rounded-full border px-3 py-1 text-sm", children: [_jsx("span", { className: `h-2 w-2 rounded-full ${aan ? "bg-emerald-500" : "bg-muted-foreground"}` }), c.computer_naam] }, c.id));
82
- }) })) })] }), _jsxs(Card, { children: [_jsxs(CardHeader, { children: [_jsxs(CardTitle, { className: "flex items-center gap-2 text-base", children: [_jsx(Printer, { className: "h-4 w-4" }), " Printers op deze plek"] }), _jsx(CardDescription, { children: "Test een printer om te zien of hij goed staat." })] }), _jsx(CardContent, { children: eigenPrinters.length === 0 ? (_jsxs("div", { className: "text-muted-foreground flex items-center gap-2 text-sm", children: [_jsx(AlertTriangle, { className: "text-destructive h-4 w-4" }), "Nog geen ingestelde printers. Voeg ze toe bij", " ", _jsx(Link, { className: "text-primary underline", to: "/layouts-printers/printplekken", children: "Printplekken" }), "."] })) : (_jsx("div", { className: "space-y-2", children: eigenPrinters.map((p) => (_jsxs("div", { className: "flex flex-wrap items-center justify-between gap-2 rounded-lg border p-2", children: [_jsxs("div", { className: "text-sm", children: [_jsx("span", { className: "font-medium", children: p.naam }), p.papierformaat ? (_jsx(Badge, { variant: "secondary", className: "ml-2", children: p.papierformaat })) : null] }), _jsx(Button, { size: "sm", variant: "ghost", onClick: async () => {
189
+ }, children: plek.naam })), _jsx("p", { className: "text-muted-foreground text-sm", children: plek.omschrijving ?? "Hier stel je alles in wat er op deze plek wordt afgedrukt." })] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsxs(Button, { variant: "outline", onClick: () => setTestprintOpen(true), children: [_jsx(Printer, { className: "mr-2 h-4 w-4" }), " Testprint controleren"] }), _jsxs(Button, { variant: "ghost", className: "text-destructive", onClick: () => setVerwijderOpen(true), children: [_jsx(Trash2, { className: "mr-2 h-4 w-4" }), " Verwijderen"] })] })] }), _jsx(PrintplekVerwijderenDialog, { plek: verwijderOpen ? { id: plek.id, naam: plek.naam } : null, onOpenChange: setVerwijderOpen, onKlaar: () => void navigate({ to: "/layouts-printers/printplekken" }) }), _jsx(TestprintDialog, { open: testprintOpen, onOpenChange: setTestprintOpen, printplekId: plekId, printplekNaam: plek.naam }), _jsxs(Card, { children: [_jsxs(CardHeader, { className: "flex flex-row items-start justify-between gap-3", children: [_jsxs("div", { children: [_jsxs(CardTitle, { className: "flex items-center gap-2 text-base", children: [_jsx(Monitor, { className: "h-4 w-4" }), " Computers op deze plek"] }), _jsx(CardDescription, { children: "Het bolletje laat zien of de computer nu aan staat." })] }), vrijeComputers.length > 0 ? (_jsxs("div", { className: "flex items-center gap-2", children: [_jsxs("select", { className: selectKlasse, value: computerToevoegen, onChange: (e) => setComputerToevoegen(e.target.value), "aria-label": "Computer kiezen", children: [_jsx("option", { value: "", children: "Computer kiezen\u2026" }), vrijeComputers.map((c) => (_jsx("option", { value: c.naam, children: c.naam }, c.id)))] }), _jsxs(Button, { size: "sm", disabled: !computerToevoegen, onClick: async () => {
190
+ try {
191
+ await koppelComputerAanPrintplek(plekId, computerToevoegen);
192
+ setComputerToevoegen("");
193
+ await vernieuw();
194
+ }
195
+ catch {
196
+ toast.error("Toevoegen mislukt");
197
+ }
198
+ }, children: [_jsx(Plus, { className: "mr-1 h-4 w-4" }), " Computer toevoegen"] })] })) : null] }), _jsx(CardContent, { className: "space-y-2", children: eigenComputers.length === 0 ? (_jsx("p", { className: "text-muted-foreground text-sm", children: "Nog geen computers gekoppeld." })) : (eigenComputers.map((c) => {
199
+ const aan = computers.find((k) => k.naam === c.computer_naam)?.online ?? false;
200
+ return (_jsxs("div", { className: "flex items-center gap-3 rounded-lg border px-3 py-2 text-sm", children: [_jsx("span", { className: `h-2 w-2 rounded-full ${aan ? "bg-emerald-500" : "bg-muted-foreground"}` }), _jsx("span", { className: "min-w-0 flex-1 truncate font-medium", children: c.computer_naam }), _jsx("span", { className: "text-muted-foreground text-xs", children: aan ? "Aan" : "Uit" }), _jsxs(Button, { size: "sm", variant: "ghost", className: "text-destructive", onClick: () => setTeVerwijderen(c.computer_naam), children: [_jsx(Trash2, { className: "mr-1 h-3.5 w-3.5" }), " Verwijderen"] })] }, c.id));
201
+ })) })] }), _jsx(ComputerVerwijderenDialog, { open: !!teVerwijderen, onOpenChange: (v) => !v && setTeVerwijderen(null), computerNaam: teVerwijderen, printplekId: plekId, printers: teVerwijderenPrinters, andereplekken: andereplekken, onKlaar: () => {
202
+ void herlaadComputers(true);
203
+ void vernieuw();
204
+ } }), _jsxs(Card, { children: [_jsxs(CardHeader, { children: [_jsxs(CardTitle, { className: "flex items-center gap-2 text-base", children: [_jsx(Printer, { className: "h-4 w-4" }), " Printers op deze plek"] }), _jsx(CardDescription, { children: "Gegroepeerd per computer waaraan de printer volgens de live scan hangt." })] }), _jsxs(CardContent, { className: "space-y-4", children: [eigenPrinters.length === 0 ? (_jsxs("div", { className: "text-muted-foreground flex items-center gap-2 text-sm", children: [_jsx(AlertTriangle, { className: "text-destructive h-4 w-4" }), "Nog geen printers gekoppeld aan deze plek."] })) : null, printersPerComputer.map((g) => g.printers.length ? (_jsxs("div", { className: "space-y-2", children: [_jsxs("p", { className: "text-muted-foreground flex items-center gap-2 text-xs font-medium", children: [_jsx(Monitor, { className: "h-3.5 w-3.5" }), " ", g.naam] }), g.printers.map((p) => _jsx(PrinterRij, { p: p }, p.id))] }, g.naam)) : null), nietGevonden.length > 0 ? (_jsxs("div", { className: "space-y-2", children: [_jsxs("p", { className: "text-muted-foreground flex items-center gap-2 text-xs font-medium", children: [_jsx(AlertTriangle, { className: "h-3.5 w-3.5" }), " Niet gevonden op een computer van deze plek"] }), nietGevonden.map((p) => _jsx(PrinterRij, { p: p }, p.id))] })) : null, nietToegewezen.length > 0 ? (_jsxs("div", { className: "flex items-center gap-2", children: [_jsxs("select", { className: selectKlasse, value: toevoegen, onChange: (e) => setToevoegen(e.target.value), children: [_jsx("option", { value: "", children: "Printer toevoegen\u2026" }), nietToegewezen.map((p) => (_jsx("option", { value: p.id, children: p.naam }, p.id)))] }), _jsxs(Button, { size: "sm", disabled: !toevoegen, onClick: async () => {
83
205
  try {
84
- await drukTestpaginaAf(p.id, p.naam);
85
- toast.success("Testpagina verstuurd");
206
+ await wijsPrinterToeAanPrintplek(plekId, toevoegen);
207
+ setToevoegen("");
208
+ await vernieuw();
86
209
  }
87
210
  catch {
88
- toast.error("Testpagina versturen is niet gelukt.");
211
+ toast.error("Toevoegen mislukt");
89
212
  }
90
- }, children: "Testpagina" })] }, p.id))) })) })] }), _jsxs(Card, { children: [_jsxs(CardHeader, { className: "flex flex-row items-start justify-between gap-3", children: [_jsxs("div", { children: [_jsx(CardTitle, { className: "text-base", children: "Welk document gaat naar welke printer?" }), _jsx(CardDescription, { children: "Alleen wat hier is ingesteld wordt op deze plek afgedrukt. Wijzigen doe je met de wizard." })] }), _jsx(Button, { variant: "ghost", size: "sm", onClick: () => setAlleDocumenten((v) => !v), children: alleDocumenten ? "Alleen ingestelde" : (_jsxs(_Fragment, { children: [_jsx(Plus, { className: "mr-1 h-4 w-4" }), " Meer documenten"] })) })] }), _jsxs(CardContent, { className: "space-y-2", children: [zichtbaar.length === 0 ? (_jsx("p", { className: "text-muted-foreground text-sm", children: "Nog niets ingesteld. Gebruik de wizard bovenaan deze pagina." })) : null, zichtbaar.map((d) => {
91
- const regel = regelVoor(d.value);
92
- // Let op: de virtuele PDF-printer hangt niet aan een computer, dus
93
- // eerst binnen deze plek zoeken en anders in álle printers.
94
- const printer = eigenPrinters.find((p) => p.id === regel?.printer_id) ??
95
- printers.find((p) => p.id === regel?.printer_id) ??
96
- null;
97
- return (_jsxs("div", { className: "flex flex-wrap items-center justify-between gap-3 rounded-lg border p-2", children: [_jsx("div", { className: "text-sm font-medium", children: documentTypeLabel(d.value) }), printer ? (_jsx("span", { className: "text-muted-foreground text-sm", children: printer.naam })) : (_jsxs("span", { className: "text-destructive flex items-center gap-1 text-xs", children: [_jsx(AlertTriangle, { className: "h-3.5 w-3.5" }), " Geen printer"] }))] }, d.value));
98
- }), !alleDocumenten && overige.length > 0 ? (_jsxs("p", { className: "text-muted-foreground text-xs", children: [overige.length, " documentsoort", overige.length === 1 ? "" : "en", " nog niet ingesteld."] })) : null] })] }), _jsxs(Card, { children: [_jsxs(CardHeader, { children: [_jsxs(CardTitle, { className: "flex items-center gap-2 text-base", children: [_jsx(Link2, { className: "h-4 w-4" }), " Gekoppeld aan"] }), _jsx(CardDescription, { children: "Andere modules (bijvoorbeeld pickstations) kunnen zich aan deze printplek koppelen." })] }), _jsxs(CardContent, { className: "space-y-4", children: [bronnen.length === 0 ? (_jsx("p", { className: "text-muted-foreground text-sm", children: "Nog niets gekoppeld. Kies deze printplek in de andere module (bijvoorbeeld bij een pickstation); de koppeling verschijnt hier vanzelf." })) : (_jsx("div", { className: "space-y-2", children: bronnen.map((b) => (_jsxs("div", { className: "flex flex-wrap items-center gap-3 rounded-lg border px-3 py-2 text-sm", children: [_jsx(Badge, { variant: "secondary", children: b.bron }), _jsxs("span", { className: "min-w-0 flex-1 truncate", children: [b.label ?? b.bron_id, _jsx("span", { className: "text-muted-foreground ml-2 text-xs", children: b.bron_id })] }), _jsxs(Button, { variant: "ghost", size: "sm", onClick: () => void ontkoppel(b.bron, b.bron_id), children: [_jsx(X, { className: "mr-1 h-3.5 w-3.5" }), " Ontkoppelen"] })] }, b.id))) })), weeskoppelingen.length > 0 ? (_jsxs("div", { className: "space-y-2 rounded-lg border border-dashed p-3", children: [_jsxs("p", { className: "text-muted-foreground flex items-center gap-2 text-xs", children: [_jsx(AlertTriangle, { className: "h-3.5 w-3.5" }), " Koppelingen zonder bestaande printplek:"] }), weeskoppelingen.map((b) => (_jsxs("div", { className: "flex flex-wrap items-center gap-3 text-sm", children: [_jsx("span", { className: "min-w-0 flex-1 truncate", children: b.label ?? `${b.bron} ${b.bron_id}` }), _jsxs(Button, { variant: "ghost", size: "sm", onClick: () => void ontkoppel(b.bron, b.bron_id), children: [_jsx(Trash2, { className: "mr-1 h-3.5 w-3.5" }), " Opruimen"] })] }, b.id)))] })) : null] })] })] }));
213
+ }, children: [_jsx(Plus, { className: "mr-1 h-4 w-4" }), " Toevoegen"] })] })) : null, _jsx(PrinterVervangenDialog, { open: !!vervangen, onOpenChange: (v) => !v && setVervangen(null), printer: vervangen, printers: printers, onKlaar: () => void vernieuw() })] })] }), _jsxs(Card, { children: [_jsxs(CardHeader, { className: "flex flex-row items-start justify-between gap-3", children: [_jsxs("div", { children: [_jsx(CardTitle, { className: "text-base", children: "Welk document gaat naar welke printer?" }), _jsx(CardDescription, { children: "Berekend met dezelfde routering als het echte printen. Kies een printer om voor deze plek een eigen regel te maken, of \"Standaard gebruiken\"." })] }), _jsx(Button, { variant: "ghost", size: "sm", onClick: () => setAlleDocumenten((v) => !v), children: alleDocumenten ? "Alleen met printer" : (_jsxs(_Fragment, { children: [_jsx(Plus, { className: "mr-1 h-4 w-4" }), " Alle documenten"] })) })] }), _jsxs(CardContent, { children: [routeringLaden ? _jsx("p", { className: "text-muted-foreground text-sm", children: "Routering berekenen\u2026" }) : null, !routeringLaden && routeringZichtbaar.length === 0 ? (_jsx("p", { className: "text-muted-foreground text-sm", children: "Voor geen enkel document is een printer gevonden. Klik op \"Alle documenten\" om er een in te stellen." })) : null, routeringZichtbaar.length > 0 ? (_jsx("div", { className: "overflow-x-auto", children: _jsxs("table", { className: "w-full text-sm", children: [_jsx("thead", { children: _jsxs("tr", { className: "text-muted-foreground border-b text-left text-xs", children: [_jsx("th", { className: "py-2 pr-3", children: "Document" }), _jsx("th", { className: "py-2 pr-3", children: "Formaat" }), _jsx("th", { className: "py-2 pr-3", children: "Winnende regel" }), _jsx("th", { className: "py-2 pr-3", children: "Printer" }), _jsx("th", { className: "py-2", children: "Printer op deze plek" })] }) }), _jsx("tbody", { children: routeringZichtbaar.flatMap((d) => {
214
+ const rijen = pcNamen.length ? pcNamen : [null];
215
+ const hoofd = uitkomstVoor(d.value, rijen[0]);
216
+ const afwijkend = rijen
217
+ .slice(1)
218
+ .map((pc) => ({ pc, u: uitkomstVoor(d.value, pc) }))
219
+ .filter((x) => x.u && (x.u.bron !== hoofd?.bron || x.u.printer_id !== hoofd?.printer_id));
220
+ const regel = eigenRegel(d.value);
221
+ return [
222
+ _jsxs("tr", { className: "border-b align-top", children: [_jsxs("td", { className: "py-2 pr-3 font-medium", children: [documentTypeLabel(d.value), pcNamen.length > 1 ? (_jsxs("div", { className: "text-muted-foreground text-xs font-normal", children: ["op ", rijen[0]] })) : null] }), _jsx(RouteringCellen, { u: hoofd }), _jsx("td", { className: "py-2", children: _jsxs("select", { className: selectKlasse, "aria-label": `Printer voor ${documentTypeLabel(d.value)}`, value: regel?.printer_id ?? "standaard", onChange: (e) => void zetRegel(d.value, e.target.value), children: [_jsx("option", { value: "standaard", children: "Standaard gebruiken" }), eigenPrinters.filter((p) => p.actief).map((p) => (_jsx("option", { value: p.id, children: p.naam }, p.id)))] }) })] }, d.value),
223
+ ...afwijkend.map((x) => (_jsxs("tr", { className: "border-b align-top", children: [_jsxs("td", { className: "text-muted-foreground py-2 pr-3 pl-4 text-xs", children: ["op ", x.pc] }), _jsx(RouteringCellen, { u: x.u }), _jsx("td", {})] }, `${d.value}-${x.pc}`))),
224
+ ];
225
+ }) })] }) })) : null] })] }), _jsxs(Card, { children: [_jsxs(CardHeader, { children: [_jsxs(CardTitle, { className: "flex items-center gap-2 text-base", children: [_jsx(Link2, { className: "h-4 w-4" }), " Gekoppeld aan"] }), _jsx(CardDescription, { children: "Andere modules (bijvoorbeeld pickstations) kunnen zich aan deze printplek koppelen." })] }), _jsxs(CardContent, { className: "space-y-4", children: [bronnen.length === 0 ? (_jsx("p", { className: "text-muted-foreground text-sm", children: "Nog niets gekoppeld. Kies deze printplek in de andere module (bijvoorbeeld bij een pickstation); de koppeling verschijnt hier vanzelf." })) : (_jsx("div", { className: "space-y-2", children: bronnen.map((b) => (_jsxs("div", { className: "flex flex-wrap items-center gap-3 rounded-lg border px-3 py-2 text-sm", children: [_jsx(Badge, { variant: "secondary", children: b.bron }), _jsxs("span", { className: "min-w-0 flex-1 truncate", children: [b.label ?? b.bron_id, _jsx("span", { className: "text-muted-foreground ml-2 text-xs", children: b.bron_id })] }), _jsxs(Button, { variant: "ghost", size: "sm", onClick: () => void ontkoppel(b.bron, b.bron_id), children: [_jsx(X, { className: "mr-1 h-3.5 w-3.5" }), " Ontkoppelen"] })] }, b.id))) })), weeskoppelingen.length > 0 ? (_jsxs("div", { className: "space-y-2 rounded-lg border border-dashed p-3", children: [_jsxs("p", { className: "text-muted-foreground flex items-center gap-2 text-xs", children: [_jsx(AlertTriangle, { className: "h-3.5 w-3.5" }), " Koppelingen zonder bestaande printplek:"] }), weeskoppelingen.map((b) => (_jsxs("div", { className: "flex flex-wrap items-center gap-3 text-sm", children: [_jsx("span", { className: "min-w-0 flex-1 truncate", children: b.label ?? `${b.bron} ${b.bron_id}` }), _jsxs(Button, { variant: "ghost", size: "sm", onClick: () => void ontkoppel(b.bron, b.bron_id), children: [_jsx(Trash2, { className: "mr-1 h-3.5 w-3.5" }), " Opruimen"] })] }, b.id)))] })) : null] })] })] }));
99
226
  }
@@ -0,0 +1,9 @@
1
+ export declare function PrintplekVerwijderenDialog({ plek, onOpenChange, onKlaar, }: {
2
+ plek: {
3
+ id: string;
4
+ naam: string;
5
+ } | null;
6
+ onOpenChange: (v: boolean) => void;
7
+ onKlaar: () => void;
8
+ }): import("react").JSX.Element;
9
+ //# sourceMappingURL=PrintplekVerwijderenDialog.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PrintplekVerwijderenDialog.d.ts","sourceRoot":"","sources":["../../src/components/PrintplekVerwijderenDialog.tsx"],"names":[],"mappings":"AAkBA,wBAAgB,0BAA0B,CAAC,EACzC,IAAI,EACJ,YAAY,EACZ,OAAO,GACR,EAAE;IACD,IAAI,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IAC1C,YAAY,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IACnC,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB,+BAmEA"}
@@ -0,0 +1,34 @@
1
+ import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
2
+ // Bevestiging vóór het verwijderen van een hele printplek, met wat er verdwijnt.
3
+ import { useEffect, useState } from "react";
4
+ import { Button, Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, toast, } from "@flowselections/core";
5
+ import { printplekVerwijderImpact, verwijderPrintplek, } from "../lib/printplekken.js";
6
+ export function PrintplekVerwijderenDialog({ plek, onOpenChange, onKlaar, }) {
7
+ const [impact, setImpact] = useState(null);
8
+ const [bezig, setBezig] = useState(false);
9
+ useEffect(() => {
10
+ setImpact(null);
11
+ if (plek)
12
+ void printplekVerwijderImpact(plek.id).then(setImpact).catch(() => setImpact(null));
13
+ }, [plek]);
14
+ const bevestig = async () => {
15
+ if (!plek)
16
+ return;
17
+ setBezig(true);
18
+ try {
19
+ await verwijderPrintplek(plek.id);
20
+ toast.success(`Printplek ${plek.naam} verwijderd`);
21
+ onOpenChange(false);
22
+ onKlaar();
23
+ }
24
+ catch {
25
+ toast.error("Verwijderen mislukt");
26
+ }
27
+ finally {
28
+ setBezig(false);
29
+ }
30
+ };
31
+ const pcs = impact?.computers ?? [];
32
+ const kop = impact?.koppelingen ?? [];
33
+ return (_jsx(Dialog, { open: plek !== null, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { children: [_jsxs(DialogHeader, { children: [_jsxs(DialogTitle, { children: ["Printplek ", plek?.naam, " verwijderen?"] }), _jsx(DialogDescription, { children: "Dit kan niet ongedaan worden gemaakt." })] }), !impact ? (_jsx("p", { className: "text-muted-foreground text-sm", children: "Laden\u2026" })) : (_jsxs("ul", { className: "list-disc space-y-1 pl-5 text-sm", children: [_jsxs("li", { children: [pcs.length, " computer", pcs.length === 1 ? "" : "s", " losgekoppeld", pcs.length ? ` (${pcs.join(", ")})` : "", " \u2014 ze komen terug onder \"Computers zonder printplek\"."] }), _jsxs("li", { children: [impact.regels, " documentregel", impact.regels === 1 ? "" : "s", " van deze plek verwijderd \u2014 die documenten gebruiken voortaan de bedrijfsbrede standaard."] }), _jsxs("li", { children: [impact.printers, " printer", impact.printers === 1 ? "" : "s", " ontkoppeld (de printers zelf blijven bestaan)."] }), kop.length > 0 && (_jsxs("li", { className: "text-destructive", children: ["Gekoppeld aan ", kop.join(", "), ": die schermen weten daarna niet meer naar welke plek ze printen."] }))] })), _jsxs(DialogFooter, { children: [_jsx(Button, { variant: "outline", onClick: () => onOpenChange(false), children: "Annuleren" }), _jsx(Button, { variant: "destructive", disabled: !impact || bezig, onClick: () => void bevestig(), children: "Definitief verwijderen" })] })] }) }));
34
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"PrintplekkenPage.d.ts","sourceRoot":"","sources":["../../src/components/PrintplekkenPage.tsx"],"names":[],"mappings":"AAoCA,wBAAgB,gBAAgB,CAAC,EAAE,OAAe,EAAE,GAAE;IAAE,OAAO,CAAC,EAAE,OAAO,CAAA;CAAO,+BAuZ/E"}
1
+ {"version":3,"file":"PrintplekkenPage.d.ts","sourceRoot":"","sources":["../../src/components/PrintplekkenPage.tsx"],"names":[],"mappings":"AAsDA,wBAAgB,gBAAgB,CAAC,EAAE,OAAe,EAAE,GAAE;IAAE,OAAO,CAAC,EAAE,OAAO,CAAA;CAAO,+BA6d/E"}
@@ -5,16 +5,17 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
5
5
  // zodat instellen en monitoren op dezelfde plek gebeuren.
6
6
  // ============================================================================
7
7
  import { useMemo, useState } from "react";
8
- import { Badge, Button, Card, CardContent, CardDescription, CardHeader, CardTitle, Input, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@flowselections/core";
8
+ import { Badge, Button, Card, CardContent, CardDescription, CardHeader, CardTitle, Input, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, toast, } from "@flowselections/core";
9
9
  import { Link, useNavigate } from "@tanstack/react-router";
10
- import { ChevronRight, MapPin, Monitor, Plus, Printer, Search, Wand2 } from "lucide-react";
11
- import { koppelComputerAanPrintplek, usePrintplekken } from "../lib/printplekken.js";
10
+ import { ChevronRight, MapPin, Monitor, Plus, Printer, RotateCcw, Search, Trash2, Wand2, } from "lucide-react";
11
+ import { koppelComputerAanPrintplek, negeerComputer, useGenegeerdeComputers, usePrintplekken, zetComputerTerug, } from "../lib/printplekken.js";
12
12
  import { usePrinters } from "../lib/useDocumentData.js";
13
13
  import { useNetwerkPrinters } from "../lib/useNetwerkPrinters.js";
14
14
  import { useComputers } from "./ComputersLijst.js";
15
15
  import { ComputersLijst } from "./ComputersLijst.js";
16
16
  import { telPrintersPerComputer } from "../lib/printerTelling.js";
17
17
  import { PrintplekWizard } from "./PrintplekWizard.js";
18
+ import { PrintplekVerwijderenDialog } from "./PrintplekVerwijderenDialog.js";
18
19
  import { TestprintDialog } from "./TestprintDialog.js";
19
20
  import { PrintStatusBanner } from "./PrintStatusBanner.js";
20
21
  import { bijgewerktTekst, useAutoRefresh } from "../lib/useAutoRefresh.js";
@@ -24,11 +25,14 @@ export function PrintplekkenPage({ ingebed = false } = {}) {
24
25
  const { computers, loading: computersLaden, fout: computersFout, reload: herlaadComputers, } = useComputers();
25
26
  const { printers, reload: herlaadPrinters } = usePrinters();
26
27
  const { gevonden, zoek } = useNetwerkPrinters();
28
+ const { genegeerd, reload: herlaadGenegeerd } = useGenegeerdeComputers();
27
29
  const navigate = useNavigate();
30
+ const [teVerwijderen, setTeVerwijderen] = useState(null);
28
31
  const [zoekterm, setZoekterm] = useState("");
29
32
  const [wizardOpen, setWizardOpen] = useState(false);
30
33
  const [testprint, setTestprint] = useState(null);
31
34
  const [computerToevoegen, setComputerToevoegen] = useState(false);
35
+ const [toonGenegeerd, setToonGenegeerd] = useState(false);
32
36
  const bijgewerkt = useAutoRefresh(async () => {
33
37
  await Promise.all([reload(true), herlaadComputers(true), herlaadPrinters(true), zoek()]);
34
38
  }, 10000, computerToevoegen || wizardOpen);
@@ -97,14 +101,32 @@ export function PrintplekkenPage({ ingebed = false } = {}) {
97
101
  const Wrapper = ingebed ? "div" : "main";
98
102
  return (_jsxs(Wrapper, { className: ingebed ? "space-y-6" : "space-y-6 p-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: "Printplekken" }), _jsx("p", { className: "text-muted-foreground text-sm", children: "Een printplek is een fysieke plek in het bedrijf, bijvoorbeeld een pickstation. Alle computers en printers op die plek horen bij elkaar." })] })), _jsxs("div", { className: "ml-auto flex items-center gap-2", children: [_jsxs(Button, { variant: "outline", onClick: () => setComputerToevoegen(true), children: [_jsx(Plus, { className: "mr-2 h-4 w-4" }), " Computer aanmelden"] }), _jsxs(Button, { onClick: () => setWizardOpen(true), children: [_jsx(Wand2, { className: "mr-2 h-4 w-4" }), " Printplek instellen"] })] })] }), _jsx(PrintStatusBanner, { fout: computersFout, offlineWerkplekken: computers.filter((c) => !c.online).map((c) => c.naam), toonWerkplekLink: false }), fout ? (_jsx("div", { className: "border-destructive/40 bg-destructive/10 text-destructive rounded-xl border p-3 text-sm", children: fout })) : null, _jsxs("div", { className: "flex flex-wrap items-center gap-3 rounded-xl border p-3", children: [_jsxs("div", { className: "relative min-w-[220px] flex-1", children: [_jsx(Search, { className: "text-muted-foreground absolute top-2.5 left-3 h-4 w-4" }), _jsx(Input, { className: "pl-9", placeholder: "Zoek printplek of computer\u2026", value: zoekterm, onChange: (e) => setZoekterm(e.target.value) })] }), _jsxs("span", { className: "text-muted-foreground text-sm", children: [plekken.length, " plek", plekken.length === 1 ? "" : "ken", " \u00B7 ", computers.length, " computer", computers.length === 1 ? "" : "s", " (", computersOnline, " aan) \u00B7 ", totaalPrinters, " printer", totaalPrinters === 1 ? "" : "s"] }), _jsx("span", { className: "text-muted-foreground ml-auto text-xs", children: bijgewerktTekst(bijgewerkt) })] }), kaarten.length === 0 ? (_jsxs(Card, { children: [_jsxs(CardHeader, { children: [_jsx(CardTitle, { className: "text-base", children: plekken.length === 0 ? "Nog geen printplekken" : "Geen resultaten" }), _jsx(CardDescription, { children: plekken.length === 0
99
103
  ? 'Maak er één aan per plek waar geprint wordt, bijvoorbeeld "Pickstation 1".'
100
- : "Geen printplek of computer gevonden met deze zoekterm." })] }), _jsx(CardContent, { className: "flex flex-wrap gap-2", children: _jsxs(Button, { onClick: () => setWizardOpen(true), children: [_jsx(Wand2, { className: "mr-2 h-4 w-4" }), " Printplek instellen"] }) })] })) : (_jsx("div", { className: "grid gap-4 xl:grid-cols-2", children: kaarten.map((k) => (_jsxs("div", { className: "rounded-xl border p-4", children: [_jsxs("div", { className: "flex items-start justify-between gap-3", children: [_jsxs("div", { className: "min-w-0", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(MapPin, { className: "text-muted-foreground h-4 w-4" }), _jsx("span", { className: "truncate font-medium", children: k.plek.naam }), _jsx("span", { className: `h-2.5 w-2.5 shrink-0 rounded-full ${k.klaar ? "bg-emerald-500" : "bg-destructive"}`, "aria-label": k.klaar ? "Klaar om te printen" : "Nog niet compleet" })] }), k.plek.omschrijving ? (_jsx("p", { className: "text-muted-foreground mt-1 text-xs", children: k.plek.omschrijving })) : null] }), _jsxs("div", { className: "flex shrink-0 items-center gap-3", children: [_jsxs("button", { type: "button", className: "text-primary flex items-center text-xs", onClick: () => setTestprint({ id: k.plek.id, naam: k.plek.naam }), children: [_jsx(Printer, { className: "mr-1 h-3.5 w-3.5" }), " Testprint"] }), _jsxs(Link, { to: "/layouts-printers/printplekken/$printplekId", params: { printplekId: k.plek.id }, className: "text-primary flex items-center text-xs", children: ["Instellen ", _jsx(ChevronRight, { className: "ml-1 h-3.5 w-3.5" })] })] })] }), _jsx("div", { className: "mt-3 space-y-2", children: k.computerLijst.length === 0 ? (_jsxs("div", { className: "text-muted-foreground flex flex-wrap items-center gap-2 rounded-lg border border-dashed p-3 text-sm", children: ["Nog geen computer gekoppeld", nietGekoppeld.length > 0 ? (_jsxs(Select, { onValueChange: (naam) => void koppel(k.plek.id, naam), children: [_jsx(SelectTrigger, { className: "h-8 w-[190px]", children: _jsx(SelectValue, { placeholder: "Computer koppelen" }) }), _jsx(SelectContent, { children: nietGekoppeld.map((c) => (_jsx(SelectItem, { value: c.naam, children: c.naam }, c.id))) })] })) : (_jsx(Button, { variant: "ghost", size: "sm", onClick: () => setComputerToevoegen(true), children: "Computer aanmelden" }))] })) : (k.computerLijst.map((c) => (_jsxs("div", { className: "bg-muted/40 flex flex-wrap items-center gap-3 rounded-lg px-3 py-2 text-sm", children: [_jsx(Monitor, { className: "text-muted-foreground h-4 w-4 shrink-0" }), _jsx("span", { className: "min-w-0 flex-1 truncate font-medium", children: c.naam }), _jsx("span", { className: `rounded-full px-2 py-0.5 text-xs font-medium ${c.online ? "bg-primary/10 text-primary" : "bg-muted text-muted-foreground"}`, children: c.aangemeld ? (c.online ? "Aan" : "Uit") : "Niet aangemeld" }), _jsxs("span", { className: "text-muted-foreground text-xs", children: [c.ingesteld, " printer", c.ingesteld === 1 ? "" : "s", " \u00B7 ", c.gevonden, " gevonden"] }), _jsx(Button, { variant: "ghost", size: "sm", disabled: !c.id, onClick: () => naarComputer(c.id), children: "Printers toevoegen" })] }, c.koppelingId)))) }), _jsxs("div", { className: "text-muted-foreground mt-3 flex items-start gap-2 text-xs", children: [_jsx(Printer, { className: "mt-0.5 h-3.5 w-3.5 shrink-0" }), _jsx("span", { children: k.documentRegels.length === 0
104
+ : "Geen printplek of computer gevonden met deze zoekterm." })] }), _jsx(CardContent, { className: "flex flex-wrap gap-2", children: _jsxs(Button, { onClick: () => setWizardOpen(true), children: [_jsx(Wand2, { className: "mr-2 h-4 w-4" }), " Printplek instellen"] }) })] })) : (_jsx("div", { className: "grid gap-4 xl:grid-cols-2", children: kaarten.map((k) => (_jsxs("div", { className: "rounded-xl border p-4", children: [_jsxs("div", { className: "flex items-start justify-between gap-3", children: [_jsxs("div", { className: "min-w-0", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(MapPin, { className: "text-muted-foreground h-4 w-4" }), _jsx("span", { className: "truncate font-medium", children: k.plek.naam }), _jsx("span", { className: `h-2.5 w-2.5 shrink-0 rounded-full ${k.klaar ? "bg-emerald-500" : "bg-destructive"}`, "aria-label": k.klaar ? "Klaar om te printen" : "Nog niet compleet" })] }), k.plek.omschrijving ? (_jsx("p", { className: "text-muted-foreground mt-1 text-xs", children: k.plek.omschrijving })) : null] }), _jsxs("div", { className: "flex shrink-0 items-center gap-3", children: [_jsxs("button", { type: "button", className: "text-primary flex items-center text-xs", onClick: () => setTestprint({ id: k.plek.id, naam: k.plek.naam }), children: [_jsx(Printer, { className: "mr-1 h-3.5 w-3.5" }), " Testprint"] }), _jsxs("button", { type: "button", className: "text-destructive flex items-center text-xs", onClick: () => setTeVerwijderen({ id: k.plek.id, naam: k.plek.naam }), children: [_jsx(Trash2, { className: "mr-1 h-3.5 w-3.5" }), " Verwijderen"] }), _jsxs(Link, { to: "/layouts-printers/printplekken/$printplekId", params: { printplekId: k.plek.id }, className: "text-primary flex items-center text-xs", children: ["Instellen ", _jsx(ChevronRight, { className: "ml-1 h-3.5 w-3.5" })] })] })] }), _jsx("div", { className: "mt-3 space-y-2", children: k.computerLijst.length === 0 ? (_jsxs("div", { className: "text-muted-foreground flex flex-wrap items-center gap-2 rounded-lg border border-dashed p-3 text-sm", children: ["Nog geen computer gekoppeld", nietGekoppeld.length > 0 ? (_jsxs(Select, { onValueChange: (naam) => void koppel(k.plek.id, naam), children: [_jsx(SelectTrigger, { className: "h-8 w-[190px]", children: _jsx(SelectValue, { placeholder: "Computer koppelen" }) }), _jsx(SelectContent, { children: nietGekoppeld.map((c) => (_jsx(SelectItem, { value: c.naam, children: c.naam }, c.id))) })] })) : (_jsx(Button, { variant: "ghost", size: "sm", onClick: () => setComputerToevoegen(true), children: "Computer aanmelden" }))] })) : (k.computerLijst.map((c) => (_jsxs("div", { className: "bg-muted/40 flex flex-wrap items-center gap-3 rounded-lg px-3 py-2 text-sm", children: [_jsx(Monitor, { className: "text-muted-foreground h-4 w-4 shrink-0" }), _jsx("span", { className: "min-w-0 flex-1 truncate font-medium", children: c.naam }), _jsx("span", { className: `rounded-full px-2 py-0.5 text-xs font-medium ${c.online ? "bg-primary/10 text-primary" : "bg-muted text-muted-foreground"}`, children: c.aangemeld ? (c.online ? "Aan" : "Uit") : "Niet aangemeld" }), _jsxs("span", { className: "text-muted-foreground text-xs", children: [c.ingesteld, " printer", c.ingesteld === 1 ? "" : "s", " \u00B7 ", c.gevonden, " gevonden"] }), _jsx(Button, { variant: "ghost", size: "sm", disabled: !c.id, onClick: () => naarComputer(c.id), children: "Printers toevoegen" })] }, c.koppelingId)))) }), _jsxs("div", { className: "text-muted-foreground mt-3 flex items-start gap-2 text-xs", children: [_jsx(Printer, { className: "mt-0.5 h-3.5 w-3.5 shrink-0" }), _jsx("span", { children: k.documentRegels.length === 0
101
105
  ? "Nog geen document aan een printer gekoppeld"
102
106
  : k.documentRegels
103
107
  .map((r) => `${documentTypeLabel(r.documenttype)} → ${printerNaam.get(r.printer_id ?? "") ?? "onbekende printer"}`)
104
108
  .join(" · ") })] }), k.bronnen.length > 0 ? (_jsx("div", { className: "mt-3 flex flex-wrap gap-1", children: Array.from(new Set(k.bronnen.map((b) => b.bron))).map((bron) => (_jsxs(Badge, { variant: "secondary", children: [k.bronnen.filter((b) => b.bron === bron).length, "\u00D7 ", bron] }, bron))) })) : null] }, k.plek.id))) })), nietGekoppeld.length > 0 ? (_jsxs(Card, { children: [_jsxs(CardHeader, { children: [_jsxs(CardTitle, { className: "text-base", children: ["Computers zonder printplek (", nietGekoppeld.length, ")"] }), _jsx(CardDescription, { children: "Deze computers zijn aangemeld maar horen nog nergens bij. Kies hieronder waar ze staan." })] }), _jsx(CardContent, { className: "space-y-2", children: nietGekoppeld.map((c) => {
105
109
  const info = perComputer.get(c.naam);
106
- return (_jsxs("div", { className: "flex flex-wrap items-center gap-3 rounded-lg border px-3 py-2 text-sm", children: [_jsx(Monitor, { className: "text-muted-foreground h-4 w-4 shrink-0" }), _jsx("span", { className: "min-w-0 flex-1 truncate font-medium", children: c.naam }), _jsx("span", { className: `rounded-full px-2 py-0.5 text-xs font-medium ${c.online ? "bg-primary/10 text-primary" : "bg-muted text-muted-foreground"}`, children: c.online ? "Aan" : "Uit" }), _jsxs("span", { className: "text-muted-foreground text-xs", children: [info?.gevonden ?? c.aantalGevonden, " printers gevonden"] }), plekken.length > 0 ? (_jsxs(Select, { onValueChange: (plekId) => void koppel(plekId, c.naam), children: [_jsx(SelectTrigger, { className: "h-8 w-[170px]", children: _jsx(SelectValue, { placeholder: "Plek kiezen" }) }), _jsx(SelectContent, { children: plekken.map((p) => (_jsx(SelectItem, { value: p.id, children: p.naam }, p.id))) })] })) : (_jsx(Button, { variant: "ghost", size: "sm", onClick: () => setWizardOpen(true), children: "Printplek maken" }))] }, c.id));
107
- }) })] })) : null, !computersLaden && computers.length === 0 && plekken.length === 0 ? (_jsx(Card, { children: _jsxs(CardContent, { className: "space-y-3 py-6 text-sm", children: [_jsx("p", { className: "font-medium", children: "Nog geen computer aangemeld" }), _jsx("p", { className: "text-muted-foreground", children: "Installeer het printprogramma op de computer waar de printers aan hangen. Zodra die zich meldt, verschijnt hij hier." }), _jsxs(Button, { onClick: () => setComputerToevoegen(true), children: [_jsx(Plus, { className: "mr-2 h-4 w-4" }), " Computer aanmelden"] })] }) })) : null, _jsx(PrintplekWizard, { open: wizardOpen, onOpenChange: setWizardOpen, onKlaar: () => vernieuwAlles() }), _jsx(TestprintDialog, { open: testprint !== null, onOpenChange: (v) => !v && setTestprint(null), printplekId: testprint?.id ?? null, printplekNaam: testprint?.naam ?? null }), _jsx(ComputersLijst, { computers: computers, loading: false, fout: null, onVernieuw: () => {
110
+ return (_jsxs("div", { className: "flex flex-wrap items-center gap-3 rounded-lg border px-3 py-2 text-sm", children: [_jsx(Monitor, { className: "text-muted-foreground h-4 w-4 shrink-0" }), _jsx("span", { className: "min-w-0 flex-1 truncate font-medium", children: c.naam }), _jsx("span", { className: `rounded-full px-2 py-0.5 text-xs font-medium ${c.online ? "bg-primary/10 text-primary" : "bg-muted text-muted-foreground"}`, children: c.online ? "Aan" : "Uit" }), _jsxs("span", { className: "text-muted-foreground text-xs", children: [info?.gevonden ?? c.aantalGevonden, " printers gevonden"] }), plekken.length > 0 ? (_jsxs(Select, { onValueChange: (plekId) => void koppel(plekId, c.naam), children: [_jsx(SelectTrigger, { className: "h-8 w-[170px]", children: _jsx(SelectValue, { placeholder: "Plek kiezen" }) }), _jsx(SelectContent, { children: plekken.map((p) => (_jsx(SelectItem, { value: p.id, children: p.naam }, p.id))) })] })) : (_jsx(Button, { variant: "ghost", size: "sm", onClick: () => setWizardOpen(true), children: "Printplek maken" })), _jsxs(Button, { variant: "ghost", size: "sm", className: "text-destructive", onClick: async () => {
111
+ try {
112
+ await negeerComputer(c.naam, null);
113
+ await Promise.all([herlaadComputers(true), herlaadGenegeerd()]);
114
+ toast.success(`${c.naam} verwijderd`);
115
+ }
116
+ catch {
117
+ toast.error("Verwijderen mislukt");
118
+ }
119
+ }, children: [_jsx(Trash2, { className: "mr-1 h-3.5 w-3.5" }), " Verwijderen"] })] }, c.id));
120
+ }) })] })) : null, genegeerd.length > 0 ? (_jsxs("div", { className: "space-y-2", children: [_jsxs(Button, { variant: "ghost", size: "sm", onClick: () => setToonGenegeerd((v) => !v), children: [toonGenegeerd ? "Verberg" : "Toon", " genegeerde computers (", genegeerd.length, ")"] }), toonGenegeerd ? (_jsx(Card, { children: _jsx(CardContent, { className: "space-y-2 py-4", children: genegeerd.map((g) => (_jsxs("div", { className: "flex flex-wrap items-center gap-3 rounded-lg border px-3 py-2 text-sm", children: [_jsx(Monitor, { className: "text-muted-foreground h-4 w-4 shrink-0" }), _jsx("span", { className: "min-w-0 flex-1 truncate font-medium", children: g.computer_naam }), _jsxs("span", { className: "text-muted-foreground text-xs", children: ["verwijderd op ", new Date(g.genegeerd_op).toLocaleString("nl-NL")] }), _jsxs(Button, { variant: "outline", size: "sm", onClick: async () => {
121
+ try {
122
+ await zetComputerTerug(g.computer_naam);
123
+ await Promise.all([herlaadComputers(true), herlaadGenegeerd()]);
124
+ toast.success(`${g.computer_naam} teruggezet`);
125
+ }
126
+ catch {
127
+ toast.error("Terugzetten mislukt");
128
+ }
129
+ }, children: [_jsx(RotateCcw, { className: "mr-1 h-3.5 w-3.5" }), " Terugzetten"] })] }, g.id))) }) })) : null] })) : null, !computersLaden && computers.length === 0 && plekken.length === 0 ? (_jsx(Card, { children: _jsxs(CardContent, { className: "space-y-3 py-6 text-sm", children: [_jsx("p", { className: "font-medium", children: "Nog geen computer aangemeld" }), _jsx("p", { className: "text-muted-foreground", children: "Installeer het printprogramma op de computer waar de printers aan hangen. Zodra die zich meldt, verschijnt hij hier." }), _jsxs(Button, { onClick: () => setComputerToevoegen(true), children: [_jsx(Plus, { className: "mr-2 h-4 w-4" }), " Computer aanmelden"] })] }) })) : null, _jsx(PrintplekVerwijderenDialog, { plek: teVerwijderen, onOpenChange: (v) => !v && setTeVerwijderen(null), onKlaar: () => vernieuwAlles() }), _jsx(PrintplekWizard, { open: wizardOpen, onOpenChange: setWizardOpen, onKlaar: () => vernieuwAlles() }), _jsx(TestprintDialog, { open: testprint !== null, onOpenChange: (v) => !v && setTestprint(null), printplekId: testprint?.id ?? null, printplekNaam: testprint?.naam ?? null }), _jsx(ComputersLijst, { computers: computers, loading: false, fout: null, onVernieuw: () => {
108
130
  void herlaadComputers();
109
131
  void zoek();
110
132
  }, onPrintersToevoegen: (naam) => {
@@ -1 +1 @@
1
- {"version":3,"file":"WerkplekConfiguratiePage.d.ts","sourceRoot":"","sources":["../../src/components/WerkplekConfiguratiePage.tsx"],"names":[],"mappings":"AAwCA,wBAAgB,wBAAwB,gCA2TvC"}
1
+ {"version":3,"file":"WerkplekConfiguratiePage.d.ts","sourceRoot":"","sources":["../../src/components/WerkplekConfiguratiePage.tsx"],"names":[],"mappings":"AA2CA,wBAAgB,wBAAwB,gCAkXvC"}