@flowselections/layout-maker 1.38.4 → 1.45.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (110) hide show
  1. package/dist-lib/components/AfleverbriefBuilderPage.d.ts +10 -0
  2. package/dist-lib/components/AfleverbriefBuilderPage.d.ts.map +1 -0
  3. package/dist-lib/components/AfleverbriefBuilderPage.js +101 -0
  4. package/dist-lib/components/BlokOntwerperPage.d.ts.map +1 -1
  5. package/dist-lib/components/BlokOntwerperPage.js +8 -3
  6. package/dist-lib/components/ComputersLijst.d.ts.map +1 -1
  7. package/dist-lib/components/ComputersLijst.js +1 -1
  8. package/dist-lib/components/FustKolomVolgorde.d.ts +11 -0
  9. package/dist-lib/components/FustKolomVolgorde.d.ts.map +1 -0
  10. package/dist-lib/components/FustKolomVolgorde.js +52 -0
  11. package/dist-lib/components/LayoutsPage.d.ts.map +1 -1
  12. package/dist-lib/components/LayoutsPage.js +10 -9
  13. package/dist-lib/components/PakbonBuilderPage.d.ts.map +1 -1
  14. package/dist-lib/components/PakbonBuilderPage.js +32 -14
  15. package/dist-lib/components/PakbonPreview.d.ts.map +1 -1
  16. package/dist-lib/components/PakbonPreview.js +21 -14
  17. package/dist-lib/components/PakbonSectiesPanel.d.ts +4 -2
  18. package/dist-lib/components/PakbonSectiesPanel.d.ts.map +1 -1
  19. package/dist-lib/components/PakbonSectiesPanel.js +5 -3
  20. package/dist-lib/components/PdfMapToestemmingBanner.d.ts +2 -1
  21. package/dist-lib/components/PdfMapToestemmingBanner.d.ts.map +1 -1
  22. package/dist-lib/components/PdfMapToestemmingBanner.js +124 -18
  23. package/dist-lib/components/PdfVoorbeeld.d.ts +20 -0
  24. package/dist-lib/components/PdfVoorbeeld.d.ts.map +1 -0
  25. package/dist-lib/components/PdfVoorbeeld.js +95 -0
  26. package/dist-lib/components/PrintProgrammaUitleg.d.ts.map +1 -1
  27. package/dist-lib/components/PrintProgrammaUitleg.js +1 -1
  28. package/dist-lib/components/PrintQueuePage.d.ts.map +1 -1
  29. package/dist-lib/components/PrintQueuePage.js +81 -43
  30. package/dist-lib/components/PrintplekDetailPage.d.ts.map +1 -1
  31. package/dist-lib/components/PrintplekDetailPage.js +2 -2
  32. package/dist-lib/components/PrintplekWizard.d.ts.map +1 -1
  33. package/dist-lib/components/PrintplekWizard.js +6 -2
  34. package/dist-lib/components/ProductiebonBuilderPage.d.ts.map +1 -1
  35. package/dist-lib/components/ProductiebonBuilderPage.js +13 -7
  36. package/dist-lib/index.d.ts +13 -5
  37. package/dist-lib/index.d.ts.map +1 -1
  38. package/dist-lib/index.js +10 -5
  39. package/dist-lib/integrations/supabase/auth-middleware.d.ts +2280 -198
  40. package/dist-lib/integrations/supabase/auth-middleware.d.ts.map +1 -1
  41. package/dist-lib/integrations/supabase/client.server.d.ts +2280 -198
  42. package/dist-lib/integrations/supabase/client.server.d.ts.map +1 -1
  43. package/dist-lib/integrations/supabase/client.server.js +1 -1
  44. package/dist-lib/integrations/supabase/moduleClient.d.ts +2280 -198
  45. package/dist-lib/integrations/supabase/moduleClient.d.ts.map +1 -1
  46. package/dist-lib/integrations/supabase/types.d.ts +2306 -182
  47. package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
  48. package/dist-lib/integrations/supabase/types.js +1 -0
  49. package/dist-lib/lib/aiLayout.functions.d.ts +2280 -198
  50. package/dist-lib/lib/aiLayout.functions.d.ts.map +1 -1
  51. package/dist-lib/lib/documentVoorbeeld.functions.d.ts +23 -0
  52. package/dist-lib/lib/documentVoorbeeld.functions.d.ts.map +1 -0
  53. package/dist-lib/lib/documentVoorbeeld.functions.js +37 -0
  54. package/dist-lib/lib/fontMetrics.d.ts +15 -0
  55. package/dist-lib/lib/fontMetrics.d.ts.map +1 -0
  56. package/dist-lib/lib/fontMetrics.js +118 -0
  57. package/dist-lib/lib/htmlNaarPdf.server.d.ts +2 -2
  58. package/dist-lib/lib/htmlNaarPdf.server.d.ts.map +1 -1
  59. package/dist-lib/lib/htmlNaarPdf.server.js +2 -1
  60. package/dist-lib/lib/htmlPdf.server.d.ts +11 -0
  61. package/dist-lib/lib/htmlPdf.server.d.ts.map +1 -1
  62. package/dist-lib/lib/htmlPdf.server.js +118 -15
  63. package/dist-lib/lib/orderData.server.d.ts.map +1 -1
  64. package/dist-lib/lib/orderData.server.js +43 -16
  65. package/dist-lib/lib/pakbonBarcodeVerzoek.d.ts +39 -0
  66. package/dist-lib/lib/pakbonBarcodeVerzoek.d.ts.map +1 -0
  67. package/dist-lib/lib/pakbonBarcodeVerzoek.js +80 -0
  68. package/dist-lib/lib/pakbonBuilder.d.ts +62 -3
  69. package/dist-lib/lib/pakbonBuilder.d.ts.map +1 -1
  70. package/dist-lib/lib/pakbonBuilder.js +386 -50
  71. package/dist-lib/lib/pakbonContent.d.ts +41 -0
  72. package/dist-lib/lib/pakbonContent.d.ts.map +1 -0
  73. package/dist-lib/lib/pakbonContent.js +668 -0
  74. package/dist-lib/lib/pakbonDocument.d.ts +199 -0
  75. package/dist-lib/lib/pakbonDocument.d.ts.map +1 -0
  76. package/dist-lib/lib/pakbonDocument.js +735 -0
  77. package/dist-lib/lib/pakbonHtml.d.ts +7 -0
  78. package/dist-lib/lib/pakbonHtml.d.ts.map +1 -1
  79. package/dist-lib/lib/pakbonHtml.js +89 -73
  80. package/dist-lib/lib/pakbonPresentation.d.ts +8 -0
  81. package/dist-lib/lib/pakbonPresentation.d.ts.map +1 -0
  82. package/dist-lib/lib/pakbonPresentation.js +31 -0
  83. package/dist-lib/lib/pakbonScan.d.ts.map +1 -1
  84. package/dist-lib/lib/pakbonScan.js +20 -38
  85. package/dist-lib/lib/pdfJobSaver.d.ts +10 -6
  86. package/dist-lib/lib/pdfJobSaver.d.ts.map +1 -1
  87. package/dist-lib/lib/pdfJobSaver.js +302 -99
  88. package/dist-lib/lib/pdfMap.d.ts +37 -2
  89. package/dist-lib/lib/pdfMap.d.ts.map +1 -1
  90. package/dist-lib/lib/pdfMap.js +120 -7
  91. package/dist-lib/lib/pdfOpslagStatus.d.ts +23 -0
  92. package/dist-lib/lib/pdfOpslagStatus.d.ts.map +1 -0
  93. package/dist-lib/lib/pdfOpslagStatus.js +74 -0
  94. package/dist-lib/lib/printApi.d.ts +31 -0
  95. package/dist-lib/lib/printApi.d.ts.map +1 -1
  96. package/dist-lib/lib/printApi.js +62 -1
  97. package/dist-lib/lib/printQueue.server.d.ts +6 -0
  98. package/dist-lib/lib/printQueue.server.d.ts.map +1 -1
  99. package/dist-lib/lib/printQueue.server.js +450 -94
  100. package/dist-lib/lib/printService.functions.d.ts +8857 -529
  101. package/dist-lib/lib/printService.functions.d.ts.map +1 -1
  102. package/dist-lib/lib/profielHtml.d.ts.map +1 -1
  103. package/dist-lib/lib/profielHtml.js +8 -1
  104. package/dist-lib/lib/useDocumentData.d.ts +8 -0
  105. package/dist-lib/lib/useDocumentData.d.ts.map +1 -1
  106. package/dist-lib/lib/useDocumentData.js +1 -1
  107. package/dist-lib/lib/voorbeeldPrint.d.ts +3 -0
  108. package/dist-lib/lib/voorbeeldPrint.d.ts.map +1 -1
  109. package/dist-lib/lib/voorbeeldPrint.js +65 -17
  110. package/package.json +9 -2
