@flowselections/floriday-voorraad 1.0.150 → 1.0.152
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/fs-ui/DataTable.d.ts +42 -0
- package/dist-lib/components/fs-ui/DataTable.d.ts.map +1 -0
- package/dist-lib/components/fs-ui/DataTable.js +68 -0
- package/dist-lib/components/fs-ui/EmptyState.d.ts +9 -0
- package/dist-lib/components/fs-ui/EmptyState.d.ts.map +1 -0
- package/dist-lib/components/fs-ui/EmptyState.js +5 -0
- package/dist-lib/components/fs-ui/FilterChips.d.ts +13 -0
- package/dist-lib/components/fs-ui/FilterChips.d.ts.map +1 -0
- package/dist-lib/components/fs-ui/FilterChips.js +8 -0
- package/dist-lib/components/fs-ui/HoverTip.d.ts +15 -0
- package/dist-lib/components/fs-ui/HoverTip.d.ts.map +1 -0
- package/dist-lib/components/fs-ui/HoverTip.js +24 -0
- package/dist-lib/components/fs-ui/InlineAlert.d.ts +12 -0
- package/dist-lib/components/fs-ui/InlineAlert.d.ts.map +1 -0
- package/dist-lib/components/fs-ui/InlineAlert.js +15 -0
- package/dist-lib/components/fs-ui/KpiRow.d.ts +10 -0
- package/dist-lib/components/fs-ui/KpiRow.d.ts.map +1 -0
- package/dist-lib/components/fs-ui/KpiRow.js +8 -0
- package/dist-lib/components/fs-ui/KpiTile.d.ts +21 -0
- package/dist-lib/components/fs-ui/KpiTile.d.ts.map +1 -0
- package/dist-lib/components/fs-ui/KpiTile.js +40 -0
- package/dist-lib/components/fs-ui/MobilePrimaryAction.d.ts +13 -0
- package/dist-lib/components/fs-ui/MobilePrimaryAction.d.ts.map +1 -0
- package/dist-lib/components/fs-ui/MobilePrimaryAction.js +10 -0
- package/dist-lib/components/fs-ui/PageHeader.d.ts +18 -0
- package/dist-lib/components/fs-ui/PageHeader.d.ts.map +1 -0
- package/dist-lib/components/fs-ui/PageHeader.js +8 -0
- package/dist-lib/components/fs-ui/PillTabs.d.ts +19 -0
- package/dist-lib/components/fs-ui/PillTabs.d.ts.map +1 -0
- package/dist-lib/components/fs-ui/PillTabs.js +18 -0
- package/dist-lib/components/fs-ui/SearchInput.d.ts +14 -0
- package/dist-lib/components/fs-ui/SearchInput.d.ts.map +1 -0
- package/dist-lib/components/fs-ui/SearchInput.js +51 -0
- package/dist-lib/components/fs-ui/StatusIcon.d.ts +13 -0
- package/dist-lib/components/fs-ui/StatusIcon.d.ts.map +1 -0
- package/dist-lib/components/fs-ui/StatusIcon.js +15 -0
- package/dist-lib/components/fs-ui/TableSkeleton.d.ts +7 -0
- package/dist-lib/components/fs-ui/TableSkeleton.d.ts.map +1 -0
- package/dist-lib/components/fs-ui/TableSkeleton.js +6 -0
- package/dist-lib/components/fs-ui/Toolbar.d.ts +8 -0
- package/dist-lib/components/fs-ui/Toolbar.d.ts.map +1 -0
- package/dist-lib/components/fs-ui/Toolbar.js +5 -0
- package/dist-lib/components/fs-ui/UpdatedAt.d.ts +6 -0
- package/dist-lib/components/fs-ui/UpdatedAt.d.ts.map +1 -0
- package/dist-lib/components/fs-ui/UpdatedAt.js +10 -0
- package/dist-lib/components/voorraad/ArtikelCompositionCard.d.ts.map +1 -1
- package/dist-lib/components/voorraad/ArtikelCompositionCard.js +46 -15
- package/dist-lib/components/voorraad/ArtikelWizard.js +4 -4
- package/dist-lib/components/voorraad/FloridayConnectionNotice.d.ts +6 -6
- package/dist-lib/components/voorraad/FloridayConnectionNotice.d.ts.map +1 -1
- package/dist-lib/components/voorraad/FloridayConnectionNotice.js +10 -12
- package/dist-lib/components/voorraad/FustenPage.d.ts.map +1 -1
- package/dist-lib/components/voorraad/FustenPage.js +4 -2
- package/dist-lib/components/voorraad/NieuwArtikelPage.d.ts.map +1 -1
- package/dist-lib/components/voorraad/NieuwArtikelPage.js +2 -1
- package/dist-lib/components/voorraad/PublicatiesPage.d.ts.map +1 -1
- package/dist-lib/components/voorraad/PublicatiesPage.js +2 -1
- package/dist-lib/components/voorraad/Sneltoetsen.d.ts +14 -0
- package/dist-lib/components/voorraad/Sneltoetsen.d.ts.map +1 -0
- package/dist-lib/components/voorraad/Sneltoetsen.js +45 -0
- package/dist-lib/components/voorraad/StockIntegrityAlert.d.ts.map +1 -1
- package/dist-lib/components/voorraad/StockIntegrityAlert.js +9 -2
- package/dist-lib/components/voorraad/StockPushMonitor.js +2 -2
- package/dist-lib/components/voorraad/TelmomentButton.d.ts +5 -1
- package/dist-lib/components/voorraad/TelmomentButton.d.ts.map +1 -1
- package/dist-lib/components/voorraad/TelmomentButton.js +13 -6
- package/dist-lib/components/voorraad/ValidationOverviewTable.js +9 -7
- package/dist-lib/components/voorraad/ValidationPage.d.ts +1 -0
- package/dist-lib/components/voorraad/ValidationPage.d.ts.map +1 -1
- package/dist-lib/components/voorraad/ValidationPage.js +33 -5
- package/dist-lib/components/voorraad/VoorraadBeheerCard.d.ts +4 -1
- package/dist-lib/components/voorraad/VoorraadBeheerCard.d.ts.map +1 -1
- package/dist-lib/components/voorraad/VoorraadBeheerCard.js +26 -5
- package/dist-lib/components/voorraad/VoorraadPage.d.ts.map +1 -1
- package/dist-lib/components/voorraad/VoorraadPage.js +48 -34
- package/dist-lib/components/voorraad/VoorraadSturingCard.js +1 -1
- package/dist-lib/components/voorraad/artikel/ArtikelPagina.d.ts.map +1 -1
- package/dist-lib/components/voorraad/artikel/ArtikelPagina.js +28 -18
- package/dist-lib/components/voorraad/lijst/VoorraadLijstTabel.d.ts +8 -2
- package/dist-lib/components/voorraad/lijst/VoorraadLijstTabel.d.ts.map +1 -1
- package/dist-lib/components/voorraad/lijst/VoorraadLijstTabel.js +62 -27
- package/dist-lib/hooks/useLocalTradeItems.d.ts +14 -0
- package/dist-lib/hooks/useLocalTradeItems.d.ts.map +1 -1
- package/dist-lib/hooks/useLocalTradeItems.js +5 -3
- package/dist-lib/hooks/useTradeItemStock.d.ts.map +1 -1
- package/dist-lib/hooks/useTradeItemStock.js +6 -3
- package/dist-lib/hooks/useVoorraadData.d.ts.map +1 -1
- package/dist-lib/hooks/useVoorraadData.js +16 -8
- package/dist-lib/hooks/useVoorraadLijst.d.ts +10 -0
- package/dist-lib/hooks/useVoorraadLijst.d.ts.map +1 -1
- package/dist-lib/hooks/useVoorraadLijst.js +22 -4
- package/dist-lib/integrations/supabase/types.d.ts +59 -0
- package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
- package/dist-lib/lib/alle-rijen.d.ts +15 -0
- package/dist-lib/lib/alle-rijen.d.ts.map +1 -0
- package/dist-lib/lib/alle-rijen.js +17 -0
- package/dist-lib/lib/db-error-message.d.ts.map +1 -1
- package/dist-lib/lib/db-error-message.js +3 -1
- package/package.json +2 -1
- package/sql/floriday-voorraad-schema.sql +315 -39
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
import { useState } from "react";
|
|
2
|
+
import { useEffect, useState } from "react";
|
|
3
3
|
import { ClipboardCheck } from "lucide-react";
|
|
4
4
|
import { Button, Dialog, DialogContent, DialogHeader, DialogTitle, Input, toast, } from "@flowselections/core";
|
|
5
5
|
import { supabase } from "../../integrations/supabase/client.js";
|
|
@@ -12,8 +12,10 @@ function lokaleInvoer(d) {
|
|
|
12
12
|
* Telmoment vastleggen: orders vóór de teldag en orders die vóór het telmoment vertrokken
|
|
13
13
|
* zijn tellen niet mee; orders van de teldag die al weg zijn vink je aan.
|
|
14
14
|
*/
|
|
15
|
-
export function TelmomentButton({ onSaved }) {
|
|
16
|
-
const [
|
|
15
|
+
export function TelmomentButton({ onSaved, open: openVan, onOpenChange, verbergKnop }) {
|
|
16
|
+
const [openEigen, setOpenEigen] = useState(false);
|
|
17
|
+
const open = openVan ?? openEigen;
|
|
18
|
+
const setOpen = (o) => { setOpenEigen(o); onOpenChange?.(o); };
|
|
17
19
|
const [moment, setMoment] = useState(() => lokaleInvoer(new Date()));
|
|
18
20
|
const [regels, setRegels] = useState(null);
|
|
19
21
|
const [alWeg, setAlWeg] = useState(new Set());
|
|
@@ -22,7 +24,8 @@ export function TelmomentButton({ onSaved }) {
|
|
|
22
24
|
setRegels(null);
|
|
23
25
|
const { data, error } = await supabase.rpc("voorraad_telmoment_lijst", { p_telmoment: new Date(m).toISOString(), p_keys: null });
|
|
24
26
|
if (error) {
|
|
25
|
-
|
|
27
|
+
console.warn(error);
|
|
28
|
+
toast.error("De lijst kon niet geladen worden. Probeer opnieuw.");
|
|
26
29
|
setRegels([]);
|
|
27
30
|
return;
|
|
28
31
|
}
|
|
@@ -42,14 +45,18 @@ export function TelmomentButton({ onSaved }) {
|
|
|
42
45
|
onSaved?.();
|
|
43
46
|
}
|
|
44
47
|
catch (e) {
|
|
45
|
-
|
|
48
|
+
console.warn(e);
|
|
49
|
+
toast.error("Telmoment opslaan lukt niet. Probeer opnieuw.");
|
|
46
50
|
}
|
|
47
51
|
finally {
|
|
48
52
|
setBusy(false);
|
|
49
53
|
}
|
|
50
54
|
};
|
|
55
|
+
// Van buitenaf geopend: lijst laden.
|
|
56
|
+
useEffect(() => { if (openVan)
|
|
57
|
+
void laden(); }, [openVan]); // eslint-disable-line react-hooks/exhaustive-deps
|
|
51
58
|
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) => {
|
|
59
|
+
return (_jsxs(_Fragment, { children: [!verbergKnop && (_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
60
|
const lijst = regels.filter((r) => r.categorie === g);
|
|
54
61
|
if (lijst.length === 0)
|
|
55
62
|
return null;
|
|
@@ -33,13 +33,14 @@ export function ValidationOverviewTable({ items, connectionId, onChanged, }) {
|
|
|
33
33
|
else {
|
|
34
34
|
const total = (res.linked ?? 0) + (res.repaired ?? 0);
|
|
35
35
|
toast.success(total > 0
|
|
36
|
-
? `${total}
|
|
36
|
+
? `${total} artikel(en) opnieuw gekoppeld aan Floriday`
|
|
37
37
|
: "Geen extra koppelingen gevonden");
|
|
38
38
|
}
|
|
39
39
|
await onChanged();
|
|
40
40
|
}
|
|
41
41
|
catch (e) {
|
|
42
|
-
|
|
42
|
+
console.warn(e);
|
|
43
|
+
toast.error("Automatisch koppelen is niet gelukt. Probeer het later opnieuw.");
|
|
43
44
|
}
|
|
44
45
|
finally {
|
|
45
46
|
setRelinking(false);
|
|
@@ -74,25 +75,26 @@ export function ValidationOverviewTable({ items, connectionId, onChanged, }) {
|
|
|
74
75
|
floridayLinkError: null,
|
|
75
76
|
lastFloridaySyncAt: new Date().toISOString(),
|
|
76
77
|
});
|
|
77
|
-
toast.success("
|
|
78
|
+
toast.success("Artikel staat nu op Floriday");
|
|
78
79
|
await onChanged();
|
|
79
80
|
}
|
|
80
81
|
catch (e) {
|
|
81
|
-
|
|
82
|
+
console.warn(e);
|
|
83
|
+
toast.error("Op Floriday zetten is niet gelukt. Probeer het later opnieuw.");
|
|
82
84
|
await onChanged();
|
|
83
85
|
}
|
|
84
86
|
};
|
|
85
87
|
const relinkButton = (_jsxs(Button, { size: "sm", variant: "outline", onClick: runRelink, disabled: relinking, children: [relinking ? (_jsx(Loader2, { className: "h-3 w-3 mr-1 animate-spin" })) : (_jsx(Link2, { className: "h-3 w-3 mr-1" })), "Nu opnieuw koppelen"] }));
|
|
86
88
|
if (rows.length === 0) {
|
|
87
|
-
return (_jsxs("div", { className: "p-8 text-center text-sm text-muted-foreground space-y-3", children: [_jsx("div", { children: "Alle
|
|
89
|
+
return (_jsxs("div", { className: "p-8 text-center text-sm text-muted-foreground space-y-3", children: [_jsx("div", { children: "Alle artikelen staan op Floriday." }), _jsx("div", { className: "flex justify-center", children: relinkButton })] }));
|
|
88
90
|
}
|
|
89
|
-
return (_jsxs("div", { className: "overflow-x-auto", children: [_jsxs("div", { className: "flex items-center justify-between gap-2 p-3 border-b", children: [_jsx("span", { className: "text-xs text-muted-foreground", children: "Koppelen gebeurt automatisch zodra Floriday-artikelen binnenkomen en elk uur op de achtergrond." }), relinkButton] }), _jsxs("table", { className: "w-full text-sm", children: [_jsx("thead", { className: "bg-muted/
|
|
91
|
+
return (_jsxs("div", { className: "overflow-x-auto", children: [_jsxs("div", { className: "flex items-center justify-between gap-2 p-3 border-b", children: [_jsx("span", { className: "text-xs text-muted-foreground", children: "Koppelen gebeurt automatisch zodra Floriday-artikelen binnenkomen en elk uur op de achtergrond." }), relinkButton] }), _jsxs("table", { className: "w-full text-sm", children: [_jsx("thead", { className: "bg-muted/40 text-left text-xs font-medium text-muted-foreground", children: _jsxs("tr", { children: [_jsx("th", { className: "p-3", children: "Artikelcode" }), _jsx("th", { className: "p-3", children: "Artikelnaam" }), _jsx("th", { className: "p-3", children: "Floriday-status" }), _jsx("th", { className: "p-3", children: "Reden" }), _jsx("th", { className: "p-3", children: "Ontbrekende Floriday-velden" }), _jsx("th", { className: "p-3 text-right", children: "Acties" })] }) }), _jsx("tbody", { children: rows.map((it) => {
|
|
90
92
|
const payload = it.raw?.custom_fields?.floriday_payload ??
|
|
91
93
|
it.raw?.floriday_payload ??
|
|
92
94
|
it.raw?.data ??
|
|
93
95
|
{};
|
|
94
96
|
const v = validateDraftForPublish(conceptMetArtikel(payload, artikelVan(it)));
|
|
95
|
-
return (_jsxs("tr", { className: "border-t align-top", children: [_jsx("td", { className: "p-3
|
|
97
|
+
return (_jsxs("tr", { className: "border-t align-top", children: [_jsx("td", { className: "p-3 text-xs tabular-nums", children: it.articleNumber || "—" }), _jsx("td", { className: "p-3", children: it.name }), _jsx("td", { className: "p-3", children: _jsx(Badge, { variant: "outline", className: "rounded-full border-border bg-muted text-muted-foreground", children: "Niet actief op Floriday" }) }), _jsx("td", { className: "p-3 text-xs text-muted-foreground max-w-[260px]", children: reasonFor(it) }), _jsx("td", { className: "p-3", children: v.missing.length === 0 ? (_jsx("span", { className: "text-xs text-success-text", children: "Klaar om te activeren" })) : (_jsxs("span", { className: "text-xs text-muted-foreground", title: v.missing.join(", "), children: ["Nog nodig: ", v.missing.slice(0, 3).join(", ").toLowerCase(), v.missing.length > 3 ? ` en nog ${v.missing.length - 3} andere` : "", "."] })) }), _jsx("td", { className: "p-3", children: _jsxs("div", { className: "flex justify-end gap-1", children: [_jsxs(Button, { size: "sm", variant: "outline", onClick: () => {
|
|
96
98
|
const target = it.productId ?? it.localId ?? it.id;
|
|
97
99
|
navigate({ to: "/voorraad/$id", params: { id: target } });
|
|
98
100
|
}, children: [_jsx(Pencil, { className: "h-3 w-3 mr-1" }), "Bewerken"] }), _jsxs(Button, { size: "sm", onClick: () => publish(it), disabled: !v.ok, children: [_jsx(Send, { className: "h-3 w-3 mr-1" }), "Activeer op Floriday"] })] }) })] }, it.id));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"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":"AAiBA,oHAAoH;AACpH,wBAAgB,cAAc,gCA0C7B"}
|
|
@@ -1,13 +1,41 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useCallback, useEffect, useState } from "react";
|
|
2
3
|
import { Link } from "@tanstack/react-router";
|
|
3
|
-
import { Card } from "@flowselections/core";
|
|
4
|
+
import { Card, Skeleton } from "@flowselections/core";
|
|
4
5
|
import { ArrowLeft } from "lucide-react";
|
|
5
6
|
import { useFloridayConnection } from "../../hooks/useFloridayConnection.js";
|
|
6
|
-
import {
|
|
7
|
+
import { supabase } from "../../integrations/supabase/client.js";
|
|
8
|
+
import { productToTradeItem } from "../../hooks/useLocalTradeItems.js";
|
|
9
|
+
import { dbFoutTekst } from "../../lib/db-error-message.js";
|
|
7
10
|
import { ValidationOverviewTable } from "./ValidationOverviewTable.js";
|
|
8
|
-
import {
|
|
11
|
+
import { VoorraadPagination } from "./VoorraadPagination.js";
|
|
12
|
+
import { PageHeader } from "../fs-ui/PageHeader.js";
|
|
13
|
+
import { UpdatedAt } from "../fs-ui/UpdatedAt.js";
|
|
14
|
+
import { InlineAlert } from "../fs-ui/InlineAlert.js";
|
|
15
|
+
const GROOTTE = 50;
|
|
16
|
+
/** Floriday-controle: artikelen die nog niet op Floriday staan, per pagina van de server (geen limiet van 1000). */
|
|
9
17
|
export function ValidationPage() {
|
|
10
18
|
const { activeId } = useFloridayConnection();
|
|
11
|
-
const
|
|
12
|
-
|
|
19
|
+
const [pagina, setPagina] = useState(1);
|
|
20
|
+
const [items, setItems] = useState([]);
|
|
21
|
+
const [totaal, setTotaal] = useState(0);
|
|
22
|
+
const [loading, setLoading] = useState(true);
|
|
23
|
+
const [fout, setFout] = useState(null);
|
|
24
|
+
const [bijgewerkt, setBijgewerkt] = useState(null);
|
|
25
|
+
const reload = useCallback(async () => {
|
|
26
|
+
const { data, error } = await supabase.rpc("voorraad_validatie_pagina", { p_pagina: pagina, p_grootte: GROOTTE });
|
|
27
|
+
if (error) {
|
|
28
|
+
setFout(dbFoutTekst(error));
|
|
29
|
+
setLoading(false);
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
const r = data;
|
|
33
|
+
setItems((r.rijen ?? []).map(productToTradeItem));
|
|
34
|
+
setTotaal(Number(r.totaal ?? 0));
|
|
35
|
+
setFout(null);
|
|
36
|
+
setLoading(false);
|
|
37
|
+
setBijgewerkt(new Date());
|
|
38
|
+
}, [pagina]);
|
|
39
|
+
useEffect(() => { void reload(); }, [reload]);
|
|
40
|
+
return (_jsxs("main", { className: "flex flex-col gap-4 p-4 pb-24 sm:p-6 sm:pb-24", children: [_jsx(PageHeader, { crumb: _jsxs(Link, { to: "/voorraad", className: "inline-flex items-center gap-1 hover:text-foreground", children: [_jsx(ArrowLeft, { className: "h-4 w-4" }), " Voorraad"] }), title: "Floriday-controle", updated: _jsx(UpdatedAt, { at: bijgewerkt }), meta: _jsxs("span", { children: ["Artikelen die nog niet op Floriday staan", loading ? "" : ` · ${totaal.toLocaleString("nl-NL")}`] }) }), fout && _jsx(InlineAlert, { tone: "fout", children: fout }), _jsx(Card, { className: "overflow-hidden rounded-xl shadow-sm", children: loading ? (_jsx("div", { className: "space-y-2 p-4", "aria-busy": "true", children: Array.from({ length: 8 }).map((_, i) => _jsx(Skeleton, { className: "h-10 w-full rounded-lg" }, i)) })) : (_jsx(ValidationOverviewTable, { items: items, connectionId: activeId, onChanged: reload })) }), totaal > GROOTTE && (_jsx(VoorraadPagination, { page: pagina, pageSize: GROOTTE, pageSizeOptions: [GROOTTE], total: totaal, onPageChange: setPagina, onPageSizeChange: () => undefined }))] }));
|
|
13
41
|
}
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
-
/**
|
|
1
|
+
/**
|
|
2
|
+
* "Voorraad — Controle Floriday-artikelen": alleen voor admin en superadmin
|
|
3
|
+
* (`voorraad_is_beheerder` in de database). Inklapbaar; ververst na de knop en zolang een controle loopt.
|
|
4
|
+
*/
|
|
2
5
|
export declare function VoorraadBeheerCard(): import("react").JSX.Element | null;
|
|
3
6
|
//# sourceMappingURL=VoorraadBeheerCard.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VoorraadBeheerCard.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/VoorraadBeheerCard.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"VoorraadBeheerCard.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/VoorraadBeheerCard.tsx"],"names":[],"mappings":"AA8BA;;;GAGG;AACH,wBAAgB,kBAAkB,uCAoFjC"}
|
|
@@ -1,28 +1,48 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useCallback, useEffect, useState } from "react";
|
|
3
|
-
import { Button, Card,
|
|
2
|
+
import { lazy, Suspense, useCallback, useEffect, useState } from "react";
|
|
3
|
+
import { Button, Card, Skeleton, toast } from "@flowselections/core";
|
|
4
|
+
import { ChevronDown, RefreshCw } from "lucide-react";
|
|
4
5
|
import { supabase } from "../../integrations/supabase/client.js";
|
|
5
6
|
import { dbFoutTekst } from "../../lib/db-error-message.js";
|
|
6
7
|
import { toonVersieBeheer } from "../../lib/floriday-versie-ronde.js";
|
|
7
8
|
import { floridayVersiesControleren } from "../../lib/floriday-versie-ronde.functions.js";
|
|
9
|
+
import { useFloridayConnection } from "../../hooks/useFloridayConnection.js";
|
|
10
|
+
// Zware beheeronderdelen laden pas als je het blok openklapt.
|
|
11
|
+
const KoppelingStatusPanel = lazy(() => import("./KoppelingStatusPanel.js").then((m) => ({ default: m.KoppelingStatusPanel })));
|
|
12
|
+
const StockPushMonitor = lazy(() => import("./StockPushMonitor.js").then((m) => ({ default: m.StockPushMonitor })));
|
|
13
|
+
const FloridayTakeoverPanel = lazy(() => import("./FloridayTakeoverPanel.js").then((m) => ({ default: m.FloridayTakeoverPanel })));
|
|
8
14
|
const STATUS_TEKST = {
|
|
9
15
|
geslaagd: "geslaagd",
|
|
10
16
|
onvolledig: "nog niet compleet (wordt automatisch opnieuw geprobeerd)",
|
|
11
17
|
mislukt: "mislukt (wordt automatisch opnieuw geprobeerd)",
|
|
12
18
|
};
|
|
13
|
-
/**
|
|
19
|
+
/**
|
|
20
|
+
* "Voorraad — Controle Floriday-artikelen": alleen voor admin en superadmin
|
|
21
|
+
* (`voorraad_is_beheerder` in de database). Inklapbaar; ververst na de knop en zolang een controle loopt.
|
|
22
|
+
*/
|
|
14
23
|
export function VoorraadBeheerCard() {
|
|
15
24
|
const [status, setStatus] = useState(null);
|
|
16
25
|
const [bezig, setBezig] = useState(false);
|
|
26
|
+
const [open, setOpen] = useState(false);
|
|
27
|
+
const [versie, setVersie] = useState(0);
|
|
28
|
+
const conn = useFloridayConnection();
|
|
17
29
|
const laad = useCallback(async () => {
|
|
18
30
|
const { data, error } = await supabase.rpc("voorraad_floriday_versieronde_status");
|
|
19
31
|
if (error) {
|
|
32
|
+
console.warn("Beheerstatus voorraad niet geladen", error);
|
|
20
33
|
setStatus(null);
|
|
21
34
|
return;
|
|
22
35
|
}
|
|
23
36
|
setStatus(data);
|
|
24
37
|
}, []);
|
|
25
38
|
useEffect(() => { void laad(); }, [laad]);
|
|
39
|
+
// Stil verversen zolang een controle loopt.
|
|
40
|
+
useEffect(() => {
|
|
41
|
+
if (!status?.bezig)
|
|
42
|
+
return;
|
|
43
|
+
const t = setInterval(() => void laad(), 5000);
|
|
44
|
+
return () => clearInterval(t);
|
|
45
|
+
}, [status?.bezig, laad]);
|
|
26
46
|
if (!toonVersieBeheer(status) || !status?.heeft_koppeling)
|
|
27
47
|
return null;
|
|
28
48
|
const start = async () => {
|
|
@@ -30,15 +50,16 @@ export function VoorraadBeheerCard() {
|
|
|
30
50
|
try {
|
|
31
51
|
await floridayVersiesControleren();
|
|
32
52
|
toast.success("Controle gestart. Dit loopt op de achtergrond.");
|
|
33
|
-
await laad();
|
|
34
53
|
}
|
|
35
54
|
catch (e) {
|
|
36
55
|
toast.error(dbFoutTekst(e));
|
|
37
56
|
}
|
|
38
57
|
finally {
|
|
39
58
|
setBezig(false);
|
|
59
|
+
setVersie((v) => v + 1);
|
|
60
|
+
await laad();
|
|
40
61
|
}
|
|
41
62
|
};
|
|
42
63
|
const l = status.laatste;
|
|
43
|
-
return (
|
|
64
|
+
return (_jsxs(Card, { className: "overflow-hidden rounded-xl shadow-sm", children: [_jsxs("button", { type: "button", onClick: () => setOpen((o) => !o), "aria-expanded": open, className: "flex w-full items-center justify-between gap-3 px-5 py-4 text-left focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring", children: [_jsxs("div", { children: [_jsx("div", { className: "text-base font-semibold", children: "Voorraad \u2014 Controle Floriday-artikelen" }), _jsxs("div", { className: "text-xs text-muted-foreground", children: ["Alleen voor beheerders \u00B7 Laatste controle ", l ? new Date(l.uitgevoerd_at).toLocaleString("nl-NL") : "nog niet uitgevoerd"] })] }), _jsx(ChevronDown, { className: "h-4 w-4 shrink-0 transition-transform motion-reduce:transition-none " + (open ? "rotate-180" : ""), "aria-hidden": true })] }), open && (_jsxs("div", { className: "space-y-4 border-t border-border px-5 py-4 text-sm", children: [_jsxs("section", { className: "space-y-2", children: [_jsx("p", { className: "text-muted-foreground", children: "Loopt vanzelf elke nacht. Oude versies van Floriday-artikelen worden geen zichtbaar artikel." }), _jsxs("p", { children: ["Laatste controle:", " ", l ? `${new Date(l.uitgevoerd_at).toLocaleString("nl-NL")} — ${STATUS_TEKST[l.status] ?? "onbekend"}` : "nog niet uitgevoerd", status.bezig && " · nu bezig"] }), l?.melding && l.status !== "geslaagd" && _jsx("p", { className: "text-muted-foreground", children: l.melding }), _jsxs("p", { className: "tabular-nums", children: ["Vervangen in Floriday: ", status.vervangen ?? 0, " \u00B7 Vervallen: ", status.vervallen ?? 0, " \u00B7 Apart gezet (geen antwoord van Floriday): ", status.onbekend ?? 0] }), _jsxs(Button, { size: "sm", variant: "outline", className: "h-8", onClick: () => void start(), disabled: bezig || status.bezig, children: [_jsx(RefreshCw, { className: "mr-2 h-4 w-4 " + (bezig || status.bezig ? "animate-spin motion-reduce:animate-none" : "") }), bezig ? "Bezig…" : "Nu opnieuw controleren"] })] }), _jsx(Suspense, { fallback: _jsx(Skeleton, { className: "h-32 w-full rounded-xl" }), children: _jsxs("div", { className: "space-y-4", children: [_jsx(KoppelingStatusPanel, { connectionId: conn.activeId ?? null, onChanged: () => void laad() }), _jsx(StockPushMonitor, { connectionId: conn.activeId ?? null }), _jsx(FloridayTakeoverPanel, { connectionId: conn.activeId ?? null })] }, versie) })] }))] }));
|
|
44
65
|
}
|
|
@@ -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":"AAuEA,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAsDnD,wBAAgB,YAAY,gCA2N3B"}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { TwijfelKiezen } from "./artikel/TwijfelKiezen.js";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { Button,
|
|
6
|
-
import { AlertTriangle, Columns3, Plus,
|
|
3
|
+
import { useCallback, useEffect, useMemo, useState, useSyncExternalStore } from "react";
|
|
4
|
+
import { useNavigate } from "@tanstack/react-router";
|
|
5
|
+
import { Button, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger, } from "@flowselections/core";
|
|
6
|
+
import { AlertTriangle, Archive, ArrowDownToLine, ClipboardCheck, Columns3, Euro, HelpCircle, Layers, Link2Off, ListChecks, MoreHorizontal, Package, PackageSearch, PackageX, Plus, Send, TrendingDown, } from "lucide-react";
|
|
7
7
|
import { supabase } from "../../integrations/supabase/client.js";
|
|
8
8
|
import { pageRequestGuard } from "../../lib/request-guard.js";
|
|
9
9
|
import { useFloridayConnection } from "../../hooks/useFloridayConnection.js";
|
|
@@ -19,11 +19,33 @@ import { OPTIONELE_KOLOMMEN, VoorraadLijstTabel } from "./lijst/VoorraadLijstTab
|
|
|
19
19
|
import { AanpassenDialog, AfboekenDialog } from "./lijst/SnelActieDialogen.js";
|
|
20
20
|
import { LijstExportKnop } from "./lijst/LijstExportKnop.js";
|
|
21
21
|
import { ImporteerKnop } from "./lijst/ImporteerKnop.js";
|
|
22
|
+
import { Kbd, Sneltoetsen } from "./Sneltoetsen.js";
|
|
23
|
+
import { PageHeader } from "../fs-ui/PageHeader.js";
|
|
24
|
+
import { UpdatedAt } from "../fs-ui/UpdatedAt.js";
|
|
25
|
+
import { KpiRow } from "../fs-ui/KpiRow.js";
|
|
26
|
+
import { KpiTile } from "../fs-ui/KpiTile.js";
|
|
27
|
+
import { PillTabs } from "../fs-ui/PillTabs.js";
|
|
28
|
+
import { Toolbar } from "../fs-ui/Toolbar.js";
|
|
29
|
+
import { SearchInput } from "../fs-ui/SearchInput.js";
|
|
30
|
+
import { InlineAlert } from "../fs-ui/InlineAlert.js";
|
|
31
|
+
import { EmptyState } from "../fs-ui/EmptyState.js";
|
|
32
|
+
import { MobilePrimaryAction } from "../fs-ui/MobilePrimaryAction.js";
|
|
33
|
+
import { DataTable } from "../fs-ui/DataTable.js";
|
|
22
34
|
// Bestaande export blijft (tests en andere modules).
|
|
23
35
|
export { countStock } from "../../lib/count-stock.js";
|
|
24
36
|
const EMPTY_TRIPS = [];
|
|
25
37
|
const KOLOMMEN_KEY = "voorraad.lijst.kolommen.v2";
|
|
26
38
|
const STANDAARD_KOLOMMEN = ["inkomend", "prijs"];
|
|
39
|
+
const FILTER_ICON = {
|
|
40
|
+
alles: _jsx(Layers, {}),
|
|
41
|
+
onder_minimum: _jsx(TrendingDown, {}),
|
|
42
|
+
verpakking: _jsx(PackageX, {}),
|
|
43
|
+
niet_gekoppeld: _jsx(Link2Off, {}),
|
|
44
|
+
verwijderd_floriday: _jsx(Link2Off, {}),
|
|
45
|
+
prijs: _jsx(Euro, {}),
|
|
46
|
+
twijfel: _jsx(HelpCircle, {}),
|
|
47
|
+
gearchiveerd: _jsx(Archive, {}),
|
|
48
|
+
};
|
|
27
49
|
function useKolommen() {
|
|
28
50
|
const [set, setSet] = useState(new Set(STANDAARD_KOLOMMEN));
|
|
29
51
|
useEffect(() => {
|
|
@@ -68,7 +90,6 @@ export function VoorraadPage() {
|
|
|
68
90
|
const navigate = useNavigate();
|
|
69
91
|
const conn = useFloridayConnection();
|
|
70
92
|
const stockConnId = useStockContextId();
|
|
71
|
-
const [zoekInvoer, setZoekInvoer] = useState("");
|
|
72
93
|
const [zoek, setZoek] = useState("");
|
|
73
94
|
const [filter, setFilter] = useState("alles");
|
|
74
95
|
const [sorteer, setSorteer] = useState({ sort: "naam", oplopend: true });
|
|
@@ -76,34 +97,23 @@ export function VoorraadPage() {
|
|
|
76
97
|
const [grootte, setGrootte] = useState(25);
|
|
77
98
|
const [selected, setSelected] = useState(new Set());
|
|
78
99
|
const [drawerOpen, setDrawerOpen] = useState(false);
|
|
100
|
+
const [telmomentOpen, setTelmomentOpen] = useState(false);
|
|
79
101
|
const [aanpassen, setAanpassen] = useState(null);
|
|
80
102
|
const [afboeken, setAfboeken] = useState(null);
|
|
81
103
|
const { kolommen, toggle: toggleKolom } = useKolommen();
|
|
82
104
|
const guardTrips = useSyncExternalStore(pageRequestGuard.subscribe, () => pageRequestGuard.trips(), () => EMPTY_TRIPS);
|
|
83
|
-
//
|
|
84
|
-
useEffect(() => {
|
|
85
|
-
const t = setTimeout(() => setZoek(zoekInvoer), 300);
|
|
86
|
-
return () => clearTimeout(t);
|
|
87
|
-
}, [zoekInvoer]);
|
|
105
|
+
// Nieuwe zoekopdracht, filter of sortering begint op pagina 1.
|
|
88
106
|
useEffect(() => setPagina(1), [zoek, filter, sorteer, grootte]);
|
|
89
|
-
|
|
90
|
-
useEffect(() => {
|
|
91
|
-
const h = (e) => {
|
|
92
|
-
const t = e.target;
|
|
93
|
-
if (t && (t.tagName === "INPUT" || t.tagName === "TEXTAREA" || t.isContentEditable))
|
|
94
|
-
return;
|
|
95
|
-
if (e.key === "n" && !e.metaKey && !e.ctrlKey && !e.altKey)
|
|
96
|
-
navigate({ to: "/voorraad/nieuw" });
|
|
97
|
-
};
|
|
98
|
-
window.addEventListener("keydown", h);
|
|
99
|
-
return () => window.removeEventListener("keydown", h);
|
|
100
|
-
}, [navigate]);
|
|
107
|
+
const nieuw = useCallback(() => navigate({ to: "/voorraad/nieuw" }), [navigate]);
|
|
101
108
|
const vraag = { zoek, filter, sort: sorteer.sort, oplopend: sorteer.oplopend, pagina, grootte };
|
|
102
|
-
const { data, loading, error, reload } = useVoorraadLijst(vraag);
|
|
109
|
+
const { data, kpi, loading, error, reload } = useVoorraadLijst(vraag);
|
|
103
110
|
const heeftKoppeling = data?.heeft_koppeling ?? false;
|
|
104
111
|
const filters = useMemo(() => zichtbareFilters(heeftKoppeling), [heeftKoppeling]);
|
|
105
112
|
const melding = data ? meldingen(data) : [];
|
|
106
|
-
|
|
113
|
+
// Twijfelgevallen bestaan alleen bij een koppeling; tonen als er iets te kiezen is of als je er al staat.
|
|
114
|
+
const tabs = filters
|
|
115
|
+
.filter((f) => f !== "twijfel" || heeftKoppeling || (data?.tellers?.twijfel ?? 0) > 0 || filter === "twijfel")
|
|
116
|
+
.map((f) => ({ value: f, label: FILTER_LABEL[f], icon: FILTER_ICON[f], count: data?.tellers?.[f] ?? null }));
|
|
107
117
|
const toggleSelect = (id) => setSelected((cur) => {
|
|
108
118
|
const next = new Set(cur);
|
|
109
119
|
if (next.has(id))
|
|
@@ -113,16 +123,20 @@ export function VoorraadPage() {
|
|
|
113
123
|
return next;
|
|
114
124
|
});
|
|
115
125
|
const toggleAll = (ids) => setSelected((cur) => (ids.length > 0 && ids.every((id) => cur.has(id)) ? new Set() : new Set(ids)));
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
}) }), _jsxs("div", { className: "flex flex-wrap items-center gap-2 border-b p-3", children: [_jsxs("div", { className: "relative flex-1 min-w-[220px] max-w-md", children: [_jsx(Search, { className: "absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" }), _jsx(Input, { placeholder: "Zoek op naam, artikelnummer of barcode\u2026", className: "pl-9", value: zoekInvoer, onChange: (e) => setZoekInvoer(e.target.value), "aria-label": "Zoeken" })] }), _jsx("div", { className: "ml-auto", children: _jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsxs(Button, { variant: "outline", size: "sm", children: [_jsx(Columns3, { className: "mr-2 h-4 w-4" }), "Kolommen"] }) }), _jsxs(DropdownMenuContent, { align: "end", children: [_jsx(DropdownMenuLabel, { children: "Kolommen kiezen" }), OPTIONELE_KOLOMMEN.map((c) => (_jsx(DropdownMenuCheckboxItem, { checked: kolommen.has(c.key), onCheckedChange: () => toggleKolom(c.key), children: c.label }, c.key)))] })] }) })] }), selected.size > 0 && filter !== "twijfel" && (_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, " gekozen"] }), _jsx(Button, { size: "sm", onClick: () => setDrawerOpen(true), children: "Voorraadsturing instellen" }), _jsx(Button, { size: "sm", variant: "ghost", onClick: () => setSelected(new Set()), children: "Keuze wissen" })] })), _jsx("div", { className: "overflow-x-auto", children: filter === "twijfel" ? (_jsx(TwijfelTabel, { rijen: data?.twijfel_rijen ?? [], loading: loading, onGekozen: () => void reload() })) : (_jsx(VoorraadLijstTabel, { rijen: data?.rijen ?? [], loading: loading, sort: sorteer.sort, oplopend: sorteer.oplopend, onSort: (k) => setSorteer((cur) => volgendeSortering(cur, k)), kolommen: kolommen, selected: selected, onToggleSelect: toggleSelect, onToggleAll: toggleAll, onAanpassen: setAanpassen, onAfboeken: setAfboeken, heeftKoppeling: heeftKoppeling })) }), _jsx(VoorraadPagination, { total: data?.totaal ?? 0, page: pagina, pageSize: grootte, onPageChange: setPagina, onPageSizeChange: setGrootte })] }), _jsx(AanpassenDialog, { rij: aanpassen, onClose: () => setAanpassen(null), onDone: reload }), _jsx(AfboekenDialog, { rij: afboeken, connectionId: stockConnId, onClose: () => setAfboeken(null), onDone: reload }), _jsx(InventorySettingsDrawer, { open: drawerOpen, onOpenChange: setDrawerOpen, connectionId: conn.activeId ?? null, productIds: [...selected], onSaved: reload })] }));
|
|
126
|
+
const selectieBalk = selected.size > 0 && filter !== "twijfel";
|
|
127
|
+
const kpiLaden = kpi === null;
|
|
128
|
+
const leeg = zoek || filter !== "alles" ? (_jsx(EmptyState, { icon: _jsx(PackageSearch, { className: "h-6 w-6" }), title: "Niets gevonden", text: zoek ? `Geen artikelen voor “${zoek}”${filter !== "alles" ? ` in ${FILTER_LABEL[filter].toLowerCase()}` : ""}.` : "Geen artikelen in deze weergave.", action: _jsx(Button, { variant: "outline", size: "sm", onClick: () => { setZoek(""); setFilter("alles"); }, children: "Filters wissen" }) })) : (_jsx(EmptyState, { icon: _jsx(Package, { className: "h-6 w-6" }), title: "Nog geen artikelen", text: "Maak je eerste artikel aan of importeer een lijst.", action: _jsxs(Button, { size: "sm", onClick: nieuw, children: [_jsx(Plus, { className: "mr-2 h-4 w-4" }), "Nieuw artikel"] }) }));
|
|
129
|
+
return (_jsxs("main", { className: "flex flex-col gap-4 p-4 pb-24 sm:p-6 sm:pb-24", children: [_jsx(Sneltoetsen, { onNieuw: nieuw }), _jsx(PageHeader, { title: "Voorraad", updated: _jsx(UpdatedAt, { at: data?.bijgewerkt ?? null }), actions: _jsxs(_Fragment, { children: [_jsx(ImporteerKnop, { connectionId: conn.activeId ?? null, onImported: reload }), _jsx(LijstExportKnop, { vraag: { zoek, filter, sort: sorteer.sort, oplopend: sorteer.oplopend } }), _jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsxs(Button, { variant: "outline", size: "sm", className: "h-9", "aria-label": "Meer acties", children: [_jsx(MoreHorizontal, { className: "h-4 w-4 sm:mr-2" }), _jsx("span", { className: "hidden sm:inline", children: "Meer" })] }) }), _jsxs(DropdownMenuContent, { align: "end", className: "w-56", children: [_jsxs(DropdownMenuItem, { onSelect: () => setTelmomentOpen(true), children: [_jsx(ClipboardCheck, { className: "mr-2 h-4 w-4" }), "Telmoment vastleggen"] }), heeftKoppeling && (_jsxs(_Fragment, { children: [_jsx(DropdownMenuSeparator, {}), _jsxs(DropdownMenuItem, { onSelect: () => navigate({ to: "/voorraad/validatie" }), children: [_jsx(ListChecks, { className: "mr-2 h-4 w-4" }), "Controle koppeling"] }), _jsxs(DropdownMenuItem, { onSelect: () => navigate({ to: "/voorraad/publicaties" }), children: [_jsx(Send, { className: "mr-2 h-4 w-4" }), "Verzonden naar Floriday"] })] }))] })] }), _jsxs(Button, { size: "sm", className: "hidden h-9 md:inline-flex", onClick: nieuw, title: "Sneltoets N", children: [_jsx(Plus, { className: "mr-2 h-4 w-4" }), "Nieuw artikel", _jsx(Kbd, { children: "N" })] })] }) }), _jsx(TelmomentButton, { verbergKnop: true, open: telmomentOpen, onOpenChange: setTelmomentOpen, onSaved: reload }), _jsxs(KpiRow, { children: [_jsx(KpiTile, { title: "Artikelen", icon: _jsx(Package, {}), loading: kpiLaden, value: aantal(kpi?.artikelen ?? 0), sub: "Actief, zonder gearchiveerd", onClick: () => setFilter("alles"), active: filter === "alles" }), _jsx(KpiTile, { title: "Onder minimum", icon: _jsx(TrendingDown, {}), tone: (kpi?.onder_minimum ?? 0) > 0 ? "warning" : "default", loading: kpiLaden, value: aantal(kpi?.onder_minimum ?? 0), sub: (kpi?.onder_minimum ?? 0) > 0 ? _jsx("span", { className: "text-destructive", children: "Bijbestellen of bijmaken" }) : "Alles boven het minimum", onClick: () => setFilter("onder_minimum"), active: filter === "onder_minimum" }), _jsx(KpiTile, { title: "Vrij beschikbaar", icon: _jsx(Layers, {}), loading: kpiLaden, value: aantal(kpi?.vrij ?? 0), sub: "Stuks, losse artikelen" }), _jsx(KpiTile, { title: "Inkomend", icon: _jsx(ArrowDownToLine, {}), loading: kpiLaden, value: aantal(kpi?.inkomend ?? 0), sub: "Stuks verwacht" })] }), guardTrips.length > 0 && (_jsx(InlineAlert, { tone: "fout", action: _jsx(Button, { size: "sm", variant: "outline", onClick: () => window.location.reload(), children: "Opnieuw laden" }), children: "De pagina is gestopt met laden. Laad de pagina opnieuw." })), _jsx(StockIntegrityAlert, {}), _jsx(FloridayConnectionNotice, { status: conn.status, environment: conn.selectedEnvironment, error: conn.error }), error && (_jsx(InlineAlert, { tone: "fout", action: _jsx(Button, { size: "sm", variant: "outline", onClick: () => void reload(), children: "Opnieuw proberen" }), children: "De voorraad laden lukt niet. Probeer opnieuw." })), melding.length > 0 && (_jsx(InlineAlert, { tone: "let_op", icon: _jsx(AlertTriangle, {}), children: _jsx("span", { className: "flex flex-wrap gap-x-4 gap-y-1", children: melding.map((m) => m.filter ? (_jsx("button", { type: "button", className: "underline-offset-2 hover:underline", onClick: () => setFilter(m.filter), children: m.tekst }, m.tekst)) : (_jsx("span", { children: m.tekst }, m.tekst))) }) })), _jsx(PillTabs, { tabs: tabs, value: filter, onChange: setFilter, label: "Filter" }), _jsx(Toolbar, { right: _jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsxs(Button, { variant: "outline", size: "sm", className: "h-9", "aria-label": "Kolommen kiezen", children: [_jsx(Columns3, { className: "h-4 w-4 sm:mr-2" }), _jsx("span", { className: "hidden sm:inline", children: "Kolommen" })] }) }), _jsxs(DropdownMenuContent, { align: "end", children: [_jsx(DropdownMenuLabel, { children: "Kolommen kiezen" }), OPTIONELE_KOLOMMEN.map((c) => (_jsx(DropdownMenuCheckboxItem, { checked: kolommen.has(c.key), onCheckedChange: () => toggleKolom(c.key), children: c.label }, c.key)))] })] }), children: _jsx(SearchInput, { value: zoek, onChange: setZoek, placeholder: "Zoek op naam, artikelnummer of barcode\u2026" }) }), selectieBalk && (_jsxs("div", { className: "sticky top-2 z-20 flex flex-wrap items-center gap-2 rounded-lg border border-border bg-card px-4 py-2 text-sm shadow-lg", children: [_jsxs("span", { className: "font-medium tabular-nums", children: [selected.size, " gekozen"] }), _jsx(Button, { size: "sm", className: "h-8", onClick: () => setDrawerOpen(true), children: "Voorraadsturing instellen" }), _jsx(Button, { size: "sm", variant: "ghost", className: "h-8", onClick: () => setSelected(new Set()), children: "Keuze wissen" })] })), filter === "twijfel" ? (_jsx(TwijfelTabel, { rijen: data?.twijfel_rijen ?? [], loading: loading, onGekozen: () => void reload() })) : (_jsx(VoorraadLijstTabel, { rijen: data?.rijen ?? [], loading: loading, sort: sorteer.sort, oplopend: sorteer.oplopend, onSort: (k) => setSorteer((cur) => volgendeSortering(cur, k)), kolommen: kolommen, selected: selected, onToggleSelect: toggleSelect, onToggleAll: toggleAll, onAanpassen: setAanpassen, onAfboeken: setAfboeken, heeftKoppeling: heeftKoppeling, leeg: leeg })), _jsx(VoorraadPagination, { total: data?.totaal ?? 0, page: pagina, pageSize: grootte, onPageChange: setPagina, onPageSizeChange: setGrootte }), _jsx(AanpassenDialog, { rij: aanpassen, onClose: () => setAanpassen(null), onDone: reload }), _jsx(AfboekenDialog, { rij: afboeken, connectionId: stockConnId, onClose: () => setAfboeken(null), onDone: reload }), _jsx(InventorySettingsDrawer, { open: drawerOpen, onOpenChange: setDrawerOpen, connectionId: conn.activeId ?? null, productIds: [...selected], onSaved: reload }), _jsx(MobilePrimaryAction, { onClick: nieuw, hidden: selectieBalk, icon: _jsx(Plus, {}), children: "Nieuw artikel" })] }));
|
|
121
130
|
}
|
|
122
131
|
function TwijfelTabel({ rijen, loading, onGekozen }) {
|
|
123
132
|
const [open, setOpen] = useState(null);
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
133
|
+
return (_jsxs("div", { className: "flex flex-col gap-3", children: [_jsx(DataTable, { rows: rijen, getId: (t) => t.floriday_id, loading: loading, keyboard: false, empty: _jsx(EmptyState, { icon: _jsx(HelpCircle, { className: "h-6 w-6" }), title: "Geen twijfelgevallen", text: "Alle artikelen uit Floriday zijn aan een eigen artikel gekoppeld." }), columns: [
|
|
134
|
+
{ key: "naam", header: "Artikel in Floriday", width: "36%", render: (t) => _jsx("span", { className: "font-medium", children: t.naam ?? "(naamloos)" }) },
|
|
135
|
+
{ key: "reden", header: "Reden", width: "36%", render: (t) => _jsx("span", { className: "text-muted-foreground", children: t.reden ?? "Meerdere mogelijke artikelen; niets aangemaakt" }) },
|
|
136
|
+
{ key: "kandidaten", header: "Mogelijke artikelen", width: "14%", render: (t) => _jsx("span", { className: "tabular-nums", children: t.kandidaten }) },
|
|
137
|
+
{
|
|
138
|
+
key: "actie", header: "Actie", width: "14%",
|
|
139
|
+
render: (t) => (_jsx(Button, { size: "sm", variant: open === t.floriday_id ? "default" : "outline", className: "h-8", onClick: () => setOpen(open === t.floriday_id ? null : t.floriday_id), children: "Zelf kiezen" })),
|
|
140
|
+
},
|
|
141
|
+
], mobileCard: (t) => (_jsxs("div", { className: "flex items-center justify-between gap-3", children: [_jsxs("div", { className: "min-w-0", children: [_jsx("div", { className: "truncate font-medium", children: t.naam ?? "(naamloos)" }), _jsxs("div", { className: "truncate text-xs text-muted-foreground", children: [t.kandidaten, " mogelijke artikelen"] })] }), _jsx(Button, { size: "sm", variant: "outline", className: "h-10", onClick: (e) => { e.stopPropagation(); setOpen(open === t.floriday_id ? null : t.floriday_id); }, children: "Zelf kiezen" })] })) }), open && (_jsx("div", { className: "rounded-xl border border-border bg-card p-4 shadow-sm", children: _jsx(TwijfelKiezen, { floridayId: open, onGekozen: () => { setOpen(null); onGekozen?.(); } }) }))] }));
|
|
128
142
|
}
|
|
@@ -41,7 +41,7 @@ export function VoorraadSturingCard() {
|
|
|
41
41
|
};
|
|
42
42
|
const opties = [
|
|
43
43
|
{ waarde: "lokaal", titel: "Lokaal", uitleg: "Je eigen voorraad leidt; Floriday wordt niet bijgewerkt tot je dat per artikel aanzet." },
|
|
44
|
-
{ waarde: "floriday", titel: "Floriday volgt", uitleg: "Je eigen voorraad gaat
|
|
44
|
+
{ waarde: "floriday", titel: "Floriday volgt", uitleg: "Je eigen voorraad gaat vanzelf naar Floriday." },
|
|
45
45
|
];
|
|
46
46
|
return (_jsxs(Card, { children: [_jsxs(CardHeader, { children: [_jsx(CardTitle, { children: "Standaard voorraadsturing" }), _jsx(CardDescription, { children: "Geldt voor artikelen die automatisch uit Floriday worden aangemaakt. Bestaande artikelen veranderen niet." })] }), _jsx(CardContent, { className: "space-y-3", children: opgeslagen === null ? (_jsx(Loader2, { className: "h-4 w-4 animate-spin text-muted-foreground" })) : (_jsxs(_Fragment, { children: [_jsx("div", { role: "radiogroup", className: "space-y-2", children: opties.map((o) => (_jsxs(Label, { className: "flex cursor-pointer items-start gap-2 rounded-md border p-3 font-normal", children: [_jsx("input", { type: "radio", name: "standaard-sturing", className: "mt-1", checked: keuze === o.waarde, onChange: () => setKeuze(o.waarde) }), _jsxs("span", { children: [_jsx("span", { className: "font-medium", children: o.titel }), _jsx("span", { className: "block text-sm text-muted-foreground", children: o.uitleg })] })] }, o.waarde))) }), _jsxs(Button, { onClick: opslaan, disabled: bezig || keuze === opgeslagen, children: [bezig && _jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }), "Opslaan"] })] })) })] }));
|
|
47
47
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ArtikelPagina.d.ts","sourceRoot":"","sources":["../../../../src/components/voorraad/artikel/ArtikelPagina.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ArtikelPagina.d.ts","sourceRoot":"","sources":["../../../../src/components/voorraad/artikel/ArtikelPagina.tsx"],"names":[],"mappings":"AAkGA,wBAAgB,aAAa,CAAC,EAAE,EAAE,EAAE,WAAW,EAAE,cAAc,EAAE,EAAE;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,cAAc,CAAC,EAAE,MAAM,IAAI,CAAA;CAAE,+BAoenI"}
|