@flowselections/floriday-voorraad 1.0.122 → 1.0.125

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 (45) hide show
  1. package/dist-lib/components/voorraad/ArtikelWizard.d.ts +14 -1
  2. package/dist-lib/components/voorraad/ArtikelWizard.d.ts.map +1 -1
  3. package/dist-lib/components/voorraad/ArtikelWizard.js +88 -26
  4. package/dist-lib/components/voorraad/FloridayInfoCard.d.ts.map +1 -1
  5. package/dist-lib/components/voorraad/FloridayInfoCard.js +4 -2
  6. package/dist-lib/components/voorraad/FloridayLoadCarriers.d.ts.map +1 -1
  7. package/dist-lib/components/voorraad/FloridayLoadCarriers.js +3 -2
  8. package/dist-lib/components/voorraad/NieuwArtikelPage.d.ts.map +1 -1
  9. package/dist-lib/components/voorraad/NieuwArtikelPage.js +72 -6
  10. package/dist-lib/components/voorraad/PublishProgressCard.d.ts +7 -0
  11. package/dist-lib/components/voorraad/PublishProgressCard.d.ts.map +1 -1
  12. package/dist-lib/components/voorraad/PublishProgressCard.js +22 -1
  13. package/dist-lib/components/voorraad/StockSyncStatus.js +1 -1
  14. package/dist-lib/hooks/useLoadCarriers.d.ts +12 -0
  15. package/dist-lib/hooks/useLoadCarriers.d.ts.map +1 -0
  16. package/dist-lib/hooks/useLoadCarriers.js +49 -0
  17. package/dist-lib/hooks/usePublishJob.d.ts +1 -0
  18. package/dist-lib/hooks/usePublishJob.d.ts.map +1 -1
  19. package/dist-lib/hooks/usePublishJob.js +20 -1
  20. package/dist-lib/hooks/useStockSyncHealth.d.ts +3 -1
  21. package/dist-lib/hooks/useStockSyncHealth.d.ts.map +1 -1
  22. package/dist-lib/hooks/useStockSyncHealth.js +68 -19
  23. package/dist-lib/integrations/supabase/types.d.ts +192 -0
  24. package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
  25. package/dist-lib/lib/__tests__/erp-test-123.test.d.ts +2 -0
  26. package/dist-lib/lib/__tests__/erp-test-123.test.d.ts.map +1 -0
  27. package/dist-lib/lib/__tests__/erp-test-123.test.js +43 -0
  28. package/dist-lib/lib/__tests__/load-carriers.test.d.ts +2 -0
  29. package/dist-lib/lib/__tests__/load-carriers.test.d.ts.map +1 -0
  30. package/dist-lib/lib/__tests__/load-carriers.test.js +15 -0
  31. package/dist-lib/lib/buyer-organizations.d.ts +26 -0
  32. package/dist-lib/lib/buyer-organizations.d.ts.map +1 -0
  33. package/dist-lib/lib/buyer-organizations.js +41 -0
  34. package/dist-lib/lib/floriday-customer-offer.functions.d.ts +4 -0
  35. package/dist-lib/lib/floriday-customer-offer.functions.d.ts.map +1 -1
  36. package/dist-lib/lib/floriday-customer-offer.functions.js +54 -16
  37. package/dist-lib/lib/floriday-info.d.ts +0 -1
  38. package/dist-lib/lib/floriday-info.d.ts.map +1 -1
  39. package/dist-lib/lib/floriday-info.js +0 -8
  40. package/dist-lib/lib/simple-packing.d.ts +31 -0
  41. package/dist-lib/lib/simple-packing.d.ts.map +1 -0
  42. package/dist-lib/lib/simple-packing.js +62 -0
  43. package/package.json +1 -1
  44. package/sql/AGENTS.md +2 -1
  45. package/sql/floriday-voorraad-schema.sql +328 -5
