@flowselections/floriday-voorraad 1.0.38 → 1.0.40
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist-lib/components/voorraad/ArtikelWizard.d.ts.map +1 -1
- package/dist-lib/components/voorraad/ArtikelWizard.js +34 -3
- package/dist-lib/components/voorraad/FustenPage.d.ts.map +1 -1
- package/dist-lib/components/voorraad/FustenPage.js +7 -1
- package/dist-lib/components/voorraad/InventorySettingsDrawer.d.ts +10 -0
- package/dist-lib/components/voorraad/InventorySettingsDrawer.d.ts.map +1 -0
- package/dist-lib/components/voorraad/InventorySettingsDrawer.js +99 -0
- package/dist-lib/components/voorraad/InventoryStatusBadge.d.ts +6 -0
- package/dist-lib/components/voorraad/InventoryStatusBadge.d.ts.map +1 -0
- package/dist-lib/components/voorraad/InventoryStatusBadge.js +43 -0
- package/dist-lib/components/voorraad/StockHistoryChart.d.ts +4 -0
- package/dist-lib/components/voorraad/StockHistoryChart.d.ts.map +1 -0
- package/dist-lib/components/voorraad/StockHistoryChart.js +53 -0
- package/dist-lib/components/voorraad/VoorraadPage.d.ts.map +1 -1
- package/dist-lib/components/voorraad/VoorraadPage.js +29 -1
- package/dist-lib/components/voorraad/VoorraadTable.d.ts +9 -1
- package/dist-lib/components/voorraad/VoorraadTable.d.ts.map +1 -1
- package/dist-lib/components/voorraad/VoorraadTable.js +17 -6
- package/dist-lib/hooks/useInventorySettings.d.ts +29 -0
- package/dist-lib/hooks/useInventorySettings.d.ts.map +1 -0
- package/dist-lib/hooks/useInventorySettings.js +62 -0
- package/dist-lib/hooks/useTradeItemStock.d.ts.map +1 -1
- package/dist-lib/hooks/useTradeItemStock.js +1 -0
- package/dist-lib/integrations/supabase/auth-middleware.d.ts +3833 -1196
- package/dist-lib/integrations/supabase/auth-middleware.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/client.d.ts +3833 -1196
- package/dist-lib/integrations/supabase/client.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/client.server.d.ts +3833 -1196
- package/dist-lib/integrations/supabase/client.server.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/types.d.ts +3987 -1324
- package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/types.js +1 -0
- package/dist-lib/lib/floricode-required-features.functions.d.ts +3833 -1196
- package/dist-lib/lib/floricode-required-features.functions.d.ts.map +1 -1
- package/dist-lib/lib/floriday-2026v1.server.d.ts +77 -0
- package/dist-lib/lib/floriday-2026v1.server.d.ts.map +1 -0
- package/dist-lib/lib/floriday-2026v1.server.js +178 -0
- package/dist-lib/lib/floriday-customer-offer.functions.d.ts +3833 -1196
- package/dist-lib/lib/floriday-customer-offer.functions.d.ts.map +1 -1
- package/dist-lib/lib/floriday-env.functions.d.ts +6593 -1319
- package/dist-lib/lib/floriday-env.functions.d.ts.map +1 -1
- package/dist-lib/lib/floriday-gateway.functions.d.ts +19621 -3799
- package/dist-lib/lib/floriday-gateway.functions.d.ts.map +1 -1
- package/dist-lib/lib/floriday-stock-supply.server.d.ts +18 -7
- package/dist-lib/lib/floriday-stock-supply.server.d.ts.map +1 -1
- package/dist-lib/lib/floriday-stock-supply.server.js +70 -68
- package/dist-lib/lib/floriday-vbn-required.functions.d.ts +6593 -1319
- package/dist-lib/lib/floriday-vbn-required.functions.d.ts.map +1 -1
- package/dist-lib/lib/floriday-warehouse.functions.d.ts +9850 -1939
- package/dist-lib/lib/floriday-warehouse.functions.d.ts.map +1 -1
- package/dist-lib/lib/floriday-writes.functions.d.ts +6593 -1319
- package/dist-lib/lib/floriday-writes.functions.d.ts.map +1 -1
- package/dist-lib/lib/fusten.d.ts +1 -0
- package/dist-lib/lib/fusten.d.ts.map +1 -1
- package/dist-lib/lib/fusten.js +1 -1
- package/dist-lib/lib/inventory-settings.functions.d.ts +51709 -0
- package/dist-lib/lib/inventory-settings.functions.d.ts.map +1 -0
- package/dist-lib/lib/inventory-settings.functions.js +97 -0
- package/dist-lib/lib/module-health.functions.d.ts +6593 -1319
- package/dist-lib/lib/module-health.functions.d.ts.map +1 -1
- package/dist-lib/lib/stock-sync.functions.d.ts +6593 -1319
- package/dist-lib/lib/stock-sync.functions.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ArtikelWizard.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/ArtikelWizard.tsx"],"names":[],"mappings":"AAqMA,wBAAgB,aAAa,CAAC,EAC5B,OAAO,EAAE,cAAc,EACvB,WAAW,EACX,SAAS,EACT,WAAW,GACZ,GAAE;IACD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;CACjB,
|
|
1
|
+
{"version":3,"file":"ArtikelWizard.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/ArtikelWizard.tsx"],"names":[],"mappings":"AAqMA,wBAAgB,aAAa,CAAC,EAC5B,OAAO,EAAE,cAAc,EACvB,WAAW,EACX,SAAS,EACT,WAAW,GACZ,GAAE;IACD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;CACjB,2CAs9DL"}
|
|
@@ -959,10 +959,41 @@ export function ArtikelWizard({ draftId: initialDraftId, tradeItemId, productId,
|
|
|
959
959
|
});
|
|
960
960
|
return;
|
|
961
961
|
}
|
|
962
|
+
// Zijn de productregels (schema) nog niet binnen? Dan publiceren we niet,
|
|
963
|
+
// maar bewaren we de wijziging ALTIJD lokaal en markeren we hem als
|
|
964
|
+
// "nog niet doorgegeven aan Floriday".
|
|
962
965
|
if (wantsPublish && !schemaQuery.isSuccess) {
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
+
setSubmitting(true);
|
|
967
|
+
try {
|
|
968
|
+
const localId = await upsertLocalTradeItem({
|
|
969
|
+
id: draftId,
|
|
970
|
+
connectionId: activeId,
|
|
971
|
+
data,
|
|
972
|
+
floridayTradeItemId: (isEditingPublished && tradeItemId) ? tradeItemId : (draftFloridayId || null),
|
|
973
|
+
sync: {
|
|
974
|
+
dirty: true,
|
|
975
|
+
last_attempt_at: new Date().toISOString(),
|
|
976
|
+
last_error: schemaQuery.error instanceof Error
|
|
977
|
+
? schemaQuery.error.message
|
|
978
|
+
: "De productregels van Floriday konden nog niet worden opgehaald.",
|
|
979
|
+
},
|
|
980
|
+
});
|
|
981
|
+
if (localId && localId !== draftId)
|
|
982
|
+
setDraftId(localId);
|
|
983
|
+
setPendingSync({
|
|
984
|
+
error: "De productregels van Floriday konden nog niet worden opgehaald.",
|
|
985
|
+
at: new Date().toISOString(),
|
|
986
|
+
});
|
|
987
|
+
toast.success("Wijziging opgeslagen", {
|
|
988
|
+
description: "Nog niet doorgegeven aan Floriday — je kunt het straks opnieuw proberen.",
|
|
989
|
+
});
|
|
990
|
+
}
|
|
991
|
+
catch (e) {
|
|
992
|
+
toast.error(`Opslaan mislukt: ${e.message}`);
|
|
993
|
+
}
|
|
994
|
+
finally {
|
|
995
|
+
setSubmitting(false);
|
|
996
|
+
}
|
|
966
997
|
return;
|
|
967
998
|
}
|
|
968
999
|
// Strikte controle van alle stappen voor verzending.
|
|
@@ -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":"AAiKA,wBAAgB,UAAU,4CAkMzB"}
|
|
@@ -53,7 +53,13 @@ function MutationHistory({ fust }) {
|
|
|
53
53
|
}
|
|
54
54
|
};
|
|
55
55
|
return (_jsxs("div", { children: [_jsx("button", { type: "button", onClick: toggle, className: "text-xs text-primary hover:underline", children: open ? "Historie verbergen" : "Historie" }), open && (_jsxs("div", { className: "mt-2 rounded-md border border-border bg-muted/30 p-2 text-xs", children: [rows === null && _jsx("p", { className: "text-muted-foreground", children: "Laden\u2026" }), rows !== null && rows.length === 0 && (_jsx("p", { className: "text-muted-foreground", children: "Nog geen mutaties." })), rows !== null &&
|
|
56
|
-
rows.map((m) => (_jsxs("div", { className: "flex items-center justify-between gap-3 py-0.5", children: [_jsx("span", { className: "text-muted-foreground", children: new Date(m.created_at).toLocaleString("nl-NL") }), _jsxs("span", { className: "flex-1 truncate", children: [m.reason === "sale"
|
|
56
|
+
rows.map((m) => (_jsxs("div", { className: "flex items-center justify-between gap-3 py-0.5", children: [_jsx("span", { className: "text-muted-foreground", children: new Date(m.created_at).toLocaleString("nl-NL") }), _jsxs("span", { className: "flex-1 truncate", children: [m.reason === "sale"
|
|
57
|
+
? "Verkoop"
|
|
58
|
+
: m.reason === "sale_cancelled"
|
|
59
|
+
? "Order geannuleerd"
|
|
60
|
+
: m.reason === "manual_sale"
|
|
61
|
+
? "Handmatige verkoop"
|
|
62
|
+
: "Handmatig", m.note ? ` — ${m.note}` : ""] }), _jsx("span", { className: `tabular-nums font-medium ${m.delta < 0 ? "text-rose-600" : "text-emerald-600"}`, children: m.delta > 0 ? `+${m.delta}` : m.delta })] }, m.id)))] }))] }));
|
|
57
63
|
}
|
|
58
64
|
export function FustenPage() {
|
|
59
65
|
const { activeId } = useFloridayConnection();
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { InventorySetting } from "../../hooks/useInventorySettings";
|
|
2
|
+
export declare function InventorySettingsDrawer({ open, onOpenChange, connectionId, productIds, current, onSaved, }: {
|
|
3
|
+
open: boolean;
|
|
4
|
+
onOpenChange: (v: boolean) => void;
|
|
5
|
+
connectionId: string | null;
|
|
6
|
+
productIds: string[];
|
|
7
|
+
current?: InventorySetting | undefined;
|
|
8
|
+
onSaved?: () => void;
|
|
9
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
10
|
+
//# sourceMappingURL=InventorySettingsDrawer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"InventorySettingsDrawer.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/InventorySettingsDrawer.tsx"],"names":[],"mappings":"AA2BA,OAAO,KAAK,EAAE,gBAAgB,EAAa,MAAM,kCAAkC,CAAC;AAWpF,wBAAgB,uBAAuB,CAAC,EACtC,IAAI,EACJ,YAAY,EACZ,YAAY,EACZ,UAAU,EACV,OAAO,EACP,OAAO,GACR,EAAE;IACD,IAAI,EAAE,OAAO,CAAC;IACd,YAAY,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IACnC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,OAAO,CAAC,EAAE,gBAAgB,GAAG,SAAS,CAAC;IACvC,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;CACtB,2CA0LA"}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* Instellingen voor de Floriday-voorraadsturing van één of meerdere producten.
|
|
4
|
+
* Bij meerdere producten (bulk) worden alleen de ingevulde velden toegepast.
|
|
5
|
+
*/
|
|
6
|
+
import { useEffect, useState } from "react";
|
|
7
|
+
import { Button, Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, Input, Label, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@flowselections/core";
|
|
8
|
+
import { useServerFn } from "@tanstack/react-start";
|
|
9
|
+
import { toast } from "sonner";
|
|
10
|
+
import { saveInventorySettings, republishInventory, } from "../../lib/inventory-settings.functions";
|
|
11
|
+
import { MODE_LABELS } from "./InventoryStatusBadge";
|
|
12
|
+
const MODE_HELP = {
|
|
13
|
+
FOLLOW_ERP: "Publiceert de beschikbare voorraad uit dit systeem, minus de buffer.",
|
|
14
|
+
MAX: "Volgt het ERP, maar publiceert nooit meer dan het opgegeven maximum.",
|
|
15
|
+
FIXED: "Publiceert altijd hetzelfde aantal, ongeacht de ERP-voorraad.",
|
|
16
|
+
MANUAL: "Publiceert het aantal dat je hier zelf invult; het ERP wordt genegeerd.",
|
|
17
|
+
DISABLED: "Er wordt niets meer naar Floriday gestuurd voor dit artikel.",
|
|
18
|
+
};
|
|
19
|
+
export function InventorySettingsDrawer({ open, onOpenChange, connectionId, productIds, current, onSaved, }) {
|
|
20
|
+
const save = useServerFn(saveInventorySettings);
|
|
21
|
+
const republish = useServerFn(republishInventory);
|
|
22
|
+
const bulk = productIds.length > 1;
|
|
23
|
+
const [mode, setMode] = useState("");
|
|
24
|
+
const [buffer, setBuffer] = useState("");
|
|
25
|
+
const [maximum, setMaximum] = useState("");
|
|
26
|
+
const [fixed, setFixed] = useState("");
|
|
27
|
+
const [manual, setManual] = useState("");
|
|
28
|
+
const [busy, setBusy] = useState(false);
|
|
29
|
+
useEffect(() => {
|
|
30
|
+
if (!open)
|
|
31
|
+
return;
|
|
32
|
+
if (bulk || !current) {
|
|
33
|
+
setMode("");
|
|
34
|
+
setBuffer("");
|
|
35
|
+
setMaximum("");
|
|
36
|
+
setFixed("");
|
|
37
|
+
setManual("");
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
setMode((current.stock_mode ?? "FOLLOW_ERP"));
|
|
41
|
+
setBuffer(String(current.buffer_quantity ?? 0));
|
|
42
|
+
setMaximum(current.maximum_quantity === null ? "" : String(current.maximum_quantity));
|
|
43
|
+
setFixed(current.fixed_quantity === null ? "" : String(current.fixed_quantity));
|
|
44
|
+
setManual(current.manual_quantity === null ? "" : String(current.manual_quantity));
|
|
45
|
+
}, [open, bulk, current]);
|
|
46
|
+
const num = (v) => {
|
|
47
|
+
if (v.trim() === "")
|
|
48
|
+
return undefined;
|
|
49
|
+
const n = Number(v);
|
|
50
|
+
return Number.isFinite(n) && n >= 0 ? Math.floor(n) : undefined;
|
|
51
|
+
};
|
|
52
|
+
const onSave = async () => {
|
|
53
|
+
if (!connectionId || productIds.length === 0)
|
|
54
|
+
return;
|
|
55
|
+
setBusy(true);
|
|
56
|
+
try {
|
|
57
|
+
const res = await save({
|
|
58
|
+
data: {
|
|
59
|
+
connectionId,
|
|
60
|
+
productIds,
|
|
61
|
+
...(mode ? { stockMode: mode } : {}),
|
|
62
|
+
...(num(buffer) !== undefined ? { bufferQuantity: num(buffer) } : {}),
|
|
63
|
+
...(maximum.trim() !== "" ? { maximumQuantity: num(maximum) ?? null } : {}),
|
|
64
|
+
...(fixed.trim() !== "" ? { fixedQuantity: num(fixed) ?? null } : {}),
|
|
65
|
+
...(manual.trim() !== "" ? { manualQuantity: num(manual) ?? null } : {}),
|
|
66
|
+
},
|
|
67
|
+
});
|
|
68
|
+
toast.success(`Voorraadsturing bijgewerkt voor ${res.updated} artikel(en).`);
|
|
69
|
+
onSaved?.();
|
|
70
|
+
onOpenChange(false);
|
|
71
|
+
}
|
|
72
|
+
catch (e) {
|
|
73
|
+
toast.error(e.message);
|
|
74
|
+
}
|
|
75
|
+
finally {
|
|
76
|
+
setBusy(false);
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
const onRepublish = async () => {
|
|
80
|
+
if (!connectionId || productIds.length === 0)
|
|
81
|
+
return;
|
|
82
|
+
setBusy(true);
|
|
83
|
+
try {
|
|
84
|
+
const res = await republish({ data: { connectionId, productIds } });
|
|
85
|
+
toast.success(`${res.queued} artikel(en) opnieuw ingepland voor Floriday.`);
|
|
86
|
+
onSaved?.();
|
|
87
|
+
}
|
|
88
|
+
catch (e) {
|
|
89
|
+
toast.error(e.message);
|
|
90
|
+
}
|
|
91
|
+
finally {
|
|
92
|
+
setBusy(false);
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
const activeMode = (mode || current?.stock_mode || "");
|
|
96
|
+
return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { className: "sm:max-w-lg", children: [_jsxs(DialogHeader, { children: [_jsx(DialogTitle, { children: "Voorraadsturing Floriday" }), _jsx(DialogDescription, { children: bulk
|
|
97
|
+
? `${productIds.length} artikelen. Alleen de velden die je invult worden aangepast.`
|
|
98
|
+
: "Bepaal welk aantal er naar Floriday wordt gepubliceerd." })] }), _jsxs("div", { className: "space-y-4", children: [_jsxs("div", { className: "space-y-1.5", children: [_jsx(Label, { children: "Modus" }), _jsxs(Select, { value: mode, onValueChange: (v) => setMode(v), children: [_jsx(SelectTrigger, { children: _jsx(SelectValue, { placeholder: bulk ? "Niet wijzigen" : "Kies een modus" }) }), _jsx(SelectContent, { children: Object.keys(MODE_LABELS).map((m) => (_jsx(SelectItem, { value: m, children: MODE_LABELS[m] }, m))) })] }), activeMode && (_jsx("p", { className: "text-xs text-muted-foreground", children: MODE_HELP[activeMode] }))] }), _jsxs("div", { className: "grid grid-cols-2 gap-3", children: [_jsxs("div", { className: "space-y-1.5", children: [_jsx(Label, { children: "Buffer" }), _jsx(Input, { inputMode: "numeric", value: buffer, onChange: (e) => setBuffer(e.target.value), placeholder: bulk ? "Niet wijzigen" : "0" }), _jsx("p", { className: "text-xs text-muted-foreground", children: "Aantal dat je achterhoudt en dus niet aanbiedt." })] }), _jsxs("div", { className: "space-y-1.5", children: [_jsx(Label, { children: "Maximum" }), _jsx(Input, { inputMode: "numeric", value: maximum, onChange: (e) => setMaximum(e.target.value), placeholder: bulk ? "Niet wijzigen" : "Geen", disabled: activeMode !== "" && activeMode !== "MAX" && !bulk })] }), _jsxs("div", { className: "space-y-1.5", children: [_jsx(Label, { children: "Vast aantal" }), _jsx(Input, { inputMode: "numeric", value: fixed, onChange: (e) => setFixed(e.target.value), placeholder: bulk ? "Niet wijzigen" : "Geen", disabled: activeMode !== "" && activeMode !== "FIXED" && !bulk })] }), _jsxs("div", { className: "space-y-1.5", children: [_jsx(Label, { children: "Handmatig aantal" }), _jsx(Input, { inputMode: "numeric", value: manual, onChange: (e) => setManual(e.target.value), placeholder: bulk ? "Niet wijzigen" : "Geen", disabled: activeMode !== "" && activeMode !== "MANUAL" && !bulk })] })] }), !bulk && current && (_jsxs("div", { className: "rounded-md border bg-muted/40 p-3 text-xs text-muted-foreground space-y-1", children: [_jsxs("div", { children: ["Gewenst: ", _jsx("strong", { className: "text-foreground", children: current.desired_quantity }), " ", "\u00B7 In Floriday:", " ", _jsx("strong", { className: "text-foreground", children: current.remote_quantity ?? "onbekend" })] }), current.last_published_at && (_jsxs("div", { children: ["Laatst verstuurd:", " ", new Date(current.last_published_at).toLocaleString("nl-NL")] })), current.last_sync_error && (_jsxs("div", { className: "text-destructive", children: ["Laatste fout: ", current.last_sync_error] }))] }))] }), _jsxs(DialogFooter, { className: "gap-2", children: [_jsx(Button, { variant: "outline", onClick: onRepublish, disabled: busy, children: "Opnieuw versturen" }), _jsx(Button, { onClick: onSave, disabled: busy, children: busy ? "Opslaan..." : "Opslaan" })] })] }) }));
|
|
99
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { InventorySetting, StockMode } from "../../hooks/useInventorySettings";
|
|
2
|
+
export declare const MODE_LABELS: Record<StockMode, string>;
|
|
3
|
+
export declare function InventoryStatusBadge({ setting }: {
|
|
4
|
+
setting: InventorySetting | undefined;
|
|
5
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
6
|
+
//# sourceMappingURL=InventoryStatusBadge.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"InventoryStatusBadge.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/InventoryStatusBadge.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,SAAS,EAAE,MAAM,kCAAkC,CAAC;AAEpF,eAAO,MAAM,WAAW,EAAE,MAAM,CAAC,SAAS,EAAE,MAAM,CAMjD,CAAC;AA4BF,wBAAgB,oBAAoB,CAAC,EAAE,OAAO,EAAE,EAAE;IAAE,OAAO,EAAE,gBAAgB,GAAG,SAAS,CAAA;CAAE,2CAyB1F"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Badge } from "@flowselections/core";
|
|
3
|
+
export const MODE_LABELS = {
|
|
4
|
+
FOLLOW_ERP: "Volgt ERP",
|
|
5
|
+
MAX: "Max",
|
|
6
|
+
FIXED: "Vast",
|
|
7
|
+
MANUAL: "Handmatig",
|
|
8
|
+
DISABLED: "Uit",
|
|
9
|
+
};
|
|
10
|
+
const MODE_CLASSES = {
|
|
11
|
+
FOLLOW_ERP: "bg-primary/10 text-primary border-primary/30",
|
|
12
|
+
MAX: "bg-sky-500/15 text-sky-700 border-sky-500/30",
|
|
13
|
+
FIXED: "bg-indigo-500/15 text-indigo-700 border-indigo-500/30",
|
|
14
|
+
MANUAL: "bg-amber-500/15 text-amber-800 border-amber-500/30",
|
|
15
|
+
DISABLED: "bg-muted text-muted-foreground border-border",
|
|
16
|
+
};
|
|
17
|
+
/** Bolletje: klopt de stand in Floriday met wat wij willen? */
|
|
18
|
+
function syncDot(status) {
|
|
19
|
+
switch (status) {
|
|
20
|
+
case "IN_SYNC":
|
|
21
|
+
return { cls: "bg-emerald-500", title: "Gelijk aan Floriday" };
|
|
22
|
+
case "PENDING":
|
|
23
|
+
return { cls: "bg-amber-500", title: "Wordt doorgezet naar Floriday" };
|
|
24
|
+
case "DRIFT":
|
|
25
|
+
return { cls: "bg-orange-500", title: "Wijkt af van Floriday" };
|
|
26
|
+
case "ERROR":
|
|
27
|
+
return { cls: "bg-destructive", title: "Doorzetten mislukt" };
|
|
28
|
+
case "DISABLED":
|
|
29
|
+
return { cls: "bg-muted-foreground/40", title: "Sturing staat uit" };
|
|
30
|
+
default:
|
|
31
|
+
return { cls: "bg-muted-foreground/40", title: "Nog niet gecontroleerd" };
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
export function InventoryStatusBadge({ setting }) {
|
|
35
|
+
if (!setting) {
|
|
36
|
+
return _jsx("span", { className: "text-xs text-muted-foreground", children: "\u2014" });
|
|
37
|
+
}
|
|
38
|
+
const mode = (setting.stock_mode ?? "FOLLOW_ERP");
|
|
39
|
+
const dot = syncDot(setting.sync_status ?? "UNKNOWN");
|
|
40
|
+
return (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Badge, { className: MODE_CLASSES[mode] ?? MODE_CLASSES.FOLLOW_ERP, children: MODE_LABELS[mode] ?? mode }), _jsx("span", { className: `h-2 w-2 shrink-0 rounded-full ${dot.cls}`, title: setting.last_sync_error
|
|
41
|
+
? `${dot.title}: ${setting.last_sync_error}`
|
|
42
|
+
: dot.title, "aria-label": dot.title }), _jsx("span", { className: "tabular-nums text-xs text-muted-foreground", children: setting.desired_quantity ?? 0 })] }));
|
|
43
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"StockHistoryChart.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/StockHistoryChart.tsx"],"names":[],"mappings":"AA0BA,wBAAgB,iBAAiB,CAAC,EAAE,YAAY,EAAE,EAAE;IAAE,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,2CAoGlF"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* Historie van de voorraadsturing: wat had het ERP beschikbaar, wat wilden we
|
|
4
|
+
* in Floriday hebben, en wat stond er werkelijk. De punten komen uit
|
|
5
|
+
* `inventory_stock_snapshots` en worden door de database gecompacteerd.
|
|
6
|
+
*/
|
|
7
|
+
import { useEffect, useState } from "react";
|
|
8
|
+
import { Card } from "@flowselections/core";
|
|
9
|
+
import { CartesianGrid, Line, LineChart, ResponsiveContainer, Tooltip, XAxis, YAxis, } from "recharts";
|
|
10
|
+
import { supabase } from "../../integrations/supabase/client";
|
|
11
|
+
export function StockHistoryChart({ connectionId }) {
|
|
12
|
+
const [points, setPoints] = useState([]);
|
|
13
|
+
const [loading, setLoading] = useState(false);
|
|
14
|
+
useEffect(() => {
|
|
15
|
+
let cancelled = false;
|
|
16
|
+
if (!connectionId) {
|
|
17
|
+
setPoints([]);
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
setLoading(true);
|
|
21
|
+
const since = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000).toISOString();
|
|
22
|
+
supabase
|
|
23
|
+
.from("inventory_stock_snapshots")
|
|
24
|
+
.select("snapshot_at, erp_available_quantity, floriday_desired_quantity, floriday_remote_quantity")
|
|
25
|
+
.eq("connection_id", connectionId)
|
|
26
|
+
.gte("snapshot_at", since)
|
|
27
|
+
.order("snapshot_at", { ascending: true })
|
|
28
|
+
.limit(1000)
|
|
29
|
+
.then(({ data }) => {
|
|
30
|
+
if (cancelled)
|
|
31
|
+
return;
|
|
32
|
+
setPoints((data ?? []).map((r) => ({
|
|
33
|
+
t: r.snapshot_at,
|
|
34
|
+
label: new Date(r.snapshot_at).toLocaleString("nl-NL", {
|
|
35
|
+
day: "2-digit",
|
|
36
|
+
month: "2-digit",
|
|
37
|
+
hour: "2-digit",
|
|
38
|
+
minute: "2-digit",
|
|
39
|
+
}),
|
|
40
|
+
erp: Number(r.erp_available_quantity ?? 0),
|
|
41
|
+
gewenst: Number(r.floriday_desired_quantity ?? 0),
|
|
42
|
+
floriday: r.floriday_remote_quantity === null
|
|
43
|
+
? null
|
|
44
|
+
: Number(r.floriday_remote_quantity),
|
|
45
|
+
})));
|
|
46
|
+
setLoading(false);
|
|
47
|
+
});
|
|
48
|
+
return () => {
|
|
49
|
+
cancelled = true;
|
|
50
|
+
};
|
|
51
|
+
}, [connectionId]);
|
|
52
|
+
return (_jsxs(Card, { className: "p-4", children: [_jsxs("div", { className: "flex items-baseline justify-between", children: [_jsx("h2", { className: "text-sm font-semibold", children: "Voorraadverloop (30 dagen)" }), _jsx("span", { className: "text-xs text-muted-foreground", children: "ERP beschikbaar \u00B7 gewenst in Floriday \u00B7 werkelijk in Floriday" })] }), _jsx("div", { className: "mt-4 h-56", children: loading ? (_jsx("p", { className: "text-sm text-muted-foreground", children: "Historie laden..." })) : points.length === 0 ? (_jsx("p", { className: "text-sm text-muted-foreground", children: "Nog geen historie beschikbaar. De eerste meetpunten verschijnen binnen een uur." })) : (_jsx(ResponsiveContainer, { width: "100%", height: "100%", children: _jsxs(LineChart, { data: points, margin: { top: 4, right: 8, bottom: 0, left: -16 }, children: [_jsx(CartesianGrid, { strokeDasharray: "3 3", className: "stroke-border" }), _jsx(XAxis, { dataKey: "label", tick: { fontSize: 11 }, minTickGap: 32 }), _jsx(YAxis, { tick: { fontSize: 11 }, allowDecimals: false }), _jsx(Tooltip, {}), _jsx(Line, { type: "monotone", dataKey: "erp", name: "ERP beschikbaar", stroke: "hsl(var(--muted-foreground))", dot: false, strokeWidth: 2 }), _jsx(Line, { type: "monotone", dataKey: "gewenst", name: "Gewenst", stroke: "hsl(var(--primary))", dot: false, strokeWidth: 2 }), _jsx(Line, { type: "monotone", dataKey: "floriday", name: "In Floriday", stroke: "hsl(142 71% 40%)", dot: false, strokeWidth: 2, connectNulls: true })] }) })) })] }));
|
|
53
|
+
}
|
|
@@ -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":"AAqGA,wBAAgB,YAAY,4CAiT3B"}
|
|
@@ -11,6 +11,9 @@ import { ExcelExportButton } from "./ExcelExportButton";
|
|
|
11
11
|
import { StockBulkButtons } from "./StockBulkButtons";
|
|
12
12
|
import { VoorraadTable } from "./VoorraadTable";
|
|
13
13
|
import { VoorraadPagination } from "./VoorraadPagination";
|
|
14
|
+
import { StockHistoryChart } from "./StockHistoryChart";
|
|
15
|
+
import { InventorySettingsDrawer } from "./InventorySettingsDrawer";
|
|
16
|
+
import { useInventorySettings } from "../../hooks/useInventorySettings";
|
|
14
17
|
function isLinkedToFloriday(i) {
|
|
15
18
|
return Boolean(i.floridayId) || i.source === "published";
|
|
16
19
|
}
|
|
@@ -37,12 +40,32 @@ function availableOf(i) {
|
|
|
37
40
|
export function VoorraadPage() {
|
|
38
41
|
const { activeId, error: connError } = useFloridayConnection();
|
|
39
42
|
const { items, loading, error, reload } = useVoorraadData(activeId);
|
|
43
|
+
const { byKey: settingsByKey, reload: reloadSettings } = useInventorySettings(activeId);
|
|
40
44
|
const [search, setSearch] = useState("");
|
|
41
45
|
const [stockFilter, setStockFilter] = useState("all");
|
|
42
46
|
const [floridayFilter, setFloridayFilter] = useState("all");
|
|
43
47
|
const [typeFilter, setTypeFilter] = useState("all");
|
|
44
48
|
const [page, setPage] = useState(1);
|
|
45
49
|
const [pageSize, setPageSize] = useState(25);
|
|
50
|
+
const [selected, setSelected] = useState(new Set());
|
|
51
|
+
const [drawerIds, setDrawerIds] = useState([]);
|
|
52
|
+
const [drawerOpen, setDrawerOpen] = useState(false);
|
|
53
|
+
const toggleSelect = (id) => setSelected((cur) => {
|
|
54
|
+
const next = new Set(cur);
|
|
55
|
+
if (next.has(id))
|
|
56
|
+
next.delete(id);
|
|
57
|
+
else
|
|
58
|
+
next.add(id);
|
|
59
|
+
return next;
|
|
60
|
+
});
|
|
61
|
+
const toggleAll = (ids) => setSelected((cur) => {
|
|
62
|
+
const all = ids.length > 0 && ids.every((id) => cur.has(id));
|
|
63
|
+
return all ? new Set() : new Set(ids);
|
|
64
|
+
});
|
|
65
|
+
const openSettings = (ids) => {
|
|
66
|
+
setDrawerIds(ids);
|
|
67
|
+
setDrawerOpen(true);
|
|
68
|
+
};
|
|
46
69
|
const counts = useMemo(() => {
|
|
47
70
|
let inStock = 0;
|
|
48
71
|
let outOfStock = 0;
|
|
@@ -99,5 +122,10 @@ export function VoorraadPage() {
|
|
|
99
122
|
return (_jsxs("button", { type: "button", onClick: () => setStockFilter(t.id), className: `relative py-3 px-3 text-sm font-medium whitespace-nowrap transition-colors ${active
|
|
100
123
|
? "text-foreground"
|
|
101
124
|
: "text-muted-foreground hover:text-foreground"}`, children: [t.label, _jsx("span", { className: `ml-2 text-xs ${active ? "text-primary" : "text-muted-foreground/70"}`, children: t.count }), active && (_jsx("span", { className: "absolute left-2 right-2 -bottom-px h-0.5 bg-primary rounded-full" }))] }, t.id));
|
|
102
|
-
}) }), _jsxs("div", { className: "flex flex-wrap items-center gap-2 p-4 border-b", children: [_jsxs("div", { className: "relative flex-1 min-w-[220px] max-w-md", children: [_jsx(Search, { className: "w-4 h-4 absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground" }), _jsx(Input, { placeholder: "Zoek op product of artikelnummer...", className: "pl-9", value: search, onChange: (e) => setSearch(e.target.value) })] }), _jsxs(Select, { value: floridayFilter, onValueChange: (v) => setFloridayFilter(v), children: [_jsx(SelectTrigger, { className: "h-9 w-[190px]", children: _jsx(SelectValue, { placeholder: "Floriday" }) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "all", children: "Alle (Floriday)" }), _jsx(SelectItem, { value: "linked", children: "Gekoppeld aan Floriday" }), _jsx(SelectItem, { value: "unlinked", children: "Niet gekoppeld" })] })] }), _jsxs(Select, { value: typeFilter, onValueChange: (v) => setTypeFilter(v), children: [_jsx(SelectTrigger, { className: "h-9 w-[170px]", children: _jsx(SelectValue, { placeholder: "Type" }) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "all", children: "Alle types" }), _jsx(SelectItem, { value: "artikel", children: "Artikel" }), _jsx(SelectItem, { value: "productgroep", children: "Productgroep" }), _jsx(SelectItem, { value: "samenstelling", children: "Samenstelling" })] })] }), _jsxs("div", { className: "ml-auto flex flex-wrap items-center gap-2", children: [_jsx(ExcelExportButton, { items: filtered }), _jsx(StockBulkButtons, { items: items, activeConnectionId: activeId, onImported: reload })] })] }), _jsx("div", { className: "overflow-x-auto", children: _jsx(VoorraadTable, { items: paged, loading: loading }) }), _jsx(VoorraadPagination, { total: filtered.length, page: page, pageSize: pageSize, onPageChange: setPage, onPageSizeChange: setPageSize })] })
|
|
125
|
+
}) }), _jsxs("div", { className: "flex flex-wrap items-center gap-2 p-4 border-b", children: [_jsxs("div", { className: "relative flex-1 min-w-[220px] max-w-md", children: [_jsx(Search, { className: "w-4 h-4 absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground" }), _jsx(Input, { placeholder: "Zoek op product of artikelnummer...", className: "pl-9", value: search, onChange: (e) => setSearch(e.target.value) })] }), _jsxs(Select, { value: floridayFilter, onValueChange: (v) => setFloridayFilter(v), children: [_jsx(SelectTrigger, { className: "h-9 w-[190px]", children: _jsx(SelectValue, { placeholder: "Floriday" }) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "all", children: "Alle (Floriday)" }), _jsx(SelectItem, { value: "linked", children: "Gekoppeld aan Floriday" }), _jsx(SelectItem, { value: "unlinked", children: "Niet gekoppeld" })] })] }), _jsxs(Select, { value: typeFilter, onValueChange: (v) => setTypeFilter(v), children: [_jsx(SelectTrigger, { className: "h-9 w-[170px]", children: _jsx(SelectValue, { placeholder: "Type" }) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "all", children: "Alle types" }), _jsx(SelectItem, { value: "artikel", children: "Artikel" }), _jsx(SelectItem, { value: "productgroep", children: "Productgroep" }), _jsx(SelectItem, { value: "samenstelling", children: "Samenstelling" })] })] }), _jsxs("div", { className: "ml-auto flex flex-wrap items-center gap-2", children: [_jsx(ExcelExportButton, { items: filtered }), _jsx(StockBulkButtons, { items: items, activeConnectionId: activeId, onImported: reload })] })] }), selected.size > 0 && (_jsxs("div", { className: "flex flex-wrap items-center gap-3 border-b bg-primary/5 px-4 py-2 text-sm", children: [_jsxs("span", { className: "font-medium", children: [selected.size, " geselecteerd"] }), _jsx(Button, { size: "sm", onClick: () => openSettings([...selected]), children: "Voorraadsturing instellen" }), _jsx(Button, { size: "sm", variant: "ghost", onClick: () => setSelected(new Set()), children: "Selectie wissen" })] })), _jsx("div", { className: "overflow-x-auto", children: _jsx(VoorraadTable, { items: paged, loading: loading, settingsByKey: settingsByKey, selected: selected, onToggleSelect: toggleSelect, onToggleAll: toggleAll, onOpenSettings: (id) => openSettings([id]) }) }), _jsx(VoorraadPagination, { total: filtered.length, page: page, pageSize: pageSize, onPageChange: setPage, onPageSizeChange: setPageSize })] }), _jsx(StockHistoryChart, { connectionId: activeId ?? null }), _jsx(InventorySettingsDrawer, { open: drawerOpen, onOpenChange: setDrawerOpen, connectionId: activeId ?? null, productIds: drawerIds, current: drawerIds.length === 1
|
|
126
|
+
? [...settingsByKey.values()].find((s) => s.product_id === drawerIds[0])
|
|
127
|
+
: undefined, onSaved: () => {
|
|
128
|
+
reloadSettings();
|
|
129
|
+
reload();
|
|
130
|
+
} })] }));
|
|
103
131
|
}
|
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
import type { TradeItem } from "../../lib/floriday-client";
|
|
2
|
-
|
|
2
|
+
import type { InventorySetting } from "../../hooks/useInventorySettings";
|
|
3
|
+
/** Sleutel waarmee een rij aan zijn Floriday-voorraadinstelling wordt gekoppeld. */
|
|
4
|
+
export declare function settingKeyOf(item: TradeItem): string | null;
|
|
5
|
+
export declare function VoorraadTable({ items, loading, settingsByKey, selected, onToggleSelect, onToggleAll, onOpenSettings, }: {
|
|
3
6
|
items: TradeItem[];
|
|
4
7
|
loading: boolean;
|
|
8
|
+
settingsByKey?: Map<string, InventorySetting>;
|
|
9
|
+
selected?: Set<string>;
|
|
10
|
+
onToggleSelect?: (productId: string) => void;
|
|
11
|
+
onToggleAll?: (productIds: string[]) => void;
|
|
12
|
+
onOpenSettings?: (productId: string) => void;
|
|
5
13
|
}): import("react/jsx-runtime").JSX.Element;
|
|
6
14
|
//# sourceMappingURL=VoorraadTable.d.ts.map
|
|
@@ -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;
|
|
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;AAiBzE,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,2CAoNA"}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useNavigate } from "@tanstack/react-router";
|
|
3
|
-
import { Package } from "lucide-react";
|
|
4
|
-
import { Badge } from "@flowselections/core";
|
|
3
|
+
import { Package, Settings2 } from "lucide-react";
|
|
4
|
+
import { Badge, Button, Checkbox } from "@flowselections/core";
|
|
5
5
|
import { useVoorraadCustomFields } from "../../hooks/useVoorraadCustomFields";
|
|
6
6
|
import { formatCustomValue } from "../../lib/voorraad-custom-fields";
|
|
7
|
+
import { InventoryStatusBadge } from "./InventoryStatusBadge";
|
|
7
8
|
const columns = [
|
|
8
9
|
{ key: "quantity", label: "Liggend" },
|
|
9
10
|
{ key: "minQuantity", label: "Minimum" },
|
|
@@ -16,17 +17,25 @@ function customValue(item, key) {
|
|
|
16
17
|
const cf = item.raw?.custom_fields;
|
|
17
18
|
return cf && typeof cf === "object" ? cf[key] : undefined;
|
|
18
19
|
}
|
|
19
|
-
|
|
20
|
+
/** Sleutel waarmee een rij aan zijn Floriday-voorraadinstelling wordt gekoppeld. */
|
|
21
|
+
export function settingKeyOf(item) {
|
|
22
|
+
return item.productId ?? item.floridayId ?? (item.source === "published" ? item.id : null);
|
|
23
|
+
}
|
|
24
|
+
export function VoorraadTable({ items, loading, settingsByKey, selected, onToggleSelect, onToggleAll, onOpenSettings, }) {
|
|
20
25
|
const navigate = useNavigate();
|
|
21
26
|
const { fields } = useVoorraadCustomFields();
|
|
22
27
|
const extraColumns = fields.filter((f) => f.show_in_columns);
|
|
28
|
+
const selectableIds = items
|
|
29
|
+
.map((i) => settingsByKey?.get(settingKeyOf(i) ?? "")?.product_id ?? null)
|
|
30
|
+
.filter((v) => Boolean(v));
|
|
31
|
+
const allSelected = selectableIds.length > 0 && selectableIds.every((id) => selected?.has(id));
|
|
23
32
|
if (loading) {
|
|
24
33
|
return _jsx("p", { className: "text-sm text-muted-foreground p-6", children: "Voorraad laden..." });
|
|
25
34
|
}
|
|
26
35
|
if (items.length === 0) {
|
|
27
36
|
return (_jsx("p", { className: "text-sm text-muted-foreground p-6", children: "Geen producten gevonden voor deze connectie." }));
|
|
28
37
|
}
|
|
29
|
-
return (_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 w-14" }), _jsx("th", { className: "p-3", children: "Product" }), _jsx("th", { className: "p-3", children: "Type" }), _jsx("th", { className: "p-3", children: "Artikelnummer" }), columns.map((c) => (_jsx("th", { className: "p-3 text-right whitespace-nowrap", children: c.label }, c.key))), extraColumns.map((f) => (_jsx("th", { className: "p-3 whitespace-nowrap", children: f.field_label }, f.id)))] }) }), _jsx("tbody", { children: items.map((item) => {
|
|
38
|
+
return (_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: [onToggleSelect && (_jsx("th", { className: "p-3 w-10", children: _jsx(Checkbox, { checked: allSelected, onCheckedChange: () => onToggleAll?.(selectableIds), "aria-label": "Alles selecteren" }) })), _jsx("th", { className: "p-3 w-14" }), _jsx("th", { className: "p-3", children: "Product" }), _jsx("th", { className: "p-3", children: "Floriday-sturing" }), _jsx("th", { className: "p-3", children: "Type" }), _jsx("th", { className: "p-3", children: "Artikelnummer" }), columns.map((c) => (_jsx("th", { className: "p-3 text-right whitespace-nowrap", children: c.label }, c.key))), extraColumns.map((f) => (_jsx("th", { className: "p-3 whitespace-nowrap", children: f.field_label }, f.id)))] }) }), _jsx("tbody", { children: items.map((item) => {
|
|
30
39
|
const low = item.minQuantity > 0 &&
|
|
31
40
|
item.availableQuantity < item.minQuantity;
|
|
32
41
|
const linkId = item.productId ?? item.localId ?? item.id;
|
|
@@ -37,6 +46,8 @@ export function VoorraadTable({ items, loading, }) {
|
|
|
37
46
|
};
|
|
38
47
|
const floriday = item.floriday ?? null;
|
|
39
48
|
const thumbUrl = floriday?.photoUrl || item.imageUrl;
|
|
49
|
+
const setting = settingsByKey?.get(settingKeyOf(item) ?? "");
|
|
50
|
+
const settingProductId = setting?.product_id ?? null;
|
|
40
51
|
return (_jsxs("tr", { className: `border-t hover:bg-muted/30 ${clickable ? "cursor-pointer" : ""}`, onClick: clickable ? open : undefined, role: clickable ? "button" : undefined, tabIndex: clickable ? 0 : undefined, onKeyDown: clickable
|
|
41
52
|
? (e) => {
|
|
42
53
|
if (e.key === "Enter" || e.key === " ") {
|
|
@@ -44,11 +55,11 @@ export function VoorraadTable({ items, loading, }) {
|
|
|
44
55
|
open();
|
|
45
56
|
}
|
|
46
57
|
}
|
|
47
|
-
: undefined, "aria-label": clickable ? `Bewerk ${item.name}` : undefined, children: [_jsx("td", { className: "p-2", children: thumbUrl ? (_jsx("img", { src: thumbUrl, alt: item.name, className: "h-10 w-10 rounded object-cover", loading: "lazy" })) : (_jsx("div", { className: "h-10 w-10 rounded bg-muted flex items-center justify-center", children: _jsx(Package, { className: "h-4 w-4 text-muted-foreground" }) })) }), _jsxs("td", { className: "p-3", children: [_jsxs("div", { className: "flex flex-col items-start gap-1", children: [_jsx("span", { className: "font-medium text-foreground", children: item.name }), _jsxs("div", { role: "status", "aria-label": floriday
|
|
58
|
+
: undefined, "aria-label": clickable ? `Bewerk ${item.name}` : undefined, children: [onToggleSelect && (_jsx("td", { className: "p-3", onClick: (e) => e.stopPropagation(), children: _jsx(Checkbox, { checked: settingProductId ? selected?.has(settingProductId) : false, disabled: !settingProductId, onCheckedChange: () => settingProductId && onToggleSelect(settingProductId), "aria-label": `Selecteer ${item.name}` }) })), _jsx("td", { className: "p-2", children: thumbUrl ? (_jsx("img", { src: thumbUrl, alt: item.name, className: "h-10 w-10 rounded object-cover", loading: "lazy" })) : (_jsx("div", { className: "h-10 w-10 rounded bg-muted flex items-center justify-center", children: _jsx(Package, { className: "h-4 w-4 text-muted-foreground" }) })) }), _jsxs("td", { className: "p-3", children: [_jsxs("div", { className: "flex flex-col items-start gap-1", children: [_jsx("span", { className: "font-medium text-foreground", children: item.name }), _jsxs("div", { role: "status", "aria-label": floriday
|
|
48
59
|
? "Online in Floriday"
|
|
49
60
|
: "Niet online in Floriday", title: floriday
|
|
50
61
|
? "Online in Floriday"
|
|
51
62
|
: "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 " +
|
|
52
|
-
(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", children: item.itemType === "productgroep" ? (_jsx(Badge, { className: "bg-indigo-500/15 text-indigo-700 border-indigo-500/30", children: "Productgroep" })) : item.itemType === "samenstelling" ? (_jsx(Badge, { className: "bg-emerald-500/15 text-emerald-700 border-emerald-500/30", children: "Samenstelling" })) : (_jsx(Badge, { variant: "secondary", children: "Artikel" })) }), _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));
|
|
63
|
+
(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: item.itemType === "productgroep" ? (_jsx(Badge, { className: "bg-indigo-500/15 text-indigo-700 border-indigo-500/30", children: "Productgroep" })) : item.itemType === "samenstelling" ? (_jsx(Badge, { className: "bg-emerald-500/15 text-emerald-700 border-emerald-500/30", children: "Samenstelling" })) : (_jsx(Badge, { variant: "secondary", children: "Artikel" })) }), _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));
|
|
53
64
|
}) })] }) }));
|
|
54
65
|
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export type StockMode = "FOLLOW_ERP" | "MAX" | "FIXED" | "MANUAL" | "DISABLED";
|
|
2
|
+
export type InventorySetting = {
|
|
3
|
+
id: string;
|
|
4
|
+
connection_id: string;
|
|
5
|
+
product_id: string | null;
|
|
6
|
+
floriday_trade_item_id: string | null;
|
|
7
|
+
stock_mode: StockMode;
|
|
8
|
+
buffer_quantity: number;
|
|
9
|
+
maximum_quantity: number | null;
|
|
10
|
+
fixed_quantity: number | null;
|
|
11
|
+
manual_quantity: number | null;
|
|
12
|
+
desired_quantity: number;
|
|
13
|
+
desired_availability: boolean;
|
|
14
|
+
remote_quantity: number | null;
|
|
15
|
+
remote_available: boolean | null;
|
|
16
|
+
remote_checked_at: string | null;
|
|
17
|
+
sync_status: string;
|
|
18
|
+
last_sync_error: string | null;
|
|
19
|
+
last_published_at: string | null;
|
|
20
|
+
last_confirmed_at: string | null;
|
|
21
|
+
};
|
|
22
|
+
export declare function useInventorySettings(connectionId: string | null): {
|
|
23
|
+
rows: InventorySetting[];
|
|
24
|
+
byKey: Map<string, InventorySetting>;
|
|
25
|
+
loading: boolean;
|
|
26
|
+
error: string | null;
|
|
27
|
+
reload: () => Promise<void>;
|
|
28
|
+
};
|
|
29
|
+
//# sourceMappingURL=useInventorySettings.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useInventorySettings.d.ts","sourceRoot":"","sources":["../../src/hooks/useInventorySettings.ts"],"names":[],"mappings":"AAQA,MAAM,MAAM,SAAS,GAAG,YAAY,GAAG,KAAK,GAAG,OAAO,GAAG,QAAQ,GAAG,UAAU,CAAC;AAE/E,MAAM,MAAM,gBAAgB,GAAG;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,sBAAsB,EAAE,MAAM,GAAG,IAAI,CAAC;IACtC,UAAU,EAAE,SAAS,CAAC;IACtB,eAAe,EAAE,MAAM,CAAC;IACxB,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,gBAAgB,EAAE,MAAM,CAAC;IACzB,oBAAoB,EAAE,OAAO,CAAC;IAC9B,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,gBAAgB,EAAE,OAAO,GAAG,IAAI,CAAC;IACjC,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;CAClC,CAAC;AAKF,wBAAgB,oBAAoB,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI;;;;;;EA0D/D"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Leest de Floriday-voorraadsturing per product. De rijen worden door de
|
|
3
|
+
* database zelf aangemaakt en bijgewerkt (`floriday_recompute_inventory`);
|
|
4
|
+
* de UI leest ze alleen en schrijft via server-functies.
|
|
5
|
+
*/
|
|
6
|
+
import { useCallback, useEffect, useState } from "react";
|
|
7
|
+
import { supabase } from "../integrations/supabase/client";
|
|
8
|
+
const SELECT = "id, connection_id, product_id, floriday_trade_item_id, stock_mode, buffer_quantity, maximum_quantity, fixed_quantity, manual_quantity, desired_quantity, desired_availability, remote_quantity, remote_available, remote_checked_at, sync_status, last_sync_error, last_published_at, last_confirmed_at";
|
|
9
|
+
export function useInventorySettings(connectionId) {
|
|
10
|
+
const [rows, setRows] = useState([]);
|
|
11
|
+
const [loading, setLoading] = useState(false);
|
|
12
|
+
const [error, setError] = useState(null);
|
|
13
|
+
const load = useCallback(async () => {
|
|
14
|
+
if (!connectionId) {
|
|
15
|
+
setRows([]);
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
setLoading(true);
|
|
19
|
+
const { data, error: err } = await supabase
|
|
20
|
+
.from("floriday_inventory_settings")
|
|
21
|
+
.select(SELECT)
|
|
22
|
+
.eq("connection_id", connectionId)
|
|
23
|
+
.limit(2000);
|
|
24
|
+
if (err)
|
|
25
|
+
setError(err.message);
|
|
26
|
+
else {
|
|
27
|
+
setError(null);
|
|
28
|
+
setRows((data ?? []));
|
|
29
|
+
}
|
|
30
|
+
setLoading(false);
|
|
31
|
+
}, [connectionId]);
|
|
32
|
+
useEffect(() => {
|
|
33
|
+
load();
|
|
34
|
+
}, [load]);
|
|
35
|
+
useEffect(() => {
|
|
36
|
+
if (!connectionId)
|
|
37
|
+
return;
|
|
38
|
+
const channel = supabase
|
|
39
|
+
.channel(`inventory-settings-${connectionId}`)
|
|
40
|
+
.on("postgres_changes", {
|
|
41
|
+
event: "*",
|
|
42
|
+
schema: "public",
|
|
43
|
+
table: "floriday_inventory_settings",
|
|
44
|
+
filter: `connection_id=eq.${connectionId}`,
|
|
45
|
+
}, () => {
|
|
46
|
+
load();
|
|
47
|
+
})
|
|
48
|
+
.subscribe();
|
|
49
|
+
return () => {
|
|
50
|
+
supabase.removeChannel(channel);
|
|
51
|
+
};
|
|
52
|
+
}, [connectionId, load]);
|
|
53
|
+
/** Zowel op product-id als op Floriday trade-item-id opzoekbaar. */
|
|
54
|
+
const byKey = new Map();
|
|
55
|
+
for (const r of rows) {
|
|
56
|
+
if (r.product_id)
|
|
57
|
+
byKey.set(r.product_id, r);
|
|
58
|
+
if (r.floriday_trade_item_id)
|
|
59
|
+
byKey.set(r.floriday_trade_item_id, r);
|
|
60
|
+
}
|
|
61
|
+
return { rows, byKey, loading, error, reload: load };
|
|
62
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useTradeItemStock.d.ts","sourceRoot":"","sources":["../../src/hooks/useTradeItemStock.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,iBAAiB;IAChC,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,EAAE,MAAM,CAAC;IACvB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAUD,wBAAgB,WAAW,CAAC,MAAM,EAAE;IAClC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B,GAAG,MAAM,GAAG,IAAI,CAEhB;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE;IACnC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B,GAAG,MAAM,EAAE,CAUX;AAGD;;;;;GAKG;AACH,wBAAgB,WAAW,CACzB,GAAG,EAAE,GAAG,CAAC,MAAM,EAAE,iBAAiB,CAAC,EACnC,MAAM,EAAE;IACN,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB,GACA,iBAAiB,GAAG,SAAS,CAa/B;AAGD,wBAAgB,iBAAiB,CAC/B,YAAY,EAAE,MAAM,GAAG,IAAI,EAC3B,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,IAAI;;;mBA4ElB,iBAAiB;
|
|
1
|
+
{"version":3,"file":"useTradeItemStock.d.ts","sourceRoot":"","sources":["../../src/hooks/useTradeItemStock.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,iBAAiB;IAChC,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,EAAE,MAAM,CAAC;IACvB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAUD,wBAAgB,WAAW,CAAC,MAAM,EAAE;IAClC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B,GAAG,MAAM,GAAG,IAAI,CAEhB;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE;IACnC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B,GAAG,MAAM,EAAE,CAUX;AAGD;;;;;GAKG;AACH,wBAAgB,WAAW,CACzB,GAAG,EAAE,GAAG,CAAC,MAAM,EAAE,iBAAiB,CAAC,EACnC,MAAM,EAAE;IACN,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB,GACA,iBAAiB,GAAG,SAAS,CAa/B;AAGD,wBAAgB,iBAAiB,CAC/B,YAAY,EAAE,MAAM,GAAG,IAAI,EAC3B,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,IAAI;;;mBA4ElB,iBAAiB;EAoEnC;AAED,wBAAsB,aAAa,CACjC,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC,CAUzC"}
|