@flowselections/layout-maker 1.13.2 → 1.20.0
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.
- package/dist-lib/components/AfdrukKnop.d.ts +5 -0
- package/dist-lib/components/AfdrukKnop.d.ts.map +1 -1
- package/dist-lib/components/AfdrukKnop.js +5 -0
- package/dist-lib/components/DocumentRouteringPage.d.ts +3 -1
- package/dist-lib/components/DocumentRouteringPage.d.ts.map +1 -1
- package/dist-lib/components/DocumentRouteringPage.js +66 -64
- package/dist-lib/components/DocumentenPage.d.ts +4 -0
- package/dist-lib/components/DocumentenPage.d.ts.map +1 -0
- package/dist-lib/components/DocumentenPage.js +16 -0
- package/dist-lib/components/LayoutsPage.d.ts +3 -1
- package/dist-lib/components/LayoutsPage.d.ts.map +1 -1
- package/dist-lib/components/LayoutsPage.js +3 -2
- package/dist-lib/components/LayoutsPrintersTabPage.d.ts +2 -0
- package/dist-lib/components/LayoutsPrintersTabPage.d.ts.map +1 -0
- package/dist-lib/components/LayoutsPrintersTabPage.js +32 -0
- package/dist-lib/components/LogosPage.d.ts +3 -1
- package/dist-lib/components/LogosPage.d.ts.map +1 -1
- package/dist-lib/components/LogosPage.js +3 -2
- package/dist-lib/components/ModuleTabs.d.ts +19 -0
- package/dist-lib/components/ModuleTabs.d.ts.map +1 -0
- package/dist-lib/components/ModuleTabs.js +17 -0
- package/dist-lib/components/PakbonBuilderPage.d.ts.map +1 -1
- package/dist-lib/components/PakbonBuilderPage.js +30 -4
- package/dist-lib/components/PrintQueuePage.d.ts.map +1 -1
- package/dist-lib/components/PrintQueuePage.js +15 -2
- package/dist-lib/components/PrintenPage.d.ts +4 -0
- package/dist-lib/components/PrintenPage.d.ts.map +1 -0
- package/dist-lib/components/PrintenPage.js +21 -0
- package/dist-lib/components/PrinterSetupWizard.js +3 -3
- package/dist-lib/components/PrintersPage.d.ts +3 -1
- package/dist-lib/components/PrintersPage.d.ts.map +1 -1
- package/dist-lib/components/PrintersPage.js +20 -38
- package/dist-lib/components/PrintersRedirectPage.d.ts +2 -0
- package/dist-lib/components/PrintersRedirectPage.d.ts.map +1 -0
- package/dist-lib/components/PrintersRedirectPage.js +14 -0
- package/dist-lib/components/PrintplekDetailPage.d.ts +2 -0
- package/dist-lib/components/PrintplekDetailPage.d.ts.map +1 -0
- package/dist-lib/components/PrintplekDetailPage.js +93 -0
- package/dist-lib/components/PrintplekWizard.d.ts +8 -0
- package/dist-lib/components/PrintplekWizard.d.ts.map +1 -0
- package/dist-lib/components/PrintplekWizard.js +194 -0
- package/dist-lib/components/PrintplekkenPage.d.ts +4 -0
- package/dist-lib/components/PrintplekkenPage.d.ts.map +1 -0
- package/dist-lib/components/PrintplekkenPage.js +110 -0
- package/dist-lib/index.d.ts +15 -1
- package/dist-lib/index.d.ts.map +1 -1
- package/dist-lib/index.js +16 -7
- package/dist-lib/integrations/supabase/auth-middleware.d.ts +371 -0
- package/dist-lib/integrations/supabase/auth-middleware.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/client.server.d.ts +371 -0
- package/dist-lib/integrations/supabase/client.server.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/types.d.ts +378 -0
- package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
- package/dist-lib/lib/aiLayout.functions.d.ts +371 -0
- package/dist-lib/lib/aiLayout.functions.d.ts.map +1 -1
- package/dist-lib/lib/browserPrint.d.ts +4 -0
- package/dist-lib/lib/browserPrint.d.ts.map +1 -1
- package/dist-lib/lib/browserPrint.js +4 -0
- package/dist-lib/lib/pakbonHtml.d.ts +5 -2
- package/dist-lib/lib/pakbonHtml.d.ts.map +1 -1
- package/dist-lib/lib/pakbonHtml.js +5 -2
- package/dist-lib/lib/printApi.d.ts +23 -7
- package/dist-lib/lib/printApi.d.ts.map +1 -1
- package/dist-lib/lib/printApi.js +51 -15
- package/dist-lib/lib/printQueue.d.ts +28 -0
- package/dist-lib/lib/printQueue.d.ts.map +1 -0
- package/dist-lib/lib/printQueue.js +70 -0
- package/dist-lib/lib/printQueue.server.d.ts.map +1 -1
- package/dist-lib/lib/printQueue.server.js +141 -18
- package/dist-lib/lib/printService.functions.d.ts +1484 -0
- package/dist-lib/lib/printService.functions.d.ts.map +1 -1
- package/dist-lib/lib/printplekWizard.d.ts +57 -0
- package/dist-lib/lib/printplekWizard.d.ts.map +1 -0
- package/dist-lib/lib/printplekWizard.js +87 -0
- package/dist-lib/lib/printplekken.d.ts +90 -0
- package/dist-lib/lib/printplekken.d.ts.map +1 -0
- package/dist-lib/lib/printplekken.js +244 -0
- package/dist-lib/lib/testpagina.d.ts.map +1 -1
- package/dist-lib/lib/testpagina.js +5 -5
- package/dist-lib/lib/useDocumentData.d.ts +6 -0
- package/dist-lib/lib/useDocumentData.d.ts.map +1 -1
- package/dist-lib/lib/useDocumentData.js +31 -0
- package/package.json +14 -14
|
@@ -11,5 +11,10 @@ export interface AfdrukKnopProps {
|
|
|
11
11
|
layoutId?: string | null;
|
|
12
12
|
kopieen?: number;
|
|
13
13
|
}
|
|
14
|
+
/**
|
|
15
|
+
* @deprecated Verouderd — deze knop gebruikt het browser-printvenster als
|
|
16
|
+
* terugval. Gebruik `printHtml()` / `printPdf()` uit `printApi` zodat alles
|
|
17
|
+
* via PrintNode loopt.
|
|
18
|
+
*/
|
|
14
19
|
export declare function AfdrukKnop({ documenttype, formaatCode, orientatie, getHtml, css, documentnaam, label, layoutId, kopieen, }: AfdrukKnopProps): import("react").JSX.Element;
|
|
15
20
|
//# sourceMappingURL=AfdrukKnop.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AfdrukKnop.d.ts","sourceRoot":"","sources":["../../src/components/AfdrukKnop.tsx"],"names":[],"mappings":"AASA,OAAO,EAAU,KAAK,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAG9D,MAAM,WAAW,eAAe;IAC9B,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,UAAU,EAAE,UAAU,CAAC;IACvB,uEAAuE;IACvE,OAAO,EAAE,MAAM,MAAM,CAAC;IACtB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,wBAAgB,UAAU,CAAC,EACzB,YAAY,EACZ,WAAW,EACX,UAAU,EACV,OAAO,EACP,GAAG,EACH,YAAY,EACZ,KAAmB,EACnB,QAAQ,EACR,OAAO,GACR,EAAE,eAAe,+BAwDjB"}
|
|
1
|
+
{"version":3,"file":"AfdrukKnop.d.ts","sourceRoot":"","sources":["../../src/components/AfdrukKnop.tsx"],"names":[],"mappings":"AASA,OAAO,EAAU,KAAK,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAG9D,MAAM,WAAW,eAAe;IAC9B,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,UAAU,EAAE,UAAU,CAAC;IACvB,uEAAuE;IACvE,OAAO,EAAE,MAAM,MAAM,CAAC;IACtB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,EACzB,YAAY,EACZ,WAAW,EACX,UAAU,EACV,OAAO,EACP,GAAG,EACH,YAAY,EACZ,KAAmB,EACnB,QAAQ,EACR,OAAO,GACR,EAAE,eAAe,+BAwDjB"}
|
|
@@ -9,6 +9,11 @@ import { Button, toast } from "@flowselections/core";
|
|
|
9
9
|
import { Loader2, Printer } from "lucide-react";
|
|
10
10
|
import { drukAf } from "../lib/browserPrint.js";
|
|
11
11
|
import { printDirect } from "../lib/directPrint.js";
|
|
12
|
+
/**
|
|
13
|
+
* @deprecated Verouderd — deze knop gebruikt het browser-printvenster als
|
|
14
|
+
* terugval. Gebruik `printHtml()` / `printPdf()` uit `printApi` zodat alles
|
|
15
|
+
* via PrintNode loopt.
|
|
16
|
+
*/
|
|
12
17
|
export function AfdrukKnop({ documenttype, formaatCode, orientatie, getHtml, css, documentnaam, label = "Afdrukken", layoutId, kopieen, }) {
|
|
13
18
|
const [bezig, setBezig] = useState(false);
|
|
14
19
|
const afdrukken = async () => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DocumentRouteringPage.d.ts","sourceRoot":"","sources":["../../src/components/DocumentRouteringPage.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"DocumentRouteringPage.d.ts","sourceRoot":"","sources":["../../src/components/DocumentRouteringPage.tsx"],"names":[],"mappings":"AAsCA,wBAAgB,qBAAqB,CAAC,EAAE,OAAe,EAAE,GAAE;IAAE,OAAO,CAAC,EAAE,OAAO,CAAA;CAAO,+BAmVpF"}
|
|
@@ -1,38 +1,41 @@
|
|
|
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/DocumentRouteringPage.tsx — overzicht: welke documentsoort
|
|
4
|
-
// gaat
|
|
4
|
+
// gaat standaard naar welke printer, en welk document daarvoor wordt gebruikt.
|
|
5
|
+
// Afwijkingen per plek staan bij de printplek zelf.
|
|
5
6
|
// ============================================================================
|
|
6
7
|
import { useMemo, useState } from "react";
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import { AlertTriangle, Monitor, Plus, Printer, Trash2 } from "lucide-react";
|
|
8
|
+
import { Badge, Card, CardContent, Input, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, Separator, toast, } from "@flowselections/core";
|
|
9
|
+
import { AlertTriangle, ChevronDown, ChevronRight } from "lucide-react";
|
|
10
10
|
import { DOCUMENT_TYPES, documentTypeLabel, isFloridayLayout } from "../lib/documentTypes.js";
|
|
11
11
|
import { documentProfiel } from "../lib/documentProfiles.js";
|
|
12
12
|
import { usePrinters } from "../lib/useDocumentData.js";
|
|
13
13
|
import { useAlleFormaatPrinters, zetFormaatPrinter } from "../lib/formaatPrinters.js";
|
|
14
|
-
import { BRONNEN, HERKOMSTEN, useBronregels, zetBronregel
|
|
14
|
+
import { BRONNEN, HERKOMSTEN, useBronregels, zetBronregel } from "../lib/documentBron.js";
|
|
15
15
|
import { useLayouts } from "../lib/useDocumentData.js";
|
|
16
|
-
import { useWerkplekPrinters, verwijderWerkplekPrinter, zetWerkplekPrinter, } from "../lib/documentRoutering.js";
|
|
17
|
-
import { useComputers } from "./ComputersLijst.js";
|
|
18
16
|
const GEEN = "geen";
|
|
19
|
-
|
|
17
|
+
/**
|
|
18
|
+
* Documentsoorten die de Logistiek Engine naar de wachtrij stuurt. Die kiest
|
|
19
|
+
* zelf geen printer meer, dus zonder standaardregel blijft zo'n opdracht
|
|
20
|
+
* liggen. Daarom staan ze altijd bovenaan met een waarschuwing.
|
|
21
|
+
*/
|
|
22
|
+
const ENGINE_TYPES = ["pakbon", "sticker", "transportdocument", "productiebon"];
|
|
23
|
+
export function DocumentRouteringPage({ ingebed = false } = {}) {
|
|
20
24
|
const { printers } = usePrinters();
|
|
21
|
-
const { computers } = useComputers();
|
|
22
25
|
const { koppelingen: standaard, reload: herlaadStandaard } = useAlleFormaatPrinters();
|
|
23
|
-
const { koppelingen: werkplekken, reload: herlaadWerkplekken } = useWerkplekPrinters();
|
|
24
26
|
const { regels, reload: herlaadRegels } = useBronregels();
|
|
25
27
|
const { layouts } = useLayouts();
|
|
26
28
|
const [zoek, setZoek] = useState("");
|
|
27
|
-
const [
|
|
28
|
-
const [
|
|
29
|
-
const [
|
|
29
|
+
const [gekozen, setGekozen] = useState("pakbon");
|
|
30
|
+
const [toonRest, setToonRest] = useState(false);
|
|
31
|
+
const [toonUitleg, setToonUitleg] = useState(false);
|
|
32
|
+
const [toonGeavanceerd, setToonGeavanceerd] = useState(false);
|
|
30
33
|
const printerNaam = (id) => printers.find((p) => p.id === id)?.naam ?? "Onbekende printer";
|
|
31
34
|
const standaardFormaat = (type) => documentProfiel(type).formaten[0]?.code ?? "A4";
|
|
35
|
+
const actieveLayouts = (type) => layouts.filter((l) => l.documenttype === type && l.actief);
|
|
32
36
|
/** Formaat zoals de gebruiker het ziet: dat van de actieve layouts zelf. */
|
|
33
37
|
const weergaveFormaat = (type) => {
|
|
34
|
-
const eigen = Array.from(new Set(
|
|
35
|
-
.filter((l) => l.documenttype === type && l.actief)
|
|
38
|
+
const eigen = Array.from(new Set(actieveLayouts(type)
|
|
36
39
|
.map((l) => l.papierformaat)
|
|
37
40
|
.filter((f) => !!f)));
|
|
38
41
|
if (eigen.length === 1)
|
|
@@ -43,11 +46,26 @@ export function DocumentRouteringPage() {
|
|
|
43
46
|
};
|
|
44
47
|
const standaardVoor = (type) => standaard.find((k) => k.documenttype === type && k.formaat_code === standaardFormaat(type))
|
|
45
48
|
?.printer_id ?? null;
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
49
|
+
const bronVan = (type, herkomst) => regels.find((r) => r.documenttype === type && r.herkomst === herkomst);
|
|
50
|
+
/** In gebruik = er is een actieve layout of er is iets ingesteld. */
|
|
51
|
+
const inGebruik = (type) => actieveLayouts(type).length > 0 ||
|
|
52
|
+
!!standaardVoor(type) ||
|
|
53
|
+
regels.some((r) => r.documenttype === type && r.bron !== "auto");
|
|
54
|
+
const term = zoek.trim().toLowerCase();
|
|
55
|
+
const gefilterd = useMemo(() => DOCUMENT_TYPES.filter((d) => (term ? d.label.toLowerCase().includes(term) : true)), [term]);
|
|
56
|
+
const engine = gefilterd.filter((d) => ENGINE_TYPES.includes(d.value));
|
|
57
|
+
const overig = gefilterd.filter((d) => !ENGINE_TYPES.includes(d.value));
|
|
58
|
+
const actief = overig.filter((d) => inGebruik(d.value));
|
|
59
|
+
const rest = overig.filter((d) => !inGebruik(d.value));
|
|
60
|
+
const aantalIngesteld = DOCUMENT_TYPES.filter((d) => !!standaardVoor(d.value)).length;
|
|
61
|
+
const huidig = gekozen;
|
|
62
|
+
const std = standaardVoor(huidig);
|
|
63
|
+
const bronSamenvatting = HERKOMSTEN.map((h) => {
|
|
64
|
+
const r = bronVan(huidig, h.value);
|
|
65
|
+
if (!r || r.bron === "auto")
|
|
66
|
+
return null;
|
|
67
|
+
return `${h.label}: ${BRONNEN.find((b) => b.value === r.bron)?.label ?? r.bron}`;
|
|
68
|
+
}).filter((s) => !!s);
|
|
51
69
|
const kiesStandaard = async (type, printerId) => {
|
|
52
70
|
try {
|
|
53
71
|
await zetFormaatPrinter(type, standaardFormaat(type), printerId === GEEN ? null : printerId);
|
|
@@ -58,24 +76,6 @@ export function DocumentRouteringPage() {
|
|
|
58
76
|
toast.error("Opslaan mislukt");
|
|
59
77
|
}
|
|
60
78
|
};
|
|
61
|
-
const voegWerkplekToe = async (type) => {
|
|
62
|
-
if (!nieuweWerkplek.trim()) {
|
|
63
|
-
toast.error("Kies eerst een werkplek");
|
|
64
|
-
return;
|
|
65
|
-
}
|
|
66
|
-
try {
|
|
67
|
-
await zetWerkplekPrinter(type, nieuweWerkplek, nieuwePrinter === GEEN ? null : nieuwePrinter, standaardFormaat(type));
|
|
68
|
-
await herlaadWerkplekken();
|
|
69
|
-
setNieuw(null);
|
|
70
|
-
setNieuweWerkplek("");
|
|
71
|
-
setNieuwePrinter(GEEN);
|
|
72
|
-
toast.success("Werkplek gekoppeld");
|
|
73
|
-
}
|
|
74
|
-
catch {
|
|
75
|
-
toast.error("Opslaan mislukt");
|
|
76
|
-
}
|
|
77
|
-
};
|
|
78
|
-
const bronVan = (type, herkomst) => regels.find((r) => r.documenttype === type && r.herkomst === herkomst);
|
|
79
79
|
const kiesBron = async (type, herkomst, bron, layoutId) => {
|
|
80
80
|
try {
|
|
81
81
|
await zetBronregel(type, herkomst, bron, layoutId ?? null);
|
|
@@ -86,30 +86,32 @@ export function DocumentRouteringPage() {
|
|
|
86
86
|
toast.error("Opslaan mislukt");
|
|
87
87
|
}
|
|
88
88
|
};
|
|
89
|
-
const
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
89
|
+
const kies = (type) => {
|
|
90
|
+
setGekozen(type);
|
|
91
|
+
setToonGeavanceerd(false);
|
|
92
|
+
};
|
|
93
|
+
const LijstItem = ({ type, label }) => {
|
|
94
|
+
const printerId = standaardVoor(type);
|
|
95
|
+
const gekozenItem = huidig === type;
|
|
96
|
+
const engineType = ENGINE_TYPES.includes(type);
|
|
97
|
+
const geenLayout = actieveLayouts(type).length === 0;
|
|
98
|
+
const waarschuw = engineType
|
|
99
|
+
? !printerId || geenLayout
|
|
100
|
+
: !printerId && actieveLayouts(type).length > 0;
|
|
101
|
+
const melding = !printerId
|
|
102
|
+
? "Geen standaardprinter"
|
|
103
|
+
: geenLayout && engineType
|
|
104
|
+
? "Geen actieve layout"
|
|
105
|
+
: printerNaam(printerId);
|
|
106
|
+
return (_jsxs("button", { type: "button", onClick: () => kies(type), className: `w-full rounded-md px-3 py-2 text-left transition-colors ${gekozenItem ? "bg-primary/10 text-primary" : "hover:bg-muted"}`, children: [_jsxs("span", { className: "flex items-center gap-2", children: [_jsx("span", { className: "min-w-0 flex-1 truncate text-sm font-medium", children: label }), waarschuw && _jsx(AlertTriangle, { className: "text-destructive h-3.5 w-3.5 shrink-0" })] }), _jsx("span", { className: `block truncate text-xs ${waarschuw ? "text-destructive" : "text-muted-foreground"}`, children: melding })] }));
|
|
98
107
|
};
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
}
|
|
108
|
-
const regel = bronVan(d.value, h.value);
|
|
109
|
-
const huidig = regel?.bron ?? "auto";
|
|
110
|
-
return (_jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsx("span", { className: "w-32 shrink-0 text-sm", children: h.label }), _jsxs(Select, { value: huidig, onValueChange: (v) => void kiesBron(d.value, h.value, v, regel?.layout_id ?? null), children: [_jsx(SelectTrigger, { className: "w-52", children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: BRONNEN.map((b) => (_jsx(SelectItem, { value: b.value, children: b.label }, b.value))) })] }), huidig === "eigen_layout" && (_jsxs(Select, { value: regel?.layout_id ?? GEEN, onValueChange: (v) => void kiesBron(d.value, h.value, "eigen_layout", v === GEEN ? null : v), children: [_jsx(SelectTrigger, { className: "w-52", children: _jsx(SelectValue, { placeholder: "Standaardontwerp" }) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: GEEN, children: "Standaardontwerp" }), layouts
|
|
111
|
-
.filter((l) => l.documenttype === d.value)
|
|
112
|
-
.map((l) => (_jsxs(SelectItem, { value: l.id, children: [l.naam, isFloridayLayout(l) ? " (uit Floriday)" : ""] }, l.id)))] })] }))] }, h.value));
|
|
113
|
-
}) })] })] }, d.value));
|
|
114
|
-
}) })] }));
|
|
108
|
+
const Wrapper = ingebed ? "div" : "main";
|
|
109
|
+
return (_jsxs(Wrapper, { className: ingebed ? "space-y-5" : "space-y-5 p-6", children: [_jsxs("header", { className: "flex flex-wrap items-end justify-between gap-4", children: [_jsxs("p", { className: "text-muted-foreground text-sm", children: [aantalIngesteld, " van ", DOCUMENT_TYPES.length, " documentsoorten hebben een standaardprinter. Deze geldt als er geen printplek is meegegeven."] }), _jsx(Input, { className: "w-full sm:w-56", placeholder: "Zoek documentsoort", value: zoek, onChange: (e) => setZoek(e.target.value) })] }), _jsxs("div", { children: [_jsxs("button", { type: "button", onClick: () => setToonUitleg((v) => !v), className: "text-muted-foreground hover:text-foreground flex items-center gap-1 text-sm", children: [toonUitleg ? (_jsx(ChevronDown, { className: "h-4 w-4" })) : (_jsx(ChevronRight, { className: "h-4 w-4" })), "Hoe wordt de printer gekozen?"] }), toonUitleg && (_jsx(Card, { className: "mt-2", children: _jsx(CardContent, { className: "text-muted-foreground py-3 text-sm", children: "1. de printer die op de opdracht zelf staat \u00B7 2. de documentregel van de printplek (bijv. het pickstation) \u00B7 3. de printer die aan het ontwerp hangt \u00B7 4. de standaardprinter hieronder \u00B7 anders blijft de opdracht liggen met een melding welke instelling ontbreekt." }) }))] }), _jsxs("div", { className: "grid gap-4 lg:grid-cols-[280px_minmax(0,1fr)]", children: [_jsx(Card, { className: "min-w-0 self-start lg:sticky lg:top-4", children: _jsxs(CardContent, { className: "space-y-1 py-3", children: [engine.length > 0 && (_jsxs(_Fragment, { children: [_jsx("p", { className: "text-muted-foreground px-3 pb-1 text-xs uppercase", children: "Logistiek (pickstations)" }), engine.map((d) => (_jsx(LijstItem, { type: d.value, label: d.label }, d.value))), _jsx(Separator, { className: "my-2" })] })), _jsx("p", { className: "text-muted-foreground px-3 pb-1 text-xs uppercase", children: "In gebruik" }), actief.length === 0 && (_jsx("p", { className: "text-muted-foreground px-3 pb-2 text-sm", children: "Nog niets in gebruik." })), actief.map((d) => (_jsx(LijstItem, { type: d.value, label: d.label }, d.value))), rest.length > 0 && (_jsxs(_Fragment, { children: [_jsx(Separator, { className: "my-2" }), _jsxs("button", { type: "button", onClick: () => setToonRest((v) => !v), className: "text-muted-foreground hover:text-foreground flex w-full items-center gap-1 px-3 py-1 text-xs uppercase", children: [toonRest ? (_jsx(ChevronDown, { className: "h-3.5 w-3.5" })) : (_jsx(ChevronRight, { className: "h-3.5 w-3.5" })), "Nog niet ingesteld (", rest.length, ")"] }), toonRest &&
|
|
110
|
+
rest.map((d) => _jsx(LijstItem, { type: d.value, label: d.label }, d.value))] }))] }) }), _jsx(Card, { className: "min-w-0", children: _jsxs(CardContent, { className: "space-y-6 py-5", children: [_jsxs("div", { className: "min-w-0", children: [_jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsx("h2", { className: "text-lg font-semibold", children: documentTypeLabel(huidig) }), _jsx(Badge, { variant: "secondary", children: weergaveFormaat(huidig) }), actieveLayouts(huidig).length > 0 && (_jsxs(Badge, { variant: "outline", children: [actieveLayouts(huidig).length, " actief ontwerp", actieveLayouts(huidig).length === 1 ? "" : "en"] }))] }), !std && (_jsxs("p", { className: "text-destructive mt-1 flex items-center gap-1 text-sm", children: [_jsx(AlertTriangle, { className: "h-4 w-4" }), " Nog geen printer gekozen \u2014 opdrachten blijven wachten in de printwachtrij."] }))] }), _jsxs("div", { className: "max-w-md space-y-1.5", children: [_jsx("p", { className: "text-sm font-medium", children: "Standaardprinter (bedrijfsbreed)" }), _jsxs(Select, { value: std ?? GEEN, onValueChange: (v) => void kiesStandaard(huidig, v), children: [_jsx(SelectTrigger, { children: _jsx(SelectValue, { placeholder: "Geen printer" }) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: GEEN, children: "Geen printer" }), printers.map((p) => (_jsx(SelectItem, { value: p.id, children: p.naam }, p.id)))] })] }), _jsx("p", { className: "text-muted-foreground text-xs", children: "Wordt gebruikt als de opdracht geen printplek meegeeft. Per printplek stel je een afwijkende printer in met de printplek-wizard." })] }), _jsxs("div", { children: [_jsxs("button", { type: "button", onClick: () => setToonGeavanceerd((v) => !v), className: "text-muted-foreground hover:text-foreground flex items-center gap-1 text-sm", children: [toonGeavanceerd || bronSamenvatting.length > 0 ? (_jsx(ChevronDown, { className: "h-4 w-4" })) : (_jsx(ChevronRight, { className: "h-4 w-4" })), "Geavanceerd: welk document wordt gebruikt", bronSamenvatting.length > 0 ? ` · ${bronSamenvatting.join(" · ")}` : ""] }), (toonGeavanceerd || bronSamenvatting.length > 0) && (_jsxs("div", { className: "mt-3 space-y-3", children: [_jsx("p", { className: "text-muted-foreground text-xs", children: "Kies of het meegestuurde document van Floriday wordt afgedrukt of een eigen ontwerp uit deze module." }), HERKOMSTEN.map((h) => {
|
|
111
|
+
const regel = bronVan(huidig, h.value);
|
|
112
|
+
const bron = regel?.bron ?? "auto";
|
|
113
|
+
return (_jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsx("span", { className: "w-32 shrink-0 text-sm", children: h.label }), _jsxs(Select, { value: bron, onValueChange: (v) => void kiesBron(huidig, h.value, v, regel?.layout_id ?? null), children: [_jsx(SelectTrigger, { className: "w-52", children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: BRONNEN.map((b) => (_jsx(SelectItem, { value: b.value, children: b.label }, b.value))) })] }), bron === "eigen_layout" && (_jsxs(Select, { value: regel?.layout_id ?? GEEN, onValueChange: (v) => void kiesBron(huidig, h.value, "eigen_layout", v === GEEN ? null : v), children: [_jsx(SelectTrigger, { className: "w-52", children: _jsx(SelectValue, { placeholder: "Standaardontwerp" }) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: GEEN, children: "Standaardontwerp" }), layouts
|
|
114
|
+
.filter((l) => l.documenttype === huidig)
|
|
115
|
+
.map((l) => (_jsxs(SelectItem, { value: l.id, children: [l.naam, isFloridayLayout(l) ? " (uit Floriday)" : ""] }, l.id)))] })] }))] }, h.value));
|
|
116
|
+
})] }))] })] }) })] })] }));
|
|
115
117
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DocumentenPage.d.ts","sourceRoot":"","sources":["../../src/components/DocumentenPage.tsx"],"names":[],"mappings":"AAeA,wBAAgB,cAAc,CAAC,EAAE,GAAe,EAAE,EAAE;IAAE,GAAG,CAAC,EAAE,SAAS,GAAG,OAAO,CAAA;CAAE,+BAUhF"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
// ============================================================================
|
|
3
|
+
// src/components/DocumentenPage.tsx — één menu-item "Documenten" met tabbladen
|
|
4
|
+
// voor de documentontwerpen en de logobibliotheek.
|
|
5
|
+
// ============================================================================
|
|
6
|
+
import { FileText, Image } from "lucide-react";
|
|
7
|
+
import { ModuleTabs } from "./ModuleTabs.js";
|
|
8
|
+
import { LayoutsPage } from "./LayoutsPage.js";
|
|
9
|
+
import { LogosPage } from "./LogosPage.js";
|
|
10
|
+
const TABS = [
|
|
11
|
+
{ label: "Documenten", href: "/layouts-printers", icon: FileText },
|
|
12
|
+
{ label: "Logo's", href: "/layouts-printers/logos", icon: Image },
|
|
13
|
+
];
|
|
14
|
+
export function DocumentenPage({ tab = "layouts" }) {
|
|
15
|
+
return (_jsx(ModuleTabs, { titel: "Documenten", omschrijving: "Ontwerp pakbonnen, facturen, stickers en beheer de logo's die daarop staan.", tabs: TABS, children: tab === "logos" ? _jsx(LogosPage, { ingebed: true }) : _jsx(LayoutsPage, { ingebed: true }) }));
|
|
16
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LayoutsPage.d.ts","sourceRoot":"","sources":["../../src/components/LayoutsPage.tsx"],"names":[],"mappings":"AAgEA,wBAAgB,WAAW,
|
|
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"}
|
|
@@ -13,7 +13,7 @@ function klantOntwerpLayout(l) {
|
|
|
13
13
|
const cfg = (l.config ?? {});
|
|
14
14
|
return Boolean(cfg?.klantlayout?.gebruik_klantlayout);
|
|
15
15
|
}
|
|
16
|
-
export function LayoutsPage() {
|
|
16
|
+
export function LayoutsPage({ ingebed = false } = {}) {
|
|
17
17
|
const { layouts, loading, reload } = useLayouts();
|
|
18
18
|
useAutoRefresh(() => reload(true), 30000);
|
|
19
19
|
const navigate = useNavigate();
|
|
@@ -144,7 +144,8 @@ export function LayoutsPage() {
|
|
|
144
144
|
setBezig(false);
|
|
145
145
|
}
|
|
146
146
|
};
|
|
147
|
-
|
|
147
|
+
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) => {
|
|
148
149
|
await updateLayout(l.id, { actief: v });
|
|
149
150
|
void reload();
|
|
150
151
|
} }) }), _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"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LayoutsPrintersTabPage.d.ts","sourceRoot":"","sources":["../../src/components/LayoutsPrintersTabPage.tsx"],"names":[],"mappings":"AAeA,wBAAgB,sBAAsB,gCAmBrC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
// ============================================================================
|
|
3
|
+
// src/components/LayoutsPrintersTabPage.tsx — één pagina die de tabblad-URL's
|
|
4
|
+
// van de module bedient (/layouts-printers/logos, /routering, /printers).
|
|
5
|
+
// ============================================================================
|
|
6
|
+
import { useEffect } from "react";
|
|
7
|
+
import { useNavigate, useRouterState } from "@tanstack/react-router";
|
|
8
|
+
import { DocumentenPage } from "./DocumentenPage.js";
|
|
9
|
+
import { PrintenPage } from "./PrintenPage.js";
|
|
10
|
+
function huidigTab(pathname) {
|
|
11
|
+
const delen = pathname.split("/").filter(Boolean);
|
|
12
|
+
return delen[delen.length - 1] ?? "";
|
|
13
|
+
}
|
|
14
|
+
export function LayoutsPrintersTabPage() {
|
|
15
|
+
const navigate = useNavigate();
|
|
16
|
+
const pathname = useRouterState({ select: (s) => s.location.pathname });
|
|
17
|
+
const tab = huidigTab(pathname);
|
|
18
|
+
const stuurDoor = tab === "printers"
|
|
19
|
+
? "/layouts-printers/printplekken"
|
|
20
|
+
: tab === "logos" || tab === "routering"
|
|
21
|
+
? null
|
|
22
|
+
: "/layouts-printers";
|
|
23
|
+
useEffect(() => {
|
|
24
|
+
if (stuurDoor)
|
|
25
|
+
void navigate({ to: stuurDoor, replace: true });
|
|
26
|
+
}, [stuurDoor, navigate]);
|
|
27
|
+
if (tab === "logos")
|
|
28
|
+
return _jsx(DocumentenPage, { tab: "logos" });
|
|
29
|
+
if (tab === "routering")
|
|
30
|
+
return _jsx(PrintenPage, { tab: "routering" });
|
|
31
|
+
return _jsx("p", { className: "text-muted-foreground p-6 text-sm", children: "Doorsturen\u2026" });
|
|
32
|
+
}
|
|
@@ -3,5 +3,7 @@ export declare function LogoThumbnail({ logo, className }: {
|
|
|
3
3
|
logo: LogoModel;
|
|
4
4
|
className?: string;
|
|
5
5
|
}): import("react").JSX.Element;
|
|
6
|
-
export declare function LogosPage(
|
|
6
|
+
export declare function LogosPage({ ingebed }?: {
|
|
7
|
+
ingebed?: boolean;
|
|
8
|
+
}): import("react").JSX.Element;
|
|
7
9
|
//# sourceMappingURL=LogosPage.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LogosPage.d.ts","sourceRoot":"","sources":["../../src/components/LogosPage.tsx"],"names":[],"mappings":"AAkCA,OAAO,EAaL,KAAK,SAAS,EACf,MAAM,oBAAoB,CAAC;AAE5B,wBAAgB,aAAa,CAAC,EAAE,IAAI,EAAE,SAAc,EAAE,EAAE;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,+BAY9F;AASD,wBAAgB,SAAS,
|
|
1
|
+
{"version":3,"file":"LogosPage.d.ts","sourceRoot":"","sources":["../../src/components/LogosPage.tsx"],"names":[],"mappings":"AAkCA,OAAO,EAaL,KAAK,SAAS,EACf,MAAM,oBAAoB,CAAC;AAE5B,wBAAgB,aAAa,CAAC,EAAE,IAAI,EAAE,SAAc,EAAE,EAAE;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,+BAY9F;AASD,wBAAgB,SAAS,CAAC,EAAE,OAAe,EAAE,GAAE;IAAE,OAAO,CAAC,EAAE,OAAO,CAAA;CAAO,+BA8LxE"}
|
|
@@ -7,7 +7,7 @@ import { LOGO_CATEGORIEEN, TOEGESTANE_LOGO_TYPES, downloadLogo, formatteerBytes,
|
|
|
7
7
|
export function LogoThumbnail({ logo, className = "" }) {
|
|
8
8
|
return (_jsx("div", { className: `flex h-12 w-16 items-center justify-center overflow-hidden rounded border bg-[repeating-conic-gradient(#e5e7eb_0%_25%,#fff_0%_50%)] bg-[length:10px_10px] ${className}`, children: logo.viewUrl ? (_jsx("img", { src: logo.viewUrl, alt: logo.naam, className: "max-h-full max-w-full object-contain" })) : (_jsx(ImagePlus, { className: "h-4 w-4 text-muted-foreground" })) }));
|
|
9
9
|
}
|
|
10
|
-
export function LogosPage() {
|
|
10
|
+
export function LogosPage({ ingebed = false } = {}) {
|
|
11
11
|
const { logos, loading, reload } = useLogos();
|
|
12
12
|
const { opslagFout, controleer } = useLogoOpslag();
|
|
13
13
|
const [zoek, setZoek] = useState("");
|
|
@@ -42,7 +42,8 @@ export function LogosPage() {
|
|
|
42
42
|
toast.error("Verwijderen mislukt");
|
|
43
43
|
}
|
|
44
44
|
};
|
|
45
|
-
|
|
45
|
+
const Wrapper = ingebed ? "div" : "main";
|
|
46
|
+
return (_jsxs(Wrapper, { className: ingebed ? "space-y-6" : "flex-1 space-y-6 p-6", children: [opslagFout ? (_jsxs("div", { className: "flex flex-wrap items-center justify-between gap-3 rounded-md border border-destructive/40 bg-destructive/10 p-4 text-sm text-destructive", children: [_jsx("span", { children: opslagFout }), _jsxs(Button, { variant: "outline", size: "sm", onClick: () => void controleer(), children: [_jsx(RefreshCw, { className: "mr-2 h-4 w-4" }), " Opnieuw controleren"] })] })) : null, _jsxs(Card, { children: [_jsxs(CardHeader, { className: "flex-row flex-wrap items-end justify-between gap-3 space-y-0", children: [_jsxs("div", { children: [_jsx(CardTitle, { children: "Logo Bibliotheek" }), _jsx(CardDescription, { children: "Centraal beheer van alle bedrijfslogo's. Layouts verwijzen naar deze bibliotheek, dus een vervangen logo werkt direct door in elke layout." })] }), _jsxs(Button, { onClick: () => setUploadOpen(true), disabled: !!opslagFout, title: opslagFout ?? undefined, children: [_jsx(Upload, { className: "mr-2 h-4 w-4" }), " Logo's uploaden"] })] }), _jsxs(CardContent, { className: "space-y-4", children: [_jsxs("div", { className: "flex flex-wrap gap-3", children: [_jsx(Input, { placeholder: "Zoek op naam of omschrijving\u2026", value: zoek, onChange: (e) => setZoek(e.target.value), className: "max-w-xs" }), _jsxs(Select, { value: categorie, onValueChange: setCategorie, children: [_jsx(SelectTrigger, { className: "w-52", children: _jsx(SelectValue, { placeholder: "Categorie" }) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "alle", children: "Alle categorie\u00EBn" }), LOGO_CATEGORIEEN.map((c) => (_jsx(SelectItem, { value: c.value, children: c.label }, c.value)))] })] })] }), _jsx("div", { className: "rounded-md border", children: _jsxs(Table, { children: [_jsx(TableHeader, { children: _jsxs(TableRow, { children: [_jsx(TableHead, { children: "Voorbeeld" }), _jsx(TableHead, { children: "Naam" }), _jsx(TableHead, { children: "Omschrijving" }), _jsx(TableHead, { children: "Categorie" }), _jsx(TableHead, { children: "Formaat" }), _jsx(TableHead, { children: "Resolutie" }), _jsx(TableHead, { children: "Transparant" }), _jsx(TableHead, { children: "Toegevoegd" }), _jsx(TableHead, { children: "Gewijzigd" }), _jsx(TableHead, { className: "text-right", children: "Acties" })] }) }), _jsxs(TableBody, { children: [loading && (_jsx(TableRow, { children: _jsx(TableCell, { colSpan: 10, children: "Laden\u2026" }) })), !loading && zichtbaar.length === 0 && (_jsx(TableRow, { children: _jsx(TableCell, { colSpan: 10, className: "text-muted-foreground", children: "Nog geen logo's gevonden." }) })), zichtbaar.map((l) => (_jsxs(TableRow, { children: [_jsx(TableCell, { children: _jsx(LogoThumbnail, { logo: l }) }), _jsxs(TableCell, { className: "font-medium", children: [l.naam, l.is_standaard && (_jsx(Badge, { variant: "secondary", className: "ml-2", children: "Standaard" }))] }), _jsx(TableCell, { className: "max-w-56 truncate text-muted-foreground", children: l.omschrijving }), _jsx(TableCell, { children: logoCategorieLabel(l.categorie) }), _jsx(TableCell, { className: "uppercase", children: l.bestandstype }), _jsxs(TableCell, { children: [l.breedte && l.hoogte ? `${l.breedte} × ${l.hoogte} px` : "—", _jsx("div", { className: "text-xs text-muted-foreground", children: formatteerBytes(l.bestandsgrootte) })] }), _jsx(TableCell, { children: l.transparant ? "Ja" : "Nee" }), _jsx(TableCell, { children: new Date(l.created_at).toLocaleDateString("nl-NL") }), _jsx(TableCell, { children: new Date(l.updated_at).toLocaleDateString("nl-NL") }), _jsx(TableCell, { children: _jsxs("div", { className: "flex justify-end gap-1", children: [_jsx(Button, { variant: "ghost", size: "icon", title: "Bewerken", onClick: () => setBewerk(l), children: _jsx(Pencil, { className: "h-4 w-4" }) }), _jsxs("label", { title: "Vervangen", children: [_jsx("input", { type: "file", className: "hidden", accept: TOEGESTANE_LOGO_TYPES.join(","), onChange: (e) => {
|
|
46
47
|
const f = e.target.files?.[0];
|
|
47
48
|
if (f)
|
|
48
49
|
void vervang(l, f);
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
import type { LucideIcon } from "lucide-react";
|
|
3
|
+
export interface ModuleTab {
|
|
4
|
+
label: string;
|
|
5
|
+
href: string;
|
|
6
|
+
icon?: LucideIcon;
|
|
7
|
+
/** Extra paden die dit tabblad actief maken (bijv. detailpagina's). */
|
|
8
|
+
ook?: string[];
|
|
9
|
+
}
|
|
10
|
+
interface Props {
|
|
11
|
+
titel: string;
|
|
12
|
+
omschrijving?: string;
|
|
13
|
+
tabs: ModuleTab[];
|
|
14
|
+
acties?: ReactNode;
|
|
15
|
+
children: ReactNode;
|
|
16
|
+
}
|
|
17
|
+
export declare function ModuleTabs({ titel, omschrijving, tabs, acties, children }: Props): import("react").JSX.Element;
|
|
18
|
+
export {};
|
|
19
|
+
//# sourceMappingURL=ModuleTabs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ModuleTabs.d.ts","sourceRoot":"","sources":["../../src/components/ModuleTabs.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE/C,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,uEAAuE;IACvE,GAAG,CAAC,EAAE,MAAM,EAAE,CAAC;CAChB;AAED,UAAU,KAAK;IACb,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,IAAI,EAAE,SAAS,EAAE,CAAC;IAClB,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB,QAAQ,EAAE,SAAS,CAAC;CACrB;AAED,wBAAgB,UAAU,CAAC,EAAE,KAAK,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,EAAE,KAAK,+BA2ChF"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Link, useRouterState } from "@tanstack/react-router";
|
|
3
|
+
export function ModuleTabs({ titel, omschrijving, tabs, acties, children }) {
|
|
4
|
+
const pad = useRouterState({ select: (s) => s.location.pathname });
|
|
5
|
+
const veilig = Array.isArray(tabs) ? tabs : [];
|
|
6
|
+
const isActief = (t) => {
|
|
7
|
+
const paden = [t.href, ...(Array.isArray(t.ook) ? t.ook : [])];
|
|
8
|
+
return paden.some((p) => pad === p || pad.startsWith(`${p}/`));
|
|
9
|
+
};
|
|
10
|
+
return (_jsxs("main", { className: "space-y-5 p-6", children: [_jsxs("header", { className: "flex flex-wrap items-end justify-between gap-4", children: [_jsxs("div", { className: "min-w-0", children: [_jsx("h1", { className: "truncate text-xl font-semibold", children: titel }), omschrijving && _jsx("p", { className: "text-muted-foreground text-sm", children: omschrijving })] }), acties && _jsx("div", { className: "flex flex-wrap items-center gap-2", children: acties })] }), _jsx("nav", { className: "bg-muted/50 inline-flex flex-wrap gap-1 rounded-lg p-1", children: veilig.map((t) => {
|
|
11
|
+
const actief = isActief(t);
|
|
12
|
+
const Icon = t.icon;
|
|
13
|
+
return (_jsxs(Link, { to: t.href, className: `flex items-center gap-2 rounded-md px-3 py-1.5 text-sm font-medium transition-colors ${actief
|
|
14
|
+
? "bg-background text-foreground shadow-sm"
|
|
15
|
+
: "text-muted-foreground hover:text-foreground"}`, children: [Icon && _jsx(Icon, { className: "h-4 w-4" }), t.label] }, t.href));
|
|
16
|
+
}) }), _jsx("div", { children: children })] }));
|
|
17
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PakbonBuilderPage.d.ts","sourceRoot":"","sources":["../../src/components/PakbonBuilderPage.tsx"],"names":[],"mappings":"
|
|
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,+BA0KvF"}
|
|
@@ -7,7 +7,8 @@ import { PakbonSectiesPanel } from "./PakbonSectiesPanel.js";
|
|
|
7
7
|
import { PakbonPreview } from "./PakbonPreview.js";
|
|
8
8
|
import { FormaatOrientatieBar } from "./FormaatOrientatieBar.js";
|
|
9
9
|
import { leesPakbonConfig } from "../lib/pakbonBuilder.js";
|
|
10
|
-
import {
|
|
10
|
+
import { renderPakbonDocument, voorbeeldPakbonOrder } from "../lib/pakbonHtml.js";
|
|
11
|
+
import { printHtml, printerVoorDocumenttype, volgJobStatus } from "../lib/printApi.js";
|
|
11
12
|
import { haalPrintLogos } from "../lib/logoLibrary.js";
|
|
12
13
|
import { updateLayout, useLayoutDetail } from "../lib/useDocumentData.js";
|
|
13
14
|
import { useLayoutIdParam } from "../lib/useLayoutIdParam.js";
|
|
@@ -44,16 +45,41 @@ export function PakbonBuilderPage({ layoutId: layoutIdProp } = {}) {
|
|
|
44
45
|
return;
|
|
45
46
|
setPrinten(true);
|
|
46
47
|
try {
|
|
48
|
+
const printerId = await printerVoorDocumenttype("pakbon", layout?.papierformaat ?? undefined);
|
|
49
|
+
if (!printerId) {
|
|
50
|
+
toast.error("Nog geen printer gekoppeld aan Pakbon — stel dit in bij Layouts & printers > Routering.");
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
47
53
|
const logos = await haalPrintLogos();
|
|
48
|
-
|
|
54
|
+
const html = renderPakbonDocument(config, voorbeeldPakbonOrder(), {
|
|
49
55
|
logos,
|
|
50
56
|
breedteMm: breedte,
|
|
51
57
|
hoogteMm: hoogte,
|
|
52
58
|
titel: layout?.naam ?? "Pakbon",
|
|
53
59
|
});
|
|
60
|
+
const { jobId } = await printHtml({
|
|
61
|
+
documenttype: "pakbon",
|
|
62
|
+
documentnaam: `Voorbeeld ${layout?.naam ?? "pakbon"}`,
|
|
63
|
+
html,
|
|
64
|
+
printerId,
|
|
65
|
+
payload: { voorbeeld: true, layout_id: layout?.id ?? layoutId },
|
|
66
|
+
isHerprint: true,
|
|
67
|
+
});
|
|
68
|
+
toast.success("Voorbeeld naar de printer verstuurd");
|
|
69
|
+
const stop = volgJobStatus(jobId, (s) => {
|
|
70
|
+
if (s.status === "klaar") {
|
|
71
|
+
toast.success("Voorbeeld afgedrukt");
|
|
72
|
+
stop();
|
|
73
|
+
}
|
|
74
|
+
else if (s.status === "mislukt") {
|
|
75
|
+
toast.error(s.foutmelding ?? "Voorbeeld printen mislukt");
|
|
76
|
+
stop();
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
window.setTimeout(() => stop(), 120000);
|
|
54
80
|
}
|
|
55
|
-
catch {
|
|
56
|
-
toast.error("Voorbeeld printen mislukt");
|
|
81
|
+
catch (e) {
|
|
82
|
+
toast.error(e instanceof Error ? e.message : "Voorbeeld printen mislukt");
|
|
57
83
|
}
|
|
58
84
|
finally {
|
|
59
85
|
setPrinten(false);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PrintQueuePage.d.ts","sourceRoot":"","sources":["../../src/components/PrintQueuePage.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"PrintQueuePage.d.ts","sourceRoot":"","sources":["../../src/components/PrintQueuePage.tsx"],"names":[],"mappings":"AA+DA,wBAAgB,cAAc,gCAsP7B"}
|
|
@@ -4,10 +4,11 @@ import { Badge, Button, Card, CardContent, CardDescription, CardHeader, CardTitl
|
|
|
4
4
|
import { RotateCcw, Search, XCircle } from "lucide-react";
|
|
5
5
|
import { bijgewerktTekst, useAutoRefresh } from "../lib/useAutoRefresh.js";
|
|
6
6
|
import { PrintStatusBanner } from "./PrintStatusBanner.js";
|
|
7
|
-
import { maakPrintJob, updateJobStatus, useLayouts, usePrinters, usePrintJobs } from "../lib/useDocumentData.js";
|
|
7
|
+
import { maakPrintJob, updateJobStatus, useLayouts, usePrinters, usePrintJobs, usePrintPlekNamen, } from "../lib/useDocumentData.js";
|
|
8
8
|
import { printStatus } from "../lib/printService.functions.js";
|
|
9
9
|
import { vraagWachtrijRonde } from "../lib/queueTickClient.js";
|
|
10
10
|
import { documentTypeLabel } from "../lib/documentTypes.js";
|
|
11
|
+
import { jobPlekLabel } from "../lib/printQueue.js";
|
|
11
12
|
const STATUS_VARIANT = {
|
|
12
13
|
wachtend: "outline",
|
|
13
14
|
bezig: "secondary",
|
|
@@ -35,15 +36,27 @@ export function PrintQueuePage() {
|
|
|
35
36
|
const { layouts } = useLayouts();
|
|
36
37
|
const [zoek, setZoek] = useState("");
|
|
37
38
|
const [status, setStatus] = useState("alle");
|
|
39
|
+
const [plekFilter, setPlekFilter] = useState("alle");
|
|
38
40
|
const [tickFout, setTickFout] = useState(null);
|
|
41
|
+
const plekNamen = usePrintPlekNamen();
|
|
39
42
|
const printerNaam = useMemo(() => new Map(printers.map((p) => [p.id, p.naam])), [printers]);
|
|
40
43
|
const layoutNaam = useMemo(() => new Map(layouts.map((l) => [l.id, l.naam])), [layouts]);
|
|
44
|
+
/** Herkomstlabel per opdracht: printplek, anders pickstation of werkplek. */
|
|
45
|
+
const plekPerJob = useMemo(() => {
|
|
46
|
+
const m = new Map();
|
|
47
|
+
for (const j of jobs)
|
|
48
|
+
m.set(j.id, jobPlekLabel(j, plekNamen));
|
|
49
|
+
return m;
|
|
50
|
+
}, [jobs, plekNamen]);
|
|
51
|
+
const plekOpties = useMemo(() => Array.from(new Set(Array.from(plekPerJob.values()).filter(Boolean))).sort(), [plekPerJob]);
|
|
41
52
|
const gefilterd = jobs.filter((j) => {
|
|
42
53
|
const q = zoek.trim().toLowerCase();
|
|
43
54
|
if (q && !`${j.documentnaam ?? ""} ${j.gebruiker_naam ?? ""}`.toLowerCase().includes(q))
|
|
44
55
|
return false;
|
|
45
56
|
if (status !== "alle" && j.status !== status)
|
|
46
57
|
return false;
|
|
58
|
+
if (plekFilter !== "alle" && (plekPerJob.get(j.id) || "—") !== plekFilter)
|
|
59
|
+
return false;
|
|
47
60
|
return true;
|
|
48
61
|
});
|
|
49
62
|
const tellingen = useMemo(() => {
|
|
@@ -83,7 +96,7 @@ export function PrintQueuePage() {
|
|
|
83
96
|
await reload(true);
|
|
84
97
|
};
|
|
85
98
|
const bijgewerkt = useAutoRefresh(ronde, lopende ? 5000 : 20000);
|
|
86
|
-
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(Table, { children: [_jsx(TableHeader, { children: _jsxs(TableRow, { children: [_jsx(TableHead, { children: "Document" }), _jsx(TableHead, { children: "Layout" }), _jsx(TableHead, { children: "Printer" }), _jsx(TableHead, { children: "Status" }), _jsx(TableHead, { children: "Tijdstip" }), _jsx(TableHead, { className: "text-right", children: "Acties" })] }) }), _jsxs(TableBody, { children: [gefilterd.length === 0 && (_jsx(TableRow, { children: _jsx(TableCell, { colSpan:
|
|
99
|
+
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" }), _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) || "—" }), _jsx(TableCell, { className: "text-sm text-muted-foreground", children: j.layout_id ? layoutNaam.get(j.layout_id) ?? "—" : "—" }), _jsx(TableCell, { className: "text-sm text-muted-foreground", children: j.printer_id ? printerNaam.get(j.printer_id) ?? "—" : "—" }), _jsx(TableCell, { children: _jsx(Badge, { variant: STATUS_VARIANT[j.status] ?? "outline", children: j.status }) }), _jsx(TableCell, { className: "text-sm text-muted-foreground", children: new Date(j.created_at).toLocaleString("nl-NL") }), _jsx(TableCell, { className: "text-right", children: _jsxs("div", { className: "flex justify-end gap-1", children: [_jsxs(Button, { variant: "ghost", size: "sm", onClick: async () => {
|
|
87
100
|
if (!j.layout_id)
|
|
88
101
|
return;
|
|
89
102
|
try {
|