@flowselections/floriday-voorraad 1.0.149 → 1.0.152
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist-lib/components/fs-ui/DataTable.d.ts +42 -0
- package/dist-lib/components/fs-ui/DataTable.d.ts.map +1 -0
- package/dist-lib/components/fs-ui/DataTable.js +68 -0
- package/dist-lib/components/fs-ui/EmptyState.d.ts +9 -0
- package/dist-lib/components/fs-ui/EmptyState.d.ts.map +1 -0
- package/dist-lib/components/fs-ui/EmptyState.js +5 -0
- package/dist-lib/components/fs-ui/FilterChips.d.ts +13 -0
- package/dist-lib/components/fs-ui/FilterChips.d.ts.map +1 -0
- package/dist-lib/components/fs-ui/FilterChips.js +8 -0
- package/dist-lib/components/fs-ui/HoverTip.d.ts +15 -0
- package/dist-lib/components/fs-ui/HoverTip.d.ts.map +1 -0
- package/dist-lib/components/fs-ui/HoverTip.js +24 -0
- package/dist-lib/components/fs-ui/InlineAlert.d.ts +12 -0
- package/dist-lib/components/fs-ui/InlineAlert.d.ts.map +1 -0
- package/dist-lib/components/fs-ui/InlineAlert.js +15 -0
- package/dist-lib/components/fs-ui/KpiRow.d.ts +10 -0
- package/dist-lib/components/fs-ui/KpiRow.d.ts.map +1 -0
- package/dist-lib/components/fs-ui/KpiRow.js +8 -0
- package/dist-lib/components/fs-ui/KpiTile.d.ts +21 -0
- package/dist-lib/components/fs-ui/KpiTile.d.ts.map +1 -0
- package/dist-lib/components/fs-ui/KpiTile.js +40 -0
- package/dist-lib/components/fs-ui/MobilePrimaryAction.d.ts +13 -0
- package/dist-lib/components/fs-ui/MobilePrimaryAction.d.ts.map +1 -0
- package/dist-lib/components/fs-ui/MobilePrimaryAction.js +10 -0
- package/dist-lib/components/fs-ui/PageHeader.d.ts +18 -0
- package/dist-lib/components/fs-ui/PageHeader.d.ts.map +1 -0
- package/dist-lib/components/fs-ui/PageHeader.js +8 -0
- package/dist-lib/components/fs-ui/PillTabs.d.ts +19 -0
- package/dist-lib/components/fs-ui/PillTabs.d.ts.map +1 -0
- package/dist-lib/components/fs-ui/PillTabs.js +18 -0
- package/dist-lib/components/fs-ui/SearchInput.d.ts +14 -0
- package/dist-lib/components/fs-ui/SearchInput.d.ts.map +1 -0
- package/dist-lib/components/fs-ui/SearchInput.js +51 -0
- package/dist-lib/components/fs-ui/StatusIcon.d.ts +13 -0
- package/dist-lib/components/fs-ui/StatusIcon.d.ts.map +1 -0
- package/dist-lib/components/fs-ui/StatusIcon.js +15 -0
- package/dist-lib/components/fs-ui/TableSkeleton.d.ts +7 -0
- package/dist-lib/components/fs-ui/TableSkeleton.d.ts.map +1 -0
- package/dist-lib/components/fs-ui/TableSkeleton.js +6 -0
- package/dist-lib/components/fs-ui/Toolbar.d.ts +8 -0
- package/dist-lib/components/fs-ui/Toolbar.d.ts.map +1 -0
- package/dist-lib/components/fs-ui/Toolbar.js +5 -0
- package/dist-lib/components/fs-ui/UpdatedAt.d.ts +6 -0
- package/dist-lib/components/fs-ui/UpdatedAt.d.ts.map +1 -0
- package/dist-lib/components/fs-ui/UpdatedAt.js +10 -0
- package/dist-lib/components/voorraad/ArtikelCompositionCard.d.ts.map +1 -1
- package/dist-lib/components/voorraad/ArtikelCompositionCard.js +46 -15
- package/dist-lib/components/voorraad/ArtikelWizard.js +4 -4
- package/dist-lib/components/voorraad/FloridayConnectionNotice.d.ts +6 -6
- package/dist-lib/components/voorraad/FloridayConnectionNotice.d.ts.map +1 -1
- package/dist-lib/components/voorraad/FloridayConnectionNotice.js +10 -12
- package/dist-lib/components/voorraad/FustenPage.d.ts.map +1 -1
- package/dist-lib/components/voorraad/FustenPage.js +4 -2
- package/dist-lib/components/voorraad/NieuwArtikelPage.d.ts.map +1 -1
- package/dist-lib/components/voorraad/NieuwArtikelPage.js +2 -1
- package/dist-lib/components/voorraad/PublicatiesPage.d.ts.map +1 -1
- package/dist-lib/components/voorraad/PublicatiesPage.js +2 -1
- package/dist-lib/components/voorraad/Sneltoetsen.d.ts +14 -0
- package/dist-lib/components/voorraad/Sneltoetsen.d.ts.map +1 -0
- package/dist-lib/components/voorraad/Sneltoetsen.js +45 -0
- package/dist-lib/components/voorraad/StockIntegrityAlert.d.ts.map +1 -1
- package/dist-lib/components/voorraad/StockIntegrityAlert.js +9 -2
- package/dist-lib/components/voorraad/StockPushMonitor.js +2 -2
- package/dist-lib/components/voorraad/TelmomentButton.d.ts +5 -1
- package/dist-lib/components/voorraad/TelmomentButton.d.ts.map +1 -1
- package/dist-lib/components/voorraad/TelmomentButton.js +13 -6
- package/dist-lib/components/voorraad/ValidationOverviewTable.js +9 -7
- package/dist-lib/components/voorraad/ValidationPage.d.ts +1 -0
- package/dist-lib/components/voorraad/ValidationPage.d.ts.map +1 -1
- package/dist-lib/components/voorraad/ValidationPage.js +33 -5
- package/dist-lib/components/voorraad/VoorraadBeheerCard.d.ts +4 -1
- package/dist-lib/components/voorraad/VoorraadBeheerCard.d.ts.map +1 -1
- package/dist-lib/components/voorraad/VoorraadBeheerCard.js +26 -5
- package/dist-lib/components/voorraad/VoorraadPage.d.ts.map +1 -1
- package/dist-lib/components/voorraad/VoorraadPage.js +48 -34
- package/dist-lib/components/voorraad/VoorraadSturingCard.js +1 -1
- package/dist-lib/components/voorraad/artikel/ArtikelPagina.d.ts.map +1 -1
- package/dist-lib/components/voorraad/artikel/ArtikelPagina.js +28 -18
- package/dist-lib/components/voorraad/lijst/VoorraadLijstTabel.d.ts +8 -2
- package/dist-lib/components/voorraad/lijst/VoorraadLijstTabel.d.ts.map +1 -1
- package/dist-lib/components/voorraad/lijst/VoorraadLijstTabel.js +62 -27
- package/dist-lib/hooks/useLocalTradeItems.d.ts +14 -0
- package/dist-lib/hooks/useLocalTradeItems.d.ts.map +1 -1
- package/dist-lib/hooks/useLocalTradeItems.js +5 -3
- package/dist-lib/hooks/useTradeItemStock.d.ts.map +1 -1
- package/dist-lib/hooks/useTradeItemStock.js +6 -3
- package/dist-lib/hooks/useVoorraadData.d.ts.map +1 -1
- package/dist-lib/hooks/useVoorraadData.js +16 -8
- package/dist-lib/hooks/useVoorraadLijst.d.ts +10 -0
- package/dist-lib/hooks/useVoorraadLijst.d.ts.map +1 -1
- package/dist-lib/hooks/useVoorraadLijst.js +22 -4
- package/dist-lib/integrations/supabase/types.d.ts +59 -0
- package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
- package/dist-lib/lib/alle-rijen.d.ts +15 -0
- package/dist-lib/lib/alle-rijen.d.ts.map +1 -0
- package/dist-lib/lib/alle-rijen.js +17 -0
- package/dist-lib/lib/db-error-message.d.ts.map +1 -1
- package/dist-lib/lib/db-error-message.js +3 -1
- package/package.json +2 -1
- package/sql/floriday-voorraad-schema.sql +315 -39
|
@@ -1,8 +1,8 @@
|
|
|
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
3
|
import { Link, useBlocker } from "@tanstack/react-router";
|
|
4
|
-
import {
|
|
5
|
-
import { AlertTriangle, ArrowLeft, Loader2, MoreHorizontal, Package, Send } from "lucide-react";
|
|
4
|
+
import { Button, Card, Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger, Input, Label, Skeleton, Tabs, TabsContent, toast, } from "@flowselections/core";
|
|
5
|
+
import { AlertTriangle, Archive, ArchiveRestore, ArrowDownToLine, ArrowLeft, Boxes, Layers, Link2, Loader2, Lock, MoreHorizontal, Minus, Package, Send, SlidersHorizontal } from "lucide-react";
|
|
6
6
|
import { supabase } from "../../../integrations/supabase/client.js";
|
|
7
7
|
import { dbFoutTekst } from "../../../lib/db-error-message.js";
|
|
8
8
|
import { ARTIKEL_GEWIJZIGD } from "../../../lib/artikel-gewijzigd.js";
|
|
@@ -11,6 +11,14 @@ import { STATUS_INFO, aantal, geld } from "../../../lib/voorraad-lijst.js";
|
|
|
11
11
|
import { AanpassenDialog, AfboekenDialog } from "../lijst/SnelActieDialogen.js";
|
|
12
12
|
import { TwijfelKiezen } from "./TwijfelKiezen.js";
|
|
13
13
|
import { OpslaanContext } from "./opslaan-context.js";
|
|
14
|
+
import { ArtikelStatusIcoon } from "../lijst/VoorraadLijstTabel.js";
|
|
15
|
+
import { PageHeader } from "../../fs-ui/PageHeader.js";
|
|
16
|
+
import { KpiRow } from "../../fs-ui/KpiRow.js";
|
|
17
|
+
import { KpiTile } from "../../fs-ui/KpiTile.js";
|
|
18
|
+
import { PillTabs } from "../../fs-ui/PillTabs.js";
|
|
19
|
+
import { InlineAlert } from "../../fs-ui/InlineAlert.js";
|
|
20
|
+
import { HoverTip } from "../../fs-ui/HoverTip.js";
|
|
21
|
+
import { MobilePrimaryAction } from "../../fs-ui/MobilePrimaryAction.js";
|
|
14
22
|
// Tabbladinhoud laadt pas als je het tabblad opent.
|
|
15
23
|
const StockMutationHistoryCard = lazy(() => import("../StockMutationHistoryCard.js").then((m) => ({ default: m.StockMutationHistoryCard })));
|
|
16
24
|
const OrderHistoryCard = lazy(() => import("../OrderHistoryCard.js").then((m) => ({ default: m.OrderHistoryCard })));
|
|
@@ -21,6 +29,12 @@ const ProductCustomFieldsCard = lazy(() => import("../ProductCustomFieldsCard.js
|
|
|
21
29
|
const ProductLinkCard = lazy(() => import("../ProductLinkCard.js").then((m) => ({ default: m.ProductLinkCard })));
|
|
22
30
|
const FloridayInfoCard = lazy(() => import("../FloridayInfoCard.js").then((m) => ({ default: m.FloridayInfoCard })));
|
|
23
31
|
const ArtikelWizard = lazy(() => import("../ArtikelWizard.js").then((m) => ({ default: m.ArtikelWizard })));
|
|
32
|
+
const TAB_ICON = {
|
|
33
|
+
voorraad: _jsx(Package, {}),
|
|
34
|
+
verpakking: _jsx(Boxes, {}),
|
|
35
|
+
koppelingen: _jsx(Link2, {}),
|
|
36
|
+
floriday: _jsx(Send, {}),
|
|
37
|
+
};
|
|
24
38
|
const TAB_LABEL = {
|
|
25
39
|
voorraad: "Voorraad",
|
|
26
40
|
verpakking: "Verpakking en samenstelling",
|
|
@@ -28,7 +42,7 @@ const TAB_LABEL = {
|
|
|
28
42
|
floriday: "Floriday",
|
|
29
43
|
};
|
|
30
44
|
function Laden() {
|
|
31
|
-
return (
|
|
45
|
+
return _jsx(Skeleton, { className: "h-40 w-full rounded-xl", "aria-label": "Laden\u2026" });
|
|
32
46
|
}
|
|
33
47
|
function toon(v) {
|
|
34
48
|
if (v === null || v === undefined || v === "")
|
|
@@ -239,7 +253,7 @@ export function ArtikelPagina({ id, resumeJobId, onNietGevonden }) {
|
|
|
239
253
|
if (alleConflicten.length)
|
|
240
254
|
setConflicten(alleConflicten);
|
|
241
255
|
if (opgeslagen)
|
|
242
|
-
toast.success(alleConflicten.length ? "Deels opgeslagen; kies bij de conflicten." : "Opgeslagen
|
|
256
|
+
toast.success(alleConflicten.length ? "Deels opgeslagen; kies bij de conflicten." : detail?.heeft_koppeling ? "Opgeslagen. Niet naar Floriday gestuurd." : "Opgeslagen.");
|
|
243
257
|
await laad();
|
|
244
258
|
}
|
|
245
259
|
/** Conflict: neem hun waarde over voor dat veld en leg de nieuwe stand vast. */
|
|
@@ -272,7 +286,7 @@ export function ArtikelPagina({ id, resumeJobId, onNietGevonden }) {
|
|
|
272
286
|
return;
|
|
273
287
|
}
|
|
274
288
|
setArchief(null);
|
|
275
|
-
toast.success(archiefKeuze === "floriday" ? "Artikel gearchiveerd. De voorraad op Floriday gaat
|
|
289
|
+
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
290
|
await laad();
|
|
277
291
|
}
|
|
278
292
|
async function terugzetten() {
|
|
@@ -285,10 +299,10 @@ export function ArtikelPagina({ id, resumeJobId, onNietGevonden }) {
|
|
|
285
299
|
await laad();
|
|
286
300
|
}
|
|
287
301
|
if (fout) {
|
|
288
|
-
return
|
|
302
|
+
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
303
|
}
|
|
290
304
|
if (!detail || !nieuw) {
|
|
291
|
-
return
|
|
305
|
+
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
306
|
}
|
|
293
307
|
const status = artikelStatus({ ...detail, velden: nieuw });
|
|
294
308
|
const info = STATUS_INFO[status];
|
|
@@ -297,17 +311,13 @@ export function ArtikelPagina({ id, resumeJobId, onNietGevonden }) {
|
|
|
297
311
|
if (floridayOpen && !tabs.includes("floriday") && actie === "op_floriday")
|
|
298
312
|
tabs.push("floriday");
|
|
299
313
|
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
|
|
314
|
+
const opslaanUit = !vuil || opslaan || Boolean(prijsFout) || Boolean(aantalFout);
|
|
315
|
+
const gearchiveerd = Boolean(detail.gearchiveerd_at);
|
|
316
|
+
return (_jsxs("main", { className: "flex flex-col gap-4 p-4 pb-24 sm:p-6 sm:pb-24", children: [_jsx(PageHeader, { crumb: _jsxs(Link, { to: "/voorraad", className: "inline-flex items-center gap-1 hover:text-foreground", children: [_jsx(ArrowLeft, { className: "h-4 w-4" }), " Voorraad"] }), 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: "inline-flex h-5 items-center rounded-full bg-warning-soft px-2 text-xs text-warning-soft-foreground", children: "Niet opgeslagen" })] }), actions: _jsxs(_Fragment, { children: [_jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsxs(Button, { variant: "outline", size: "sm", className: "h-9", "aria-label": "Meer acties", children: [_jsx(MoreHorizontal, { className: "h-4 w-4 sm:mr-2" }), _jsx("span", { className: "hidden sm:inline", children: "Meer" })] }) }), _jsxs(DropdownMenuContent, { align: "end", className: "w-56", children: [_jsxs(DropdownMenuItem, { 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
|
|
317
|
+
? _jsxs(DropdownMenuItem, { onSelect: terugzetten, children: [_jsx(ArchiveRestore, { className: "mr-2 h-4 w-4" }), "Terugzetten in voorraad"] })
|
|
318
|
+
: _jsxs(DropdownMenuItem, { onSelect: () => { setArchiefKeuze("hier"); setArchief({}); }, children: [_jsx(Archive, { className: "mr-2 h-4 w-4" }), "Archiveren\u2026"] })] })] }), _jsxs(Button, { size: "sm", className: "hidden h-9 md:inline-flex", onClick: doeOpslaan, disabled: opslaanUit, children: [opslaan && _jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }), "Opslaan"] })] }) }), _jsxs(KpiRow, { children: [_jsx(KpiTile, { title: "Liggend", icon: _jsx(Package, {}), value: aantal(detail.liggend), sub: _jsxs(_Fragment, { children: ["Telmoment ", detail.telmoment ? new Date(detail.telmoment).toLocaleDateString("nl-NL") : _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" })] }), 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."] })), _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
319
|
? "Floriday blijft ongewijzigd: het artikel blijft op Floriday te koop."
|
|
310
320
|
: "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
|
-
? "
|
|
321
|
+
? "Wordt op de gewone manier naar Floriday gestuurd. Staat het versturen naar Floriday stil, dan wacht dit tot het weer aan staat."
|
|
312
322
|
: "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
323
|
}
|
|
@@ -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 = "") => _jsx("span", { className: "tabular-nums " + extra, children: aantal(n) });
|
|
47
|
+
const kolomDefs = [
|
|
48
|
+
{
|
|
49
|
+
key: "naam", header: "Artikel", width: "34%", 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: "12%", sortKey: "artikelnummer", render: (r) => _jsx("span", { className: "text-muted-foreground", children: r.artikelnummer ?? "—" }) },
|
|
53
|
+
{ key: "liggend", header: "Liggend", width: "9%", sortKey: "liggend", render: (r) => getal(r.liggend) },
|
|
54
|
+
{ key: "gereserveerd", header: "Gereserveerd", width: "10%", sortKey: "gereserveerd", render: (r) => getal(r.gereserveerd, "text-muted-foreground") },
|
|
55
|
+
{
|
|
56
|
+
key: "vrij", header: "Vrij", width: "9%", 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: "9%", render: (r) => getal(r.minimum, "text-muted-foreground") },
|
|
63
|
+
k("inkomend") && { key: "inkomend", header: "Inkomend", width: "9%", sortKey: "inkomend", render: (r) => getal(r.inkomend, "text-muted-foreground") },
|
|
64
|
+
k("prijs") && { key: "prijs", header: "Prijs", width: "9%", sortKey: "prijs", render: (r) => _jsx("span", { className: "tabular-nums", children: geld(r.prijs) }) },
|
|
65
|
+
{ key: "status", header: "Status", width: "7%", 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
|
}
|
|
@@ -19,6 +19,19 @@ export interface LocalTradeItemRow {
|
|
|
19
19
|
last_floriday_sync_at?: string | null;
|
|
20
20
|
source?: string | null;
|
|
21
21
|
}
|
|
22
|
+
type ProductRow = {
|
|
23
|
+
id: string;
|
|
24
|
+
product: string | null;
|
|
25
|
+
article_number: string | null;
|
|
26
|
+
barcode: string | null;
|
|
27
|
+
image_url: string | null;
|
|
28
|
+
floriday_trade_item_id: string | null;
|
|
29
|
+
trade_item_key: string | null;
|
|
30
|
+
custom_fields: any;
|
|
31
|
+
updated_at: string;
|
|
32
|
+
created_at: string;
|
|
33
|
+
};
|
|
34
|
+
export declare function productToTradeItem(row: ProductRow): TradeItem;
|
|
22
35
|
/**
|
|
23
36
|
* `connectionId` wordt vandaag genegeerd: producten zijn niet meer per Floriday-connectie gescoped.
|
|
24
37
|
* De parameter blijft in de signatuur zodat bestaande callers zonder wijziging werken.
|
|
@@ -69,4 +82,5 @@ export declare function updateDraftMeta(id: string, meta: DraftMeta): Promise<vo
|
|
|
69
82
|
* De UI-flow moet dit alleen aanroepen bij een expliciete verwijderactie.
|
|
70
83
|
*/
|
|
71
84
|
export declare function deleteLocalTradeItem(id: string): Promise<void>;
|
|
85
|
+
export {};
|
|
72
86
|
//# sourceMappingURL=useLocalTradeItems.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useLocalTradeItems.d.ts","sourceRoot":"","sources":["../../src/hooks/useLocalTradeItems.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"useLocalTradeItems.d.ts","sourceRoot":"","sources":["../../src/hooks/useLocalTradeItems.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,EAAwB,KAAK,kBAAkB,EAAE,KAAK,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAIvG;;;;;GAKG;AAEH,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,IAAI,EAAE,UAAU,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,oBAAoB,CAAC,EAAE,kBAAkB,CAAC;IAC1C,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,mBAAmB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,uBAAuB,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IAC1C,qBAAqB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtC,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACxB;AAED,KAAK,UAAU,GAAG;IAChB,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,sBAAsB,EAAE,MAAM,GAAG,IAAI,CAAC;IACtC,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,aAAa,EAAE,GAAG,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAQF,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,UAAU,GAAG,SAAS,CAgD7D;AAKD;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,aAAa,EAAE,MAAM,GAAG,IAAI;;;;EA6B9D;AAED,wBAAsB,iBAAiB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC,CA4BrF;AAED,MAAM,WAAW,SAAS;IACxB,kBAAkB,CAAC,EAAE,kBAAkB,CAAC;IACxC,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,qBAAqB,CAAC,EAAE,MAAM,EAAE,CAAC;IACjC,kBAAkB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,MAAM,CAAC,EAAE,QAAQ,GAAG,cAAc,CAAC;CACpC;AAED,0FAA0F;AAC1F,MAAM,WAAW,eAAe;IAC9B,wEAAwE;IACxE,KAAK,EAAE,OAAO,CAAC;IACf,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAChC;AAED,wBAAgB,mBAAmB,CAAC,YAAY,EAAE,OAAO,GAAG,eAAe,GAAG,IAAI,CAWjF;AAqBD,wBAAsB,oBAAoB,CAAC,MAAM,EAAE;IACjD,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,UAAU,CAAC;IACjB,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,iFAAiF;IACjF,mBAAmB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,IAAI,CAAC,EAAE,eAAe,CAAC;CACxB,GAAG,OAAO,CAAC,MAAM,CAAC,CA0GlB;AAED,wEAAwE;AACxE,wBAAsB,mBAAmB,CACvC,YAAY,EAAE,MAAM,EACpB,MAAM,EAAE,eAAe,GACtB,OAAO,CAAC,IAAI,CAAC,CAqBf;AAGD;;;;GAIG;AACH,wBAAsB,eAAe,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAiBhF;AAED;;;;GAIG;AACH,wBAAsB,oBAAoB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAGpE"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { alleRijen } from "../lib/alle-rijen.js";
|
|
1
2
|
import { useCallback, useEffect, useState } from "react";
|
|
2
3
|
import { supabase } from "../integrations/supabase/client.js";
|
|
3
4
|
import { derivePrimaryPacking } from "../lib/floriday-client.js";
|
|
@@ -7,7 +8,7 @@ function asRecord(value) {
|
|
|
7
8
|
? value
|
|
8
9
|
: {};
|
|
9
10
|
}
|
|
10
|
-
function productToTradeItem(row) {
|
|
11
|
+
export function productToTradeItem(row) {
|
|
11
12
|
const cf = asRecord(row.custom_fields);
|
|
12
13
|
const payload = asRecord(cf.floriday_payload);
|
|
13
14
|
const floridayId = row.floriday_trade_item_id ||
|
|
@@ -62,11 +63,12 @@ export function useLocalTradeItems(_connectionId) {
|
|
|
62
63
|
const load = useCallback(async () => {
|
|
63
64
|
setLoading(true);
|
|
64
65
|
try {
|
|
65
|
-
const { data, error } = await supabase
|
|
66
|
+
const { data, error } = await alleRijen((van, tot) => supabase
|
|
66
67
|
.from("products")
|
|
67
68
|
.select(SELECT_COLS)
|
|
68
69
|
.order("updated_at", { ascending: false })
|
|
69
|
-
.
|
|
70
|
+
.order("id", { ascending: true })
|
|
71
|
+
.range(van, tot));
|
|
70
72
|
if (error)
|
|
71
73
|
throw error;
|
|
72
74
|
setItems((data ?? []).map(productToTradeItem));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useTradeItemStock.d.ts","sourceRoot":"","sources":["../../src/hooks/useTradeItemStock.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"useTradeItemStock.d.ts","sourceRoot":"","sources":["../../src/hooks/useTradeItemStock.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,iBAAiB;IAChC,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,EAAE,MAAM,CAAC;IACvB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAUD,wBAAgB,WAAW,CAAC,MAAM,EAAE;IAClC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B,GAAG,MAAM,GAAG,IAAI,CAEhB;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE;IACnC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B,GAAG,MAAM,EAAE,CAUX;AAGD;;;;;GAKG;AACH,wBAAgB,WAAW,CACzB,GAAG,EAAE,GAAG,CAAC,MAAM,EAAE,iBAAiB,CAAC,EACnC,MAAM,EAAE;IACN,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB,GACA,iBAAiB,GAAG,SAAS,CAa/B;AAGD,wBAAgB,iBAAiB,CAC/B,YAAY,EAAE,MAAM,GAAG,IAAI,EAC3B,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,IAAI,EACnC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI;;;mBA2Gb,iBAAiB,SACnB;QAAE,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,sBAAsB,CAAC,EAAE,OAAO,CAAA;KAAE,KAC3E,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;;;;;EA+EtC;AAED,wBAAsB,aAAa,CACjC,YAAY,EAAE,MAAM,GAAG,IAAI,GAC1B,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC,CA2BzC"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { alleRijen } from "../lib/alle-rijen.js";
|
|
1
2
|
import { buildScopedStockMap } from "../lib/stock-scope.js";
|
|
2
3
|
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
3
4
|
import { supabase } from "../integrations/supabase/client.js";
|
|
@@ -240,12 +241,14 @@ export async function fetchStockMap(connectionId) {
|
|
|
240
241
|
if (!scopeId)
|
|
241
242
|
return new Map(); // nog nooit eigen voorraad opgeslagen
|
|
242
243
|
}
|
|
243
|
-
const { data, error } = await supabase
|
|
244
|
+
const { data, error } = await alleRijen((van, tot) => supabase
|
|
244
245
|
.from("trade_item_stock")
|
|
245
246
|
.select("connection_id,trade_item_key,physical_stock,minimum_stock,reserved_stock,incoming_stock,notes,updated_at")
|
|
246
|
-
.eq("connection_id", scopeId)
|
|
247
|
+
.eq("connection_id", scopeId)
|
|
248
|
+
.order("trade_item_key", { ascending: true })
|
|
249
|
+
.range(van, tot));
|
|
247
250
|
if (error)
|
|
248
|
-
throw new Error(error.message);
|
|
251
|
+
throw new Error(String(error.message ?? error));
|
|
249
252
|
const scoped = buildScopedStockMap((data ?? []), scopeId);
|
|
250
253
|
const map = new Map();
|
|
251
254
|
for (const [k, r] of scoped)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useVoorraadData.d.ts","sourceRoot":"","sources":["../../src/hooks/useVoorraadData.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"useVoorraadData.d.ts","sourceRoot":"","sources":["../../src/hooks/useVoorraadData.ts"],"names":[],"mappings":"AAKA,OAAO,EAAsB,KAAK,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAsB5E;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI;;;;;EA0V1D"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { alleRijen } from "../lib/alle-rijen.js";
|
|
1
2
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
2
3
|
import { hideMirrorItems } from "../lib/mirror-products.js";
|
|
3
4
|
import { createBundler, pageRequestGuard } from "../lib/request-guard.js";
|
|
@@ -55,25 +56,32 @@ export function useVoorraadData(connectionId) {
|
|
|
55
56
|
const [pubRes, stock, groupsRes, bomRes, groupItemsRes] = await Promise.all([
|
|
56
57
|
// Zonder koppeling geen Floriday-kopie; eigen artikelen blijven zichtbaar.
|
|
57
58
|
connectionId
|
|
58
|
-
? supabase
|
|
59
|
+
? alleRijen((van, tot) => supabase
|
|
59
60
|
.from("floriday_trade_items_cache")
|
|
60
61
|
.select("data,floriday_id,is_deleted")
|
|
61
62
|
.eq("connection_id", connectionId)
|
|
62
63
|
.eq("is_deleted", false)
|
|
63
|
-
.
|
|
64
|
+
.order("floriday_id", { ascending: true })
|
|
65
|
+
.range(van, tot))
|
|
64
66
|
: Promise.resolve({ data: [], error: null }),
|
|
65
67
|
fetchStockMap(connectionId),
|
|
66
|
-
supabase
|
|
68
|
+
alleRijen((van, tot) => supabase
|
|
67
69
|
.from("product_groups")
|
|
68
70
|
.select("id,head_product_id,head_trade_item_floriday_id,updated_at")
|
|
69
71
|
.eq("is_active", true)
|
|
70
|
-
.order("updated_at", { ascending: true })
|
|
71
|
-
|
|
72
|
+
.order("updated_at", { ascending: true })
|
|
73
|
+
.order("id", { ascending: true })
|
|
74
|
+
.range(van, tot)),
|
|
75
|
+
alleRijen((van, tot) => supabase
|
|
72
76
|
.from("product_bom_items")
|
|
73
|
-
.select("parent_product_id,parent_trade_item_floriday_id")
|
|
74
|
-
|
|
77
|
+
.select("parent_product_id,parent_trade_item_floriday_id")
|
|
78
|
+
.order("id", { ascending: true })
|
|
79
|
+
.range(van, tot)),
|
|
80
|
+
alleRijen((van, tot) => supabase
|
|
75
81
|
.from("product_group_items")
|
|
76
|
-
.select("product_group_id,trade_item_id,quantity")
|
|
82
|
+
.select("product_group_id,trade_item_id,quantity")
|
|
83
|
+
.order("id", { ascending: true })
|
|
84
|
+
.range(van, tot)),
|
|
77
85
|
]);
|
|
78
86
|
if (pubRes.error)
|
|
79
87
|
throw pubRes.error;
|
|
@@ -7,6 +7,15 @@ export interface LijstVraag {
|
|
|
7
7
|
pagina: number;
|
|
8
8
|
grootte: number;
|
|
9
9
|
}
|
|
10
|
+
/** Kerncijfers van het eigen bedrijf; veranderen niet tijdens zoeken of filteren. */
|
|
11
|
+
export interface VoorraadKpi {
|
|
12
|
+
artikelen: number;
|
|
13
|
+
onder_minimum: number;
|
|
14
|
+
vrij: number;
|
|
15
|
+
inkomend: number;
|
|
16
|
+
bijgewerkt: string;
|
|
17
|
+
}
|
|
18
|
+
export declare function haalVoorraadKpi(): Promise<VoorraadKpi>;
|
|
10
19
|
export declare function haalVoorraadLijst(v: LijstVraag): Promise<LijstAntwoord>;
|
|
11
20
|
/**
|
|
12
21
|
* Eén aanvraag per wijziging van zoeken/filter/sortering/pagina. Live-updates
|
|
@@ -14,6 +23,7 @@ export declare function haalVoorraadLijst(v: LijstVraag): Promise<LijstAntwoord>
|
|
|
14
23
|
*/
|
|
15
24
|
export declare function useVoorraadLijst(vraag: LijstVraag): {
|
|
16
25
|
data: LijstAntwoord | null;
|
|
26
|
+
kpi: VoorraadKpi | null;
|
|
17
27
|
loading: boolean;
|
|
18
28
|
error: string | null;
|
|
19
29
|
reload: () => Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useVoorraadLijst.d.ts","sourceRoot":"","sources":["../../src/hooks/useVoorraadLijst.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAEnF,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,WAAW,CAAC;IACpB,IAAI,EAAE,SAAS,CAAC;IAChB,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,wBAAsB,iBAAiB,CAAC,CAAC,EAAE,UAAU,GAAG,OAAO,CAAC,aAAa,CAAC,CAW7E;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,UAAU
|
|
1
|
+
{"version":3,"file":"useVoorraadLijst.d.ts","sourceRoot":"","sources":["../../src/hooks/useVoorraadLijst.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAEnF,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,WAAW,CAAC;IACpB,IAAI,EAAE,SAAS,CAAC;IAChB,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,qFAAqF;AACrF,MAAM,WAAW,WAAW;IAAG,SAAS,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE;AAC7H,wBAAsB,eAAe,IAAI,OAAO,CAAC,WAAW,CAAC,CAI5D;AAGD,wBAAsB,iBAAiB,CAAC,CAAC,EAAE,UAAU,GAAG,OAAO,CAAC,aAAa,CAAC,CAW7E;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,UAAU;;;;;;EAuDjD"}
|
|
@@ -1,8 +1,15 @@
|
|
|
1
1
|
import { useCallback, useEffect, useRef, useState } from "react";
|
|
2
2
|
import { supabase } from "../integrations/supabase/client.js";
|
|
3
3
|
import { createBundler, pageRequestGuard } from "../lib/request-guard.js";
|
|
4
|
+
export async function haalVoorraadKpi() {
|
|
5
|
+
const { data, error } = await supabase.rpc("voorraad_kpi");
|
|
6
|
+
if (error)
|
|
7
|
+
throw new Error(error.message);
|
|
8
|
+
return data;
|
|
9
|
+
}
|
|
10
|
+
// De lijst leest voorraad_lijst_v2 (1.0.151): zelfde uitkomst als voorraad_lijst, en "Onder minimum" ook voor samenstellingen.
|
|
4
11
|
export async function haalVoorraadLijst(v) {
|
|
5
|
-
const { data, error } = await supabase.rpc("
|
|
12
|
+
const { data, error } = await supabase.rpc("voorraad_lijst_v2", {
|
|
6
13
|
p_zoek: v.zoek.trim() || null,
|
|
7
14
|
p_filter: v.filter,
|
|
8
15
|
p_sort: v.sort,
|
|
@@ -20,11 +27,21 @@ export async function haalVoorraadLijst(v) {
|
|
|
20
27
|
*/
|
|
21
28
|
export function useVoorraadLijst(vraag) {
|
|
22
29
|
const [data, setData] = useState(null);
|
|
30
|
+
const [kpi, setKpi] = useState(null);
|
|
23
31
|
const [loading, setLoading] = useState(true);
|
|
24
32
|
const [error, setError] = useState(null);
|
|
25
33
|
const vraagRef = useRef(vraag);
|
|
26
34
|
vraagRef.current = vraag;
|
|
27
35
|
const seq = useRef(0);
|
|
36
|
+
const laadKpi = useCallback(async () => {
|
|
37
|
+
try {
|
|
38
|
+
setKpi(await haalVoorraadKpi());
|
|
39
|
+
}
|
|
40
|
+
catch (e) {
|
|
41
|
+
console.warn("Kerncijfers voorraad niet geladen", e);
|
|
42
|
+
}
|
|
43
|
+
}, []);
|
|
44
|
+
useEffect(() => { void laadKpi(); }, [laadKpi]);
|
|
28
45
|
const laad = useCallback(async () => {
|
|
29
46
|
if (!pageRequestGuard.allow("voorraad_lijst"))
|
|
30
47
|
return;
|
|
@@ -52,7 +69,7 @@ export function useVoorraadLijst(vraag) {
|
|
|
52
69
|
void laad();
|
|
53
70
|
}, [key, laad]);
|
|
54
71
|
useEffect(() => {
|
|
55
|
-
const bundler = createBundler(() => void laad(), 3000);
|
|
72
|
+
const bundler = createBundler(() => { void laad(); void laadKpi(); }, 3000);
|
|
56
73
|
const trigger = () => bundler.trigger();
|
|
57
74
|
const channel = supabase
|
|
58
75
|
.channel(`voorraad-lijst-${Math.random().toString(36).slice(2)}`)
|
|
@@ -66,6 +83,7 @@ export function useVoorraadLijst(vraag) {
|
|
|
66
83
|
document.removeEventListener("visibilitychange", onVis);
|
|
67
84
|
supabase.removeChannel(channel);
|
|
68
85
|
};
|
|
69
|
-
}, [laad]);
|
|
70
|
-
|
|
86
|
+
}, [laad, laadKpi]);
|
|
87
|
+
const reload = useCallback(async () => { await Promise.all([laad(), laadKpi()]); }, [laad, laadKpi]);
|
|
88
|
+
return { data, kpi, loading, error, reload };
|
|
71
89
|
}
|