@flowselections/floriday-voorraad 1.0.143 → 1.0.145
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist-lib/components/voorraad/ArtikelWizard.js +1 -1
- package/dist-lib/components/voorraad/FustAdministratie.d.ts +12 -0
- package/dist-lib/components/voorraad/FustAdministratie.d.ts.map +1 -0
- package/dist-lib/components/voorraad/FustAdministratie.js +69 -0
- package/dist-lib/components/voorraad/FustenPage.d.ts.map +1 -1
- package/dist-lib/components/voorraad/FustenPage.js +9 -14
- package/dist-lib/components/voorraad/KoppelingStatusPanel.d.ts +6 -0
- package/dist-lib/components/voorraad/KoppelingStatusPanel.d.ts.map +1 -0
- package/dist-lib/components/voorraad/KoppelingStatusPanel.js +47 -0
- package/dist-lib/components/voorraad/NieuwArtikelPage.js +2 -2
- package/dist-lib/components/voorraad/TelmomentButton.d.ts +8 -0
- package/dist-lib/components/voorraad/TelmomentButton.d.ts.map +1 -0
- package/dist-lib/components/voorraad/TelmomentButton.js +65 -0
- package/dist-lib/components/voorraad/ValidationPage.d.ts.map +1 -1
- package/dist-lib/components/voorraad/ValidationPage.js +2 -1
- package/dist-lib/components/voorraad/VoorraadPage.d.ts.map +1 -1
- package/dist-lib/components/voorraad/VoorraadPage.js +2 -1
- package/dist-lib/integrations/supabase/types.d.ts +525 -0
- package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
- package/dist-lib/lib/fust-administratie.d.ts +29 -0
- package/dist-lib/lib/fust-administratie.d.ts.map +1 -0
- package/dist-lib/lib/fust-administratie.js +63 -0
- package/dist-lib/lib/fusten.d.ts +4 -0
- package/dist-lib/lib/fusten.d.ts.map +1 -1
- package/package.json +1 -1
- package/sql/AGENTS.md +3 -0
- package/sql/floriday-voorraad-schema.sql +910 -58
|
@@ -1929,7 +1929,7 @@ export function ArtikelWizard({ draftId: initialDraftId, tradeItemId, productId,
|
|
|
1929
1929
|
name: data.name || data.vbnName,
|
|
1930
1930
|
articleNumber: data.articleCode,
|
|
1931
1931
|
gtin: data.gtin,
|
|
1932
|
-
} }), _jsx(VoorraadStockCard, { connectionId: activeId, tradeItemId: tradeItemId ?? null, draftId: draftId ?? null, productId: linkedProductId ?? productId ?? null }), _jsx(OrderHistoryCard, { tradeItemKey: localTradeItemKey, productName: data.name || data.vbnName || null, productId: linkedProductId ?? productId ?? null }), _jsx(StockMutationHistoryCard, { tradeItemKey: localTradeItemKey, productId: linkedProductId ?? productId ?? null }), _jsx(ProductLinkCard, { tradeItemKey: localTradeItemKey, productName: data.vbnName || null })] })), _jsx("ol", { className: "space-y-3", children: steps.map((s, i) => {
|
|
1932
|
+
} }), _jsx(VoorraadStockCard, { connectionId: activeId, tradeItemId: tradeItemId ?? null, draftId: draftId ?? null, productId: linkedProductId ?? productId ?? null }), _jsx(OrderHistoryCard, { tradeItemKey: localTradeItemKey, productName: data.name || data.vbnName || null, productId: linkedProductId ?? productId ?? null }), _jsx(StockMutationHistoryCard, { tradeItemKey: localTradeItemKey, productId: linkedProductId ?? productId ?? null }), _jsx(ProductLinkCard, { tradeItemKey: localTradeItemKey, productName: data.name || data.vbnName || null })] })), _jsx("ol", { className: "space-y-3", children: steps.map((s, i) => {
|
|
1933
1933
|
const isOpen = i === step;
|
|
1934
1934
|
const isValid = stepValidity[i];
|
|
1935
1935
|
const isVisited = i < step || isEditing;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { FustType } from "../../lib/fusten.js";
|
|
2
|
+
/** Startmoment en beginstand: boekingen vóór dit moment tellen niet mee in het saldo, maar blijven zichtbaar. */
|
|
3
|
+
export declare function FustStartmomentCard({ items, onSaved }: {
|
|
4
|
+
items: FustType[];
|
|
5
|
+
onSaved: () => void;
|
|
6
|
+
}): import("react").JSX.Element;
|
|
7
|
+
/** Per fust: saldo bijhouden en eigen naam. */
|
|
8
|
+
export declare function FustInstellingenCell({ fust, onSaved }: {
|
|
9
|
+
fust: FustType;
|
|
10
|
+
onSaved: () => void;
|
|
11
|
+
}): import("react").JSX.Element;
|
|
12
|
+
//# sourceMappingURL=FustAdministratie.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FustAdministratie.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/FustAdministratie.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAejD,iHAAiH;AACjH,wBAAgB,mBAAmB,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;IAAE,KAAK,EAAE,QAAQ,EAAE,CAAC;IAAC,OAAO,EAAE,MAAM,IAAI,CAAA;CAAE,+BA8EjG;AAED,+CAA+C;AAC/C,wBAAgB,oBAAoB,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,OAAO,EAAE,MAAM,IAAI,CAAA;CAAE,+BAiD9F"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useState } from "react";
|
|
3
|
+
import { Button, Card, Input, toast } from "@flowselections/core";
|
|
4
|
+
import { fetchFustStartmomenten, fustNaam, saldoBijhouden, saveFustInstellingen, saveFustStartmoment, } from "../../lib/fust-administratie.js";
|
|
5
|
+
function lokaleInvoer(d) {
|
|
6
|
+
const p = (n) => String(n).padStart(2, "0");
|
|
7
|
+
return `${d.getFullYear()}-${p(d.getMonth() + 1)}-${p(d.getDate())}T${p(d.getHours())}:${p(d.getMinutes())}`;
|
|
8
|
+
}
|
|
9
|
+
/** Startmoment en beginstand: boekingen vóór dit moment tellen niet mee in het saldo, maar blijven zichtbaar. */
|
|
10
|
+
export function FustStartmomentCard({ items, onSaved }) {
|
|
11
|
+
const [rows, setRows] = useState(null);
|
|
12
|
+
const [fustId, setFustId] = useState("");
|
|
13
|
+
const [moment, setMoment] = useState(() => lokaleInvoer(new Date()));
|
|
14
|
+
const [beginstand, setBeginstand] = useState("0");
|
|
15
|
+
const [busy, setBusy] = useState(false);
|
|
16
|
+
const load = () => fetchFustStartmomenten().then(setRows).catch(() => setRows([]));
|
|
17
|
+
useEffect(() => { void load(); }, []);
|
|
18
|
+
const algemeen = rows?.find((r) => r.fust_type_id === null) ?? null;
|
|
19
|
+
const perFust = (rows ?? []).filter((r) => r.fust_type_id !== null);
|
|
20
|
+
const opslaan = async () => {
|
|
21
|
+
const n = Number(beginstand);
|
|
22
|
+
if (!Number.isFinite(n)) {
|
|
23
|
+
toast.error("Vul een geldige beginstand in.");
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
const d = new Date(moment);
|
|
27
|
+
if (Number.isNaN(d.getTime())) {
|
|
28
|
+
toast.error("Vul een geldig startmoment in.");
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
setBusy(true);
|
|
32
|
+
try {
|
|
33
|
+
await saveFustStartmoment(fustId || null, d, n);
|
|
34
|
+
toast.success("Startmoment opgeslagen; het saldo is opnieuw berekend.");
|
|
35
|
+
await load();
|
|
36
|
+
onSaved();
|
|
37
|
+
}
|
|
38
|
+
catch (e) {
|
|
39
|
+
toast.error(e.message);
|
|
40
|
+
}
|
|
41
|
+
finally {
|
|
42
|
+
setBusy(false);
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
return (_jsxs(Card, { className: "p-4 space-y-3", children: [_jsxs("div", { children: [_jsx("p", { className: "font-medium", children: "Startmoment fustenadministratie" }), rows !== null && !algemeen && perFust.length === 0 ? (_jsx("p", { className: "text-sm text-amber-700", children: "Er is nog geen startmoment. Stel een startmoment en beginstand in; tot die tijd telt het saldo alle boekingen uit het verleden mee." })) : (_jsxs("p", { className: "text-sm text-muted-foreground", children: [algemeen
|
|
46
|
+
? `Alle fusten: vanaf ${new Date(algemeen.moment).toLocaleString("nl-NL")}${algemeen.automatisch ? " (automatisch bij de eerste order)" : ""}.`
|
|
47
|
+
: "Geen algemeen startmoment.", perFust.length > 0 && ` ${perFust.length} fust${perFust.length === 1 ? "" : "en"} met een eigen startmoment.`, " ", "Boekingen v\u00F3\u00F3r het startmoment blijven zichtbaar in de historie maar tellen niet mee."] }))] }), _jsxs("div", { className: "flex flex-wrap items-end gap-2", children: [_jsxs("label", { className: "text-xs text-muted-foreground space-y-1", children: [_jsx("span", { className: "block", children: "Voor" }), _jsxs("select", { className: "h-9 rounded-md border border-input bg-background px-2 text-sm", value: fustId, onChange: (e) => setFustId(e.target.value), children: [_jsx("option", { value: "", children: "Alle fusten" }), items.map((f) => (_jsx("option", { value: f.id, children: fustNaam(f) }, f.id)))] })] }), _jsxs("label", { className: "text-xs text-muted-foreground space-y-1", children: [_jsx("span", { className: "block", children: "Startmoment" }), _jsx(Input, { type: "datetime-local", className: "h-9 w-52", value: moment, onChange: (e) => setMoment(e.target.value) })] }), _jsxs("label", { className: "text-xs text-muted-foreground space-y-1", children: [_jsxs("span", { className: "block", children: ["Beginstand", fustId ? "" : " (per fust)"] }), _jsx(Input, { type: "number", className: "h-9 w-28 text-right", value: beginstand, onChange: (e) => setBeginstand(e.target.value) })] }), _jsx(Button, { size: "sm", onClick: () => void opslaan(), disabled: busy, children: busy ? "Opslaan…" : "Startmoment opslaan" })] })] }));
|
|
48
|
+
}
|
|
49
|
+
/** Per fust: saldo bijhouden en eigen naam. */
|
|
50
|
+
export function FustInstellingenCell({ fust, onSaved }) {
|
|
51
|
+
const [open, setOpen] = useState(false);
|
|
52
|
+
const [naam, setNaam] = useState(fust.eigen_naam ?? "");
|
|
53
|
+
const [busy, setBusy] = useState(false);
|
|
54
|
+
const bijhouden = saldoBijhouden(fust);
|
|
55
|
+
const opslaan = async (saldo, eigenNaam) => {
|
|
56
|
+
setBusy(true);
|
|
57
|
+
try {
|
|
58
|
+
await saveFustInstellingen(fust.id, saldo, eigenNaam);
|
|
59
|
+
onSaved();
|
|
60
|
+
}
|
|
61
|
+
catch (e) {
|
|
62
|
+
toast.error(e.message);
|
|
63
|
+
}
|
|
64
|
+
finally {
|
|
65
|
+
setBusy(false);
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
return (_jsxs("div", { className: "mt-1 space-y-1 text-xs", children: [_jsxs("label", { className: "flex items-center gap-1 text-muted-foreground", children: [_jsx("input", { type: "checkbox", checked: bijhouden, disabled: busy, onChange: (e) => void opslaan(e.target.checked, fust.eigen_naam ?? null) }), "Saldo bijhouden", fust.saldo_bijhouden == null ? " (standaard)" : ""] }), !open ? (_jsx("button", { type: "button", className: "text-primary hover:underline", onClick: () => setOpen(true), children: fust.eigen_naam ? "Eigen naam wijzigen" : "Eigen naam geven" })) : (_jsxs("div", { className: "flex items-center gap-1", children: [_jsx(Input, { className: "h-7 w-40", value: naam, placeholder: fust.name, onChange: (e) => setNaam(e.target.value) }), _jsx(Button, { size: "sm", className: "h-7", disabled: busy, onClick: async () => { await opslaan(fust.saldo_bijhouden ?? null, naam); setOpen(false); }, children: "Opslaan" }), _jsx(Button, { size: "sm", variant: "ghost", className: "h-7", onClick: () => setOpen(false), children: "Annuleren" })] }))] }));
|
|
69
|
+
}
|
|
@@ -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":"AAmLA,wBAAgB,UAAU,gCA0PzB"}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import {
|
|
2
|
+
import { useEffect, useMemo, useState } from "react";
|
|
3
3
|
import { Boxes, Minus, Plus, RefreshCw, Search } from "lucide-react";
|
|
4
4
|
import { Button, Card, Input, Badge, toast } from "@flowselections/core";
|
|
5
|
-
import {
|
|
5
|
+
import { FustStartmomentCard, FustInstellingenCell } from "./FustAdministratie.js";
|
|
6
|
+
import { fustNaam, fustTekorten, saldoBijhouden } from "../../lib/fust-administratie.js";
|
|
6
7
|
import { useFloridayConnection } from "../../hooks/useFloridayConnection.js";
|
|
7
8
|
import { useFusten } from "../../hooks/useFusten.js";
|
|
8
9
|
import { fetchFloricodeFustMap, fetchFloridayCustomPackageMap, fetchFustMutations, formatFustDimensions, resolveFustDimensions, } from "../../lib/fusten.js";
|
|
@@ -61,7 +62,9 @@ function MutationHistory({ fust }) {
|
|
|
61
62
|
? "Order geannuleerd"
|
|
62
63
|
: m.reason === "manual_sale"
|
|
63
64
|
? "Handmatige verkoop"
|
|
64
|
-
:
|
|
65
|
+
: m.reason === "startmoment"
|
|
66
|
+
? "Startmoment / beginstand"
|
|
67
|
+
: "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)))] }))] }));
|
|
65
68
|
}
|
|
66
69
|
export function FustenPage() {
|
|
67
70
|
const { activeId } = useFloridayConnection();
|
|
@@ -80,14 +83,7 @@ export function FustenPage() {
|
|
|
80
83
|
const [customPackages, setCustomPackages] = useState(null);
|
|
81
84
|
const [busyId, setBusyId] = useState(null);
|
|
82
85
|
const [amounts, setAmounts] = useState({});
|
|
83
|
-
const
|
|
84
|
-
const loadNegative = useCallback(async () => {
|
|
85
|
-
const { data } = await supabase.rpc("fust_negative_stock_report");
|
|
86
|
-
setNegative(Array.isArray(data) ? data : []);
|
|
87
|
-
}, []);
|
|
88
|
-
useEffect(() => {
|
|
89
|
-
void loadNegative();
|
|
90
|
-
}, [loadNegative, items]);
|
|
86
|
+
const negative = useMemo(() => fustTekorten(items), [items]);
|
|
91
87
|
const filtered = useMemo(() => {
|
|
92
88
|
const q = search.trim().toLowerCase();
|
|
93
89
|
if (!q)
|
|
@@ -107,7 +103,7 @@ export function FustenPage() {
|
|
|
107
103
|
return;
|
|
108
104
|
// Waarschuwen (niet blokkeren) bij afboeken boven de voorraad.
|
|
109
105
|
if (sign < 0 && f.stock - step < 0) {
|
|
110
|
-
toast.warning(`Tekort van ${Math.abs(f.stock - step).toLocaleString("nl-NL")} fusten na deze afboeking (${f
|
|
106
|
+
toast.warning(`Tekort van ${Math.abs(f.stock - step).toLocaleString("nl-NL")} fusten na deze afboeking (${fustNaam(f)}).`);
|
|
111
107
|
}
|
|
112
108
|
setBusyId(f.id);
|
|
113
109
|
setAmounts((a) => ({ ...a, [f.id]: "" }));
|
|
@@ -116,10 +112,9 @@ export function FustenPage() {
|
|
|
116
112
|
}
|
|
117
113
|
finally {
|
|
118
114
|
setBusyId(null);
|
|
119
|
-
void loadNegative();
|
|
120
115
|
}
|
|
121
116
|
};
|
|
122
|
-
return (_jsxs("main", { className: "p-6 space-y-6", children: [_jsxs("header", { className: "grid grid-cols-[minmax(0,1fr)_auto] items-start gap-4", children: [_jsxs("div", { className: "min-w-0", children: [_jsx("h1", { className: "text-2xl font-bold tracking-tight truncate", children: "Fusten" }), _jsx("p", { className: "text-sm text-muted-foreground mt-1", children: "Alle fusttypen worden automatisch afgeleid uit de beladingen van je producten." })] }), _jsxs(Button, { variant: "outline", size: "sm", onClick: () => void sync(), disabled: syncing, children: [_jsx(RefreshCw, { className: `mr-2 h-4 w-4 ${syncing ? "animate-spin" : ""}` }), "Synchroniseren"] })] }), error && (_jsxs("div", { className: "rounded-md border border-destructive/40 bg-destructive/10 text-destructive p-3 text-sm", children: ["Fusten laden mislukt: ", error] })), floricodeError && (_jsxs("div", { className: "rounded-md border border-amber-500/40 bg-amber-500/10 p-3 text-sm", children: ["Floricode-fustcodes niet leesbaar: ", floricodeError, ". Alleen handmatig ingevoerde maten worden getoond."] })), negative.length > 0 && (_jsxs("div", { className: "rounded-md border border-destructive/40 bg-destructive/10 p-3 text-sm text-destructive space-y-1", children: [_jsxs("p", { className: "font-medium", children: [negative.length, " fusttype", negative.length === 1 ? " heeft" : "n hebben", " een tekort."] }), _jsx("p", { className: "text-foreground/80", children: "Er is meer uitgeleverd dan ingeboekt. Controleer
|
|
117
|
+
return (_jsxs("main", { className: "p-6 space-y-6", children: [_jsxs("header", { className: "grid grid-cols-[minmax(0,1fr)_auto] items-start gap-4", children: [_jsxs("div", { className: "min-w-0", children: [_jsx("h1", { className: "text-2xl font-bold tracking-tight truncate", children: "Fusten" }), _jsx("p", { className: "text-sm text-muted-foreground mt-1", children: "Alle fusttypen worden automatisch afgeleid uit de beladingen van je producten." })] }), _jsxs(Button, { variant: "outline", size: "sm", onClick: () => void sync(), disabled: syncing, children: [_jsx(RefreshCw, { className: `mr-2 h-4 w-4 ${syncing ? "animate-spin" : ""}` }), "Synchroniseren"] })] }), error && (_jsxs("div", { className: "rounded-md border border-destructive/40 bg-destructive/10 text-destructive p-3 text-sm", children: ["Fusten laden mislukt: ", error] })), floricodeError && (_jsxs("div", { className: "rounded-md border border-amber-500/40 bg-amber-500/10 p-3 text-sm", children: ["Floricode-fustcodes niet leesbaar: ", floricodeError, ". Alleen handmatig ingevoerde maten worden getoond."] })), negative.length > 0 && (_jsxs("div", { className: "rounded-md border border-destructive/40 bg-destructive/10 p-3 text-sm text-destructive space-y-1", children: [_jsxs("p", { className: "font-medium", children: [negative.length, " fusttype", negative.length === 1 ? " heeft" : "n hebben", " een tekort."] }), _jsx("p", { className: "text-foreground/80", children: "Er is sinds het startmoment meer uitgeleverd dan ingeboekt. Alleen fusten waarvan je het saldo bijhoudt staan hier. Controleer ontvangsten en retouren, of boek de werkelijke telling bij." }), _jsx("ul", { className: "text-xs text-foreground/80", children: negative.map((n) => (_jsxs("li", { children: [fustNaam(n), ": tekort van ", Math.abs(n.stock).toLocaleString("nl-NL"), " fusten"] }, n.id))) })] })), _jsx(FustStartmomentCard, { items: items, onSaved: () => void sync() }), _jsxs("div", { className: "grid gap-4 sm:grid-cols-3", children: [_jsxs(Card, { className: "p-4", children: [_jsx("p", { className: "text-sm text-muted-foreground", children: "Fusttypen" }), _jsx("p", { className: "mt-2 text-3xl font-bold tabular-nums", children: totals.types })] }), _jsxs(Card, { className: "p-4", children: [_jsx("p", { className: "text-sm text-muted-foreground", children: "Totale fustvoorraad" }), _jsx("p", { className: "mt-2 text-3xl font-bold tabular-nums", children: totals.stock })] }), _jsxs(Card, { className: "p-4", children: [_jsx("p", { className: "text-sm text-muted-foreground", children: "Onder minimum" }), _jsx("p", { className: "mt-2 text-3xl font-bold tabular-nums", children: totals.belowMin })] })] }), _jsxs("div", { className: "relative max-w-sm", children: [_jsx(Search, { className: "absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" }), _jsx(Input, { value: search, onChange: (e) => setSearch(e.target.value), placeholder: "Zoek fust\u2026", className: "pl-9" })] }), _jsx(Card, { className: "overflow-hidden", children: loading ? (_jsx("p", { className: "p-6 text-sm text-muted-foreground", children: "Fusten laden\u2026" })) : filtered.length === 0 ? (_jsxs("div", { className: "p-6 text-sm text-muted-foreground flex items-center gap-2", children: [_jsx(Boxes, { className: "h-4 w-4" }), "Nog geen fusttypen gevonden. Voeg een belading toe aan een product."] })) : (_jsx("div", { className: "overflow-x-auto", children: _jsxs("table", { className: "w-full text-sm", children: [_jsx("thead", { className: "bg-muted/50 text-left", children: _jsxs("tr", { children: [_jsx("th", { className: "p-3", children: "Fust" }), _jsx("th", { className: "p-3", children: "Bron" }), _jsx("th", { className: "p-3", children: "Afmeting (L \u00D7 B \u00D7 H)" }), _jsx("th", { className: "p-3 text-right", children: "Gebruikt door" }), _jsx("th", { className: "p-3 text-right", children: "Voorraad" }), _jsx("th", { className: "p-3 text-right", children: "Minimum" }), _jsx("th", { className: "p-3 text-right", children: "Aanpassen" })] }) }), _jsx("tbody", { children: filtered.map((f) => (_jsxs("tr", { className: "border-t border-border align-top", children: [_jsxs("td", { className: "p-3", children: [_jsx("div", { className: "font-medium", children: fustNaam(f) }), f.eigen_naam?.trim() && _jsx("div", { className: "text-xs text-muted-foreground", children: f.name }), _jsx("div", { className: "text-xs text-muted-foreground", children: f.vbn_code ? `VBN ${f.vbn_code}` : f.fust_key }), !f.in_use && (_jsx(Badge, { variant: "outline", className: "mt-1", children: "Niet meer in gebruik" })), _jsx(FustInstellingenCell, { fust: f, onSaved: () => void sync() }), _jsx("div", { className: "mt-1", children: _jsx(MutationHistory, { fust: f }) })] }), _jsx("td", { className: "p-3", children: f.source === "SUPPLIER" ? "Leverancier" : "VBN" }), _jsx("td", { className: "p-3 text-sm", children: _jsx(DimensionCell, { fust: f, floricode: floricode, customPackages: customPackages, onSave: (v) => patch(f.id, v) }) }), _jsx("td", { className: "p-3 text-right tabular-nums", children: f.used_by_count }), _jsx("td", { className: "p-3 text-right", children: f.stock < 0 && saldoBijhouden(f) ? (_jsxs("span", { className: "tabular-nums font-semibold text-rose-600", title: "Er is meer uitgeleverd dan ingeboekt. Boek ontvangen of geretourneerde fusten bij.", children: ["Tekort van ", Math.abs(f.stock).toLocaleString("nl-NL"), " fusten"] })) : (_jsx("span", { className: `tabular-nums font-semibold ${f.minimum_stock > 0 && f.stock < f.minimum_stock
|
|
123
118
|
? "text-rose-600"
|
|
124
119
|
: ""}`, children: f.stock })) }), _jsx("td", { className: "p-3 text-right", children: _jsx(Input, { type: "number", className: "h-8 w-28 text-right", defaultValue: f.minimum_stock, onBlur: (e) => {
|
|
125
120
|
const v = Math.max(0, Math.trunc(Number(e.target.value) || 0));
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/** Automatisch aanmaken van Floriday-artikelen: wat nog open staat en wat handmatig moet. */
|
|
2
|
+
export declare function KoppelingStatusPanel({ connectionId, onChanged }: {
|
|
3
|
+
connectionId: string | null;
|
|
4
|
+
onChanged?: () => void;
|
|
5
|
+
}): import("react").JSX.Element | null;
|
|
6
|
+
//# sourceMappingURL=KoppelingStatusPanel.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"KoppelingStatusPanel.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/KoppelingStatusPanel.tsx"],"names":[],"mappings":"AAYA,6FAA6F;AAC7F,wBAAgB,oBAAoB,CAAC,EAAE,YAAY,EAAE,SAAS,EAAE,EAAE;IAAE,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,IAAI,CAAA;CAAE,sCAqExH"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useCallback, useEffect, useState } from "react";
|
|
3
|
+
import { Button, Card, toast } from "@flowselections/core";
|
|
4
|
+
import { supabase } from "../../integrations/supabase/client.js";
|
|
5
|
+
/** Automatisch aanmaken van Floriday-artikelen: wat nog open staat en wat handmatig moet. */
|
|
6
|
+
export function KoppelingStatusPanel({ connectionId, onChanged }) {
|
|
7
|
+
const [check, setCheck] = useState(null);
|
|
8
|
+
const [twijfel, setTwijfel] = useState([]);
|
|
9
|
+
const [busy, setBusy] = useState(false);
|
|
10
|
+
const load = useCallback(async () => {
|
|
11
|
+
const [c, t] = await Promise.all([
|
|
12
|
+
supabase.rpc("voorraad_koppeling_check"),
|
|
13
|
+
supabase.from("voorraad_koppel_twijfel").select("floriday_id, naam, reden").is("opgelost_at", null).order("laatst_gezien", { ascending: false }).limit(50),
|
|
14
|
+
]);
|
|
15
|
+
if (c.error)
|
|
16
|
+
toast.error(c.error.message);
|
|
17
|
+
else
|
|
18
|
+
setCheck(c.data);
|
|
19
|
+
setTwijfel((t.data ?? []));
|
|
20
|
+
}, []);
|
|
21
|
+
useEffect(() => { void load(); }, [load]);
|
|
22
|
+
const nuControleren = async () => {
|
|
23
|
+
setBusy(true);
|
|
24
|
+
try {
|
|
25
|
+
const { data, error } = await supabase.rpc("voorraad_floriday_artikelen_aanmaken", {
|
|
26
|
+
p_connection_id: connectionId, p_limiet: 200, p_uitvoeren: true, p_max_seconden: 20,
|
|
27
|
+
});
|
|
28
|
+
if (error)
|
|
29
|
+
throw new Error(error.message);
|
|
30
|
+
toast.success(`${data?.aangemaakt ?? 0} aangemaakt, ${data?.gekoppeld ?? 0} gekoppeld, ${data?.twijfel ?? 0} twijfelgevallen${data?.resterend ? `, ${data.resterend} nog te doen` : ""}.`);
|
|
31
|
+
await load();
|
|
32
|
+
onChanged?.();
|
|
33
|
+
}
|
|
34
|
+
catch (e) {
|
|
35
|
+
toast.error(e.message);
|
|
36
|
+
}
|
|
37
|
+
finally {
|
|
38
|
+
setBusy(false);
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
if (!check)
|
|
42
|
+
return null;
|
|
43
|
+
const niets = check.fouten.length === 0 && check.floriday_zonder_eigen_artikel === 0 && twijfel.length === 0;
|
|
44
|
+
return (_jsxs(Card, { className: "p-4 space-y-2 text-sm", children: [_jsxs("div", { className: "flex items-start justify-between gap-3", children: [_jsxs("div", { children: [_jsx("p", { className: "font-medium", children: "Floriday-artikelen automatisch overnemen" }), _jsxs("p", { className: "text-muted-foreground", children: [niets
|
|
45
|
+
? "Alle Floriday-artikelen hebben een eigen artikel."
|
|
46
|
+
: `${check.floriday_zonder_eigen_artikel} Floriday-artikelen zonder eigen artikel, ${twijfel.length} twijfelgevallen.`, " ", "Nieuwe artikelen starten met voorraad 0 tot je telt of importeert."] })] }), _jsx(Button, { size: "sm", variant: "outline", onClick: () => void nuControleren(), disabled: busy, children: busy ? "Bezig…" : "Nu controleren" })] }), check.fouten.map((f) => (_jsx("p", { className: "rounded-md border border-destructive/40 bg-destructive/10 p-2 text-destructive", children: f }, f))), twijfel.length > 0 && (_jsxs("div", { children: [_jsx("p", { className: "font-medium", children: "Kies zelf het juiste artikel" }), _jsx("ul", { className: "mt-1 divide-y divide-border rounded-md border border-border", children: twijfel.map((t) => (_jsxs("li", { className: "px-3 py-1.5", children: [_jsx("span", { className: "font-medium", children: t.naam ?? t.floriday_id }), _jsxs("span", { className: "text-muted-foreground", children: [" \u2014 ", t.reden] })] }, t.floriday_id))) })] }))] }));
|
|
47
|
+
}
|
|
@@ -64,7 +64,7 @@ export function NieuwArtikelPage() {
|
|
|
64
64
|
void (async () => {
|
|
65
65
|
const { data } = await supabase
|
|
66
66
|
.from("fust_types")
|
|
67
|
-
.select("name, vbn_code")
|
|
67
|
+
.select("name, eigen_naam, vbn_code")
|
|
68
68
|
.is("merged_into", null)
|
|
69
69
|
.not("vbn_code", "is", null)
|
|
70
70
|
.order("name");
|
|
@@ -76,7 +76,7 @@ export function NieuwArtikelPage() {
|
|
|
76
76
|
const v = Number(r.vbn_code);
|
|
77
77
|
if (v > 0 && !seen.has(v)) {
|
|
78
78
|
seen.add(v);
|
|
79
|
-
list.push({ vbn: v, name: String(r.name
|
|
79
|
+
list.push({ vbn: v, name: String((r.eigen_naam && String(r.eigen_naam).trim()) || r.name || `VBN ${v}`) });
|
|
80
80
|
}
|
|
81
81
|
}
|
|
82
82
|
setFusts(list);
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Telmoment vastleggen: orders vóór de teldag en orders die vóór het telmoment vertrokken
|
|
3
|
+
* zijn tellen niet mee; orders van de teldag die al weg zijn vink je aan.
|
|
4
|
+
*/
|
|
5
|
+
export declare function TelmomentButton({ onSaved }: {
|
|
6
|
+
onSaved?: () => void;
|
|
7
|
+
}): import("react").JSX.Element;
|
|
8
|
+
//# sourceMappingURL=TelmomentButton.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TelmomentButton.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/TelmomentButton.tsx"],"names":[],"mappings":"AA6BA;;;GAGG;AACH,wBAAgB,eAAe,CAAC,EAAE,OAAO,EAAE,EAAE;IAAE,OAAO,CAAC,EAAE,MAAM,IAAI,CAAA;CAAE,+BAkGpE"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { useState } from "react";
|
|
3
|
+
import { ClipboardCheck } from "lucide-react";
|
|
4
|
+
import { Button, Dialog, DialogContent, DialogHeader, DialogTitle, Input, toast, } from "@flowselections/core";
|
|
5
|
+
import { supabase } from "../../integrations/supabase/client.js";
|
|
6
|
+
import { TELMOMENT_LABEL } from "../../lib/fust-administratie.js";
|
|
7
|
+
function lokaleInvoer(d) {
|
|
8
|
+
const p = (n) => String(n).padStart(2, "0");
|
|
9
|
+
return `${d.getFullYear()}-${p(d.getMonth() + 1)}-${p(d.getDate())}T${p(d.getHours())}:${p(d.getMinutes())}`;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Telmoment vastleggen: orders vóór de teldag en orders die vóór het telmoment vertrokken
|
|
13
|
+
* zijn tellen niet mee; orders van de teldag die al weg zijn vink je aan.
|
|
14
|
+
*/
|
|
15
|
+
export function TelmomentButton({ onSaved }) {
|
|
16
|
+
const [open, setOpen] = useState(false);
|
|
17
|
+
const [moment, setMoment] = useState(() => lokaleInvoer(new Date()));
|
|
18
|
+
const [regels, setRegels] = useState(null);
|
|
19
|
+
const [alWeg, setAlWeg] = useState(new Set());
|
|
20
|
+
const [busy, setBusy] = useState(false);
|
|
21
|
+
const laden = async (m = moment) => {
|
|
22
|
+
setRegels(null);
|
|
23
|
+
const { data, error } = await supabase.rpc("voorraad_telmoment_lijst", { p_telmoment: new Date(m).toISOString(), p_keys: null });
|
|
24
|
+
if (error) {
|
|
25
|
+
toast.error(error.message);
|
|
26
|
+
setRegels([]);
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
setRegels((data?.regels ?? []));
|
|
30
|
+
setAlWeg(new Set());
|
|
31
|
+
};
|
|
32
|
+
const opslaan = async () => {
|
|
33
|
+
setBusy(true);
|
|
34
|
+
try {
|
|
35
|
+
const { data, error } = await supabase.rpc("voorraad_telmoment_opslaan", {
|
|
36
|
+
p_telmoment: new Date(moment).toISOString(), p_keys: null, p_al_weg: [...alWeg], p_notitie: null,
|
|
37
|
+
});
|
|
38
|
+
if (error)
|
|
39
|
+
throw new Error(error.message);
|
|
40
|
+
toast.success(`Telmoment vastgelegd: ${data?.buiten_telling ?? 0} orderregels tellen niet mee.`);
|
|
41
|
+
setOpen(false);
|
|
42
|
+
onSaved?.();
|
|
43
|
+
}
|
|
44
|
+
catch (e) {
|
|
45
|
+
toast.error(e.message);
|
|
46
|
+
}
|
|
47
|
+
finally {
|
|
48
|
+
setBusy(false);
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
const groepen = ["teldag_open", "vertrokken", "voor_teldag"];
|
|
52
|
+
return (_jsxs(_Fragment, { children: [_jsxs(Button, { variant: "outline", size: "sm", onClick: () => { setOpen(true); void laden(); }, children: [_jsx(ClipboardCheck, { className: "mr-2 h-4 w-4" }), "Telmoment"] }), _jsx(Dialog, { open: open, onOpenChange: setOpen, children: _jsxs(DialogContent, { className: "max-w-3xl", children: [_jsx(DialogHeader, { children: _jsx(DialogTitle, { children: "Telmoment vastleggen" }) }), _jsxs("div", { className: "flex items-end gap-2", children: [_jsxs("label", { className: "text-xs text-muted-foreground space-y-1", children: [_jsx("span", { className: "block", children: "Moment van tellen" }), _jsx(Input, { type: "datetime-local", className: "h-9 w-56", value: moment, onChange: (e) => setMoment(e.target.value) })] }), _jsx(Button, { size: "sm", variant: "outline", onClick: () => void laden(), children: "Ververs lijst" })] }), _jsxs("div", { className: "max-h-[50vh] overflow-y-auto space-y-4 text-sm", children: [regels === null && _jsx("p", { className: "text-muted-foreground", children: "Laden\u2026" }), regels !== null && regels.length === 0 && (_jsx("p", { className: "text-muted-foreground", children: "Geen openstaande orderregels tot en met de teldag." })), regels !== null && groepen.map((g) => {
|
|
53
|
+
const lijst = regels.filter((r) => r.categorie === g);
|
|
54
|
+
if (lijst.length === 0)
|
|
55
|
+
return null;
|
|
56
|
+
return (_jsxs("div", { children: [_jsxs("p", { className: "font-medium", children: [TELMOMENT_LABEL[g], " (", lijst.length, ")"] }), _jsx("ul", { className: "mt-1 divide-y divide-border rounded-md border border-border", children: lijst.map((r) => (_jsxs("li", { className: "flex items-center gap-3 px-3 py-1.5", children: [g === "teldag_open" && (_jsx("input", { type: "checkbox", "aria-label": "Is al weg", checked: alWeg.has(r.order_item_id), onChange: (e) => setAlWeg((s) => {
|
|
57
|
+
const n = new Set(s);
|
|
58
|
+
if (e.target.checked)
|
|
59
|
+
n.add(r.order_item_id);
|
|
60
|
+
else
|
|
61
|
+
n.delete(r.order_item_id);
|
|
62
|
+
return n;
|
|
63
|
+
}) })), _jsx("span", { className: "w-28 text-muted-foreground", children: r.order_number ?? "—" }), _jsxs("span", { className: "flex-1 truncate", children: [r.artikel ?? "—", " \u00B7 ", r.klant ?? ""] }), _jsx("span", { className: "tabular-nums", children: r.aantal })] }, r.order_item_id))) })] }, g));
|
|
64
|
+
})] }), _jsxs("div", { className: "flex justify-end gap-2", children: [_jsx(Button, { variant: "ghost", onClick: () => setOpen(false), children: "Annuleren" }), _jsx(Button, { onClick: () => void opslaan(), disabled: busy || regels === null, children: busy ? "Opslaan…" : "Telmoment opslaan" })] })] }) })] }));
|
|
65
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ValidationPage.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/ValidationPage.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ValidationPage.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/ValidationPage.tsx"],"names":[],"mappings":"AAQA,wBAAgB,cAAc,gCAkC7B"}
|
|
@@ -5,8 +5,9 @@ import { ArrowLeft } from "lucide-react";
|
|
|
5
5
|
import { useFloridayConnection } from "../../hooks/useFloridayConnection.js";
|
|
6
6
|
import { useVoorraadData } from "../../hooks/useVoorraadData.js";
|
|
7
7
|
import { ValidationOverviewTable } from "./ValidationOverviewTable.js";
|
|
8
|
+
import { KoppelingStatusPanel } from "./KoppelingStatusPanel.js";
|
|
8
9
|
export function ValidationPage() {
|
|
9
10
|
const { activeId } = useFloridayConnection();
|
|
10
11
|
const { items, loading, reload } = useVoorraadData(activeId);
|
|
11
|
-
return (_jsxs("main", { className: "p-6 space-y-4", children: [_jsx("div", { className: "flex items-center gap-2", children: _jsxs(Link, { to: "/voorraad", className: "inline-flex items-center gap-1 text-sm text-muted-foreground hover:text-foreground", children: [_jsx(ArrowLeft, { className: "h-4 w-4" }), "Terug naar voorraad"] }) }), _jsxs("div", { children: [_jsx("h1", { className: "text-xl font-semibold", children: "Floriday-koppeling \u2014 validatie" }), _jsx("p", { className: "text-sm text-muted-foreground", children: "Alle artikelen die nog niet succesvol met Floriday gekoppeld zijn." })] }), _jsx(Card, { className: "overflow-hidden", children: loading ? (_jsx("div", { className: "p-6 text-sm text-muted-foreground", children: "Laden\u2026" })) : (_jsx(ValidationOverviewTable, { items: items, connectionId: activeId, onChanged: reload })) })] }));
|
|
12
|
+
return (_jsxs("main", { className: "p-6 space-y-4", children: [_jsx("div", { className: "flex items-center gap-2", children: _jsxs(Link, { to: "/voorraad", className: "inline-flex items-center gap-1 text-sm text-muted-foreground hover:text-foreground", children: [_jsx(ArrowLeft, { className: "h-4 w-4" }), "Terug naar voorraad"] }) }), _jsxs("div", { children: [_jsx("h1", { className: "text-xl font-semibold", children: "Floriday-koppeling \u2014 validatie" }), _jsx("p", { className: "text-sm text-muted-foreground", children: "Alle artikelen die nog niet succesvol met Floriday gekoppeld zijn." })] }), _jsx(KoppelingStatusPanel, { connectionId: activeId, onChanged: reload }), _jsx(Card, { className: "overflow-hidden", children: loading ? (_jsx("div", { className: "p-6 text-sm text-muted-foreground", children: "Laden\u2026" })) : (_jsx(ValidationOverviewTable, { items: items, connectionId: activeId, onChanged: reload })) })] }));
|
|
12
13
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VoorraadPage.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/VoorraadPage.tsx"],"names":[],"mappings":"AA8BA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;
|
|
1
|
+
{"version":3,"file":"VoorraadPage.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/VoorraadPage.tsx"],"names":[],"mappings":"AA8BA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAoF3D;;;GAGG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,SAAS,EAAE,UAAU,GAAG,mBAAmB,GAAG,aAAa,CAAC,CAAC;;;;;;;EAgBzG;AAED,wBAAgB,YAAY,gCAuW3B"}
|
|
@@ -15,6 +15,7 @@ import { StockIntegrityAlert } from "./StockIntegrityAlert.js";
|
|
|
15
15
|
import { SupplyReadScopeAlert } from "./SupplyReadScopeAlert.js";
|
|
16
16
|
import { ExcelExportButton } from "./ExcelExportButton.js";
|
|
17
17
|
import { StockBulkButtons } from "./StockBulkButtons.js";
|
|
18
|
+
import { TelmomentButton } from "./TelmomentButton.js";
|
|
18
19
|
import { VoorraadTable } from "./VoorraadTable.js";
|
|
19
20
|
import { VoorraadPagination } from "./VoorraadPagination.js";
|
|
20
21
|
import { StockHistoryChart } from "./StockHistoryChart.js";
|
|
@@ -156,7 +157,7 @@ export function VoorraadPage() {
|
|
|
156
157
|
return (_jsxs("button", { type: "button", onClick: () => setStockFilter(t.id), className: `relative py-3 px-3 text-sm font-medium whitespace-nowrap transition-colors ${active
|
|
157
158
|
? "text-foreground"
|
|
158
159
|
: "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));
|
|
159
|
-
}) }), _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
|
|
160
|
+
}) }), _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(TelmomentButton, { onSaved: reload }), _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
|
|
160
161
|
? [...settingsByKey.values()].find((s) => s.product_id === drawerIds[0])
|
|
161
162
|
: undefined, onSaved: () => {
|
|
162
163
|
reloadSettings();
|