@flowselections/layout-maker 1.32.0 → 1.36.7

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 (73) hide show
  1. package/dist-lib/components/ComputersLijst.d.ts.map +1 -1
  2. package/dist-lib/components/ComputersLijst.js +5 -5
  3. package/dist-lib/components/DocumentenPage.d.ts.map +1 -1
  4. package/dist-lib/components/DocumentenPage.js +3 -2
  5. package/dist-lib/components/LayoutPrinterSelect.d.ts +8 -0
  6. package/dist-lib/components/LayoutPrinterSelect.d.ts.map +1 -0
  7. package/dist-lib/components/LayoutPrinterSelect.js +23 -0
  8. package/dist-lib/components/LayoutsPage.d.ts.map +1 -1
  9. package/dist-lib/components/LayoutsPage.js +16 -3
  10. package/dist-lib/components/PakbonBuilderPage.d.ts.map +1 -1
  11. package/dist-lib/components/PakbonBuilderPage.js +3 -1
  12. package/dist-lib/components/PakbonSectiesPanel.d.ts.map +1 -1
  13. package/dist-lib/components/PakbonSectiesPanel.js +6 -2
  14. package/dist-lib/components/PdfMapAutoSaver.d.ts +6 -0
  15. package/dist-lib/components/PdfMapAutoSaver.d.ts.map +1 -0
  16. package/dist-lib/components/PdfMapAutoSaver.js +12 -0
  17. package/dist-lib/components/PdfMapGlobaleBanner.d.ts +12 -0
  18. package/dist-lib/components/PdfMapGlobaleBanner.d.ts.map +1 -0
  19. package/dist-lib/components/PdfMapGlobaleBanner.js +30 -0
  20. package/dist-lib/components/PdfMapToestemmingBanner.d.ts +7 -0
  21. package/dist-lib/components/PdfMapToestemmingBanner.d.ts.map +1 -0
  22. package/dist-lib/components/PdfMapToestemmingBanner.js +139 -0
  23. package/dist-lib/components/PrintQueuePage.d.ts.map +1 -1
  24. package/dist-lib/components/PrintQueuePage.js +42 -3
  25. package/dist-lib/components/PrintenPage.d.ts.map +1 -1
  26. package/dist-lib/components/PrintenPage.js +3 -2
  27. package/dist-lib/components/PrinterSetupWizard.d.ts.map +1 -1
  28. package/dist-lib/components/PrinterSetupWizard.js +25 -2
  29. package/dist-lib/components/PrintplekDetailPage.d.ts.map +1 -1
  30. package/dist-lib/components/PrintplekDetailPage.js +30 -4
  31. package/dist-lib/components/PrintplekWizard.d.ts.map +1 -1
  32. package/dist-lib/components/PrintplekWizard.js +81 -3
  33. package/dist-lib/components/ProductiebonBuilderPage.d.ts.map +1 -1
  34. package/dist-lib/components/ProductiebonBuilderPage.js +3 -1
  35. package/dist-lib/index.d.ts +6 -0
  36. package/dist-lib/index.d.ts.map +1 -1
  37. package/dist-lib/index.js +11 -1
  38. package/dist-lib/integrations/supabase/auth-middleware.d.ts +242 -1
  39. package/dist-lib/integrations/supabase/auth-middleware.d.ts.map +1 -1
  40. package/dist-lib/integrations/supabase/client.server.d.ts +242 -1
  41. package/dist-lib/integrations/supabase/client.server.d.ts.map +1 -1
  42. package/dist-lib/integrations/supabase/types.d.ts +244 -1
  43. package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
  44. package/dist-lib/lib/aiLayout.functions.d.ts +242 -1
  45. package/dist-lib/lib/aiLayout.functions.d.ts.map +1 -1
  46. package/dist-lib/lib/htmlPdf.server.d.ts.map +1 -1
  47. package/dist-lib/lib/htmlPdf.server.js +43 -6
  48. package/dist-lib/lib/orderData.server.d.ts.map +1 -1
  49. package/dist-lib/lib/orderData.server.js +117 -10
  50. package/dist-lib/lib/pdfJobSaver.d.ts +20 -0
  51. package/dist-lib/lib/pdfJobSaver.d.ts.map +1 -0
  52. package/dist-lib/lib/pdfJobSaver.js +173 -0
  53. package/dist-lib/lib/pdfMap.d.ts +48 -0
  54. package/dist-lib/lib/pdfMap.d.ts.map +1 -0
  55. package/dist-lib/lib/pdfMap.js +219 -0
  56. package/dist-lib/lib/pdfPrinter.d.ts +9 -0
  57. package/dist-lib/lib/pdfPrinter.d.ts.map +1 -0
  58. package/dist-lib/lib/pdfPrinter.js +34 -0
  59. package/dist-lib/lib/previewPdf.d.ts.map +1 -1
  60. package/dist-lib/lib/previewPdf.js +41 -9
  61. package/dist-lib/lib/printApi.d.ts.map +1 -1
  62. package/dist-lib/lib/printApi.js +9 -1
  63. package/dist-lib/lib/printQueue.server.d.ts.map +1 -1
  64. package/dist-lib/lib/printQueue.server.js +99 -26
  65. package/dist-lib/lib/printService.functions.d.ts +968 -4
  66. package/dist-lib/lib/printService.functions.d.ts.map +1 -1
  67. package/dist-lib/lib/useDocumentData.d.ts +10 -0
  68. package/dist-lib/lib/useDocumentData.d.ts.map +1 -1
  69. package/dist-lib/lib/useDocumentData.js +19 -0
  70. package/dist-lib/lib/voorbeeldPrint.d.ts +4 -0
  71. package/dist-lib/lib/voorbeeldPrint.d.ts.map +1 -1
  72. package/dist-lib/lib/voorbeeldPrint.js +13 -2
  73. package/package.json +1 -1
