@flowselections/layout-maker 1.36.7 → 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.
Files changed (67) hide show
  1. package/README.md +139 -139
  2. package/dist-lib/components/AfdrukKnop.d.ts +19 -19
  3. package/dist-lib/components/NetwerkPrintersDialog.d.ts +7 -7
  4. package/dist-lib/components/PaginaDocument.d.ts +4 -1
  5. package/dist-lib/components/PaginaDocument.d.ts.map +1 -1
  6. package/dist-lib/components/PaginaDocument.js +10 -5
  7. package/dist-lib/components/PakbonBuilderPage.d.ts.map +1 -1
  8. package/dist-lib/components/PakbonBuilderPage.js +4 -2
  9. package/dist-lib/components/PakbonPreview.d.ts +4 -2
  10. package/dist-lib/components/PakbonPreview.d.ts.map +1 -1
  11. package/dist-lib/components/PakbonPreview.js +55 -12
  12. package/dist-lib/components/PakbonSectiesPanel.d.ts +2 -2
  13. package/dist-lib/components/PakbonSectiesPanel.d.ts.map +1 -1
  14. package/dist-lib/components/PakbonSectiesPanel.js +3 -2
  15. package/dist-lib/components/PrinterSetupWizard.js +3 -3
  16. package/dist-lib/components/PrintersRedirectPage.d.ts +1 -1
  17. package/dist-lib/components/PrintersRedirectPage.js +14 -14
  18. package/dist-lib/index.d.ts +4 -2
  19. package/dist-lib/index.d.ts.map +1 -1
  20. package/dist-lib/index.js +3 -2
  21. package/dist-lib/integrations/supabase/auth-middleware.d.ts +1240 -812
  22. package/dist-lib/integrations/supabase/auth-middleware.d.ts.map +1 -1
  23. package/dist-lib/integrations/supabase/client.server.d.ts +1240 -812
  24. package/dist-lib/integrations/supabase/client.server.d.ts.map +1 -1
  25. package/dist-lib/integrations/supabase/client.server.js +1 -1
  26. package/dist-lib/integrations/supabase/moduleClient.d.ts +24008 -0
  27. package/dist-lib/integrations/supabase/moduleClient.d.ts.map +1 -0
  28. package/dist-lib/integrations/supabase/moduleClient.js +29 -0
  29. package/dist-lib/integrations/supabase/types.d.ts +1231 -821
  30. package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
  31. package/dist-lib/lib/aiLayout.functions.d.ts +1240 -812
  32. package/dist-lib/lib/aiLayout.functions.d.ts.map +1 -1
  33. package/dist-lib/lib/aiLayout.server.js +9 -9
  34. package/dist-lib/lib/browserPrint.js +1 -1
  35. package/dist-lib/lib/code128.d.ts +5 -0
  36. package/dist-lib/lib/code128.d.ts.map +1 -0
  37. package/dist-lib/lib/code128.js +51 -0
  38. package/dist-lib/lib/htmlPdf.server.d.ts.map +1 -1
  39. package/dist-lib/lib/htmlPdf.server.js +58 -0
  40. package/dist-lib/lib/layoutPreview.d.ts.map +1 -1
  41. package/dist-lib/lib/layoutPreview.js +11 -8
  42. package/dist-lib/lib/orderData.server.d.ts.map +1 -1
  43. package/dist-lib/lib/orderData.server.js +6 -0
  44. package/dist-lib/lib/pakbonBuilder.d.ts +28 -2
  45. package/dist-lib/lib/pakbonBuilder.d.ts.map +1 -1
  46. package/dist-lib/lib/pakbonBuilder.js +69 -8
  47. package/dist-lib/lib/pakbonHtml.d.ts +14 -2
  48. package/dist-lib/lib/pakbonHtml.d.ts.map +1 -1
  49. package/dist-lib/lib/pakbonHtml.js +155 -63
  50. package/dist-lib/lib/pakbonScan.d.ts +44 -0
  51. package/dist-lib/lib/pakbonScan.d.ts.map +1 -0
  52. package/dist-lib/lib/pakbonScan.js +105 -0
  53. package/dist-lib/lib/printQueue.server.d.ts +7 -0
  54. package/dist-lib/lib/printQueue.server.d.ts.map +1 -1
  55. package/dist-lib/lib/printQueue.server.js +247 -26
  56. package/dist-lib/lib/printService.functions.d.ts +4445 -2733
  57. package/dist-lib/lib/printService.functions.d.ts.map +1 -1
  58. package/dist-lib/lib/productiebonHtml.js +15 -15
  59. package/dist-lib/lib/profielHtml.js +18 -18
  60. package/dist-lib/lib/supabaseAdmin.server.d.ts +5 -5
  61. package/dist-lib/lib/supabaseAdmin.server.js +43 -43
  62. package/dist-lib/lib/supabaseClient.d.ts +1 -3
  63. package/dist-lib/lib/supabaseClient.d.ts.map +1 -1
  64. package/dist-lib/lib/supabaseClient.js +4 -8
  65. package/dist-lib/lib/testpagina.js +3 -3
  66. package/dist-lib/lib/testprint.js +4 -4
  67. package/package.json +151 -151
@@ -7,8 +7,9 @@
7
7
  // layoutconfiguratie en de echte ordergegevens. Zo tonen het live voorbeeld en
8
8
  // de afdruk altijd exact dezelfde secties — inclusief de orderregels.
9
9
  // ============================================================================
10
- import { DEMO_ORDER, conditieContext, groepeerIdentiekeKarren, groepeerIdentiekeLagen, karBundelTekst, fustVerzamellijst, pickMethode, productVeldBreedtes, sectieOnderdrukt, laagBundelTekst, leesPakbonConfig, sectieZichtbaar, sorteerProducten, totalen, vulVeldcodes, } from "./pakbonBuilder.js";
10
+ import { DEMO_ORDER, conditieContext, groepeerIdentiekeKarren, groepeerIdentiekeLagen, karBundelTekst, fustVerzamellijst, pickMethode, pakbonLayoutVariant, productVeldBreedtes, sectieOnderdruktVoorVariant, laagBundelTekst, leesPakbonConfig, sectieZichtbaar, sorteerProducten, totalen, vulVeldcodes, } from "./pakbonBuilder.js";
11
11
  import { haalPrintLogos } from "./logoLibrary.js";
