@flowselections/floriday-voorraad 1.0.32 → 1.0.34
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 +11 -5
- package/dist-lib/components/voorraad/ModuleHealthCard.d.ts +2 -0
- package/dist-lib/components/voorraad/ModuleHealthCard.d.ts.map +1 -0
- package/dist-lib/components/voorraad/ModuleHealthCard.js +40 -0
- package/dist-lib/components/voorraad/StockBulkButtons.d.ts.map +1 -1
- package/dist-lib/components/voorraad/StockBulkButtons.js +5 -2
- package/dist-lib/components/voorraad/StockPushMonitor.d.ts +4 -0
- package/dist-lib/components/voorraad/StockPushMonitor.d.ts.map +1 -0
- package/dist-lib/components/voorraad/StockPushMonitor.js +110 -0
- package/dist-lib/components/voorraad/VoorraadPage.d.ts.map +1 -1
- package/dist-lib/components/voorraad/VoorraadPage.js +2 -1
- package/dist-lib/components/voorraad/VoorraadStockCard.d.ts.map +1 -1
- package/dist-lib/components/voorraad/VoorraadStockCard.js +10 -5
- package/dist-lib/hooks/useStockPushJobs.d.ts +23 -0
- package/dist-lib/hooks/useStockPushJobs.d.ts.map +1 -0
- package/dist-lib/hooks/useStockPushJobs.js +86 -0
- package/dist-lib/hooks/useTradeItemStock.d.ts +12 -0
- package/dist-lib/hooks/useTradeItemStock.d.ts.map +1 -1
- package/dist-lib/hooks/useTradeItemStock.js +22 -0
- package/dist-lib/hooks/useVoorraadData.d.ts.map +1 -1
- package/dist-lib/hooks/useVoorraadData.js +11 -3
- package/dist-lib/index.d.ts +1 -0
- package/dist-lib/index.d.ts.map +1 -1
- package/dist-lib/index.js +6 -1
- package/dist-lib/integrations/supabase/auth-middleware.d.ts +410 -4
- package/dist-lib/integrations/supabase/auth-middleware.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/client.d.ts +410 -4
- package/dist-lib/integrations/supabase/client.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/client.server.d.ts +410 -4
- package/dist-lib/integrations/supabase/client.server.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/types.d.ts +411 -5
- package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/types.js +12 -1
- package/dist-lib/lib/centralSecrets.server.js +1 -1
- package/dist-lib/lib/floricode-required-features.functions.d.ts +410 -4
- package/dist-lib/lib/floricode-required-features.functions.d.ts.map +1 -1
- package/dist-lib/lib/floriday-customer-offer.functions.d.ts +410 -4
- package/dist-lib/lib/floriday-customer-offer.functions.d.ts.map +1 -1
- package/dist-lib/lib/floriday-env.functions.d.ts +820 -8
- package/dist-lib/lib/floriday-env.functions.d.ts.map +1 -1
- package/dist-lib/lib/floriday-gateway.functions.d.ts +2460 -24
- package/dist-lib/lib/floriday-gateway.functions.d.ts.map +1 -1
- package/dist-lib/lib/floriday-gateway.server.d.ts +14 -0
- package/dist-lib/lib/floriday-gateway.server.d.ts.map +1 -1
- package/dist-lib/lib/floriday-gateway.server.js +62 -10
- package/dist-lib/lib/floriday-vbn-required.functions.d.ts +820 -8
- package/dist-lib/lib/floriday-vbn-required.functions.d.ts.map +1 -1
- package/dist-lib/lib/floriday-warehouse.functions.d.ts +1230 -12
- package/dist-lib/lib/floriday-warehouse.functions.d.ts.map +1 -1
- package/dist-lib/lib/floriday-writes.functions.d.ts +820 -8
- package/dist-lib/lib/floriday-writes.functions.d.ts.map +1 -1
- package/dist-lib/lib/module-health.functions.d.ts +41725 -0
- package/dist-lib/lib/module-health.functions.d.ts.map +1 -0
- package/dist-lib/lib/module-health.functions.js +173 -0
- package/dist-lib/lib/module-runtime.server.d.ts +8 -1
- package/dist-lib/lib/module-runtime.server.d.ts.map +1 -1
- package/dist-lib/lib/module-runtime.server.js +103 -12
- package/dist-lib/lib/stock-sync.functions.d.ts +21302 -31
- package/dist-lib/lib/stock-sync.functions.d.ts.map +1 -1
- package/dist-lib/lib/stock-sync.functions.js +46 -3
- package/package.json +2 -2
|
@@ -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,+BAs7DL"}
|
|
@@ -405,12 +405,18 @@ export function ArtikelWizard({ draftId: initialDraftId, tradeItemId, productId,
|
|
|
405
405
|
// met 0).
|
|
406
406
|
let physical = 0;
|
|
407
407
|
try {
|
|
408
|
-
const
|
|
408
|
+
const stockKeys = [
|
|
409
|
+
product.floriday_trade_item_id,
|
|
410
|
+
product.trade_item_key,
|
|
411
|
+
`product:${product.id}`,
|
|
412
|
+
].filter((k) => Boolean(k));
|
|
413
|
+
const { data: stockRows } = await supabase
|
|
409
414
|
.from("trade_item_stock")
|
|
410
|
-
.select("physical_stock")
|
|
411
|
-
.
|
|
412
|
-
|
|
413
|
-
|
|
415
|
+
.select("trade_item_key,physical_stock")
|
|
416
|
+
.in("trade_item_key", stockKeys);
|
|
417
|
+
const rows = (stockRows ?? []);
|
|
418
|
+
const hit = stockKeys.map((k) => rows.find((r) => r.trade_item_key === k)).find(Boolean) ?? null;
|
|
419
|
+
physical = Number(hit?.physical_stock ?? 0);
|
|
414
420
|
}
|
|
415
421
|
catch { /* stock niet gevonden = 0 */ }
|
|
416
422
|
setData({
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ModuleHealthCard.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/ModuleHealthCard.tsx"],"names":[],"mappings":"AAqBA,wBAAgB,gBAAgB,gCA8D/B"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* Statuskaart in de instellingen: laat in gewone taal zien of de module in
|
|
4
|
+
* déze omgeving volledig werkt, en biedt één knop om vastgelopen
|
|
5
|
+
* voorraadwijzigingen weer op gang te helpen.
|
|
6
|
+
*/
|
|
7
|
+
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
|
8
|
+
import { useServerFn } from "@tanstack/react-start";
|
|
9
|
+
import { AlertTriangle, CheckCircle2, RotateCw, XCircle } from "lucide-react";
|
|
10
|
+
import { toast } from "sonner";
|
|
11
|
+
import { getModuleHealth, recoverStockQueue, } from "../../lib/module-health.functions";
|
|
12
|
+
function StatusIcon({ status }) {
|
|
13
|
+
if (status === "ok")
|
|
14
|
+
return _jsx(CheckCircle2, { className: "mt-0.5 h-4 w-4 shrink-0 text-emerald-600" });
|
|
15
|
+
if (status === "warn")
|
|
16
|
+
return _jsx(AlertTriangle, { className: "mt-0.5 h-4 w-4 shrink-0 text-amber-600" });
|
|
17
|
+
return _jsx(XCircle, { className: "mt-0.5 h-4 w-4 shrink-0 text-destructive" });
|
|
18
|
+
}
|
|
19
|
+
export function ModuleHealthCard() {
|
|
20
|
+
const fetchHealth = useServerFn(getModuleHealth);
|
|
21
|
+
const recover = useServerFn(recoverStockQueue);
|
|
22
|
+
const queryClient = useQueryClient();
|
|
23
|
+
const { data, isLoading } = useQuery({
|
|
24
|
+
queryKey: ["module-health"],
|
|
25
|
+
queryFn: () => fetchHealth({}),
|
|
26
|
+
refetchInterval: 60000,
|
|
27
|
+
});
|
|
28
|
+
const mutation = useMutation({
|
|
29
|
+
mutationFn: () => recover({}),
|
|
30
|
+
onSuccess: (res) => {
|
|
31
|
+
toast.success(res?.restored
|
|
32
|
+
? `${res.restored} voorraadwijziging${res.restored === 1 ? "" : "en"} opnieuw ingepland.`
|
|
33
|
+
: "Er stond niets vast.");
|
|
34
|
+
void queryClient.invalidateQueries({ queryKey: ["module-health"] });
|
|
35
|
+
void queryClient.invalidateQueries({ queryKey: ["stock-sync-health"] });
|
|
36
|
+
},
|
|
37
|
+
onError: (e) => toast.error(e?.message ?? "Herstellen is niet gelukt."),
|
|
38
|
+
});
|
|
39
|
+
return (_jsxs("section", { className: "rounded-lg border border-border bg-card p-4", children: [_jsxs("div", { className: "flex items-start justify-between gap-3", children: [_jsxs("div", { children: [_jsx("h3", { className: "text-base font-semibold text-foreground", children: "Status van de voorraadmodule" }), _jsx("p", { className: "text-sm text-muted-foreground", children: "Controleert de koppeling, de achtergrondtaken en de synchronisatie naar Floriday." })] }), _jsxs("button", { type: "button", className: "inline-flex shrink-0 items-center rounded-md border border-border px-2.5 py-1.5 text-xs font-medium text-foreground hover:bg-muted disabled:opacity-50", disabled: mutation.isPending, onClick: () => mutation.mutate(), children: [_jsx(RotateCw, { className: `mr-1.5 h-3.5 w-3.5 ${mutation.isPending ? "animate-spin" : ""}` }), "Vastgelopen taken hervatten"] })] }), _jsxs("ul", { className: "mt-4 space-y-2.5", children: [isLoading && _jsx("li", { className: "text-sm text-muted-foreground", children: "Bezig met controleren\u2026" }), data?.checks.map((check) => (_jsxs("li", { className: "flex items-start gap-2 text-sm", children: [_jsx(StatusIcon, { status: check.status }), _jsxs("div", { className: "min-w-0", children: [_jsxs("span", { className: "font-medium text-foreground", children: [check.label, ": "] }), _jsx("span", { className: "text-muted-foreground break-words", children: check.detail }), check.hint && (_jsx("p", { className: "mt-0.5 text-xs text-muted-foreground/80 break-words", children: check.hint }))] })] }, check.id)))] })] }));
|
|
40
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"StockBulkButtons.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/StockBulkButtons.tsx"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;
|
|
1
|
+
{"version":3,"file":"StockBulkButtons.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/StockBulkButtons.tsx"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAyF3D,wBAAgB,gBAAgB,CAAC,EAC/B,KAAK,EACL,kBAAkB,EAClB,UAAU,GACX,EAAE;IACD,KAAK,EAAE,SAAS,EAAE,CAAC;IACnB,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,UAAU,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACxC,+BAmoBA"}
|
|
@@ -24,10 +24,13 @@ const COLUMNS = [
|
|
|
24
24
|
"Inkomende voorraad",
|
|
25
25
|
];
|
|
26
26
|
/**
|
|
27
|
-
* Bepaalt de sleutel in `trade_item_stock
|
|
28
|
-
*
|
|
27
|
+
* Bepaalt de canonieke sleutel in `trade_item_stock`: de kale Floriday-UUID
|
|
28
|
+
* zodra het artikel gekoppeld is (die sleutel activeert de push naar Floriday),
|
|
29
|
+
* anders `product:<uuid>`.
|
|
29
30
|
*/
|
|
30
31
|
function toKey(item) {
|
|
32
|
+
if (item.floridayId)
|
|
33
|
+
return item.floridayId;
|
|
31
34
|
if (item.tradeItemKey)
|
|
32
35
|
return item.tradeItemKey;
|
|
33
36
|
if (item.productId)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"StockPushMonitor.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/StockPushMonitor.tsx"],"names":[],"mappings":"AAiFA,wBAAgB,gBAAgB,CAAC,EAAE,YAAY,EAAE,EAAE;IAAE,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,+BAyJjF"}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* Testpaneel voor de voorraadpush naar Floriday.
|
|
4
|
+
*
|
|
5
|
+
* De push zelf gebeurt niet in dit scherm: elke voorraadwijziging schrijft een
|
|
6
|
+
* opdracht in de gedeelde wachtrij, die de Floriday-Authenticatiemodule elke
|
|
7
|
+
* minuut verwerkt. Dit paneel maakt die keten zichtbaar en laat je een testpush
|
|
8
|
+
* inplannen zonder de voorraad te wijzigen.
|
|
9
|
+
*/
|
|
10
|
+
import { useEffect, useMemo, useState } from "react";
|
|
11
|
+
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
|
12
|
+
import { useServerFn } from "@tanstack/react-start";
|
|
13
|
+
import { toast } from "sonner";
|
|
14
|
+
import { ChevronDown, ChevronRight, RefreshCw, Send } from "lucide-react";
|
|
15
|
+
import { supabase } from "../../integrations/supabase/client";
|
|
16
|
+
import { useStockPushJobs, useCentralAuthBlockade } from "../../hooks/useStockPushJobs";
|
|
17
|
+
import { enqueueTestStockPush } from "../../lib/stock-sync.functions";
|
|
18
|
+
const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
19
|
+
const STATUS_LABEL = {
|
|
20
|
+
pending: "In wachtrij",
|
|
21
|
+
processing: "Bezig",
|
|
22
|
+
completed: "Verstuurd",
|
|
23
|
+
failed: "Mislukt",
|
|
24
|
+
superseded: "Vervangen",
|
|
25
|
+
};
|
|
26
|
+
function statusClass(status) {
|
|
27
|
+
if (status === "completed")
|
|
28
|
+
return "bg-emerald-500/15 text-emerald-700";
|
|
29
|
+
if (status === "failed")
|
|
30
|
+
return "bg-destructive/15 text-destructive";
|
|
31
|
+
if (status === "superseded")
|
|
32
|
+
return "bg-muted text-muted-foreground";
|
|
33
|
+
return "bg-amber-500/15 text-amber-700";
|
|
34
|
+
}
|
|
35
|
+
function timeLabel(iso) {
|
|
36
|
+
if (!iso)
|
|
37
|
+
return "—";
|
|
38
|
+
const d = new Date(iso);
|
|
39
|
+
return Number.isNaN(d.getTime()) ? "—" : d.toLocaleTimeString("nl-NL");
|
|
40
|
+
}
|
|
41
|
+
function useLinkedTradeItems(connectionId) {
|
|
42
|
+
return useQuery({
|
|
43
|
+
queryKey: ["linked-trade-items", connectionId],
|
|
44
|
+
enabled: !!connectionId,
|
|
45
|
+
queryFn: async () => {
|
|
46
|
+
const { data, error } = await supabase
|
|
47
|
+
.from("trade_item_stock")
|
|
48
|
+
.select("trade_item_key, physical_stock")
|
|
49
|
+
.eq("connection_id", connectionId)
|
|
50
|
+
.limit(1000);
|
|
51
|
+
if (error)
|
|
52
|
+
throw error;
|
|
53
|
+
const rows = (data ?? []).filter((r) => UUID_RE.test(String(r.trade_item_key)));
|
|
54
|
+
if (rows.length === 0)
|
|
55
|
+
return [];
|
|
56
|
+
const ids = rows.map((r) => String(r.trade_item_key));
|
|
57
|
+
const { data: cached } = await supabase
|
|
58
|
+
.from("floriday_trade_items_cache")
|
|
59
|
+
.select("floriday_id, data")
|
|
60
|
+
.eq("connection_id", connectionId)
|
|
61
|
+
.in("floriday_id", ids);
|
|
62
|
+
const names = new Map();
|
|
63
|
+
for (const row of (cached ?? [])) {
|
|
64
|
+
const d = row.data;
|
|
65
|
+
const label = d?.tradeItemName ?? d?.name ?? d?.productName ?? null;
|
|
66
|
+
if (label)
|
|
67
|
+
names.set(String(row.floriday_id), String(label));
|
|
68
|
+
}
|
|
69
|
+
return rows
|
|
70
|
+
.filter((r) => names.has(String(r.trade_item_key)))
|
|
71
|
+
.map((r) => ({
|
|
72
|
+
id: String(r.trade_item_key),
|
|
73
|
+
name: names.get(String(r.trade_item_key)),
|
|
74
|
+
stock: Number(r.physical_stock ?? 0),
|
|
75
|
+
}))
|
|
76
|
+
.sort((a, b) => a.name.localeCompare(b.name, "nl"));
|
|
77
|
+
},
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
export function StockPushMonitor({ connectionId }) {
|
|
81
|
+
const [open, setOpen] = useState(false);
|
|
82
|
+
const [selected, setSelected] = useState("");
|
|
83
|
+
const queryClient = useQueryClient();
|
|
84
|
+
const jobs = useStockPushJobs(10);
|
|
85
|
+
const blockade = useCentralAuthBlockade();
|
|
86
|
+
const items = useLinkedTradeItems(open ? connectionId : null);
|
|
87
|
+
const testPush = useServerFn(enqueueTestStockPush);
|
|
88
|
+
useEffect(() => {
|
|
89
|
+
if (!selected && items.data && items.data.length > 0) {
|
|
90
|
+
setSelected(items.data[0].id);
|
|
91
|
+
}
|
|
92
|
+
}, [items.data, selected]);
|
|
93
|
+
const mutation = useMutation({
|
|
94
|
+
mutationFn: (tradeItemId) => testPush({ data: { tradeItemId } }),
|
|
95
|
+
onSuccess: (res) => {
|
|
96
|
+
toast.success(`Testpush ingepland (${res?.numberOfPieces ?? 0} stuks). Binnen een minuut verwerkt.`);
|
|
97
|
+
void queryClient.invalidateQueries({ queryKey: ["stock-push-jobs"] });
|
|
98
|
+
void queryClient.invalidateQueries({ queryKey: ["stock-sync-health"] });
|
|
99
|
+
},
|
|
100
|
+
onError: (e) => toast.error(e?.message ?? "Testpush inplannen is niet gelukt."),
|
|
101
|
+
});
|
|
102
|
+
const rows = jobs.data ?? [];
|
|
103
|
+
const summary = useMemo(() => {
|
|
104
|
+
if (rows.length === 0)
|
|
105
|
+
return "nog geen pushes";
|
|
106
|
+
const last = rows[0];
|
|
107
|
+
return `laatste: ${STATUS_LABEL[last.status] ?? last.status} om ${timeLabel(last.createdAt)}`;
|
|
108
|
+
}, [rows]);
|
|
109
|
+
return (_jsxs("section", { className: "rounded-lg border bg-card", children: [_jsxs("button", { type: "button", className: "flex w-full items-center gap-2 px-4 py-3 text-left text-sm font-medium", onClick: () => setOpen((v) => !v), children: [open ? (_jsx(ChevronDown, { className: "h-4 w-4 text-muted-foreground" })) : (_jsx(ChevronRight, { className: "h-4 w-4 text-muted-foreground" })), "Voorraadsync naar Floriday testen", _jsx("span", { className: "ml-auto text-xs font-normal text-muted-foreground", children: summary })] }), open && (_jsxs("div", { className: "space-y-4 border-t px-4 py-4 text-sm", children: [_jsx("p", { className: "text-muted-foreground", children: "Elke voorraadwijziging van een gekoppeld artikel zet automatisch een opdracht in de wachtrij. De Floriday-Authenticatiemodule verwerkt die elke minuut en stuurt dan het aantal stuks en de beschikbaarheid naar Floriday. Hieronder plan je een testopdracht in met de huidige voorraadstand, zonder iets te wijzigen." }), blockade.data?.blocked && (_jsxs("div", { className: "rounded-md border border-amber-500/30 bg-amber-500/10 px-3 py-2 text-amber-800", children: [_jsx("span", { className: "font-medium", children: "Let op: " }), "de tokenaanvraag bij Central Auth mislukt op dit moment, dus elke push loopt daarop vast. Dit ligt buiten deze module.", blockade.data.message && (_jsxs("span", { className: "block opacity-80", children: ["Melding: ", blockade.data.message] }))] })), _jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsx("select", { className: "h-9 min-w-[16rem] rounded-md border bg-background px-2 text-sm", value: selected, onChange: (e) => setSelected(e.target.value), disabled: !connectionId || items.isLoading || (items.data ?? []).length === 0, children: (items.data ?? []).length === 0 ? (_jsx("option", { value: "", children: items.isLoading ? "Artikelen laden…" : "Geen gekoppelde artikelen gevonden" })) : ((items.data ?? []).map((i) => (_jsxs("option", { value: i.id, children: [i.name, " \u2014 ", i.stock, " stuks"] }, i.id)))) }), _jsxs("button", { type: "button", className: "inline-flex h-9 items-center rounded-md bg-primary px-3 text-sm font-medium text-primary-foreground hover:opacity-90 disabled:opacity-50", disabled: !selected || mutation.isPending, onClick: () => selected && mutation.mutate(selected), children: [_jsx(Send, { className: "mr-2 h-4 w-4" }), "Testpush uitvoeren"] }), _jsxs("button", { type: "button", className: "inline-flex h-9 items-center rounded-md border px-3 text-sm font-medium hover:bg-muted disabled:opacity-50", disabled: jobs.isFetching, onClick: () => void jobs.refetch(), children: [_jsx(RefreshCw, { className: `mr-2 h-4 w-4 ${jobs.isFetching ? "animate-spin" : ""}` }), "Ververs"] })] }), _jsx("div", { className: "overflow-x-auto", children: _jsxs("table", { className: "w-full text-left text-xs", children: [_jsx("thead", { className: "text-muted-foreground", children: _jsxs("tr", { children: [_jsx("th", { className: "py-1 pr-3 font-medium", children: "Artikel" }), _jsx("th", { className: "py-1 pr-3 font-medium", children: "Stuks" }), _jsx("th", { className: "py-1 pr-3 font-medium", children: "Status" }), _jsx("th", { className: "py-1 pr-3 font-medium", children: "Pogingen" }), _jsx("th", { className: "py-1 pr-3 font-medium", children: "Aangemaakt" }), _jsx("th", { className: "py-1 font-medium", children: "Melding" })] }) }), _jsxs("tbody", { children: [rows.length === 0 && (_jsx("tr", { children: _jsx("td", { colSpan: 6, className: "py-3 text-muted-foreground", children: "Er staan nog geen push-opdrachten in de wachtrij." }) })), rows.map((r) => (_jsxs("tr", { className: "border-t", children: [_jsx("td", { className: "py-1.5 pr-3", children: r.name }), _jsx("td", { className: "py-1.5 pr-3 tabular-nums", children: r.numberOfPieces }), _jsx("td", { className: "py-1.5 pr-3", children: _jsx("span", { className: `rounded px-1.5 py-0.5 ${statusClass(r.status)}`, children: STATUS_LABEL[r.status] ?? r.status }) }), _jsx("td", { className: "py-1.5 pr-3 tabular-nums", children: r.attempts }), _jsx("td", { className: "py-1.5 pr-3 whitespace-nowrap", children: timeLabel(r.createdAt) }), _jsx("td", { className: "py-1.5 max-w-[24rem] truncate text-muted-foreground", title: r.lastError ?? "", children: r.lastError ?? "—" })] }, r.id)))] })] }) })] }))] }));
|
|
110
|
+
}
|
|
@@ -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":"AAiGA,wBAAgB,YAAY,gCA+O3B"}
|
|
@@ -6,6 +6,7 @@ import { AlertTriangle, CheckCircle2, FileText, PackageX, Plus, Search, } from "
|
|
|
6
6
|
import { useFloridayConnection } from "../../hooks/useFloridayConnection";
|
|
7
7
|
import { useVoorraadData } from "../../hooks/useVoorraadData";
|
|
8
8
|
import { StockSyncStatus } from "./StockSyncStatus";
|
|
9
|
+
import { StockPushMonitor } from "./StockPushMonitor";
|
|
9
10
|
import { ExcelExportButton } from "./ExcelExportButton";
|
|
10
11
|
import { StockBulkButtons } from "./StockBulkButtons";
|
|
11
12
|
import { VoorraadTable } from "./VoorraadTable";
|
|
@@ -93,7 +94,7 @@ export function VoorraadPage() {
|
|
|
93
94
|
{ id: "out_of_stock", label: "Niet op voorraad", count: counts.outOfStock },
|
|
94
95
|
{ id: "below_min", label: "Onder minimum", count: counts.belowMin },
|
|
95
96
|
];
|
|
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: "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/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(StockSyncStatus, {}), 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) => {
|
|
97
|
+
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/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(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) => {
|
|
97
98
|
const active = stockFilter === t.id;
|
|
98
99
|
return (_jsxs("button", { type: "button", onClick: () => setStockFilter(t.id), className: `relative py-3 px-3 text-sm font-medium whitespace-nowrap transition-colors ${active
|
|
99
100
|
? "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,+
|
|
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,+BA2HzF"}
|
|
@@ -10,19 +10,21 @@ export function VoorraadStockCard({ connectionId, tradeItemId, draftId, productI
|
|
|
10
10
|
const { row, loading, save } = useTradeItemStock(connectionId, keys);
|
|
11
11
|
const [values, setValues] = useState(row);
|
|
12
12
|
const [saving, setSaving] = useState(false);
|
|
13
|
+
const [touched, setTouched] = useState(false);
|
|
13
14
|
const floridayPieces = useFloridayContinuousStock(connectionId, tradeItemId ?? null);
|
|
14
15
|
useEffect(() => {
|
|
15
16
|
setValues(row);
|
|
17
|
+
setTouched(false);
|
|
16
18
|
}, [row.updated_at, row.physical_stock, row.minimum_stock, row.reserved_stock, row.incoming_stock, row.notes]);
|
|
17
|
-
//
|
|
18
|
-
//
|
|
19
|
+
// Floriday mag "Liggend" alleen bijwerken zolang de gebruiker het veld niet
|
|
20
|
+
// zelf heeft aangepast — anders zou een handmatige invoer stil verdwijnen.
|
|
19
21
|
useEffect(() => {
|
|
20
|
-
if (floridayPieces === undefined)
|
|
22
|
+
if (floridayPieces === undefined || touched)
|
|
21
23
|
return;
|
|
22
24
|
setValues((v) => v.physical_stock === floridayPieces
|
|
23
25
|
? v
|
|
24
26
|
: { ...v, physical_stock: floridayPieces });
|
|
25
|
-
}, [floridayPieces]);
|
|
27
|
+
}, [floridayPieces, touched]);
|
|
26
28
|
if (!key) {
|
|
27
29
|
return (_jsx(Card, { className: "p-4 text-sm text-muted-foreground", children: "Voorraad kan pas worden ingesteld nadat het artikel voor het eerst is opgeslagen." }));
|
|
28
30
|
}
|
|
@@ -40,6 +42,9 @@ export function VoorraadStockCard({ connectionId, tradeItemId, draftId, productI
|
|
|
40
42
|
setSaving(false);
|
|
41
43
|
}
|
|
42
44
|
};
|
|
43
|
-
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) =>
|
|
45
|
+
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) => {
|
|
46
|
+
setTouched(true);
|
|
47
|
+
setValues((v) => ({ ...v, [id]: Number(e.target.value) || 0 }));
|
|
48
|
+
} })] }));
|
|
44
49
|
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"] }) })] })) })] }));
|
|
45
50
|
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export interface StockPushJob {
|
|
2
|
+
id: string;
|
|
3
|
+
tradeItemId: string;
|
|
4
|
+
name: string;
|
|
5
|
+
numberOfPieces: number;
|
|
6
|
+
desiredAvailable: boolean | null;
|
|
7
|
+
status: string;
|
|
8
|
+
attempts: number;
|
|
9
|
+
lastError: string | null;
|
|
10
|
+
createdAt: string;
|
|
11
|
+
updatedAt: string | null;
|
|
12
|
+
sentAt: string | null;
|
|
13
|
+
}
|
|
14
|
+
export declare function useStockPushJobs(limit?: number): import("@tanstack/react-query").UseQueryResult<NoInfer<StockPushJob[]>, Error>;
|
|
15
|
+
/**
|
|
16
|
+
* Signaleert of de tokenaanvraag bij Central Auth structureel faalt. Zolang dat
|
|
17
|
+
* zo is, mislukt elke push — ongeacht wat deze module doet.
|
|
18
|
+
*/
|
|
19
|
+
export declare function useCentralAuthBlockade(): import("@tanstack/react-query").UseQueryResult<NoInfer<{
|
|
20
|
+
blocked: boolean;
|
|
21
|
+
message: string | null;
|
|
22
|
+
}>, Error>;
|
|
23
|
+
//# sourceMappingURL=useStockPushJobs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useStockPushJobs.d.ts","sourceRoot":"","sources":["../../src/hooks/useStockPushJobs.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,cAAc,EAAE,MAAM,CAAC;IACvB,gBAAgB,EAAE,OAAO,GAAG,IAAI,CAAC;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB;AAaD,wBAAgB,gBAAgB,CAAC,KAAK,SAAK,kFA4C1C;AAED;;;GAGG;AACH,wBAAgB,sBAAsB;aACT,OAAO;aAAW,MAAM,GAAG,IAAI;WAoB3D"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Leest de laatste push-opdrachten richting Floriday uit de gedeelde wachtrij,
|
|
3
|
+
* inclusief de artikelnaam uit de Floriday-cache. Bedoeld om een testpush te
|
|
4
|
+
* kunnen volgen zonder databasekennis.
|
|
5
|
+
*/
|
|
6
|
+
import { useQuery } from "@tanstack/react-query";
|
|
7
|
+
import { supabase } from "../integrations/supabase/client";
|
|
8
|
+
function nameFromCache(data) {
|
|
9
|
+
if (!data || typeof data !== "object")
|
|
10
|
+
return null;
|
|
11
|
+
return (data.tradeItemName ??
|
|
12
|
+
data.name ??
|
|
13
|
+
data.productName ??
|
|
14
|
+
data.vbnProductName ??
|
|
15
|
+
null);
|
|
16
|
+
}
|
|
17
|
+
export function useStockPushJobs(limit = 10) {
|
|
18
|
+
return useQuery({
|
|
19
|
+
queryKey: ["stock-push-jobs", limit],
|
|
20
|
+
refetchInterval: 15000,
|
|
21
|
+
queryFn: async () => {
|
|
22
|
+
const { data, error } = await supabase
|
|
23
|
+
.from("floriday_continuous_stock_queue")
|
|
24
|
+
.select("id, trade_item_id, number_of_pieces, desired_available, status, attempts, last_error, created_at, updated_at, sent_at")
|
|
25
|
+
.order("created_at", { ascending: false })
|
|
26
|
+
.limit(limit);
|
|
27
|
+
if (error)
|
|
28
|
+
throw error;
|
|
29
|
+
const rows = (data ?? []);
|
|
30
|
+
const ids = Array.from(new Set(rows.map((r) => r.trade_item_id).filter(Boolean)));
|
|
31
|
+
const names = new Map();
|
|
32
|
+
if (ids.length > 0) {
|
|
33
|
+
const { data: cached } = await supabase
|
|
34
|
+
.from("floriday_trade_items_cache")
|
|
35
|
+
.select("floriday_id, data")
|
|
36
|
+
.in("floriday_id", ids);
|
|
37
|
+
for (const row of (cached ?? [])) {
|
|
38
|
+
const label = nameFromCache(row.data);
|
|
39
|
+
if (label)
|
|
40
|
+
names.set(String(row.floriday_id), String(label));
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return rows.map((r) => ({
|
|
44
|
+
id: String(r.id),
|
|
45
|
+
tradeItemId: String(r.trade_item_id ?? ""),
|
|
46
|
+
name: names.get(String(r.trade_item_id)) ?? String(r.trade_item_id ?? "").slice(0, 8),
|
|
47
|
+
numberOfPieces: Number(r.number_of_pieces ?? 0),
|
|
48
|
+
desiredAvailable: r.desired_available ?? null,
|
|
49
|
+
status: String(r.status ?? ""),
|
|
50
|
+
attempts: Number(r.attempts ?? 0),
|
|
51
|
+
lastError: r.last_error ?? null,
|
|
52
|
+
createdAt: String(r.created_at),
|
|
53
|
+
updatedAt: r.updated_at ?? null,
|
|
54
|
+
sentAt: r.sent_at ?? null,
|
|
55
|
+
}));
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Signaleert of de tokenaanvraag bij Central Auth structureel faalt. Zolang dat
|
|
61
|
+
* zo is, mislukt elke push — ongeacht wat deze module doet.
|
|
62
|
+
*/
|
|
63
|
+
export function useCentralAuthBlockade() {
|
|
64
|
+
return useQuery({
|
|
65
|
+
queryKey: ["central-auth-blockade"],
|
|
66
|
+
refetchInterval: 60000,
|
|
67
|
+
queryFn: async () => {
|
|
68
|
+
const since = new Date(Date.now() - 30 * 60000).toISOString();
|
|
69
|
+
const { data, error } = await supabase
|
|
70
|
+
.from("floriday_token_refresh_log")
|
|
71
|
+
.select("ok, error, attempted_at")
|
|
72
|
+
.gte("attempted_at", since)
|
|
73
|
+
.order("attempted_at", { ascending: false })
|
|
74
|
+
.limit(5);
|
|
75
|
+
if (error)
|
|
76
|
+
return { blocked: false, message: null };
|
|
77
|
+
const rows = (data ?? []);
|
|
78
|
+
if (rows.length === 0)
|
|
79
|
+
return { blocked: false, message: null };
|
|
80
|
+
const latest = rows[0];
|
|
81
|
+
if (latest.ok)
|
|
82
|
+
return { blocked: false, message: null };
|
|
83
|
+
return { blocked: true, message: latest.error ?? null };
|
|
84
|
+
},
|
|
85
|
+
});
|
|
86
|
+
}
|
|
@@ -23,6 +23,18 @@ export declare function stockKeysFor(params: {
|
|
|
23
23
|
draftId?: string | null;
|
|
24
24
|
productId?: string | null;
|
|
25
25
|
}): string[];
|
|
26
|
+
/**
|
|
27
|
+
* Zoekt de voorraadrij van één artikel op in een reeds geladen map. Sinds de
|
|
28
|
+
* canonicalisatie is er nog maar één rij per artikel (de Floriday-UUID wanneer
|
|
29
|
+
* het artikel gekoppeld is, anders `product:<uuid>`), maar we kijken bewust
|
|
30
|
+
* naar alle bekende varianten zodat oude data nooit als "0" wordt getoond.
|
|
31
|
+
*/
|
|
32
|
+
export declare function lookupStock(map: Map<string, TradeItemStockRow>, params: {
|
|
33
|
+
tradeItemKey?: string | null;
|
|
34
|
+
floridayId?: string | null;
|
|
35
|
+
productId?: string | null;
|
|
36
|
+
draftId?: string | null;
|
|
37
|
+
}): TradeItemStockRow | undefined;
|
|
26
38
|
export declare function useTradeItemStock(connectionId: string | null, keyOrKeys: string | string[] | null): {
|
|
27
39
|
row: TradeItemStockRow;
|
|
28
40
|
loading: boolean;
|
|
@@ -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,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B,GAAG,MAAM,GAAG,IAAI,CAKhB;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE;IACnC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B,GAAG,MAAM,EAAE,CAMX;AAED,wBAAgB,iBAAiB,CAC/B,YAAY,EAAE,MAAM,GAAG,IAAI,EAC3B,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,IAAI;;;mBA4ElB,iBAAiB;EAmEnC;AAED,wBAAsB,aAAa,CACjC,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC,CAUzC"}
|
|
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,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B,GAAG,MAAM,GAAG,IAAI,CAKhB;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE;IACnC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B,GAAG,MAAM,EAAE,CAMX;AAED;;;;;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;EAmEnC;AAED,wBAAsB,aAAa,CACjC,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC,CAUzC"}
|
|
@@ -33,6 +33,28 @@ export function stockKeysFor(params) {
|
|
|
33
33
|
keys.push(`draft:${params.draftId}`);
|
|
34
34
|
return keys;
|
|
35
35
|
}
|
|
36
|
+
/**
|
|
37
|
+
* Zoekt de voorraadrij van één artikel op in een reeds geladen map. Sinds de
|
|
38
|
+
* canonicalisatie is er nog maar één rij per artikel (de Floriday-UUID wanneer
|
|
39
|
+
* het artikel gekoppeld is, anders `product:<uuid>`), maar we kijken bewust
|
|
40
|
+
* naar alle bekende varianten zodat oude data nooit als "0" wordt getoond.
|
|
41
|
+
*/
|
|
42
|
+
export function lookupStock(map, params) {
|
|
43
|
+
const candidates = [
|
|
44
|
+
params.floridayId ?? null,
|
|
45
|
+
params.tradeItemKey ?? null,
|
|
46
|
+
params.productId ? `product:${params.productId}` : null,
|
|
47
|
+
params.draftId ? `draft:${params.draftId}` : null,
|
|
48
|
+
];
|
|
49
|
+
for (const key of candidates) {
|
|
50
|
+
if (!key)
|
|
51
|
+
continue;
|
|
52
|
+
const hit = map.get(key);
|
|
53
|
+
if (hit)
|
|
54
|
+
return hit;
|
|
55
|
+
}
|
|
56
|
+
return undefined;
|
|
57
|
+
}
|
|
36
58
|
export function useTradeItemStock(connectionId, keyOrKeys) {
|
|
37
59
|
const keysKey = Array.isArray(keyOrKeys)
|
|
38
60
|
? keyOrKeys.filter(Boolean).join("|")
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useVoorraadData.d.ts","sourceRoot":"","sources":["../../src/hooks/useVoorraadData.ts"],"names":[],"mappings":"AAEA,OAAO,EAAsB,KAAK,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAsB5E;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI;;;;;
|
|
1
|
+
{"version":3,"file":"useVoorraadData.d.ts","sourceRoot":"","sources":["../../src/hooks/useVoorraadData.ts"],"names":[],"mappings":"AAEA,OAAO,EAAsB,KAAK,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAsB5E;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI;;;;;EAsV1D"}
|
|
@@ -3,7 +3,7 @@ import { supabase } from "../integrations/supabase/client";
|
|
|
3
3
|
import { normaliseTradeItem } from "../lib/floriday-client";
|
|
4
4
|
import { floridayInfoFromCache } from "../lib/floriday-info";
|
|
5
5
|
import { useLocalTradeItems } from "./useLocalTradeItems";
|
|
6
|
-
import { fetchStockMap } from "./useTradeItemStock";
|
|
6
|
+
import { fetchStockMap, lookupStock } from "./useTradeItemStock";
|
|
7
7
|
import { useFloridayContinuousStockMap } from "./useFloridayContinuousStock";
|
|
8
8
|
function applyStock(item, s) {
|
|
9
9
|
if (!s)
|
|
@@ -283,10 +283,18 @@ export function useVoorraadData(connectionId) {
|
|
|
283
283
|
return { ...i, floriday: info };
|
|
284
284
|
};
|
|
285
285
|
const merged = [
|
|
286
|
-
...localItems.map((i) => applyProductGroupReservation(classify(attachFloriday(applyStock(i, stockMap
|
|
286
|
+
...localItems.map((i) => applyProductGroupReservation(classify(attachFloriday(applyStock(i, lookupStock(stockMap, {
|
|
287
|
+
tradeItemKey: i.tradeItemKey ?? null,
|
|
288
|
+
floridayId: i.floridayId ?? null,
|
|
289
|
+
productId: i.productId ?? null,
|
|
290
|
+
})))))),
|
|
287
291
|
...publishedItems
|
|
288
292
|
.filter((i) => !i.id || !localFloridayIds.has(i.id))
|
|
289
|
-
.map((i) => applyProductGroupReservation(classify(applyFloridayOverride(applyStock(i, stockMap
|
|
293
|
+
.map((i) => applyProductGroupReservation(classify(applyFloridayOverride(applyStock(i, lookupStock(stockMap, {
|
|
294
|
+
floridayId: i.id,
|
|
295
|
+
tradeItemKey: i.tradeItemKey ?? null,
|
|
296
|
+
productId: i.productId ?? null,
|
|
297
|
+
})))))),
|
|
290
298
|
];
|
|
291
299
|
return {
|
|
292
300
|
items: merged,
|
package/dist-lib/index.d.ts
CHANGED
|
@@ -8,5 +8,6 @@ export { ArtikelWizard } from './components/voorraad/ArtikelWizard';
|
|
|
8
8
|
export { ProductLinkCard } from './components/voorraad/ProductLinkCard';
|
|
9
9
|
export { VoorraadVeldenCard } from './components/voorraad/VoorraadVeldenCard';
|
|
10
10
|
export { ProductCustomFieldsCard } from './components/voorraad/ProductCustomFieldsCard';
|
|
11
|
+
export { ModuleHealthCard } from './components/voorraad/ModuleHealthCard';
|
|
11
12
|
export declare const floridayVoorraadModule: FlowModule;
|
|
12
13
|
//# sourceMappingURL=index.d.ts.map
|
package/dist-lib/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAEvD,cAAc,kBAAkB,CAAC;AACjC,OAAO,EAAE,YAAY,EAAE,MAAM,oCAAoC,CAAC;AAClE,OAAO,EAAE,gBAAgB,EAAE,MAAM,wCAAwC,CAAC;AAC1E,OAAO,EAAE,eAAe,EAAE,MAAM,uCAAuC,CAAC;AACxE,OAAO,EAAE,cAAc,EAAE,MAAM,sCAAsC,CAAC;AACtE,OAAO,EAAE,aAAa,EAAE,MAAM,qCAAqC,CAAC;AACpE,OAAO,EAAE,eAAe,EAAE,MAAM,uCAAuC,CAAC;AACxE,OAAO,EAAE,kBAAkB,EAAE,MAAM,0CAA0C,CAAC;AAC9E,OAAO,EAAE,uBAAuB,EAAE,MAAM,+CAA+C,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAEvD,cAAc,kBAAkB,CAAC;AACjC,OAAO,EAAE,YAAY,EAAE,MAAM,oCAAoC,CAAC;AAClE,OAAO,EAAE,gBAAgB,EAAE,MAAM,wCAAwC,CAAC;AAC1E,OAAO,EAAE,eAAe,EAAE,MAAM,uCAAuC,CAAC;AACxE,OAAO,EAAE,cAAc,EAAE,MAAM,sCAAsC,CAAC;AACtE,OAAO,EAAE,aAAa,EAAE,MAAM,qCAAqC,CAAC;AACpE,OAAO,EAAE,eAAe,EAAE,MAAM,uCAAuC,CAAC;AACxE,OAAO,EAAE,kBAAkB,EAAE,MAAM,0CAA0C,CAAC;AAC9E,OAAO,EAAE,uBAAuB,EAAE,MAAM,+CAA+C,CAAC;AAKxF,OAAO,EAAE,gBAAgB,EAAE,MAAM,wCAAwC,CAAC;AAE1E,eAAO,MAAM,sBAAsB,EAAE,UAiBpC,CAAC"}
|
package/dist-lib/index.js
CHANGED
|
@@ -12,6 +12,8 @@ export { ProductLinkCard } from './components/voorraad/ProductLinkCard';
|
|
|
12
12
|
export { VoorraadVeldenCard } from './components/voorraad/VoorraadVeldenCard';
|
|
13
13
|
export { ProductCustomFieldsCard } from './components/voorraad/ProductCustomFieldsCard';
|
|
14
14
|
import { VoorraadVeldenCard } from './components/voorraad/VoorraadVeldenCard';
|
|
15
|
+
import { ModuleHealthCard } from './components/voorraad/ModuleHealthCard';
|
|
16
|
+
export { ModuleHealthCard } from './components/voorraad/ModuleHealthCard';
|
|
15
17
|
export const floridayVoorraadModule = {
|
|
16
18
|
id: "voorraad",
|
|
17
19
|
name: "Voorraad",
|
|
@@ -25,5 +27,8 @@ export const floridayVoorraadModule = {
|
|
|
25
27
|
{ label: "Fusten", href: "/voorraad/fusten", icon: Boxes },
|
|
26
28
|
],
|
|
27
29
|
},
|
|
28
|
-
settingsCards: [
|
|
30
|
+
settingsCards: [
|
|
31
|
+
{ component: VoorraadVeldenCard, order: 50 },
|
|
32
|
+
{ component: ModuleHealthCard, order: 60 },
|
|
33
|
+
],
|
|
29
34
|
};
|