@flowselections/floriday-voorraad 1.0.150 → 1.0.154
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/FilterPanel.d.ts +19 -0
- package/dist-lib/components/fs-ui/FilterPanel.d.ts.map +1 -0
- package/dist-lib/components/fs-ui/FilterPanel.js +10 -0
- package/dist-lib/components/fs-ui/FiltersButton.d.ts +6 -0
- package/dist-lib/components/fs-ui/FiltersButton.d.ts.map +1 -0
- package/dist-lib/components/fs-ui/FiltersButton.js +7 -0
- package/dist-lib/components/fs-ui/HeaderButton.d.ts +16 -0
- package/dist-lib/components/fs-ui/HeaderButton.d.ts.map +1 -0
- package/dist-lib/components/fs-ui/HeaderButton.js +10 -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/NoticeBar.d.ts +10 -0
- package/dist-lib/components/fs-ui/NoticeBar.d.ts.map +1 -0
- package/dist-lib/components/fs-ui/NoticeBar.js +16 -0
- package/dist-lib/components/fs-ui/PageHeader.d.ts +19 -0
- package/dist-lib/components/fs-ui/PageHeader.d.ts.map +1 -0
- package/dist-lib/components/fs-ui/PageHeader.js +9 -0
- package/dist-lib/components/fs-ui/Pagination.d.ts +10 -0
- package/dist-lib/components/fs-ui/Pagination.d.ts.map +1 -0
- package/dist-lib/components/fs-ui/Pagination.js +10 -0
- package/dist-lib/components/fs-ui/PillTabs.d.ts +17 -0
- package/dist-lib/components/fs-ui/PillTabs.d.ts.map +1 -0
- package/dist-lib/components/fs-ui/PillTabs.js +28 -0
- package/dist-lib/components/fs-ui/RowMenu.d.ts +13 -0
- package/dist-lib/components/fs-ui/RowMenu.d.ts.map +1 -0
- package/dist-lib/components/fs-ui/RowMenu.js +7 -0
- package/dist-lib/components/fs-ui/SearchInput.d.ts +11 -0
- package/dist-lib/components/fs-ui/SearchInput.d.ts.map +1 -0
- package/dist-lib/components/fs-ui/SearchInput.js +55 -0
- package/dist-lib/components/fs-ui/ShortcutHelp.d.ts +8 -0
- package/dist-lib/components/fs-ui/ShortcutHelp.d.ts.map +1 -0
- package/dist-lib/components/fs-ui/ShortcutHelp.js +6 -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 +12 -0
- package/dist-lib/components/fs-ui/Toolbar.d.ts.map +1 -0
- package/dist-lib/components/fs-ui/Toolbar.js +8 -0
- package/dist-lib/components/fs-ui/UpdatedAt.d.ts +10 -0
- package/dist-lib/components/fs-ui/UpdatedAt.d.ts.map +1 -0
- package/dist-lib/components/fs-ui/UpdatedAt.js +18 -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 +7 -6
- package/dist-lib/components/voorraad/FloridayConnectionNotice.d.ts.map +1 -1
- package/dist-lib/components/voorraad/FloridayConnectionNotice.js +15 -12
- package/dist-lib/components/voorraad/FustenPage.d.ts.map +1 -1
- package/dist-lib/components/voorraad/FustenPage.js +92 -26
- 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/OrderHistoryCard.d.ts +3 -1
- package/dist-lib/components/voorraad/OrderHistoryCard.d.ts.map +1 -1
- package/dist-lib/components/voorraad/OrderHistoryCard.js +26 -50
- package/dist-lib/components/voorraad/PublicatiesPage.d.ts.map +1 -1
- package/dist-lib/components/voorraad/PublicatiesPage.js +42 -36
- 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 +44 -0
- package/dist-lib/components/voorraad/StockIntegrityAlert.d.ts +42 -1
- package/dist-lib/components/voorraad/StockIntegrityAlert.d.ts.map +1 -1
- package/dist-lib/components/voorraad/StockIntegrityAlert.js +38 -7
- package/dist-lib/components/voorraad/StockMutationHistoryCard.d.ts.map +1 -1
- package/dist-lib/components/voorraad/StockMutationHistoryCard.js +10 -46
- 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.d.ts.map +1 -1
- package/dist-lib/components/voorraad/ValidationOverviewTable.js +11 -40
- 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 +67 -38
- 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 +42 -19
- package/dist-lib/components/voorraad/lijst/ImporteerKnop.d.ts.map +1 -1
- package/dist-lib/components/voorraad/lijst/ImporteerKnop.js +1 -1
- package/dist-lib/components/voorraad/lijst/LijstExportKnop.d.ts.map +1 -1
- package/dist-lib/components/voorraad/lijst/LijstExportKnop.js +1 -1
- 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 +85 -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/dist-lib/lib/labels.d.ts +42 -0
- package/dist-lib/lib/labels.d.ts.map +1 -0
- package/dist-lib/lib/labels.js +152 -0
- package/dist-lib/lib/notatie.d.ts +4 -0
- package/dist-lib/lib/notatie.d.ts.map +1 -0
- package/dist-lib/lib/notatie.js +18 -0
- package/dist-lib/lib/voorraad-lijst.d.ts +2 -0
- package/dist-lib/lib/voorraad-lijst.d.ts.map +1 -1
- package/dist-lib/lib/voorraad-lijst.js +14 -3
- package/package.json +2 -1
- package/sql/floriday-voorraad-schema.sql +315 -39
|
@@ -1,29 +1,55 @@
|
|
|
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 {
|
|
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 { 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";
|
|
10
10
|
import { useEnsureVoorraadDefaults } from "../../hooks/useEnsureVoorraadDefaults.js";
|
|
11
11
|
import { useVoorraadLijst } from "../../hooks/useVoorraadLijst.js";
|
|
12
|
-
import { aantal, FILTER_LABEL, meldingen, volgendeSortering, zichtbareFilters, } from "../../lib/voorraad-lijst.js";
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
12
|
+
import { aantal, FILTER_LABEL, FILTER_HINT, meldingen, volgendeSortering, zichtbareFilters, } from "../../lib/voorraad-lijst.js";
|
|
13
|
+
import { floridayConnectionNotice } from "./FloridayConnectionNotice.js";
|
|
14
|
+
import { useStockIntegrityNotices } from "./StockIntegrityAlert.js";
|
|
15
15
|
import { TelmomentButton } from "./TelmomentButton.js";
|
|
16
|
-
import { VoorraadPagination } from "./VoorraadPagination.js";
|
|
17
16
|
import { InventorySettingsDrawer } from "./InventorySettingsDrawer.js";
|
|
18
17
|
import { OPTIONELE_KOLOMMEN, VoorraadLijstTabel } from "./lijst/VoorraadLijstTabel.js";
|
|
19
18
|
import { AanpassenDialog, AfboekenDialog } from "./lijst/SnelActieDialogen.js";
|
|
20
19
|
import { LijstExportKnop } from "./lijst/LijstExportKnop.js";
|
|
21
20
|
import { ImporteerKnop } from "./lijst/ImporteerKnop.js";
|
|
21
|
+
import { Sneltoetsen } from "./Sneltoetsen.js";
|
|
22
|
+
import { PageHeader } from "../fs-ui/PageHeader.js";
|
|
23
|
+
import { UpdatedAt } from "../fs-ui/UpdatedAt.js";
|
|
24
|
+
import { KpiRow } from "../fs-ui/KpiRow.js";
|
|
25
|
+
import { KpiTile } from "../fs-ui/KpiTile.js";
|
|
26
|
+
import { PillTabs } from "../fs-ui/PillTabs.js";
|
|
27
|
+
import { Toolbar } from "../fs-ui/Toolbar.js";
|
|
28
|
+
import { SearchInput } from "../fs-ui/SearchInput.js";
|
|
29
|
+
import { NoticeBar } from "../fs-ui/NoticeBar.js";
|
|
30
|
+
import { FiltersButton } from "../fs-ui/FiltersButton.js";
|
|
31
|
+
import { FilterChips } from "../fs-ui/FilterChips.js";
|
|
32
|
+
import { FilterPanel, FilterGroup } from "../fs-ui/FilterPanel.js";
|
|
33
|
+
import { Pagination } from "../fs-ui/Pagination.js";
|
|
34
|
+
import { HeaderPrimary } from "../fs-ui/HeaderButton.js";
|
|
35
|
+
import { EmptyState } from "../fs-ui/EmptyState.js";
|
|
36
|
+
import { MobilePrimaryAction } from "../fs-ui/MobilePrimaryAction.js";
|
|
37
|
+
import { DataTable } from "../fs-ui/DataTable.js";
|
|
22
38
|
// Bestaande export blijft (tests en andere modules).
|
|
23
39
|
export { countStock } from "../../lib/count-stock.js";
|
|
24
40
|
const EMPTY_TRIPS = [];
|
|
25
41
|
const KOLOMMEN_KEY = "voorraad.lijst.kolommen.v2";
|
|
26
42
|
const STANDAARD_KOLOMMEN = ["inkomend", "prijs"];
|
|
43
|
+
const FILTER_ICON = {
|
|
44
|
+
alles: _jsx(Layers, {}),
|
|
45
|
+
onder_minimum: _jsx(TrendingDown, {}),
|
|
46
|
+
verpakking: _jsx(PackageX, {}),
|
|
47
|
+
niet_gekoppeld: _jsx(Link2Off, {}),
|
|
48
|
+
verwijderd_floriday: _jsx(Link2Off, {}),
|
|
49
|
+
prijs: _jsx(Euro, {}),
|
|
50
|
+
twijfel: _jsx(HelpCircle, {}),
|
|
51
|
+
gearchiveerd: _jsx(Archive, {}),
|
|
52
|
+
};
|
|
27
53
|
function useKolommen() {
|
|
28
54
|
const [set, setSet] = useState(new Set(STANDAARD_KOLOMMEN));
|
|
29
55
|
useEffect(() => {
|
|
@@ -68,7 +94,6 @@ export function VoorraadPage() {
|
|
|
68
94
|
const navigate = useNavigate();
|
|
69
95
|
const conn = useFloridayConnection();
|
|
70
96
|
const stockConnId = useStockContextId();
|
|
71
|
-
const [zoekInvoer, setZoekInvoer] = useState("");
|
|
72
97
|
const [zoek, setZoek] = useState("");
|
|
73
98
|
const [filter, setFilter] = useState("alles");
|
|
74
99
|
const [sorteer, setSorteer] = useState({ sort: "naam", oplopend: true });
|
|
@@ -76,34 +101,25 @@ export function VoorraadPage() {
|
|
|
76
101
|
const [grootte, setGrootte] = useState(25);
|
|
77
102
|
const [selected, setSelected] = useState(new Set());
|
|
78
103
|
const [drawerOpen, setDrawerOpen] = useState(false);
|
|
104
|
+
const [telmomentOpen, setTelmomentOpen] = useState(false);
|
|
105
|
+
const [filterOpen, setFilterOpen] = useState(false);
|
|
106
|
+
const integriteit = useStockIntegrityNotices();
|
|
79
107
|
const [aanpassen, setAanpassen] = useState(null);
|
|
80
108
|
const [afboeken, setAfboeken] = useState(null);
|
|
81
109
|
const { kolommen, toggle: toggleKolom } = useKolommen();
|
|
82
110
|
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]);
|
|
111
|
+
// Nieuwe zoekopdracht, filter of sortering begint op pagina 1.
|
|
88
112
|
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]);
|
|
113
|
+
const nieuw = useCallback(() => navigate({ to: "/voorraad/nieuw" }), [navigate]);
|
|
101
114
|
const vraag = { zoek, filter, sort: sorteer.sort, oplopend: sorteer.oplopend, pagina, grootte };
|
|
102
|
-
const { data, loading, error, reload } = useVoorraadLijst(vraag);
|
|
115
|
+
const { data, kpi, loading, error, reload } = useVoorraadLijst(vraag);
|
|
103
116
|
const heeftKoppeling = data?.heeft_koppeling ?? false;
|
|
104
117
|
const filters = useMemo(() => zichtbareFilters(heeftKoppeling), [heeftKoppeling]);
|
|
105
118
|
const melding = data ? meldingen(data) : [];
|
|
106
|
-
|
|
119
|
+
// Twijfelgevallen bestaan alleen bij een koppeling; tonen als er iets te kiezen is of als je er al staat.
|
|
120
|
+
const tabs = filters
|
|
121
|
+
.filter((f) => f !== "twijfel" || heeftKoppeling || (data?.tellers?.twijfel ?? 0) > 0 || filter === "twijfel")
|
|
122
|
+
.map((f) => ({ value: f, label: FILTER_LABEL[f], icon: FILTER_ICON[f], hint: FILTER_HINT[f], count: data?.tellers?.[f] ?? null }));
|
|
107
123
|
const toggleSelect = (id) => setSelected((cur) => {
|
|
108
124
|
const next = new Set(cur);
|
|
109
125
|
if (next.has(id))
|
|
@@ -113,16 +129,29 @@ export function VoorraadPage() {
|
|
|
113
129
|
return next;
|
|
114
130
|
});
|
|
115
131
|
const toggleAll = (ids) => setSelected((cur) => (ids.length > 0 && ids.every((id) => cur.has(id)) ? new Set() : new Set(ids)));
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
132
|
+
const selectieBalk = selected.size > 0 && filter !== "twijfel";
|
|
133
|
+
const kpiLaden = kpi === null;
|
|
134
|
+
// Meldingen: één regel per melding, direct onder de kop.
|
|
135
|
+
const verbindingTekst = floridayConnectionNotice(conn.status, conn.error);
|
|
136
|
+
const noticeItems = [
|
|
137
|
+
...(guardTrips.length > 0 ? [_jsxs("span", { children: ["De pagina is gestopt met laden. ", _jsx("span", { role: "button", tabIndex: 0, className: "font-medium underline underline-offset-2", onClick: () => window.location.reload(), children: "Opnieuw laden" })] }, "guard")] : []),
|
|
138
|
+
...(error ? [_jsxs("span", { children: ["De voorraad laden lukt niet. ", _jsx("span", { role: "button", tabIndex: 0, className: "font-medium underline underline-offset-2", onClick: (e) => { e.stopPropagation(); void reload(); }, children: "Opnieuw proberen" })] }, "err")] : []),
|
|
139
|
+
...integriteit.items,
|
|
140
|
+
...(verbindingTekst ? [verbindingTekst] : []),
|
|
141
|
+
...melding.map((m) => m.filter ? (_jsx("span", { role: "button", tabIndex: 0, className: "underline-offset-2 hover:underline", onClick: (e) => { e.stopPropagation(); setFilter(m.filter); }, children: m.tekst }, m.tekst)) : m.tekst),
|
|
142
|
+
];
|
|
143
|
+
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_HINT[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"] }) }));
|
|
144
|
+
return (_jsxs("main", { className: "flex w-full min-w-0 flex-col gap-4 overflow-x-clip p-4 pb-[calc(7rem+env(safe-area-inset-bottom))] 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-10 w-10 p-0 sm:h-9 sm:w-auto sm:px-3", "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"] })] }))] })] }), _jsx(HeaderPrimary, { icon: _jsx(Plus, {}), label: "Nieuw artikel", kbd: "N", onClick: nieuw })] }) }), _jsx(TelmomentButton, { verbergKnop: true, open: telmomentOpen, onOpenChange: setTelmomentOpen, onSaved: reload }), _jsx(NoticeBar, { items: noticeItems }), integriteit.dialog, _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: false }), _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" })] }), _jsx(PillTabs, { tabs: tabs, value: filter, onChange: setFilter, label: "Filter" }), _jsx(Toolbar, { filters: _jsx(FiltersButton, { count: filter !== "alles" ? 1 : 0, onClick: () => setFilterOpen(true) }), chips: _jsx(FilterChips, { chips: filter !== "alles" ? [{ key: "weergave", label: FILTER_HINT[filter], icon: FILTER_ICON[filter], onRemove: () => setFilter("alles") }] : [] }), right: _jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsxs(Button, { variant: "outline", size: "sm", className: "h-9 px-3", "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" }) }), 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(Pagination, { totalCount: data?.totaal ?? 0, page: pagina, pageSize: grootte, onPage: setPagina, onPageSize: setGrootte }), _jsx(FilterPanel, { open: filterOpen, onOpenChange: setFilterOpen, onClear: () => setFilter("alles"), children: _jsx(FilterGroup, { title: "Weergave", value: filter, onChange: setFilter, options: tabs.map((t) => ({ value: t.value, label: FILTER_HINT[t.value] })) }) }), _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
145
|
}
|
|
122
146
|
function TwijfelTabel({ rijen, loading, onGekozen }) {
|
|
123
147
|
const [open, setOpen] = useState(null);
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
148
|
+
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: [
|
|
149
|
+
{ key: "naam", header: "Artikel in Floriday", width: "36%", render: (t) => _jsx("span", { className: "font-medium", children: t.naam ?? "(naamloos)" }) },
|
|
150
|
+
{ key: "reden", header: "Reden", width: "36%", render: (t) => _jsx("span", { className: "text-muted-foreground", children: t.reden ?? "Meerdere mogelijke artikelen; niets aangemaakt" }) },
|
|
151
|
+
{ key: "kandidaten", header: "Mogelijke artikelen", width: "14%", render: (t) => _jsx("span", { className: "tabular-nums", children: t.kandidaten }) },
|
|
152
|
+
{
|
|
153
|
+
key: "actie", header: "Actie", width: "14%",
|
|
154
|
+
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" })),
|
|
155
|
+
},
|
|
156
|
+
], 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
157
|
}
|
|
@@ -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":"AAmGA,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,+BA6enI"}
|
|
@@ -1,8 +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 { lazy, Suspense, useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
3
|
-
import { Link, useBlocker } from "@tanstack/react-router";
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
3
|
+
import { Link, useBlocker, useRouterState } from "@tanstack/react-router";
|
|
4
|
+
import { datum } from "../../../lib/notatie.js";
|
|
5
|
+
import { Button, Card, Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger, Input, Label, Skeleton, Tabs, TabsContent, toast, } from "@flowselections/core";
|
|
6
|
+
import { AlertTriangle, Archive, ArchiveRestore, ArrowDownToLine, ArrowLeft, Boxes, Layers, Link2, Loader2, Lock, MoreHorizontal, Minus, Package, Send, SlidersHorizontal } from "lucide-react";
|
|
6
7
|
import { supabase } from "../../../integrations/supabase/client.js";
|
|
7
8
|
import { dbFoutTekst } from "../../../lib/db-error-message.js";
|
|
8
9
|
import { ARTIKEL_GEWIJZIGD } from "../../../lib/artikel-gewijzigd.js";
|
|
@@ -11,6 +12,14 @@ import { STATUS_INFO, aantal, geld } from "../../../lib/voorraad-lijst.js";
|
|
|
11
12
|
import { AanpassenDialog, AfboekenDialog } from "../lijst/SnelActieDialogen.js";
|
|
12
13
|
import { TwijfelKiezen } from "./TwijfelKiezen.js";
|
|
13
14
|
import { OpslaanContext } from "./opslaan-context.js";
|
|
15
|
+
import { ArtikelStatusIcoon } from "../lijst/VoorraadLijstTabel.js";
|
|
16
|
+
import { PageHeader } from "../../fs-ui/PageHeader.js";
|
|
17
|
+
import { KpiRow } from "../../fs-ui/KpiRow.js";
|
|
18
|
+
import { KpiTile } from "../../fs-ui/KpiTile.js";
|
|
19
|
+
import { PillTabs } from "../../fs-ui/PillTabs.js";
|
|
20
|
+
import { InlineAlert } from "../../fs-ui/InlineAlert.js";
|
|
21
|
+
import { HoverTip } from "../../fs-ui/HoverTip.js";
|
|
22
|
+
import { MobilePrimaryAction } from "../../fs-ui/MobilePrimaryAction.js";
|
|
14
23
|
// Tabbladinhoud laadt pas als je het tabblad opent.
|
|
15
24
|
const StockMutationHistoryCard = lazy(() => import("../StockMutationHistoryCard.js").then((m) => ({ default: m.StockMutationHistoryCard })));
|
|
16
25
|
const OrderHistoryCard = lazy(() => import("../OrderHistoryCard.js").then((m) => ({ default: m.OrderHistoryCard })));
|
|
@@ -21,6 +30,12 @@ const ProductCustomFieldsCard = lazy(() => import("../ProductCustomFieldsCard.js
|
|
|
21
30
|
const ProductLinkCard = lazy(() => import("../ProductLinkCard.js").then((m) => ({ default: m.ProductLinkCard })));
|
|
22
31
|
const FloridayInfoCard = lazy(() => import("../FloridayInfoCard.js").then((m) => ({ default: m.FloridayInfoCard })));
|
|
23
32
|
const ArtikelWizard = lazy(() => import("../ArtikelWizard.js").then((m) => ({ default: m.ArtikelWizard })));
|
|
33
|
+
const TAB_ICON = {
|
|
34
|
+
voorraad: _jsx(Package, {}),
|
|
35
|
+
verpakking: _jsx(Boxes, {}),
|
|
36
|
+
koppelingen: _jsx(Link2, {}),
|
|
37
|
+
floriday: _jsx(Send, {}),
|
|
38
|
+
};
|
|
24
39
|
const TAB_LABEL = {
|
|
25
40
|
voorraad: "Voorraad",
|
|
26
41
|
verpakking: "Verpakking en samenstelling",
|
|
@@ -28,7 +43,7 @@ const TAB_LABEL = {
|
|
|
28
43
|
floriday: "Floriday",
|
|
29
44
|
};
|
|
30
45
|
function Laden() {
|
|
31
|
-
return (
|
|
46
|
+
return _jsx(Skeleton, { className: "h-40 w-full rounded-xl", "aria-label": "Laden\u2026" });
|
|
32
47
|
}
|
|
33
48
|
function toon(v) {
|
|
34
49
|
if (v === null || v === undefined || v === "")
|
|
@@ -239,7 +254,7 @@ export function ArtikelPagina({ id, resumeJobId, onNietGevonden }) {
|
|
|
239
254
|
if (alleConflicten.length)
|
|
240
255
|
setConflicten(alleConflicten);
|
|
241
256
|
if (opgeslagen)
|
|
242
|
-
toast.success(alleConflicten.length ? "Deels opgeslagen; kies bij de conflicten." : "Opgeslagen
|
|
257
|
+
toast.success(alleConflicten.length ? "Deels opgeslagen; kies bij de conflicten." : detail?.heeft_koppeling ? "Opgeslagen. Niet naar Floriday gestuurd." : "Opgeslagen.");
|
|
243
258
|
await laad();
|
|
244
259
|
}
|
|
245
260
|
/** Conflict: neem hun waarde over voor dat veld en leg de nieuwe stand vast. */
|
|
@@ -272,7 +287,7 @@ export function ArtikelPagina({ id, resumeJobId, onNietGevonden }) {
|
|
|
272
287
|
return;
|
|
273
288
|
}
|
|
274
289
|
setArchief(null);
|
|
275
|
-
toast.success(archiefKeuze === "floriday" ? "Artikel gearchiveerd. De voorraad op Floriday gaat
|
|
290
|
+
toast.success(archiefKeuze === "floriday" ? "Artikel gearchiveerd. De voorraad op Floriday gaat naar 0 zodra Floriday het verwerkt." : "Artikel gearchiveerd. Op Floriday is niets veranderd.");
|
|
276
291
|
await laad();
|
|
277
292
|
}
|
|
278
293
|
async function terugzetten() {
|
|
@@ -284,11 +299,23 @@ export function ArtikelPagina({ id, resumeJobId, onNietGevonden }) {
|
|
|
284
299
|
toast.success("Artikel staat weer in de voorraad.");
|
|
285
300
|
await laad();
|
|
286
301
|
}
|
|
302
|
+
// Vanaf de controlepagina: open het overzicht vooraf (er wordt niets verstuurd zonder bevestiging).
|
|
303
|
+
const vraagOverzicht = useRouterState({ select: (st) => Boolean(st.location.state?.stuurOverzicht) });
|
|
304
|
+
const overzichtGeopend = useRef(false);
|
|
305
|
+
useEffect(() => {
|
|
306
|
+
if (!vraagOverzicht || overzichtGeopend.current || !detail)
|
|
307
|
+
return;
|
|
308
|
+
overzichtGeopend.current = true;
|
|
309
|
+
if (floridayActie(detail)) {
|
|
310
|
+
setFloridayOpen(true);
|
|
311
|
+
setTab("floriday");
|
|
312
|
+
}
|
|
313
|
+
}, [vraagOverzicht, detail]); // eslint-disable-line react-hooks/exhaustive-deps
|
|
287
314
|
if (fout) {
|
|
288
|
-
return
|
|
315
|
+
return (_jsxs("main", { className: "flex flex-col gap-4 p-4 pb-24 sm:p-6 sm:pb-24", 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" }), " Voorraad"] }), _jsx(InlineAlert, { tone: "fout", action: _jsx(Button, { size: "sm", variant: "outline", onClick: () => { setFout(null); geladen.current = false; void laad(); }, children: "Opnieuw proberen" }), children: fout })] }));
|
|
289
316
|
}
|
|
290
317
|
if (!detail || !nieuw) {
|
|
291
|
-
return
|
|
318
|
+
return (_jsxs("main", { className: "flex flex-col gap-4 p-4 pb-24 sm:p-6 sm:pb-24", "aria-busy": "true", children: [_jsx(Skeleton, { className: "h-4 w-24" }), _jsxs("div", { className: "flex items-start gap-4", children: [_jsx(Skeleton, { className: "h-16 w-16 rounded-xl" }), _jsxs("div", { className: "space-y-2", children: [_jsx(Skeleton, { className: "h-7 w-64" }), _jsx(Skeleton, { className: "h-3 w-32" })] })] }), _jsx(KpiRow, { children: [0, 1, 2, 3].map((i) => _jsx(KpiTile, { title: "", value: "", loading: true }, i)) }), _jsx(Skeleton, { className: "h-9 w-80 rounded-lg" }), _jsx(Skeleton, { className: "h-48 w-full rounded-xl" })] }));
|
|
292
319
|
}
|
|
293
320
|
const status = artikelStatus({ ...detail, velden: nieuw });
|
|
294
321
|
const info = STATUS_INFO[status];
|
|
@@ -297,17 +324,13 @@ export function ArtikelPagina({ id, resumeJobId, onNietGevonden }) {
|
|
|
297
324
|
if (floridayOpen && !tabs.includes("floriday") && actie === "op_floriday")
|
|
298
325
|
tabs.push("floriday");
|
|
299
326
|
const rij = alsLijstRij(detail);
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
["Prijs per stuk", geld(detail.velden.sale_price === null ? null : Number(detail.velden.sale_price))],
|
|
306
|
-
].map(([l, v], i) => (_jsxs("div", { children: [_jsx("dt", { className: "text-xs text-muted-foreground", children: l }), _jsx("dd", { className: `text-lg font-semibold tabular-nums ${i === 3 && rij.onder_minimum ? "text-destructive" : ""}`, children: v })] }, l))) }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsxs(Button, { onClick: doeOpslaan, disabled: !vuil || opslaan || Boolean(prijsFout) || Boolean(aantalFout), children: [opslaan && _jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }), "Opslaan"] }), actie && (_jsxs(Button, { variant: "outline", onClick: () => { setFloridayOpen(true); setTab("floriday"); }, children: [_jsx(Send, { className: "mr-2 h-4 w-4" }), actie === "sturen" ? "Naar Floriday sturen" : "Op Floriday zetten"] })), _jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsx(Button, { variant: "ghost", size: "icon", "aria-label": "Meer acties", children: _jsx(MoreHorizontal, { className: "h-4 w-4" }) }) }), _jsx(DropdownMenuContent, { align: "end", children: detail.gearchiveerd_at
|
|
307
|
-
? _jsx(DropdownMenuItem, { onClick: terugzetten, children: "Terugzetten in voorraad" })
|
|
308
|
-
: _jsx(DropdownMenuItem, { onClick: () => { setArchiefKeuze("hier"); setArchief({}); }, children: "Archiveren\u2026" }) })] })] })] }) }), detail.gearchiveerd_at && (detail.orders_gearchiveerd ?? 0) > 0 && (_jsxs("div", { role: "alert", className: "flex flex-wrap items-center gap-2 rounded-md border border-destructive/40 bg-destructive/10 p-3 text-sm", children: [_jsx(AlertTriangle, { className: "h-4 w-4 text-destructive" }), _jsxs("span", { children: ["Dit artikel is gearchiveerd, maar er ", detail.orders_gearchiveerd === 1 ? "is 1 open orderregel" : `zijn ${detail.orders_gearchiveerd} open orderregels`, " binnengekomen. De orders zijn gewoon gekoppeld."] }), _jsx(Button, { size: "sm", variant: "outline", onClick: terugzetten, children: "Terugzetten in voorraad" })] })), _jsx(OpslaanContext.Provider, { value: register, children: _jsxs(Tabs, { value: tab, onValueChange: setTab, children: [_jsx(TabsList, { children: tabs.map((t) => _jsx(TabsTrigger, { value: t, children: TAB_LABEL[t] }, t)) }), _jsxs(TabsContent, { value: "voorraad", forceMount: bezocht.has("voorraad") ? true : undefined, className: "space-y-4 data-[state=inactive]:hidden", children: [_jsxs(Card, { className: "p-4 space-y-4", children: [_jsxs("div", { className: "flex flex-wrap items-center justify-between gap-2", children: [_jsx("h2", { className: "font-medium", children: "Artikelgegevens" }), _jsx("span", { className: "text-xs text-muted-foreground", children: "Opslaan is alleen lokaal; er gaat niets automatisch naar Floriday." })] }), _jsx("div", { className: "grid gap-3 sm:grid-cols-2 lg:grid-cols-5", children: ARTIKEL_VELDEN.map((k) => (_jsxs("div", { className: "space-y-1", children: [_jsx(Label, { htmlFor: `veld-${k}`, children: VELD_LABEL[k] }), _jsx(Input, { id: `veld-${k}`, value: invoer[k] ?? "", inputMode: k === "sale_price" ? "decimal" : undefined, onChange: (e) => setInvoer((s) => ({ ...s, [k]: e.target.value })) }), k === "sale_price" && prijsFout && _jsx("p", { className: "text-xs text-destructive", children: prijsFout })] }, k))) })] }), _jsxs(Card, { className: "p-4 flex flex-wrap items-center justify-between gap-2", children: [_jsxs("div", { className: "grid w-full gap-3 sm:grid-cols-3", children: [VOORRAAD_VELDEN.map((k) => (_jsxs("div", { className: "space-y-1", children: [_jsx(Label, { htmlFor: `voorraad-${k}`, children: VOORRAAD_LABEL[k] }), _jsx(Input, { id: `voorraad-${k}`, value: invoer[k] ?? "", inputMode: k === "notes" ? undefined : "numeric", onChange: (e) => setInvoer((s) => ({ ...s, [k]: e.target.value })) })] }, k))), aantalFout && _jsx("p", { className: "text-xs text-destructive sm:col-span-3", children: aantalFout })] }), _jsxs("div", { className: "text-sm", children: [_jsx("div", { className: "font-medium", children: "Voorraad" }), _jsxs("div", { className: "text-muted-foreground", children: ["Liggend wijzig je met Aanpassen of Afboeken \u00B7 Telmoment ", detail.telmoment ? new Date(detail.telmoment).toLocaleString("nl-NL") : _jsx("span", { className: "text-destructive", children: "ontbreekt" })] })] }), _jsxs("div", { className: "flex gap-2", children: [_jsx(Button, { variant: "outline", onClick: () => setAanpassen(true), disabled: Boolean(detail.gearchiveerd_at), children: "Aanpassen" }), _jsx(Button, { variant: "outline", onClick: () => setAfboeken(true), disabled: Boolean(detail.gearchiveerd_at), children: "Afboeken" })] })] }), _jsxs(Suspense, { fallback: _jsx(Laden, {}), children: [_jsx(StockMutationHistoryCard, { tradeItemKey: detail.voorraad_sleutel, productId: detail.id, sleutels: detail.controle?.mutatie_sleutels ?? undefined }), _jsx(OrderHistoryCard, { tradeItemKey: detail.voorraad_sleutel, productName: detail.naam, productId: detail.id, productIds: detail.controle?.zelfde_sleutel_ids }), _jsx(ProductCustomFieldsCard, { productId: detail.id, beginVelden: detail.eigen_velden?.definities, beginWaarden: detail.eigen_velden?.waarden })] })] }), _jsx(TabsContent, { value: "verpakking", forceMount: bezocht.has("verpakking") ? true : undefined, className: "space-y-4 data-[state=inactive]:hidden", children: _jsxs(Suspense, { fallback: _jsx(Laden, {}), children: [_jsx(ProductStructureCard, { productId: detail.id }), _jsx(ArtikelCompositionCard, { productId: detail.id, floridayId: detail.floriday_id }), _jsx(TrayStickerCard, { productId: detail.id })] }) }), _jsxs(TabsContent, { value: "koppelingen", className: "space-y-4", children: [_jsx(TwijfelKiezen, { productId: detail.id, onGekozen: () => void laad() }), _jsxs(Suspense, { fallback: _jsx(Laden, {}), children: [detail.floriday_id && detail.heeft_koppeling ? (_jsx(FloridayInfoCard, { floridayTradeItemId: detail.floriday_id, local: { name: detail.naam, articleNumber: detail.artikelnummer, gtin: detail.velden.barcode ?? null } })) : (_jsx(Card, { className: "p-4 text-sm text-muted-foreground", children: detail.heeft_koppeling ? (detail.floriday_verwijderd_at ? "Dit artikel is in Floriday verwijderd." : "Dit artikel is niet aan Floriday gekoppeld.") : "Er is geen Floriday-koppeling in deze omgeving; het artikel werkt volledig lokaal." })), _jsx(ProductLinkCard, { tradeItemKey: detail.voorraad_sleutel, productName: detail.naam })] })] }), tabs.includes("floriday") && (_jsx(TabsContent, { value: "floriday", children: _jsx(Suspense, { fallback: _jsx(Laden, {}), children: _jsx(ArtikelWizard, { productId: detail.id, resumeJobId: resumeJobId, embedded: true }) }) }))] }) }), _jsx(AanpassenDialog, { rij: aanpassen ? rij : null, onClose: () => setAanpassen(false), onDone: () => { setAanpassen(false); void laad(); } }), _jsx(AfboekenDialog, { rij: afboeken ? rij : null, connectionId: null, onClose: () => setAfboeken(false), onDone: () => { setAfboeken(false); void laad(); } }), _jsx(Dialog, { open: Boolean(conflicten && conflicten.length), onOpenChange: (o) => !o && setConflicten(null), children: _jsxs(DialogContent, { children: [_jsxs(DialogHeader, { children: [_jsx(DialogTitle, { children: "Iemand anders heeft dit ook gewijzigd" }), _jsx(DialogDescription, { children: "Er is niets opgeslagen. Kies per veld welke waarde blijft." })] }), _jsx("div", { className: "space-y-3", children: (conflicten ?? []).map((c) => (_jsxs("div", { className: "rounded-md border p-3 text-sm space-y-2", children: [_jsx("div", { className: "font-medium", children: VELD_LABEL[c.veld] ?? VOORRAAD_LABEL[c.veld] }), _jsxs("div", { className: "grid grid-cols-2 gap-2", children: [_jsxs("div", { children: [_jsx("div", { className: "text-xs text-muted-foreground", children: "Jouw wijziging" }), toon(c.jouw)] }), _jsxs("div", { children: [_jsx("div", { className: "text-xs text-muted-foreground", children: "Hun wijziging" }), toon(c.hun)] })] }), _jsxs("div", { className: "flex gap-2", children: [_jsx(Button, { size: "sm", variant: "outline", onClick: () => houdJouw(c), children: "Mijn waarde houden" }), _jsx(Button, { size: "sm", variant: "outline", onClick: () => neemHunOver(c), children: "Hun waarde overnemen" })] })] }, c.veld))) })] }) }), _jsx(Dialog, { open: archief !== null, onOpenChange: (o) => !o && setArchief(null), children: _jsxs(DialogContent, { children: [_jsxs(DialogHeader, { children: [_jsx(DialogTitle, { children: "Artikel archiveren" }), _jsx(DialogDescription, { children: "Het artikel wordt nooit verwijderd. Je kunt het later terugzetten." })] }), archief?.waarschuwing && (_jsxs("div", { className: "flex gap-2 rounded-md border border-destructive/40 bg-destructive/10 p-3 text-sm", children: [_jsx(AlertTriangle, { className: "h-4 w-4 shrink-0 text-destructive" }), _jsxs("span", { children: ["Let op: er ", archief.waarschuwing.open_orders === 1 ? "is 1 open order" : `zijn ${archief.waarschuwing.open_orders} open orders`, " en ", aantal(archief.waarschuwing.gereserveerd), " gereserveerd. Die blijven bestaan."] })] })), _jsxs("div", { className: "space-y-2 text-sm", children: [_jsxs("label", { className: "flex gap-2 items-start", children: [_jsx("input", { type: "radio", checked: archiefKeuze === "hier", onChange: () => setArchiefKeuze("hier") }), _jsxs("span", { children: [_jsx("strong", { children: detail.floriday_id && detail.heeft_koppeling ? "Alleen hier." : "Archiveren." }), " ", detail.floriday_id && detail.heeft_koppeling && !detail.floriday_verwijderd_at
|
|
327
|
+
const opslaanUit = !vuil || opslaan || Boolean(prijsFout) || Boolean(aantalFout);
|
|
328
|
+
const gearchiveerd = Boolean(detail.gearchiveerd_at);
|
|
329
|
+
return (_jsxs("main", { className: "flex w-full min-w-0 flex-col gap-4 overflow-x-clip p-4 pb-[calc(7rem+env(safe-area-inset-bottom))] 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"] }), media: _jsx("div", { className: "flex h-16 w-16 shrink-0 items-center justify-center overflow-hidden rounded-xl border border-border bg-muted", children: detail.foto ? _jsx("img", { src: detail.foto, alt: detail.naam, className: "h-full w-full object-cover" }) : _jsx(Package, { className: "h-7 w-7 text-muted-foreground" }) }), title: detail.naam, updated: _jsx("span", { className: "block break-words", children: detail.artikelnummer ?? "Geen artikelnummer" }), meta: _jsxs(_Fragment, { children: [_jsx(ArtikelStatusIcoon, { status: status }), _jsx("span", { className: status === "ok" ? "text-muted-foreground" : "font-medium", children: info.label }), info.actie && info.tab && tabs.includes(info.tab) && (_jsx("button", { type: "button", className: "text-primary underline-offset-2 hover:underline", onClick: () => setTab(info.tab), children: info.actie })), _jsx("span", { className: "text-muted-foreground", children: "\u00B7" }), _jsxs("span", { className: "tabular-nums", children: [geld(detail.velden.sale_price === null ? null : Number(detail.velden.sale_price)), " per stuk"] }), detail.floriday_id && detail.heeft_koppeling && (_jsx(HoverTip, { label: "Gekoppeld met Floriday", content: "Gekoppeld met Floriday", className: "h-5 w-5 text-muted-foreground [&_svg]:h-3.5 [&_svg]:w-3.5", children: _jsx(Link2, {}) })), vuil && _jsx("span", { className: "text-xs font-medium text-warning", children: "Niet opgeslagen" })] }), actions: _jsxs(_Fragment, { children: [_jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsxs(Button, { variant: "outline", size: "sm", className: "h-10 w-10 p-0 sm:h-9 sm:w-auto sm:px-3", "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, { disabled: gearchiveerd, onSelect: () => setAanpassen(true), children: [_jsx(SlidersHorizontal, { className: "mr-2 h-4 w-4" }), "Voorraad aanpassen"] }), _jsxs(DropdownMenuItem, { disabled: gearchiveerd, onSelect: () => setAfboeken(true), children: [_jsx(Minus, { className: "mr-2 h-4 w-4" }), "Afboeken"] }), actie && (_jsxs(_Fragment, { children: [_jsx(DropdownMenuSeparator, {}), _jsxs(DropdownMenuItem, { onSelect: () => { setFloridayOpen(true); setTab("floriday"); }, children: [_jsx(Send, { className: "mr-2 h-4 w-4" }), actie === "sturen" ? "Naar Floriday sturen" : "Op Floriday zetten"] })] })), _jsx(DropdownMenuSeparator, {}), gearchiveerd
|
|
330
|
+
? _jsxs(DropdownMenuItem, { onSelect: terugzetten, children: [_jsx(ArchiveRestore, { className: "mr-2 h-4 w-4" }), "Terugzetten in voorraad"] })
|
|
331
|
+
: _jsxs(DropdownMenuItem, { onSelect: () => { setArchiefKeuze("hier"); setArchief({}); }, children: [_jsx(Archive, { className: "mr-2 h-4 w-4" }), "Archiveren\u2026"] })] })] }), _jsxs(Button, { size: "sm", className: "hidden sm:inline-flex", onClick: doeOpslaan, disabled: opslaanUit, children: [opslaan && _jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }), "Opslaan"] })] }) }), gearchiveerd && (detail.orders_gearchiveerd ?? 0) > 0 && (_jsxs(InlineAlert, { tone: "let_op", action: _jsx(Button, { size: "sm", variant: "outline", className: "h-8", onClick: terugzetten, children: "Terugzetten in voorraad" }), children: ["Dit artikel is gearchiveerd, maar er ", detail.orders_gearchiveerd === 1 ? "is 1 open orderregel" : `zijn ${detail.orders_gearchiveerd} open orderregels`, " binnengekomen. De orders zijn gewoon gekoppeld."] })), _jsxs(KpiRow, { children: [_jsx(KpiTile, { title: "Liggend", icon: _jsx(Package, {}), value: aantal(detail.liggend), sub: _jsxs(_Fragment, { children: ["Telmoment ", detail.telmoment ? datum(detail.telmoment) : _jsx("span", { className: "text-destructive", children: "ontbreekt" })] }) }), _jsx(KpiTile, { title: "Gereserveerd", icon: _jsx(Lock, {}), value: aantal(detail.gereserveerd), sub: "Voor open orders" }), _jsx(KpiTile, { title: "Vrij beschikbaar", icon: _jsx(Layers, {}), tone: rij.onder_minimum ? "warning" : "default", value: _jsx("span", { className: rij.onder_minimum ? "text-destructive" : "", children: aantal(detail.vrij) }), sub: detail.minimum > 0 ? (rij.onder_minimum ? _jsxs("span", { className: "text-destructive", children: ["Onder minimum (", aantal(detail.minimum), ")"] }) : `Minimum ${aantal(detail.minimum)}`) : "Geen minimum" }), _jsx(KpiTile, { title: "Inkomend", icon: _jsx(ArrowDownToLine, {}), value: aantal(detail.inkomend), sub: "Stuks verwacht" })] }), _jsx(OpslaanContext.Provider, { value: register, children: _jsxs(Tabs, { value: tab, onValueChange: setTab, children: [_jsx(PillTabs, { tabs: tabs.map((t) => ({ value: t, label: TAB_LABEL[t], icon: TAB_ICON[t] })), value: tab, onChange: setTab, label: "Onderdelen van het artikel" }), _jsxs(TabsContent, { value: "voorraad", forceMount: bezocht.has("voorraad") ? true : undefined, className: "mt-4 space-y-4 data-[state=inactive]:hidden", children: [_jsxs(Card, { className: "space-y-4 rounded-xl p-4 shadow-sm sm:p-5", children: [_jsxs("div", { className: "flex flex-wrap items-center justify-between gap-2", children: [_jsx("h2", { className: "text-base font-semibold", children: "Artikelgegevens" }), detail.heeft_koppeling && _jsx("span", { className: "text-xs text-muted-foreground", children: "Opslaan bewaart alleen in je eigen administratie. Naar Floriday sturen doe je apart." })] }), _jsx("div", { className: "grid gap-3 sm:grid-cols-2 lg:grid-cols-5", children: ARTIKEL_VELDEN.map((k) => (_jsxs("div", { className: "space-y-1", children: [_jsx(Label, { htmlFor: `veld-${k}`, children: VELD_LABEL[k] }), _jsx(Input, { id: `veld-${k}`, className: "h-10 rounded-lg text-base sm:h-9 sm:text-sm", value: invoer[k] ?? "", inputMode: k === "sale_price" ? "decimal" : undefined, onChange: (e) => setInvoer((s) => ({ ...s, [k]: e.target.value })) }), k === "sale_price" && prijsFout && _jsx("p", { className: "text-xs text-destructive", children: prijsFout })] }, k))) })] }), _jsxs(Card, { className: "space-y-4 rounded-xl p-4 shadow-sm sm:p-5", children: [_jsxs("div", { className: "flex flex-wrap items-center justify-between gap-2", children: [_jsxs("div", { children: [_jsx("h2", { className: "text-base font-semibold", children: "Voorraad" }), _jsxs("p", { className: "text-xs text-muted-foreground", children: ["Liggend wijzig je met Aanpassen of Afboeken \u00B7 Telmoment ", detail.telmoment ? new Date(detail.telmoment).toLocaleString("nl-NL") : _jsx("span", { className: "text-destructive", children: "ontbreekt" })] })] }), _jsxs("div", { className: "flex gap-2", children: [_jsxs(Button, { variant: "outline", size: "sm", className: "h-9", onClick: () => setAanpassen(true), disabled: gearchiveerd, children: [_jsx(SlidersHorizontal, { className: "mr-2 h-4 w-4" }), "Aanpassen"] }), _jsxs(Button, { variant: "outline", size: "sm", className: "h-9", onClick: () => setAfboeken(true), disabled: gearchiveerd, children: [_jsx(Minus, { className: "mr-2 h-4 w-4" }), "Afboeken"] })] })] }), _jsxs("div", { className: "grid w-full gap-3 sm:grid-cols-3", children: [VOORRAAD_VELDEN.map((k) => (_jsxs("div", { className: "space-y-1", children: [_jsx(Label, { htmlFor: `voorraad-${k}`, children: VOORRAAD_LABEL[k] }), _jsx(Input, { id: `voorraad-${k}`, className: "h-10 rounded-lg text-base sm:h-9 sm:text-sm", value: invoer[k] ?? "", inputMode: k === "notes" ? undefined : "numeric", onChange: (e) => setInvoer((s) => ({ ...s, [k]: e.target.value })) })] }, k))), aantalFout && _jsx("p", { className: "text-xs text-destructive sm:col-span-3", children: aantalFout })] })] }), _jsxs(Suspense, { fallback: _jsx(Laden, {}), children: [_jsx(StockMutationHistoryCard, { tradeItemKey: detail.voorraad_sleutel, productId: detail.id, sleutels: detail.controle?.mutatie_sleutels ?? undefined }), _jsx(OrderHistoryCard, { tradeItemKey: detail.voorraad_sleutel, productName: detail.naam, productId: detail.id, productIds: detail.controle?.zelfde_sleutel_ids }), _jsx(ProductCustomFieldsCard, { productId: detail.id, beginVelden: detail.eigen_velden?.definities, beginWaarden: detail.eigen_velden?.waarden })] })] }), _jsx(TabsContent, { value: "verpakking", forceMount: bezocht.has("verpakking") ? true : undefined, className: "mt-4 space-y-4 data-[state=inactive]:hidden", children: _jsxs(Suspense, { fallback: _jsx(Laden, {}), children: [_jsx(ProductStructureCard, { productId: detail.id }), _jsx(ArtikelCompositionCard, { productId: detail.id, floridayId: detail.floriday_id }), _jsx(TrayStickerCard, { productId: detail.id })] }) }), _jsxs(TabsContent, { value: "koppelingen", className: "mt-4 space-y-4", children: [_jsx(TwijfelKiezen, { productId: detail.id, onGekozen: () => void laad() }), _jsxs(Suspense, { fallback: _jsx(Laden, {}), children: [detail.floriday_id && detail.heeft_koppeling ? (_jsx(FloridayInfoCard, { floridayTradeItemId: detail.floriday_id, local: { name: detail.naam, articleNumber: detail.artikelnummer, gtin: detail.velden.barcode ?? null } })) : detail.heeft_koppeling ? (_jsx(Card, { className: "rounded-xl p-4 text-sm text-muted-foreground shadow-sm", children: detail.floriday_verwijderd_at ? "Dit artikel is in Floriday verwijderd." : "Dit artikel is niet aan Floriday gekoppeld." })) : null, _jsx(ProductLinkCard, { tradeItemKey: detail.voorraad_sleutel, productName: detail.naam })] })] }), tabs.includes("floriday") && (_jsx(TabsContent, { value: "floriday", className: "mt-4", children: _jsx(Suspense, { fallback: _jsx(Laden, {}), children: _jsx(ArtikelWizard, { productId: detail.id, resumeJobId: resumeJobId, embedded: true }) }) }))] }) }), _jsx(MobilePrimaryAction, { onClick: () => void doeOpslaan(), disabled: opslaanUit, children: opslaan ? "Opslaan…" : "Opslaan" }), _jsx(AanpassenDialog, { rij: aanpassen ? rij : null, onClose: () => setAanpassen(false), onDone: () => { setAanpassen(false); void laad(); } }), _jsx(AfboekenDialog, { rij: afboeken ? rij : null, connectionId: null, onClose: () => setAfboeken(false), onDone: () => { setAfboeken(false); void laad(); } }), _jsx(Dialog, { open: Boolean(conflicten && conflicten.length), onOpenChange: (o) => !o && setConflicten(null), children: _jsxs(DialogContent, { children: [_jsxs(DialogHeader, { children: [_jsx(DialogTitle, { children: "Iemand anders heeft dit ook gewijzigd" }), _jsx(DialogDescription, { children: "Er is niets opgeslagen. Kies per veld welke waarde blijft." })] }), _jsx("div", { className: "space-y-3", children: (conflicten ?? []).map((c) => (_jsxs("div", { className: "rounded-md border p-3 text-sm space-y-2", children: [_jsx("div", { className: "font-medium", children: VELD_LABEL[c.veld] ?? VOORRAAD_LABEL[c.veld] }), _jsxs("div", { className: "grid grid-cols-2 gap-2", children: [_jsxs("div", { children: [_jsx("div", { className: "text-xs text-muted-foreground", children: "Jouw wijziging" }), toon(c.jouw)] }), _jsxs("div", { children: [_jsx("div", { className: "text-xs text-muted-foreground", children: "Hun wijziging" }), toon(c.hun)] })] }), _jsxs("div", { className: "flex gap-2", children: [_jsx(Button, { size: "sm", variant: "outline", onClick: () => houdJouw(c), children: "Mijn waarde houden" }), _jsx(Button, { size: "sm", variant: "outline", onClick: () => neemHunOver(c), children: "Hun waarde overnemen" })] })] }, c.veld))) })] }) }), _jsx(Dialog, { open: archief !== null, onOpenChange: (o) => !o && setArchief(null), children: _jsxs(DialogContent, { children: [_jsxs(DialogHeader, { children: [_jsx(DialogTitle, { children: "Artikel archiveren" }), _jsx(DialogDescription, { children: "Het artikel wordt nooit verwijderd. Je kunt het later terugzetten." })] }), archief?.waarschuwing && (_jsxs("div", { className: "flex gap-2 rounded-md border border-destructive/40 bg-destructive/10 p-3 text-sm", children: [_jsx(AlertTriangle, { className: "h-4 w-4 shrink-0 text-destructive" }), _jsxs("span", { children: ["Let op: er ", archief.waarschuwing.open_orders === 1 ? "is 1 open order" : `zijn ${archief.waarschuwing.open_orders} open orders`, " en ", aantal(archief.waarschuwing.gereserveerd), " gereserveerd. Die blijven bestaan."] })] })), _jsxs("div", { className: "space-y-2 text-sm", children: [_jsxs("label", { className: "flex gap-2 items-start", children: [_jsx("input", { type: "radio", checked: archiefKeuze === "hier", onChange: () => setArchiefKeuze("hier") }), _jsxs("span", { children: [_jsx("strong", { children: detail.floriday_id && detail.heeft_koppeling ? "Alleen hier." : "Archiveren." }), " ", detail.floriday_id && detail.heeft_koppeling && !detail.floriday_verwijderd_at
|
|
309
332
|
? "Floriday blijft ongewijzigd: het artikel blijft op Floriday te koop."
|
|
310
333
|
: "Het artikel verdwijnt uit de voorraadlijst en de tellers."] })] }), detail.floriday_id && detail.heeft_koppeling && !detail.floriday_verwijderd_at && (_jsxs("label", { className: "flex gap-2 items-start", children: [_jsx("input", { type: "radio", checked: archiefKeuze === "floriday", onChange: () => setArchiefKeuze("floriday") }), _jsxs("span", { children: [_jsx("strong", { children: "Ook in Floriday." }), " De voorraad op Floriday wordt 0."] })] })), archiefKeuze === "floriday" && (_jsxs("div", { className: "rounded-md border p-3 space-y-1", children: [_jsx("div", { className: "font-medium", children: "Overzicht vooraf" }), _jsx("div", { children: "Voorraad op Floriday: wordt 0 (niet meer te koop)." }), _jsxs("div", { children: ["Je eigen voorraad blijft ", aantal(detail.liggend), " liggend."] }), _jsx("div", { className: "text-muted-foreground", children: detail.floriday_stuurt
|
|
311
|
-
? "
|
|
334
|
+
? "Wordt op de gewone manier naar Floriday gestuurd. Staat het versturen naar Floriday stil, dan wacht dit tot het weer aan staat."
|
|
312
335
|
: "Floriday is voor dit artikel leidend: er wordt niets verstuurd tot het systeem de voorraad stuurt." })] }))] }), _jsxs(DialogFooter, { children: [_jsx(Button, { variant: "ghost", onClick: () => setArchief(null), children: "Annuleren" }), _jsx(Button, { variant: "destructive", disabled: bezig, onClick: () => archiveren(Boolean(archief?.waarschuwing)), children: archief?.waarschuwing ? "Toch archiveren" : "Archiveren" })] })] }) })] }));
|
|
313
336
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ImporteerKnop.d.ts","sourceRoot":"","sources":["../../../../src/components/voorraad/lijst/ImporteerKnop.tsx"],"names":[],"mappings":"AAwBA,wBAAgB,aAAa,CAAC,EAAE,YAAY,EAAE,UAAU,EAAE,EAAE;IAAE,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,UAAU,EAAE,MAAM,IAAI,CAAA;CAAE,+
|
|
1
|
+
{"version":3,"file":"ImporteerKnop.d.ts","sourceRoot":"","sources":["../../../../src/components/voorraad/lijst/ImporteerKnop.tsx"],"names":[],"mappings":"AAwBA,wBAAgB,aAAa,CAAC,EAAE,YAAY,EAAE,UAAU,EAAE,EAAE;IAAE,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,UAAU,EAAE,MAAM,IAAI,CAAA;CAAE,+BAkBlH"}
|
|
@@ -16,5 +16,5 @@ function ImportInhoud({ connectionId, onImported }) {
|
|
|
16
16
|
}
|
|
17
17
|
export function ImporteerKnop({ connectionId, onImported }) {
|
|
18
18
|
const [open, setOpen] = useState(false);
|
|
19
|
-
return (_jsxs(_Fragment, { children: [_jsxs(Button, { variant: "outline", size: "sm", onClick: () => setOpen(true), children: [_jsx(Upload, { className: "
|
|
19
|
+
return (_jsxs(_Fragment, { children: [_jsxs(Button, { variant: "outline", size: "sm", onClick: () => setOpen(true), "aria-label": "Importeren", className: "h-10 w-10 p-0 sm:h-9 sm:w-auto sm:px-3", children: [_jsx(Upload, { className: "h-4 w-4 sm:mr-2" }), " ", _jsx("span", { className: "hidden sm:inline", children: "Importeren" })] }), _jsx(Dialog, { open: open, onOpenChange: setOpen, children: _jsxs(DialogContent, { children: [_jsxs(DialogHeader, { children: [_jsx(DialogTitle, { children: "Importeren" }), _jsx(DialogDescription, { children: "Download het sjabloon of kies een bestand. Je krijgt eerst een proefcontrole." })] }), open && _jsx(ImportInhoud, { connectionId: connectionId, onImported: onImported })] }) })] }));
|
|
20
20
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LijstExportKnop.d.ts","sourceRoot":"","sources":["../../../../src/components/voorraad/lijst/LijstExportKnop.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAqB,KAAK,UAAU,EAAE,MAAM,iCAAiC,CAAC;AAGrF,sGAAsG;AACtG,wBAAgB,eAAe,CAAC,EAAE,KAAK,EAAE,EAAE;IAAE,KAAK,EAAE,IAAI,CAAC,UAAU,EAAE,QAAQ,GAAG,SAAS,CAAC,CAAA;CAAE,+
|
|
1
|
+
{"version":3,"file":"LijstExportKnop.d.ts","sourceRoot":"","sources":["../../../../src/components/voorraad/lijst/LijstExportKnop.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAqB,KAAK,UAAU,EAAE,MAAM,iCAAiC,CAAC;AAGrF,sGAAsG;AACtG,wBAAgB,eAAe,CAAC,EAAE,KAAK,EAAE,EAAE;IAAE,KAAK,EAAE,IAAI,CAAC,UAAU,EAAE,QAAQ,GAAG,SAAS,CAAC,CAAA;CAAE,+BAsC3F"}
|
|
@@ -39,5 +39,5 @@ export function LijstExportKnop({ vraag }) {
|
|
|
39
39
|
setBezig(null);
|
|
40
40
|
}
|
|
41
41
|
};
|
|
42
|
-
return (_jsxs(Button, { variant: "outline", size: "sm", onClick: exporteer, disabled: !!bezig, children: [_jsx(Download, { className: "
|
|
42
|
+
return (_jsxs(Button, { variant: "outline", size: "sm", onClick: exporteer, disabled: !!bezig, "aria-label": "Exporteren", className: "h-10 w-10 p-0 sm:h-9 sm:w-auto sm:px-3", children: [_jsx(Download, { className: "h-4 w-4 sm:mr-2" }), " ", _jsx("span", { className: "hidden sm:inline", children: bezig ?? "Exporteren" })] }));
|
|
43
43
|
}
|
|
@@ -1,10 +1,14 @@
|
|
|
1
|
-
import
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
import { type LijstRij, type LijstSort, type LijstStatus } from "../../../lib/voorraad-lijst.js";
|
|
2
3
|
export type OptioneleKolom = "artikelnummer" | "minimum" | "inkomend" | "prijs";
|
|
3
4
|
export declare const OPTIONELE_KOLOMMEN: Array<{
|
|
4
5
|
key: OptioneleKolom;
|
|
5
6
|
label: string;
|
|
6
7
|
}>;
|
|
7
|
-
export declare function
|
|
8
|
+
export declare function ArtikelStatusIcoon({ status }: {
|
|
9
|
+
status: LijstStatus;
|
|
10
|
+
}): import("react").JSX.Element;
|
|
11
|
+
export declare function VoorraadLijstTabel({ rijen, loading, sort, oplopend, onSort, kolommen, selected, onToggleSelect, onToggleAll, onAanpassen, onAfboeken, heeftKoppeling, leeg, }: {
|
|
8
12
|
heeftKoppeling?: boolean;
|
|
9
13
|
rijen: LijstRij[];
|
|
10
14
|
loading: boolean;
|
|
@@ -17,5 +21,7 @@ export declare function VoorraadLijstTabel({ rijen, loading, sort, oplopend, onS
|
|
|
17
21
|
onToggleAll: (ids: string[]) => void;
|
|
18
22
|
onAanpassen: (r: LijstRij) => void;
|
|
19
23
|
onAfboeken: (r: LijstRij) => void;
|
|
24
|
+
/** Lege staat (verschilt per filter/zoekopdracht). */
|
|
25
|
+
leeg?: ReactNode;
|
|
20
26
|
}): import("react").JSX.Element;
|
|
21
27
|
//# sourceMappingURL=VoorraadLijstTabel.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VoorraadLijstTabel.d.ts","sourceRoot":"","sources":["../../../../src/components/voorraad/lijst/VoorraadLijstTabel.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"VoorraadLijstTabel.d.ts","sourceRoot":"","sources":["../../../../src/components/voorraad/lijst/VoorraadLijstTabel.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAgB,SAAS,EAAE,MAAM,OAAO,CAAC;AAOrD,OAAO,EAML,KAAK,QAAQ,EACb,KAAK,SAAS,EACd,KAAK,WAAW,EACjB,MAAM,6BAA6B,CAAC;AAErC,MAAM,MAAM,cAAc,GAAG,eAAe,GAAG,SAAS,GAAG,UAAU,GAAG,OAAO,CAAC;AAChF,eAAO,MAAM,kBAAkB,EAAE,KAAK,CAAC;IAAE,GAAG,EAAE,cAAc,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAK5E,CAAC;AAmBF,wBAAgB,kBAAkB,CAAC,EAAE,MAAM,EAAE,EAAE;IAAE,MAAM,EAAE,WAAW,CAAA;CAAE,+BAGrE;AAmBD,wBAAgB,kBAAkB,CAAC,EACjC,KAAK,EACL,OAAO,EACP,IAAI,EACJ,QAAQ,EACR,MAAM,EACN,QAAQ,EACR,QAAQ,EACR,cAAc,EACd,WAAW,EACX,WAAW,EACX,UAAU,EACV,cAAsB,EACtB,IAAI,GACL,EAAE;IACD,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE,SAAS,CAAC;IAChB,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,EAAE,CAAC,CAAC,EAAE,SAAS,KAAK,IAAI,CAAC;IAC/B,QAAQ,EAAE,GAAG,CAAC,cAAc,CAAC,CAAC;IAC9B,QAAQ,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACtB,cAAc,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC,WAAW,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IACrC,WAAW,EAAE,CAAC,CAAC,EAAE,QAAQ,KAAK,IAAI,CAAC;IACnC,UAAU,EAAE,CAAC,CAAC,EAAE,QAAQ,KAAK,IAAI,CAAC;IAClC,sDAAsD;IACtD,IAAI,CAAC,EAAE,SAAS,CAAC;CAClB,+BA6FA"}
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { useNavigate } from "@tanstack/react-router";
|
|
3
|
+
import { CheckCircle2, Euro, Link2, Link2Off, Minus, Package, PackageSearch, PackageX, SlidersHorizontal } from "lucide-react";
|
|
4
|
+
import { DataTable } from "../../fs-ui/DataTable.js";
|
|
5
|
+
import { EmptyState } from "../../fs-ui/EmptyState.js";
|
|
6
|
+
import { HoverTip } from "../../fs-ui/HoverTip.js";
|
|
7
|
+
import { StatusIcon } from "../../fs-ui/StatusIcon.js";
|
|
6
8
|
import { aantal, geld, toonFloridayLabel, onderMinimum, STATUS_INFO, } from "../../../lib/voorraad-lijst.js";
|
|
7
9
|
export const OPTIONELE_KOLOMMEN = [
|
|
8
10
|
{ key: "artikelnummer", label: "Artikelnummer" },
|
|
@@ -10,30 +12,63 @@ export const OPTIONELE_KOLOMMEN = [
|
|
|
10
12
|
{ key: "inkomend", label: "Inkomend" },
|
|
11
13
|
{ key: "prijs", label: "Prijs" },
|
|
12
14
|
];
|
|
13
|
-
|
|
15
|
+
const STATUS_TONE = {
|
|
16
|
+
ok: "klaar",
|
|
17
|
+
verpakking: "fout",
|
|
18
|
+
verwijderd_floriday: "fout",
|
|
19
|
+
niet_gekoppeld: "open",
|
|
20
|
+
prijs: "fout",
|
|
21
|
+
gearchiveerd: "open",
|
|
22
|
+
};
|
|
23
|
+
const STATUS_ICON = {
|
|
24
|
+
ok: _jsx(CheckCircle2, {}),
|
|
25
|
+
verpakking: _jsx(PackageX, {}),
|
|
26
|
+
verwijderd_floriday: _jsx(Link2Off, {}),
|
|
27
|
+
niet_gekoppeld: _jsx(Link2Off, {}),
|
|
28
|
+
prijs: _jsx(Euro, {}),
|
|
29
|
+
gearchiveerd: _jsx(Minus, {}),
|
|
30
|
+
};
|
|
31
|
+
export function ArtikelStatusIcoon({ status }) {
|
|
14
32
|
const info = STATUS_INFO[status];
|
|
15
|
-
|
|
16
|
-
const cls = "h-4 w-4";
|
|
17
|
-
const icon = status === "verpakking" ? _jsx(PackageX, { className: `${cls} text-destructive` }) :
|
|
18
|
-
status === "verwijderd_floriday" ? _jsx(Link2Off, { className: `${cls} text-destructive` }) :
|
|
19
|
-
status === "niet_gekoppeld" ? _jsx(Link2Off, { className: `${cls} text-muted-foreground` }) :
|
|
20
|
-
status === "prijs" ? _jsx(Euro, { className: `${cls} text-destructive` }) :
|
|
21
|
-
status === "gearchiveerd" ? _jsx(Minus, { className: `${cls} text-muted-foreground` }) :
|
|
22
|
-
_jsx(CheckCircle2, { className: `${cls} text-primary` });
|
|
23
|
-
return (_jsxs("span", { title: titel, "aria-label": titel, className: "inline-flex items-center gap-1.5", children: [icon, status !== "ok" && _jsx("span", { className: "text-xs text-muted-foreground hidden xl:inline", children: info.label })] }));
|
|
33
|
+
return _jsx(StatusIcon, { tone: STATUS_TONE[status], icon: STATUS_ICON[status], label: info.label, detail: info.actie ?? undefined });
|
|
24
34
|
}
|
|
25
|
-
function
|
|
26
|
-
|
|
27
|
-
return (_jsx("th", { className: `px-3 py-2 font-medium ${rechts ? "text-right" : "text-left"}`, "aria-sort": actief ? (oplopend ? "ascending" : "descending") : "none", children: _jsxs("button", { type: "button", onClick: () => onSort(kolom), className: `inline-flex items-center gap-1 hover:text-foreground ${actief ? "text-foreground" : ""}`, children: [label, actief && (oplopend ? _jsx(ArrowUp, { className: "h-3 w-3" }) : _jsx(ArrowDown, { className: "h-3 w-3" }))] }) }));
|
|
35
|
+
function Foto({ r }) {
|
|
36
|
+
return r.foto ? (_jsx("img", { src: r.foto, alt: "", loading: "lazy", className: "h-9 w-9 shrink-0 rounded-lg bg-muted object-cover" })) : (_jsx("span", { className: "flex h-9 w-9 shrink-0 items-center justify-center rounded-lg bg-muted", children: _jsx(Package, { className: "h-4 w-4 text-muted-foreground" }) }));
|
|
28
37
|
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
38
|
+
function Koppeling({ r, heeftKoppeling }) {
|
|
39
|
+
if (!toonFloridayLabel(r, heeftKoppeling))
|
|
40
|
+
return null;
|
|
41
|
+
return (_jsx(HoverTip, { label: "Gekoppeld met Floriday", content: "Gekoppeld met Floriday", className: "h-5 w-5 text-muted-foreground [&_svg]:h-3.5 [&_svg]:w-3.5", children: _jsx(Link2, {}) }));
|
|
42
|
+
}
|
|
43
|
+
export function VoorraadLijstTabel({ rijen, loading, sort, oplopend, onSort, kolommen, selected, onToggleSelect, onToggleAll, onAanpassen, onAfboeken, heeftKoppeling = false, leeg, }) {
|
|
44
|
+
const navigate = useNavigate();
|
|
32
45
|
const k = (s) => kolommen.has(s);
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
46
|
+
const getal = (n, extra = "") => _jsxs("span", { className: "tabular-nums " + extra, children: [aantal(n), " ", _jsx("span", { className: "text-xs font-normal text-muted-foreground", children: "stuks" })] });
|
|
47
|
+
const kolomDefs = [
|
|
48
|
+
{
|
|
49
|
+
key: "naam", header: "Artikel", width: "260px", sortKey: "naam",
|
|
50
|
+
render: (r) => (_jsxs("div", { className: "flex min-w-0 items-center gap-3", children: [_jsx(Foto, { r: r }), _jsxs("div", { className: "min-w-0", children: [_jsxs("div", { className: "flex min-w-0 items-center gap-1.5", children: [_jsx("span", { className: "truncate font-medium text-foreground", children: r.naam }), _jsx(Koppeling, { r: r, heeftKoppeling: heeftKoppeling })] }), _jsxs("div", { className: "truncate text-xs text-muted-foreground", children: [r.artikelnummer || "Geen artikelnummer", r.soort === "SET" ? " · Samenstelling" : r.soort === "GROUP" ? " · Productgroep" : ""] })] })] })),
|
|
51
|
+
},
|
|
52
|
+
k("artikelnummer") && { key: "artikelnummer", header: "Artikelnummer", width: "132px", sortKey: "artikelnummer", render: (r) => _jsx("span", { className: "text-muted-foreground", children: r.artikelnummer ?? "—" }) },
|
|
53
|
+
{ key: "liggend", header: "Liggend", width: "112px", sortKey: "liggend", render: (r) => getal(r.liggend) },
|
|
54
|
+
{ key: "gereserveerd", header: "Gereserveerd", width: "120px", sortKey: "gereserveerd", render: (r) => getal(r.gereserveerd, "text-muted-foreground") },
|
|
55
|
+
{
|
|
56
|
+
key: "vrij", header: "Vrij", width: "112px", sortKey: "vrij",
|
|
57
|
+
render: (r) => {
|
|
58
|
+
const rood = onderMinimum(r);
|
|
59
|
+
return rood ? (_jsx(HoverTip, { label: `Onder minimum (${aantal(r.minimum)})`, content: `Onder minimum (${aantal(r.minimum)})`, className: "rounded-sm", children: getal(r.vrij, "font-semibold text-destructive") })) : getal(r.vrij, "font-medium");
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
k("minimum") && { key: "minimum", header: "Minimum", width: "112px", render: (r) => getal(r.minimum, "text-muted-foreground") },
|
|
63
|
+
k("inkomend") && { key: "inkomend", header: "Inkomend", width: "112px", sortKey: "inkomend", render: (r) => getal(r.inkomend, "text-muted-foreground") },
|
|
64
|
+
k("prijs") && { key: "prijs", header: "Prijs", width: "104px", sortKey: "prijs", render: (r) => _jsx("span", { className: "tabular-nums", children: geld(r.prijs) }) },
|
|
65
|
+
{ key: "status", header: "Status", width: "76px", render: (r) => _jsx(ArtikelStatusIcoon, { status: r.status }) },
|
|
66
|
+
];
|
|
67
|
+
const columns = kolomDefs.filter(Boolean);
|
|
68
|
+
const open = (r) => navigate({ to: "/voorraad/$id", params: { id: r.id } });
|
|
69
|
+
return (_jsx(DataTable, { columns: columns, rows: rijen, getId: (r) => r.id, loading: loading, sort: { key: sort, asc: oplopend }, onSort: (key) => onSort(key), onOpen: open, openLabel: "Artikel openen", selection: { selected, onToggle: onToggleSelect, onToggleAll, label: (r) => r.naam }, empty: leeg ?? _jsx(EmptyState, { icon: _jsx(PackageSearch, { className: "h-6 w-6" }), title: "Geen artikelen gevonden" }), rowActions: (r) => {
|
|
70
|
+
const gearchiveerd = !!r.gearchiveerd_at;
|
|
71
|
+
const knop = "inline-flex h-8 w-8 items-center justify-center rounded-lg text-muted-foreground hover:bg-muted hover:text-foreground disabled:pointer-events-none disabled:opacity-40 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring";
|
|
72
|
+
return (_jsxs(_Fragment, { children: [_jsx("button", { type: "button", className: knop, title: "Voorraad aanpassen (+/\u2212)", "aria-label": "Voorraad aanpassen", disabled: gearchiveerd || r.soort === "GROUP", onClick: () => onAanpassen(r), children: _jsx(SlidersHorizontal, { className: "h-4 w-4" }) }), _jsx("button", { type: "button", className: knop, title: r.voorraad_sleutel ? "Afboeken" : "Nog geen voorraad om af te boeken", "aria-label": "Afboeken", disabled: gearchiveerd || !r.voorraad_sleutel || r.liggend <= 0, onClick: () => onAfboeken(r), children: _jsx(Minus, { className: "h-4 w-4" }) })] }));
|
|
73
|
+
}, mobileCard: (r) => (_jsxs("div", { className: "flex items-center gap-3", children: [_jsx(Foto, { r: r }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsxs("div", { className: "flex items-center gap-1.5", children: [_jsx("span", { className: "truncate font-medium", children: r.naam }), _jsx(Koppeling, { r: r, heeftKoppeling: heeftKoppeling })] }), _jsx("div", { className: "truncate text-xs text-muted-foreground", children: r.artikelnummer || "Geen artikelnummer" }), _jsxs("div", { className: "mt-1 flex flex-wrap gap-x-3 text-xs tabular-nums text-muted-foreground", children: [_jsxs("span", { children: ["Liggend ", aantal(r.liggend)] }), _jsxs("span", { className: onderMinimum(r) ? "font-semibold text-destructive" : "text-foreground", children: ["Vrij ", aantal(r.vrij)] }), _jsx("span", { children: geld(r.prijs) })] })] }), _jsx(ArtikelStatusIcoon, { status: r.status })] })) }));
|
|
39
74
|
}
|