@flowselections/floriday-voorraad 1.0.122 → 1.0.127
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist-lib/components/voorraad/ArtikelWizard.d.ts +14 -1
- package/dist-lib/components/voorraad/ArtikelWizard.d.ts.map +1 -1
- package/dist-lib/components/voorraad/ArtikelWizard.js +88 -26
- package/dist-lib/components/voorraad/FloridayInfoCard.d.ts.map +1 -1
- package/dist-lib/components/voorraad/FloridayInfoCard.js +4 -2
- package/dist-lib/components/voorraad/FloridayLoadCarriers.d.ts.map +1 -1
- package/dist-lib/components/voorraad/FloridayLoadCarriers.js +3 -2
- package/dist-lib/components/voorraad/NieuwArtikelPage.d.ts.map +1 -1
- package/dist-lib/components/voorraad/NieuwArtikelPage.js +72 -6
- package/dist-lib/components/voorraad/PublishProgressCard.d.ts +7 -0
- package/dist-lib/components/voorraad/PublishProgressCard.d.ts.map +1 -1
- package/dist-lib/components/voorraad/PublishProgressCard.js +22 -1
- package/dist-lib/components/voorraad/StockSyncStatus.d.ts.map +1 -1
- package/dist-lib/components/voorraad/StockSyncStatus.js +1 -1
- package/dist-lib/components/voorraad/VoorraadTable.d.ts.map +1 -1
- package/dist-lib/components/voorraad/VoorraadTable.js +1 -1
- package/dist-lib/hooks/useLoadCarriers.d.ts +12 -0
- package/dist-lib/hooks/useLoadCarriers.d.ts.map +1 -0
- package/dist-lib/hooks/useLoadCarriers.js +49 -0
- package/dist-lib/hooks/useLocalTradeItems.d.ts.map +1 -1
- package/dist-lib/hooks/useLocalTradeItems.js +3 -1
- package/dist-lib/hooks/usePublishJob.d.ts +1 -0
- package/dist-lib/hooks/usePublishJob.d.ts.map +1 -1
- package/dist-lib/hooks/usePublishJob.js +20 -1
- package/dist-lib/hooks/useStockSyncHealth.d.ts +8 -1
- package/dist-lib/hooks/useStockSyncHealth.d.ts.map +1 -1
- package/dist-lib/hooks/useStockSyncHealth.js +91 -19
- package/dist-lib/integrations/supabase/types.d.ts +400 -0
- package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
- package/dist-lib/lib/__tests__/erp-test-123.test.d.ts +2 -0
- package/dist-lib/lib/__tests__/erp-test-123.test.d.ts.map +1 -0
- package/dist-lib/lib/__tests__/erp-test-123.test.js +43 -0
- package/dist-lib/lib/__tests__/load-carriers.test.d.ts +2 -0
- package/dist-lib/lib/__tests__/load-carriers.test.d.ts.map +1 -0
- package/dist-lib/lib/__tests__/load-carriers.test.js +15 -0
- package/dist-lib/lib/buyer-organizations.d.ts +26 -0
- package/dist-lib/lib/buyer-organizations.d.ts.map +1 -0
- package/dist-lib/lib/buyer-organizations.js +41 -0
- package/dist-lib/lib/floriday-customer-offer.functions.d.ts +4 -0
- package/dist-lib/lib/floriday-customer-offer.functions.d.ts.map +1 -1
- package/dist-lib/lib/floriday-customer-offer.functions.js +54 -16
- package/dist-lib/lib/floriday-info.d.ts +0 -1
- package/dist-lib/lib/floriday-info.d.ts.map +1 -1
- package/dist-lib/lib/floriday-info.js +0 -8
- package/dist-lib/lib/simple-packing.d.ts +31 -0
- package/dist-lib/lib/simple-packing.d.ts.map +1 -0
- package/dist-lib/lib/simple-packing.js +62 -0
- package/dist-lib/lib/voorraad-page-head.d.ts +19 -0
- package/dist-lib/lib/voorraad-page-head.d.ts.map +1 -0
- package/dist-lib/lib/voorraad-page-head.js +11 -0
- package/package.json +1 -1
- package/sql/AGENTS.md +2 -1
- package/sql/floriday-voorraad-schema.sql +465 -52
|
@@ -1,7 +1,20 @@
|
|
|
1
|
-
|
|
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":"
|
|
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
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
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
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
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:
|
|
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: "
|
|
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 ||
|
|
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,
|
|
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,
|
|
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:
|
|
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,+
|
|
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 {
|
|
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)" : "", ":", " ",
|
|
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":"
|
|
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
|
-
|
|
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
|
-
|
|
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."] })] }),
|
|
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,
|
|
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
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"StockSyncStatus.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/StockSyncStatus.tsx"],"names":[],"mappings":"AAeA,wBAAgB,eAAe,
|
|
1
|
+
{"version":3,"file":"StockSyncStatus.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/StockSyncStatus.tsx"],"names":[],"mappings":"AAeA,wBAAgB,eAAe,uCA8H9B"}
|