@@ -428,14 +428,34 @@ class Tekenaar {
428
428
  function regelHoogte(s) {
429
429
  return s.grootte * 1.35;
430
430
  }
431
- /** Breekt tekst af op de beschikbare breedte. */
431
+ /** Breekt tekst af op de beschikbare breedte; te lange woorden worden hard gesplitst. */
432
432
  function breekAf(tk, tekst, s, breedte) {
433
433
  const woorden = veiligeTekst(tekst).split(/\s+/).filter((w) => w.length);
434
434
  if (!woorden.length)
435
435
  return [];
436
436
  const regels = [];
437
437
  let huidig = "";
438
+ const duw = () => {
439
+ if (huidig)
440
+ regels.push(huidig);
441
+ huidig = "";
442
+ };
438
443
  for (const w of woorden) {
444
+ // Woord past sowieso niet: hak het op zodat het nooit de kolom uitloopt.
445
+ if (tk.breedteVan(w, s) > breedte) {
446
+ duw();
447
+ let deel = "";
448
+ for (const teken of w) {
449
+ if (deel && tk.breedteVan(deel + teken, s) > breedte) {
450
+ regels.push(deel);
451
+ deel = teken;
452
+ }
453
+ else
454
+ deel += teken;
455
+ }
456
+ huidig = deel;
457
+ continue;
458
+ }
439
459
  const kandidaat = huidig ? `${huidig} ${w}` : w;
440
460
  if (tk.breedteVan(kandidaat, s) <= breedte || !huidig)
441
461
  huidig = kandidaat;
@@ -444,10 +464,25 @@ function breekAf(tk, tekst, s, breedte) {
444
464
  huidig = w;
445
465
  }
446
466
  }
447
- if (huidig)
448
- regels.push(huidig);
467
+ duw();
449
468
  return regels;
450
469
  }
470
+ /**
471
+ * Verkleint de letter zo min mogelijk tot het langste woord binnen de breedte
472
+ * past. Zo blijft tekst maximaal leesbaar zonder ooit over te lopen.
473
+ */
474
+ function passendeStijl(tk, tekst, s, breedte) {
475
+ const woorden = veiligeTekst(tekst).split(/\s+/).filter((w) => w.length);
476
+ if (!woorden.length || breedte <= 0)
477
+ return s;
478
+ const langste = woorden.reduce((a, b) => (tk.breedteVan(b, s) > tk.breedteVan(a, s) ? b : a));
479
+ const nodig = tk.breedteVan(langste, s);
480
+ if (nodig <= breedte)
481
+ return s;
482
+ const min = Math.max(4.5, s.grootte * 0.6);
483
+ const geschaald = Math.max(min, Math.floor((s.grootte * breedte) / nodig * 10) / 10);
484
+ return geschaald < s.grootte ? { ...s, grootte: geschaald } : s;
485
+ }
451
486
  function tekenRegel(tk, regel, x, breedte, y, s) {
452
487
  const t = metTransform(veiligeTekst(regel), s);
453
488
  const w = tk.breedteVan(t, s);
@@ -770,9 +805,11 @@ async function tekenTabel(ctx, tabel, vak, s) {
770
805
  const eigenCelRanden = rijen.some((r) => r.kinderen.some((c) => (c.tag === "td" || c.tag === "th") && !!celRandOnder(c)));
771
806
  const tekenRij = (rij, herhaald = false) => {
772
807
  const cellen = rij.kinderen.filter((c) => c.tag === "td" || c.tag === "th");
773
- const cellenStijl = cellen.map((c) => stijlVan(c, s));
774
- const teksten = cellen.map((c, i) => breekAf(tk, plattTekst(c), cellenStijl[i], Math.max(4, (breedtes[i] ?? 20) - padX * 2)));
775
- const hoogte = Math.max(1, ...teksten.map((t) => t.length)) * regelHoogte(cellenStijl[0] ?? s) + padY * 2;
808
+ const celBreedte = (i) => Math.max(4, (breedtes[i] ?? 20) - padX * 2);
809
+ const celTekst = cellen.map((c) => plattTekst(c));
810
+ const cellenStijl = cellen.map((c, i) => passendeStijl(tk, celTekst[i], stijlVan(c, s), celBreedte(i)));
811
+ const teksten = cellen.map((_, i) => breekAf(tk, celTekst[i], cellenStijl[i], celBreedte(i)));
812
+ const hoogte = Math.max(regelHoogte(s), ...teksten.map((t, i) => Math.max(1, t.length) * regelHoogte(cellenStijl[i] ?? s))) + padY * 2;
776
813
  if (hoogte > tk.ruimte) {
777
814
  tk.nieuwePagina();
778
815
  if (kopRij && !herhaald && rij !== kopRij)
@@ -1 +1 @@
1
- {"version":3,"file":"orderData.server.d.ts","sourceRoot":"","sources":["../../src/lib/orderData.server.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAEnD,KAAK,GAAG,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;AAE/B,MAAM,MAAM,WAAW,GAAG,OAAO,GAAG,QAAQ,GAAG,MAAM,CAAC;AAwBtD,mEAAmE;AACnE,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAKvD;AAED,+EAA+E;AAC/E,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,GAAG;IACrD,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB,CAgCA;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,OAAO,GAAG;IAC7C,KAAK,EAAE,WAAW,CAAC;IACnB,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;IAClB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B,CAMA;AAED;;;GAGG;AACH,wBAAsB,cAAc,CAClC,EAAE,EAAE,OAAO,EACX,MAAM,EAAE;IAAE,KAAK,EAAE,WAAW,CAAC;IAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,GAC7E,OAAO,CAAC,GAAG,GAAG,IAAI,CAAC,CA2OrB;AA+DD;;;GAGG;AACH,wBAAsB,uBAAuB,CAAC,EAAE,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,GAAG,GAAG,IAAI,CAAC,CAGhG"}
1
+ {"version":3,"file":"orderData.server.d.ts","sourceRoot":"","sources":["../../src/lib/orderData.server.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAEnD,KAAK,GAAG,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;AAE/B,MAAM,MAAM,WAAW,GAAG,OAAO,GAAG,QAAQ,GAAG,MAAM,CAAC;AAwBtD,mEAAmE;AACnE,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAKvD;AAED,+EAA+E;AAC/E,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,GAAG;IACrD,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB,CAgCA;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,OAAO,GAAG;IAC7C,KAAK,EAAE,WAAW,CAAC;IACnB,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;IAClB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B,CAMA;AAED;;;GAGG;AACH,wBAAsB,cAAc,CAClC,EAAE,EAAE,OAAO,EACX,MAAM,EAAE;IAAE,KAAK,EAAE,WAAW,CAAC;IAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,GAC7E,OAAO,CAAC,GAAG,GAAG,IAAI,CAAC,CAwXrB;AA+DD;;;GAGG;AACH,wBAAsB,uBAAuB,CAAC,EAAE,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,GAAG,GAAG,IAAI,CAAC,CAGhG"}
@@ -205,24 +205,127 @@ export async function laadPakbonData(db, opties) {
205
205
  if (productIds.length) {
206
206
  const data = await veilig(() => db
207
207
  .from("products")
208
- .select("id, product, article_number, barcode, location, unit, weight, custom_fields")
208
+ .select("id, product, article_number, barcode, location, unit, weight, custom_fields, floriday_trade_item_id, trade_item_key")
209
209
  .in("id", productIds));
210
210
  for (const p of arr(data))
211
211
  producten.set(String(p["id"]), p);
212
212
  }
213
+ // --- verrijking vanuit Floriday-artikelcache ------------------------------
214
+ // `products.unit` is vrijwel altijd "stuks" en zegt niets over het fust.
215
+ // De echte fustsoort, potmaat en kleur staan in de Floriday-cache:
216
+ // - fustsoort: primaire verpakkingsconfiguratie → vbnPackageCode, met de
217
+ // gebruikersnaam uit `fust_types`.
218
+ // - potmaat: kenmerk S01 (Potmaat).
219
+ // - kleur: kenmerk B01 (Hoofdkleur 1).
220
+ const floridayIds = uniek(Array.from(producten.values()).map((p) => {
221
+ const extra = (p["custom_fields"] ?? {});
222
+ return String(p["floriday_trade_item_id"] ?? p["trade_item_key"] ?? extra["floriday_id"] ?? "");
223
+ }));
224
+ const floridayInfo = new Map();
225
+ if (floridayIds.length) {
226
+ const items = arr(await veilig(() => db
227
+ .from("floriday_trade_items_cache")
228
+ .select("floriday_id, data")
229
+ .in("floriday_id", floridayIds)));
230
+ for (const it of items) {
231
+ const d = (it["data"] ?? {});
232
+ const configs = arr(d["packingConfigurations"]);
233
+ const primair = configs.find((c) => c["primary"]) ?? configs[0] ?? {};
234
+ const fustCode = String((primair["package"] ?? {})["vbnPackageCode"] ?? "");
235
+ const kenmerken = arr(d["characteristics"]);
236
+ const b01 = kenmerken.find((k) => String(k["vbnCode"]) === "B01");
237
+ const s01 = kenmerken.find((k) => String(k["vbnCode"]).toUpperCase() === "S01");
238
+ const kleurCode = b01 ? String(b01["vbnValueCode"] ?? "") : "";
239
+ const potmaatCode = s01 ? String(s01["vbnValueCode"] ?? "") : "";
240
+ floridayInfo.set(String(it["floriday_id"]), { fustCode, kleurCode, potmaatCode });
241
+ }
242
+ // VBN-verpakkincode → de ingestelde fustnaam. `fust_types` is hiervoor de
243
+ // bron van waarheid; `crate_types` bevat niet alle VBN-codes.
244
+ const fustCodes = uniek(Array.from(floridayInfo.values()).map((i) => i.fustCode));
245
+ const fustNamen = new Map();
246
+ if (fustCodes.length) {
247
+ const fusten = arr(await veilig(() => db.from("fust_types").select("vbn_code, name").in("vbn_code", fustCodes)));
248
+ for (const fust of fusten) {
249
+ const code = String(fust["vbn_code"] ?? "");
250
+ const naam = String(fust["name"] ?? "").trim();
251
+ if (code && naam && !fustNamen.has(code))
252
+ fustNamen.set(code, naam);
253
+ }
254
+ }
255
+ // Floriday-kenmerkcodes → leesbare labels uit de VBN-codelijst.
256
+ const kenmerkSleutels = uniek(Array.from(floridayInfo.values()).flatMap((i) => [
257
+ i.kleurCode ? `B01:${i.kleurCode}` : "",
258
+ i.potmaatCode ? `S01:${i.potmaatCode}` : "",
259
+ ]));
260
+ const kenmerkNamen = new Map();
261
+ if (kenmerkSleutels.length) {
262
+ const waarden = arr(await veilig(() => db
263
+ .from("floriday_code_lists_cache")
264
+ .select("code, label")
265
+ .eq("list_name", "vbn-characteristic-value")
266
+ .in("code", kenmerkSleutels)));
267
+ for (const w of waarden) {
268
+ const code = String(w["code"] ?? "");
269
+ const label = String(w["label"] ?? "").trim();
270
+ if (code && label && !kenmerkNamen.has(code))
271
+ kenmerkNamen.set(code, label);
272
+ }
273
+ }
274
+ // Schrijf de verrijking terug in de custom_fields van het product.
275
+ for (const p of producten.values()) {
276
+ const extra = (p["custom_fields"] ?? {});
277
+ const floridayId = String(p["floriday_trade_item_id"] ?? p["trade_item_key"] ?? extra["floriday_id"] ?? "");
278
+ const info = floridayInfo.get(floridayId);
279
+ if (!info)
280
+ continue;
281
+ if (info.fustCode) {
282
+ const naam = fustNamen.get(info.fustCode);
283
+ extra["__fustsoort"] = naam ?? `VBN ${info.fustCode}`;
284
+ }
285
+ if (info.kleurCode) {
286
+ extra["__kleur"] = kenmerkNamen.get(`B01:${info.kleurCode}`) ?? info.kleurCode;
287
+ }
288
+ if (info.potmaatCode) {
289
+ extra["__potmaat"] = kenmerkNamen.get(`S01:${info.potmaatCode}`) ?? info.potmaatCode;
290
+ }
291
+ p["custom_fields"] = extra;
292
+ }
293
+ }
213
294
  const maakProduct = (bron, aantalPlanten, fusten, perFust) => {
214
295
  const p = producten.get(String(bron["product_id"] ?? "")) ?? {};
215
296
  const extra = (p["custom_fields"] ?? {});
297
+ const unit = String(p["unit"] ?? "").trim();
216
298
  return {
217
299
  artikelnummer: String(p["article_number"] ?? ""),
218
300
  artikelnaam: taalTekst(bron["product_name"]) || taalTekst(p["product"]),
219
- potmaat: String(extra["potmaat"] ?? extra["Potmaat"] ?? ""),
301
+ potmaat: String(extra["potmaat"] ??
302
+ extra["Potmaat"] ??
303
+ extra["pot_size"] ??
304
+ extra["potSize"] ??
305
+ extra["potmaat_cm"] ??
306
+ extra["maat"] ??
307
+ extra["__potmaat"] ??
308
+ "Niet ingesteld"),
309
+ kleur: String(extra["kleur"] ??
310
+ extra["Kleur"] ??
311
+ extra["colour"] ??
312
+ extra["color"] ??
313
+ extra["__kleur"] ??
314
+ "Niet ingesteld"),
220
315
  aantal: aantalPlanten,
221
316
  barcode: String(p["barcode"] ?? ""),
222
- locatie: String(p["location"] ?? ""),
317
+ locatie: String(p["location"] ||
318
+ extra["locatie"] ||
319
+ extra["location"] ||
320
+ extra["warehouse_location"] ||
321
+ "Niet ingesteld"),
223
322
  gewicht: bron["weight_kg"] ? `${num(bron["weight_kg"]).toFixed(1)} kg` : "",
224
323
  fust_aantal: fusten,
225
- fustsoort: String(extra["fustsoort"] ?? p["unit"] ?? ""),
324
+ // "stuks" is een aantalseenheid, geen fustsoort — die slaan we over.
325
+ fustsoort: String(extra["fustsoort"] ??
326
+ extra["Fustsoort"] ??
327
+ extra["__fustsoort"] ??
328
+ (unit && unit.toLowerCase() !== "stuks" ? unit : "Niet ingesteld")),
226
329
  planten_per_fust: perFust,
227
330
  };
228
331
  };
@@ -256,12 +359,16 @@ export async function laadPakbonData(db, opties) {
256
359
  gewicht: kar["weight_kg"] ? `${num(kar["weight_kg"]).toFixed(1)} kg` : "",
257
360
  gedeeld: kar["__gedeeld"] === true,
258
361
  opmerking: String(kar["notes"] ?? ""),
259
- lagen: lagen.slice(0, Math.max(maxLagen, lagen.length)).map((laag, j) => ({
260
- laagnummer: j + 1,
261
- belading: `${Math.round(Math.min(laag.ruimte, 1) * 100)}%`,
262
- producten: laag.producten,
263
- fusten: vatFustenSamen(laag.producten),
264
- })),
362
+ lagen: lagen.slice(0, Math.max(maxLagen, lagen.length)).map((laag, j) => {
363
+ const potmaten = uniek(laag.producten.map((product) => product["potmaat"]));
364
+ return {
365
+ laagnummer: j + 1,
366
+ belading: `${Math.round(Math.min(laag.ruimte, 1) * 100)}%`,
367
+ potmaat: potmaten.length === 1 ? potmaten[0] : "",
368
+ producten: laag.producten,
369
+ fusten: vatFustenSamen(laag.producten),
370
+ };
371
+ }),
265
372
  };
266
373
  });
267
374
  // --- losse orderregels (zonder karindeling) -------------------------------
@@ -0,0 +1,20 @@
1
+ type PdfServiceState = {
2
+ gestart: boolean;
3
+ bezig: boolean;
4
+ interval: number | null;
5
+ gemeldePrinters: Set<string>;
6
+ };
7
+ declare global {
8
+ interface Window {
9
+ __layoutMakerPdfService?: PdfServiceState;
10
+ }
11
+ }
12
+ /** Start één permanente lokale PDF-afhandelaar voor de volledige shell. */
13
+ export declare function startPdfMapService(): void;
14
+ /**
15
+ * Wacht na een printactie op de server-PDF en schrijft hem vervolgens direct
16
+ * naar de map die op deze computer voor de virtuele printer is gekozen.
17
+ */
18
+ export declare function bewaarPdfJobZodraKlaar(jobId: string, printerId: string): Promise<boolean>;
19
+ export {};
20
+ //# sourceMappingURL=pdfJobSaver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pdfJobSaver.d.ts","sourceRoot":"","sources":["../../src/lib/pdfJobSaver.ts"],"names":[],"mappings":"AA2BA,KAAK,eAAe,GAAG;IACrB,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,OAAO,CAAC;IACf,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,eAAe,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CAC9B,CAAC;AAEF,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,MAAM;QACd,uBAAuB,CAAC,EAAE,eAAe,CAAC;KAC3C;CACF;AA8HD,2EAA2E;AAC3E,wBAAgB,kBAAkB,IAAI,IAAI,CAyBzC;AAED;;;GAGG;AACH,wBAAsB,sBAAsB,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAyB/F"}
@@ -0,0 +1,173 @@
1
+ import { supabase } from "./supabaseClient.js";
2
+ import { toast } from "@flowselections/core";
3
+ import { bewaarInPdfMap, herstelPdfMapToestemming, heeftPdfMap, luisterNaarPdfMapWijziging, pdfMapToestemming, } from "./pdfMap.js";
4
+ const BEWAARD_SLEUTEL = "layout-maker-pdf-bewaard";
5
+ const WACHT_INTERVAL_MS = 2000;
6
+ 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);
19
+ }
20
+ function veiligeBestandsnaam(naam) {
21
+ return naam.replace(/[\\/:*?"<>|]/g, "-").replace(/\s+/g, " ").trim() || "document";
22
+ }
23
+ function pdfBlob(base64) {
24
+ const bytes = Uint8Array.from(atob(base64), (teken) => teken.charCodeAt(0));
25
+ return new Blob([bytes], { type: "application/pdf" });
26
+ }
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.
37
+ }
38
+ }
39
+ async function schrijfPdfJob(job) {
40
+ if (!job.printer_id || !job.resultaat_pdf_base64)
41
+ return "overgeslagen";
42
+ 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
+ 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";
50
+ }
51
+ 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";
56
+ }
57
+ markeerBewaard(job.id);
58
+ await noteerLokaleStatus(job.id, "opgeslagen");
59
+ return "opgeslagen";
60
+ }
61
+ async function verwerkGereedstaandePdfs(state) {
62
+ if (state.bezig)
63
+ return;
64
+ state.bezig = true;
65
+ 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))
74
+ return;
75
+ 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;
82
+ }
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
+ });
106
+ }
107
+ }
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`);
112
+ }
113
+ }
114
+ finally {
115
+ state.bezig = false;
116
+ }
117
+ }
118
+ /** Start één permanente lokale PDF-afhandelaar voor de volledige shell. */
119
+ export function startPdfMapService() {
120
+ if (typeof window === "undefined")
121
+ return;
122
+ const bestaand = window.__layoutMakerPdfService;
123
+ if (bestaand?.gestart) {
124
+ void verwerkGereedstaandePdfs(bestaand);
125
+ return;
126
+ }
127
+ const state = {
128
+ gestart: true,
129
+ bezig: false,
130
+ interval: null,
131
+ gemeldePrinters: new Set(),
132
+ };
133
+ window.__layoutMakerPdfService = state;
134
+ const controleer = () => void verwerkGereedstaandePdfs(state);
135
+ state.interval = window.setInterval(controleer, SERVICE_INTERVAL_MS);
136
+ window.addEventListener("focus", controleer);
137
+ window.addEventListener("online", controleer);
138
+ document.addEventListener("visibilitychange", () => {
139
+ if (document.visibilityState === "visible")
140
+ controleer();
141
+ });
142
+ luisterNaarPdfMapWijziging(controleer);
143
+ controleer();
144
+ }
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
+ }
154
+ const deadline = Date.now() + MAX_WACHTTIJD_MS;
155
+ while (Date.now() < deadline) {
156
+ const { data, error } = await supabase
157
+ .from("print_jobs")
158
+ .select("id, status, documentnaam, resultaat_pdf_base64, lokale_pdf_status")
159
+ .eq("id", jobId)
160
+ .maybeSingle();
161
+ if (error)
162
+ return false;
163
+ const job = data;
164
+ if (job?.status === "mislukt" || job?.status === "geannuleerd")
165
+ return false;
166
+ if (job?.resultaat_pdf_base64) {
167
+ return (await schrijfPdfJob({ ...job, printer_id: printerId })) === "opgeslagen";
168
+ }
169
+ await new Promise((resolve) => window.setTimeout(resolve, WACHT_INTERVAL_MS));
170
+ }
171
+ await noteerLokaleStatus(jobId, "timeout", "De PDF was niet binnen vijf minuten gereed.");
172
+ return false;
173
+ }
@@ -0,0 +1,48 @@
1
+ export declare function mapKiezenMogelijk(): boolean;
2
+ export declare function luisterNaarPdfMapWijziging(handler: () => void): () => void;
3
+ export type PdfMapKeuze = {
4
+ status: "gekozen";
5
+ naam: string;
6
+ } | {
7
+ status: "geannuleerd";
8
+ } | {
9
+ status: "niet-ondersteund";
10
+ } | {
11
+ status: "iframe-geblokkeerd";
12
+ } | {
13
+ status: "fout";
14
+ melding: string;
15
+ };
16
+ /**
17
+ * Browsers blokkeren showDirectoryPicker in een cross-origin iframe (zoals de
18
+ * Lovable-preview). De aanroeper kan dan aanbieden om de app in een eigen
19
+ * tabblad te openen, waar de verkenner wel werkt.
20
+ */
21
+ export declare function draaitInIframe(): boolean;
22
+ /** Opent de huidige pagina in een eigen tabblad (buiten het preview-iframe). */
23
+ export declare function openInEigenTabblad(): void;
24
+ /**
25
+ * Opent de mappenverkenner van het besturingssysteem zodat de gebruiker een
26
+ * map kan selecteren. Start in Downloads zodat het pad snel te vinden is.
27
+ * Geeft terug wat er is gebeurd zodat de aanroeper gericht kan reageren.
28
+ */
29
+ export declare function kiesPdfMap(printerId: string): Promise<PdfMapKeuze>;
30
+ /** Is er voor deze printer een map gekozen op deze computer? */
31
+ export declare function heeftPdfMap(printerId: string): Promise<boolean>;
32
+ /** Naam van de gekozen map (voor meldingen), of null. */
33
+ export declare function pdfMapNaam(printerId: string): Promise<string | null>;
34
+ /**
35
+ * Probeert het bestand rechtstreeks in de gekozen map te zetten. Lukt dat niet
36
+ * (andere computer, geen toestemming, browser zonder ondersteuning), dan geeft
37
+ * de functie false terug en kan de aanroeper gewoon downloaden.
38
+ */
39
+ export declare function bewaarInPdfMap(printerId: string, bestandsnaam: string, blob: Blob): Promise<boolean>;
40
+ /** Controleert toestemming zonder een bestandsactie te starten. */
41
+ export declare function pdfMapToestemming(printerId: string): Promise<"granted" | "prompt" | "denied" | "ontbreekt">;
42
+ /**
43
+ * Vraagt vanuit een expliciete gebruikersklik opnieuw schrijftoestemming voor
44
+ * een eerder gekozen map. Achtergrondtaken mogen deze browserprompt niet zelf
45
+ * openen, omdat browsers daarvoor een actieve gebruikershandeling vereisen.
46
+ */
47
+ export declare function herstelPdfMapToestemming(printerId: string): Promise<"granted" | "denied" | "ontbreekt">;
48
+ //# sourceMappingURL=pdfMap.d.ts.map
@@ -0,0 +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"}