@flowselections/layout-maker 1.32.0 → 1.38.4
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 -139
- package/dist-lib/components/AfdrukKnop.d.ts +19 -19
- package/dist-lib/components/ComputersLijst.d.ts.map +1 -1
- package/dist-lib/components/ComputersLijst.js +5 -5
- package/dist-lib/components/DocumentenPage.d.ts.map +1 -1
- package/dist-lib/components/DocumentenPage.js +3 -2
- package/dist-lib/components/LayoutPrinterSelect.d.ts +8 -0
- package/dist-lib/components/LayoutPrinterSelect.d.ts.map +1 -0
- package/dist-lib/components/LayoutPrinterSelect.js +23 -0
- package/dist-lib/components/LayoutsPage.d.ts.map +1 -1
- package/dist-lib/components/LayoutsPage.js +16 -3
- package/dist-lib/components/NetwerkPrintersDialog.d.ts +7 -7
- package/dist-lib/components/PaginaDocument.d.ts +4 -1
- package/dist-lib/components/PaginaDocument.d.ts.map +1 -1
- package/dist-lib/components/PaginaDocument.js +10 -5
- package/dist-lib/components/PakbonBuilderPage.d.ts.map +1 -1
- package/dist-lib/components/PakbonBuilderPage.js +6 -2
- package/dist-lib/components/PakbonPreview.d.ts +4 -2
- package/dist-lib/components/PakbonPreview.d.ts.map +1 -1
- package/dist-lib/components/PakbonPreview.js +55 -12
- package/dist-lib/components/PakbonSectiesPanel.d.ts +2 -2
- package/dist-lib/components/PakbonSectiesPanel.d.ts.map +1 -1
- package/dist-lib/components/PakbonSectiesPanel.js +7 -2
- package/dist-lib/components/PdfMapAutoSaver.d.ts +6 -0
- package/dist-lib/components/PdfMapAutoSaver.d.ts.map +1 -0
- package/dist-lib/components/PdfMapAutoSaver.js +12 -0
- package/dist-lib/components/PdfMapGlobaleBanner.d.ts +12 -0
- package/dist-lib/components/PdfMapGlobaleBanner.d.ts.map +1 -0
- package/dist-lib/components/PdfMapGlobaleBanner.js +30 -0
- package/dist-lib/components/PdfMapToestemmingBanner.d.ts +7 -0
- package/dist-lib/components/PdfMapToestemmingBanner.d.ts.map +1 -0
- package/dist-lib/components/PdfMapToestemmingBanner.js +139 -0
- package/dist-lib/components/PrintQueuePage.d.ts.map +1 -1
- package/dist-lib/components/PrintQueuePage.js +42 -3
- package/dist-lib/components/PrintenPage.d.ts.map +1 -1
- package/dist-lib/components/PrintenPage.js +3 -2
- package/dist-lib/components/PrinterSetupWizard.d.ts.map +1 -1
- package/dist-lib/components/PrinterSetupWizard.js +28 -5
- package/dist-lib/components/PrintersRedirectPage.d.ts +1 -1
- package/dist-lib/components/PrintersRedirectPage.js +14 -14
- package/dist-lib/components/PrintplekDetailPage.d.ts.map +1 -1
- package/dist-lib/components/PrintplekDetailPage.js +30 -4
- package/dist-lib/components/PrintplekWizard.d.ts.map +1 -1
- package/dist-lib/components/PrintplekWizard.js +81 -3
- package/dist-lib/components/ProductiebonBuilderPage.d.ts.map +1 -1
- package/dist-lib/components/ProductiebonBuilderPage.js +3 -1
- package/dist-lib/index.d.ts +10 -2
- package/dist-lib/index.d.ts.map +1 -1
- package/dist-lib/index.js +13 -2
- package/dist-lib/integrations/supabase/auth-middleware.d.ts +1396 -727
- package/dist-lib/integrations/supabase/auth-middleware.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/client.server.d.ts +1396 -727
- package/dist-lib/integrations/supabase/client.server.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/client.server.js +1 -1
- package/dist-lib/integrations/supabase/moduleClient.d.ts +24008 -0
- package/dist-lib/integrations/supabase/moduleClient.d.ts.map +1 -0
- package/dist-lib/integrations/supabase/moduleClient.js +29 -0
- package/dist-lib/integrations/supabase/types.d.ts +1403 -750
- package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
- package/dist-lib/lib/aiLayout.functions.d.ts +1396 -727
- 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 +1 -1
- package/dist-lib/lib/code128.d.ts +5 -0
- package/dist-lib/lib/code128.d.ts.map +1 -0
- package/dist-lib/lib/code128.js +51 -0
- package/dist-lib/lib/htmlPdf.server.d.ts.map +1 -1
- package/dist-lib/lib/htmlPdf.server.js +101 -6
- package/dist-lib/lib/layoutPreview.d.ts.map +1 -1
- package/dist-lib/lib/layoutPreview.js +11 -8
- package/dist-lib/lib/orderData.server.d.ts.map +1 -1
- package/dist-lib/lib/orderData.server.js +123 -10
- package/dist-lib/lib/pakbonBuilder.d.ts +28 -2
- package/dist-lib/lib/pakbonBuilder.d.ts.map +1 -1
- package/dist-lib/lib/pakbonBuilder.js +69 -8
- package/dist-lib/lib/pakbonHtml.d.ts +14 -2
- package/dist-lib/lib/pakbonHtml.d.ts.map +1 -1
- package/dist-lib/lib/pakbonHtml.js +155 -63
- package/dist-lib/lib/pakbonScan.d.ts +44 -0
- package/dist-lib/lib/pakbonScan.d.ts.map +1 -0
- package/dist-lib/lib/pakbonScan.js +105 -0
- package/dist-lib/lib/pdfJobSaver.d.ts +20 -0
- package/dist-lib/lib/pdfJobSaver.d.ts.map +1 -0
- package/dist-lib/lib/pdfJobSaver.js +173 -0
- package/dist-lib/lib/pdfMap.d.ts +48 -0
- package/dist-lib/lib/pdfMap.d.ts.map +1 -0
- package/dist-lib/lib/pdfMap.js +219 -0
- package/dist-lib/lib/pdfPrinter.d.ts +9 -0
- package/dist-lib/lib/pdfPrinter.d.ts.map +1 -0
- package/dist-lib/lib/pdfPrinter.js +34 -0
- package/dist-lib/lib/previewPdf.d.ts.map +1 -1
- package/dist-lib/lib/previewPdf.js +41 -9
- package/dist-lib/lib/printApi.d.ts.map +1 -1
- package/dist-lib/lib/printApi.js +9 -1
- package/dist-lib/lib/printQueue.server.d.ts +7 -0
- package/dist-lib/lib/printQueue.server.d.ts.map +1 -1
- package/dist-lib/lib/printQueue.server.js +346 -52
- package/dist-lib/lib/printService.functions.d.ts +5365 -2689
- package/dist-lib/lib/printService.functions.d.ts.map +1 -1
- 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/supabaseClient.d.ts +1 -3
- package/dist-lib/lib/supabaseClient.d.ts.map +1 -1
- package/dist-lib/lib/supabaseClient.js +4 -8
- package/dist-lib/lib/testpagina.js +3 -3
- package/dist-lib/lib/testprint.js +4 -4
- package/dist-lib/lib/useDocumentData.d.ts +10 -0
- package/dist-lib/lib/useDocumentData.d.ts.map +1 -1
- package/dist-lib/lib/useDocumentData.js +19 -0
- package/dist-lib/lib/voorbeeldPrint.d.ts +4 -0
- package/dist-lib/lib/voorbeeldPrint.d.ts.map +1 -1
- package/dist-lib/lib/voorbeeldPrint.js +13 -2
- package/package.json +151 -151
package/README.md
CHANGED
|
@@ -1,139 +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, 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.
|
|
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.
|
|
@@ -1,20 +1,20 @@
|
|
|
1
|
-
import { type Orientatie } from "../lib/browserPrint.js";
|
|
2
|
-
export interface AfdrukKnopProps {
|
|
3
|
-
documenttype: string;
|
|
4
|
-
formaatCode: string | null;
|
|
5
|
-
orientatie: Orientatie;
|
|
6
|
-
/** Levert de HTML van het document aan op het moment van afdrukken. */
|
|
7
|
-
getHtml: () => string;
|
|
8
|
-
css?: string;
|
|
9
|
-
documentnaam?: string;
|
|
10
|
-
label?: string;
|
|
11
|
-
layoutId?: string | null;
|
|
12
|
-
kopieen?: number;
|
|
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
|
-
*/
|
|
19
|
-
export declare function AfdrukKnop({ documenttype, formaatCode, orientatie, getHtml, css, documentnaam, label, layoutId, kopieen, }: AfdrukKnopProps): import("react/jsx-runtime").JSX.Element;
|
|
1
|
+
import { type Orientatie } from "../lib/browserPrint.js";
|
|
2
|
+
export interface AfdrukKnopProps {
|
|
3
|
+
documenttype: string;
|
|
4
|
+
formaatCode: string | null;
|
|
5
|
+
orientatie: Orientatie;
|
|
6
|
+
/** Levert de HTML van het document aan op het moment van afdrukken. */
|
|
7
|
+
getHtml: () => string;
|
|
8
|
+
css?: string;
|
|
9
|
+
documentnaam?: string;
|
|
10
|
+
label?: string;
|
|
11
|
+
layoutId?: string | null;
|
|
12
|
+
kopieen?: number;
|
|
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
|
+
*/
|
|
19
|
+
export declare function AfdrukKnop({ documenttype, formaatCode, orientatie, getHtml, css, documentnaam, label, layoutId, kopieen, }: AfdrukKnopProps): import("react/jsx-runtime").JSX.Element;
|
|
20
20
|
//# sourceMappingURL=AfdrukKnop.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ComputersLijst.d.ts","sourceRoot":"","sources":["../../src/components/ComputersLijst.tsx"],"names":[],"mappings":"AAoBA,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,OAAO,CAAC;IAChB,6DAA6D;IAC7D,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,wBAAgB,YAAY;;;;;EA6C3B;AAED,wBAAgB,cAAc,CAAC,EAC7B,SAAS,EACT,OAAO,EACP,IAAI,EACJ,UAAU,EACV,mBAAmB,EACnB,aAAa,EACb,qBAAqB,EACrB,YAAoB,GACrB,EAAE;IACD,SAAS,EAAE,aAAa,EAAE,CAAC;IAC3B,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,UAAU,EAAE,MAAM,IAAI,CAAC;IACvB,mBAAmB,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IAChD,aAAa,EAAE,OAAO,CAAC;IACvB,qBAAqB,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IAC5C,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB,+
|
|
1
|
+
{"version":3,"file":"ComputersLijst.d.ts","sourceRoot":"","sources":["../../src/components/ComputersLijst.tsx"],"names":[],"mappings":"AAoBA,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,OAAO,CAAC;IAChB,6DAA6D;IAC7D,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,wBAAgB,YAAY;;;;;EA6C3B;AAED,wBAAgB,cAAc,CAAC,EAC7B,SAAS,EACT,OAAO,EACP,IAAI,EACJ,UAAU,EACV,mBAAmB,EACnB,aAAa,EACb,qBAAqB,EACrB,YAAoB,GACrB,EAAE;IACD,SAAS,EAAE,aAAa,EAAE,CAAC;IAC3B,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,UAAU,EAAE,MAAM,IAAI,CAAC;IACvB,mBAAmB,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IAChD,aAAa,EAAE,OAAO,CAAC;IACvB,qBAAqB,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IAC5C,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB,+BAwIA"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
// ============================================================================
|
|
3
3
|
// src/components/ComputersLijst.tsx — overzicht van computers die het
|
|
4
4
|
// printprogramma draaien, plus een korte stroom om er een toe te voegen.
|
|
@@ -68,8 +68,8 @@ export function ComputersLijst({ computers, loading, fout, onVernieuw, onPrinter
|
|
|
68
68
|
if (gevonden)
|
|
69
69
|
setNieuw(gevonden);
|
|
70
70
|
}, [computers, toevoegenOpen, wachtOp, nieuw]);
|
|
71
|
-
return (_jsxs("div", { className: alleenDialog ? "" : "space-y-3", children: [!alleenDialog && loading && _jsx("p", { className: "text-muted-foreground text-sm", children: "Laden\u2026" }), !alleenDialog && !loading && fout && (_jsx(Card, { children: _jsxs(CardContent, { className: "space-y-3 py-6", children: [_jsx("p", { className: "font-medium", children: "Geen verbinding" }), _jsx("p", { className: "text-muted-foreground text-sm", children: fout }), _jsxs(Button, { variant: "outline", onClick: onVernieuw, children: [_jsx(RefreshCw, { className: "mr-2 h-4 w-4" }), " Opnieuw proberen"] })] }) })), !alleenDialog && !loading && !fout && computers.length === 0 && (_jsx(Card, { children: _jsxs(CardContent, { className: "space-y-4 py-8", children: [_jsx(PrintProgrammaUitleg, {}), _jsxs(Button, { onClick: () => onToevoegenOpenChange(true), children: [_jsx(Plus, { className: "mr-2 h-4 w-4" }), " Computer toevoegen"] })] }) })), !alleenDialog && computers.map((c) => (_jsxs("div", { className: "bg-card flex items-center gap-4 rounded-xl border p-4", children: [_jsx("div", { className: "bg-muted rounded-lg p-2.5", children: _jsx(Monitor, { className: "text-muted-foreground h-5 w-5" }) }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("p", { className: "truncate font-medium", children: c.naam }), _jsxs("p", { className: "text-muted-foreground text-sm", children: [c.aantalGevonden, " ", c.aantalGevonden === 1 ? "printer gevonden" : "printers gevonden"] })] }), _jsx("span", { className: `rounded-full px-2.5 py-1 text-xs font-medium ${c.online ? "bg-primary/10 text-primary" : "bg-muted text-muted-foreground"}`, children: c.online ? "Aan" : "Uit" }), _jsx(Button, { variant: "ghost", size: "sm", onClick: () => onPrintersToevoegen(c.naam), children: "Printers toevoegen" })] }, c.id))), !alleenDialog && computers.length > 0 && _jsx(WerktHetNiet, {}), _jsx(Dialog, { open: toevoegenOpen, onOpenChange: onToevoegenOpenChange, children: _jsxs(DialogContent, { className: "sm:max-w-lg", children: [_jsxs(DialogHeader, { children: [_jsx(DialogTitle, { children: "Computer toevoegen" }), _jsx(DialogDescription, { children: "Zet het printprogramma klaar op de computer waar de printers aan hangen." })] }), _jsx(PrintProgrammaUitleg, { compact: true }), _jsx("div", { className: "rounded-lg border p-4 text-sm", children:
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
71
|
+
return (_jsxs("div", { className: alleenDialog ? "" : "space-y-3", children: [!alleenDialog && loading && _jsx("p", { className: "text-muted-foreground text-sm", children: "Laden\u2026" }), !alleenDialog && !loading && fout && (_jsx(Card, { children: _jsxs(CardContent, { className: "space-y-3 py-6", children: [_jsx("p", { className: "font-medium", children: "Geen verbinding" }), _jsx("p", { className: "text-muted-foreground text-sm", children: fout }), _jsxs(Button, { variant: "outline", onClick: onVernieuw, children: [_jsx(RefreshCw, { className: "mr-2 h-4 w-4" }), " Opnieuw proberen"] })] }) })), !alleenDialog && !loading && !fout && computers.length === 0 && (_jsx(Card, { children: _jsxs(CardContent, { className: "space-y-4 py-8", children: [_jsx(PrintProgrammaUitleg, {}), _jsxs(Button, { onClick: () => onToevoegenOpenChange(true), children: [_jsx(Plus, { className: "mr-2 h-4 w-4" }), " Computer toevoegen"] })] }) })), !alleenDialog && computers.map((c) => (_jsxs("div", { className: "bg-card flex items-center gap-4 rounded-xl border p-4", children: [_jsx("div", { className: "bg-muted rounded-lg p-2.5", children: _jsx(Monitor, { className: "text-muted-foreground h-5 w-5" }) }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("p", { className: "truncate font-medium", children: c.naam }), _jsxs("p", { className: "text-muted-foreground text-sm", children: [c.aantalGevonden, " ", c.aantalGevonden === 1 ? "printer gevonden" : "printers gevonden"] })] }), _jsx("span", { className: `rounded-full px-2.5 py-1 text-xs font-medium ${c.online ? "bg-primary/10 text-primary" : "bg-muted text-muted-foreground"}`, children: c.online ? "Aan" : "Uit" }), _jsx(Button, { variant: "ghost", size: "sm", onClick: () => onPrintersToevoegen(c.naam), children: "Printers toevoegen" })] }, c.id))), !alleenDialog && computers.length > 0 && _jsx(WerktHetNiet, {}), _jsx(Dialog, { open: toevoegenOpen, onOpenChange: onToevoegenOpenChange, children: _jsxs(DialogContent, { className: "sm:max-w-lg", children: [_jsxs(DialogHeader, { children: [_jsx(DialogTitle, { children: "Computer toevoegen" }), _jsx(DialogDescription, { children: "Zet het printprogramma klaar op de computer waar de printers aan hangen." })] }), _jsx(PrintProgrammaUitleg, { compact: true }), _jsx("div", { className: "space-y-2 rounded-lg border p-4 text-sm", children: computers.length === 0 ? (_jsxs("p", { className: "text-muted-foreground flex items-center gap-2", children: [_jsx(Loader2, { className: "h-4 w-4 animate-spin" }), " We wachten tot de computer zich meldt\u2026"] })) : (_jsxs(_Fragment, { children: [_jsx("p", { className: "font-medium", children: nieuw ? `Nieuwe computer gevonden: ${nieuw.naam}` : "Gevonden computers" }), computers.map((c) => (_jsxs("div", { className: "flex items-center gap-3 rounded-md border p-2", children: [_jsx(Monitor, { className: "text-muted-foreground h-4 w-4" }), _jsx("span", { className: "min-w-0 flex-1 truncate", children: c.naam }), _jsx("span", { className: "text-muted-foreground text-xs", children: c.online ? "Aan" : "Uit" }), _jsx(Button, { size: "sm", onClick: () => {
|
|
72
|
+
onToevoegenOpenChange(false);
|
|
73
|
+
onPrintersToevoegen(c.naam);
|
|
74
|
+
}, children: "Printers toevoegen" })] }, c.id))), _jsx("p", { className: "text-muted-foreground text-xs", children: "Staat jouw Mac er niet bij? Controleer of het printprogramma draait en of je daar met dezelfde beheerdergegevens bent ingelogd." })] })) }), _jsx("div", { className: "flex justify-end", children: _jsx(Button, { variant: "outline", onClick: () => onToevoegenOpenChange(false), children: "Sluiten" }) }), _jsx(WerktHetNiet, {})] }) })] }));
|
|
75
75
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DocumentenPage.d.ts","sourceRoot":"","sources":["../../src/components/DocumentenPage.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"DocumentenPage.d.ts","sourceRoot":"","sources":["../../src/components/DocumentenPage.tsx"],"names":[],"mappings":"AAiBA,wBAAgB,cAAc,CAAC,EAAE,GAAe,EAAE,EAAE;IAAE,GAAG,CAAC,EAAE,SAAS,GAAG,OAAO,CAAA;CAAE,+BAiBhF"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
// ============================================================================
|
|
3
3
|
// src/components/DocumentenPage.tsx — één menu-item "Documenten" met tabbladen
|
|
4
4
|
// voor de documentontwerpen en de logobibliotheek.
|
|
@@ -8,11 +8,12 @@ import { FileText, Image } from "lucide-react";
|
|
|
8
8
|
import { ModuleTabs } from "./ModuleTabs.js";
|
|
9
9
|
import { LayoutsPage } from "./LayoutsPage.js";
|
|
10
10
|
import { LogosPage } from "./LogosPage.js";
|
|
11
|
+
import { PdfMapAutoSaver } from "./PdfMapAutoSaver.js";
|
|
11
12
|
const TABS = [
|
|
12
13
|
{ id: "layouts", label: "Documenten", icon: FileText },
|
|
13
14
|
{ id: "logos", label: "Logo's", icon: Image },
|
|
14
15
|
];
|
|
15
16
|
export function DocumentenPage({ tab = "layouts" }) {
|
|
16
17
|
const [actiefTab, setActiefTab] = useState(tab);
|
|
17
|
-
return (_jsx(ModuleTabs, { titel: "Documenten", omschrijving: "Ontwerp pakbonnen, facturen, stickers en beheer de logo's die daarop staan.", tabs: TABS, actiefTab: actiefTab, onTabWijzig: (nieuwTab) => setActiefTab(nieuwTab === "logos" ? "logos" : "layouts"), children: actiefTab === "logos" ? _jsx(LogosPage, { ingebed: true }) : _jsx(LayoutsPage, { ingebed: true }) }));
|
|
18
|
+
return (_jsxs(_Fragment, { children: [_jsx(PdfMapAutoSaver, {}), _jsx(ModuleTabs, { titel: "Documenten", omschrijving: "Ontwerp pakbonnen, facturen, stickers en beheer de logo's die daarop staan.", tabs: TABS, actiefTab: actiefTab, onTabWijzig: (nieuwTab) => setActiefTab(nieuwTab === "logos" ? "logos" : "layouts"), children: actiefTab === "logos" ? _jsx(LogosPage, { ingebed: true }) : _jsx(LayoutsPage, { ingebed: true }) })] }));
|
|
18
19
|
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Printerkeuze voor één layout (zelfde koppeling als de kolom op de
|
|
3
|
+
* Documenten-pagina). Compact, bedoeld voor de header van de builders.
|
|
4
|
+
*/
|
|
5
|
+
export declare function LayoutPrinterSelect({ layoutId }: {
|
|
6
|
+
layoutId: string;
|
|
7
|
+
}): import("react").JSX.Element;
|
|
8
|
+
//# sourceMappingURL=LayoutPrinterSelect.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LayoutPrinterSelect.d.ts","sourceRoot":"","sources":["../../src/components/LayoutPrinterSelect.tsx"],"names":[],"mappings":"AAUA;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,EAAE,QAAQ,EAAE,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,+BAgCrE"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, toast, } from "@flowselections/core";
|
|
3
|
+
import { usePrinters, zetLayoutPrinter } from "../lib/useDocumentData.js";
|
|
4
|
+
/**
|
|
5
|
+
* Printerkeuze voor één layout (zelfde koppeling als de kolom op de
|
|
6
|
+
* Documenten-pagina). Compact, bedoeld voor de header van de builders.
|
|
7
|
+
*/
|
|
8
|
+
export function LayoutPrinterSelect({ layoutId }) {
|
|
9
|
+
const { printers, koppelingen, reload } = usePrinters();
|
|
10
|
+
const huidige = koppelingen.find((k) => k.layout_id === layoutId)?.printer_id ?? "geen";
|
|
11
|
+
return (_jsxs(Select, { value: huidige, onValueChange: async (printerId) => {
|
|
12
|
+
try {
|
|
13
|
+
await zetLayoutPrinter(layoutId, printerId === "geen" ? null : printerId);
|
|
14
|
+
await reload(true);
|
|
15
|
+
toast.success("Printer bij document opgeslagen");
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
toast.error("Printerkeuze opslaan mislukt");
|
|
19
|
+
}
|
|
20
|
+
}, children: [_jsx(SelectTrigger, { className: "h-8 w-48 text-sm", "aria-label": "Printer bij dit document", children: _jsx(SelectValue, { placeholder: "Geen printer" }) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "geen", children: "Geen printer" }), printers
|
|
21
|
+
.filter((p) => p.actief)
|
|
22
|
+
.map((printer) => (_jsx(SelectItem, { value: printer.id, children: printer.naam }, printer.id)))] })] }));
|
|
23
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LayoutsPage.d.ts","sourceRoot":"","sources":["../../src/components/LayoutsPage.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"LayoutsPage.d.ts","sourceRoot":"","sources":["../../src/components/LayoutsPage.tsx"],"names":[],"mappings":"AAkEA,wBAAgB,WAAW,CAAC,EAAE,OAAe,EAAE,GAAE;IAAE,OAAO,CAAC,EAAE,OAAO,CAAA;CAAO,+BA8mB1E"}
|
|
@@ -4,7 +4,7 @@ import { useAutoRefresh } from "../lib/useAutoRefresh.js";
|
|
|
4
4
|
import { useNavigate } from "@tanstack/react-router";
|
|
5
5
|
import { Badge, Button, Card, CardContent, CardDescription, CardHeader, CardTitle, Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, Input, Label, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, Separator, Switch, Table, TableBody, TableCell, TableHead, TableHeader, TableRow, Textarea, toast, } from "@flowselections/core";
|
|
6
6
|
import { Copy, FileText, Pencil, Plus, Search, Settings2, Trash2 } from "lucide-react";
|
|
7
|
-
import { dupliceerLayout, maakLayout, updateLayout, useLayouts, verwijderLayout, } from "../lib/useDocumentData.js";
|
|
7
|
+
import { dupliceerLayout, maakLayout, updateLayout, useLayouts, usePrinters, zetLayoutPrinter, verwijderLayout, } from "../lib/useDocumentData.js";
|
|
8
8
|
import { DOCUMENT_TYPES, FLORIDAY_LAYOUT_TOELICHTING, PAPIERFORMATEN, documentTypeLabel, isFloridayLayout, } from "../lib/documentTypes.js";
|
|
9
9
|
import { DOCUMENT_PROFIELEN, documentProfiel, primaireProfielen, standaardConfig, } from "../lib/documentProfiles.js";
|
|
10
10
|
function klantOntwerpLayout(l) {
|
|
@@ -15,7 +15,11 @@ function klantOntwerpLayout(l) {
|
|
|
15
15
|
}
|
|
16
16
|
export function LayoutsPage({ ingebed = false } = {}) {
|
|
17
17
|
const { layouts, loading, reload } = useLayouts();
|
|
18
|
-
|
|
18
|
+
const { printers, koppelingen, reload: reloadPrinters } = usePrinters();
|
|
19
|
+
useAutoRefresh(() => {
|
|
20
|
+
void reload(true);
|
|
21
|
+
void reloadPrinters(true);
|
|
22
|
+
}, 30000);
|
|
19
23
|
const navigate = useNavigate();
|
|
20
24
|
const [zoek, setZoek] = useState("");
|
|
21
25
|
const [typeFilter, setTypeFilter] = useState("alle");
|
|
@@ -145,7 +149,16 @@ export function LayoutsPage({ ingebed = false } = {}) {
|
|
|
145
149
|
}
|
|
146
150
|
};
|
|
147
151
|
const Wrapper = ingebed ? "div" : "main";
|
|
148
|
-
return (_jsxs(Wrapper, { className: ingebed ? "space-y-6" : "p-6 space-y-6", children: [_jsxs("div", { className: "flex flex-wrap items-end justify-between gap-4", children: [!ingebed && (_jsxs("div", { children: [_jsx("h1", { className: "text-xl font-semibold", children: "Documentlayouts" }), _jsx("p", { className: "text-sm text-muted-foreground", children: "Ontwerp pakbonnen, facturen, etiketten en alle andere documenten." })] })), _jsx("div", { className: "ml-auto flex gap-2", children: _jsxs(Button, { onClick: () => setWizardOpen(true), children: [_jsx(Plus, { className: "mr-2 h-4 w-4" }), " Nieuwe layout"] }) })] }), _jsxs(Card, { children: [_jsxs(CardHeader, { className: "pb-3", children: [_jsx(CardTitle, { className: "text-base", children: "Overzicht" }), _jsx(CardDescription, { children: loading ? "Laden…" : `${gefilterd.length} van ${layouts.length} layouts` })] }), _jsxs(CardContent, { className: "space-y-4", children: [_jsxs("div", { className: "grid gap-3 md:grid-cols-5", children: [_jsxs("div", { className: "relative md:col-span-2", children: [_jsx(Search, { className: "absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" }), _jsx(Input, { className: "pl-9", placeholder: "Zoek op naam of omschrijving", value: zoek, onChange: (e) => setZoek(e.target.value) })] }), _jsxs(Select, { value: typeFilter, onValueChange: setTypeFilter, children: [_jsx(SelectTrigger, { children: _jsx(SelectValue, { placeholder: "Documenttype" }) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "alle", children: "Alle documenttypes" }), DOCUMENT_TYPES.map((d) => (_jsx(SelectItem, { value: d.value, children: d.label }, d.value)))] })] }), _jsxs(Select, { value: statusFilter, onValueChange: setStatusFilter, children: [_jsx(SelectTrigger, { children: _jsx(SelectValue, { placeholder: "Status" }) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "alle", children: "Alle statussen" }), _jsx(SelectItem, { value: "actief", children: "Actief" }), _jsx(SelectItem, { value: "inactief", children: "Inactief" })] })] }), _jsxs(Select, { value: bronFilter, onValueChange: setBronFilter, children: [_jsx(SelectTrigger, { children: _jsx(SelectValue, { placeholder: "Herkomst" }) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "alle", children: "Alle herkomsten" }), _jsx(SelectItem, { value: "eigen", children: "Eigen ontwerp" }), _jsx(SelectItem, { value: "floriday", children: "Uit Floriday" })] })] })] }), _jsxs(Table, { children: [_jsx(TableHeader, { children: _jsxs(TableRow, { children: [_jsx(TableHead, { children: "Naam" }), _jsx(TableHead, { children: "Documenttype" }), _jsx(TableHead, { children: "Formaat" }), _jsx(TableHead, { children: "Actief" }), _jsx(TableHead, { className: "text-right", children: "Acties" })] }) }), _jsxs(TableBody, { children: [gefilterd.length === 0 && (_jsx(TableRow, { children: _jsx(TableCell, { colSpan:
|
|
152
|
+
return (_jsxs(Wrapper, { className: ingebed ? "space-y-6" : "p-6 space-y-6", children: [_jsxs("div", { className: "flex flex-wrap items-end justify-between gap-4", children: [!ingebed && (_jsxs("div", { children: [_jsx("h1", { className: "text-xl font-semibold", children: "Documentlayouts" }), _jsx("p", { className: "text-sm text-muted-foreground", children: "Ontwerp pakbonnen, facturen, etiketten en alle andere documenten." })] })), _jsx("div", { className: "ml-auto flex gap-2", children: _jsxs(Button, { onClick: () => setWizardOpen(true), children: [_jsx(Plus, { className: "mr-2 h-4 w-4" }), " Nieuwe layout"] }) })] }), _jsxs(Card, { children: [_jsxs(CardHeader, { className: "pb-3", children: [_jsx(CardTitle, { className: "text-base", children: "Overzicht" }), _jsx(CardDescription, { children: loading ? "Laden…" : `${gefilterd.length} van ${layouts.length} layouts` })] }), _jsxs(CardContent, { className: "space-y-4", children: [_jsxs("div", { className: "grid gap-3 md:grid-cols-5", children: [_jsxs("div", { className: "relative md:col-span-2", children: [_jsx(Search, { className: "absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" }), _jsx(Input, { className: "pl-9", placeholder: "Zoek op naam of omschrijving", value: zoek, onChange: (e) => setZoek(e.target.value) })] }), _jsxs(Select, { value: typeFilter, onValueChange: setTypeFilter, children: [_jsx(SelectTrigger, { children: _jsx(SelectValue, { placeholder: "Documenttype" }) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "alle", children: "Alle documenttypes" }), DOCUMENT_TYPES.map((d) => (_jsx(SelectItem, { value: d.value, children: d.label }, d.value)))] })] }), _jsxs(Select, { value: statusFilter, onValueChange: setStatusFilter, children: [_jsx(SelectTrigger, { children: _jsx(SelectValue, { placeholder: "Status" }) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "alle", children: "Alle statussen" }), _jsx(SelectItem, { value: "actief", children: "Actief" }), _jsx(SelectItem, { value: "inactief", children: "Inactief" })] })] }), _jsxs(Select, { value: bronFilter, onValueChange: setBronFilter, children: [_jsx(SelectTrigger, { children: _jsx(SelectValue, { placeholder: "Herkomst" }) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "alle", children: "Alle herkomsten" }), _jsx(SelectItem, { value: "eigen", children: "Eigen ontwerp" }), _jsx(SelectItem, { value: "floriday", children: "Uit Floriday" })] })] })] }), _jsxs(Table, { children: [_jsx(TableHeader, { children: _jsxs(TableRow, { children: [_jsx(TableHead, { children: "Naam" }), _jsx(TableHead, { children: "Documenttype" }), _jsx(TableHead, { children: "Formaat" }), _jsx(TableHead, { children: "Printer bij document" }), _jsx(TableHead, { children: "Actief" }), _jsx(TableHead, { className: "text-right", children: "Acties" })] }) }), _jsxs(TableBody, { children: [gefilterd.length === 0 && (_jsx(TableRow, { children: _jsx(TableCell, { colSpan: 6, className: "py-10 text-center text-sm text-muted-foreground", children: "Geen layouts gevonden." }) })), gefilterd.map((l) => (_jsxs(TableRow, { children: [_jsxs(TableCell, { children: [_jsxs("div", { className: "flex items-center gap-2 font-medium", children: [_jsx(FileText, { className: "h-4 w-4 text-muted-foreground" }), l.naam, l.is_standaard && _jsx(Badge, { variant: "secondary", children: "Standaard" }), isFloridayLayout(l) && _jsx(Badge, { variant: "outline", children: "Uit Floriday" })] }), (l.omschrijving || isFloridayLayout(l)) && (_jsx("span", { className: "text-xs text-muted-foreground", children: isFloridayLayout(l) ? FLORIDAY_LAYOUT_TOELICHTING : l.omschrijving }))] }), _jsx(TableCell, { children: documentTypeLabel(l.documenttype) }), _jsxs(TableCell, { className: "text-sm text-muted-foreground", children: [l.papierformaat, " \u00B7 ", Number(l.breedte_mm), " \u00D7 ", Number(l.hoogte_mm), " mm"] }), _jsx(TableCell, { className: "min-w-52", children: _jsxs(Select, { value: koppelingen.find((k) => k.layout_id === l.id)?.printer_id ?? "geen", onValueChange: async (printerId) => {
|
|
153
|
+
try {
|
|
154
|
+
await zetLayoutPrinter(l.id, printerId === "geen" ? null : printerId);
|
|
155
|
+
await reloadPrinters(true);
|
|
156
|
+
toast.success("Printer bij document opgeslagen");
|
|
157
|
+
}
|
|
158
|
+
catch {
|
|
159
|
+
toast.error("Printerkeuze opslaan mislukt");
|
|
160
|
+
}
|
|
161
|
+
}, children: [_jsx(SelectTrigger, { "aria-label": `Printer voor ${l.naam}`, children: _jsx(SelectValue, { placeholder: "Geen printer" }) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "geen", children: "Geen printer" }), printers.filter((p) => p.actief).map((printer) => (_jsx(SelectItem, { value: printer.id, children: printer.naam }, printer.id)))] })] }) }), _jsx(TableCell, { children: _jsx(Switch, { checked: l.actief, onCheckedChange: async (v) => {
|
|
149
162
|
await updateLayout(l.id, { actief: v });
|
|
150
163
|
void reload();
|
|
151
164
|
} }) }), _jsx(TableCell, { className: "text-right", children: _jsxs("div", { className: "flex justify-end gap-1", children: [isFloridayLayout(l) ? (_jsx("span", { className: "text-xs text-muted-foreground", children: "Niet aanpasbaar \u2014 komt uit Floriday" })) : klantOntwerpLayout(l) ? (_jsx(Badge, { variant: "outline", children: "Ontwerp van klant" })) : (_jsxs(Button, { variant: "ghost", size: "sm", onClick: () => navigate(l.documenttype === "productiebon"
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
export declare function NetwerkPrintersDialog({ open, onOpenChange, huidigePrinterId, huidigePrinterNaam, onKlaar, }: {
|
|
2
|
-
open: boolean;
|
|
3
|
-
onOpenChange: (v: boolean) => void;
|
|
4
|
-
huidigePrinterId?: string | null;
|
|
5
|
-
huidigePrinterNaam?: string | null;
|
|
6
|
-
onKlaar?: () => void;
|
|
7
|
-
}): import("react/jsx-runtime").JSX.Element;
|
|
1
|
+
export declare function NetwerkPrintersDialog({ open, onOpenChange, huidigePrinterId, huidigePrinterNaam, onKlaar, }: {
|
|
2
|
+
open: boolean;
|
|
3
|
+
onOpenChange: (v: boolean) => void;
|
|
4
|
+
huidigePrinterId?: string | null;
|
|
5
|
+
huidigePrinterNaam?: string | null;
|
|
6
|
+
onKlaar?: () => void;
|
|
7
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
8
8
|
//# sourceMappingURL=NetwerkPrintersDialog.d.ts.map
|
|
@@ -3,13 +3,14 @@ type Item = {
|
|
|
3
3
|
id: string;
|
|
4
4
|
node: ReactNode;
|
|
5
5
|
breakBefore?: boolean;
|
|
6
|
+
pageLabel?: string;
|
|
6
7
|
};
|
|
7
8
|
/**
|
|
8
9
|
* Verdeelt losse blokken over echte pagina's van het gekozen formaat en toont
|
|
9
10
|
* er telkens één, met pijltjes om te bladeren. De pagina schaalt automatisch
|
|
10
11
|
* mee zodat hij volledig binnen het kader past.
|
|
11
12
|
*/
|
|
12
|
-
export declare function PaginaDocument({ items, breedteMm, hoogteMm, padMm, leeg, voet, }: {
|
|
13
|
+
export declare function PaginaDocument({ items, breedteMm, hoogteMm, padMm, leeg, voet, kop, }: {
|
|
13
14
|
items: Item[];
|
|
14
15
|
breedteMm: number;
|
|
15
16
|
hoogteMm: number;
|
|
@@ -17,6 +18,8 @@ export declare function PaginaDocument({ items, breedteMm, hoogteMm, padMm, leeg
|
|
|
17
18
|
leeg?: ReactNode;
|
|
18
19
|
/** Voettekst die onder elke pagina komt te staan. */
|
|
19
20
|
voet?: (pagina: number, totaal: number) => ReactNode;
|
|
21
|
+
/** Kop die op iedere pagina opnieuw wordt getoond. */
|
|
22
|
+
kop?: (label?: string) => ReactNode;
|
|
20
23
|
}): import("react").JSX.Element;
|
|
21
24
|
export {};
|
|
22
25
|
//# sourceMappingURL=PaginaDocument.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PaginaDocument.d.ts","sourceRoot":"","sources":["../../src/components/PaginaDocument.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAgD,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAIrF,KAAK,IAAI,GAAG;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,SAAS,CAAC;IAAC,WAAW,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"PaginaDocument.d.ts","sourceRoot":"","sources":["../../src/components/PaginaDocument.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAgD,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAIrF,KAAK,IAAI,GAAG;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,SAAS,CAAC;IAAC,WAAW,CAAC,EAAE,OAAO,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAEvF;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,EAC7B,KAAK,EACL,SAAS,EACT,QAAQ,EACR,KAAU,EACV,IAAI,EACJ,IAAI,EACJ,GAAG,GACJ,EAAE;IACD,KAAK,EAAE,IAAI,EAAE,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,qDAAqD;IACrD,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,SAAS,CAAC;IACrD,sDAAsD;IACtD,GAAG,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,MAAM,KAAK,SAAS,CAAC;CACrC,+BAwJA"}
|
|
@@ -7,10 +7,11 @@ import { ChevronLeft, ChevronRight } from "lucide-react";
|
|
|
7
7
|
* er telkens één, met pijltjes om te bladeren. De pagina schaalt automatisch
|
|
8
8
|
* mee zodat hij volledig binnen het kader past.
|
|
9
9
|
*/
|
|
10
|
-
export function PaginaDocument({ items, breedteMm, hoogteMm, padMm = 12, leeg, voet, }) {
|
|
10
|
+
export function PaginaDocument({ items, breedteMm, hoogteMm, padMm = 12, leeg, voet, kop, }) {
|
|
11
11
|
const meetRef = useRef(null);
|
|
12
12
|
const mmRef = useRef(null);
|
|
13
13
|
const voetMeetRef = useRef(null);
|
|
14
|
+
const kopMeetRef = useRef(null);
|
|
14
15
|
const buiten = useRef(null);
|
|
15
16
|
const pagina = useRef(null);
|
|
16
17
|
const [paginas, setPaginas] = useState([items]);
|
|
@@ -26,7 +27,10 @@ export function PaginaDocument({ items, breedteMm, hoogteMm, padMm = 12, leeg, v
|
|
|
26
27
|
if (!pxPerMm)
|
|
27
28
|
return;
|
|
28
29
|
const voetPx = voetMeetRef.current?.getBoundingClientRect().height ?? 0;
|
|
29
|
-
const
|
|
30
|
+
const kopPx = kopMeetRef.current?.getBoundingClientRect().height ?? 0;
|
|
31
|
+
const maxPx = (hoogteMm - padMm * 2) * pxPerMm -
|
|
32
|
+
(voetPx ? voetPx + 8 : 0) -
|
|
33
|
+
(kopPx ? kopPx + 12 : 0);
|
|
30
34
|
const kinderen = Array.from(meet.children);
|
|
31
35
|
const result = [];
|
|
32
36
|
let huidig = [];
|
|
@@ -49,7 +53,7 @@ export function PaginaDocument({ items, breedteMm, hoogteMm, padMm = 12, leeg, v
|
|
|
49
53
|
result.push(huidig);
|
|
50
54
|
setPaginas(result.length ? result : [[]]);
|
|
51
55
|
setIndex((i) => Math.min(i, Math.max(0, result.length - 1)));
|
|
52
|
-
}, [items, hoogteMm, breedteMm, padMm, voet]);
|
|
56
|
+
}, [items, hoogteMm, breedteMm, padMm, voet, kop]);
|
|
53
57
|
// Pagina passend schalen op breedte; hoogte mag doorlopen zodat je kunt scrollen.
|
|
54
58
|
const pas = () => {
|
|
55
59
|
const b = buiten.current;
|
|
@@ -69,12 +73,13 @@ export function PaginaDocument({ items, breedteMm, hoogteMm, padMm = 12, leeg, v
|
|
|
69
73
|
}, []);
|
|
70
74
|
const totaal = paginas.length;
|
|
71
75
|
const huidige = paginas[Math.min(index, totaal - 1)] ?? [];
|
|
72
|
-
|
|
76
|
+
const huidigLabel = huidige.find((item) => item.pageLabel)?.pageLabel;
|
|
77
|
+
return (_jsxs("div", { className: "flex h-full w-full flex-col gap-2", children: [_jsxs("div", { className: "pointer-events-none absolute -z-10 opacity-0", "aria-hidden": true, children: [_jsx("div", { ref: mmRef, style: { height: "100mm", width: "1px" } }), _jsx("div", { ref: meetRef, className: "bg-white text-[11px] text-neutral-900", style: { width: `${breedteMm - padMm * 2}mm` }, children: items.map((it) => (_jsx("div", { children: it.node }, it.id))) }), _jsx("div", { ref: kopMeetRef, className: "bg-white text-[11px] text-neutral-900", style: { width: `${breedteMm - padMm * 2}mm` }, children: kop?.("Hele kar") }), _jsx("div", { ref: voetMeetRef, className: "bg-white text-[11px] text-neutral-900", style: { width: `${breedteMm - padMm * 2}mm` }, children: voet?.(1, 1) })] }), _jsx("div", { ref: buiten, className: "flex min-h-0 flex-1 items-start justify-center overflow-auto", children: _jsxs("div", { ref: pagina, className: "flex flex-col bg-white text-[11px] text-neutral-900 shadow-sm", style: {
|
|
73
78
|
width: `${breedteMm}mm`,
|
|
74
79
|
height: `${hoogteMm}mm`,
|
|
75
80
|
padding: `${padMm}mm`,
|
|
76
81
|
transform: `scale(${schaal})`,
|
|
77
82
|
transformOrigin: "top center",
|
|
78
83
|
overflow: "hidden",
|
|
79
|
-
}, children: [_jsxs("div", { className: "min-h-0 flex-1 space-y-3", children: [huidige.map((it) => (_jsx("div", { children: it.node }, it.id))), huidige.length === 0 && (_jsx("p", { className: "py-10 text-center text-neutral-400", children: leeg ?? "Nog niets te tonen." }))] }), voet && _jsx("div", { className: "mt-2 shrink-0", children: voet(Math.min(index + 1, totaal), totaal) })] }) }), _jsxs("div", { className: "flex shrink-0 items-center justify-center gap-3", children: [_jsx(Button, { type: "button", variant: "outline", size: "icon", onClick: () => setIndex((i) => Math.max(0, i - 1)), disabled: index <= 0, "aria-label": "Vorige pagina", children: _jsx(ChevronLeft, { className: "h-4 w-4" }) }), _jsxs("span", { className: "text-xs text-muted-foreground", children: ["Pagina ", Math.min(index + 1, totaal), " van ", totaal] }), _jsx(Button, { type: "button", variant: "outline", size: "icon", onClick: () => setIndex((i) => Math.min(totaal - 1, i + 1)), disabled: index >= totaal - 1, "aria-label": "Volgende pagina", children: _jsx(ChevronRight, { className: "h-4 w-4" }) })] })] }));
|
|
84
|
+
}, children: [kop?.(huidigLabel), _jsxs("div", { className: "min-h-0 flex-1 space-y-3", children: [huidige.map((it) => (_jsx("div", { children: it.node }, it.id))), huidige.length === 0 && (_jsx("p", { className: "py-10 text-center text-neutral-400", children: leeg ?? "Nog niets te tonen." }))] }), voet && _jsx("div", { className: "mt-2 shrink-0", children: voet(Math.min(index + 1, totaal), totaal) })] }) }), _jsxs("div", { className: "flex shrink-0 items-center justify-center gap-3", children: [_jsx(Button, { type: "button", variant: "outline", size: "icon", onClick: () => setIndex((i) => Math.max(0, i - 1)), disabled: index <= 0, "aria-label": "Vorige pagina", children: _jsx(ChevronLeft, { className: "h-4 w-4" }) }), _jsxs("span", { className: "text-xs text-muted-foreground", children: ["Pagina ", Math.min(index + 1, totaal), " van ", totaal] }), _jsx(Button, { type: "button", variant: "outline", size: "icon", onClick: () => setIndex((i) => Math.min(totaal - 1, i + 1)), disabled: index >= totaal - 1, "aria-label": "Volgende pagina", children: _jsx(ChevronRight, { className: "h-4 w-4" }) })] })] }));
|
|
80
85
|
}
|
|
@@ -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":"AA+BA,iFAAiF;AACjF,wBAAgB,iBAAiB,CAAC,EAAE,QAAQ,EAAE,YAAY,EAAE,GAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAO,+BAqKvF"}
|