@flowselections/floriday-voorraad 1.0.145 → 1.0.149
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 +3 -1
- package/dist-lib/components/voorraad/ArtikelWizard.d.ts.map +1 -1
- package/dist-lib/components/voorraad/ArtikelWizard.js +56 -52
- package/dist-lib/components/voorraad/EditArtikelPage.d.ts.map +1 -1
- package/dist-lib/components/voorraad/EditArtikelPage.js +15 -23
- package/dist-lib/components/voorraad/OrderHistoryCard.d.ts +3 -1
- package/dist-lib/components/voorraad/OrderHistoryCard.d.ts.map +1 -1
- package/dist-lib/components/voorraad/OrderHistoryCard.js +7 -3
- package/dist-lib/components/voorraad/ProductCustomFieldsCard.d.ts +5 -1
- package/dist-lib/components/voorraad/ProductCustomFieldsCard.d.ts.map +1 -1
- package/dist-lib/components/voorraad/ProductCustomFieldsCard.js +37 -18
- package/dist-lib/components/voorraad/ProductSalePriceField.d.ts.map +1 -1
- package/dist-lib/components/voorraad/ProductSalePriceField.js +11 -8
- package/dist-lib/components/voorraad/StepSamenstelling.d.ts.map +1 -1
- package/dist-lib/components/voorraad/StepSamenstelling.js +35 -10
- package/dist-lib/components/voorraad/StockMutationHistoryCard.d.ts +3 -1
- package/dist-lib/components/voorraad/StockMutationHistoryCard.d.ts.map +1 -1
- package/dist-lib/components/voorraad/StockMutationHistoryCard.js +16 -10
- package/dist-lib/components/voorraad/TrayStickerCard.d.ts +5 -1
- package/dist-lib/components/voorraad/TrayStickerCard.d.ts.map +1 -1
- package/dist-lib/components/voorraad/TrayStickerCard.js +64 -66
- package/dist-lib/components/voorraad/ValidationOverviewTable.d.ts +0 -5
- package/dist-lib/components/voorraad/ValidationOverviewTable.d.ts.map +1 -1
- package/dist-lib/components/voorraad/ValidationOverviewTable.js +10 -4
- package/dist-lib/components/voorraad/VoorraadBeheerCard.d.ts +3 -0
- package/dist-lib/components/voorraad/VoorraadBeheerCard.d.ts.map +1 -0
- package/dist-lib/components/voorraad/VoorraadBeheerCard.js +44 -0
- package/dist-lib/components/voorraad/VoorraadPage.d.ts +1 -13
- package/dist-lib/components/voorraad/VoorraadPage.d.ts.map +1 -1
- package/dist-lib/components/voorraad/VoorraadPage.js +102 -140
- package/dist-lib/components/voorraad/VoorraadSturingCard.d.ts +6 -0
- package/dist-lib/components/voorraad/VoorraadSturingCard.d.ts.map +1 -0
- package/dist-lib/components/voorraad/VoorraadSturingCard.js +47 -0
- package/dist-lib/components/voorraad/artikel/ArtikelPagina.d.ts +6 -0
- package/dist-lib/components/voorraad/artikel/ArtikelPagina.d.ts.map +1 -0
- package/dist-lib/components/voorraad/artikel/ArtikelPagina.js +313 -0
- package/dist-lib/components/voorraad/artikel/TwijfelKiezen.d.ts +7 -0
- package/dist-lib/components/voorraad/artikel/TwijfelKiezen.d.ts.map +1 -0
- package/dist-lib/components/voorraad/artikel/TwijfelKiezen.js +54 -0
- package/dist-lib/components/voorraad/artikel/opslaan-context.d.ts +21 -0
- package/dist-lib/components/voorraad/artikel/opslaan-context.d.ts.map +1 -0
- package/dist-lib/components/voorraad/artikel/opslaan-context.js +15 -0
- package/dist-lib/components/voorraad/lijst/ImporteerKnop.d.ts +5 -0
- package/dist-lib/components/voorraad/lijst/ImporteerKnop.d.ts.map +1 -0
- package/dist-lib/components/voorraad/lijst/ImporteerKnop.js +20 -0
- package/dist-lib/components/voorraad/lijst/LijstExportKnop.d.ts +6 -0
- package/dist-lib/components/voorraad/lijst/LijstExportKnop.d.ts.map +1 -0
- package/dist-lib/components/voorraad/lijst/LijstExportKnop.js +43 -0
- package/dist-lib/components/voorraad/lijst/SnelActieDialogen.d.ts +16 -0
- package/dist-lib/components/voorraad/lijst/SnelActieDialogen.d.ts.map +1 -0
- package/dist-lib/components/voorraad/lijst/SnelActieDialogen.js +95 -0
- package/dist-lib/components/voorraad/lijst/VoorraadLijstTabel.d.ts +21 -0
- package/dist-lib/components/voorraad/lijst/VoorraadLijstTabel.d.ts.map +1 -0
- package/dist-lib/components/voorraad/lijst/VoorraadLijstTabel.js +39 -0
- package/dist-lib/hooks/useVoorraadCustomFields.d.ts +2 -1
- package/dist-lib/hooks/useVoorraadCustomFields.d.ts.map +1 -1
- package/dist-lib/hooks/useVoorraadCustomFields.js +8 -4
- package/dist-lib/hooks/useVoorraadLijst.d.ts +21 -0
- package/dist-lib/hooks/useVoorraadLijst.d.ts.map +1 -0
- package/dist-lib/hooks/useVoorraadLijst.js +71 -0
- package/dist-lib/index.d.ts.map +1 -1
- package/dist-lib/index.js +4 -0
- package/dist-lib/integrations/supabase/types.d.ts +748 -1
- package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
- package/dist-lib/lib/artikel-gewijzigd.d.ts +4 -0
- package/dist-lib/lib/artikel-gewijzigd.d.ts.map +1 -0
- package/dist-lib/lib/artikel-gewijzigd.js +7 -0
- package/dist-lib/lib/artikel-pagina.d.ts +68 -0
- package/dist-lib/lib/artikel-pagina.d.ts.map +1 -0
- package/dist-lib/lib/artikel-pagina.js +97 -0
- package/dist-lib/lib/count-stock.d.ts +17 -0
- package/dist-lib/lib/count-stock.d.ts.map +1 -0
- package/dist-lib/lib/count-stock.js +26 -0
- package/dist-lib/lib/floriday-import-validator.d.ts +9 -0
- package/dist-lib/lib/floriday-import-validator.d.ts.map +1 -1
- package/dist-lib/lib/floriday-import-validator.js +13 -0
- package/dist-lib/lib/floriday-versie-ronde.d.ts +59 -0
- package/dist-lib/lib/floriday-versie-ronde.d.ts.map +1 -0
- package/dist-lib/lib/floriday-versie-ronde.functions.d.ts +10 -0
- package/dist-lib/lib/floriday-versie-ronde.functions.d.ts.map +1 -0
- package/dist-lib/lib/floriday-versie-ronde.functions.js +22 -0
- package/dist-lib/lib/floriday-versie-ronde.js +106 -0
- package/dist-lib/lib/pending-photos.d.ts +20 -0
- package/dist-lib/lib/pending-photos.d.ts.map +1 -0
- package/dist-lib/lib/pending-photos.js +90 -0
- package/dist-lib/lib/voorraad-custom-fields.d.ts +16 -1
- package/dist-lib/lib/voorraad-custom-fields.d.ts.map +1 -1
- package/dist-lib/lib/voorraad-custom-fields.js +12 -6
- package/dist-lib/lib/voorraad-lijst.d.ts +92 -0
- package/dist-lib/lib/voorraad-lijst.d.ts.map +1 -0
- package/dist-lib/lib/voorraad-lijst.js +86 -0
- package/dist-lib/server-hooks/floriday-stock-reconcile.d.ts.map +1 -1
- package/dist-lib/server-hooks/floriday-stock-reconcile.js +5 -0
- package/dist-lib/server-hooks/floriday-versieronde.d.ts +2 -0
- package/dist-lib/server-hooks/floriday-versieronde.d.ts.map +1 -0
- package/dist-lib/server-hooks/floriday-versieronde.js +29 -0
- package/dist-lib/server-hooks/guard.js +1 -1
- package/package.json +1 -1
- package/sql/AGENTS.md +6 -0
- package/sql/floriday-voorraad-schema.sql +1355 -22
|
@@ -1,95 +1,109 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { TwijfelKiezen } from "./artikel/TwijfelKiezen.js";
|
|
3
|
+
import { Fragment, useEffect, useMemo, useState, useSyncExternalStore } from "react";
|
|
4
|
+
import { Link, useNavigate } from "@tanstack/react-router";
|
|
5
|
+
import { Button, Card, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuLabel, DropdownMenuTrigger, Input, } from "@flowselections/core";
|
|
6
|
+
import { AlertTriangle, Columns3, Plus, Search } from "lucide-react";
|
|
7
|
+
import { supabase } from "../../integrations/supabase/client.js";
|
|
4
8
|
import { pageRequestGuard } from "../../lib/request-guard.js";
|
|
5
|
-
const EMPTY_TRIPS = [];
|
|
6
|
-
import { Link } from "@tanstack/react-router";
|
|
7
|
-
import { Button, Card, Input, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@flowselections/core";
|
|
8
|
-
import { AlertTriangle, CheckCircle2, FileText, PackageX, Plus, Search, } from "lucide-react";
|
|
9
9
|
import { useFloridayConnection } from "../../hooks/useFloridayConnection.js";
|
|
10
10
|
import { useEnsureVoorraadDefaults } from "../../hooks/useEnsureVoorraadDefaults.js";
|
|
11
|
+
import { useVoorraadLijst } from "../../hooks/useVoorraadLijst.js";
|
|
12
|
+
import { aantal, FILTER_LABEL, meldingen, volgendeSortering, zichtbareFilters, } from "../../lib/voorraad-lijst.js";
|
|
11
13
|
import { FloridayConnectionNotice } from "./FloridayConnectionNotice.js";
|
|
12
|
-
import { useVoorraadData } from "../../hooks/useVoorraadData.js";
|
|
13
|
-
import { StockSyncStatus } from "./StockSyncStatus.js";
|
|
14
14
|
import { StockIntegrityAlert } from "./StockIntegrityAlert.js";
|
|
15
|
-
import { SupplyReadScopeAlert } from "./SupplyReadScopeAlert.js";
|
|
16
|
-
import { ExcelExportButton } from "./ExcelExportButton.js";
|
|
17
|
-
import { StockBulkButtons } from "./StockBulkButtons.js";
|
|
18
15
|
import { TelmomentButton } from "./TelmomentButton.js";
|
|
19
|
-
import { VoorraadTable } from "./VoorraadTable.js";
|
|
20
16
|
import { VoorraadPagination } from "./VoorraadPagination.js";
|
|
21
|
-
import { StockHistoryChart } from "./StockHistoryChart.js";
|
|
22
17
|
import { InventorySettingsDrawer } from "./InventorySettingsDrawer.js";
|
|
23
|
-
import {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
* "Op voorraad" = artikelen met liggende voorraad (fysiek > 0).
|
|
49
|
-
* "Vrij beschikbaar" = artikelen waarvan liggend min gereserveerd > 0.
|
|
50
|
-
*/
|
|
51
|
-
export function countStock(items) {
|
|
52
|
-
let inStock = 0;
|
|
53
|
-
let outOfStock = 0;
|
|
54
|
-
let belowMin = 0;
|
|
55
|
-
let fullyReserved = 0;
|
|
56
|
-
let freeAvailable = 0;
|
|
57
|
-
for (const i of items) {
|
|
58
|
-
const physical = Number(i.quantity ?? 0);
|
|
59
|
-
const avail = availableOf(i);
|
|
60
|
-
if (physical > 0)
|
|
61
|
-
inStock++;
|
|
18
|
+
import { OPTIONELE_KOLOMMEN, VoorraadLijstTabel } from "./lijst/VoorraadLijstTabel.js";
|
|
19
|
+
import { AanpassenDialog, AfboekenDialog } from "./lijst/SnelActieDialogen.js";
|
|
20
|
+
import { LijstExportKnop } from "./lijst/LijstExportKnop.js";
|
|
21
|
+
import { ImporteerKnop } from "./lijst/ImporteerKnop.js";
|
|
22
|
+
// Bestaande export blijft (tests en andere modules).
|
|
23
|
+
export { countStock } from "../../lib/count-stock.js";
|
|
24
|
+
const EMPTY_TRIPS = [];
|
|
25
|
+
const KOLOMMEN_KEY = "voorraad.lijst.kolommen.v2";
|
|
26
|
+
const STANDAARD_KOLOMMEN = ["inkomend", "prijs"];
|
|
27
|
+
function useKolommen() {
|
|
28
|
+
const [set, setSet] = useState(new Set(STANDAARD_KOLOMMEN));
|
|
29
|
+
useEffect(() => {
|
|
30
|
+
try {
|
|
31
|
+
const raw = window.localStorage.getItem(KOLOMMEN_KEY);
|
|
32
|
+
if (raw)
|
|
33
|
+
setSet(new Set(JSON.parse(raw)));
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
/* standaard houden */
|
|
37
|
+
}
|
|
38
|
+
}, []);
|
|
39
|
+
const toggle = (k) => setSet((cur) => {
|
|
40
|
+
const next = new Set(cur);
|
|
41
|
+
if (next.has(k))
|
|
42
|
+
next.delete(k);
|
|
62
43
|
else
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
44
|
+
next.add(k);
|
|
45
|
+
try {
|
|
46
|
+
window.localStorage.setItem(KOLOMMEN_KEY, JSON.stringify([...next]));
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
/* niet opslaan is geen probleem */
|
|
50
|
+
}
|
|
51
|
+
return next;
|
|
52
|
+
});
|
|
53
|
+
return { kolommen: set, toggle };
|
|
54
|
+
}
|
|
55
|
+
/** Verbinding van de voorraadcontext (nodig voor afboeken op de juiste voorraadregel). */
|
|
56
|
+
function useStockContextId() {
|
|
57
|
+
const [id, setId] = useState(null);
|
|
58
|
+
useEffect(() => {
|
|
59
|
+
void supabase.rpc("get_stock_context").then(({ data }) => {
|
|
60
|
+
const row = Array.isArray(data) ? data[0] : data;
|
|
61
|
+
setId(row?.connection_id ?? null);
|
|
62
|
+
});
|
|
63
|
+
}, []);
|
|
64
|
+
return id;
|
|
72
65
|
}
|
|
73
66
|
export function VoorraadPage() {
|
|
74
67
|
useEnsureVoorraadDefaults();
|
|
68
|
+
const navigate = useNavigate();
|
|
75
69
|
const conn = useFloridayConnection();
|
|
76
|
-
const
|
|
77
|
-
const
|
|
78
|
-
const
|
|
79
|
-
const [
|
|
80
|
-
const [
|
|
81
|
-
const [
|
|
82
|
-
const [
|
|
83
|
-
const [page, setPage] = useState(1);
|
|
84
|
-
const [pageSize, setPageSize] = useState(25);
|
|
70
|
+
const stockConnId = useStockContextId();
|
|
71
|
+
const [zoekInvoer, setZoekInvoer] = useState("");
|
|
72
|
+
const [zoek, setZoek] = useState("");
|
|
73
|
+
const [filter, setFilter] = useState("alles");
|
|
74
|
+
const [sorteer, setSorteer] = useState({ sort: "naam", oplopend: true });
|
|
75
|
+
const [pagina, setPagina] = useState(1);
|
|
76
|
+
const [grootte, setGrootte] = useState(25);
|
|
85
77
|
const [selected, setSelected] = useState(new Set());
|
|
86
|
-
const [drawerIds, setDrawerIds] = useState([]);
|
|
87
78
|
const [drawerOpen, setDrawerOpen] = useState(false);
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
const {
|
|
91
|
-
const [noPriceOnly, setNoPriceOnly] = useState(false);
|
|
79
|
+
const [aanpassen, setAanpassen] = useState(null);
|
|
80
|
+
const [afboeken, setAfboeken] = useState(null);
|
|
81
|
+
const { kolommen, toggle: toggleKolom } = useKolommen();
|
|
92
82
|
const guardTrips = useSyncExternalStore(pageRequestGuard.subscribe, () => pageRequestGuard.trips(), () => EMPTY_TRIPS);
|
|
83
|
+
// Zoeken met korte vertraging; nieuwe zoekopdracht of filter begint op pagina 1.
|
|
84
|
+
useEffect(() => {
|
|
85
|
+
const t = setTimeout(() => setZoek(zoekInvoer), 300);
|
|
86
|
+
return () => clearTimeout(t);
|
|
87
|
+
}, [zoekInvoer]);
|
|
88
|
+
useEffect(() => setPagina(1), [zoek, filter, sorteer, grootte]);
|
|
89
|
+
// Sneltoets N = nieuw artikel (niet tijdens typen).
|
|
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]);
|
|
101
|
+
const vraag = { zoek, filter, sort: sorteer.sort, oplopend: sorteer.oplopend, pagina, grootte };
|
|
102
|
+
const { data, loading, error, reload } = useVoorraadLijst(vraag);
|
|
103
|
+
const heeftKoppeling = data?.heeft_koppeling ?? false;
|
|
104
|
+
const filters = useMemo(() => zichtbareFilters(heeftKoppeling), [heeftKoppeling]);
|
|
105
|
+
const melding = data ? meldingen(data) : [];
|
|
106
|
+
const bijgewerkt = data?.bijgewerkt ? new Date(data.bijgewerkt).toLocaleTimeString("nl-NL", { hour: "2-digit", minute: "2-digit" }) : null;
|
|
93
107
|
const toggleSelect = (id) => setSelected((cur) => {
|
|
94
108
|
const next = new Set(cur);
|
|
95
109
|
if (next.has(id))
|
|
@@ -98,69 +112,17 @@ export function VoorraadPage() {
|
|
|
98
112
|
next.add(id);
|
|
99
113
|
return next;
|
|
100
114
|
});
|
|
101
|
-
const toggleAll = (ids) => setSelected((cur) =>
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
const
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
return false;
|
|
115
|
-
const avail = availableOf(i);
|
|
116
|
-
const physicalQty = Number(i.quantity ?? 0);
|
|
117
|
-
if (stockFilter === "in_stock" && physicalQty <= 0)
|
|
118
|
-
return false;
|
|
119
|
-
if (stockFilter === "out_of_stock" && physicalQty > 0)
|
|
120
|
-
return false;
|
|
121
|
-
if (stockFilter === "below_min" &&
|
|
122
|
-
!((i.minQuantity ?? 0) > 0 && avail < i.minQuantity))
|
|
123
|
-
return false;
|
|
124
|
-
if (floridayFilter === "linked" && !isLinkedToFloriday(i))
|
|
125
|
-
return false;
|
|
126
|
-
if (floridayFilter === "unlinked" && isLinkedToFloriday(i))
|
|
127
|
-
return false;
|
|
128
|
-
if (typeFilter !== "all" && (i.itemType ?? "artikel") !== typeFilter)
|
|
129
|
-
return false;
|
|
130
|
-
if (!q)
|
|
131
|
-
return true;
|
|
132
|
-
return (i.name.toLowerCase().includes(q) ||
|
|
133
|
-
(i.articleNumber ?? "").toLowerCase().includes(q));
|
|
134
|
-
});
|
|
135
|
-
}, [items, search, stockFilter, floridayFilter, typeFilter, noPriceOnly, noPriceIds]);
|
|
136
|
-
const totalPages = Math.max(1, Math.ceil(filtered.length / pageSize));
|
|
137
|
-
useEffect(() => {
|
|
138
|
-
if (page > totalPages)
|
|
139
|
-
setPage(1);
|
|
140
|
-
}, [page, totalPages]);
|
|
141
|
-
const paged = useMemo(() => filtered.slice((page - 1) * pageSize, page * pageSize), [filtered, page, pageSize]);
|
|
142
|
-
const toggle = (id) => setStockFilter((cur) => (cur === id && id !== "all" ? "all" : id));
|
|
143
|
-
const TABS = [
|
|
144
|
-
{ id: "all", label: "Alle", count: counts.total },
|
|
145
|
-
{ id: "in_stock", label: "Op voorraad", count: counts.inStock },
|
|
146
|
-
{ id: "out_of_stock", label: "Niet op voorraad", count: counts.outOfStock },
|
|
147
|
-
{ id: "below_min", label: "Onder minimum", count: counts.belowMin },
|
|
148
|
-
];
|
|
149
|
-
return (_jsxs("main", { className: "p-6 space-y-6", children: [_jsxs("header", { className: "flex flex-wrap items-start justify-between gap-4", children: [_jsxs("div", { className: "min-w-[12rem] flex-1", children: [_jsx("h1", { className: "text-2xl font-bold tracking-tight", children: "Voorraad" }), _jsx("p", { className: "text-sm text-muted-foreground mt-1", children: "Beheer je artikelen en voorraadniveaus." })] }), _jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsx(Button, { asChild: true, variant: "outline", size: "sm", children: _jsx(Link, { to: "/voorraad/validatie", children: "Validatie" }) }), _jsx(Button, { asChild: true, variant: "outline", size: "sm", children: _jsx(Link, { to: "/voorraad/publicaties", children: "Publicaties" }) }), _jsx(Button, { asChild: true, size: "sm", children: _jsxs(Link, { to: "/voorraad/nieuw", children: [_jsx(Plus, { className: "mr-2 h-4 w-4" }), "Artikel toevoegen"] }) })] })] }), guardTrips.length > 0 && (_jsxs("div", { role: "alert", className: "rounded-md border border-destructive/30 bg-destructive/10 px-3 py-2 text-sm text-destructive", children: ["Deze pagina vroeg te vaak gegevens op en is daarmee gestopt. Open de pagina opnieuw.", _jsxs("details", { className: "text-xs opacity-80", children: [_jsx("summary", { className: "cursor-pointer", children: "Details voor beheer" }), guardTrips.map((t) => (_jsxs("div", { children: [t.table, ": ", t.count, " aanvragen binnen 10 s om ", new Date(t.at).toLocaleTimeString("nl-NL")] }, t.table)))] })] })), _jsx(StockIntegrityAlert, {}), _jsx(SupplyReadScopeAlert, {}), _jsx(StockSyncStatus, {}), noPriceIds.size > 0 && (_jsxs("div", { className: "flex flex-wrap items-start gap-2 rounded-md border border-amber-500/30 bg-amber-500/10 px-3 py-2 text-sm text-amber-800", children: [_jsx(AlertTriangle, { className: "mt-0.5 h-4 w-4 shrink-0" }), _jsxs("div", { className: "flex-1 min-w-[12rem]", children: [_jsxs("span", { className: "font-medium", children: [noPriceIds.size, " artikel", noPriceIds.size === 1 ? " staat" : "en staan", " niet in Floriday zonder geldige prijs:", " ", [
|
|
150
|
-
noPriceBreakdown.empty > 0 ? `${noPriceBreakdown.empty} zonder verkoopprijs` : null,
|
|
151
|
-
noPriceBreakdown.zero > 0 ? `${noPriceBreakdown.zero} met prijs € 0,00` : null,
|
|
152
|
-
noPriceBreakdown.unit > 0 ? `${noPriceBreakdown.unit} met een andere eenheid dan stuks` : null,
|
|
153
|
-
]
|
|
154
|
-
.filter(Boolean)
|
|
155
|
-
.join(", "), "."] }), " ", "Kies \"Toon deze artikelen\", open een artikel en vul bij Artikel regulier de verkoopprijs per stuk (excl. btw) in. De voorraadsturing van elk artikel heeft ook een link \"Prijs invullen\"."] }), _jsx(Button, { variant: "outline", size: "sm", onClick: () => setNoPriceOnly((v) => !v), children: noPriceOnly ? "Alle artikelen tonen" : "Toon deze artikelen" })] })), _jsx(FloridayConnectionNotice, { status: conn.status, environment: conn.selectedEnvironment, error: 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-5 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"), hint: "Artikelen waarvan nu iets op de kwekerij staat." }), _jsx(KpiCard, { icon: _jsx(CheckCircle2, { className: "h-4 w-4" }), tone: "primary", label: "Vrij beschikbaar", count: counts.freeAvailable, active: false, hint: "Artikelen waarvan na aftrek van wat voor orders gereserveerd is nog iets over is." }), _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) => {
|
|
156
|
-
const active = stockFilter === t.id;
|
|
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
|
|
158
|
-
? "text-foreground"
|
|
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));
|
|
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
|
|
161
|
-
? [...settingsByKey.values()].find((s) => s.product_id === drawerIds[0])
|
|
162
|
-
: undefined, onSaved: () => {
|
|
163
|
-
reloadSettings();
|
|
164
|
-
reload();
|
|
165
|
-
} })] }));
|
|
115
|
+
const toggleAll = (ids) => setSelected((cur) => (ids.length > 0 && ids.every((id) => cur.has(id)) ? new Set() : new Set(ids)));
|
|
116
|
+
return (_jsxs("main", { className: "p-6 space-y-4", children: [_jsxs("header", { className: "flex flex-wrap items-start justify-between gap-4", children: [_jsxs("div", { className: "min-w-[12rem] flex-1", children: [_jsx("h1", { className: "text-2xl font-bold tracking-tight", children: "Voorraad" }), _jsx("p", { className: "text-sm text-muted-foreground mt-1", children: bijgewerkt ? `Bijgewerkt ${bijgewerkt}` : "Laden…" })] }), _jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsx(ImporteerKnop, { connectionId: conn.activeId ?? null, onImported: reload }), _jsx(LijstExportKnop, { vraag: { zoek, filter, sort: sorteer.sort, oplopend: sorteer.oplopend } }), _jsx(TelmomentButton, { onSaved: reload }), _jsx(Button, { asChild: true, variant: "outline", size: "sm", children: _jsx(Link, { to: "/voorraad/validatie", children: "Validatie" }) }), _jsx(Button, { asChild: true, size: "sm", title: "Sneltoets N", children: _jsxs(Link, { to: "/voorraad/nieuw", children: [_jsx(Plus, { className: "mr-2 h-4 w-4" }), "Nieuw artikel"] }) })] })] }), guardTrips.length > 0 && (_jsx("div", { role: "alert", className: "rounded-md border border-destructive/30 bg-destructive/10 px-3 py-2 text-sm text-destructive", children: "Deze pagina vroeg te vaak gegevens op en is daarmee gestopt. Open de pagina opnieuw." })), _jsx(StockIntegrityAlert, {}), _jsx(FloridayConnectionNotice, { status: conn.status, environment: conn.selectedEnvironment, error: conn.error }), error && (_jsxs("div", { role: "alert", className: "rounded-md border border-destructive/40 bg-destructive/10 text-destructive p-3 text-sm", children: ["Voorraad laden mislukt: ", error] })), melding.length > 0 && (_jsxs("div", { className: "flex flex-wrap items-center gap-x-4 gap-y-1 rounded-md border border-border bg-muted/40 px-3 py-2 text-sm", children: [_jsx(AlertTriangle, { className: "h-4 w-4 shrink-0 text-muted-foreground" }), 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)))] })), _jsxs(Card, { className: "overflow-hidden p-0", children: [_jsx("div", { role: "tablist", "aria-label": "Filter", className: "flex flex-wrap items-center gap-1 border-b px-2", children: filters.map((f) => {
|
|
117
|
+
const actief = filter === f;
|
|
118
|
+
const n = data?.tellers?.[f];
|
|
119
|
+
return (_jsxs("button", { role: "tab", "aria-selected": actief, type: "button", onClick: () => setFilter(f), className: `relative px-3 py-3 text-sm font-medium whitespace-nowrap transition-colors ${actief ? "text-foreground" : "text-muted-foreground hover:text-foreground"}`, children: [FILTER_LABEL[f], _jsx("span", { className: `ml-2 text-xs tabular-nums ${actief ? "text-primary" : "text-muted-foreground/70"}`, children: n == null ? "…" : aantal(n) }), actief && _jsx("span", { className: "absolute left-2 right-2 -bottom-px h-0.5 rounded-full bg-primary" })] }, f));
|
|
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 })] }));
|
|
121
|
+
}
|
|
122
|
+
function TwijfelTabel({ rijen, loading, onGekozen }) {
|
|
123
|
+
const [open, setOpen] = useState(null);
|
|
124
|
+
if (!loading && rijen.length === 0) {
|
|
125
|
+
return _jsx("p", { className: "px-4 py-10 text-center text-sm text-muted-foreground", children: "Geen twijfelgevallen." });
|
|
126
|
+
}
|
|
127
|
+
return (_jsxs("table", { className: "w-full text-sm", children: [_jsx("thead", { className: "border-b bg-muted/40 text-xs text-muted-foreground", children: _jsxs("tr", { children: [_jsx("th", { className: "px-3 py-2 text-left font-medium", children: "Floriday-artikel" }), _jsx("th", { className: "px-3 py-2 text-left font-medium", children: "Reden" }), _jsx("th", { className: "px-3 py-2 text-right font-medium", children: "Mogelijke artikelen" }), _jsx("th", { className: "px-3 py-2 text-right font-medium", children: "Actie" })] }) }), _jsx("tbody", { children: rijen.map((t) => (_jsxs(Fragment, { children: [_jsxs("tr", { className: "border-b", children: [_jsx("td", { className: "px-3 py-2 font-medium", children: t.naam ?? "(naamloos)" }), _jsx("td", { className: "px-3 py-2 text-muted-foreground", children: t.reden ?? "Meerdere mogelijke artikelen; niets aangemaakt" }), _jsx("td", { className: "px-3 py-2 text-right tabular-nums", children: t.kandidaten }), _jsx("td", { className: "px-3 py-2 text-right", children: _jsx(Button, { size: "sm", variant: "outline", onClick: () => setOpen(open === t.floriday_id ? null : t.floriday_id), children: "Zelf kiezen" }) })] }), open === t.floriday_id && (_jsx("tr", { className: "border-b", children: _jsx("td", { colSpan: 4, className: "px-3 py-2", children: _jsx(TwijfelKiezen, { floridayId: t.floriday_id, onGekozen: () => { setOpen(null); onGekozen?.(); } }) }) }))] }, t.floriday_id))) })] }));
|
|
166
128
|
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shellinstelling: welke voorraadsturing krijgen artikelen die automatisch uit
|
|
3
|
+
* Floriday worden aangemaakt. Bestaande artikelen veranderen niet.
|
|
4
|
+
*/
|
|
5
|
+
export declare function VoorraadSturingCard(): import("react").JSX.Element;
|
|
6
|
+
//# sourceMappingURL=VoorraadSturingCard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"VoorraadSturingCard.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/VoorraadSturingCard.tsx"],"names":[],"mappings":"AAiBA;;;GAGG;AACH,wBAAgB,mBAAmB,gCAgElC"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useState } from "react";
|
|
3
|
+
import { Button, Card, CardContent, CardDescription, CardHeader, CardTitle, Label, toast, } from "@flowselections/core";
|
|
4
|
+
import { Loader2 } from "lucide-react";
|
|
5
|
+
import { supabase } from "../../integrations/supabase/client.js";
|
|
6
|
+
import { dbFoutTekst } from "../../lib/db-error-message.js";
|
|
7
|
+
/**
|
|
8
|
+
* Shellinstelling: welke voorraadsturing krijgen artikelen die automatisch uit
|
|
9
|
+
* Floriday worden aangemaakt. Bestaande artikelen veranderen niet.
|
|
10
|
+
*/
|
|
11
|
+
export function VoorraadSturingCard() {
|
|
12
|
+
const [opgeslagen, setOpgeslagen] = useState(null);
|
|
13
|
+
const [keuze, setKeuze] = useState("lokaal");
|
|
14
|
+
const [bezig, setBezig] = useState(false);
|
|
15
|
+
useEffect(() => {
|
|
16
|
+
void supabase
|
|
17
|
+
.rpc("voorraad_instellingen_lezen")
|
|
18
|
+
.then(({ data, error }) => {
|
|
19
|
+
if (error)
|
|
20
|
+
toast.error(dbFoutTekst(error));
|
|
21
|
+
const v = (data?.standaard_sturing ?? "lokaal");
|
|
22
|
+
setOpgeslagen(v);
|
|
23
|
+
setKeuze(v);
|
|
24
|
+
});
|
|
25
|
+
}, []);
|
|
26
|
+
const opslaan = async () => {
|
|
27
|
+
setBezig(true);
|
|
28
|
+
const { data, error } = await supabase.rpc("voorraad_instellingen_opslaan", { p_standaard_sturing: keuze });
|
|
29
|
+
setBezig(false);
|
|
30
|
+
if (error) {
|
|
31
|
+
toast.error(dbFoutTekst(error));
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
const r = data;
|
|
35
|
+
if (!r.ok) {
|
|
36
|
+
toast.error(r.melding ?? "Niet opgeslagen");
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
setOpgeslagen(keuze);
|
|
40
|
+
toast.success("Standaard voorraadsturing opgeslagen");
|
|
41
|
+
};
|
|
42
|
+
const opties = [
|
|
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 via de gewone wachtrij naar Floriday." },
|
|
45
|
+
];
|
|
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
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ArtikelPagina.d.ts","sourceRoot":"","sources":["../../../../src/components/voorraad/artikel/ArtikelPagina.tsx"],"names":[],"mappings":"AAwFA,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,+BA+bnI"}
|