@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.
Files changed (31) hide show
  1. package/dist-lib/components/voorraad/ArtikelWizard.js +1 -1
  2. package/dist-lib/components/voorraad/FloridaySentCard.d.ts +24 -0
  3. package/dist-lib/components/voorraad/FloridaySentCard.d.ts.map +1 -0
  4. package/dist-lib/components/voorraad/FloridaySentCard.js +85 -0
  5. package/dist-lib/components/voorraad/FustenPage.d.ts.map +1 -1
  6. package/dist-lib/components/voorraad/FustenPage.js +18 -4
  7. package/dist-lib/components/voorraad/NieuwArtikelPage.d.ts.map +1 -1
  8. package/dist-lib/components/voorraad/NieuwArtikelPage.js +72 -2
  9. package/dist-lib/components/voorraad/PublicatiesPage.d.ts.map +1 -1
  10. package/dist-lib/components/voorraad/PublicatiesPage.js +11 -3
  11. package/dist-lib/components/voorraad/ValidationOverviewTable.d.ts.map +1 -1
  12. package/dist-lib/components/voorraad/ValidationOverviewTable.js +1 -1
  13. package/dist-lib/components/voorraad/VoorraadPage.d.ts.map +1 -1
  14. package/dist-lib/components/voorraad/VoorraadPage.js +28 -2
  15. package/dist-lib/components/voorraad/VoorraadStockCard.d.ts.map +1 -1
  16. package/dist-lib/components/voorraad/VoorraadStockCard.js +6 -33
  17. package/dist-lib/components/voorraad/VoorraadTable.d.ts.map +1 -1
  18. package/dist-lib/components/voorraad/VoorraadTable.js +1 -1
  19. package/dist-lib/integrations/supabase/types.d.ts +328 -3
  20. package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
  21. package/dist-lib/lib/__tests__/floriday-base-supply.test.d.ts +2 -0
  22. package/dist-lib/lib/__tests__/floriday-base-supply.test.d.ts.map +1 -0
  23. package/dist-lib/lib/__tests__/floriday-base-supply.test.js +58 -0
  24. package/dist-lib/lib/floriday-base-supply.d.ts +53 -0
  25. package/dist-lib/lib/floriday-base-supply.d.ts.map +1 -0
  26. package/dist-lib/lib/floriday-base-supply.js +85 -0
  27. package/dist-lib/lib/floriday-gateway.server.d.ts +21 -0
  28. package/dist-lib/lib/floriday-gateway.server.d.ts.map +1 -1
  29. package/dist-lib/lib/floriday-gateway.server.js +30 -0
  30. package/dist-lib/lib/fusten.js +1 -1
  31. 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":"AAiKA,wBAAgB,UAAU,gCAkMzB"}
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":"AAEA,wBAAgB,gBAAgB,gCAE/B"}
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
- return _jsx(ArtikelWizard, {});
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,gCAuI9B"}
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
- 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"), " \u00B7 referentie ", job.reference_code, job.environment !== "live" ? " · testomgeving" : ""] })] }), _jsxs("div", { className: "flex items-center gap-2", children: [_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
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,+BA2KA"}
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" })) : (_jsx("div", { className: "flex flex-wrap gap-1", children: v.missing.map((m) => (_jsx(Badge, { variant: "outline", className: "text-[10px]", children: m }, m))) })) }), _jsx("td", { className: "p-3", children: _jsxs("div", { className: "flex justify-end gap-1", children: [_jsxs(Button, { size: "sm", variant: "outline", onClick: () => {
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":"AAuGA,wBAAgB,YAAY,gCAuT3B"}
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: "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: "Voorraad" }), _jsx("p", { className: "text-sm text-muted-foreground mt-1", children: "Beheer je artikelen en voorraadniveaus." })] }), _jsxs("div", { className: "flex shrink-0 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, {}), _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) => {
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":"AAcA,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,sCAgPzF"}
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, Factory } from "lucide-react";
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
- const [produceQty, setProduceQty] = useState("");
20
- const [producing, setProducing] = useState(false);
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. Gebruik 'Produceren' om onderdelen af te schrijven, of pas de voorraad van het onderdeel eerst aan.";
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"] }) }), hasBom && (_jsxs("div", { className: "space-y-2 rounded-md border border-border bg-muted/30 p-3", children: [_jsx("p", { className: "text-sm font-medium", children: "Produceren" }), _jsx("p", { className: "text-xs text-muted-foreground", children: "Handmatig aanpassen hierboven is een correctie: onderdelen blijven ongemoeid. Wil je onderdelen w\u00E9l afschrijven, geef dan hier het geproduceerde aantal op." }), _jsxs("div", { className: "flex items-end gap-2", children: [_jsxs("div", { className: "space-y-1", children: [_jsx(Label, { htmlFor: "produce-qty", children: "Aantal" }), _jsx(Input, { id: "produce-qty", type: "number", min: 1, className: "w-32", value: produceQty, onChange: (e) => setProduceQty(e.target.value) })] }), _jsxs(Button, { variant: "outline", onClick: handleProduce, disabled: producing, children: [producing ? (_jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" })) : (_jsx(Factory, { className: "mr-2 h-4 w-4" })), "Produceren"] })] })] }))] })) })] }));
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,+BAiOA"}
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: "Berekend uit de onderdelen; nog niet naar Floriday tot de Logistieke engine onderdelen reserveert.", children: "Berekend \u00B7 nog niet in Floriday" }))] }));
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
  }