@@ -1,7 +1,20 @@
1
- export declare function ArtikelWizard({ draftId: initialDraftId, tradeItemId, productId, resumeJobId, }?: {
1
+ import { type WizardData } from "../../lib/floriday-payload.js";
2
+ import { type SimplePacking } from "../../lib/simple-packing.js";
3
+ export interface WizardInitial {
4
+ name: string;
5
+ articleCode: string;
6
+ stockPieces: number;
7
+ pricePerPiece: number;
8
+ packing?: SimplePacking;
9
+ }
10
+ /** Neemt naam, code, voorraad, prijs en (indien compleet) belading over. */
11
+ export declare function applyWizardInitial(base: WizardData, init?: WizardInitial): WizardData;
12
+ export declare function ArtikelWizard({ draftId: initialDraftId, tradeItemId, productId, resumeJobId, initial, }?: {
2
13
  draftId?: string;
3
14
  tradeItemId?: string;
4
15
  productId?: string;
5
16
  resumeJobId?: string;
17
+ /** Invoer uit de eenvoudige route; gaat mee naar de wizard. */
18
+ initial?: WizardInitial;
6
19
  }): import("react").JSX.Element;
7
20
  //# sourceMappingURL=ArtikelWizard.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ArtikelWizard.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/ArtikelWizard.tsx"],"names":[],"mappings":"AA+MA,wBAAgB,aAAa,CAAC,EAC5B,OAAO,EAAE,cAAc,EACvB,WAAW,EACX,SAAS,EACT,WAAW,GACZ,GAAE;IACD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;CACjB,+BAwwEL"}
1
+ {"version":3,"file":"ArtikelWizard.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/ArtikelWizard.tsx"],"names":[],"mappings":"AA2DA,OAAO,EAUL,KAAK,UAAU,EAMhB,MAAM,4BAA4B,CAAC;AAwDpC,OAAO,EAAwB,KAAK,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAMpF,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,EAAE,aAAa,CAAC;CACzB;AAED,4EAA4E;AAC5E,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,UAAU,EAAE,IAAI,CAAC,EAAE,aAAa,GAAG,UAAU,CA0BrF;AAiED,wBAAgB,aAAa,CAAC,EAC5B,OAAO,EAAE,cAAc,EACvB,WAAW,EACX,SAAS,EACT,WAAW,EACX,OAAO,GACR,GAAE;IACD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,+DAA+D;IAC/D,OAAO,CAAC,EAAE,aAAa,CAAC;CACpB,+BA4xEL"}
@@ -43,15 +43,41 @@ import { ProductStructureCard, FloridayStockReasonLine } from "../../components/
43
43
  import { StepSamenstelling } from "../../components/voorraad/StepSamenstelling.js";
44
44
  import { FloridayInfoCard } from "../../components/voorraad/FloridayInfoCard.js";
45
45
  import { stockKeyFor } from "../../hooks/useTradeItemStock.js";
46
+ import { missingPackingFields } from "../../lib/simple-packing.js";
46
47
  const empty = emptyWizardData;
47
- const LOAD_CARRIER_LABELS = {
48
- AUCTION_TROLLEY: "Stapelwagen",
49
- DANISH_TROLLEY: "Deense container",
50
- EURO_TROLLEY: "Euro-rolcontainer",
51
- PALLET: "Pallet",
52
- EURO_PALLET: "Euro-pallet",
53
- NONE: "Geen ladingdrager",
54
- };
48
+ /** Neemt naam, code, voorraad, prijs en (indien compleet) belading over. */
49
+ export function applyWizardInitial(base, init) {
50
+ if (!init)
51
+ return base;
52
+ const next = { ...base };
53
+ if (init.name)
54
+ next.name = init.name;
55
+ if (init.articleCode)
56
+ next.articleCode = init.articleCode;
57
+ if (init.stockPieces > 0)
58
+ next.saleStockPieces = init.stockPieces;
59
+ if (init.pricePerPiece > 0)
60
+ next.salePricePerPiece = init.pricePerPiece;
61
+ const p = init.packing;
62
+ if (p && missingPackingFields(p).length === 0) {
63
+ next.packingConfigurations = [
64
+ {
65
+ id: crypto.randomUUID(),
66
+ source: "VBN",
67
+ loadCarrier: p.loadCarrier,
68
+ packagingType: p.vbnPackageCode.trim(),
69
+ unitsPerPackage: Number(p.piecesPerPackage),
70
+ packagesPerLayer: Number(p.packagesPerLayer),
71
+ layersPerLoadCarrier: Number(p.layersPerLoadCarrier),
72
+ isCustomerSpecific: false,
73
+ customerOrganizationIds: [],
74
+ isPrimary: true,
75
+ isHiddenForDirectSales: false,
76
+ },
77
+ ];
78
+ }
79
+ return next;
80
+ }
55
81
  const STEPS = [
56
82
  { key: "vbn", label: "VBN-code" },
57
83
  { key: "artikel", label: "Artikel" },
@@ -107,7 +133,7 @@ async function lookupProductGroupForVbn(vbnCode) {
107
133
  .maybeSingle();
108
134
  return data ? groupOfRecord(data) : null;
109
135
  }
110
- export function ArtikelWizard({ draftId: initialDraftId, tradeItemId, productId, resumeJobId, } = {}) {
136
+ export function ArtikelWizard({ draftId: initialDraftId, tradeItemId, productId, resumeJobId, initial, } = {}) {
111
137
  const navigate = useNavigate();
112
138
  const { activeId, activeConnection, loading: connLoading } = useFloridayConnection();
113
139
  const connectionReadiness = useQuery({
@@ -120,10 +146,10 @@ export function ArtikelWizard({ draftId: initialDraftId, tradeItemId, productId,
120
146
  const { customers: floridayCustomers } = useFlorydayCustomers(activeId);
121
147
  const { priceGroups } = useFloridayPriceGroups(activeId);
122
148
  const [step, setStep] = useState(0);
123
- const [data, setData] = useState(empty);
149
+ const [data, setData] = useState(() => applyWizardInitial(empty, initial));
124
150
  const [submitting, setSubmitting] = useState(false);
125
151
  const [publishJobId, setPublishJobId] = useState(resumeJobId ?? null);
126
- const { job: publishJob, steps: publishSteps } = usePublishJob(publishJobId);
152
+ const { job: publishJob, steps: publishSteps, reloadFor: reloadPublishJob } = usePublishJob(publishJobId);
127
153
  const [publishToFloriday, setPublishToFloriday] = useState(true);
128
154
  const [draftId, setDraftId] = useState(initialDraftId);
129
155
  const [draftFloridayId, setDraftFloridayId] = useState(null);
@@ -1340,13 +1366,16 @@ export function ArtikelWizard({ draftId: initialDraftId, tradeItemId, productId,
1340
1366
  }
1341
1367
  const createdId = res.tradeItemId ?? payload.tradeItemId;
1342
1368
  await saveWarehousePreference(createdId, data.warehouseId);
1343
- if (draftId) {
1344
- try {
1345
- await deleteLocalTradeItem(draftId);
1346
- }
1347
- catch {
1348
- /* niet kritiek */
1349
- }
1369
+ // 1.0.123: het lokale artikel blijft bestaan en wordt aan het nieuwe
1370
+ // Floriday-artikel gekoppeld (alleen als het nog niet gekoppeld is),
1371
+ // zodat voorraad, prijs en belading niet verloren gaan.
1372
+ const localTarget = savedLocalId ?? draftId ?? null;
1373
+ if (localTarget && createdId) {
1374
+ await supabase
1375
+ .from("products")
1376
+ .update({ floriday_trade_item_id: createdId })
1377
+ .eq("id", localTarget)
1378
+ .is("floriday_trade_item_id", null);
1350
1379
  }
1351
1380
  return { tradeItemId: createdId };
1352
1381
  }, stepOpts);
@@ -1355,13 +1384,28 @@ export function ArtikelWizard({ draftId: initialDraftId, tradeItemId, productId,
1355
1384
  if (!publishedTradeItemId)
1356
1385
  await keepLocalInputAfterFailure();
1357
1386
  await handleStepFailure(core.error);
1358
- if (jobId)
1387
+ if (jobId) {
1359
1388
  await finishPublishJob(jobId);
1389
+ await reloadPublishJob(jobId);
1390
+ }
1360
1391
  return;
1361
1392
  }
1362
1393
  resolvedTradeItemId =
1363
1394
  core.value?.tradeItemId ??
1364
1395
  resolvedTradeItemId;
1396
+ // Nieuw artikel: ingevoerde voorraad, prijs en belading altijd lokaal vastleggen
1397
+ // (nooit iets overschrijven wat er al staat).
1398
+ if (!publishedTradeItemId) {
1399
+ await keepLocalInputAfterFailure();
1400
+ if (savedLocalId) {
1401
+ try {
1402
+ await saveOwnPackings(savedLocalId, packingRowsFromWizard(data.packingConfigurations));
1403
+ }
1404
+ catch (e) {
1405
+ toast.error("Belading kon niet lokaal bewaard worden", { description: e.message });
1406
+ }
1407
+ }
1408
+ }
1365
1409
  const primary = data.packingConfigurations.find((p) => p.isPrimary) ??
1366
1410
  data.packingConfigurations[0];
1367
1411
  // 2) Voorraad ---------------------------------------------------------
@@ -1400,7 +1444,7 @@ export function ArtikelWizard({ draftId: initialDraftId, tradeItemId, productId,
1400
1444
  if (activeStepKeys.includes("offer") && resolvedTradeItemId && !isEditingPublished) {
1401
1445
  await runPublishStep(jobId, "offer", async () => {
1402
1446
  const packingBase = buildPrimaryPackingBase(data);
1403
- await publishCustomerOfferFn({
1447
+ const offerRes = (await publishCustomerOfferFn({
1404
1448
  data: {
1405
1449
  customerOfferId: crypto.randomUUID(),
1406
1450
  tradeItemId: resolvedTradeItemId,
@@ -1411,11 +1455,14 @@ export function ArtikelWizard({ draftId: initialDraftId, tradeItemId, productId,
1411
1455
  despatchWarehouseId: data.warehouseId,
1412
1456
  numberOfPieces: data.saleStockPieces,
1413
1457
  allowedCustomerOrganizationIds: data.customerOrganizationIds,
1458
+ priceGroupIds: Object.entries(data.priceGroupPrices ?? {})
1459
+ .filter(([, v]) => Number(v) > 0)
1460
+ .map(([k]) => k),
1414
1461
  packingConfiguration: packingBase,
1415
1462
  orderStart: new Date(`${data.offerStartDate}T00:00:00Z`).toISOString(),
1416
1463
  },
1417
- });
1418
- return { published: true };
1464
+ }));
1465
+ return { published: true, buyerSource: offerRes?.buyerSource, buyerCount: offerRes?.buyerCount };
1419
1466
  }, stepOpts);
1420
1467
  }
1421
1468
  else if (jobId && activeStepKeys.includes("offer")) {
@@ -1525,6 +1572,8 @@ export function ArtikelWizard({ draftId: initialDraftId, tradeItemId, productId,
1525
1572
  const status = jobId ? await finishPublishJob(jobId, {
1526
1573
  tradeItemId: resolvedTradeItemId,
1527
1574
  }) : "succeeded";
1575
+ if (jobId)
1576
+ await reloadPublishJob(jobId);
1528
1577
  if (status === "succeeded") {
1529
1578
  if (draftId) {
1530
1579
  await markLocalSyncStatus(draftId, {
@@ -1549,8 +1598,10 @@ export function ArtikelWizard({ draftId: initialDraftId, tradeItemId, productId,
1549
1598
  catch (e) {
1550
1599
  const friendly = translatePublishError(e, { characteristicLabels });
1551
1600
  await handleStepFailure(friendly);
1552
- if (jobId)
1601
+ if (jobId) {
1553
1602
  await finishPublishJob(jobId);
1603
+ await reloadPublishJob(jobId);
1604
+ }
1554
1605
  }
1555
1606
  finally {
1556
1607
  setSubmitting(false);
@@ -2029,6 +2080,9 @@ function StepVbn({ data, update, acceptedVbnCodes }) {
2029
2080
  const selectVbn = (code, name, group) => {
2030
2081
  update("vbnCode", code);
2031
2082
  update("vbnName", name);
2083
+ // De VBN-productnaam is de botanische naam (Floricode VbnProduct); alleen invullen als het veld leeg is.
2084
+ if (!data.botanicalName.trim() && name.trim())
2085
+ update("botanicalName", name.trim());
2032
2086
  if (group)
2033
2087
  update("productGroup", group);
2034
2088
  pushRecentVbn({ code, name, group });
@@ -2139,6 +2193,7 @@ function StepArtikel({ data, update, collectionsList, setCollectionsList, articl
2139
2193
  }
2140
2194
  import { COUNTRIES } from "../../lib/countries.js";
2141
2195
  import { ProductSalePriceField } from "./ProductSalePriceField.js";
2196
+ import { useLoadCarriers } from "../../hooks/useLoadCarriers.js";
2142
2197
  /** Zoekbare keuzelijst voor lange waardelijsten (bijv. S62 land van herkomst).
2143
2198
  * Nederland (ISO-code NL) staat bovenaan als die waarde voorkomt. */
2144
2199
  function SearchableValuePicker({ values, value, onChange, }) {
@@ -2171,6 +2226,11 @@ function StepPaspoort({ data, update, botanicalDraft: liftedDraft, setBotanicalD
2171
2226
  else
2172
2227
  set.add(code);
2173
2228
  update("countryOfOrigin", Array.from(set).join(","));
2229
+ // S62 (land van herkomst) vooraf invullen met het eerste land, alleen als S62 nog leeg is.
2230
+ const first = Array.from(set)[0];
2231
+ if (first && !String(data.features?.["S62"] ?? "").trim()) {
2232
+ update("features", { ...(data.features ?? {}), S62: first });
2233
+ }
2174
2234
  };
2175
2235
  const filteredCountries = COUNTRIES.filter((c) => {
2176
2236
  const q = countryQuery.trim().toLowerCase();
@@ -2578,6 +2638,7 @@ function StepWarehouse({ data, update, activeId }) {
2578
2638
  * 4. Optionele artikelfoto in dit fust (upload via Floriday /media)
2579
2639
  */
2580
2640
  function StepBeladingen({ data, update, packagingTypes, loadCarriers, floridayCustomers, activeId, packingStatus, parentFloridayId, }) {
2641
+ const { label: carrierLabel } = useLoadCarriers();
2581
2642
  const [dialogOpen, setDialogOpen] = useState(false);
2582
2643
  const [editingId, setEditingId] = useState(null);
2583
2644
  const openNew = () => {
@@ -2626,7 +2687,7 @@ function StepBeladingen({ data, update, packagingTypes, loadCarriers, floridayCu
2626
2687
  const lcLabel = loadCarriers.find((r) => codeOf(r) === p.loadCarrier);
2627
2688
  return (_jsxs("tr", { className: "hover:bg-muted/30", children: [_jsx("td", { className: "px-3 py-2", children: _jsx("input", { type: "radio", name: "primary-packing", checked: Boolean(p.isPrimary), onChange: () => setPrimary(p.id), "aria-label": "Als primaire belading instellen" }) }), _jsxs("td", { className: "px-3 py-2", children: [_jsx("div", { className: "font-medium", children: pkgLabel
2628
2689
  ? `${codeOf(pkgLabel)}: ${labelOf(pkgLabel)}`
2629
- : p.packagingType || "—" }), p.source === "SUPPLIER" && (_jsx("div", { className: "text-xs text-muted-foreground", children: p.packagingSupplierName ?? p.packagingSupplierId }))] }), _jsx("td", { className: "px-3 py-2", children: LOAD_CARRIER_LABELS[p.loadCarrier] ?? p.loadCarrier ?? "—" }), _jsxs("td", { className: "px-3 py-2 tabular-nums", children: [p.layersPerLoadCarrier, "\u00D7", p.packagesPerLayer, "\u00D7", p.unitsPerPackage] }), _jsx("td", { className: "px-3 py-2", children: _jsxs("div", { className: "flex flex-wrap items-center gap-1", children: [p.isCustomerSpecific ? (_jsxs("span", { className: "rounded bg-amber-100 px-2 py-0.5 text-xs text-amber-900", children: ["Klantspecifiek (", p.customerOrganizationIds.length, ")"] })) : (_jsx("span", { className: "text-xs text-muted-foreground", children: "Regulier" })), p.isHiddenForDirectSales && (_jsx("span", { className: "rounded bg-slate-200 px-2 py-0.5 text-xs text-slate-700", children: "Verborgen" }))] }) }), _jsx("td", { className: "px-3 py-2 text-right", children: _jsxs("div", { className: "flex justify-end gap-1", children: [_jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => openEdit(p.id), children: "Bewerk" }), _jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => remove(p.id), children: _jsx(Trash2, { className: "h-4 w-4" }) })] }) })] }, p.id));
2690
+ : p.packagingType || "—" }), p.source === "SUPPLIER" && (_jsx("div", { className: "text-xs text-muted-foreground", children: p.packagingSupplierName ?? p.packagingSupplierId }))] }), _jsx("td", { className: "px-3 py-2", children: carrierLabel(p.loadCarrier) }), _jsxs("td", { className: "px-3 py-2 tabular-nums", children: [p.layersPerLoadCarrier, "\u00D7", p.packagesPerLayer, "\u00D7", p.unitsPerPackage] }), _jsx("td", { className: "px-3 py-2", children: _jsxs("div", { className: "flex flex-wrap items-center gap-1", children: [p.isCustomerSpecific ? (_jsxs("span", { className: "rounded bg-amber-100 px-2 py-0.5 text-xs text-amber-900", children: ["Klantspecifiek (", p.customerOrganizationIds.length, ")"] })) : (_jsx("span", { className: "text-xs text-muted-foreground", children: "Regulier" })), p.isHiddenForDirectSales && (_jsx("span", { className: "rounded bg-slate-200 px-2 py-0.5 text-xs text-slate-700", children: "Verborgen" }))] }) }), _jsx("td", { className: "px-3 py-2 text-right", children: _jsxs("div", { className: "flex justify-end gap-1", children: [_jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => openEdit(p.id), children: "Bewerk" }), _jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => remove(p.id), children: _jsx(Trash2, { className: "h-4 w-4" }) })] }) })] }, p.id));
2630
2691
  }) })] }) })), _jsx(FloridayLoadCarriers, { connectionId: activeId, floridayId: parentFloridayId }), _jsxs(Button, { type: "button", variant: "outline", size: "sm", onClick: openNew, children: [_jsx(Plus, { className: "mr-2 h-4 w-4" }), " Belading toevoegen"] }), _jsx(PackingConfigDialog, { open: dialogOpen, onOpenChange: (o) => {
2631
2692
  setDialogOpen(o);
2632
2693
  if (!o)
@@ -2637,10 +2698,11 @@ function StepBeladingen({ data, update, packagingTypes, loadCarriers, floridayCu
2637
2698
  // Dialog voor toevoegen/bewerken van één beladingsconfiguratie
2638
2699
  // ---------------------------------------------------------------
2639
2700
  function PackingConfigDialog({ open, onOpenChange, initial, isFirst, packagingTypes, loadCarriers, floridayCustomers, activeId, onSave, }) {
2701
+ const { items: carrierList, error: carrierError } = useLoadCarriers();
2640
2702
  const emptyItem = () => ({
2641
2703
  id: crypto.randomUUID(),
2642
2704
  source: "VBN",
2643
- loadCarrier: "AUCTION_TROLLEY",
2705
+ loadCarrier: "",
2644
2706
  packagingType: "",
2645
2707
  // Geen standaardwaarden: de gebruiker vult alle drie aantallen zelf in.
2646
2708
  unitsPerPackage: 0,
@@ -2749,7 +2811,7 @@ function PackingConfigDialog({ open, onOpenChange, initial, isFirst, packagingTy
2749
2811
  ? (packagingTypes.find((r) => codeOf(r) === draft.packagingType)
2750
2812
  ? `${labelOf(packagingTypes.find((r) => codeOf(r) === draft.packagingType))} (${draft.packagingType})`
2751
2813
  : draft.packagingType)
2752
- : "Selecteer fust..." }), _jsx(ChevronDown, { className: "ml-2 h-4 w-4 shrink-0 opacity-50" })] }) }), _jsxs(PopoverContent, { className: "w-[380px] p-0", align: "start", children: [_jsx("div", { className: "border-b p-2", children: _jsx(Input, { placeholder: "Zoek op naam of code...", value: pkgSearch, onChange: (e) => setPkgSearch(e.target.value) }) }), _jsx("div", { className: "max-h-64 overflow-y-auto", children: filteredPkg.length === 0 ? (_jsx("div", { className: "px-3 py-4 text-center text-xs text-muted-foreground", children: "Geen resultaten" })) : (filteredPkg.map((r) => (_jsxs("button", { type: "button", onClick: () => patch("packagingType", codeOf(r)), className: `flex w-full items-start gap-2 px-3 py-2 text-left text-sm hover:bg-muted ${draft.packagingType === codeOf(r) ? "bg-muted" : ""}`, children: [_jsx("span", { className: "font-mono text-xs text-muted-foreground w-14 shrink-0", children: codeOf(r) }), _jsx("span", { className: "truncate", children: labelOf(r) })] }, r.record_id)))) })] })] })] }), _jsxs("div", { className: "space-y-1", children: [_jsx(Label, { className: "text-xs", children: "Ladingdrager" }), _jsxs(Select, { value: draft.loadCarrier || "AUCTION_TROLLEY", onValueChange: (v) => patch("loadCarrier", v), children: [_jsx(SelectTrigger, { children: _jsx(SelectValue, { placeholder: "Kies een ladingdrager" }) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "AUCTION_TROLLEY", children: "Stapelwagen" }), _jsx(SelectItem, { value: "DANISH_TROLLEY", children: "Deense container" }), _jsx(SelectItem, { value: "EURO_TROLLEY", children: "Euro-rolcontainer" }), _jsx(SelectItem, { value: "PALLET", children: "Pallet" }), _jsx(SelectItem, { value: "EURO_PALLET", children: "Euro-pallet" }), _jsx(SelectItem, { value: "NONE", children: "Geen ladingdrager" })] })] })] })] }), _jsxs("div", { className: "grid grid-cols-3 gap-3", children: [_jsxs("div", { className: "space-y-1", children: [_jsx(Label, { className: "text-xs", children: "Stuks per fust" }), _jsx(Input, { type: "number", min: 1, placeholder: "Invullen", value: draft.unitsPerPackage > 0 ? draft.unitsPerPackage : "", onChange: (e) => patch("unitsPerPackage", Math.max(0, Math.floor(Number(e.target.value) || 0))) })] }), _jsxs("div", { className: "space-y-1", children: [_jsx(Label, { className: "text-xs", children: "Fusten per laag" }), _jsx(Input, { type: "number", min: 1, placeholder: "Invullen", value: draft.packagesPerLayer > 0 ? draft.packagesPerLayer : "", onChange: (e) => patch("packagesPerLayer", Math.max(0, Math.floor(Number(e.target.value) || 0))) })] }), _jsxs("div", { className: "space-y-1", children: [_jsx(Label, { className: "text-xs", children: "Lagen per drager" }), _jsx(Input, { type: "number", min: 1, placeholder: "Invullen", value: draft.layersPerLoadCarrier > 0 ? draft.layersPerLoadCarrier : "", onChange: (e) => patch("layersPerLoadCarrier", Math.max(0, Math.floor(Number(e.target.value) || 0))) })] }), !countsComplete && (_jsx("p", { className: "col-span-3 text-xs text-muted-foreground", children: "Vul stuks per fust, fusten per laag en lagen per drager in; er wordt niets vooraf ingevuld." }))] }), _jsxs("div", { className: "rounded-md border p-3 space-y-3", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { children: [_jsx("div", { className: "text-sm font-medium", children: "Klantspecifieke belading" }), _jsx("div", { className: "text-xs text-muted-foreground", children: "Alleen zichtbaar voor geselecteerde klantorganisaties." })] }), _jsx(Switch, { checked: draft.isCustomerSpecific, onCheckedChange: (v) => patch("isCustomerSpecific", Boolean(v)) })] }), draft.isCustomerSpecific && (_jsx("div", { className: "max-h-40 overflow-y-auto rounded border divide-y", children: floridayCustomers.length === 0 ? (_jsx("div", { className: "p-3 text-xs text-muted-foreground", children: "Geen klantorganisaties geladen." })) : (floridayCustomers.map((c) => (_jsxs("label", { className: "flex items-center gap-2 px-3 py-2 text-sm hover:bg-muted cursor-pointer", children: [_jsx("input", { type: "checkbox", checked: draft.customerOrganizationIds.includes(c.id), onChange: () => toggleCustomer(c.id) }), _jsx("span", { className: "truncate", children: c.name })] }, c.id)))) }))] }), _jsxs("div", { className: "rounded-md border p-3 space-y-2", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { children: [_jsx("div", { className: "text-sm font-medium", children: "Foto van dit fust (optioneel)" }), _jsx("div", { className: "text-xs text-muted-foreground", children: "JPG of PNG \u00B7 min. 640 \u00D7 640 px \u00B7 max. 5 MB" })] }), _jsx(Button, { type: "button", variant: "outline", size: "sm", disabled: uploadingPhoto || !activeId, onClick: () => photoInputRef.current?.click(), children: uploadingPhoto ? (_jsxs(_Fragment, { children: [_jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }), " Uploaden"] })) : (_jsxs(_Fragment, { children: [_jsx(Upload, { className: "mr-2 h-4 w-4" }), " Foto kiezen"] })) }), _jsx("input", { ref: photoInputRef, type: "file", accept: "image/png,image/jpeg", className: "hidden", onChange: (e) => {
2814
+ : "Selecteer fust..." }), _jsx(ChevronDown, { className: "ml-2 h-4 w-4 shrink-0 opacity-50" })] }) }), _jsxs(PopoverContent, { className: "w-[380px] p-0", align: "start", children: [_jsx("div", { className: "border-b p-2", children: _jsx(Input, { placeholder: "Zoek op naam of code...", value: pkgSearch, onChange: (e) => setPkgSearch(e.target.value) }) }), _jsx("div", { className: "max-h-64 overflow-y-auto", children: filteredPkg.length === 0 ? (_jsx("div", { className: "px-3 py-4 text-center text-xs text-muted-foreground", children: "Geen resultaten" })) : (filteredPkg.map((r) => (_jsxs("button", { type: "button", onClick: () => patch("packagingType", codeOf(r)), className: `flex w-full items-start gap-2 px-3 py-2 text-left text-sm hover:bg-muted ${draft.packagingType === codeOf(r) ? "bg-muted" : ""}`, children: [_jsx("span", { className: "font-mono text-xs text-muted-foreground w-14 shrink-0", children: codeOf(r) }), _jsx("span", { className: "truncate", children: labelOf(r) })] }, r.record_id)))) })] })] })] }), _jsxs("div", { className: "space-y-1", children: [_jsx(Label, { className: "text-xs", children: "Ladingdrager" }), _jsxs(Select, { value: draft.loadCarrier || undefined, onValueChange: (v) => patch("loadCarrier", v), children: [_jsx(SelectTrigger, { children: _jsx(SelectValue, { placeholder: "Kies een ladingdrager" }) }), _jsx(SelectContent, { children: carrierList.map((c) => (_jsx(SelectItem, { value: c.code, children: c.naam }, c.code))) })] }), carrierError && _jsx("p", { className: "text-xs text-destructive", children: carrierError })] })] }), _jsxs("div", { className: "grid grid-cols-3 gap-3", children: [_jsxs("div", { className: "space-y-1", children: [_jsx(Label, { className: "text-xs", children: "Stuks per fust" }), _jsx(Input, { type: "number", min: 1, placeholder: "Invullen", value: draft.unitsPerPackage > 0 ? draft.unitsPerPackage : "", onChange: (e) => patch("unitsPerPackage", Math.max(0, Math.floor(Number(e.target.value) || 0))) })] }), _jsxs("div", { className: "space-y-1", children: [_jsx(Label, { className: "text-xs", children: "Fusten per laag" }), _jsx(Input, { type: "number", min: 1, placeholder: "Invullen", value: draft.packagesPerLayer > 0 ? draft.packagesPerLayer : "", onChange: (e) => patch("packagesPerLayer", Math.max(0, Math.floor(Number(e.target.value) || 0))) })] }), _jsxs("div", { className: "space-y-1", children: [_jsx(Label, { className: "text-xs", children: "Lagen per drager" }), _jsx(Input, { type: "number", min: 1, placeholder: "Invullen", value: draft.layersPerLoadCarrier > 0 ? draft.layersPerLoadCarrier : "", onChange: (e) => patch("layersPerLoadCarrier", Math.max(0, Math.floor(Number(e.target.value) || 0))) })] }), !countsComplete && (_jsx("p", { className: "col-span-3 text-xs text-muted-foreground", children: "Vul stuks per fust, fusten per laag en lagen per drager in; er wordt niets vooraf ingevuld." }))] }), _jsxs("div", { className: "rounded-md border p-3 space-y-3", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { children: [_jsx("div", { className: "text-sm font-medium", children: "Klantspecifieke belading" }), _jsx("div", { className: "text-xs text-muted-foreground", children: "Alleen zichtbaar voor geselecteerde klantorganisaties." })] }), _jsx(Switch, { checked: draft.isCustomerSpecific, onCheckedChange: (v) => patch("isCustomerSpecific", Boolean(v)) })] }), draft.isCustomerSpecific && (_jsx("div", { className: "max-h-40 overflow-y-auto rounded border divide-y", children: floridayCustomers.length === 0 ? (_jsx("div", { className: "p-3 text-xs text-muted-foreground", children: "Geen klantorganisaties geladen." })) : (floridayCustomers.map((c) => (_jsxs("label", { className: "flex items-center gap-2 px-3 py-2 text-sm hover:bg-muted cursor-pointer", children: [_jsx("input", { type: "checkbox", checked: draft.customerOrganizationIds.includes(c.id), onChange: () => toggleCustomer(c.id) }), _jsx("span", { className: "truncate", children: c.name })] }, c.id)))) }))] }), _jsxs("div", { className: "rounded-md border p-3 space-y-2", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { children: [_jsx("div", { className: "text-sm font-medium", children: "Foto van dit fust (optioneel)" }), _jsx("div", { className: "text-xs text-muted-foreground", children: "JPG of PNG \u00B7 min. 640 \u00D7 640 px \u00B7 max. 5 MB" })] }), _jsx(Button, { type: "button", variant: "outline", size: "sm", disabled: uploadingPhoto || !activeId, onClick: () => photoInputRef.current?.click(), children: uploadingPhoto ? (_jsxs(_Fragment, { children: [_jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }), " Uploaden"] })) : (_jsxs(_Fragment, { children: [_jsx(Upload, { className: "mr-2 h-4 w-4" }), " Foto kiezen"] })) }), _jsx("input", { ref: photoInputRef, type: "file", accept: "image/png,image/jpeg", className: "hidden", onChange: (e) => {
2753
2815
  onUploadPhoto(e.target.files?.[0]);
2754
2816
  e.target.value = "";
2755
2817
  } })] }), (draft.photoPreviewUrl || draft.photoUrl) && (_jsxs("div", { className: "relative inline-block", children: [_jsx("img", { src: draft.photoPreviewUrl || draft.photoUrl, alt: "Belading foto", className: "h-24 rounded-md border object-cover" }), _jsx("button", { type: "button", onClick: () => setDraft((d) => ({
@@ -1 +1 @@
1
- {"version":3,"file":"FloridayInfoCard.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/FloridayInfoCard.tsx"],"names":[],"mappings":"AAYA,UAAU,KAAK;IACb,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,8EAA8E;IAC9E,KAAK,CAAC,EAAE;QACN,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QACrB,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAC9B,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;KACtB,CAAC;CACH;AA6DD,wBAAgB,gBAAgB,CAAC,EAAE,mBAAmB,EAAE,KAAK,EAAE,EAAE,KAAK,sCAmRrE"}
1
+ {"version":3,"file":"FloridayInfoCard.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/FloridayInfoCard.tsx"],"names":[],"mappings":"AAYA,UAAU,KAAK;IACb,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,8EAA8E;IAC9E,KAAK,CAAC,EAAE;QACN,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QACrB,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAC9B,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;KACtB,CAAC;CACH;AA6DD,wBAAgB,gBAAgB,CAAC,EAAE,mBAAmB,EAAE,KAAK,EAAE,EAAE,KAAK,sCAoRrE"}
@@ -3,7 +3,8 @@ import { useEffect, useState } from "react";
3
3
  import { Card, CardContent, Badge } from "@flowselections/core";
4
4
  import { AlertTriangle, ChevronDown, ChevronRight, ExternalLink } from "lucide-react";
5
5
  import { supabase } from "../../integrations/supabase/client.js";
6
- import { floridayInfoFromCache, LOAD_CARRIER_LABELS, } from "../../lib/floriday-info.js";
6
+ import { floridayInfoFromCache, } from "../../lib/floriday-info.js";
7
+ import { useLoadCarriers } from "../../hooks/useLoadCarriers.js";
7
8
  const floridayLogo = "/flowselections-assets/Voorraad/floriday-logo.png";
8
9
  function formatDate(iso) {
9
10
  if (!iso)
@@ -29,6 +30,7 @@ function Field({ label, value, }) {
29
30
  return (_jsxs("div", { className: "grid grid-cols-[140px_1fr] gap-2 text-sm py-1", children: [_jsx("div", { className: "text-muted-foreground", children: label }), _jsx("div", { className: "font-medium break-words", children: v })] }));
30
31
  }
31
32
  export function FloridayInfoCard({ floridayTradeItemId, local }) {
33
+ const { label: carrierLabel } = useLoadCarriers();
32
34
  const [info, setInfo] = useState(null);
33
35
  const [loading, setLoading] = useState(false);
34
36
  const [notFound, setNotFound] = useState(false);
@@ -81,5 +83,5 @@ export function FloridayInfoCard({ floridayTradeItemId, local }) {
81
83
  ? info.seasonalPeriods
82
84
  .map((s) => `wk ${s.startWeek}–${s.endWeek}`)
83
85
  .join(", ")
84
- : null }), _jsx(Field, { label: "Verborgen in catalogus", value: info.isHiddenInCatalog ? "Ja" : "Nee" }), _jsx(Field, { label: "Laatst gewijzigd", value: formatDate(info.lastModifiedDateTime) })] })] }), info.characteristics.length > 0 && (_jsxs("div", { children: [_jsx("div", { className: "text-xs uppercase tracking-wide text-muted-foreground mb-2", children: "Kenmerken" }), _jsx("div", { className: "rounded-md border overflow-hidden", children: _jsxs("table", { className: "w-full text-sm", children: [_jsx("thead", { className: "bg-muted/50 text-left", children: _jsxs("tr", { children: [_jsx("th", { className: "p-2 font-medium", children: "VBN-code" }), _jsx("th", { className: "p-2 font-medium", children: "Waarde" })] }) }), _jsx("tbody", { children: info.characteristics.map((c) => (_jsxs("tr", { className: "border-t", children: [_jsx("td", { className: "p-2 font-mono text-xs", children: c.vbnCode }), _jsx("td", { className: "p-2 font-mono text-xs", children: c.vbnValueCode })] }, c.vbnCode))) })] }) })] })), info.packingConfigurations.length > 0 && (_jsxs("div", { children: [_jsx("div", { className: "text-xs uppercase tracking-wide text-muted-foreground mb-2", children: "Beladingen (Floriday)" }), _jsx("div", { className: "rounded-md border overflow-x-auto", children: _jsxs("table", { className: "w-full text-sm", children: [_jsx("thead", { className: "bg-muted/50 text-left", children: _jsxs("tr", { children: [_jsx("th", { className: "p-2 font-medium", children: "Ladingdrager" }), _jsx("th", { className: "p-2 font-medium", children: "Verpakking" }), _jsx("th", { className: "p-2 text-right font-medium", children: "Stuks/verp." }), _jsx("th", { className: "p-2 text-right font-medium", children: "Verp./laag" }), _jsx("th", { className: "p-2 text-right font-medium", children: "Lagen" }), _jsx("th", { className: "p-2 font-medium", children: "Primair" })] }) }), _jsx("tbody", { children: info.packingConfigurations.map((p, i) => (_jsxs("tr", { className: "border-t", children: [_jsx("td", { className: "p-2", children: LOAD_CARRIER_LABELS[p.loadCarrier] ?? p.loadCarrier }), _jsx("td", { className: "p-2 font-mono text-xs", children: p.packagingCode ?? p.customPackageId ?? "—" }), _jsx("td", { className: "p-2 text-right tabular-nums", children: p.piecesPerPackage }), _jsx("td", { className: "p-2 text-right tabular-nums", children: p.packagesPerLayer }), _jsx("td", { className: "p-2 text-right tabular-nums", children: p.layersPerLoadCarrier }), _jsx("td", { className: "p-2", children: p.primary ? (_jsx(Badge, { className: "bg-emerald-500/15 text-emerald-700 border-emerald-500/30", children: "Ja" })) : (_jsx("span", { className: "text-muted-foreground text-xs", children: "nee" })) })] }, i))) })] }) })] })), _jsxs("div", { children: [_jsxs("button", { type: "button", onClick: () => setRawOpen((o) => !o), className: "inline-flex items-center gap-1 text-xs text-muted-foreground hover:text-foreground", children: [rawOpen ? (_jsx(ChevronDown, { className: "h-3 w-3" })) : (_jsx(ChevronRight, { className: "h-3 w-3" })), "Alles wat Floriday teruggeeft (raw)"] }), rawOpen && (_jsx("pre", { className: "mt-2 max-h-96 overflow-auto rounded-md border bg-muted/30 p-3 text-[11px] leading-tight font-mono", children: JSON.stringify(info.raw, null, 2) }))] }), _jsxs("div", { className: "text-xs text-muted-foreground flex items-center gap-1", children: [_jsx(ExternalLink, { className: "h-3 w-3" }), "Floriday trade item ID:", " ", _jsx("code", { className: "font-mono", children: info.id })] })] }))] }) }));
86
+ : null }), _jsx(Field, { label: "Verborgen in catalogus", value: info.isHiddenInCatalog ? "Ja" : "Nee" }), _jsx(Field, { label: "Laatst gewijzigd", value: formatDate(info.lastModifiedDateTime) })] })] }), info.characteristics.length > 0 && (_jsxs("div", { children: [_jsx("div", { className: "text-xs uppercase tracking-wide text-muted-foreground mb-2", children: "Kenmerken" }), _jsx("div", { className: "rounded-md border overflow-hidden", children: _jsxs("table", { className: "w-full text-sm", children: [_jsx("thead", { className: "bg-muted/50 text-left", children: _jsxs("tr", { children: [_jsx("th", { className: "p-2 font-medium", children: "VBN-code" }), _jsx("th", { className: "p-2 font-medium", children: "Waarde" })] }) }), _jsx("tbody", { children: info.characteristics.map((c) => (_jsxs("tr", { className: "border-t", children: [_jsx("td", { className: "p-2 font-mono text-xs", children: c.vbnCode }), _jsx("td", { className: "p-2 font-mono text-xs", children: c.vbnValueCode })] }, c.vbnCode))) })] }) })] })), info.packingConfigurations.length > 0 && (_jsxs("div", { children: [_jsx("div", { className: "text-xs uppercase tracking-wide text-muted-foreground mb-2", children: "Beladingen (Floriday)" }), _jsx("div", { className: "rounded-md border overflow-x-auto", children: _jsxs("table", { className: "w-full text-sm", children: [_jsx("thead", { className: "bg-muted/50 text-left", children: _jsxs("tr", { children: [_jsx("th", { className: "p-2 font-medium", children: "Ladingdrager" }), _jsx("th", { className: "p-2 font-medium", children: "Verpakking" }), _jsx("th", { className: "p-2 text-right font-medium", children: "Stuks/verp." }), _jsx("th", { className: "p-2 text-right font-medium", children: "Verp./laag" }), _jsx("th", { className: "p-2 text-right font-medium", children: "Lagen" }), _jsx("th", { className: "p-2 font-medium", children: "Primair" })] }) }), _jsx("tbody", { children: info.packingConfigurations.map((p, i) => (_jsxs("tr", { className: "border-t", children: [_jsx("td", { className: "p-2", children: carrierLabel(p.loadCarrier) }), _jsx("td", { className: "p-2 font-mono text-xs", children: p.packagingCode ?? p.customPackageId ?? "—" }), _jsx("td", { className: "p-2 text-right tabular-nums", children: p.piecesPerPackage }), _jsx("td", { className: "p-2 text-right tabular-nums", children: p.packagesPerLayer }), _jsx("td", { className: "p-2 text-right tabular-nums", children: p.layersPerLoadCarrier }), _jsx("td", { className: "p-2", children: p.primary ? (_jsx(Badge, { className: "bg-emerald-500/15 text-emerald-700 border-emerald-500/30", children: "Ja" })) : (_jsx("span", { className: "text-muted-foreground text-xs", children: "nee" })) })] }, i))) })] }) })] })), _jsxs("div", { children: [_jsxs("button", { type: "button", onClick: () => setRawOpen((o) => !o), className: "inline-flex items-center gap-1 text-xs text-muted-foreground hover:text-foreground", children: [rawOpen ? (_jsx(ChevronDown, { className: "h-3 w-3" })) : (_jsx(ChevronRight, { className: "h-3 w-3" })), "Alles wat Floriday teruggeeft (raw)"] }), rawOpen && (_jsx("pre", { className: "mt-2 max-h-96 overflow-auto rounded-md border bg-muted/30 p-3 text-[11px] leading-tight font-mono", children: JSON.stringify(info.raw, null, 2) }))] }), _jsxs("div", { className: "text-xs text-muted-foreground flex items-center gap-1", children: [_jsx(ExternalLink, { className: "h-3 w-3" }), "Floriday trade item ID:", " ", _jsx("code", { className: "font-mono", children: info.id })] })] }))] }) }));
85
87
  }