12
+ import { code128BarsHtml } from "./code128.js";
12
13
  // --- kleine hulpjes ---------------------------------------------------------
13
14
  const arr = (v) => (Array.isArray(v) ? v : []);
14
15
  const opt = (s, key, fallback) => s.opties?.[key] ?? fallback;
@@ -24,6 +25,24 @@ const cel = (p, veld) => {
24
25
  return "";
25
26
  return String(p[veld] ?? "");
26
27
  };
28
+ /** Rendert uitsluitend een bestaande barcodewaarde uit de Engine-payload. */
29
+ function barcodeHtml(value, hoogteMm = 15) {
30
+ const tekst = String(value ?? "").trim();
31
+ if (!tekst)
32
+ return "";
33
+ const hoogte = Number.isFinite(hoogteMm) && hoogteMm > 0 ? hoogteMm : 15;
34
+ return `<div class="barcode"><div class="barcode-bars" data-barcode="${esc(tekst)}" style="height:${hoogte}mm">${code128BarsHtml(tekst)}</div><div>${esc(tekst)}</div></div>`;
35
+ }
36
+ /** Barcode van één pakbon; bij één geselecteerde kar mag diens Engine-barcode worden gebruikt. */
37
+ function pakbonBarcode(order) {
38
+ const direct = String(order.barcode ?? order.cart?.barcode ?? "").trim();
39
+ if (direct)
40
+ return direct;
41
+ const karren = Array.isArray(order.karren) ? order.karren : [];
42
+ if (karren.length !== 1)
43
+ return undefined;
44
+ return String(karren[0]?.barcode ?? "").trim() || undefined;
45
+ }
27
46
  /**
28
47
  * Vult ontbrekende onderdelen aan met veilige lege waarden, zodat een module
29
48
  * die maar een deel van de gegevens heeft nooit een lege of stukke pakbon
@@ -38,11 +57,19 @@ export function maakPakbonOrder(input) {
38
57
  out[v] = b[v] === undefined || b[v] === null ? "" : String(b[v]);
39
58
  return out;
40
59
  };
60
+ const cart = (bron["cart"] && typeof bron["cart"] === "object" ? bron["cart"] : {});
61
+ const topBarcode = String(bron["barcode"] ?? cart["barcode"] ?? "").trim();
41
62
  const karren = arr(bron["karren"]).map((k, i) => ({
42
63
  karnummer: Number(k["karnummer"] ?? i + 1),
64
+ barcode: String(k["barcode"] ?? "").trim() || undefined,
43
65
  route: String(k["route"] ?? ""),
44
66
  chauffeur: String(k["chauffeur"] ?? ""),
45
67
  gewicht: String(k["gewicht"] ?? ""),
68
+ cart_label: String(k["cart_label"] ?? cart["cart_label"] ?? "").trim() || undefined,
69
+ cart_index: Number(k["cart_index"] ?? cart["cart_index"] ?? 0) || undefined,
70
+ cart_total: Number(k["cart_total"] ?? cart["cart_total"] ?? 0) || undefined,
71
+ cart_id: String(k["cart_id"] ?? cart["cart_id"] ?? "").trim() || undefined,
72
+ cart_items: Array.isArray(k["cart_items"] ?? cart["cart_items"]) ? (k["cart_items"] ?? cart["cart_items"]) : undefined,
46
73
  gedeeld: k["gedeeld"] === true,
47
74
  lagen: arr(k["lagen"]).map((l, j) => ({
48
75
  laagnummer: Number(l["laagnummer"] ?? j + 1),
@@ -89,6 +116,15 @@ export function maakPakbonOrder(input) {
89
116
  afleveradres: groep("afleveradres", ["naam", "adres", "plaats", "land"]),
90
117
  factuuradres: groep("factuuradres", ["naam", "adres", "plaats", "land"]),
91
118
  transport: groep("transport", ["transporteur", "route", "chauffeur", "track"]),
119
+ barcode: topBarcode || undefined,
120
+ cart: Object.keys(cart).length ? {
121
+ cart_label: String(cart["cart_label"] ?? "").trim() || undefined,
122
+ cart_index: Number(cart["cart_index"] ?? 0) || undefined,
123
+ cart_total: Number(cart["cart_total"] ?? 0) || undefined,
124
+ cart_id: String(cart["cart_id"] ?? "").trim() || undefined,
125
+ cart_items: Array.isArray(cart["cart_items"]) ? cart["cart_items"] : undefined,
126
+ barcode: String(cart["barcode"] ?? "").trim() || undefined,
127
+ } : undefined,
92
128
  karren,
93
129
  };
94
130
  }
@@ -323,6 +359,14 @@ function karKop(s, kar, velden, bundelTekst = "") {
323
359
  info.push(kar.route);
324
360
  if (kar.gedeeld)
325
361
  info.push("gedeelde kar");
362
+ const cartLabel = kar.cart_label ||
363
+ (kar.cart_index && kar.cart_total ? `Kar ${kar.cart_index} van ${kar.cart_total}` : "");
364
+ if (cartLabel)
365
+ info.push(cartLabel);
366
+ if (kar.cart_id)
367
+ info.push(`ID ${kar.cart_id}`);
368
+ if (kar.cart_items?.length)
369
+ info.push(`${kar.cart_items.length} karregels`);
326
370
  const toonNummer = velden.includes("karnummer");
327
371
  if (!toonNummer && !info.length && !bundelTekst)
328
372
  return "";
@@ -344,6 +388,28 @@ function karKop(s, kar, velden, bundelTekst = "") {
344
388
  .map((t) => `<span>${esc(t)}</span>`)
345
389
  .join("")}</span></div>`;
346
390
  }
391
+ function cartRegelsHtml(kar) {
392
+ const regels = arr(kar.cart_items);
393
+ if (!regels.length)
394
+ return "";
395
+ const velden = [
396
+ ["artikelnummer", "Artikel"],
397
+ ["artikelnaam", "Omschrijving"],
398
+ ["omschrijving", "Omschrijving"],
399
+ ["aantal", "Aantal"],
400
+ ["eenheid", "Eenheid"],
401
+ ];
402
+ const rijen = regels.map((regel) => {
403
+ const waarden = velden
404
+ .map(([sleutel]) => String(regel[sleutel] ?? "").trim())
405
+ .filter(Boolean);
406
+ const tekst = waarden.length
407
+ ? waarden.map((waarde) => `<span>${esc(waarde)}</span>`).join(" ")
408
+ : Object.values(regel).map((waarde) => `<span>${esc(String(waarde ?? ""))}</span>`).join(" ");
409
+ return `<div class="cartregel">${tekst}</div>`;
410
+ });
411
+ return `<div class="cartregels"><div class="vet">Karregels</div>${rijen.join("")}</div>`;
412
+ }
347
413
  function laagKop(s, laag, velden, compact, bundelTekst = "") {
348
414
  const tekst = optS(s, "koptekst", "Laag {laag}").replace(/\{laag\}/g, String(laag.laagnummer));
349
415
  const info = [];
@@ -367,14 +433,16 @@ function bundelMelding(tekst) {
367
433
  return `<div class="bundel">${esc(tekst)}</div>`;
368
434
  }
369
435
  function karBlok(s, kar, lagen, zoek, omkaderd, aantal = 1) {
370
- // Bij samenvoegen van identieke karren vervalt het unieke karnummer.
371
- const bundelt = opt(s, "bundel_identiek", true) !== false;
436
+ // Alleen bij écht samengevoegde karren vervalt het unieke karnummer; een
437
+ // losse kar (bijv. met eigen Engine-barcode) houdt zijn eigen gegevens.
438
+ const bundelt = opt(s, "bundel_identiek", true) !== false && aantal > 1;
372
439
  const velden = lijst(s, "velden").filter((v) => !(bundelt && (v === "karnummer" || v === "barcode" || v === "qrcode")));
373
440
  const lagenSectie = zoek("lagen");
374
441
  const productSectie = zoek("producten");
375
442
  const fustSectie = zoek("fusten");
376
443
  const lagenModus = lagenSectie ? optS(lagenSectie, "modus", "apart") : "geen";
377
444
  let inhoud = karKop(s, kar, velden, karBundelTekst(aantal));
445
+ inhoud += cartRegelsHtml(kar);
378
446
  if (lagen.length && lagenSectie && lagenModus !== "geen") {
379
447
  const lVelden = lijst(lagenSectie, "velden");
380
448
  const toonProducten = lagenModus !== "nummer" && lagenModus !== "regel";
@@ -392,6 +460,10 @@ function karBlok(s, kar, lagen, zoek, omkaderd, aantal = 1) {
392
460
  }
393
461
  if (fustSectie && optS(fustSectie, "modus", "totaal") === "kar")
394
462
  inhoud += fustTabel(lijst(fustSectie, "velden"), lagen.flatMap((l) => l.fusten), `Fusten kar ${kar.karnummer}`);
463
+ // Een volle-karpakbon kan meerdere karren/pagina's bevatten. De barcode
464
+ // hoort daarom in het eigen karblok en niet één keer onderaan de serie.
465
+ if (kar.barcode)
466
+ inhoud += barcodeHtml(kar.barcode);
395
467
  return `<div class="karblok${omkaderd ? " omkaderd" : ""}">${inhoud}</div>`;
396
468
  }
397
469
  const FUST_KOLOM_LABELS = {
@@ -558,7 +630,7 @@ function sectieHtml(s, order, zoek, opties) {
558
630
  return titelHtml(s) + body;
559
631
  }
560
632
  case "pickmethode":
561
- if (optS(s, "methode", "kar") !== "fusten")
633
+ if (optS(s, "methode", "kar") === "kar")
562
634
  return "";
563
635
  return fustVerzamellijstHtml(order, s);
564
636
  case "karren":
@@ -591,8 +663,11 @@ function sectieHtml(s, order, zoek, opties) {
591
663
  return `<div class="${opt(s, "kader", false) ? "kader" : ""}" style="font-size:${Number(opt(s, "fontgrootte", 9))}pt">${esc(vulVeldcodes(optS(s, "tekst", ""), order))}</div>`;
592
664
  case "qrcode":
593
665
  return `<div><div class="qr" style="width:${Number(opt(s, "grootte", 25))}mm;height:${Number(opt(s, "grootte", 25))}mm"></div><div class="klein">${esc(vulVeldcodes(optS(s, "waarde", ""), order))}</div></div>`;
594
- case "barcode":
595
- return `<div><div class="streepjes" style="height:${Number(opt(s, "hoogte", 15))}mm"></div><div>${esc(vulVeldcodes(optS(s, "waarde", ""), order))}</div></div>`;
666
+ case "barcode": {
667
+ // Alleen de Engine-waarde mag op de pakbon komen; nooit een lokaal
668
+ // gegenereerde orderwaarde of een layout-default als barcode gebruiken.
669
+ return barcodeHtml(pakbonBarcode(order), Number(opt(s, "hoogte", 15)));
670
+ }
596
671
  case "afbeelding": {
597
672
  const url = optS(s, "url", "");
598
673
  return url
@@ -610,20 +685,36 @@ export function renderPakbonBody(config, order, opties = {}) {
610
685
  const ctx = conditieContext(order);
611
686
  const alleSecties = arr(cfg.secties);
612
687
  const methode = pickMethode(alleSecties);
613
- const secties = alleSecties.filter((s) => sectieZichtbaar(s, ctx) && !sectieOnderdrukt(s.type, methode));
688
+ const variant = pakbonLayoutVariant(opties.layoutVariant);
689
+ const secties = alleSecties.filter((s) => sectieZichtbaar(s, ctx) && !sectieOnderdruktVoorVariant(s.type, methode, variant));
690
+ const heeftKarBarcodes = secties.some((s) => s.type === "karren") &&
691
+ (Array.isArray(order.karren) ? order.karren : []).some((kar) => Boolean(String(kar.barcode ?? "").trim()));
614
692
  const zoek = (t) => secties.find((s) => s.type === t);
615
693
  const opts = { ...opties, logoId: opties.logoId ?? cfg.logo?.logoId ?? null };
616
- const kop = documentLogoHtml(cfg.logo, opts);
694
+ const variantKop = variant
695
+ ? `<div class="pakbonvariant">${variant === "full_cart" ? "HELE KAR" : "DEELKAR"}</div>`
696
+ : "";
697
+ const kop = variantKop + documentLogoHtml(cfg.logo, opts);
617
698
  const totalenSectie = zoek("totalen");
618
699
  const totalenElkePagina = !!totalenSectie && opt(totalenSectie, "elke_pagina", false) === true;
619
700
  const inhoud = secties
620
701
  .filter((s) => !(s.type === "totalen" && totalenElkePagina))
702
+ .filter((s) => !(s.type === "barcode" && heeftKarBarcodes))
621
703
  .map((s) => s.type === "karren" ? karrenHtml(s, order, zoek) : blokFrame(s, sectieHtml(s, order, zoek, opts)))
622
704
  .join("");
705
+ const pickSectie = alleSecties.find((s) => s.type === "pickmethode");
706
+ const pickInstellingen = pickSectie ?? alleSecties[0];
707
+ // Elke pakbon krijgt de Engine-barcode wanneer die is aangeleverd. Een
708
+ // expliciet barcodeblok rendert hem zelf; anders voegen we hem één keer
709
+ // automatisch toe. Zonder Engine-barcode wordt nooit een waarde verzonnen.
710
+ const automatischeBarcode = !heeftKarBarcodes && !secties.some((s) => s.type === "barcode");
711
+ const barcode = automatischeBarcode
712
+ ? barcodeHtml(pakbonBarcode(order), Number(pickInstellingen ? opt(pickInstellingen, "barcodeHoogte", 15) : 15))
713
+ : "";
623
714
  const voet = totalenElkePagina && totalenSectie
624
715
  ? `<div class="paginavoet">${sectieHtml(totalenSectie, order, zoek, opts)}</div>`
625
716
  : "";
626
- return kop + inhoud + voet;
717
+ return kop + inhoud + (barcode ? `<div class="blok">${barcode}</div>` : "") + voet;
627
718
  }
628
719
  /** Het documentlogo bovenaan de pakbon. */
