@flowselections/layout-maker 1.8.3 → 1.13.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.
- package/README.md +139 -132
- package/dist-lib/components/ComputersLijst.d.ts +2 -1
- package/dist-lib/components/ComputersLijst.d.ts.map +1 -1
- package/dist-lib/components/ComputersLijst.js +2 -2
- package/dist-lib/components/DocumentRouteringPage.d.ts +2 -0
- package/dist-lib/components/DocumentRouteringPage.d.ts.map +1 -0
- package/dist-lib/components/DocumentRouteringPage.js +115 -0
- package/dist-lib/components/LayoutsPage.d.ts.map +1 -1
- package/dist-lib/components/LayoutsPage.js +13 -8
- package/dist-lib/components/PrintQueuePage.d.ts.map +1 -1
- package/dist-lib/components/PrintQueuePage.js +16 -1
- package/dist-lib/components/PrintStatusBanner.d.ts +8 -0
- package/dist-lib/components/PrintStatusBanner.d.ts.map +1 -0
- package/dist-lib/components/PrintStatusBanner.js +15 -0
- package/dist-lib/components/PrinterSetupWizard.js +1 -1
- package/dist-lib/components/PrintersPage.d.ts.map +1 -1
- package/dist-lib/components/PrintersPage.js +7 -23
- package/dist-lib/components/WerkplekConfiguratiePage.d.ts.map +1 -1
- package/dist-lib/components/WerkplekConfiguratiePage.js +35 -24
- package/dist-lib/components/WerkplekkenTabel.d.ts +2 -2
- package/dist-lib/components/WerkplekkenTabel.d.ts.map +1 -1
- package/dist-lib/components/WerkplekkenTabel.js +4 -3
- package/dist-lib/index.d.ts +8 -3
- package/dist-lib/index.d.ts.map +1 -1
- package/dist-lib/index.js +9 -5
- package/dist-lib/integrations/supabase/auth-middleware.d.ts +491 -145
- package/dist-lib/integrations/supabase/auth-middleware.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/client.server.d.ts +491 -145
- package/dist-lib/integrations/supabase/client.server.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/types.d.ts +505 -150
- package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
- package/dist-lib/lib/aiLayout.functions.d.ts +491 -145
- package/dist-lib/lib/aiLayout.functions.d.ts.map +1 -1
- package/dist-lib/lib/aiLayout.server.js +9 -9
- package/dist-lib/lib/browserPrint.js +5 -5
- package/dist-lib/lib/documentBron.d.ts +35 -0
- package/dist-lib/lib/documentBron.d.ts.map +1 -0
- package/dist-lib/lib/documentBron.js +62 -0
- package/dist-lib/lib/documentRoutering.d.ts +23 -0
- package/dist-lib/lib/documentRoutering.d.ts.map +1 -0
- package/dist-lib/lib/documentRoutering.js +58 -0
- package/dist-lib/lib/documentTypes.d.ts +61 -0
- package/dist-lib/lib/documentTypes.d.ts.map +1 -1
- package/dist-lib/lib/documentTypes.js +64 -0
- package/dist-lib/lib/formaatPrinters.d.ts +5 -0
- package/dist-lib/lib/formaatPrinters.d.ts.map +1 -1
- package/dist-lib/lib/formaatPrinters.js +12 -0
- package/dist-lib/lib/layoutPreview.js +7 -7
- package/dist-lib/lib/pakbonHtml.js +50 -50
- package/dist-lib/lib/printApi.d.ts +20 -5
- package/dist-lib/lib/printApi.d.ts.map +1 -1
- package/dist-lib/lib/printApi.js +93 -32
- package/dist-lib/lib/printQueue.server.d.ts.map +1 -1
- package/dist-lib/lib/printQueue.server.js +72 -1
- package/dist-lib/lib/printService.functions.d.ts +1964 -580
- package/dist-lib/lib/printService.functions.d.ts.map +1 -1
- package/dist-lib/lib/printerTelling.d.ts +24 -0
- package/dist-lib/lib/printerTelling.d.ts.map +1 -0
- package/dist-lib/lib/printerTelling.js +41 -0
- package/dist-lib/lib/productiebonHtml.js +15 -15
- package/dist-lib/lib/profielHtml.js +18 -18
- package/dist-lib/lib/supabaseAdmin.server.d.ts +5 -5
- package/dist-lib/lib/supabaseAdmin.server.js +43 -43
- package/dist-lib/lib/testpagina.js +1 -1
- package/dist-lib/lib/useDocumentData.d.ts +4 -0
- package/dist-lib/lib/useDocumentData.d.ts.map +1 -1
- package/dist-lib/lib/useDocumentData.js +130 -1
- package/package.json +6 -1
package/README.md
CHANGED
|
@@ -1,132 +1,139 @@
|
|
|
1
|
-
# Layout maker (pakbonnen, verzendbrieven, ectra)
|
|
2
|
-
|
|
3
|
-
blank project start
|
|
4
|
-
|
|
5
|
-
This project was built with [Lovable](https://lovable.dev).
|
|
6
|
-
|
|
7
|
-
## Build with Lovable
|
|
8
|
-
|
|
9
|
-
Continue developing this project in the [Lovable editor](https://lovable.dev/projects/4735fccb-b385-4b48-b4f8-ef135f63f4f7).
|
|
10
|
-
|
|
11
|
-
- **Ship faster**: describe what you want to build and Lovable handles the code.
|
|
12
|
-
- **Stay in sync**: every change made in Lovable is committed straight to this repository.
|
|
13
|
-
- **Full ownership**: this code is yours. Push to `main` on GitHub and your changes sync back into Lovable, ready for your next prompt.
|
|
14
|
-
|
|
15
|
-
## Development
|
|
16
|
-
|
|
17
|
-
Prefer working locally? You need Node.js and npm — [install with nvm](https://github.com/nvm-sh/nvm#installing-and-updating).
|
|
18
|
-
|
|
19
|
-
```sh
|
|
20
|
-
git clone <this-repository-url>
|
|
21
|
-
cd <repository-name>
|
|
22
|
-
npm i
|
|
23
|
-
npm run dev
|
|
24
|
-
```
|
|
25
|
-
|
|
26
|
-
## Installeren in een andere shell
|
|
27
|
-
|
|
28
|
-
1. Draai `sql/print-schema.sql` één keer in de Supabase-database van die shell
|
|
29
|
-
(SQL editor). Het script is idempotent en zet tabellen, rechten, RLS,
|
|
30
|
-
functies, de geplande taken én de opslagmap `layout-logos` voor de Logo
|
|
31
|
-
Bibliotheek klaar. Zorg dat de extensies `pg_cron` en `pg_net` aan staan.
|
|
32
|
-
|
|
33
|
-
2. Zet in de omgeving van de shell:
|
|
34
|
-
- `SUPABASE_URL` en `SERVICE_ROLE_KEY` — nodig voor achtergrondprinten
|
|
35
|
-
- `CENTRAL_AUTH_URL` en `CENTRAL_AUTH_SECRET` — nodig voor de printdienst
|
|
36
|
-
3. Zorg dat de shell het tick-eindpunt host. De setup-CLI doet dit automatisch
|
|
37
|
-
op basis van `flowselections.serverRoutes` in `package.json`. Handmatig
|
|
38
|
-
opgezette shells maken zelf dit bestand aan:
|
|
39
|
-
|
|
40
|
-
```ts
|
|
41
|
-
// src/routes/api/public/print/queue-tick.ts
|
|
42
|
-
import { createFileRoute } from "@tanstack/react-router";
|
|
43
|
-
import { behandelQueueTick } from "@flowselections/layout-maker/print";
|
|
44
|
-
|
|
45
|
-
export const Route = createFileRoute("/api/public/print/queue-tick")({
|
|
46
|
-
server: {
|
|
47
|
-
handlers: {
|
|
48
|
-
POST: async ({ request }) => {
|
|
49
|
-
// De shell levert de databaseverbinding; de module maakt er nooit zelf een.
|
|
50
|
-
const { supabaseAdmin } = await import(
|
|
51
|
-
"../../../../integrations/supabase/client.server"
|
|
52
|
-
);
|
|
53
|
-
return behandelQueueTick(request, supabaseAdmin);
|
|
54
|
-
},
|
|
55
|
-
},
|
|
56
|
-
},
|
|
57
|
-
});
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
Het eindpunt accepteert drie toegangsbewijzen: een runner-sleutel uit
|
|
61
|
-
`print_queue_runners.tick_sleutel`, `SUPABASE_PUBLISHABLE_KEY` (de cron) of
|
|
62
|
-
het sessietoken van een ingelogde gebruiker (de knop in de Printwachtrij).
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
4. Klaar. Zodra de module in de shell draait meldt hij zichzelf aan als
|
|
66
|
-
verwerker van de printwachtrij. Een nieuwe printopdracht wordt meteen
|
|
67
|
-
opgepakt; daarnaast verwerkt de geplande taak `print-queue-tick` elke minuut
|
|
68
|
-
de wachtende opdrachten. Reageert een shell een uur lang niet, dan wordt hij
|
|
69
|
-
automatisch op inactief gezet. De taak `print-queue-cleanup` ruimt dagelijks
|
|
70
|
-
oude opdrachten en bestanden op.
|
|
71
|
-
|
|
72
|
-
## Printen vanuit een andere module
|
|
73
|
-
|
|
74
|
-
Andere modules in dezelfde shell gebruiken uitsluitend de print-API van deze
|
|
75
|
-
module; zij hoeven de printtabellen niet te kennen en mogen er ook niet zelf in
|
|
76
|
-
schrijven (dan blijft de opdracht wachten tot de volgende cronronde).
|
|
77
|
-
|
|
78
|
-
```ts
|
|
79
|
-
import {
|
|
80
|
-
printDocument, printPdf, printHtml,
|
|
81
|
-
printerVoorWerkplek, printerVoorDocumenttype, volgJobStatus,
|
|
82
|
-
} from "@flowselections/layout-maker";
|
|
83
|
-
|
|
84
|
-
// 1. Document dat via een ontwerp uit deze module wordt opgemaakt
|
|
85
|
-
const { jobId } = await printDocument({
|
|
86
|
-
documenttype: "pakbon", // moet een bestaande documentsoort zijn
|
|
87
|
-
documentnaam: "PB-2601284",
|
|
88
|
-
payload: { order },
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
// 2. Kant-en-klare PDF (Floriday-document, klantsticker, externe factuur)
|
|
92
|
-
// Zonder printerId wordt de printer bepaald via de gekoppelde layout van
|
|
93
|
-
// de documentsoort (of de koppeling op papierformaat).
|
|
94
|
-
await printPdf({
|
|
95
|
-
documenttype: "factuur",
|
|
96
|
-
documentnaam: "F-2026-00123",
|
|
97
|
-
pdfBase64, // met of zonder data:-voorvoegsel
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
1
|
+
# Layout maker (pakbonnen, verzendbrieven, ectra)
|
|
2
|
+
|
|
3
|
+
blank project start
|
|
4
|
+
|
|
5
|
+
This project was built with [Lovable](https://lovable.dev).
|
|
6
|
+
|
|
7
|
+
## Build with Lovable
|
|
8
|
+
|
|
9
|
+
Continue developing this project in the [Lovable editor](https://lovable.dev/projects/4735fccb-b385-4b48-b4f8-ef135f63f4f7).
|
|
10
|
+
|
|
11
|
+
- **Ship faster**: describe what you want to build and Lovable handles the code.
|
|
12
|
+
- **Stay in sync**: every change made in Lovable is committed straight to this repository.
|
|
13
|
+
- **Full ownership**: this code is yours. Push to `main` on GitHub and your changes sync back into Lovable, ready for your next prompt.
|
|
14
|
+
|
|
15
|
+
## Development
|
|
16
|
+
|
|
17
|
+
Prefer working locally? You need Node.js and npm — [install with nvm](https://github.com/nvm-sh/nvm#installing-and-updating).
|
|
18
|
+
|
|
19
|
+
```sh
|
|
20
|
+
git clone <this-repository-url>
|
|
21
|
+
cd <repository-name>
|
|
22
|
+
npm i
|
|
23
|
+
npm run dev
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Installeren in een andere shell
|
|
27
|
+
|
|
28
|
+
1. Draai `sql/print-schema.sql` één keer in de Supabase-database van die shell
|
|
29
|
+
(SQL editor). Het script is idempotent en zet tabellen, rechten, RLS,
|
|
30
|
+
functies, de geplande taken én de opslagmap `layout-logos` voor de Logo
|
|
31
|
+
Bibliotheek klaar. Zorg dat de extensies `pg_cron` en `pg_net` aan staan.
|
|
32
|
+
|
|
33
|
+
2. Zet in de omgeving van de shell:
|
|
34
|
+
- `SUPABASE_URL` en `SERVICE_ROLE_KEY` — nodig voor achtergrondprinten
|
|
35
|
+
- `CENTRAL_AUTH_URL` en `CENTRAL_AUTH_SECRET` — nodig voor de printdienst
|
|
36
|
+
3. Zorg dat de shell het tick-eindpunt host. De setup-CLI doet dit automatisch
|
|
37
|
+
op basis van `flowselections.serverRoutes` in `package.json`. Handmatig
|
|
38
|
+
opgezette shells maken zelf dit bestand aan:
|
|
39
|
+
|
|
40
|
+
```ts
|
|
41
|
+
// src/routes/api/public/print/queue-tick.ts
|
|
42
|
+
import { createFileRoute } from "@tanstack/react-router";
|
|
43
|
+
import { behandelQueueTick } from "@flowselections/layout-maker/print";
|
|
44
|
+
|
|
45
|
+
export const Route = createFileRoute("/api/public/print/queue-tick")({
|
|
46
|
+
server: {
|
|
47
|
+
handlers: {
|
|
48
|
+
POST: async ({ request }) => {
|
|
49
|
+
// De shell levert de databaseverbinding; de module maakt er nooit zelf een.
|
|
50
|
+
const { supabaseAdmin } = await import(
|
|
51
|
+
"../../../../integrations/supabase/client.server"
|
|
52
|
+
);
|
|
53
|
+
return behandelQueueTick(request, supabaseAdmin);
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Het eindpunt accepteert drie toegangsbewijzen: een runner-sleutel uit
|
|
61
|
+
`print_queue_runners.tick_sleutel`, `SUPABASE_PUBLISHABLE_KEY` (de cron) of
|
|
62
|
+
het sessietoken van een ingelogde gebruiker (de knop in de Printwachtrij).
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
4. Klaar. Zodra de module in de shell draait meldt hij zichzelf aan als
|
|
66
|
+
verwerker van de printwachtrij. Een nieuwe printopdracht wordt meteen
|
|
67
|
+
opgepakt; daarnaast verwerkt de geplande taak `print-queue-tick` elke minuut
|
|
68
|
+
de wachtende opdrachten. Reageert een shell een uur lang niet, dan wordt hij
|
|
69
|
+
automatisch op inactief gezet. De taak `print-queue-cleanup` ruimt dagelijks
|
|
70
|
+
oude opdrachten en bestanden op.
|
|
71
|
+
|
|
72
|
+
## Printen vanuit een andere module
|
|
73
|
+
|
|
74
|
+
Andere modules in dezelfde shell gebruiken uitsluitend de print-API van deze
|
|
75
|
+
module; zij hoeven de printtabellen niet te kennen en mogen er ook niet zelf in
|
|
76
|
+
schrijven (dan blijft de opdracht wachten tot de volgende cronronde).
|
|
77
|
+
|
|
78
|
+
```ts
|
|
79
|
+
import {
|
|
80
|
+
printDocument, printPdf, printHtml,
|
|
81
|
+
printerVoorWerkplek, printerVoorDocumenttype, volgJobStatus,
|
|
82
|
+
} from "@flowselections/layout-maker";
|
|
83
|
+
|
|
84
|
+
// 1. Document dat via een ontwerp uit deze module wordt opgemaakt
|
|
85
|
+
const { jobId } = await printDocument({
|
|
86
|
+
documenttype: "pakbon", // moet een bestaande documentsoort zijn
|
|
87
|
+
documentnaam: "PB-2601284",
|
|
88
|
+
payload: { order, order_id: order.id }, // order_id: verplicht kenmerk
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
// 2. Kant-en-klare PDF (Floriday-document, klantsticker, externe factuur)
|
|
92
|
+
// Zonder printerId wordt de printer bepaald via de gekoppelde layout van
|
|
93
|
+
// de documentsoort (of de koppeling op papierformaat).
|
|
94
|
+
await printPdf({
|
|
95
|
+
documenttype: "factuur",
|
|
96
|
+
documentnaam: "F-2026-00123",
|
|
97
|
+
pdfBase64, // met of zonder data:-voorvoegsel
|
|
98
|
+
payload: { order_id: factuur.id },
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
// Geef in payload altijd een order-kenmerk mee: queue_item_id, order_id of
|
|
102
|
+
// ordernummer. De wachtrij gebruikt dat om dubbele aanvragen te herkennen;
|
|
103
|
+
// zonder kenmerk kunnen twee orders met dezelfde documentnaam binnen tien
|
|
104
|
+
// minuten als dubbel worden gezien.
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
// Of expliciet per werkplek:
|
|
108
|
+
const printerId = await printerVoorWerkplek("PICKSTATION-1", "A4");
|
|
109
|
+
await printPdf({
|
|
110
|
+
documenttype: "afleverbrief",
|
|
111
|
+
documentnaam: "AFL-12345",
|
|
112
|
+
pdfBase64,
|
|
113
|
+
printerId: printerId!,
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
// 3. Eigen opmaak afdrukken (geen layout, anders wint de layout)
|
|
118
|
+
await printHtml({
|
|
119
|
+
documenttype: "controlelijst",
|
|
120
|
+
documentnaam: "Controlelijst kar 12",
|
|
121
|
+
html,
|
|
122
|
+
printerId: printerId!,
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
// Status live volgen (realtime staat aan op print_jobs)
|
|
126
|
+
const stop = volgJobStatus(jobId, (s) => console.log(s.status, s.foutmelding));
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Belangrijk:
|
|
130
|
+
|
|
131
|
+
- **Documentsoorten** liggen vast in de database: pakbon, factuur, offerte,
|
|
132
|
+
orderbevestiging, etiket, verzendlabel, productlabel, barcode_label,
|
|
133
|
+
magazijnlijst, controlelijst, transportdocument, sticker, productiebon,
|
|
134
|
+
veilingbon, afleverbrief, veilingbrief en vrij.
|
|
135
|
+
- **Beslisvolgorde bij het afdrukken**: kant-en-klare PDF > gekoppelde layout >
|
|
136
|
+
meegestuurde opmaak > actieve layout van de documentsoort.
|
|
137
|
+
- **Dubbelbeveiliging**: een identieke aanvraag binnen 10 minuten wordt
|
|
138
|
+
genegeerd; de aanroep werpt dan `DubbeleAanvraagFout`. Geef bij bulkprints per
|
|
139
|
+
document een eigen `documentnaam` zodat elke opdracht uniek is.
|
|
@@ -2,7 +2,8 @@ export interface ComputerModel {
|
|
|
2
2
|
id: string;
|
|
3
3
|
naam: string;
|
|
4
4
|
online: boolean;
|
|
5
|
-
|
|
5
|
+
/** Printers die het printprogramma op deze computer ziet. */
|
|
6
|
+
aantalGevonden: number;
|
|
6
7
|
}
|
|
7
8
|
export declare function useComputers(): {
|
|
8
9
|
computers: ComputerModel[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ComputersLijst.d.ts","sourceRoot":"","sources":["../../src/components/ComputersLijst.tsx"],"names":[],"mappings":"AAoBA,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,OAAO,CAAC;IAChB,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,wBAAgB,YAAY;;;;;EA6C3B;AAED,wBAAgB,cAAc,CAAC,EAC7B,SAAS,EACT,OAAO,EACP,IAAI,EACJ,UAAU,EACV,mBAAmB,EACnB,aAAa,EACb,qBAAqB,EACrB,YAAoB,GACrB,EAAE;IACD,SAAS,EAAE,aAAa,EAAE,CAAC;IAC3B,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,UAAU,EAAE,MAAM,IAAI,CAAC;IACvB,mBAAmB,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IAChD,aAAa,EAAE,OAAO,CAAC;IACvB,qBAAqB,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IAC5C,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB,+BAuHA"}
|
|
1
|
+
{"version":3,"file":"ComputersLijst.d.ts","sourceRoot":"","sources":["../../src/components/ComputersLijst.tsx"],"names":[],"mappings":"AAoBA,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,OAAO,CAAC;IAChB,6DAA6D;IAC7D,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,wBAAgB,YAAY;;;;;EA6C3B;AAED,wBAAgB,cAAc,CAAC,EAC7B,SAAS,EACT,OAAO,EACP,IAAI,EACJ,UAAU,EACV,mBAAmB,EACnB,aAAa,EACb,qBAAqB,EACrB,YAAoB,GACrB,EAAE;IACD,SAAS,EAAE,aAAa,EAAE,CAAC;IAC3B,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,UAAU,EAAE,MAAM,IAAI,CAAC;IACvB,mBAAmB,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IAChD,aAAa,EAAE,OAAO,CAAC;IACvB,qBAAqB,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IAC5C,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB,+BAuHA"}
|
|
@@ -29,7 +29,7 @@ export function useComputers() {
|
|
|
29
29
|
id: String(comp.id),
|
|
30
30
|
naam: comp.naam,
|
|
31
31
|
online: !!comp.online,
|
|
32
|
-
|
|
32
|
+
aantalGevonden: printers.filter((pr) => pr.computer === comp.naam).length,
|
|
33
33
|
})));
|
|
34
34
|
}
|
|
35
35
|
catch (e) {
|
|
@@ -68,7 +68,7 @@ export function ComputersLijst({ computers, loading, fout, onVernieuw, onPrinter
|
|
|
68
68
|
if (gevonden)
|
|
69
69
|
setNieuw(gevonden);
|
|
70
70
|
}, [computers, toevoegenOpen, wachtOp, nieuw]);
|
|
71
|
-
return (_jsxs("div", { className: alleenDialog ? "" : "space-y-3", children: [!alleenDialog && loading && _jsx("p", { className: "text-muted-foreground text-sm", children: "Laden\u2026" }), !alleenDialog && !loading && fout && (_jsx(Card, { children: _jsxs(CardContent, { className: "space-y-3 py-6", children: [_jsx("p", { className: "font-medium", children: "Geen verbinding" }), _jsx("p", { className: "text-muted-foreground text-sm", children: fout }), _jsxs(Button, { variant: "outline", onClick: onVernieuw, children: [_jsx(RefreshCw, { className: "mr-2 h-4 w-4" }), " Opnieuw proberen"] })] }) })), !alleenDialog && !loading && !fout && computers.length === 0 && (_jsx(Card, { children: _jsxs(CardContent, { className: "space-y-4 py-8", children: [_jsx(PrintProgrammaUitleg, {}), _jsxs(Button, { onClick: () => onToevoegenOpenChange(true), children: [_jsx(Plus, { className: "mr-2 h-4 w-4" }), " Computer toevoegen"] })] }) })), !alleenDialog && computers.map((c) => (_jsxs("div", { className: "bg-card flex items-center gap-4 rounded-xl border p-4", children: [_jsx("div", { className: "bg-muted rounded-lg p-2.5", children: _jsx(Monitor, { className: "text-muted-foreground h-5 w-5" }) }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("p", { className: "truncate font-medium", children: c.naam }), _jsxs("p", { className: "text-muted-foreground text-sm", children: [c.
|
|
71
|
+
return (_jsxs("div", { className: alleenDialog ? "" : "space-y-3", children: [!alleenDialog && loading && _jsx("p", { className: "text-muted-foreground text-sm", children: "Laden\u2026" }), !alleenDialog && !loading && fout && (_jsx(Card, { children: _jsxs(CardContent, { className: "space-y-3 py-6", children: [_jsx("p", { className: "font-medium", children: "Geen verbinding" }), _jsx("p", { className: "text-muted-foreground text-sm", children: fout }), _jsxs(Button, { variant: "outline", onClick: onVernieuw, children: [_jsx(RefreshCw, { className: "mr-2 h-4 w-4" }), " Opnieuw proberen"] })] }) })), !alleenDialog && !loading && !fout && computers.length === 0 && (_jsx(Card, { children: _jsxs(CardContent, { className: "space-y-4 py-8", children: [_jsx(PrintProgrammaUitleg, {}), _jsxs(Button, { onClick: () => onToevoegenOpenChange(true), children: [_jsx(Plus, { className: "mr-2 h-4 w-4" }), " Computer toevoegen"] })] }) })), !alleenDialog && computers.map((c) => (_jsxs("div", { className: "bg-card flex items-center gap-4 rounded-xl border p-4", children: [_jsx("div", { className: "bg-muted rounded-lg p-2.5", children: _jsx(Monitor, { className: "text-muted-foreground h-5 w-5" }) }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("p", { className: "truncate font-medium", children: c.naam }), _jsxs("p", { className: "text-muted-foreground text-sm", children: [c.aantalGevonden, " ", c.aantalGevonden === 1 ? "printer gevonden" : "printers gevonden"] })] }), _jsx("span", { className: `rounded-full px-2.5 py-1 text-xs font-medium ${c.online ? "bg-primary/10 text-primary" : "bg-muted text-muted-foreground"}`, children: c.online ? "Aan" : "Uit" }), _jsx(Button, { variant: "ghost", size: "sm", onClick: () => onPrintersToevoegen(c.naam), children: "Printers toevoegen" })] }, c.id))), !alleenDialog && computers.length > 0 && _jsx(WerktHetNiet, {}), _jsx(Dialog, { open: toevoegenOpen, onOpenChange: onToevoegenOpenChange, children: _jsxs(DialogContent, { className: "sm:max-w-lg", children: [_jsxs(DialogHeader, { children: [_jsx(DialogTitle, { children: "Computer toevoegen" }), _jsx(DialogDescription, { children: "Zet het printprogramma klaar op de computer waar de printers aan hangen." })] }), _jsx(PrintProgrammaUitleg, { compact: true }), _jsx("div", { className: "rounded-lg border p-4 text-sm", children: nieuw ? (_jsxs("div", { className: "space-y-3", children: [_jsxs("p", { className: "font-medium", children: ["Computer gevonden: ", nieuw.naam] }), _jsx(Button, { onClick: () => {
|
|
72
72
|
onToevoegenOpenChange(false);
|
|
73
73
|
onPrintersToevoegen(nieuw.naam);
|
|
74
74
|
}, children: "Printers van deze computer toevoegen" })] })) : (_jsxs("p", { className: "text-muted-foreground flex items-center gap-2", children: [_jsx(Loader2, { className: "h-4 w-4 animate-spin" }), " We wachten tot de computer zich meldt\u2026"] })) }), _jsx("div", { className: "flex justify-end", children: _jsx(Button, { variant: "outline", onClick: () => onToevoegenOpenChange(false), children: "Sluiten" }) }), _jsx(WerktHetNiet, {})] }) })] }));
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DocumentRouteringPage.d.ts","sourceRoot":"","sources":["../../src/components/DocumentRouteringPage.tsx"],"names":[],"mappings":"AA2CA,wBAAgB,qBAAqB,gCAiTpC"}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
// ============================================================================
|
|
3
|
+
// src/components/DocumentRouteringPage.tsx — overzicht: welke documentsoort
|
|
4
|
+
// gaat via welke werkplek naar welke printer.
|
|
5
|
+
// ============================================================================
|
|
6
|
+
import { useMemo, useState } from "react";
|
|
7
|
+
import { Button, Card, CardContent, CardDescription, CardHeader, CardTitle, Input, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, toast, } from "@flowselections/core";
|
|
8
|
+
import { Link } from "@tanstack/react-router";
|
|
9
|
+
import { AlertTriangle, Monitor, Plus, Printer, Trash2 } from "lucide-react";
|
|
10
|
+
import { DOCUMENT_TYPES, documentTypeLabel, isFloridayLayout } from "../lib/documentTypes.js";
|
|
11
|
+
import { documentProfiel } from "../lib/documentProfiles.js";
|
|
12
|
+
import { usePrinters } from "../lib/useDocumentData.js";
|
|
13
|
+
import { useAlleFormaatPrinters, zetFormaatPrinter } from "../lib/formaatPrinters.js";
|
|
14
|
+
import { BRONNEN, HERKOMSTEN, useBronregels, zetBronregel, } from "../lib/documentBron.js";
|
|
15
|
+
import { useLayouts } from "../lib/useDocumentData.js";
|
|
16
|
+
import { useWerkplekPrinters, verwijderWerkplekPrinter, zetWerkplekPrinter, } from "../lib/documentRoutering.js";
|
|
17
|
+
import { useComputers } from "./ComputersLijst.js";
|
|
18
|
+
const GEEN = "geen";
|
|
19
|
+
export function DocumentRouteringPage() {
|
|
20
|
+
const { printers } = usePrinters();
|
|
21
|
+
const { computers } = useComputers();
|
|
22
|
+
const { koppelingen: standaard, reload: herlaadStandaard } = useAlleFormaatPrinters();
|
|
23
|
+
const { koppelingen: werkplekken, reload: herlaadWerkplekken } = useWerkplekPrinters();
|
|
24
|
+
const { regels, reload: herlaadRegels } = useBronregels();
|
|
25
|
+
const { layouts } = useLayouts();
|
|
26
|
+
const [zoek, setZoek] = useState("");
|
|
27
|
+
const [nieuw, setNieuw] = useState(null);
|
|
28
|
+
const [nieuweWerkplek, setNieuweWerkplek] = useState("");
|
|
29
|
+
const [nieuwePrinter, setNieuwePrinter] = useState(GEEN);
|
|
30
|
+
const printerNaam = (id) => printers.find((p) => p.id === id)?.naam ?? "Onbekende printer";
|
|
31
|
+
const standaardFormaat = (type) => documentProfiel(type).formaten[0]?.code ?? "A4";
|
|
32
|
+
/** Formaat zoals de gebruiker het ziet: dat van de actieve layouts zelf. */
|
|
33
|
+
const weergaveFormaat = (type) => {
|
|
34
|
+
const eigen = Array.from(new Set(layouts
|
|
35
|
+
.filter((l) => l.documenttype === type && l.actief)
|
|
36
|
+
.map((l) => l.papierformaat)
|
|
37
|
+
.filter((f) => !!f)));
|
|
38
|
+
if (eigen.length === 1)
|
|
39
|
+
return eigen[0];
|
|
40
|
+
if (eigen.length > 1)
|
|
41
|
+
return `${eigen.length} formaten`;
|
|
42
|
+
return standaardFormaat(type);
|
|
43
|
+
};
|
|
44
|
+
const standaardVoor = (type) => standaard.find((k) => k.documenttype === type && k.formaat_code === standaardFormaat(type))
|
|
45
|
+
?.printer_id ?? null;
|
|
46
|
+
const werkplekkenVoor = (type) => werkplekken.filter((w) => w.documenttype === type);
|
|
47
|
+
const rijen = useMemo(() => {
|
|
48
|
+
const term = zoek.trim().toLowerCase();
|
|
49
|
+
return DOCUMENT_TYPES.filter((d) => (term ? d.label.toLowerCase().includes(term) : true));
|
|
50
|
+
}, [zoek]);
|
|
51
|
+
const kiesStandaard = async (type, printerId) => {
|
|
52
|
+
try {
|
|
53
|
+
await zetFormaatPrinter(type, standaardFormaat(type), printerId === GEEN ? null : printerId);
|
|
54
|
+
await herlaadStandaard();
|
|
55
|
+
toast.success("Standaardprinter bijgewerkt");
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
toast.error("Opslaan mislukt");
|
|
59
|
+
}
|
|
60
|
+
};
|
|
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
|
+
const kiesBron = async (type, herkomst, bron, layoutId) => {
|
|
80
|
+
try {
|
|
81
|
+
await zetBronregel(type, herkomst, bron, layoutId ?? null);
|
|
82
|
+
await herlaadRegels();
|
|
83
|
+
toast.success("Documentbron bijgewerkt");
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
toast.error("Opslaan mislukt");
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
const verwijder = async (id) => {
|
|
90
|
+
try {
|
|
91
|
+
await verwijderWerkplekPrinter(id);
|
|
92
|
+
await herlaadWerkplekken();
|
|
93
|
+
toast.success("Uitzondering verwijderd");
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
toast.error("Verwijderen mislukt");
|
|
97
|
+
}
|
|
98
|
+
};
|
|
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: "Documentroutering" }), _jsx("p", { className: "text-muted-foreground text-sm", children: "Bepaal per documentsoort welke printer wordt gebruikt, en welke printer geldt op een specifieke werkplek." })] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Input, { className: "w-56", placeholder: "Zoek documentsoort", value: zoek, onChange: (e) => setZoek(e.target.value) }), _jsx(Button, { variant: "outline", asChild: true, children: _jsxs(Link, { to: "/layouts-printers/printers", children: [_jsx(Printer, { className: "mr-2 h-4 w-4" }), " Printers"] }) })] })] }), _jsx(Card, { children: _jsxs(CardHeader, { className: "pb-3", children: [_jsx(CardTitle, { className: "text-base", children: "Keuzevolgorde" }), _jsx(CardDescription, { children: "1. printer die de module meestuurt \u00B7 2. uitzondering voor de werkplek \u00B7 3. printer gekoppeld aan het ontwerp \u00B7 4. standaardprinter hieronder \u00B7 5. is er maar \u00E9\u00E9n actieve printer, dan die \u00B7 6. anders blijft de opdracht wachten in de printwachtrij." })] }) }), _jsx("div", { className: "space-y-3", children: rijen.map((d) => {
|
|
100
|
+
const uitzonderingen = werkplekkenVoor(d.value);
|
|
101
|
+
const std = standaardVoor(d.value);
|
|
102
|
+
const zonderPrinter = !std && uitzonderingen.length === 0;
|
|
103
|
+
return (_jsxs(Card, { children: [_jsxs(CardContent, { className: "grid gap-4 py-4 lg:grid-cols-[220px_260px_1fr]", children: [_jsxs("div", { className: "min-w-0", children: [_jsx("p", { className: "font-medium", children: documentTypeLabel(d.value) }), _jsx("p", { className: "text-muted-foreground text-xs", children: weergaveFormaat(d.value) }), zonderPrinter && (_jsxs("p", { className: "text-destructive mt-1 flex items-center gap-1 text-xs", children: [_jsx(AlertTriangle, { className: "h-3.5 w-3.5" }), " Geen printer ingesteld"] }))] }), _jsxs("div", { className: "space-y-1", children: [_jsx("p", { className: "text-muted-foreground text-xs uppercase", children: "Standaardprinter" }), _jsxs(Select, { value: std ?? GEEN, onValueChange: (v) => void kiesStandaard(d.value, 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)))] })] })] }), _jsxs("div", { className: "space-y-2", children: [_jsx("p", { className: "text-muted-foreground text-xs uppercase", children: "Per werkplek" }), uitzonderingen.map((w) => (_jsxs("div", { className: "flex items-center gap-2 text-sm", children: [_jsx(Monitor, { className: "text-muted-foreground h-4 w-4 shrink-0" }), _jsxs("span", { className: "min-w-0 flex-1 truncate", children: [w.werkplek, " \u2192 ", printerNaam(w.printer_id)] }), _jsx(Button, { variant: "ghost", size: "sm", onClick: () => void verwijder(w.id), children: _jsx(Trash2, { className: "h-4 w-4" }) })] }, w.id))), nieuw === d.value ? (_jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsxs(Select, { value: nieuweWerkplek, onValueChange: setNieuweWerkplek, children: [_jsx(SelectTrigger, { className: "w-48", children: _jsx(SelectValue, { placeholder: "Kies werkplek" }) }), _jsx(SelectContent, { children: computers.map((c) => (_jsx(SelectItem, { value: c.naam, children: c.naam }, c.id))) })] }), _jsxs(Select, { value: nieuwePrinter, onValueChange: setNieuwePrinter, children: [_jsx(SelectTrigger, { className: "w-48", children: _jsx(SelectValue, { placeholder: "Kies 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(Button, { size: "sm", onClick: () => void voegWerkplekToe(d.value), children: "Opslaan" }), _jsx(Button, { size: "sm", variant: "ghost", onClick: () => setNieuw(null), children: "Annuleren" })] })) : (_jsxs(Button, { size: "sm", variant: "outline", onClick: () => {
|
|
104
|
+
setNieuw(d.value);
|
|
105
|
+
setNieuweWerkplek("");
|
|
106
|
+
setNieuwePrinter(GEEN);
|
|
107
|
+
}, children: [_jsx(Plus, { className: "mr-2 h-4 w-4" }), " Werkplek toevoegen"] }))] })] }), _jsxs(CardContent, { className: "border-t pt-4", children: [_jsx("p", { className: "text-muted-foreground mb-2 text-xs uppercase", children: "Bron per herkomst" }), _jsx("div", { className: "grid gap-2 sm:grid-cols-2", children: HERKOMSTEN.map((h) => {
|
|
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
|
+
}) })] }));
|
|
115
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LayoutsPage.d.ts","sourceRoot":"","sources":["../../src/components/LayoutsPage.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"LayoutsPage.d.ts","sourceRoot":"","sources":["../../src/components/LayoutsPage.tsx"],"names":[],"mappings":"AAgEA,wBAAgB,WAAW,gCA4kB1B"}
|
|
@@ -3,9 +3,9 @@ import { useMemo, useState } from "react";
|
|
|
3
3
|
import { useAutoRefresh } from "../lib/useAutoRefresh.js";
|
|
4
4
|
import { useNavigate } from "@tanstack/react-router";
|
|
5
5
|
import { Badge, Button, Card, CardContent, CardDescription, CardHeader, CardTitle, Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, Input, Label, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, Separator, Switch, Table, TableBody, TableCell, TableHead, TableHeader, TableRow, Textarea, toast, } from "@flowselections/core";
|
|
6
|
-
import { Copy, FileText,
|
|
6
|
+
import { Copy, FileText, Pencil, Plus, Search, Settings2, Trash2 } from "lucide-react";
|
|
7
7
|
import { dupliceerLayout, maakLayout, updateLayout, useLayouts, verwijderLayout, } from "../lib/useDocumentData.js";
|
|
8
|
-
import { DOCUMENT_TYPES, PAPIERFORMATEN, documentTypeLabel } from "../lib/documentTypes.js";
|
|
8
|
+
import { DOCUMENT_TYPES, FLORIDAY_LAYOUT_TOELICHTING, PAPIERFORMATEN, documentTypeLabel, isFloridayLayout, } from "../lib/documentTypes.js";
|
|
9
9
|
import { DOCUMENT_PROFIELEN, documentProfiel, primaireProfielen, standaardConfig, } from "../lib/documentProfiles.js";
|
|
10
10
|
function klantOntwerpLayout(l) {
|
|
11
11
|
if (l.documenttype !== "sticker")
|
|
@@ -20,6 +20,7 @@ export function LayoutsPage() {
|
|
|
20
20
|
const [zoek, setZoek] = useState("");
|
|
21
21
|
const [typeFilter, setTypeFilter] = useState("alle");
|
|
22
22
|
const [statusFilter, setStatusFilter] = useState("alle");
|
|
23
|
+
const [bronFilter, setBronFilter] = useState("alle");
|
|
23
24
|
const [wizardOpen, setWizardOpen] = useState(false);
|
|
24
25
|
const [stap, setStap] = useState(1);
|
|
25
26
|
const [naam, setNaam] = useState("");
|
|
@@ -89,8 +90,12 @@ export function LayoutsPage() {
|
|
|
89
90
|
return false;
|
|
90
91
|
if (statusFilter === "inactief" && l.actief)
|
|
91
92
|
return false;
|
|
93
|
+
if (bronFilter === "floriday" && !isFloridayLayout(l))
|
|
94
|
+
return false;
|
|
95
|
+
if (bronFilter === "eigen" && isFloridayLayout(l))
|
|
96
|
+
return false;
|
|
92
97
|
return true;
|
|
93
|
-
}), [layouts, zoek, typeFilter, statusFilter]);
|
|
98
|
+
}), [layouts, zoek, typeFilter, statusFilter, bronFilter]);
|
|
94
99
|
const aanmaken = async () => {
|
|
95
100
|
if (!naam.trim()) {
|
|
96
101
|
toast.error("Geef de layout een naam");
|
|
@@ -139,15 +144,15 @@ export function LayoutsPage() {
|
|
|
139
144
|
setBezig(false);
|
|
140
145
|
}
|
|
141
146
|
};
|
|
142
|
-
return (_jsxs("main", { className: "p-6 space-y-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: "Documentlayouts" }), _jsx("p", { className: "text-sm text-muted-foreground", children: "Ontwerp pakbonnen, facturen, etiketten en alle andere documenten." })] }),
|
|
147
|
+
return (_jsxs("main", { className: "p-6 space-y-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: "Documentlayouts" }), _jsx("p", { className: "text-sm text-muted-foreground", children: "Ontwerp pakbonnen, facturen, etiketten en alle andere documenten." })] }), _jsx("div", { className: "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: "Versie" }), _jsx(TableHead, { children: "Actief" }), _jsx(TableHead, { className: "text-right", children: "Acties" })] }) }), _jsxs(TableBody, { children: [gefilterd.length === 0 && (_jsx(TableRow, { children: _jsx(TableCell, { colSpan: 6, className: "py-10 text-center text-sm text-muted-foreground", children: "Geen layouts gevonden." }) })), gefilterd.map((l) => (_jsxs(TableRow, { children: [_jsxs(TableCell, { children: [_jsxs("div", { className: "flex items-center gap-2 font-medium", children: [_jsx(FileText, { className: "h-4 w-4 text-muted-foreground" }), l.naam, l.is_standaard && _jsx(Badge, { variant: "secondary", children: "Standaard" }), isFloridayLayout(l) && _jsx(Badge, { variant: "outline", children: "Uit Floriday" })] }), (l.omschrijving || isFloridayLayout(l)) && (_jsx("span", { className: "text-xs text-muted-foreground", children: isFloridayLayout(l) ? FLORIDAY_LAYOUT_TOELICHTING : l.omschrijving }))] }), _jsx(TableCell, { children: documentTypeLabel(l.documenttype) }), _jsxs(TableCell, { className: "text-sm text-muted-foreground", children: [l.papierformaat, " \u00B7 ", Number(l.breedte_mm), " \u00D7 ", Number(l.hoogte_mm), " mm"] }), _jsxs(TableCell, { children: ["v", l.huidige_versie] }), _jsx(TableCell, { children: _jsx(Switch, { checked: l.actief, onCheckedChange: async (v) => {
|
|
143
148
|
await updateLayout(l.id, { actief: v });
|
|
144
149
|
void reload();
|
|
145
|
-
} }) }), _jsx(TableCell, { className: "text-right", children: _jsxs("div", { className: "flex justify-end gap-1", children: [klantOntwerpLayout(l) ? (_jsx(Badge, { variant: "outline", children: "Ontwerp van klant" })) : (_jsxs(Button, { variant: "ghost", size: "sm", onClick: () => navigate(l.documenttype === "productiebon"
|
|
150
|
+
} }) }), _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"
|
|
146
151
|
? {
|
|
147
152
|
to: "/layouts-printers/productiebon/$layoutId",
|
|
148
153
|
params: { layoutId: l.id },
|
|
149
154
|
}
|
|
150
|
-
: { to: "/layouts-printers/pakbon/$layoutId", params: { layoutId: l.id } }), children: [_jsx(Pencil, { className: "mr-2 h-4 w-4" }), " Ontwerpen"] })), _jsx(Button, { variant: "ghost", size: "sm", title: "Naam, documenttype en formaat wijzigen", onClick: () => openBewerken(l), children: _jsx(Settings2, { className: "h-4 w-4" }) }), _jsx(Button, { variant: "ghost", size: "sm", onClick: async () => {
|
|
155
|
+
: { to: "/layouts-printers/pakbon/$layoutId", params: { layoutId: l.id } }), children: [_jsx(Pencil, { className: "mr-2 h-4 w-4" }), " Ontwerpen"] })), !isFloridayLayout(l) && (_jsx(Button, { variant: "ghost", size: "sm", title: "Naam, documenttype en formaat wijzigen", onClick: () => openBewerken(l), children: _jsx(Settings2, { className: "h-4 w-4" }) })), !isFloridayLayout(l) && (_jsx(Button, { variant: "ghost", size: "sm", title: "Kopie maken", onClick: async () => {
|
|
151
156
|
try {
|
|
152
157
|
await dupliceerLayout(l.id);
|
|
153
158
|
toast.success("Layout gedupliceerd");
|
|
@@ -156,7 +161,7 @@ export function LayoutsPage() {
|
|
|
156
161
|
catch {
|
|
157
162
|
toast.error("Dupliceren mislukt");
|
|
158
163
|
}
|
|
159
|
-
}, children: _jsx(Copy, { className: "h-4 w-4" }) }), _jsx(Button, { variant: "ghost", size: "sm", onClick: async () => {
|
|
164
|
+
}, children: _jsx(Copy, { className: "h-4 w-4" }) })), !isFloridayLayout(l) && (_jsx(Button, { variant: "ghost", size: "sm", title: "Layout verwijderen", onClick: async () => {
|
|
160
165
|
try {
|
|
161
166
|
await verwijderLayout(l.id);
|
|
162
167
|
toast.success("Layout verwijderd");
|
|
@@ -165,7 +170,7 @@ export function LayoutsPage() {
|
|
|
165
170
|
catch {
|
|
166
171
|
toast.error("Verwijderen mislukt");
|
|
167
172
|
}
|
|
168
|
-
}, children: _jsx(Trash2, { className: "h-4 w-4" }) })] }) })] }, l.id)))] })] })] })] }), _jsx(Dialog, { open: wizardOpen, onOpenChange: (v) => {
|
|
173
|
+
}, children: _jsx(Trash2, { className: "h-4 w-4" }) }))] }) })] }, l.id)))] })] })] })] }), _jsx(Dialog, { open: wizardOpen, onOpenChange: (v) => {
|
|
169
174
|
setWizardOpen(v);
|
|
170
175
|
if (!v)
|
|
171
176
|
setStap(1);
|
|
@@ -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":"AAuDA,wBAAgB,cAAc,gCAqN7B"}
|
|
@@ -3,6 +3,7 @@ import { useMemo, useRef, useState } from "react";
|
|
|
3
3
|
import { Badge, Button, Card, CardContent, CardDescription, CardHeader, CardTitle, Input, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, Table, TableBody, TableCell, TableHead, TableHeader, TableRow, toast, } from "@flowselections/core";
|
|
4
4
|
import { RotateCcw, Search, XCircle } from "lucide-react";
|
|
5
5
|
import { bijgewerktTekst, useAutoRefresh } from "../lib/useAutoRefresh.js";
|
|
6
|
+
import { PrintStatusBanner } from "./PrintStatusBanner.js";
|
|
6
7
|
import { maakPrintJob, updateJobStatus, useLayouts, usePrinters, usePrintJobs } from "../lib/useDocumentData.js";
|
|
7
8
|
import { printStatus } from "../lib/printService.functions.js";
|
|
8
9
|
import { vraagWachtrijRonde } from "../lib/queueTickClient.js";
|
|
@@ -14,6 +15,20 @@ const STATUS_VARIANT = {
|
|
|
14
15
|
mislukt: "destructive",
|
|
15
16
|
geannuleerd: "outline",
|
|
16
17
|
};
|
|
18
|
+
/**
|
|
19
|
+
* Heeft deze opdracht een kenmerk waarmee hij van andere orders te
|
|
20
|
+
* onderscheiden is? Zo niet, dan valt de dubbelbescherming terug op het
|
|
21
|
+
* document zelf en kan de aanleverende module dit beter aanvullen.
|
|
22
|
+
*/
|
|
23
|
+
function heeftOrderKenmerk(job) {
|
|
24
|
+
const p = (job.payload ?? {});
|
|
25
|
+
if (typeof p !== "object" || p === null)
|
|
26
|
+
return false;
|
|
27
|
+
return ["queue_item_id", "order_id", "ordernummer", "order_number", "orderId"].some((k) => {
|
|
28
|
+
const w = p[k];
|
|
29
|
+
return (typeof w === "string" && w.trim() !== "") || typeof w === "number";
|
|
30
|
+
});
|
|
31
|
+
}
|
|
17
32
|
export function PrintQueuePage() {
|
|
18
33
|
const { jobs, loading, reload } = usePrintJobs();
|
|
19
34
|
const { printers } = usePrinters();
|
|
@@ -68,7 +83,7 @@ export function PrintQueuePage() {
|
|
|
68
83
|
await reload(true);
|
|
69
84
|
};
|
|
70
85
|
const bijgewerkt = useAutoRefresh(ronde, lopende ? 5000 : 20000);
|
|
71
|
-
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) })] }),
|
|
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: 6, 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: 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 () => {
|
|
72
87
|
if (!j.layout_id)
|
|
73
88
|
return;
|
|
74
89
|
try {
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export declare function PrintStatusBanner({ fout, offlineWerkplekken, toonWerkplekLink, }: {
|
|
2
|
+
/** Foutmelding van de printdienst, als die er is. */
|
|
3
|
+
fout?: string | null;
|
|
4
|
+
/** Namen van werkplekken die nu uit staan. */
|
|
5
|
+
offlineWerkplekken?: string[];
|
|
6
|
+
toonWerkplekLink?: boolean;
|
|
7
|
+
}): import("react").JSX.Element | null;
|
|
8
|
+
//# sourceMappingURL=PrintStatusBanner.d.ts.map
|