@flowselections/floriday-voorraad 1.0.62 → 1.0.64
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.js +1 -1
- package/dist-lib/components/voorraad/FloridaySentCard.d.ts +24 -0
- package/dist-lib/components/voorraad/FloridaySentCard.d.ts.map +1 -0
- package/dist-lib/components/voorraad/FloridaySentCard.js +85 -0
- package/dist-lib/components/voorraad/FustenPage.d.ts.map +1 -1
- package/dist-lib/components/voorraad/FustenPage.js +18 -4
- package/dist-lib/components/voorraad/NieuwArtikelPage.d.ts.map +1 -1
- package/dist-lib/components/voorraad/NieuwArtikelPage.js +72 -2
- package/dist-lib/components/voorraad/PublicatiesPage.d.ts.map +1 -1
- package/dist-lib/components/voorraad/PublicatiesPage.js +11 -3
- package/dist-lib/components/voorraad/ValidationOverviewTable.d.ts.map +1 -1
- package/dist-lib/components/voorraad/ValidationOverviewTable.js +1 -1
- package/dist-lib/components/voorraad/VoorraadPage.d.ts.map +1 -1
- package/dist-lib/components/voorraad/VoorraadPage.js +28 -2
- package/dist-lib/components/voorraad/VoorraadStockCard.d.ts.map +1 -1
- package/dist-lib/components/voorraad/VoorraadStockCard.js +6 -33
- package/dist-lib/components/voorraad/VoorraadTable.d.ts.map +1 -1
- package/dist-lib/components/voorraad/VoorraadTable.js +1 -1
- package/dist-lib/integrations/supabase/types.d.ts +328 -3
- package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
- package/dist-lib/lib/__tests__/floriday-base-supply.test.d.ts +2 -0
- package/dist-lib/lib/__tests__/floriday-base-supply.test.d.ts.map +1 -0
- package/dist-lib/lib/__tests__/floriday-base-supply.test.js +58 -0
- package/dist-lib/lib/floriday-base-supply.d.ts +53 -0
- package/dist-lib/lib/floriday-base-supply.d.ts.map +1 -0
- package/dist-lib/lib/floriday-base-supply.js +85 -0
- package/dist-lib/lib/floriday-gateway.server.d.ts +21 -0
- package/dist-lib/lib/floriday-gateway.server.d.ts.map +1 -1
- package/dist-lib/lib/floriday-gateway.server.js +30 -0
- package/dist-lib/lib/fusten.js +1 -1
- package/package.json +1 -1
|
@@ -2815,7 +2815,7 @@ function StepAanbieden({ data, update, priceGroups }) {
|
|
|
2815
2815
|
};
|
|
2816
2816
|
return (_jsxs("div", { className: "space-y-6", children: [_jsxs("div", { className: "rounded-md border bg-muted/30 p-3 text-sm text-muted-foreground", children: ["Zet dit product direct in de verkoop bij je gekoppelde Floriday-klanten. We sturen een ", _jsx("code", { children: "TradeItem" }), " + ", _jsx("code", { children: "CustomerOffer" }), " met vaste basisprijs per stuk en publiceren de opgegeven voorraad via de continuous-stock feed. Prijzen per prijsgroep worden als aparte", " ", _jsx("code", { children: "weekly-supply-lines" }), " naar Floriday gepusht."] }), _jsxs("div", { className: "flex items-center justify-between rounded-md border p-3", children: [_jsxs("div", { children: [_jsx("div", { className: "text-sm font-medium", children: "Direct aanbieden voor verkoop" }), _jsx("div", { className: "text-xs text-muted-foreground", children: data.offerForSale
|
|
2817
2817
|
? "Aanbod wordt aangemaakt direct na publiceren."
|
|
2818
|
-
: "Alleen artikel publiceren, geen aanbod aanmaken." })] }), _jsx(Switch, { checked: data.offerForSale, onCheckedChange: (v) => update("offerForSale", Boolean(v)) })] }), data.offerForSale && (_jsxs(_Fragment, { children: [_jsxs("div", { className: "grid gap-4 sm:grid-cols-2", children: [_jsxs("div", { className: "space-y-2", children: [_jsx(Label, { children: "Voorraad (stuks)" }), _jsx(Input, { type: "number", inputMode: "numeric", min: 0, step: 1, value: stockStr, onChange: (e) => update("saleStockPieces", Math.max(0, Math.floor(Number(e.target.value) || 0))) }), _jsx("p", { className: "text-xs text-muted-foreground", children: "Wordt als fysieke voorraad geboekt en via continuous-stock naar Floriday gepusht." })] }), _jsxs("div", { className: "space-y-2", children: [_jsx(Label, { children: "Basisprijs per stuk (EUR)" }), _jsx(Input, { type: "number", inputMode: "decimal", min: 0, step: "0.01", value: priceStr, onChange: (e) => update("salePricePerPiece", Number(e.target.value) || 0) }), _jsxs("p", { className: "text-xs text-muted-foreground", children: ["Wordt gepusht als ", _jsx("code", { children: "weekly-base-supply" }), " \u2014 de standaard catalogusprijs voor klanten zonder eigen prijsgroep."] })] }), _jsxs("div", { className: "space-y-2", children: [_jsx(Label, { children: "Aanbod geldig vanaf" }), _jsx(Input, { type: "date", value: data.offerStartDate, onChange: (e) => update("offerStartDate", e.target.value) }), _jsx("p", { className: "text-xs text-muted-foreground", children: "Aanbod loopt automatisch tot 1 jaar na deze datum." })] }), _jsxs("div", { className: "space-y-2", children: [_jsx(Label, { children: "Zichtbaar voor" }), _jsx("div", { className: "rounded-md border bg-muted/20 px-3 py-2 text-sm", children: data.customerOrganizationIds.length
|
|
2818
|
+
: "Alleen artikel publiceren, geen aanbod aanmaken." })] }), _jsx(Switch, { checked: data.offerForSale, onCheckedChange: (v) => update("offerForSale", Boolean(v)) })] }), data.offerForSale && (_jsxs(_Fragment, { children: [_jsxs("div", { className: "grid gap-4 sm:grid-cols-2", children: [_jsxs("div", { className: "space-y-2", children: [_jsx(Label, { children: "Voorraad (stuks)" }), _jsx(Input, { type: "number", inputMode: "numeric", min: 0, step: 1, value: stockStr, onChange: (e) => update("saleStockPieces", Math.max(0, Math.floor(Number(e.target.value) || 0))) }), _jsx("p", { className: "text-xs text-muted-foreground", children: "Wordt als fysieke voorraad geboekt en via continuous-stock naar Floriday gepusht." })] }), _jsxs("div", { className: "space-y-2", children: [_jsx(Label, { children: "Basisprijs per stuk (EUR, excl. btw)" }), _jsx(Input, { type: "number", inputMode: "decimal", min: 0, step: "0.01", value: priceStr, onChange: (e) => update("salePricePerPiece", Number(e.target.value) || 0) }), _jsxs("p", { className: "text-xs text-muted-foreground", children: ["Wordt gepusht als ", _jsx("code", { children: "weekly-base-supply" }), " \u2014 de standaard catalogusprijs voor klanten zonder eigen prijsgroep."] })] }), _jsxs("div", { className: "space-y-2", children: [_jsx(Label, { children: "Aanbod geldig vanaf" }), _jsx(Input, { type: "date", value: data.offerStartDate, onChange: (e) => update("offerStartDate", e.target.value) }), _jsx("p", { className: "text-xs text-muted-foreground", children: "Aanbod loopt automatisch tot 1 jaar na deze datum." })] }), _jsxs("div", { className: "space-y-2", children: [_jsx(Label, { children: "Zichtbaar voor" }), _jsx("div", { className: "rounded-md border bg-muted/20 px-3 py-2 text-sm", children: data.customerOrganizationIds.length
|
|
2819
2819
|
? `${data.customerOrganizationIds.length} geselecteerde klant(en)`
|
|
2820
2820
|
: "Alle gekoppelde Floriday-klanten" }), _jsxs("p", { className: "text-xs text-muted-foreground", children: ["Klantselectie beheer je in de stap ", _jsx("em", { children: "Klanten" }), "."] })] })] }), _jsxs("div", { className: "space-y-2 rounded-md border p-4", children: [_jsxs("div", { className: "flex items-baseline justify-between", children: [_jsxs("div", { children: [_jsx("div", { className: "text-sm font-medium", children: "Prijzen per Floriday-prijsgroep" }), _jsx("div", { className: "text-xs text-muted-foreground", children: "Laat leeg om voor die groep g\u00E9\u00E9n afwijkende prijs te publiceren. Basisprijs blijft dan gelden." })] }), _jsxs("div", { className: "text-xs text-muted-foreground", children: [priceGroups.length, " groep", priceGroups.length === 1 ? "" : "en"] })] }), priceGroups.length === 0 ? (_jsx("p", { className: "text-sm text-muted-foreground py-4", children: "Geen actieve prijsgroepen gevonden voor deze Floriday-connectie." })) : (_jsx("div", { className: "grid gap-3 sm:grid-cols-2 pt-2", children: priceGroups.map((g) => {
|
|
2821
2821
|
const val = data.priceGroupPrices?.[g.id];
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
type Row = {
|
|
2
|
+
id: string;
|
|
3
|
+
status: string;
|
|
4
|
+
number_of_pieces: number | null;
|
|
5
|
+
price_sent: number | null;
|
|
6
|
+
stock_mode: string | null;
|
|
7
|
+
target_year: number | null;
|
|
8
|
+
target_week: number | null;
|
|
9
|
+
updated_at: string;
|
|
10
|
+
sent_at: string | null;
|
|
11
|
+
skip_reason: string | null;
|
|
12
|
+
last_error: string | null;
|
|
13
|
+
result_label: string | null;
|
|
14
|
+
price_note?: string | null;
|
|
15
|
+
};
|
|
16
|
+
export declare function resultText(r: Pick<Row, "status" | "result_label" | "skip_reason" | "last_error" | "stock_mode" | "target_week">): string;
|
|
17
|
+
export declare function FloridaySentCard({ connectionId, productId, tradeItemId, productType, }: {
|
|
18
|
+
connectionId: string | null;
|
|
19
|
+
productId?: string | null;
|
|
20
|
+
tradeItemId?: string | null;
|
|
21
|
+
productType?: string | null;
|
|
22
|
+
}): import("react").JSX.Element;
|
|
23
|
+
export {};
|
|
24
|
+
//# sourceMappingURL=FloridaySentCard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FloridaySentCard.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/FloridaySentCard.tsx"],"names":[],"mappings":"AAUA,KAAK,GAAG,GAAG;IACT,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B,CAAC;AAUF,wBAAgB,UAAU,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,EAAE,QAAQ,GAAG,cAAc,GAAG,aAAa,GAAG,YAAY,GAAG,YAAY,GAAG,aAAa,CAAC,GAAG,MAAM,CA0BxI;AAED,wBAAgB,gBAAgB,CAAC,EAC/B,YAAY,EACZ,SAAS,EACT,WAAW,EACX,WAAW,GACZ,EAAE;IACD,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B,+BAmGA"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* "Laatst naar Floriday": per week wat er naar Floriday ging (aantal, prijs,
|
|
4
|
+
* modus, tijd, resultaat). Leest de wachtrij op artikel-id of Floriday-id.
|
|
5
|
+
* Toont ook het Floriday-id en de uitleg productgroep (ERP) vs. set (Floriday).
|
|
6
|
+
*/
|
|
7
|
+
import { useEffect, useState } from "react";
|
|
8
|
+
import { Card, CardContent } from "@flowselections/core";
|
|
9
|
+
import { Send } from "lucide-react";
|
|
10
|
+
import { supabase } from "../../integrations/supabase/client";
|
|
11
|
+
const MODE_LABEL = {
|
|
12
|
+
FOLLOW_ERP: "Volgt ERP",
|
|
13
|
+
MAXIMUM: "Maximum",
|
|
14
|
+
FIXED_REFILL: "Vast aanvullen",
|
|
15
|
+
MANUAL: "Handmatig",
|
|
16
|
+
DISABLED: "Uit",
|
|
17
|
+
};
|
|
18
|
+
export function resultText(r) {
|
|
19
|
+
if (r.stock_mode === "DISABLED")
|
|
20
|
+
return "Niet verzonden: modus uit";
|
|
21
|
+
if (r.result_label)
|
|
22
|
+
return r.result_label;
|
|
23
|
+
switch (r.status) {
|
|
24
|
+
case "sent":
|
|
25
|
+
return "Aantal geaccepteerd door Floriday";
|
|
26
|
+
case "skipped_no_price":
|
|
27
|
+
return "Prijs ontbreekt";
|
|
28
|
+
case "skipped_no_warehouse":
|
|
29
|
+
return r.skip_reason || "Geen magazijn gekozen";
|
|
30
|
+
case "needs_base_supply":
|
|
31
|
+
case "creating_base_supply":
|
|
32
|
+
return "Aanbod wordt aangemaakt";
|
|
33
|
+
case "skipped_week_closed":
|
|
34
|
+
return `Week ${r.target_week ?? "?"} is gesloten in Floriday`;
|
|
35
|
+
case "pending":
|
|
36
|
+
case "processing":
|
|
37
|
+
return r.last_error ? `Wacht op Floriday (laatste fout: ${r.last_error.slice(0, 80)})` : "Wacht op Floriday";
|
|
38
|
+
case "superseded":
|
|
39
|
+
return "Vervangen door een nieuwere stand";
|
|
40
|
+
case "dead_letter":
|
|
41
|
+
case "failed":
|
|
42
|
+
return r.last_error ? `Mislukt: ${r.last_error.slice(0, 120)}` : "Mislukt";
|
|
43
|
+
default:
|
|
44
|
+
return r.status;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
export function FloridaySentCard({ connectionId, productId, tradeItemId, productType, }) {
|
|
48
|
+
const [rows, setRows] = useState([]);
|
|
49
|
+
useEffect(() => {
|
|
50
|
+
if (!connectionId || (!productId && !tradeItemId))
|
|
51
|
+
return;
|
|
52
|
+
let cancelled = false;
|
|
53
|
+
(async () => {
|
|
54
|
+
const ors = [
|
|
55
|
+
productId ? `product_id.eq.${productId}` : null,
|
|
56
|
+
tradeItemId ? `trade_item_id.eq.${tradeItemId}` : null,
|
|
57
|
+
].filter(Boolean).join(",");
|
|
58
|
+
const { data } = await supabase
|
|
59
|
+
.from("floriday_continuous_stock_queue")
|
|
60
|
+
.select("id,status,number_of_pieces,price_sent,stock_mode,target_year,target_week,updated_at,sent_at,skip_reason,last_error,result_label,price_note")
|
|
61
|
+
.eq("connection_id", connectionId)
|
|
62
|
+
.or(ors)
|
|
63
|
+
.neq("status", "superseded")
|
|
64
|
+
.order("updated_at", { ascending: false })
|
|
65
|
+
.limit(40);
|
|
66
|
+
if (cancelled)
|
|
67
|
+
return;
|
|
68
|
+
// Per week alleen de nieuwste regel.
|
|
69
|
+
const seen = new Set();
|
|
70
|
+
const out = [];
|
|
71
|
+
for (const r of (data ?? [])) {
|
|
72
|
+
const k = `${r.target_year ?? ""}-${r.target_week ?? ""}`;
|
|
73
|
+
if (seen.has(k))
|
|
74
|
+
continue;
|
|
75
|
+
seen.add(k);
|
|
76
|
+
out.push(r);
|
|
77
|
+
}
|
|
78
|
+
setRows(out.slice(0, 8));
|
|
79
|
+
})();
|
|
80
|
+
return () => {
|
|
81
|
+
cancelled = true;
|
|
82
|
+
};
|
|
83
|
+
}, [connectionId, productId, tradeItemId]);
|
|
84
|
+
return (_jsx(Card, { children: _jsxs(CardContent, { className: "space-y-3 p-4", children: [_jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsx(Send, { className: "h-4 w-4 text-muted-foreground" }), _jsx("p", { className: "text-sm font-medium", children: "Laatst naar Floriday" })] }), _jsxs("p", { className: "text-xs text-muted-foreground break-all", children: ["Floriday-id: ", tradeItemId ? _jsx("span", { className: "font-mono", children: tradeItemId }) : "nog niet gekoppeld"] }), (productType === "GROUP" || productType === "SET" || productType === "COMPOSITION") && (_jsx("p", { className: "text-xs text-muted-foreground", children: "Productgroep (ERP): leden met eigen voorraad; alleen het totaal gaat naar Floriday. Set (Floriday): \u00E9\u00E9n artikel samengesteld uit onderdelen; de voorraad gaat pas naar Floriday na akkoord." })), rows.length === 0 ? (_jsx("p", { className: "text-sm text-muted-foreground", children: "Nog niets naar Floriday gestuurd." })) : (_jsx("div", { className: "overflow-x-auto", children: _jsxs("table", { className: "w-full text-xs", children: [_jsx("thead", { className: "text-left text-muted-foreground", children: _jsxs("tr", { children: [_jsx("th", { className: "py-1 pr-3", children: "Week" }), _jsx("th", { className: "py-1 pr-3 text-right", children: "Aantal" }), _jsx("th", { className: "py-1 pr-3 text-right", children: "Prijs (excl. btw)" }), _jsx("th", { className: "py-1 pr-3", children: "Modus" }), _jsx("th", { className: "py-1 pr-3", children: "Tijd" }), _jsx("th", { className: "py-1", children: "Resultaat" })] }) }), _jsx("tbody", { children: rows.map((r) => (_jsxs("tr", { className: "border-t align-top", children: [_jsx("td", { className: "py-1 pr-3 whitespace-nowrap", children: r.target_week ? `${r.target_year}-${r.target_week}` : "—" }), _jsx("td", { className: "py-1 pr-3 text-right tabular-nums", children: r.number_of_pieces ?? "—" }), _jsx("td", { className: "py-1 pr-3 text-right tabular-nums", children: r.price_sent != null ? `€ ${Number(r.price_sent).toFixed(2)}` : "—" }), _jsx("td", { className: "py-1 pr-3 whitespace-nowrap", children: r.stock_mode ? MODE_LABEL[r.stock_mode] ?? r.stock_mode : "—" }), _jsx("td", { className: "py-1 pr-3 whitespace-nowrap", children: new Date(r.sent_at ?? r.updated_at).toLocaleString("nl-NL") }), _jsxs("td", { className: "py-1", children: [resultText(r), r.price_note && r.price_note !== resultText(r) && (_jsx("div", { className: "text-xs text-muted-foreground", children: r.price_note }))] })] }, r.id))) })] }) }))] }) }));
|
|
85
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FustenPage.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/FustenPage.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"FustenPage.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/FustenPage.tsx"],"names":[],"mappings":"AAkKA,wBAAgB,UAAU,gCAgPzB"}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useMemo, useState } from "react";
|
|
2
|
+
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
3
3
|
import { Boxes, Minus, Plus, RefreshCw, Search } from "lucide-react";
|
|
4
|
-
import { Button, Card, Input, Badge } from "@flowselections/core";
|
|
4
|
+
import { Button, Card, Input, Badge, toast } from "@flowselections/core";
|
|
5
|
+
import { supabase } from "../../integrations/supabase/client";
|
|
5
6
|
import { useFloridayConnection } from "../../hooks/useFloridayConnection";
|
|
6
7
|
import { useFusten } from "../../hooks/useFusten";
|
|
7
8
|
import { fetchFustMutations, formatFustDimensions, } from "../../lib/fusten";
|
|
@@ -67,6 +68,14 @@ export function FustenPage() {
|
|
|
67
68
|
const [search, setSearch] = useState("");
|
|
68
69
|
const [busyId, setBusyId] = useState(null);
|
|
69
70
|
const [amounts, setAmounts] = useState({});
|
|
71
|
+
const [negative, setNegative] = useState([]);
|
|
72
|
+
const loadNegative = useCallback(async () => {
|
|
73
|
+
const { data } = await supabase.rpc("fust_negative_stock_report");
|
|
74
|
+
setNegative(Array.isArray(data) ? data : []);
|
|
75
|
+
}, []);
|
|
76
|
+
useEffect(() => {
|
|
77
|
+
void loadNegative();
|
|
78
|
+
}, [loadNegative, items]);
|
|
70
79
|
const filtered = useMemo(() => {
|
|
71
80
|
const q = search.trim().toLowerCase();
|
|
72
81
|
if (!q)
|
|
@@ -84,6 +93,10 @@ export function FustenPage() {
|
|
|
84
93
|
const step = Math.trunc(Number(amounts[f.id]) || 0);
|
|
85
94
|
if (step <= 0)
|
|
86
95
|
return;
|
|
96
|
+
// Waarschuwen (niet blokkeren) bij afboeken boven de voorraad.
|
|
97
|
+
if (sign < 0 && f.stock - step < 0) {
|
|
98
|
+
toast.warning(`Tekort van ${Math.abs(f.stock - step).toLocaleString("nl-NL")} fusten na deze afboeking (${f.name.trim()}).`);
|
|
99
|
+
}
|
|
87
100
|
setBusyId(f.id);
|
|
88
101
|
setAmounts((a) => ({ ...a, [f.id]: "" }));
|
|
89
102
|
try {
|
|
@@ -91,11 +104,12 @@ export function FustenPage() {
|
|
|
91
104
|
}
|
|
92
105
|
finally {
|
|
93
106
|
setBusyId(null);
|
|
107
|
+
void loadNegative();
|
|
94
108
|
}
|
|
95
109
|
};
|
|
96
|
-
return (_jsxs("main", { className: "p-6 space-y-6", children: [_jsxs("header", { className: "grid grid-cols-[minmax(0,1fr)_auto] items-start gap-4", children: [_jsxs("div", { className: "min-w-0", children: [_jsx("h1", { className: "text-2xl font-bold tracking-tight truncate", children: "Fusten" }), _jsx("p", { className: "text-sm text-muted-foreground mt-1", children: "Alle fusttypen worden automatisch afgeleid uit de beladingen van je producten." })] }), _jsxs(Button, { variant: "outline", size: "sm", onClick: () => void sync(), disabled: syncing, children: [_jsx(RefreshCw, { className: `mr-2 h-4 w-4 ${syncing ? "animate-spin" : ""}` }), "Synchroniseren"] })] }), error && (_jsxs("div", { className: "rounded-md border border-destructive/40 bg-destructive/10 text-destructive p-3 text-sm", children: ["Fusten laden mislukt: ", error] })), _jsxs("div", { className: "grid gap-4 sm:grid-cols-3", children: [_jsxs(Card, { className: "p-4", children: [_jsx("p", { className: "text-sm text-muted-foreground", children: "Fusttypen" }), _jsx("p", { className: "mt-2 text-3xl font-bold tabular-nums", children: totals.types })] }), _jsxs(Card, { className: "p-4", children: [_jsx("p", { className: "text-sm text-muted-foreground", children: "Totale fustvoorraad" }), _jsx("p", { className: "mt-2 text-3xl font-bold tabular-nums", children: totals.stock })] }), _jsxs(Card, { className: "p-4", children: [_jsx("p", { className: "text-sm text-muted-foreground", children: "Onder minimum" }), _jsx("p", { className: "mt-2 text-3xl font-bold tabular-nums", children: totals.belowMin })] })] }), _jsxs("div", { className: "relative max-w-sm", children: [_jsx(Search, { className: "absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" }), _jsx(Input, { value: search, onChange: (e) => setSearch(e.target.value), placeholder: "Zoek fust\u2026", className: "pl-9" })] }), _jsx(Card, { className: "overflow-hidden", children: loading ? (_jsx("p", { className: "p-6 text-sm text-muted-foreground", children: "Fusten laden\u2026" })) : filtered.length === 0 ? (_jsxs("div", { className: "p-6 text-sm text-muted-foreground flex items-center gap-2", children: [_jsx(Boxes, { className: "h-4 w-4" }), "Nog geen fusttypen gevonden. Voeg een belading toe aan een product."] })) : (_jsx("div", { className: "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-3", children: "Fust" }), _jsx("th", { className: "p-3", children: "Bron" }), _jsx("th", { className: "p-3", children: "Afmeting (L \u00D7 B \u00D7 H)" }), _jsx("th", { className: "p-3 text-right", children: "Gebruikt door" }), _jsx("th", { className: "p-3 text-right", children: "Voorraad" }), _jsx("th", { className: "p-3 text-right", children: "Minimum" }), _jsx("th", { className: "p-3 text-right", children: "Aanpassen" })] }) }), _jsx("tbody", { children: filtered.map((f) => (_jsxs("tr", { className: "border-t border-border align-top", children: [_jsxs("td", { className: "p-3", children: [_jsx("div", { className: "font-medium", children: f.name }), _jsx("div", { className: "text-xs text-muted-foreground", children: f.vbn_code ? `VBN ${f.vbn_code}` : f.fust_key }), !f.in_use && (_jsx(Badge, { variant: "outline", className: "mt-1", children: "Niet meer in gebruik" })), _jsx("div", { className: "mt-1", children: _jsx(MutationHistory, { fust: f }) })] }), _jsx("td", { className: "p-3", children: f.source === "SUPPLIER" ? "Leverancier" : "VBN" }), _jsx("td", { className: "p-3 text-sm", children: _jsx(DimensionCell, { fust: f, onSave: (v) => patch(f.id, v) }) }), _jsx("td", { className: "p-3 text-right tabular-nums", children: f.used_by_count }), _jsx("td", { className: "p-3 text-right", children: _jsx("span", { className: `tabular-nums font-semibold ${f.minimum_stock > 0 && f.stock < f.minimum_stock
|
|
110
|
+
return (_jsxs("main", { className: "p-6 space-y-6", children: [_jsxs("header", { className: "grid grid-cols-[minmax(0,1fr)_auto] items-start gap-4", children: [_jsxs("div", { className: "min-w-0", children: [_jsx("h1", { className: "text-2xl font-bold tracking-tight truncate", children: "Fusten" }), _jsx("p", { className: "text-sm text-muted-foreground mt-1", children: "Alle fusttypen worden automatisch afgeleid uit de beladingen van je producten." })] }), _jsxs(Button, { variant: "outline", size: "sm", onClick: () => void sync(), disabled: syncing, children: [_jsx(RefreshCw, { className: `mr-2 h-4 w-4 ${syncing ? "animate-spin" : ""}` }), "Synchroniseren"] })] }), error && (_jsxs("div", { className: "rounded-md border border-destructive/40 bg-destructive/10 text-destructive p-3 text-sm", children: ["Fusten laden mislukt: ", error] })), negative.length > 0 && (_jsxs("div", { className: "rounded-md border border-destructive/40 bg-destructive/10 p-3 text-sm text-destructive space-y-1", children: [_jsxs("p", { className: "font-medium", children: [negative.length, " fusttype", negative.length === 1 ? " heeft" : "n hebben", " een tekort."] }), _jsx("p", { className: "text-foreground/80", children: "Er is meer uitgeleverd dan ingeboekt. Controleer de ontvangsten en retouren, of boek de werkelijke telling bij." }), _jsx("ul", { className: "text-xs text-foreground/80", children: negative.map((n) => (_jsxs("li", { children: [n.name.trim(), ": tekort van ", n.shortage.toLocaleString("nl-NL"), " fusten", n.duplicate_names > 0 ? " (dit fusttype bestaat meerdere keren)" : ""] }, n.fust_type_id))) })] })), _jsxs("div", { className: "grid gap-4 sm:grid-cols-3", children: [_jsxs(Card, { className: "p-4", children: [_jsx("p", { className: "text-sm text-muted-foreground", children: "Fusttypen" }), _jsx("p", { className: "mt-2 text-3xl font-bold tabular-nums", children: totals.types })] }), _jsxs(Card, { className: "p-4", children: [_jsx("p", { className: "text-sm text-muted-foreground", children: "Totale fustvoorraad" }), _jsx("p", { className: "mt-2 text-3xl font-bold tabular-nums", children: totals.stock })] }), _jsxs(Card, { className: "p-4", children: [_jsx("p", { className: "text-sm text-muted-foreground", children: "Onder minimum" }), _jsx("p", { className: "mt-2 text-3xl font-bold tabular-nums", children: totals.belowMin })] })] }), _jsxs("div", { className: "relative max-w-sm", children: [_jsx(Search, { className: "absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" }), _jsx(Input, { value: search, onChange: (e) => setSearch(e.target.value), placeholder: "Zoek fust\u2026", className: "pl-9" })] }), _jsx(Card, { className: "overflow-hidden", children: loading ? (_jsx("p", { className: "p-6 text-sm text-muted-foreground", children: "Fusten laden\u2026" })) : filtered.length === 0 ? (_jsxs("div", { className: "p-6 text-sm text-muted-foreground flex items-center gap-2", children: [_jsx(Boxes, { className: "h-4 w-4" }), "Nog geen fusttypen gevonden. Voeg een belading toe aan een product."] })) : (_jsx("div", { className: "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-3", children: "Fust" }), _jsx("th", { className: "p-3", children: "Bron" }), _jsx("th", { className: "p-3", children: "Afmeting (L \u00D7 B \u00D7 H)" }), _jsx("th", { className: "p-3 text-right", children: "Gebruikt door" }), _jsx("th", { className: "p-3 text-right", children: "Voorraad" }), _jsx("th", { className: "p-3 text-right", children: "Minimum" }), _jsx("th", { className: "p-3 text-right", children: "Aanpassen" })] }) }), _jsx("tbody", { children: filtered.map((f) => (_jsxs("tr", { className: "border-t border-border align-top", children: [_jsxs("td", { className: "p-3", children: [_jsx("div", { className: "font-medium", children: f.name }), _jsx("div", { className: "text-xs text-muted-foreground", children: f.vbn_code ? `VBN ${f.vbn_code}` : f.fust_key }), !f.in_use && (_jsx(Badge, { variant: "outline", className: "mt-1", children: "Niet meer in gebruik" })), _jsx("div", { className: "mt-1", children: _jsx(MutationHistory, { fust: f }) })] }), _jsx("td", { className: "p-3", children: f.source === "SUPPLIER" ? "Leverancier" : "VBN" }), _jsx("td", { className: "p-3 text-sm", children: _jsx(DimensionCell, { fust: f, onSave: (v) => patch(f.id, v) }) }), _jsx("td", { className: "p-3 text-right tabular-nums", children: f.used_by_count }), _jsx("td", { className: "p-3 text-right", children: f.stock < 0 ? (_jsxs("span", { className: "tabular-nums font-semibold text-rose-600", title: "Er is meer uitgeleverd dan ingeboekt. Boek ontvangen of geretourneerde fusten bij.", children: ["Tekort van ", Math.abs(f.stock).toLocaleString("nl-NL"), " fusten"] })) : (_jsx("span", { className: `tabular-nums font-semibold ${f.minimum_stock > 0 && f.stock < f.minimum_stock
|
|
97
111
|
? "text-rose-600"
|
|
98
|
-
: ""}`, children: f.stock }) }), _jsx("td", { className: "p-3 text-right", children: _jsx(Input, { type: "number", className: "h-8 w-28 text-right", defaultValue: f.minimum_stock, onBlur: (e) => {
|
|
112
|
+
: ""}`, children: f.stock })) }), _jsx("td", { className: "p-3 text-right", children: _jsx(Input, { type: "number", className: "h-8 w-28 text-right", defaultValue: f.minimum_stock, onBlur: (e) => {
|
|
99
113
|
const v = Math.max(0, Math.trunc(Number(e.target.value) || 0));
|
|
100
114
|
if (v !== f.minimum_stock)
|
|
101
115
|
void patch(f.id, { minimum_stock: v });
|
|
@@ -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":"AAgBA,wBAAgB,gBAAgB,gCA2K/B"}
|
|
@@ -1,5 +1,75 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* Artikel toevoegen: basisroute in drie stappen (naam en code, voorraad,
|
|
4
|
+
* prijs). Floriday-gegevens volgen pas bij "Op Floriday zetten", met een
|
|
5
|
+
* lijst van wat daarvoor nog ontbreekt. De volledige Floriday-invoer blijft
|
|
6
|
+
* beschikbaar via de knop onderaan.
|
|
7
|
+
*/
|
|
8
|
+
import { useState } from "react";
|
|
9
|
+
import { Link, useNavigate } from "@tanstack/react-router";
|
|
10
|
+
import { Button, Card, Input, Label, toast } from "@flowselections/core";
|
|
11
|
+
import { ArrowLeft, Check, Loader2, Send } from "lucide-react";
|
|
12
|
+
import { supabase } from "../../integrations/supabase/client";
|
|
13
|
+
import { validateDraftForPublish } from "../../lib/floriday-import-validator";
|
|
2
14
|
import { ArtikelWizard } from "./ArtikelWizard";
|
|
15
|
+
const STEPS = ["Naam en code", "Voorraad", "Prijs"];
|
|
3
16
|
export function NieuwArtikelPage() {
|
|
4
|
-
|
|
17
|
+
const navigate = useNavigate();
|
|
18
|
+
const [full, setFull] = useState(false);
|
|
19
|
+
const [step, setStep] = useState(0);
|
|
20
|
+
const [name, setName] = useState("");
|
|
21
|
+
const [code, setCode] = useState("");
|
|
22
|
+
const [stock, setStock] = useState("0");
|
|
23
|
+
const [price, setPrice] = useState("");
|
|
24
|
+
const [saving, setSaving] = useState(false);
|
|
25
|
+
const [createdId, setCreatedId] = useState(null);
|
|
26
|
+
if (full)
|
|
27
|
+
return _jsx(ArtikelWizard, {});
|
|
28
|
+
const canNext = step === 0 ? name.trim().length > 0 : step === 1 ? Number(stock) >= 0 && stock !== "" : true;
|
|
29
|
+
const save = async () => {
|
|
30
|
+
setSaving(true);
|
|
31
|
+
try {
|
|
32
|
+
const priceNum = price.trim() === "" ? null : Number(price.replace(",", "."));
|
|
33
|
+
if (priceNum !== null && !(priceNum >= 0))
|
|
34
|
+
throw new Error("Vul een geldige prijs in");
|
|
35
|
+
const { data, error } = await supabase
|
|
36
|
+
.from("products")
|
|
37
|
+
.insert({
|
|
38
|
+
product: name.trim(),
|
|
39
|
+
article_number: code.trim() || null,
|
|
40
|
+
unit: "stuks",
|
|
41
|
+
sale_price: priceNum,
|
|
42
|
+
batch: "",
|
|
43
|
+
location: "",
|
|
44
|
+
})
|
|
45
|
+
.select("id")
|
|
46
|
+
.single();
|
|
47
|
+
if (error)
|
|
48
|
+
throw new Error(error.message);
|
|
49
|
+
const id = data.id;
|
|
50
|
+
const { error: stockErr } = await supabase.rpc("set_product_stock", {
|
|
51
|
+
p_product_id: id,
|
|
52
|
+
p_physical_stock: Math.trunc(Number(stock) || 0),
|
|
53
|
+
p_minimum_stock: null,
|
|
54
|
+
p_incoming_stock: null,
|
|
55
|
+
p_notes: "Nieuw artikel",
|
|
56
|
+
});
|
|
57
|
+
if (stockErr)
|
|
58
|
+
throw new Error(`Artikel aangemaakt, voorraad niet opgeslagen: ${stockErr.message}`);
|
|
59
|
+
toast.success("Artikel aangemaakt");
|
|
60
|
+
setCreatedId(id);
|
|
61
|
+
}
|
|
62
|
+
catch (e) {
|
|
63
|
+
toast.error(e?.message ?? "Opslaan mislukt");
|
|
64
|
+
}
|
|
65
|
+
finally {
|
|
66
|
+
setSaving(false);
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
if (createdId) {
|
|
70
|
+
const missing = validateDraftForPublish({}).missing;
|
|
71
|
+
const hasPrice = Number(price.replace(",", ".")) > 0;
|
|
72
|
+
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."] })] }), _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)))] }), _jsxs("div", { className: "flex flex-wrap gap-2", children: [_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" }) })] })] }) }));
|
|
73
|
+
}
|
|
74
|
+
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]] })] }), _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: stock, onChange: (e) => setStock(e.target.value), autoFocus: true })] })), 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\")." })] })), _jsxs("div", { className: "flex flex-wrap justify-between gap-2", children: [_jsx(Button, { variant: "outline", disabled: step === 0, 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" }), "Artikel opslaan"] }))] })] }), _jsx("button", { type: "button", className: "text-sm text-muted-foreground underline hover:text-foreground", onClick: () => setFull(true), children: "Meteen alle Floriday-gegevens invullen" })] }));
|
|
5
75
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PublicatiesPage.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/PublicatiesPage.tsx"],"names":[],"mappings":"AA+BA,wBAAgB,eAAe,
|
|
1
|
+
{"version":3,"file":"PublicatiesPage.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/PublicatiesPage.tsx"],"names":[],"mappings":"AA+BA,wBAAgB,eAAe,gCAwJ9B"}
|
|
@@ -48,10 +48,18 @@ export function PublicatiesPage() {
|
|
|
48
48
|
};
|
|
49
49
|
}, [jobs]);
|
|
50
50
|
return (_jsxs("main", { className: "p-6 space-y-4", children: [_jsxs("div", { className: "flex items-center justify-between", 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(Button, { variant: "outline", size: "sm", onClick: () => void reload(), children: [_jsx(RefreshCw, { className: "mr-1 h-3 w-3" }), "Vernieuwen"] })] }), _jsxs("div", { children: [_jsx("h1", { className: "text-xl font-semibold", children: "Publicaties" }), _jsx("p", { className: "text-sm text-muted-foreground", children: "Alle producten die je hebt gepubliceerd, met wat er eventueel nog moet gebeuren." })] }), loading ? (_jsxs(Card, { className: "p-6 flex items-center gap-2 text-sm text-muted-foreground", children: [_jsx(Loader2, { className: "h-4 w-4 animate-spin" }), "Laden\u2026"] })) : jobs.length === 0 ? (_jsx(Card, { className: "p-6 text-sm text-muted-foreground", children: "Er zijn nog geen publicaties." })) : (_jsx("div", { className: "space-y-3", children: jobs.map((job) => {
|
|
51
|
-
const steps = stepsByJob[job.id] ?? [];
|
|
52
|
-
const failed = steps.filter((s) => s.status === "failed");
|
|
53
51
|
const target = job.product_id ?? job.trade_item_id;
|
|
54
|
-
|
|
52
|
+
// Oude "Actie nodig" sluit vanzelf zodra een latere publicatie van
|
|
53
|
+
// hetzelfde artikel (op id) wel gelukt is.
|
|
54
|
+
const repaired = job.status === "failed" &&
|
|
55
|
+
jobs.some((o) => o.id !== job.id &&
|
|
56
|
+
(o.product_id ?? o.trade_item_id) === target &&
|
|
57
|
+
target != null &&
|
|
58
|
+
new Date(o.created_at) > new Date(job.created_at) &&
|
|
59
|
+
(o.status === "succeeded" || o.status === "succeeded_with_warnings"));
|
|
60
|
+
const steps = stepsByJob[job.id] ?? [];
|
|
61
|
+
const failed = repaired ? [] : steps.filter((s) => s.status === "failed");
|
|
62
|
+
return (_jsxs(Card, { className: "p-4 space-y-2", children: [_jsxs("div", { className: "flex flex-wrap items-center justify-between gap-2", children: [_jsxs("div", { className: "min-w-0", children: [_jsx("div", { className: "truncate text-sm font-medium", children: job.title || "Product" }), _jsxs("div", { className: "text-xs text-muted-foreground", children: [new Date(job.created_at).toLocaleString("nl-NL"), job.environment !== "live" ? " · testomgeving" : ""] })] }), _jsxs("div", { className: "flex items-center gap-2", children: [repaired ? (_jsx("span", { className: "rounded-full px-2 py-0.5 text-xs font-medium bg-emerald-100 text-emerald-800", children: "Hersteld" })) : (_jsx(StatusChip, { status: job.status })), target && failed.length > 0 && (_jsx(Button, { asChild: true, size: "sm", variant: "outline", children: _jsx(Link, { to: "/voorraad/$id", params: { id: String(target) }, search: { hervat: job.id }, children: "Afronden" }) }))] })] }), steps.length > 0 && (_jsx("ul", { className: "flex flex-wrap gap-x-4 gap-y-1 text-xs", children: steps
|
|
55
63
|
.filter((s) => s.status !== "skipped")
|
|
56
64
|
.map((s) => (_jsxs("li", { className: "flex items-center gap-1", children: [s.status === "done" ? (_jsx(Check, { className: "h-3 w-3 text-emerald-600" })) : s.status === "failed" ? (_jsx(AlertTriangle, { className: "h-3 w-3 text-destructive" })) : (_jsx(Loader2, { className: "h-3 w-3 text-muted-foreground" })), _jsx("span", { className: s.status === "failed"
|
|
57
65
|
? "text-destructive"
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ValidationOverviewTable.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/ValidationOverviewTable.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAQ3D;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,EACtC,KAAK,EACL,YAAY,EACZ,SAAS,GACV,EAAE;IACD,KAAK,EAAE,SAAS,EAAE,CAAC;IACnB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,SAAS,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACvC,+
|
|
1
|
+
{"version":3,"file":"ValidationOverviewTable.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/ValidationOverviewTable.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAQ3D;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,EACtC,KAAK,EACL,YAAY,EACZ,SAAS,GACV,EAAE;IACD,KAAK,EAAE,SAAS,EAAE,CAAC;IACnB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,SAAS,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACvC,+BAwKA"}
|
|
@@ -86,7 +86,7 @@ export function ValidationOverviewTable({ items, connectionId, onChanged, }) {
|
|
|
86
86
|
it.raw?.data ??
|
|
87
87
|
{};
|
|
88
88
|
const v = validateDraftForPublish(payload);
|
|
89
|
-
return (_jsxs("tr", { className: "border-t align-top", children: [_jsx("td", { className: "p-3 font-mono text-xs", children: it.articleNumber || "—" }), _jsx("td", { className: "p-3", children: it.name }), _jsx("td", { className: "p-3", children: _jsx(Badge, { className: "bg-slate-500/15 text-slate-700 border-slate-500/30", children: "Niet actief op Floriday" }) }), _jsx("td", { className: "p-3 text-xs text-muted-foreground max-w-[260px]", children: reasonFor(it) }), _jsx("td", { className: "p-3", children: v.missing.length === 0 ? (_jsx("span", { className: "text-xs text-emerald-700", children: "Klaar om te activeren" })) : (
|
|
89
|
+
return (_jsxs("tr", { className: "border-t align-top", children: [_jsx("td", { className: "p-3 font-mono text-xs", children: it.articleNumber || "—" }), _jsx("td", { className: "p-3", children: it.name }), _jsx("td", { className: "p-3", children: _jsx(Badge, { className: "bg-slate-500/15 text-slate-700 border-slate-500/30", children: "Niet actief op Floriday" }) }), _jsx("td", { className: "p-3 text-xs text-muted-foreground max-w-[260px]", children: reasonFor(it) }), _jsx("td", { className: "p-3", children: v.missing.length === 0 ? (_jsx("span", { className: "text-xs text-emerald-700", children: "Klaar om te activeren" })) : (_jsxs("span", { className: "text-xs text-muted-foreground", title: v.missing.join(", "), children: ["Nog nodig: ", v.missing.slice(0, 3).join(", ").toLowerCase(), v.missing.length > 3 ? ` en nog ${v.missing.length - 3} andere` : "", "."] })) }), _jsx("td", { className: "p-3", children: _jsxs("div", { className: "flex justify-end gap-1", children: [_jsxs(Button, { size: "sm", variant: "outline", onClick: () => {
|
|
90
90
|
const target = it.productId ?? it.localId ?? it.id;
|
|
91
91
|
navigate({ to: "/voorraad/$id", params: { id: target } });
|
|
92
92
|
}, children: [_jsx(Pencil, { className: "h-3 w-3 mr-1" }), "Bewerken"] }), _jsxs(Button, { size: "sm", onClick: () => publish(it), disabled: !v.ok, children: [_jsx(Send, { className: "h-3 w-3 mr-1" }), "Activeer op Floriday"] })] }) })] }, it.id));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VoorraadPage.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/VoorraadPage.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"VoorraadPage.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/VoorraadPage.tsx"],"names":[],"mappings":"AAwGA,wBAAgB,YAAY,gCA8V3B"}
|
|
@@ -4,6 +4,7 @@ import { Link } from "@tanstack/react-router";
|
|
|
4
4
|
import { Button, Card, Input, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@flowselections/core";
|
|
5
5
|
import { AlertTriangle, CheckCircle2, FileText, PackageX, Plus, Search, } from "lucide-react";
|
|
6
6
|
import { useFloridayConnection } from "../../hooks/useFloridayConnection";
|
|
7
|
+
import { supabase } from "../../integrations/supabase/client";
|
|
7
8
|
import { useVoorraadData } from "../../hooks/useVoorraadData";
|
|
8
9
|
import { StockSyncStatus } from "./StockSyncStatus";
|
|
9
10
|
import { StockIntegrityAlert } from "./StockIntegrityAlert";
|
|
@@ -52,6 +53,29 @@ export function VoorraadPage() {
|
|
|
52
53
|
const [selected, setSelected] = useState(new Set());
|
|
53
54
|
const [drawerIds, setDrawerIds] = useState([]);
|
|
54
55
|
const [drawerOpen, setDrawerOpen] = useState(false);
|
|
56
|
+
// Artikelen zonder geldige prijs per stuk (eenheid "stuks", prijs > 0), op id.
|
|
57
|
+
const [noPriceIds, setNoPriceIds] = useState(new Set());
|
|
58
|
+
const [noPriceOnly, setNoPriceOnly] = useState(false);
|
|
59
|
+
useEffect(() => {
|
|
60
|
+
let cancelled = false;
|
|
61
|
+
void supabase
|
|
62
|
+
.from("products")
|
|
63
|
+
.select("id, unit, sale_price")
|
|
64
|
+
.then(({ data }) => {
|
|
65
|
+
if (cancelled)
|
|
66
|
+
return;
|
|
67
|
+
const s = new Set();
|
|
68
|
+
for (const p of (data ?? [])) {
|
|
69
|
+
const isPiece = (p.unit ?? "").trim().toLowerCase() === "stuks";
|
|
70
|
+
if (!isPiece || !(Number(p.sale_price) > 0))
|
|
71
|
+
s.add(p.id);
|
|
72
|
+
}
|
|
73
|
+
setNoPriceIds(s);
|
|
74
|
+
});
|
|
75
|
+
return () => {
|
|
76
|
+
cancelled = true;
|
|
77
|
+
};
|
|
78
|
+
}, [items]);
|
|
55
79
|
const toggleSelect = (id) => setSelected((cur) => {
|
|
56
80
|
const next = new Set(cur);
|
|
57
81
|
if (next.has(id))
|
|
@@ -86,6 +110,8 @@ export function VoorraadPage() {
|
|
|
86
110
|
const filtered = useMemo(() => {
|
|
87
111
|
const q = search.trim().toLowerCase();
|
|
88
112
|
return items.filter((i) => {
|
|
113
|
+
if (noPriceOnly && !(i.productId && noPriceIds.has(i.productId)))
|
|
114
|
+
return false;
|
|
89
115
|
const avail = availableOf(i);
|
|
90
116
|
if (stockFilter === "in_stock" && avail <= 0)
|
|
91
117
|
return false;
|
|
@@ -105,7 +131,7 @@ export function VoorraadPage() {
|
|
|
105
131
|
return (i.name.toLowerCase().includes(q) ||
|
|
106
132
|
(i.articleNumber ?? "").toLowerCase().includes(q));
|
|
107
133
|
});
|
|
108
|
-
}, [items, search, stockFilter, floridayFilter, typeFilter]);
|
|
134
|
+
}, [items, search, stockFilter, floridayFilter, typeFilter, noPriceOnly, noPriceIds]);
|
|
109
135
|
const totalPages = Math.max(1, Math.ceil(filtered.length / pageSize));
|
|
110
136
|
useEffect(() => {
|
|
111
137
|
if (page > totalPages)
|
|
@@ -119,7 +145,7 @@ export function VoorraadPage() {
|
|
|
119
145
|
{ id: "out_of_stock", label: "Niet op voorraad", count: counts.outOfStock },
|
|
120
146
|
{ id: "below_min", label: "Onder minimum", count: counts.belowMin },
|
|
121
147
|
];
|
|
122
|
-
return (_jsxs("main", { className: "p-6 space-y-6", children: [_jsxs("header", { className: "
|
|
148
|
+
return (_jsxs("main", { className: "p-6 space-y-6", children: [_jsxs("header", { className: "flex flex-wrap items-start justify-between gap-4", children: [_jsxs("div", { className: "min-w-[12rem] flex-1", children: [_jsx("h1", { className: "text-2xl font-bold tracking-tight", children: "Voorraad" }), _jsx("p", { className: "text-sm text-muted-foreground mt-1", children: "Beheer je artikelen en voorraadniveaus." })] }), _jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsx(Button, { asChild: true, variant: "outline", size: "sm", children: _jsx(Link, { to: "/voorraad/validatie", children: "Validatie" }) }), _jsx(Button, { asChild: true, variant: "outline", size: "sm", children: _jsx(Link, { to: "/voorraad/publicaties", children: "Publicaties" }) }), _jsx(Button, { asChild: true, disabled: !activeId, size: "sm", children: _jsxs(Link, { to: "/voorraad/nieuw", children: [_jsx(Plus, { className: "mr-2 h-4 w-4" }), "Artikel toevoegen"] }) })] })] }), _jsx(StockIntegrityAlert, {}), _jsx(SupplyReadScopeAlert, {}), _jsx(StockSyncStatus, {}), noPriceIds.size > 0 && (_jsxs("div", { className: "flex flex-wrap items-start gap-2 rounded-md border border-amber-500/30 bg-amber-500/10 px-3 py-2 text-sm text-amber-800", children: [_jsx(AlertTriangle, { className: "mt-0.5 h-4 w-4 shrink-0" }), _jsxs("div", { className: "flex-1 min-w-[12rem]", children: [_jsxs("span", { className: "font-medium", children: [noPriceIds.size, " artikel", noPriceIds.size === 1 ? "" : "en", " staan niet in Floriday omdat de prijs ontbreekt."] }), " ", "Vul een verkoopprijs per stuk (excl. btw) in bij het artikel."] }), _jsx(Button, { variant: "outline", size: "sm", onClick: () => setNoPriceOnly((v) => !v), children: noPriceOnly ? "Alle artikelen tonen" : "Toon deze artikelen" })] })), _jsx(StockPushMonitor, { connectionId: activeId ?? null }), connError && (_jsxs("div", { className: "rounded-md border border-destructive/40 bg-destructive/10 text-destructive p-3 text-sm", children: ["Floriday-connectie laden mislukt: ", connError] })), error && (_jsxs("div", { className: "rounded-md border border-destructive/40 bg-destructive/10 text-destructive p-3 text-sm", children: ["Voorraad laden mislukt: ", error] })), _jsxs("div", { className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4", children: [_jsx(KpiCard, { icon: _jsx(FileText, { className: "h-4 w-4" }), tone: "primary", label: "Artikelen", count: counts.total, active: stockFilter === "all", onClick: () => toggle("all") }), _jsx(KpiCard, { icon: _jsx(CheckCircle2, { className: "h-4 w-4" }), tone: "emerald", label: "Op voorraad", count: counts.inStock, active: stockFilter === "in_stock", onClick: () => toggle("in_stock") }), _jsx(KpiCard, { icon: _jsx(PackageX, { className: "h-4 w-4" }), tone: "rose", label: "Niet op voorraad", count: counts.outOfStock, active: stockFilter === "out_of_stock", onClick: () => toggle("out_of_stock") }), _jsx(KpiCard, { icon: _jsx(AlertTriangle, { className: "h-4 w-4" }), tone: "amber", label: "Onder minimum", count: counts.belowMin, active: stockFilter === "below_min", onClick: () => toggle("below_min") })] }), _jsxs(Card, { className: "overflow-hidden p-0", children: [_jsx("div", { className: "flex flex-wrap items-center gap-1 border-b px-4", children: TABS.map((t) => {
|
|
123
149
|
const active = stockFilter === t.id;
|
|
124
150
|
return (_jsxs("button", { type: "button", onClick: () => setStockFilter(t.id), className: `relative py-3 px-3 text-sm font-medium whitespace-nowrap transition-colors ${active
|
|
125
151
|
? "text-foreground"
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VoorraadStockCard.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/VoorraadStockCard.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"VoorraadStockCard.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/VoorraadStockCard.tsx"],"names":[],"mappings":"AAeA,UAAU,KAAK;IACb,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED,wBAAgB,iBAAiB,CAAC,EAAE,YAAY,EAAE,WAAW,EAAE,OAAO,EAAE,SAAS,EAAE,EAAE,KAAK,sCA+LzF"}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { useEffect, useState } from "react";
|
|
3
3
|
import { Button, Card, CardContent, Input, Label, Textarea, toast, } from "@flowselections/core";
|
|
4
|
-
import { Loader2, Package, Check
|
|
4
|
+
import { Loader2, Package, Check } from "lucide-react";
|
|
5
5
|
import { useTradeItemStock, stockKeysFor } from "../../hooks/useTradeItemStock";
|
|
6
6
|
import { supabase } from "../../integrations/supabase/client";
|
|
7
|
+
import { FloridaySentCard } from "./FloridaySentCard";
|
|
7
8
|
export function VoorraadStockCard({ connectionId, tradeItemId, draftId, productId }) {
|
|
8
9
|
const keys = stockKeysFor({
|
|
9
10
|
tradeItemId,
|
|
@@ -16,10 +17,8 @@ export function VoorraadStockCard({ connectionId, tradeItemId, draftId, productI
|
|
|
16
17
|
const [saving, setSaving] = useState(false);
|
|
17
18
|
const [touched, setTouched] = useState(false);
|
|
18
19
|
const [hasBom, setHasBom] = useState(false);
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
// Heeft dit artikel onderdelen (samenstelling)? Alleen dan tonen we de
|
|
22
|
-
// knop "Produceren"; handmatig bijstellen blijft altijd een correctie.
|
|
20
|
+
// Heeft dit artikel onderdelen (samenstelling)? Bepaalt alleen het label
|
|
21
|
+
// in de Floriday-kaart. Produceren bestaat niet meer in deze module.
|
|
23
22
|
useEffect(() => {
|
|
24
23
|
let cancelled = false;
|
|
25
24
|
(async () => {
|
|
@@ -81,7 +80,7 @@ export function VoorraadStockCard({ connectionId, tradeItemId, draftId, productI
|
|
|
81
80
|
const parsed = JSON.parse(raw);
|
|
82
81
|
const msg = parsed?.message ?? parsed?.error ?? raw;
|
|
83
82
|
if (typeof msg === "string" && msg.toLowerCase().includes("onvoldoende voorraad")) {
|
|
84
|
-
return "Onvoldoende voorraad van een onderdeel.
|
|
83
|
+
return "Onvoldoende voorraad van een onderdeel. Pas de voorraad van het onderdeel eerst aan.";
|
|
85
84
|
}
|
|
86
85
|
return typeof msg === "string" ? msg : raw;
|
|
87
86
|
}
|
|
@@ -102,35 +101,9 @@ export function VoorraadStockCard({ connectionId, tradeItemId, draftId, productI
|
|
|
102
101
|
setSaving(false);
|
|
103
102
|
}
|
|
104
103
|
};
|
|
105
|
-
const handleProduce = async () => {
|
|
106
|
-
const qty = Number(produceQty);
|
|
107
|
-
if (!Number.isFinite(qty) || qty <= 0) {
|
|
108
|
-
toast.error("Vul een aantal groter dan 0 in");
|
|
109
|
-
return;
|
|
110
|
-
}
|
|
111
|
-
setProducing(true);
|
|
112
|
-
try {
|
|
113
|
-
const { error } = await supabase.rpc("produce_designed_product", {
|
|
114
|
-
p_parent_kind: productId ? "product" : "floriday",
|
|
115
|
-
p_parent_id: (productId ?? tradeItemId),
|
|
116
|
-
p_quantity: qty,
|
|
117
|
-
});
|
|
118
|
-
if (error)
|
|
119
|
-
throw new Error(error.message);
|
|
120
|
-
toast.success(`${qty} stuks geproduceerd; onderdelen afgeschreven`);
|
|
121
|
-
setProduceQty("");
|
|
122
|
-
setTouched(false);
|
|
123
|
-
}
|
|
124
|
-
catch (e) {
|
|
125
|
-
toast.error(`Produceren mislukt: ${humanError(e)}`);
|
|
126
|
-
}
|
|
127
|
-
finally {
|
|
128
|
-
setProducing(false);
|
|
129
|
-
}
|
|
130
|
-
};
|
|
131
104
|
const numField = (id, label) => (_jsxs("div", { className: "space-y-1", children: [_jsx(Label, { htmlFor: id, children: label }), _jsx(Input, { id: id, type: "number", min: 0, value: String(values[id] ?? 0), onChange: (e) => {
|
|
132
105
|
setTouched(true);
|
|
133
106
|
setValues((v) => ({ ...v, [id]: Number(e.target.value) || 0 }));
|
|
134
107
|
} })] }));
|
|
135
|
-
return (_jsxs(Card, { children: [_jsxs("div", { className: "grid grid-cols-[auto_minmax(0,1fr)_auto] items-center gap-3 px-6 py-4", children: [_jsx("span", { className: "flex h-7 w-7 items-center justify-center rounded-full border border-primary/40 bg-primary/10 text-primary", children: _jsx(Package, { className: "h-4 w-4" }) }), _jsx("span", { className: "font-medium", children: "Voorraad" }), _jsxs("span", { className: "text-xs text-muted-foreground", children: ["Beschikbaar: ", _jsx("span", { className: "tabular-nums font-medium text-foreground", children: available })] })] }), _jsx(CardContent, { className: "pt-0 space-y-4", children: loading ? (_jsxs("div", { className: "flex items-center gap-2 text-sm text-muted-foreground", children: [_jsx(Loader2, { className: "h-4 w-4 animate-spin" }), " Voorraad laden\u2026"] })) : (_jsxs(_Fragment, { children: [_jsxs("div", { className: "grid grid-cols-2 gap-4 sm:grid-cols-4", children: [numField("physical_stock", "Liggend"), numField("minimum_stock", "Minimum"), numField("reserved_stock", "Gereserveerd"), numField("incoming_stock", "Inkomend")] }), _jsxs("div", { className: "space-y-1", children: [_jsx(Label, { htmlFor: "stock-notes", children: "Notitie (optioneel)" }), _jsx(Textarea, { id: "stock-notes", rows: 2, value: values.notes ?? "", onChange: (e) => setValues((v) => ({ ...v, notes: e.target.value })), placeholder: "Bijv. locatie of opmerking" })] }), _jsx("div", { className: "flex justify-end", children: _jsxs(Button, { onClick: handleSave, disabled: saving, children: [saving ? (_jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" })) : (_jsx(Check, { className: "mr-2 h-4 w-4" })), "Voorraad opslaan"] }) })
|
|
108
|
+
return (_jsxs("div", { className: "space-y-4", children: [_jsxs(Card, { children: [_jsxs("div", { className: "grid grid-cols-[auto_minmax(0,1fr)_auto] items-center gap-3 px-6 py-4", children: [_jsx("span", { className: "flex h-7 w-7 items-center justify-center rounded-full border border-primary/40 bg-primary/10 text-primary", children: _jsx(Package, { className: "h-4 w-4" }) }), _jsx("span", { className: "font-medium", children: "Voorraad" }), _jsxs("span", { className: "text-xs text-muted-foreground", children: ["Beschikbaar: ", _jsx("span", { className: "tabular-nums font-medium text-foreground", children: available })] })] }), _jsx(CardContent, { className: "pt-0 space-y-4", children: loading ? (_jsxs("div", { className: "flex items-center gap-2 text-sm text-muted-foreground", children: [_jsx(Loader2, { className: "h-4 w-4 animate-spin" }), " Voorraad laden\u2026"] })) : (_jsxs(_Fragment, { children: [_jsxs("div", { className: "grid grid-cols-2 gap-4 sm:grid-cols-4", children: [numField("physical_stock", "Liggend"), numField("minimum_stock", "Minimum"), numField("reserved_stock", "Gereserveerd"), numField("incoming_stock", "Inkomend")] }), _jsxs("div", { className: "space-y-1", children: [_jsx(Label, { htmlFor: "stock-notes", children: "Notitie (optioneel)" }), _jsx(Textarea, { id: "stock-notes", rows: 2, value: values.notes ?? "", onChange: (e) => setValues((v) => ({ ...v, notes: e.target.value })), placeholder: "Bijv. locatie of opmerking" })] }), _jsx("div", { className: "flex justify-end", children: _jsxs(Button, { onClick: handleSave, disabled: saving, children: [saving ? (_jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" })) : (_jsx(Check, { className: "mr-2 h-4 w-4" })), "Voorraad opslaan"] }) })] })) })] }), _jsx(FloridaySentCard, { connectionId: connectionId, productId: productId, tradeItemId: tradeItemId, productType: isGroupHead ? "GROUP" : hasBom ? "SET" : null })] }));
|
|
136
109
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VoorraadTable.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/VoorraadTable.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAI3D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;AAkBzE,oFAAoF;AACpF,wBAAgB,YAAY,CAAC,IAAI,EAAE,SAAS,GAAG,MAAM,GAAG,IAAI,CAE3D;AAED,wBAAgB,aAAa,CAAC,EAC5B,KAAK,EACL,OAAO,EACP,aAAa,EACb,QAAQ,EACR,cAAc,EACd,WAAW,EACX,cAAc,GACf,EAAE;IACD,KAAK,EAAE,SAAS,EAAE,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;IACjB,aAAa,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;IAC9C,QAAQ,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACvB,cAAc,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7C,WAAW,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IAC7C,cAAc,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;CAC9C,+
|
|
1
|
+
{"version":3,"file":"VoorraadTable.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/VoorraadTable.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAI3D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;AAkBzE,oFAAoF;AACpF,wBAAgB,YAAY,CAAC,IAAI,EAAE,SAAS,GAAG,MAAM,GAAG,IAAI,CAE3D;AAED,wBAAgB,aAAa,CAAC,EAC5B,KAAK,EACL,OAAO,EACP,aAAa,EACb,QAAQ,EACR,cAAc,EACd,WAAW,EACX,cAAc,GACf,EAAE;IACD,KAAK,EAAE,SAAS,EAAE,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;IACjB,aAAa,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;IAC9C,QAAQ,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACvB,cAAc,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7C,WAAW,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IAC7C,cAAc,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;CAC9C,+BA0OA"}
|
|
@@ -63,7 +63,7 @@ export function VoorraadTable({ items, loading, settingsByKey, selected, onToggl
|
|
|
63
63
|
: "Niet online in Floriday", className: "inline-flex items-center gap-1.5 rounded-full border border-border bg-background px-2 py-0.5 text-[11px] font-medium leading-none text-muted-foreground", children: [_jsx("span", { className: "h-1.5 w-1.5 rounded-full " +
|
|
64
64
|
(floriday ? "bg-emerald-500" : "bg-red-500"), "aria-hidden": true }), _jsx("span", { children: "Floriday" })] })] }), Boolean(customValue(item, "floriday_sync")?.dirty) && (_jsx("div", { className: "mt-1 inline-flex items-center rounded-full bg-amber-100 px-2 py-0.5 text-[11px] font-medium leading-none text-amber-800", title: "Opgeslagen in dit systeem, wacht nog op doorgifte naar Floriday", children: "Wacht op Floriday" })), low && (_jsx("div", { className: "text-xs text-destructive", children: "onder minimumvoorraad" }))] }), _jsx("td", { className: "p-3", onClick: (e) => e.stopPropagation(), children: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(InventoryStatusBadge, { setting: setting }), settingProductId && onOpenSettings && (_jsx(Button, { variant: "ghost", size: "sm", className: "h-7 w-7 p-0", onClick: () => onOpenSettings(settingProductId), "aria-label": `Voorraadsturing van ${item.name}`, children: _jsx(Settings2, { className: "h-4 w-4" }) }))] }) }), _jsx("td", { className: "p-3", children: (() => {
|
|
65
65
|
const t = toStructureType(setting?.product_type, item.itemType);
|
|
66
|
-
return (_jsxs("div", { className: "flex flex-col items-start gap-1", children: [_jsx(Badge, { variant: t === "NORMAL" ? "secondary" : undefined, className: PRODUCT_TYPE_CLASSES[t], title: PRODUCT_TYPE_HELP[t], children: PRODUCT_TYPE_LABELS[t] }), setting?.action_reason === "GROUP_MEMBER_OFF" && (_jsx("span", { className: "text-[11px] text-muted-foreground", title: "Dit artikel is lid van een actieve productgroep; alleen de groep staat in Floriday.", children: "Lid van groep \u00B7 niet los in Floriday" })), setting?.action_reason === "SET_AWAITS_LOGISTICS" && (_jsx("span", { className: "text-[11px] text-amber-700", title: "
|
|
66
|
+
return (_jsxs("div", { className: "flex flex-col items-start gap-1", children: [_jsx(Badge, { variant: t === "NORMAL" ? "secondary" : undefined, className: PRODUCT_TYPE_CLASSES[t], title: PRODUCT_TYPE_HELP[t], children: PRODUCT_TYPE_LABELS[t] }), setting?.action_reason === "GROUP_MEMBER_OFF" && (_jsx("span", { className: "text-[11px] text-muted-foreground", title: "Dit artikel is lid van een actieve productgroep; alleen de groep staat in Floriday.", children: "Lid van groep \u00B7 niet los in Floriday" })), setting?.action_reason === "SET_AWAITS_LOGISTICS" && (item.floridayId ? (_jsx("span", { className: "text-[11px] text-amber-700", title: "Set: dit artikel staat in Floriday. De voorraad (berekend uit de onderdelen) gaat pas naar Floriday na akkoord, als de Logistieke engine onderdelen reserveert.", children: "In Floriday \u00B7 voorraad nog niet doorgezet (set)" })) : (_jsx("span", { className: "text-[11px] text-amber-700", title: "Berekend uit de onderdelen; nog niet gekoppeld aan een Floriday-artikel.", children: "Berekend \u00B7 nog niet gekoppeld aan Floriday" })))] }));
|
|
67
67
|
})() }), _jsx("td", { className: "p-3 font-mono text-xs", children: item.articleNumber || "—" }), columns.map((c) => (_jsx("td", { className: "p-3 text-right tabular-nums text-muted-foreground", children: Number(item[c.key] ?? 0) }, c.key))), extraColumns.map((f) => (_jsx("td", { className: "p-3 text-muted-foreground whitespace-nowrap", children: formatCustomValue(customValue(item, f.field_key)) }, f.id)))] }, item.id));
|
|
68
68
|
}) })] }) }));
|
|
69
69
|
}
|