629
720
  function documentLogoHtml(logoCfg, opties) {
@@ -647,60 +738,61 @@ export function renderPakbonDocument(config, order, opties = {}) {
647
738
  // leest dit uit de meta, zodat de wachtrij niets extra hoeft door te geven.
648
739
  const totalen = arr(leesPakbonConfig(config).secties).find((s) => s.type === "totalen");
649
740
  const paginanummers = !!totalen && opt(totalen, "toon_paginanummer", false) === true;
650
- return `<!doctype html><html lang="nl"><head><meta charset="utf-8" />
651
- <meta name="pdf-paginanummers" content="${paginanummers ? "ja" : "nee"}" />
652
- <title>${esc(opties.titel ?? "Pakbon")}</title>
653
-
654
- <style>
655
- @page { size: ${b}mm ${h}mm; margin: ${marge}mm; }
656
- * { box-sizing: border-box; }
657
- html, body { margin: 0; padding: 0; }
658
- body { font-family: ${font}; font-size: ${grootte}pt; color: #111827; line-height: 1.35; }
659
- .blok { margin-bottom: 2mm; }
660
- .sectitel { font-size: 90%; font-weight: 600; text-transform: uppercase; color: #6b7280; margin-bottom: 0.6mm; }
661
- .fustgroepkop { display: flex; justify-content: space-between; font-weight: 600; color: #111827; border-bottom: 0.45mm solid #111827; padding-bottom: 0.5mm; margin-bottom: 0.6mm; }
662
-
663
- .vet { font-weight: 600; }
664
- .kv { display: flex; justify-content: space-between; gap: 3mm; }
665
- .kv .k { color: #6b7280; }
666
- .kv .v { font-weight: 500; }
667
- .grid2 { display: grid; grid-template-columns: 1fr 1fr; gap: 0 6mm; }
668
- .kader { border: 0.25mm solid #d1d5db; border-radius: 1mm; padding: 1.5mm; }
669
- table.regels { width: 100%; border-collapse: collapse; }
670
- table.regels.vast { table-layout: fixed; }
671
- table.regels.vast td, table.regels.vast th { overflow-wrap: anywhere; }
672
- table.regels th, table.regels td { padding: 0.7mm 1mm; vertical-align: top; }
673
- table.regels th { font-size: 90%; text-transform: uppercase; color: #6b7280; font-weight: 600; text-align: left; }
674
- .rand { border: 0.2mm solid #d1d5db; }
675
- .onder { border-bottom: 0.2mm solid #d1d5db; }
676
- table.regels.compact th, table.regels.compact td { padding: 0.3mm 0.8mm; }
677
- tr.zebra td { background: #f3f4f6; }
678
- .plijst { margin: 0; padding-left: 4mm; }
679
- .groep { margin-bottom: 1.5mm; }
680
- .regelrij { display: flex; gap: 2mm; }
681
- .tweekolom { display: grid; grid-template-columns: 1fr 1fr; gap: 3mm; }
682
- .karblok { margin-bottom: 2mm; }
683
- .karblok.omkaderd { border: 0.25mm solid #d1d5db; border-radius: 1mm; padding: 1.5mm; }
684
- .karkop { display: flex; justify-content: space-between; align-items: center; padding: 0.8mm 1.5mm; border-radius: 1mm; margin-bottom: 1mm; }
685
- .bundel { display: inline-block; border: 0.3mm dashed #6b7280; border-radius: 1mm; padding: 0.4mm 1.5mm; margin-bottom: 1mm; font-weight: 700; text-transform: uppercase; letter-spacing: 0.2mm; font-size: 90%; }
686
- .laag { margin-bottom: 1.5mm; }
687
- .laagkop { display: flex; justify-content: space-between; font-weight: 500; border-bottom: 0.2mm solid #d1d5db; padding-bottom: 0.4mm; margin-bottom: 0.6mm; }
688
- .laagkop.compact { border-bottom: none; }
689
- .meta { display: flex; gap: 3mm; font-size: 88%; font-weight: 400; color: #4b5563; }
690
- .totalen { display: flex; flex-wrap: wrap; gap: 5mm; border-top: 0.25mm solid #d1d5db; padding-top: 1.2mm; font-weight: 500; }
691
- .paginavoet { position: fixed; bottom: 0; left: 0; right: 0; background: #fff; border-top: 0.35mm solid #9ca3af; padding-top: 1mm; }
692
- .handtekening { margin-top: 3mm; }
693
- .lijnveld { margin-top: 4mm; border-bottom: 0.25mm solid #6b7280; padding-bottom: 3mm; }
694
- .lijnveld.hoog { padding-bottom: 10mm; }
695
- .lijn { border-top: 0.25mm solid #d1d5db; margin: 1.5mm 0; }
696
- .balk { height: 1mm; background: #374151; margin: 1.5mm 0; }
697
- .qr { border: 0.25mm dashed #9ca3af; }
698
- .streepjes { width: 45mm; background: repeating-linear-gradient(to right, #111827 0 2px, transparent 2px 5px); }
699
- .klein { font-size: 85%; color: #6b7280; }
700
- .breekvoor { break-before: page; page-break-before: always; }
701
- .heelhouden { break-inside: avoid; page-break-inside: avoid; }
702
- img { max-width: 100%; }
703
- </style></head>
741
+ return `<!doctype html><html lang="nl"><head><meta charset="utf-8" />
742
+ <meta name="pdf-paginanummers" content="${paginanummers ? "ja" : "nee"}" />
743
+ <title>${esc(opties.titel ?? "Pakbon")}</title>
744
+
745
+ <style>
746
+ @page { size: ${b}mm ${h}mm; margin: ${marge}mm; }
747
+ * { box-sizing: border-box; }
748
+ html, body { margin: 0; padding: 0; }
749
+ body { font-family: ${font}; font-size: ${grootte}pt; color: #111827; line-height: 1.35; }
750
+ .pakbonvariant { border-top: 0.6mm solid #111827; border-bottom: 0.6mm solid #111827; padding: 2mm 1mm; margin-bottom: 2mm; text-align: center; font-size: 15pt; font-weight: 800; line-height: 1.1; }
751
+ .blok { margin-bottom: 2mm; }
752
+ .sectitel { font-size: 90%; font-weight: 600; text-transform: uppercase; color: #6b7280; margin-bottom: 0.6mm; }
753
+ .fustgroepkop { display: flex; justify-content: space-between; font-weight: 600; color: #111827; border-bottom: 0.45mm solid #111827; padding-bottom: 0.5mm; margin-bottom: 0.6mm; }
754
+
755
+ .vet { font-weight: 600; }
756
+ .kv { display: flex; justify-content: space-between; gap: 3mm; }
757
+ .kv .k { color: #6b7280; }
758
+ .kv .v { font-weight: 500; }
759
+ .grid2 { display: grid; grid-template-columns: 1fr 1fr; gap: 0 6mm; }
760
+ .kader { border: 0.25mm solid #d1d5db; border-radius: 1mm; padding: 1.5mm; }
761
+ table.regels { width: 100%; border-collapse: collapse; }
762
+ table.regels.vast { table-layout: fixed; }
763
+ table.regels.vast td, table.regels.vast th { overflow-wrap: anywhere; }
764
+ table.regels th, table.regels td { padding: 0.7mm 1mm; vertical-align: top; }
765
+ table.regels th { font-size: 90%; text-transform: uppercase; color: #6b7280; font-weight: 600; text-align: left; }
766
+ .rand { border: 0.2mm solid #d1d5db; }
767
+ .onder { border-bottom: 0.2mm solid #d1d5db; }
768
+ table.regels.compact th, table.regels.compact td { padding: 0.3mm 0.8mm; }
769
+ tr.zebra td { background: #f3f4f6; }
770
+ .plijst { margin: 0; padding-left: 4mm; }
771
+ .groep { margin-bottom: 1.5mm; }
772
+ .regelrij { display: flex; gap: 2mm; }
773
+ .tweekolom { display: grid; grid-template-columns: 1fr 1fr; gap: 3mm; }
774
+ .karblok { margin-bottom: 2mm; }
775
+ .karblok.omkaderd { border: 0.25mm solid #d1d5db; border-radius: 1mm; padding: 1.5mm; }
776
+ .karkop { display: flex; justify-content: space-between; align-items: center; padding: 0.8mm 1.5mm; border-radius: 1mm; margin-bottom: 1mm; }
777
+ .bundel { display: inline-block; border: 0.3mm dashed #6b7280; border-radius: 1mm; padding: 0.4mm 1.5mm; margin-bottom: 1mm; font-weight: 700; text-transform: uppercase; letter-spacing: 0.2mm; font-size: 90%; }
778
+ .laag { margin-bottom: 1.5mm; }
779
+ .laagkop { display: flex; justify-content: space-between; font-weight: 500; border-bottom: 0.2mm solid #d1d5db; padding-bottom: 0.4mm; margin-bottom: 0.6mm; }
780
+ .laagkop.compact { border-bottom: none; }
781
+ .meta { display: flex; gap: 3mm; font-size: 88%; font-weight: 400; color: #4b5563; }
782
+ .totalen { display: flex; flex-wrap: wrap; gap: 5mm; border-top: 0.25mm solid #d1d5db; padding-top: 1.2mm; font-weight: 500; }
783
+ .paginavoet { position: fixed; bottom: 0; left: 0; right: 0; background: #fff; border-top: 0.35mm solid #9ca3af; padding-top: 1mm; }
784
+ .handtekening { margin-top: 3mm; }
785
+ .lijnveld { margin-top: 4mm; border-bottom: 0.25mm solid #6b7280; padding-bottom: 3mm; }
786
+ .lijnveld.hoog { padding-bottom: 10mm; }
787
+ .lijn { border-top: 0.25mm solid #d1d5db; margin: 1.5mm 0; }
788
+ .balk { height: 1mm; background: #374151; margin: 1.5mm 0; }
789
+ .qr { border: 0.25mm dashed #9ca3af; }
790
+ .barcode { display:flex; flex-direction:column; align-items:center; gap:0.8mm; } .barcode-bars { width:45mm; display:flex; background:#fff; }
791
+ .klein { font-size: 85%; color: #6b7280; }
792
+ .breekvoor { break-before: page; page-break-before: always; }
793
+ .heelhouden { break-inside: avoid; page-break-inside: avoid; }
794
+ img { max-width: 100%; }
795
+ </style></head>
704
796
  <body>${body}</body></html>`;
705
797
  }
706
798
  /** Handig voor testen: het document met de voorbeeldgegevens. */
@@ -0,0 +1,44 @@
1
+ import { type PakbonLayoutVariant } from "./pakbonBuilder.js";
2
+ export interface PakbonScanRegel {
3
+ artikelnummer: string;
4
+ artikelnaam: string;
5
+ potmaat: string;
6
+ kleur: string;
7
+ fustsoort: string;
8
+ planten_per_fust: number;
9
+ aantal: number;
10
+ fust_aantal: number;
11
+ }
12
+ export interface PakbonScanInhoud {
13
+ barcode: string;
14
+ order_ref: string | null;
15
+ order_nummer: string | null;
16
+ klant_naam: string | null;
17
+ klant_nummer: string | null;
18
+ cart_id: string | null;
19
+ cart_label: string | null;
20
+ cart_index: number | null;
21
+ cart_total: number | null;
22
+ layout_variant: PakbonLayoutVariant | null;
23
+ layout_id: string | null;
24
+ print_job_id: string | null;
25
+ is_herprint: boolean;
26
+ regels: PakbonScanRegel[];
27
+ }
28
+ /**
29
+ * Bouwt de scanregel uit de order zoals die op de pakbon is gerenderd.
30
+ * Bij een deelkar (`partial_order`) staan uitsluitend de fusten van die
31
+ * deelkar op de pakbon, dus ook uitsluitend die regels in de registratie.
32
+ */
33
+ export declare function bouwPakbonScanInhoud(order: unknown, opties: {
34
+ barcode: unknown;
35
+ layoutVariant?: PakbonLayoutVariant | null;
36
+ layoutId?: string | null;
37
+ printJobId?: string | null;
38
+ isHerprint?: boolean;
39
+ }): PakbonScanInhoud | null;
40
+ /** Slaat de scaninhoud op; een herprint werkt dezelfde barcode bij. */
41
+ export declare function bewaarPakbonScanInhoud(db: {
42
+ from: (tabel: string) => any;
43
+ }, inhoud: PakbonScanInhoud | null): Promise<void>;
44
+ //# sourceMappingURL=pakbonScan.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pakbonScan.d.ts","sourceRoot":"","sources":["../../src/lib/pakbonScan.ts"],"names":[],"mappings":"AASA,OAAO,EAAqB,KAAK,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAE9E,MAAM,WAAW,eAAe;IAC9B,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,gBAAgB,EAAE,MAAM,CAAC;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,cAAc,EAAE,mBAAmB,GAAG,IAAI,CAAC;IAC3C,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,WAAW,EAAE,OAAO,CAAC;IACrB,MAAM,EAAE,eAAe,EAAE,CAAC;CAC3B;AAWD;;;;GAIG;AACH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,OAAO,EACd,MAAM,EAAE;IACN,OAAO,EAAE,OAAO,CAAC;IACjB,aAAa,CAAC,EAAE,mBAAmB,GAAG,IAAI,CAAC;IAC3C,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB,GACA,gBAAgB,GAAG,IAAI,CA8EzB;AAED,uEAAuE;AACvE,wBAAsB,sBAAsB,CAC1C,EAAE,EAAE;IAAE,IAAI,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,GAAG,CAAA;CAAE,EACpC,MAAM,EAAE,gBAAgB,GAAG,IAAI,GAC9B,OAAO,CAAC,IAAI,CAAC,CAMf"}
@@ -0,0 +1,105 @@
1
+ // ============================================================================
2
+ // src/lib/pakbonScan.ts — registreert wat er achter een pakbonbarcode zit
3
+ // ----------------------------------------------------------------------------
4
+ // De barcodewaarde blijft eigendom van de Logistics Engine: deze module
5
+ // genereert of wijzigt nooit een barcode. Wel legt zij vast WAT er op de
6
+ // gedrukte pakbon staat, zodat een scanner daaruit een afleverbrief kan
7
+ // opbouwen: order, klant, kar en de gedrukte regels.
8
+ // ============================================================================
9
+ import { fustVerzamellijst } from "./pakbonBuilder.js";
10
+ function tekst(waarde) {
11
+ return waarde === undefined || waarde === null ? "" : String(waarde).trim();
12
+ }
13
+ function getal(waarde) {
14
+ const n = Number(waarde);
15
+ return Number.isFinite(n) && n !== 0 ? n : null;
16
+ }
17
+ /**
18
+ * Bouwt de scanregel uit de order zoals die op de pakbon is gerenderd.
19
+ * Bij een deelkar (`partial_order`) staan uitsluitend de fusten van die
20
+ * deelkar op de pakbon, dus ook uitsluitend die regels in de registratie.
21
+ */
22
+ export function bouwPakbonScanInhoud(order, opties) {
23
+ const barcode = tekst(opties.barcode);
24
+ if (!barcode)
25
+ return null;
26
+ const o = (order && typeof order === "object" ? order : {});
27
+ const alleKarren = Array.isArray(o["karren"]) ? o["karren"] : [];
28
+ const cart = (o["cart"] && typeof o["cart"] === "object" ? o["cart"] : {});
29
+ const cartId = tekst(cart["cart_id"]);
30
+ // Een order kan meerdere karren bevatten. Voor een deelkar mag de scanner
31
+ // nooit de inhoud van de andere karren terugkrijgen: selecteer dus eerst op
32
+ // het door de Engine meegestuurde cart_id.
33
+ const passendeKarren = cartId
34
+ ? alleKarren.filter((kar) => tekst(kar["cart_id"]) === cartId)
35
+ : alleKarren;
36
+ // Bij partial_order is cart_items de autoritatieve inhoud van deze deelkar.
37
+ // Alleen als die lijst ontbreekt, gebruiken we de passende karregels.
38
+ const cartItems = Array.isArray(cart["cart_items"]) ? cart["cart_items"] : [];
39
+ const karren = passendeKarren;
40
+ const eersteKar = (karren[0] ?? {});
41
+ const regels = [];
42
+ if (opties.layoutVariant !== "partial_order" || !cartItems.length) {
43
+ for (const groep of fustVerzamellijst(karren)) {
44
+ for (const pr of groep.producten) {
45
+ const bestaand = regels.find((r) => r.artikelnummer === pr.artikelnummer && r.fustsoort === (pr.fustsoort || groep.fusttype));
46
+ if (bestaand) {
47
+ bestaand.aantal += Number(pr.aantal) || 0;
48
+ continue;
49
+ }
50
+ regels.push({
51
+ artikelnummer: pr.artikelnummer,
52
+ artikelnaam: pr.artikelnaam,
53
+ potmaat: pr.potmaat,
54
+ kleur: pr.kleur,
55
+ fustsoort: pr.fustsoort || groep.fusttype,
56
+ planten_per_fust: Number(pr.planten_per_fust) || 0,
57
+ aantal: Number(pr.aantal) || 0,
58
+ fust_aantal: Number(groep.aantal) || 0,
59
+ });
60
+ }
61
+ }
62
+ }
63
+ // De Engine kan de deelkarinhoud direct als cart_items meesturen. Die lijst
64
+ // wint bij partial_order en bevat dan uitsluitend deze barcode-kar.
65
+ if (opties.layoutVariant === "partial_order" && cartItems.length) {
66
+ for (const item of cartItems) {
67
+ regels.push({
68
+ artikelnummer: tekst(item["artikelnummer"] ?? item["artikelcode"]),
69
+ artikelnaam: tekst(item["artikelnaam"] ?? item["product_name"] ?? item["omschrijving"]),
70
+ potmaat: tekst(item["potmaat"]),
71
+ kleur: tekst(item["kleur"]),
72
+ fustsoort: tekst(item["fustsoort"] ?? item["fusttype"]),
73
+ planten_per_fust: Number(item["planten_per_fust"] ?? 0) || 0,
74
+ aantal: Number(item["aantal"] ?? item["quantity"] ?? 0) || 0,
75
+ fust_aantal: Number(item["fust_aantal"] ?? item["aantal_fusten"] ?? 0) || 0,
76
+ });
77
+ }
78
+ }
79
+ return {
80
+ barcode,
81
+ order_ref: tekst(o["order"]?.referentie) || null,
82
+ order_nummer: tekst(o["order"]?.nummer) || null,
83
+ klant_naam: tekst(o["klant"]?.naam) || null,
84
+ klant_nummer: tekst(o["klant"]?.nummer) || null,
85
+ cart_id: tekst(cart["cart_id"] ?? eersteKar["cart_id"]) || null,
86
+ cart_label: tekst(cart["cart_label"] ?? eersteKar["cart_label"]) || null,
87
+ cart_index: getal(cart["cart_index"] ?? eersteKar["cart_index"]),
88
+ cart_total: getal(cart["cart_total"] ?? eersteKar["cart_total"]),
89
+ layout_variant: opties.layoutVariant ?? null,
90
+ layout_id: opties.layoutId ?? null,
91
+ print_job_id: opties.printJobId ?? null,
92
+ is_herprint: opties.isHerprint === true,
93
+ regels,
94
+ };
95
+ }
96
+ /** Slaat de scaninhoud op; een herprint werkt dezelfde barcode bij. */
97
+ export async function bewaarPakbonScanInhoud(db, inhoud) {
98
+ if (!inhoud)
99
+ return;
100
+ const { error } = await db
101
+ .from("pakbon_barcode_inhoud")
102
+ .upsert({ ...inhoud, updated_at: new Date().toISOString() }, { onConflict: "barcode" });
103
+ if (error)
104
+ throw new Error(error.message ?? "Opslaan van pakbon-scaninhoud mislukt");
105
+ }
@@ -12,6 +12,10 @@ export declare function controleerClient(db: PrintDb): Promise<string | null>;
12
12
  export declare class GeenDocumentFout extends Error {
13
13
  name: string;
14
14
  }
15
+ /** De Engine heeft de taak gepland, maar de layout is nog niet aangeleverd. */
16
+ export declare class WachtOpLayoutFout extends Error {
17
+ name: string;
18
+ }
15
19
  /**
16
20
  * `ontwerpWint` = deze layout is een echt ontwerp van deze module (actief, geen
17
21
  * Floriday-systeemlayout) en gaat daarom vóór meegestuurde HTML. Er is bewust
@@ -30,6 +34,8 @@ export type BronUitkomst = {
30
34
  };
31
35
  /** Zet een binnenkomend documenttype om naar de sleutel van deze module. */
32
36
  export declare function normaliseerDocumenttype(type: unknown): string;
37
+ /** Het layout-id zoals de Engine het aanlevert (kolom of payload). */
38
+ export declare function leesEngineLayoutId(job: Job): string;
33
39
  /**
34
40
  * Haalt een kant-en-klare PDF van de opdracht op (klantstickers, standaard
35
41
  * Floriday-documenten). Die gaat ongewijzigd naar de printer.
@@ -50,6 +56,7 @@ export interface WachtrijResultaat {
50
56
  opgepakt: number;
51
57
  verstuurd: number;
52
58
  mislukt: number;
59
+ wachtend: number;
53
60
  bijgewerkt: number;
54
61
  }
55
62
  /** Eén ronde: vastgelopen opdrachten opruimen, afdrukken en statussen bijwerken. */
@@ -1 +1 @@
1
- {"version":3,"file":"printQueue.server.d.ts","sourceRoot":"","sources":["../../src/lib/printQueue.server.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAsB5D,KAAK,GAAG,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;AAE/B,mFAAmF;AACnF,MAAM,MAAM,OAAO,GAAG,cAAc,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;AAEpD;;;;GAIG;AACH,wBAAsB,gBAAgB,CAAC,EAAE,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAW1E;AAUD,2EAA2E;AAC3E,qBAAa,gBAAiB,SAAQ,KAAK;IAChC,IAAI,SAAsB;CACpC;AAED;;;;;GAKG;AACH,MAAM,MAAM,WAAW,GAAG;IAAE,MAAM,EAAE,GAAG,GAAG,IAAI,CAAC;IAAC,WAAW,EAAE,OAAO,CAAA;CAAE,CAAC;AAEvE,yEAAyE;AACzE,MAAM,MAAM,aAAa,GAAG,YAAY,GAAG,cAAc,GAAG,aAAa,GAAG,aAAa,CAAC;AAE1F,6EAA6E;AAC7E,MAAM,MAAM,YAAY,GAAG;IAAE,IAAI,EAAE,aAAa,CAAA;CAAE,CAAC;AAcnD,4EAA4E;AAC5E,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,CAI7D;AAkHD;;;GAGG;AACH,wBAAsB,cAAc,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CA+BnF;AAwQD;;;;GAIG;AACH,wBAAsB,QAAQ,CAC5B,EAAE,EAAE,OAAO,EACX,GAAG,EAAE,GAAG,EACR,KAAK,EAAE,WAAW,EAClB,QAAQ,CAAC,EAAE,YAAY,GACtB,OAAO,CAAC,MAAM,CAAC,CA8IjB;AAGD;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAY1D;AAmVD,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,oFAAoF;AACpF,wBAAsB,eAAe,CAAC,EAAE,EAAE,OAAO,EAAE,GAAG,SAAI,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAwCtF;AAED;;;;GAIG;AACH,wBAAsB,mBAAmB,CACvC,EAAE,EAAE,OAAO,EACX,GAAG,EAAE,GAAG,GACP,OAAO,CAAC;IAAE,KAAK,EAAE,WAAW,CAAC;IAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC,CA0B1D"}
1
+ {"version":3,"file":"printQueue.server.d.ts","sourceRoot":"","sources":["../../src/lib/printQueue.server.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAwB5D,KAAK,GAAG,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;AAE/B,mFAAmF;AACnF,MAAM,MAAM,OAAO,GAAG,cAAc,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;AAEpD;;;;GAIG;AACH,wBAAsB,gBAAgB,CAAC,EAAE,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAW1E;AAUD,2EAA2E;AAC3E,qBAAa,gBAAiB,SAAQ,KAAK;IAChC,IAAI,SAAsB;CACpC;AAED,+EAA+E;AAC/E,qBAAa,iBAAkB,SAAQ,KAAK;IACjC,IAAI,SAAuB;CACrC;AAED;;;;;GAKG;AACH,MAAM,MAAM,WAAW,GAAG;IAAE,MAAM,EAAE,GAAG,GAAG,IAAI,CAAC;IAAC,WAAW,EAAE,OAAO,CAAA;CAAE,CAAC;AAEvE,yEAAyE;AACzE,MAAM,MAAM,aAAa,GAAG,YAAY,GAAG,cAAc,GAAG,aAAa,GAAG,aAAa,CAAC;AAE1F,6EAA6E;AAC7E,MAAM,MAAM,YAAY,GAAG;IAAE,IAAI,EAAE,aAAa,CAAA;CAAE,CAAC;AAcnD,4EAA4E;AAC5E,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,CAI7D;AASD,sEAAsE;AACtE,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,GAAG,GAAG,MAAM,CAQnD;AA4LD;;;GAGG;AACH,wBAAsB,cAAc,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CA+BnF;AA8RD;;;;GAIG;AACH,wBAAsB,QAAQ,CAC5B,EAAE,EAAE,OAAO,EACX,GAAG,EAAE,GAAG,EACR,KAAK,EAAE,WAAW,EAClB,QAAQ,CAAC,EAAE,YAAY,GACtB,OAAO,CAAC,MAAM,CAAC,CA2OjB;AAGD;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAY1D;AAuWD,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,oFAAoF;AACpF,wBAAsB,eAAe,CAAC,EAAE,EAAE,OAAO,EAAE,GAAG,SAAI,GAAG,OAAO,CAAC,iBAAiB,CAAC,CA2CtF;AAED;;;;GAIG;AACH,wBAAsB,mBAAmB,CACvC,EAAE,EAAE,OAAO,EACX,GAAG,EAAE,GAAG,GACP,OAAO,CAAC;IAAE,KAAK,EAAE,WAAW,CAAC;IAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC,CA4B1D"}