@@ -1 +1 @@
1
- {"version":3,"file":"FloridayLoadCarriers.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/FloridayLoadCarriers.tsx"],"names":[],"mappings":"AASA,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,OAAO,GAAG,KAAK,CAAC;IAAE,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAAC,CAO5G;AAED,wBAAgB,oBAAoB,CAAC,EACnC,YAAY,EACZ,UAAU,GACX,EAAE;IACD,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B,+BAsDA"}
1
+ {"version":3,"file":"FloridayLoadCarriers.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/FloridayLoadCarriers.tsx"],"names":[],"mappings":"AASA,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,OAAO,GAAG,KAAK,CAAC;IAAE,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAAC,CAO5G;AAED,wBAAgB,oBAAoB,CAAC,EACnC,YAAY,EACZ,UAAU,GACX,EAAE;IACD,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B,+BAuDA"}
@@ -6,7 +6,7 @@ import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
6
6
  */
7
7
  import { useEffect, useState } from "react";
8
8
  import { supabase } from "../../integrations/supabase/client.js";
9
- import { LOAD_CARRIER_LABELS } from "../../lib/floriday-info.js";
9
+ import { useLoadCarriers } from "../../hooks/useLoadCarriers.js";
10
10
  export function loadCarriersFromCache(data) {
11
11
  const list = data?.packingConfigurations;
12
12
  if (!Array.isArray(list))
@@ -17,6 +17,7 @@ export function loadCarriersFromCache(data) {
17
17
  }));