@@ -1,21 +1,24 @@
1
- import { supabase } from "./supabaseClient.js";
2
1
  import { toast } from "@flowselections/core";
3
- import { bewaarInPdfMap, herstelPdfMapToestemming, heeftPdfMap, luisterNaarPdfMapWijziging, pdfMapToestemming, } from "./pdfMap.js";
4
- const BEWAARD_SLEUTEL = "layout-maker-pdf-bewaard";
2
+ import { supabase } from "./supabaseClient.js";
3
+ import { startPdfMapGlobaleBanner } from "../components/PdfMapGlobaleBanner.js";
4
+ import { bewaarInPdfMap, herstelPdfMapToestemming, koppelPdfMapAanPrintplek, luisterNaarPdfMapWijziging, lokalePdfPrintplek, pdfMapKoppeling, pdfMapToestemming, } from "./pdfMap.js";
5
+ import { PDF_OPSLAG_MELDING } from "./pdfOpslagStatus.js";
6
+ const SERVICE_INTERVAL_MS = 5000;
5
7
  const WACHT_INTERVAL_MS = 2000;
6
8
  const MAX_WACHTTIJD_MS = 5 * 60 * 1000;
7
- const SERVICE_INTERVAL_MS = 5000;
8
- async function noteerLokaleStatus(jobId, status, fout = null) {
9
- await supabase
10
- .from("print_jobs")
11
- .update({
12
- lokale_pdf_status: status,
13
- lokale_pdf_fout: fout,
14
- ...(status === "opgeslagen"
15
- ? { status: "voltooid", lokaal_opgeslagen_op: new Date().toISOString() }
16
- : {}),
17
- })
18
- .eq("id", jobId);
9
+ const WRITER_SLEUTEL = "layout-maker-pdf-writer-id";
10
+ function writerId() {
11
+ try {
12
+ const bestaand = localStorage.getItem(WRITER_SLEUTEL)?.trim();
13
+ if (bestaand)
14
+ return bestaand;
15
+ const nieuw = crypto.randomUUID();
16
+ localStorage.setItem(WRITER_SLEUTEL, nieuw);
17
+ return nieuw;
18
+ }
19
+ catch {
20
+ return crypto.randomUUID();
21
+ }
19
22
  }
20
23
  function veiligeBestandsnaam(naam) {
21
24
  return naam.replace(/[\\/:*?"<>|]/g, "-").replace(/\s+/g, " ").trim() || "document";
@@ -24,101 +27,306 @@ function pdfBlob(base64) {
24
27
  const bytes = Uint8Array.from(atob(base64), (teken) => teken.charCodeAt(0));
25
28
  return new Blob([bytes], { type: "application/pdf" });
26
29
  }
27
- function markeerBewaard(jobId) {
28
- try {
29
- const ruw = localStorage.getItem(BEWAARD_SLEUTEL);
30
- const gelezen = ruw ? JSON.parse(ruw) : [];
31
- const ids = new Set(Array.isArray(gelezen) ? gelezen.map(String) : []);
32
- ids.add(jobId);
33
- localStorage.setItem(BEWAARD_SLEUTEL, JSON.stringify(Array.from(ids).slice(-500)));
34
- }
35
- catch {
36
- // Opslaan in de gekozen map is al gelukt; lokale historie is alleen deduplicatie.
30
+ async function laadPdfBlob(job) {
31
+ if (job.artefact_pad) {
32
+ const { data, error } = await supabase.storage.from("print-artifacts").download(job.artefact_pad);
33
+ if (error || !data)
34
+ throw new Error(`PDF-bestand kon niet worden opgehaald: ${error?.message ?? "onbekend"}`);
35
+ return data;
37
36
  }
37
+ if (job.resultaat_pdf_base64)
38
+ return pdfBlob(job.resultaat_pdf_base64);
39
+ throw new Error("Voor deze opdracht is geen PDF-bestand beschikbaar.");
40
+ }
41
+ async function rondClaimAf(jobId, schrijver, status, fout = null) {
42
+ const { data, error } = await supabase.rpc("finish_local_pdf_job", {
43
+ _job_id: jobId,
44
+ _writer_id: schrijver,
45
+ _status: status,
46
+ _fout: fout ?? undefined,
47
+ });
48
+ if (error)
49
+ throw new Error(`Lokale PDF-status kon niet worden opgeslagen: ${error.message}`);
50
+ if (data !== true)
51
+ throw new Error("De lokale PDF-claim is verlopen of door een andere werkplek overgenomen.");
52
+ }
53
+ /**
54
+ * Geeft een geclaimde opdracht terug aan de wachtrij. Een tabblad zonder
55
+ * bruikbare map mag nooit afkeuren wat een andere werkplek wél kan opslaan.
56
+ */
57
+ async function geefClaimTerug(jobId, schrijver) {
58
+ const { error } = await supabase.rpc("release_local_pdf_job", {
59
+ _job_id: jobId,
60
+ _writer_id: schrijver,
61
+ });
62
+ if (error)
63
+ console.warn("[layout-maker] PDF-opdracht teruggeven mislukt", error.message);
38
64
  }
39
- async function schrijfPdfJob(job) {
40
- if (!job.printer_id || !job.resultaat_pdf_base64)
41
- return "overgeslagen";
65
+ async function schrijfGeclaimdePdf(job, schrijver) {
66
+ if (!job.printer_id || (!job.artefact_pad && !job.resultaat_pdf_base64))
67
+ return false;
42
68
  const toestemming = await pdfMapToestemming(job.printer_id);
43
- if (toestemming === "ontbreekt") {
44
- await noteerLokaleStatus(job.id, "map_ontbreekt", "Kies op deze Mac eerst een PDF-map.");
45
- return "overgeslagen";
46
- }
47
69
  if (toestemming !== "granted") {
48
- await noteerLokaleStatus(job.id, "toestemming_nodig", "Klik op 'Geef maptoestemming' om deze Mac opnieuw toegang tot de PDF-map te geven.");
49
- return "overgeslagen";
70
+ // Geen map of geen schrijfrecht op dít tabblad: terug in de wachtrij.
71
+ await geefClaimTerug(job.id, schrijver);
72
+ return false;
50
73
  }
51
74
  const naam = `${veiligeBestandsnaam(job.documentnaam ?? "document")}-${job.id.slice(0, 8)}.pdf`;
52
- const gelukt = await bewaarInPdfMap(job.printer_id, naam, pdfBlob(job.resultaat_pdf_base64));
53
- if (!gelukt) {
54
- await noteerLokaleStatus(job.id, "schrijffout", "De PDF kon niet naar de gekozen map worden geschreven.");
55
- return "overgeslagen";
75
+ let geschreven = false;
76
+ try {
77
+ geschreven = await bewaarInPdfMap(job.printer_id, naam, await laadPdfBlob(job));
78
+ }
79
+ catch {
80
+ geschreven = false;
81
+ }
82
+ if (!geschreven) {
83
+ await rondClaimAf(job.id, schrijver, "schrijffout", PDF_OPSLAG_MELDING.schrijffout);
84
+ return false;
56
85
  }
57
- markeerBewaard(job.id);
58
- await noteerLokaleStatus(job.id, "opgeslagen");
59
- return "opgeslagen";
86
+ // Eerst het bestand sluiten, daarna pas de database bevestigen. Een fout in
87
+ // deze bevestiging wordt nooit meer genegeerd of als succes aan de UI gemeld.
88
+ await rondClaimAf(job.id, schrijver, "opgeslagen");
89
+ return true;
90
+ }
91
+ async function bepaalEigenPrintplek() {
92
+ const opgeslagen = await lokalePdfPrintplek();
93
+ if (opgeslagen)
94
+ return opgeslagen;
95
+ // Zonder een in deze browser gekozen map doet dit tabblad niet mee: anders
96
+ // kaapt een preview- of extra tabblad opdrachten die het niet kan opslaan.
97
+ const koppeling = await pdfMapKoppeling();
98
+ if (!koppeling?.handle)
99
+ return null;
100
+ // Wél een map, maar nog geen plek eraan gekoppeld (oude installatie):
101
+ // uitsluitend als deze database exact één printplek met een PDF-printer heeft
102
+ // kan er geen twijfel bestaan over de eigenaar van die map.
103
+ const { data, error } = await supabase
104
+ .from("document_printplek_printers")
105
+ .select("print_plek_id, printers!inner(afdrukmethode)")
106
+ .eq("printers.afdrukmethode", "pdf");
107
+ if (error)
108
+ return null;
109
+ const ids = [...new Set((Array.isArray(data) ? data : []).map((rij) => String(rij.print_plek_id ?? "")).filter(Boolean))];
110
+ if (ids.length !== 1)
111
+ return null;
112
+ const enigePrintplek = ids[0];
113
+ if (!enigePrintplek)
114
+ return null;
115
+ await koppelPdfMapAanPrintplek(enigePrintplek);
116
+ return enigePrintplek;
117
+ }
118
+ /** Heeft dít tabblad een bruikbare map met schrijfrecht voor deze printplek? */
119
+ async function magOpdrachtenClaimen() {
120
+ const koppeling = await pdfMapKoppeling();
121
+ if (!koppeling?.handle)
122
+ return false;
123
+ try {
124
+ const toestemming = await koppeling.handle.queryPermission?.({ mode: "readwrite" });
125
+ return (toestemming ?? "granted") === "granted";
126
+ }
127
+ catch {
128
+ return false;
129
+ }
130
+ }
131
+ function meldMapProbleem(state, printerId) {
132
+ if (state.gemeldePrinters.has(printerId))
133
+ return;
134
+ state.gemeldePrinters.add(printerId);
135
+ toast.error("De PDF-map van deze werkplek is niet bruikbaar", {
136
+ description: "Bevestig de maptoestemming en kies daarna bij de opdracht ‘Opnieuw opslaan’.",
137
+ action: {
138
+ label: "Maptoestemming regelen",
139
+ onClick: () => {
140
+ void herstelPdfMapToestemming(printerId).then((resultaat) => {
141
+ if (resultaat === "granted")
142
+ state.gemeldePrinters.delete(printerId);
143
+ else
144
+ toast.error("Maptoestemming is niet verleend");
145
+ });
146
+ },
147
+ },
148
+ duration: 15000,
149
+ });
150
+ }
151
+ /** Schrijft hooguit dit aantal bestanden tegelijk naar de map. */
152
+ const SCHRIJF_PARALLEL = 2;
153
+ /** Vaste batchlimiet per ronde. */
154
+ const CLAIM_MAX = 6;
155
+ async function metPool(items, max, fn) {
156
+ let volgende = 0;
157
+ const werkers = Array.from({ length: Math.max(1, Math.min(max, items.length)) }, async () => {
158
+ for (;;) {
159
+ const i = volgende++;
160
+ if (i >= items.length)
161
+ return;
162
+ await fn(items[i]);
163
+ }
164
+ });
165
+ await Promise.all(werkers);
60
166
  }
61
167
  async function verwerkGereedstaandePdfs(state) {
62
168
  if (state.bezig)
63
169
  return;
64
170
  state.bezig = true;
65
171
  try {
66
- const { data, error } = await supabase
67
- .from("print_jobs")
68
- .select("id, status, printer_id, documentnaam, resultaat_pdf_base64, lokale_pdf_status")
69
- .in("lokale_pdf_status", ["gereed", "map_ontbreekt", "toestemming_nodig", "schrijffout", "timeout"])
70
- .not("resultaat_pdf_base64", "is", null)
71
- .order("voltooid_op", { ascending: true })
72
- .limit(100);
73
- if (error || !Array.isArray(data))
172
+ const printPlekId = await bepaalEigenPrintplek();
173
+ if (!printPlekId)
74
174
  return;
175
+ // Alleen een tabblad met een bruikbare map pakt werk op.
176
+ if (!(await magOpdrachtenClaimen()))
177
+ return;
178
+ const { data, error } = await supabase.rpc("claim_local_pdf_jobs", {
179
+ _print_plek_id: printPlekId,
180
+ _writer_id: state.writerId,
181
+ _max: CLAIM_MAX,
182
+ });
183
+ if (error)
184
+ throw new Error(`PDF-opdrachten ophalen mislukt: ${error.message}`);
75
185
  let opgeslagen = 0;
76
- for (const job of data) {
77
- if (await schrijfPdfJob(job) === "opgeslagen") {
78
- opgeslagen += 1;
79
- if (job.printer_id)
80
- state.gemeldePrinters.delete(job.printer_id);
81
- continue;
186
+ const jobs = (Array.isArray(data) ? data : []);
187
+ await metPool(jobs, SCHRIJF_PARALLEL, async (job) => {
188
+ try {
189
+ if (await schrijfGeclaimdePdf(job, state.writerId)) {
190
+ opgeslagen += 1;
191
+ if (job.printer_id)
192
+ state.gemeldePrinters.delete(job.printer_id);
193
+ }
194
+ else if (job.printer_id) {
195
+ meldMapProbleem(state, job.printer_id);
196
+ }
82
197
  }
83
- if (!job.printer_id || state.gemeldePrinters.has(job.printer_id))
84
- continue;
85
- const toestemming = await pdfMapToestemming(job.printer_id);
86
- if (toestemming === "prompt" || toestemming === "denied") {
87
- state.gemeldePrinters.add(job.printer_id);
88
- toast.error("De PDF-map heeft opnieuw toestemming nodig", {
89
- description: "Geef deze Mac opnieuw toegang; gereedstaande PDF's worden daarna direct opgeslagen.",
90
- action: {
91
- label: "Geef maptoestemming",
92
- onClick: () => {
93
- void herstelPdfMapToestemming(job.printer_id).then((resultaat) => {
94
- if (resultaat === "granted") {
95
- state.gemeldePrinters.delete(job.printer_id);
96
- void verwerkGereedstaandePdfs(state);
97
- }
98
- else {
99
- toast.error("Maptoestemming is niet verleend");
100
- }
101
- });
102
- },
103
- },
104
- duration: 15000,
105
- });
198
+ catch (fout) {
199
+ console.error("[layout-maker] lokale PDF-opslag kon niet worden bevestigd", fout);
200
+ toast.error(fout instanceof Error ? fout.message : "Lokale PDF-opslag mislukt");
106
201
  }
202
+ });
203
+ if (opgeslagen) {
204
+ toast.success(opgeslagen === 1 ? "PDF opgeslagen in de ingestelde map" : `${opgeslagen} PDF's opgeslagen`);
107
205
  }
108
- if (opgeslagen > 0) {
109
- toast.success(opgeslagen === 1
110
- ? "PDF opgeslagen in de gekozen map op deze Mac"
111
- : `${opgeslagen} PDF's opgeslagen in de gekozen map op deze Mac`);
206
+ // Zat de batch vol? Dan staat er mogelijk nog werk klaar.
207
+ if (jobs.length >= CLAIM_MAX) {
208
+ state.bezig = false;
209
+ await verwerkGereedstaandePdfs(state);
210
+ return;
112
211
  }
113
212
  }
213
+ catch (fout) {
214
+ console.error("[layout-maker] lokale PDF-service", fout);
215
+ }
114
216
  finally {
115
217
  state.bezig = false;
116
218
  }
117
219
  }
118
- /** Start één permanente lokale PDF-afhandelaar voor de volledige shell. */
220
+ /** Herstelt uitsluitend de lokale aflevering van een reeds gerenderde PDF. */
221
+ export async function bewaarPdfJobOpnieuw(jobId) {
222
+ const printPlekId = await bepaalEigenPrintplek();
223
+ if (!printPlekId)
224
+ throw new Error("Kies eerst bij deze printplek de lokale PDF-map.");
225
+ const { data: job, error: leesFout } = await supabase
226
+ .from("print_jobs")
227
+ .select("id, resultaat_pdf_base64, artefact_pad, lokale_pdf_print_plek_id")
228
+ .eq("id", jobId)
229
+ .maybeSingle();
230
+ if (leesFout)
231
+ throw leesFout;
232
+ if (!job?.resultaat_pdf_base64 && !job?.artefact_pad)
233
+ throw new Error("Voor deze opdracht is nog geen bestaande PDF beschikbaar.");
234
+ if (job.lokale_pdf_print_plek_id && job.lokale_pdf_print_plek_id !== printPlekId) {
235
+ throw new Error("Deze PDF hoort bij een andere printplek.");
236
+ }
237
+ const { error } = await supabase
238
+ .from("print_jobs")
239
+ .update({
240
+ lokale_pdf_print_plek_id: printPlekId,
241
+ lokale_pdf_status: "gereed",
242
+ lokale_pdf_fout: null,
243
+ lokale_pdf_claim_id: null,
244
+ lokale_pdf_claim_tot: null,
245
+ status: "bezig",
246
+ foutmelding: null,
247
+ voltooid_op: null,
248
+ updated_at: new Date().toISOString(),
249
+ })
250
+ .eq("id", jobId);
251
+ if (error)
252
+ throw error;
253
+ startPdfMapService();
254
+ const state = window.__layoutMakerPdfService;
255
+ if (state)
256
+ await verwerkGereedstaandePdfs(state);
257
+ return true;
258
+ }
259
+ /** Downloadt exact het opgeslagen artefact; legacy jobs vallen terug op base64. */
260
+ export async function downloadPdfJob(jobId, documentnaam) {
261
+ const { data, error } = await supabase
262
+ .from("print_jobs")
263
+ .select("id, artefact_pad, resultaat_pdf_base64")
264
+ .eq("id", jobId)
265
+ .maybeSingle();
266
+ if (error)
267
+ throw error;
268
+ if (!data)
269
+ throw new Error("De printopdracht bestaat niet meer.");
270
+ const blob = await laadPdfBlob({
271
+ ...data,
272
+ status: "",
273
+ printer_id: null,
274
+ documentnaam,
275
+ lokale_pdf_status: null,
276
+ lokale_pdf_print_plek_id: null,
277
+ artefact_sha256: null,
278
+ artefact_bytes: null,
279
+ });
280
+ const url = URL.createObjectURL(blob);
281
+ const link = document.createElement("a");
282
+ link.href = url;
283
+ link.download = `${veiligeBestandsnaam(documentnaam)}.pdf`;
284
+ link.click();
285
+ URL.revokeObjectURL(url);
286
+ }
287
+ export async function controleerPdfMap(printerId) {
288
+ const toestemming = await pdfMapToestemming(printerId);
289
+ if (toestemming === "ontbreekt")
290
+ return "map_ontbreekt";
291
+ return toestemming === "granted" ? "ok" : "geen_rechten";
292
+ }
293
+ /**
294
+ * Volgt de printopdrachten van de eigen printplek live. Zodra er een PDF
295
+ * gereed staat begint het wegschrijven direct; mislukt het abonnement, dan
296
+ * blijft de poll het vangnet en wordt dat zichtbaar in de console.
297
+ */
298
+ async function abonneerOpGereedstaandePdfs(state, controleer) {
299
+ try {
300
+ const printPlekId = await bepaalEigenPrintplek();
301
+ if (!printPlekId)
302
+ return;
303
+ if (state.kanaal)
304
+ return;
305
+ state.kanaal = supabase
306
+ .channel(`layout-maker-lokale-pdf-${printPlekId}`)
307
+ .on("postgres_changes", {
308
+ event: "*",
309
+ schema: "public",
310
+ table: "print_jobs",
311
+ filter: `lokale_pdf_print_plek_id=eq.${printPlekId}`,
312
+ }, (payload) => {
313
+ const rij = (payload.new ?? {});
314
+ if (rij.lokale_pdf_status === "gereed")
315
+ controleer();
316
+ })
317
+ .subscribe();
318
+ }
319
+ catch (fout) {
320
+ console.warn("[layout-maker] live meeluisteren op PDF-opdrachten mislukt", fout);
321
+ }
322
+ }
119
323
  export function startPdfMapService() {
120
324
  if (typeof window === "undefined")
121
325
  return;
326
+ // De zwevende mapmelding hoort bij deze service: zodra de map of toestemming
327
+ // ontbreekt verschijnt hij boven elke pagina, en verdwijnt hij weer zodra de
328
+ // toestemming verleend is.
329
+ startPdfMapGlobaleBanner();
122
330
  const bestaand = window.__layoutMakerPdfService;
123
331
  if (bestaand?.gestart) {
124
332
  void verwerkGereedstaandePdfs(bestaand);
@@ -129,9 +337,13 @@ export function startPdfMapService() {
129
337
  bezig: false,
130
338
  interval: null,
131
339
  gemeldePrinters: new Set(),
340
+ writerId: writerId(),
132
341
  };
133
342
  window.__layoutMakerPdfService = state;
134
343
  const controleer = () => void verwerkGereedstaandePdfs(state);
344
+ // Realtime is het snelle pad: zodra een opdracht 'gereed' staat begint het
345
+ // wegschrijven meteen. De vijfsecondenpoll blijft uitsluitend vangnet.
346
+ void abonneerOpGereedstaandePdfs(state, controleer);
135
347
  state.interval = window.setInterval(controleer, SERVICE_INTERVAL_MS);
136
348
  window.addEventListener("focus", controleer);
137
349
  window.addEventListener("online", controleer);
@@ -142,32 +354,23 @@ export function startPdfMapService() {
142
354
  luisterNaarPdfMapWijziging(controleer);
143
355
  controleer();
144
356
  }
145
- /**
146
- * Wacht na een printactie op de server-PDF en schrijft hem vervolgens direct
147
- * naar de map die op deze computer voor de virtuele printer is gekozen.
148
- */
149
- export async function bewaarPdfJobZodraKlaar(jobId, printerId) {
150
- if (!(await heeftPdfMap(printerId))) {
151
- await noteerLokaleStatus(jobId, "map_ontbreekt", "Kies op deze computer eerst een PDF-map.");
152
- return false;
153
- }
357
+ /** Wacht alleen op het eindresultaat; de singleton met databaseclaim schrijft. */
358
+ export async function bewaarPdfJobZodraKlaar(jobId, _printerId) {
359
+ startPdfMapService();
154
360
  const deadline = Date.now() + MAX_WACHTTIJD_MS;
155
361
  while (Date.now() < deadline) {
156
362
  const { data, error } = await supabase
157
363
  .from("print_jobs")
158
- .select("id, status, documentnaam, resultaat_pdf_base64, lokale_pdf_status")
364
+ .select("status, lokale_pdf_status")
159
365
  .eq("id", jobId)
160
366
  .maybeSingle();
161
367
  if (error)
162
368
  return false;
163
- const job = data;
164
- if (job?.status === "mislukt" || job?.status === "geannuleerd")
369
+ if (data?.lokale_pdf_status === "opgeslagen")
370
+ return true;
371
+ if (data?.status === "mislukt" || data?.status === "geannuleerd")
165
372
  return false;
166
- if (job?.resultaat_pdf_base64) {
167
- return (await schrijfPdfJob({ ...job, printer_id: printerId })) === "opgeslagen";
168
- }
169
373
  await new Promise((resolve) => window.setTimeout(resolve, WACHT_INTERVAL_MS));
170
374
  }
171
- await noteerLokaleStatus(jobId, "timeout", "De PDF was niet binnen vijf minuten gereed.");
172
375
  return false;
173
376
  }
@@ -1,8 +1,42 @@
1
+ type MapHandle = {
2
+ name: string;
3
+ queryPermission?: (o: {
4
+ mode: string;
5
+ }) => Promise<string>;
6
+ requestPermission?: (o: {
7
+ mode: string;
8
+ }) => Promise<string>;
9
+ getFileHandle: (naam: string, o?: {
10
+ create?: boolean;
11
+ }) => Promise<{
12
+ createWritable: () => Promise<{
13
+ write: (d: Blob) => Promise<void>;
14
+ close: () => Promise<void>;
15
+ }>;
16
+ }>;
17
+ };
18
+ export type PdfMapKoppeling = {
19
+ versie: 1;
20
+ handle: MapHandle;
21
+ printPlekId: string | null;
22
+ persistent: boolean | null;
23
+ bijgewerktOp: string;
24
+ };
1
25
  export declare function mapKiezenMogelijk(): boolean;
26
+ /** Koppelt deze browsermap aan de fysieke printplek die hij mag afhandelen. */
27
+ export declare function koppelPdfMapAanPrintplek(printPlekId: string): Promise<void>;
28
+ /** De printplek waarvoor deze browser lokale PDF's mag schrijven. */
29
+ export declare function lokalePdfPrintplek(): Promise<string | null>;
30
+ /**
31
+ * Leest de samenhangende lokale map/printplek-koppeling. Oude losse sleutels
32
+ * worden eenmalig naar dit record gemigreerd zonder een geldige handle te wissen.
33
+ */
34
+ export declare function pdfMapKoppeling(): Promise<PdfMapKoppeling | null>;
2
35
  export declare function luisterNaarPdfMapWijziging(handler: () => void): () => void;
3
36
  export type PdfMapKeuze = {
4
37
  status: "gekozen";
5
38
  naam: string;
39
+ persistent: boolean | null;
6
40
  } | {
7
41
  status: "geannuleerd";
8
42
  } | {
@@ -20,13 +54,13 @@ export type PdfMapKeuze = {
20
54
  */
21
55
  export declare function draaitInIframe(): boolean;
22
56
  /** Opent de huidige pagina in een eigen tabblad (buiten het preview-iframe). */
23
- export declare function openInEigenTabblad(): void;
57
+ export declare function openInEigenTabblad(printPlekId?: string): void;
24
58
  /**
25
59
  * Opent de mappenverkenner van het besturingssysteem zodat de gebruiker een
26
60
  * map kan selecteren. Start in Downloads zodat het pad snel te vinden is.
27
61
  * Geeft terug wat er is gebeurd zodat de aanroeper gericht kan reageren.
28
62
  */
29
- export declare function kiesPdfMap(printerId: string): Promise<PdfMapKeuze>;
63
+ export declare function kiesPdfMap(printerId: string, printPlekId?: string): Promise<PdfMapKeuze>;
30
64
  /** Is er voor deze printer een map gekozen op deze computer? */
31
65
  export declare function heeftPdfMap(printerId: string): Promise<boolean>;
32
66
  /** Naam van de gekozen map (voor meldingen), of null. */
@@ -45,4 +79,5 @@ export declare function pdfMapToestemming(printerId: string): Promise<"granted"
45
79
  * openen, omdat browsers daarvoor een actieve gebruikershandeling vereisen.
46
80
  */
47
81
  export declare function herstelPdfMapToestemming(printerId: string): Promise<"granted" | "denied" | "ontbreekt">;
82
+ export {};
48
83
  //# sourceMappingURL=pdfMap.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"pdfMap.d.ts","sourceRoot":"","sources":["../../src/lib/pdfMap.ts"],"names":[],"mappings":"AAsBA,wBAAgB,iBAAiB,IAAI,OAAO,CAE3C;AA6BD,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAI1E;AAgDD,MAAM,MAAM,WAAW,GACnB;IAAE,MAAM,EAAE,SAAS,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACnC;IAAE,MAAM,EAAE,aAAa,CAAA;CAAE,GACzB;IAAE,MAAM,EAAE,kBAAkB,CAAA;CAAE,GAC9B;IAAE,MAAM,EAAE,oBAAoB,CAAA;CAAE,GAChC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAExC;;;;GAIG;AACH,wBAAgB,cAAc,IAAI,OAAO,CAMxC;AAED,gFAAgF;AAChF,wBAAgB,kBAAkB,IAAI,IAAI,CAEzC;AAED;;;;GAIG;AACH,wBAAsB,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAqCxE;AAED,gEAAgE;AAChE,wBAAsB,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAErE;AAED,yDAAyD;AACzD,wBAAsB,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAG1E;AAED;;;;GAIG;AACH,wBAAsB,cAAc,CAClC,SAAS,EAAE,MAAM,EACjB,YAAY,EAAE,MAAM,EACpB,IAAI,EAAE,IAAI,GACT,OAAO,CAAC,OAAO,CAAC,CAmBlB;AAED,mEAAmE;AACnE,wBAAsB,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,GAAG,QAAQ,GAAG,QAAQ,GAAG,WAAW,CAAC,CAWjH;AAED;;;;GAIG;AACH,wBAAsB,wBAAwB,CAC5C,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,SAAS,GAAG,QAAQ,GAAG,WAAW,CAAC,CAe7C"}
1
+ {"version":3,"file":"pdfMap.d.ts","sourceRoot":"","sources":["../../src/lib/pdfMap.ts"],"names":[],"mappings":"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"}