18
18
  }
19
19
  export function FloridayLoadCarriers({ connectionId, floridayId, }) {
20
+ const { label: carrierLabel } = useLoadCarriers();
20
21
  const [state, setState] = useState({ kind: "loading" });
21
22
  useEffect(() => {
22
23
  if (!connectionId || !floridayId)
@@ -55,6 +56,6 @@ export function FloridayLoadCarriers({ connectionId, floridayId, }) {
55
56
  else if (state.rows.length === 0)
56
57
  body = "Floriday heeft geen belading voor dit artikel";
57
58
  else
58
- body = (_jsx("ul", { className: "list-disc pl-5", children: state.rows.map((r, i) => (_jsxs("li", { children: ["Belading ", i + 1, r.primary ? " (primair)" : "", ":", " ", r.loadCarrier ? (LOAD_CARRIER_LABELS[r.loadCarrier] ?? r.loadCarrier) : "—"] }, i))) }));
59
+ body = (_jsx("ul", { className: "list-disc pl-5", children: state.rows.map((r, i) => (_jsxs("li", { children: ["Belading ", i + 1, r.primary ? " (primair)" : "", ":", " ", carrierLabel(r.loadCarrier)] }, i))) }));
59
60
  return (_jsxs("div", { className: "rounded-md border bg-muted/30 p-3 text-sm", children: [_jsx("div", { className: "mb-1 font-medium", children: "Ladingdrager volgens Floriday" }), _jsx("div", { className: "text-muted-foreground", children: body })] }));
60
61
  }
@@ -1 +1 @@
1
- {"version":3,"file":"NieuwArtikelPage.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/NieuwArtikelPage.tsx"],"names":[],"mappings":"AAmBA,wBAAgB,gBAAgB,gCAkO/B"}
1
+ {"version":3,"file":"NieuwArtikelPage.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/NieuwArtikelPage.tsx"],"names":[],"mappings":"AA4BA,wBAAgB,gBAAgB,gCA2X/B"}
@@ -5,7 +5,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
5
5
  * lijst van wat daarvoor nog ontbreekt. De volledige Floriday-invoer blijft
6
6
  * beschikbaar via de knop onderaan.
7
7
  */
8
- import { useRef, useState } from "react";
8
+ import { useEffect, useRef, useState } from "react";
9
9
  import { Link, useNavigate } from "@tanstack/react-router";
10
10
  import { Button, Card, Input, Label, toast } from "@flowselections/core";
11
11
  import { ArrowLeft, Check, Loader2, Send } from "lucide-react";
@@ -15,8 +15,12 @@ import { ArtikelWizard } from "./ArtikelWizard.js";
15
15
  import { useFloridayConnection } from "../../hooks/useFloridayConnection.js";
16
16
  import { LOCAL_STOCK_TEXT } from "../../lib/stock-scope.js";
17
17
  import { FloridayConnectionNotice } from "./FloridayConnectionNotice.js";
18
- const STEPS = ["Naam en code", "Voorraad", "Prijs"];
18
+ import { useLoadCarriers } from "../../hooks/useLoadCarriers.js";
19
+ import { saveOwnPackings } from "../../lib/product-packings.js";
20
+ import { EMPTY_SIMPLE_PACKING, engineOordeelRegel, missingPackingFields, simplePackingToRow, } from "../../lib/simple-packing.js";
21
+ const STEPS = ["Naam en code", "Voorraad", "Prijs", "Belading"];
19
22
  export function NieuwArtikelPage() {
23
+ const carriers = useLoadCarriers();
20
24
  const navigate = useNavigate();
21
25
  const conn = useFloridayConnection();
22
26
  // Sinds 1.0.107 slaat set_product_stock zonder koppeling op in de lokale
@@ -32,12 +36,55 @@ export function NieuwArtikelPage() {
32
36
  const [saving, setSaving] = useState(false);
33
37
  const [createdId, setCreatedId] = useState(null);
34
38
  const [saveError, setSaveError] = useState(null);
39
+ const [packing, setPacking] = useState(EMPTY_SIMPLE_PACKING);
40
+ const [fusts, setFusts] = useState([]);
41
+ const [oordeel, setOordeel] = useState(null);
42
+ const missingPacking = missingPackingFields(packing);
43
+ const setPack = (k, v) => setPacking((p) => ({ ...p, [k]: v }));
44
+ useEffect(() => {
45
+ let off = false;
46
+ void (async () => {
47
+ const { data } = await supabase
48
+ .from("fust_types")
49
+ .select("name, vbn_code")
50
+ .is("merged_into", null)
51
+ .not("vbn_code", "is", null)
52
+ .order("name");
53
+ if (off)
54
+ return;
55
+ const seen = new Set();
56
+ const list = [];
57
+ for (const r of (data ?? [])) {
58
+ const v = Number(r.vbn_code);
59
+ if (v > 0 && !seen.has(v)) {
60
+ seen.add(v);
61
+ list.push({ vbn: v, name: String(r.name ?? `VBN ${v}`) });
62
+ }
63
+ }
64
+ setFusts(list);
65
+ })();
66
+ return () => {
67
+ off = true;
68
+ };
69
+ }, []);
35
70
  // Voorkomt dubbele artikelen bij snel herhaald klikken of opnieuw proberen.
36
71
  const savingRef = useRef(false);
37
72
  const insertedIdRef = useRef(null);
38
73
  if (full)
39
- return _jsx(ArtikelWizard, {});
40
- const canNext = step === 0 ? name.trim().length > 0 : step === 1 ? !canSaveStock || (Number(stock) >= 0 && stock !== "") : true;
74
+ return (_jsx(ArtikelWizard, { initial: {
75
+ name: name.trim(),
76
+ articleCode: code.trim(),
77
+ stockPieces: Math.trunc(Number(stock) || 0),
78
+ pricePerPiece: Number(price.replace(",", ".")) || 0,
79
+ packing,
80
+ } }));
81
+ const canNext = step === 0
82
+ ? name.trim().length > 0
83
+ : step === 1
84
+ ? !canSaveStock || (Number(stock) >= 0 && stock !== "")
85
+ : step === 3
86
+ ? missingPacking.length === 0
87
+ : true;
41
88
  const save = async () => {
42
89
  if (savingRef.current)
43
90
  return;
@@ -48,6 +95,7 @@ export function NieuwArtikelPage() {
48
95
  const priceNum = price.trim() === "" ? null : Number(price.replace(",", "."));
49
96
  if (priceNum !== null && !(priceNum > 0))
50
97
  throw new Error("Vul een geldige prijs in (groter dan 0)");
98
+ const packingRow = simplePackingToRow(packing);
51
99
  let id = insertedIdRef.current;
52
100
  if (!id) {
53
101
  const { data, error } = await supabase
@@ -81,6 +129,22 @@ export function NieuwArtikelPage() {
81
129
  if (stockErr) {
82
130
  throw new Error(`Artikel is aangemaakt, maar de voorraad is niet opgeslagen: ${stockErr.message}. Klik opnieuw op "Artikel opslaan" om alleen de voorraad nog eens te proberen.`);
83
131
  }
132
+ try {
133
+ await saveOwnPackings(id, [packingRow]);
134
+ }
135
+ catch (e) {
136
+ throw new Error(`Artikel is aangemaakt, maar de belading is niet opgeslagen: ${e?.message ?? e}. Klik opnieuw op "Artikel opslaan".`);
137
+ }
138
+ // Controle door de Engine (alleen lezen); uitkomst tonen, niet blokkeren.
139
+ const { data: oo, error: ooErr } = await supabase.rpc("logistics_product_oordeel_belading", {
140
+ p_regels: [engineOordeelRegel(id, packing)],
141
+ });
142
+ const first = Array.isArray(oo) ? oo[0] : null;
143
+ setOordeel(ooErr
144
+ ? { oordeel: "fout", tekst: `Controle door de karindeling lukte niet: ${ooErr.message}`, regels: [] }
145
+ : first
146
+ ? { oordeel: String(first.oordeel), tekst: String(first.tekst ?? ""), regels: (first.regels ?? []) }
147
+ : null);
84
148
  toast.success(canSaveStock ? "Artikel aangemaakt" : "Artikel aangemaakt (zonder voorraad)");
85
149
  setCreatedId(id);
86
150
  }
@@ -97,7 +161,9 @@ export function NieuwArtikelPage() {
97
161
  if (createdId) {
98
162
  const missing = validateDraftForPublish({}).missing;
99
163
  const hasPrice = Number(price.replace(",", ".")) > 0;
100
- return (_jsx("main", { className: "p-6 space-y-4 max-w-2xl", children: _jsxs(Card, { className: "p-5 space-y-3", children: [_jsxs("div", { className: "flex items-center gap-2 text-emerald-700", children: [_jsx(Check, { className: "h-5 w-5" }), _jsxs("p", { className: "font-medium", children: [name, " is aangemaakt."] })] }), hasFloriday ? (_jsxs(_Fragment, { children: [_jsx("p", { className: "text-sm text-muted-foreground", children: "Wil je het artikel ook op Floriday zetten? Daarvoor is nog nodig:" }), _jsxs("ul", { className: "list-disc pl-5 text-sm", children: [!hasPrice && _jsx("li", { children: "Prijs per stuk (excl. btw)" }), missing.map((m) => (_jsx("li", { children: m }, m)))] })] })) : (_jsx("p", { className: "text-sm text-muted-foreground", children: "De voorraad is opgeslagen als eigen voorraad. Die gaat niet naar Floriday." })), _jsxs("div", { className: "flex flex-wrap gap-2", children: [hasFloriday && (_jsxs(Button, { onClick: () => navigate({ to: "/voorraad/$id", params: { id: createdId } }), children: [_jsx(Send, { className: "mr-2 h-4 w-4" }), "Op Floriday zetten"] })), _jsx(Button, { variant: "outline", asChild: true, children: _jsx(Link, { to: "/voorraad", children: "Terug naar voorraad" }) })] })] }) }));
164
+ return (_jsx("main", { className: "p-6 space-y-4 max-w-2xl", children: _jsxs(Card, { className: "p-5 space-y-3", children: [_jsxs("div", { className: "flex items-center gap-2 text-emerald-700", children: [_jsx(Check, { className: "h-5 w-5" }), _jsxs("p", { className: "font-medium", children: [name, " is aangemaakt."] })] }), oordeel && (_jsxs("div", { role: oordeel.oordeel === "compleet" ? "status" : "alert", className: oordeel.oordeel === "compleet"
165
+ ? "rounded-md border p-3 text-sm"
166
+ : "rounded-md border border-destructive/40 bg-destructive/10 p-3 text-sm text-destructive", children: [_jsxs("p", { className: "font-medium", children: ["Controle karindeling: ", oordeel.tekst] }), oordeel.regels.length > 0 && (_jsx("ul", { className: "list-disc pl-5", children: oordeel.regels.map((r) => (_jsx("li", { children: r }, r))) }))] })), hasFloriday ? (_jsxs(_Fragment, { children: [_jsx("p", { className: "text-sm text-muted-foreground", children: "Wil je het artikel ook op Floriday zetten? Daarvoor is nog nodig:" }), _jsxs("ul", { className: "list-disc pl-5 text-sm", children: [!hasPrice && _jsx("li", { children: "Prijs per stuk (excl. btw)" }), missing.map((m) => (_jsx("li", { children: m }, m)))] })] })) : (_jsx("p", { className: "text-sm text-muted-foreground", children: "De voorraad is opgeslagen als eigen voorraad. Die gaat niet naar Floriday." })), _jsxs("div", { className: "flex flex-wrap gap-2", children: [hasFloriday && (_jsxs(Button, { onClick: () => navigate({ to: "/voorraad/$id", params: { id: createdId } }), children: [_jsx(Send, { className: "mr-2 h-4 w-4" }), "Op Floriday zetten"] })), _jsx(Button, { variant: "outline", asChild: true, children: _jsx(Link, { to: "/voorraad", children: "Terug naar voorraad" }) })] })] }) }));
101
167
  }
102
- return (_jsxs("main", { className: "p-6 space-y-4 max-w-2xl", children: [_jsxs(Link, { to: "/voorraad", className: "inline-flex items-center gap-1 text-sm text-muted-foreground hover:text-foreground", children: [_jsx(ArrowLeft, { className: "h-4 w-4" }), "Terug naar voorraad"] }), _jsxs("div", { children: [_jsx("h1", { className: "text-xl font-semibold", children: "Artikel toevoegen" }), _jsxs("p", { className: "text-sm text-muted-foreground", children: ["Stap ", step + 1, " van ", STEPS.length, ": ", STEPS[step]] })] }), _jsx(FloridayConnectionNotice, { status: conn.status, environment: conn.selectedEnvironment, error: conn.error }), _jsxs(Card, { className: "p-5 space-y-4", children: [step === 0 && (_jsxs(_Fragment, { children: [_jsxs("div", { className: "space-y-1", children: [_jsx(Label, { htmlFor: "na-name", children: "Naam" }), _jsx(Input, { id: "na-name", value: name, onChange: (e) => setName(e.target.value), autoFocus: true })] }), _jsxs("div", { className: "space-y-1", children: [_jsx(Label, { htmlFor: "na-code", children: "Artikelcode (optioneel)" }), _jsx(Input, { id: "na-code", value: code, onChange: (e) => setCode(e.target.value) }), _jsx("p", { className: "text-xs text-muted-foreground", children: "Leeg laten: er wordt automatisch een code gemaakt." })] })] })), step === 1 && (_jsxs("div", { className: "space-y-1", children: [_jsx(Label, { htmlFor: "na-stock", children: "Voorraad (stuks)" }), _jsx(Input, { id: "na-stock", type: "number", min: 0, value: canSaveStock ? stock : "", onChange: (e) => setStock(e.target.value), disabled: !canSaveStock, "aria-describedby": "na-stock-help", autoFocus: true }), !canSaveStock && (_jsx("p", { id: "na-stock-help", className: "text-xs text-muted-foreground", children: "Koppeling wordt gecontroleerd\u2026" })), canSaveStock && !hasFloriday && (_jsx("p", { id: "na-stock-help", className: "text-xs text-muted-foreground", children: LOCAL_STOCK_TEXT }))] })), step === 2 && (_jsxs("div", { className: "space-y-1", children: [_jsx(Label, { htmlFor: "na-price", children: "Prijs per stuk (EUR, excl. btw)" }), _jsx(Input, { id: "na-price", inputMode: "decimal", value: price, onChange: (e) => setPrice(e.target.value), placeholder: "Bijv. 1,10", autoFocus: true }), _jsx("p", { className: "text-xs text-muted-foreground", children: "Zonder prijs komt het artikel niet in Floriday (\"Prijs ontbreekt\")." })] })), saveError && (_jsx("div", { role: "alert", className: "rounded-md border border-destructive/40 bg-destructive/10 p-3 text-sm text-destructive", children: saveError })), _jsxs("div", { className: "flex flex-wrap justify-between gap-2", children: [_jsx(Button, { variant: "outline", disabled: step === 0 || saving, onClick: () => setStep((s) => s - 1), children: "Vorige" }), step < STEPS.length - 1 ? (_jsx(Button, { disabled: !canNext, onClick: () => setStep((s) => s + 1), children: "Volgende" })) : (_jsxs(Button, { disabled: saving || !name.trim(), onClick: () => void save(), children: [saving ? _jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }) : _jsx(Check, { className: "mr-2 h-4 w-4" }), saving ? "Bezig met opslaan…" : "Artikel opslaan"] }))] })] }), hasFloriday && (_jsx("button", { type: "button", className: "text-sm text-muted-foreground underline hover:text-foreground", onClick: () => setFull(true), children: "Meteen alle Floriday-gegevens invullen" }))] }));
168
+ return (_jsxs("main", { className: "p-6 space-y-4 max-w-2xl", children: [_jsxs(Link, { to: "/voorraad", className: "inline-flex items-center gap-1 text-sm text-muted-foreground hover:text-foreground", children: [_jsx(ArrowLeft, { className: "h-4 w-4" }), "Terug naar voorraad"] }), _jsxs("div", { children: [_jsx("h1", { className: "text-xl font-semibold", children: "Artikel toevoegen" }), _jsxs("p", { className: "text-sm text-muted-foreground", children: ["Stap ", step + 1, " van ", STEPS.length, ": ", STEPS[step]] })] }), _jsx(FloridayConnectionNotice, { status: conn.status, environment: conn.selectedEnvironment, error: conn.error }), _jsxs(Card, { className: "p-5 space-y-4", children: [step === 0 && (_jsxs(_Fragment, { children: [_jsxs("div", { className: "space-y-1", children: [_jsx(Label, { htmlFor: "na-name", children: "Naam" }), _jsx(Input, { id: "na-name", value: name, onChange: (e) => setName(e.target.value), autoFocus: true })] }), _jsxs("div", { className: "space-y-1", children: [_jsx(Label, { htmlFor: "na-code", children: "Artikelcode (optioneel)" }), _jsx(Input, { id: "na-code", value: code, onChange: (e) => setCode(e.target.value) }), _jsx("p", { className: "text-xs text-muted-foreground", children: "Leeg laten: er wordt automatisch een code gemaakt." })] })] })), step === 1 && (_jsxs("div", { className: "space-y-1", children: [_jsx(Label, { htmlFor: "na-stock", children: "Voorraad (stuks)" }), _jsx(Input, { id: "na-stock", type: "number", min: 0, value: canSaveStock ? stock : "", onChange: (e) => setStock(e.target.value), disabled: !canSaveStock, "aria-describedby": "na-stock-help", autoFocus: true }), !canSaveStock && (_jsx("p", { id: "na-stock-help", className: "text-xs text-muted-foreground", children: "Koppeling wordt gecontroleerd\u2026" })), canSaveStock && !hasFloriday && (_jsx("p", { id: "na-stock-help", className: "text-xs text-muted-foreground", children: LOCAL_STOCK_TEXT }))] })), step === 2 && (_jsxs("div", { className: "space-y-1", children: [_jsx(Label, { htmlFor: "na-price", children: "Prijs per stuk (EUR, excl. btw)" }), _jsx(Input, { id: "na-price", inputMode: "decimal", value: price, onChange: (e) => setPrice(e.target.value), placeholder: "Bijv. 1,10", autoFocus: true }), _jsx("p", { className: "text-xs text-muted-foreground", children: "Zonder prijs komt het artikel niet in Floriday (\"Prijs ontbreekt\")." })] })), step === 3 && (_jsxs("div", { className: "space-y-3", children: [_jsx("p", { className: "text-sm text-muted-foreground", children: "Zonder volledige belading kan de karindeling dit artikel niet plannen. Alle vijf velden zijn verplicht." }), _jsxs("div", { className: "space-y-1", children: [_jsx(Label, { htmlFor: "na-fust", children: "Fust (VBN-code)" }), _jsxs("select", { id: "na-fust", className: "w-full rounded-md border bg-background px-3 py-2 text-sm", value: packing.vbnPackageCode, onChange: (e) => setPack("vbnPackageCode", e.target.value), children: [_jsx("option", { value: "", children: "Kies een fust\u2026" }), fusts.map((f) => (_jsxs("option", { value: String(f.vbn), children: [f.vbn, " \u2013 ", f.name] }, f.vbn)))] }), _jsx(Input, { "aria-label": "Andere VBN-fustcode", placeholder: "Of typ een VBN-fustcode, bijv. 412", inputMode: "numeric", value: packing.vbnPackageCode, onChange: (e) => setPack("vbnPackageCode", e.target.value) })] }), _jsxs("div", { className: "grid grid-cols-1 gap-3 sm:grid-cols-3", children: [_jsxs("div", { className: "space-y-1", children: [_jsx(Label, { htmlFor: "na-ppc", children: "Planten per fust" }), _jsx(Input, { id: "na-ppc", inputMode: "numeric", value: packing.piecesPerPackage, onChange: (e) => setPack("piecesPerPackage", e.target.value) })] }), _jsxs("div", { className: "space-y-1", children: [_jsx(Label, { htmlFor: "na-cpl", children: "Fusten per laag" }), _jsx(Input, { id: "na-cpl", inputMode: "numeric", value: packing.packagesPerLayer, onChange: (e) => setPack("packagesPerLayer", e.target.value) })] }), _jsxs("div", { className: "space-y-1", children: [_jsx(Label, { htmlFor: "na-lpc", children: "Lagen per kar" }), _jsx(Input, { id: "na-lpc", inputMode: "numeric", value: packing.layersPerLoadCarrier, onChange: (e) => setPack("layersPerLoadCarrier", e.target.value) })] })] }), _jsxs("div", { className: "space-y-1", children: [_jsx(Label, { htmlFor: "na-lc", children: "Ladingdrager" }), _jsxs("select", { id: "na-lc", className: "w-full rounded-md border bg-background px-3 py-2 text-sm", value: packing.loadCarrier, onChange: (e) => setPack("loadCarrier", e.target.value), children: [_jsx("option", { value: "", children: "Kies een ladingdrager\u2026" }), carriers.items.map((c) => (_jsx("option", { value: c.code, children: c.naam }, c.code)))] }), carriers.error && _jsx("p", { className: "text-xs text-destructive", children: carriers.error })] }), missingPacking.length > 0 && (_jsxs("p", { className: "text-xs text-muted-foreground", children: ["Nog invullen: ", missingPacking.join(", "), "."] }))] })), saveError && (_jsx("div", { role: "alert", className: "rounded-md border border-destructive/40 bg-destructive/10 p-3 text-sm text-destructive", children: saveError })), _jsxs("div", { className: "flex flex-wrap justify-between gap-2", children: [_jsx(Button, { variant: "outline", disabled: step === 0 || saving, onClick: () => setStep((s) => s - 1), children: "Vorige" }), step < STEPS.length - 1 ? (_jsx(Button, { disabled: !canNext, onClick: () => setStep((s) => s + 1), children: "Volgende" })) : (_jsxs(Button, { disabled: saving || !name.trim() || missingPacking.length > 0, onClick: () => void save(), children: [saving ? _jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }) : _jsx(Check, { className: "mr-2 h-4 w-4" }), saving ? "Bezig met opslaan…" : "Artikel opslaan"] }))] })] }), hasFloriday && (_jsx("button", { type: "button", className: "text-sm text-muted-foreground underline hover:text-foreground", onClick: () => setFull(true), children: "Meteen alle Floriday-gegevens invullen" }))] }));
103
169
  }
@@ -14,4 +14,11 @@ export declare function PublishProgressCard({ job, steps, onRetry, retrying, onF
14
14
  * stap mislukt is, telt als mislukt (scherm nooit blijven hangen op "Bezig").
15
15
  */
16
16
  export declare function effectiveJobStatus(jobStatus: string, steps: Array<Pick<PublishStepRow, "status" | "step_key">>): string;
17
+ /** Uitkomst per onderdeel, zodra de publicatie klaar is. */
18
+ export declare function publishOutcome(steps: Array<Pick<PublishStepRow, "status" | "step_key">>): {
19
+ key: string;
20
+ label: string;
21
+ text: string;
22
+ failed: boolean;
23
+ }[];
17
24
  //# sourceMappingURL=PublishProgressCard.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"PublishProgressCard.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/PublishProgressCard.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAoBhF;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,EAClC,GAAG,EACH,KAAK,EACL,OAAO,EACP,QAAQ,EACR,SAAS,GACV,EAAE;IACD,GAAG,EAAE,aAAa,GAAG,IAAI,CAAC;IAC1B,KAAK,EAAE,cAAc,EAAE,CAAC;IACxB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CACvC,sCA0DA;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,cAAc,EAAE,QAAQ,GAAG,UAAU,CAAC,CAAC,GACxD,MAAM,CAMR"}
1
+ {"version":3,"file":"PublishProgressCard.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/PublishProgressCard.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAoBhF;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,EAClC,GAAG,EACH,KAAK,EACL,OAAO,EACP,QAAQ,EACR,SAAS,GACV,EAAE;IACD,GAAG,EAAE,aAAa,GAAG,IAAI,CAAC;IAC1B,KAAK,EAAE,cAAc,EAAE,CAAC;IACxB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CACvC,sCA4DA;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,cAAc,EAAE,QAAQ,GAAG,UAAU,CAAC,CAAC,GACxD,MAAM,CAMR;AAQD,4DAA4D;AAC5D,wBAAgB,cAAc,CAAC,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,cAAc,EAAE,QAAQ,GAAG,UAAU,CAAC,CAAC;;;;;IAWvF"}
@@ -27,7 +27,7 @@ export function PublishProgressCard({ job, steps, onRetry, retrying, onFixStep,
27
27
  const visible = steps.filter((s) => s.status !== "skipped");
28
28
  const failed = visible.filter((s) => s.status === "failed");
29
29
  const status = effectiveJobStatus(job.status, visible);
30
- return (_jsxs(Card, { className: "p-4 space-y-3", children: [_jsxs("div", { className: "flex items-start justify-between gap-3", children: [_jsxs("div", { children: [_jsx("div", { className: "text-sm font-medium", children: JOB_TITLE[status] ?? "Publicatie" }), _jsxs("p", { className: "text-xs text-muted-foreground", children: ["Referentie ", job.reference_code] })] }), status !== "running" && failed.length > 0 && onRetry && (_jsxs(Button, { size: "sm", variant: "outline", onClick: onRetry, disabled: retrying, children: [retrying ? (_jsx(Loader2, { className: "mr-1 h-3 w-3 animate-spin" })) : (_jsx(RefreshCw, { className: "mr-1 h-3 w-3" })), "Opnieuw proberen"] }))] }), _jsx("ul", { className: "space-y-2", children: visible.map((s) => (_jsxs("li", { className: "flex items-start gap-2 text-sm", children: [_jsx("span", { className: "mt-0.5", children: _jsx(StatusIcon, { status: s.status }) }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("div", { className: s.status === "done" ? "text-muted-foreground" : "", children: s.label }), s.status === "failed" && s.error_message && (_jsx("div", { className: "text-xs text-destructive", children: s.error_message }))] }), s.status === "failed" && onFixStep && (_jsx(Button, { size: "sm", variant: "ghost", className: "h-6 px-2 text-xs", onClick: () => onFixStep(s.step_key), children: "Oplossen" }))] }, s.id))) })] }));
30
+ return (_jsxs(Card, { className: "p-4 space-y-3", children: [_jsxs("div", { className: "flex items-start justify-between gap-3", children: [_jsxs("div", { children: [_jsx("div", { className: "text-sm font-medium", children: JOB_TITLE[status] ?? "Publicatie" }), _jsxs("p", { className: "text-xs text-muted-foreground", children: ["Referentie ", job.reference_code] })] }), status !== "running" && failed.length > 0 && onRetry && (_jsxs(Button, { size: "sm", variant: "outline", onClick: onRetry, disabled: retrying, children: [retrying ? (_jsx(Loader2, { className: "mr-1 h-3 w-3 animate-spin" })) : (_jsx(RefreshCw, { className: "mr-1 h-3 w-3" })), "Opnieuw proberen"] }))] }), status !== "running" && _jsx(PublishOutcomeSummary, { steps: visible }), _jsx("ul", { className: "space-y-2", children: visible.map((s) => (_jsxs("li", { className: "flex items-start gap-2 text-sm", children: [_jsx("span", { className: "mt-0.5", children: _jsx(StatusIcon, { status: s.status }) }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("div", { className: s.status === "done" ? "text-muted-foreground" : "", children: s.label }), s.status === "failed" && s.error_message && (_jsx("div", { className: "text-xs text-destructive", children: s.error_message }))] }), s.status === "failed" && onFixStep && (_jsx(Button, { size: "sm", variant: "ghost", className: "h-6 px-2 text-xs", onClick: () => onFixStep(s.step_key), children: "Oplossen" }))] }, s.id))) })] }));
31
31
  }
32
32
  /**
33
33
  * Een job die nog op "running" staat terwijl geen stap meer loopt en er een
@@ -43,3 +43,24 @@ export function effectiveJobStatus(jobStatus, steps) {
43
43
  return "failed";
44
44
  return jobStatus;
45
45
  }
46
+ const OUTCOME_PARTS = [
47
+ { key: "trade-item", label: "Artikel" },
48
+ { key: "offer", label: "Aanbod" },
49
+ { key: "verify", label: "Controle" },
50
+ ];
51
+ /** Uitkomst per onderdeel, zodra de publicatie klaar is. */
52
+ export function publishOutcome(steps) {
53
+ return OUTCOME_PARTS.map((p) => {
54
+ const s = steps.find((x) => x.step_key === p.key);
55
+ const text = !s ? "niet van toepassing"
56
+ : s.status === "done" ? "gelukt"
57
+ : s.status === "failed" ? "niet gelukt"
58
+ : s.status === "skipped" ? "overgeslagen"
59
+ : "niet uitgevoerd";
60
+ return { key: p.key, label: p.label, text, failed: s?.status === "failed" };
61
+ });
62
+ }
63
+ function PublishOutcomeSummary({ steps }) {
64
+ const parts = publishOutcome(steps);
65
+ return (_jsx("div", { className: "flex flex-wrap gap-x-4 gap-y-1 text-sm", "aria-label": "Uitkomst per onderdeel", children: parts.map((p) => (_jsxs("span", { className: p.failed ? "text-destructive" : "text-muted-foreground", children: [_jsxs("span", { className: "font-medium", children: [p.label, ":"] }), " ", p.text] }, p.key))) }));
66
+ }