@flowselections/floriday-voorraad 1.0.150 → 1.0.152

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (100) hide show
  1. package/dist-lib/components/fs-ui/DataTable.d.ts +42 -0
  2. package/dist-lib/components/fs-ui/DataTable.d.ts.map +1 -0
  3. package/dist-lib/components/fs-ui/DataTable.js +68 -0
  4. package/dist-lib/components/fs-ui/EmptyState.d.ts +9 -0
  5. package/dist-lib/components/fs-ui/EmptyState.d.ts.map +1 -0
  6. package/dist-lib/components/fs-ui/EmptyState.js +5 -0
  7. package/dist-lib/components/fs-ui/FilterChips.d.ts +13 -0
  8. package/dist-lib/components/fs-ui/FilterChips.d.ts.map +1 -0
  9. package/dist-lib/components/fs-ui/FilterChips.js +8 -0
  10. package/dist-lib/components/fs-ui/HoverTip.d.ts +15 -0
  11. package/dist-lib/components/fs-ui/HoverTip.d.ts.map +1 -0
  12. package/dist-lib/components/fs-ui/HoverTip.js +24 -0
  13. package/dist-lib/components/fs-ui/InlineAlert.d.ts +12 -0
  14. package/dist-lib/components/fs-ui/InlineAlert.d.ts.map +1 -0
  15. package/dist-lib/components/fs-ui/InlineAlert.js +15 -0
  16. package/dist-lib/components/fs-ui/KpiRow.d.ts +10 -0
  17. package/dist-lib/components/fs-ui/KpiRow.d.ts.map +1 -0
  18. package/dist-lib/components/fs-ui/KpiRow.js +8 -0
  19. package/dist-lib/components/fs-ui/KpiTile.d.ts +21 -0
  20. package/dist-lib/components/fs-ui/KpiTile.d.ts.map +1 -0
  21. package/dist-lib/components/fs-ui/KpiTile.js +40 -0
  22. package/dist-lib/components/fs-ui/MobilePrimaryAction.d.ts +13 -0
  23. package/dist-lib/components/fs-ui/MobilePrimaryAction.d.ts.map +1 -0
  24. package/dist-lib/components/fs-ui/MobilePrimaryAction.js +10 -0
  25. package/dist-lib/components/fs-ui/PageHeader.d.ts +18 -0
  26. package/dist-lib/components/fs-ui/PageHeader.d.ts.map +1 -0
  27. package/dist-lib/components/fs-ui/PageHeader.js +8 -0
  28. package/dist-lib/components/fs-ui/PillTabs.d.ts +19 -0
  29. package/dist-lib/components/fs-ui/PillTabs.d.ts.map +1 -0
  30. package/dist-lib/components/fs-ui/PillTabs.js +18 -0
  31. package/dist-lib/components/fs-ui/SearchInput.d.ts +14 -0
  32. package/dist-lib/components/fs-ui/SearchInput.d.ts.map +1 -0
  33. package/dist-lib/components/fs-ui/SearchInput.js +51 -0
  34. package/dist-lib/components/fs-ui/StatusIcon.d.ts +13 -0
  35. package/dist-lib/components/fs-ui/StatusIcon.d.ts.map +1 -0
  36. package/dist-lib/components/fs-ui/StatusIcon.js +15 -0
  37. package/dist-lib/components/fs-ui/TableSkeleton.d.ts +7 -0
  38. package/dist-lib/components/fs-ui/TableSkeleton.d.ts.map +1 -0
  39. package/dist-lib/components/fs-ui/TableSkeleton.js +6 -0
  40. package/dist-lib/components/fs-ui/Toolbar.d.ts +8 -0
  41. package/dist-lib/components/fs-ui/Toolbar.d.ts.map +1 -0
  42. package/dist-lib/components/fs-ui/Toolbar.js +5 -0
  43. package/dist-lib/components/fs-ui/UpdatedAt.d.ts +6 -0
  44. package/dist-lib/components/fs-ui/UpdatedAt.d.ts.map +1 -0
  45. package/dist-lib/components/fs-ui/UpdatedAt.js +10 -0
  46. package/dist-lib/components/voorraad/ArtikelCompositionCard.d.ts.map +1 -1
  47. package/dist-lib/components/voorraad/ArtikelCompositionCard.js +46 -15
  48. package/dist-lib/components/voorraad/ArtikelWizard.js +4 -4
  49. package/dist-lib/components/voorraad/FloridayConnectionNotice.d.ts +6 -6
  50. package/dist-lib/components/voorraad/FloridayConnectionNotice.d.ts.map +1 -1
  51. package/dist-lib/components/voorraad/FloridayConnectionNotice.js +10 -12
  52. package/dist-lib/components/voorraad/FustenPage.d.ts.map +1 -1
  53. package/dist-lib/components/voorraad/FustenPage.js +4 -2
  54. package/dist-lib/components/voorraad/NieuwArtikelPage.d.ts.map +1 -1
  55. package/dist-lib/components/voorraad/NieuwArtikelPage.js +2 -1
  56. package/dist-lib/components/voorraad/PublicatiesPage.d.ts.map +1 -1
  57. package/dist-lib/components/voorraad/PublicatiesPage.js +2 -1
  58. package/dist-lib/components/voorraad/Sneltoetsen.d.ts +14 -0
  59. package/dist-lib/components/voorraad/Sneltoetsen.d.ts.map +1 -0
  60. package/dist-lib/components/voorraad/Sneltoetsen.js +45 -0
  61. package/dist-lib/components/voorraad/StockIntegrityAlert.d.ts.map +1 -1
  62. package/dist-lib/components/voorraad/StockIntegrityAlert.js +9 -2
  63. package/dist-lib/components/voorraad/StockPushMonitor.js +2 -2
  64. package/dist-lib/components/voorraad/TelmomentButton.d.ts +5 -1
  65. package/dist-lib/components/voorraad/TelmomentButton.d.ts.map +1 -1
  66. package/dist-lib/components/voorraad/TelmomentButton.js +13 -6
  67. package/dist-lib/components/voorraad/ValidationOverviewTable.js +9 -7
  68. package/dist-lib/components/voorraad/ValidationPage.d.ts +1 -0
  69. package/dist-lib/components/voorraad/ValidationPage.d.ts.map +1 -1
  70. package/dist-lib/components/voorraad/ValidationPage.js +33 -5
  71. package/dist-lib/components/voorraad/VoorraadBeheerCard.d.ts +4 -1
  72. package/dist-lib/components/voorraad/VoorraadBeheerCard.d.ts.map +1 -1
  73. package/dist-lib/components/voorraad/VoorraadBeheerCard.js +26 -5
  74. package/dist-lib/components/voorraad/VoorraadPage.d.ts.map +1 -1
  75. package/dist-lib/components/voorraad/VoorraadPage.js +48 -34
  76. package/dist-lib/components/voorraad/VoorraadSturingCard.js +1 -1
  77. package/dist-lib/components/voorraad/artikel/ArtikelPagina.d.ts.map +1 -1
  78. package/dist-lib/components/voorraad/artikel/ArtikelPagina.js +28 -18
  79. package/dist-lib/components/voorraad/lijst/VoorraadLijstTabel.d.ts +8 -2
  80. package/dist-lib/components/voorraad/lijst/VoorraadLijstTabel.d.ts.map +1 -1
  81. package/dist-lib/components/voorraad/lijst/VoorraadLijstTabel.js +62 -27
  82. package/dist-lib/hooks/useLocalTradeItems.d.ts +14 -0
  83. package/dist-lib/hooks/useLocalTradeItems.d.ts.map +1 -1
  84. package/dist-lib/hooks/useLocalTradeItems.js +5 -3
  85. package/dist-lib/hooks/useTradeItemStock.d.ts.map +1 -1
  86. package/dist-lib/hooks/useTradeItemStock.js +6 -3
  87. package/dist-lib/hooks/useVoorraadData.d.ts.map +1 -1
  88. package/dist-lib/hooks/useVoorraadData.js +16 -8
  89. package/dist-lib/hooks/useVoorraadLijst.d.ts +10 -0
  90. package/dist-lib/hooks/useVoorraadLijst.d.ts.map +1 -1
  91. package/dist-lib/hooks/useVoorraadLijst.js +22 -4
  92. package/dist-lib/integrations/supabase/types.d.ts +59 -0
  93. package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
  94. package/dist-lib/lib/alle-rijen.d.ts +15 -0
  95. package/dist-lib/lib/alle-rijen.d.ts.map +1 -0
  96. package/dist-lib/lib/alle-rijen.js +17 -0
  97. package/dist-lib/lib/db-error-message.d.ts.map +1 -1
  98. package/dist-lib/lib/db-error-message.js +3 -1
  99. package/package.json +2 -1
  100. 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 { Badge, Button, Card, Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, Input, Label, Tabs, TabsContent, TabsList, TabsTrigger, toast, } from "@flowselections/core";
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 (_jsxs("div", { className: "flex items-center gap-2 p-4 text-sm text-muted-foreground", children: [_jsx(Loader2, { className: "h-4 w-4 animate-spin" }), " Laden\u2026"] }));
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 (alleen lokaal).");
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 via de gewone wachtrij naar 0." : "Artikel gearchiveerd. Op Floriday is niets veranderd.");
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 _jsx("main", { className: "p-6", children: _jsx(Card, { className: "p-6 text-sm", children: fout }) });
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 _jsx("main", { className: "p-6", children: _jsx(Card, { className: "p-6", children: _jsx(Laden, {}) }) });
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
- return (_jsxs("main", { className: "p-6 space-y-4", children: [_jsxs(Link, { to: "/voorraad", className: "inline-flex items-center gap-1 text-sm text-muted-foreground hover:text-foreground", children: [_jsx(ArrowLeft, { className: "h-4 w-4" }), " Terug naar voorraad"] }), _jsx(Card, { className: "p-4", children: _jsxs("div", { className: "flex flex-wrap items-start gap-4", children: [_jsx("div", { className: "h-20 w-20 shrink-0 overflow-hidden rounded-md border bg-muted flex items-center justify-center", children: detail.foto ? _jsx("img", { src: detail.foto, alt: detail.naam, className: "h-full w-full object-cover" }) : _jsx(Package, { className: "h-8 w-8 text-muted-foreground" }) }), _jsxs("div", { className: "min-w-0 flex-1 basis-64", children: [_jsx("h1", { className: "text-xl font-semibold leading-tight break-words", children: detail.naam }), _jsx("div", { className: "mt-1 text-sm text-muted-foreground break-words", children: detail.artikelnummer ?? "Geen artikelnummer" }), _jsxs("div", { className: "mt-2 flex flex-wrap items-center gap-2 text-sm", children: [_jsx(Badge, { variant: status === "ok" ? "secondary" : status === "gearchiveerd" ? "outline" : "destructive", children: info.label }), info.actie && info.tab && tabs.includes(info.tab) && (_jsx("button", { type: "button", className: "underline text-muted-foreground", onClick: () => setTab(info.tab), children: info.actie })), vuil && _jsx("span", { className: "text-muted-foreground", children: "\u00B7 Niet-opgeslagen wijzigingen" })] })] }), _jsx("dl", { className: "grid grid-cols-3 gap-4 text-center sm:grid-cols-5", children: [
301
- ["Liggend", aantal(detail.liggend)],
302
- ["Gereserveerd", aantal(detail.gereserveerd)],
303
- ["Inkomend", aantal(detail.inkomend)],
304
- ["Vrij", aantal(detail.vrij)],
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
- ? "Gaat via de gewone Floriday-wachtrij, met de bestaande schrijfstop."
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 { type LijstRij, type LijstSort } from "../../../lib/voorraad-lijst.js";
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 VoorraadLijstTabel({ rijen, loading, sort, oplopend, onSort, kolommen, selected, onToggleSelect, onToggleAll, onAanpassen, onAfboeken, heeftKoppeling, }: {
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":"AAeA,OAAO,EAML,KAAK,QAAQ,EACb,KAAK,SAAS,EAEf,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;AA+CF,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,GACvB,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;CACnC,+BAyFA"}
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 { Link } from "@tanstack/react-router";
3
- import { Button, Checkbox } from "@flowselections/core";
4
- import { ArrowDown, ArrowUp, CheckCircle2, Euro, ExternalLink, Link2Off, Minus, Package, PackageX, SlidersHorizontal, } from "lucide-react";
5
- import floridayLogo from "../../../assets/floriday-logo.png.asset.json";
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
- function StatusIcoon({ status }) {
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
- const titel = info.actie ? `${info.label} — ${info.actie}` : info.label;
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 Kop({ kolom, label, sort, oplopend, onSort, rechts, }) {
26
- const actief = sort === kolom;
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
- export function VoorraadLijstTabel({ rijen, loading, sort, oplopend, onSort, kolommen, selected, onToggleSelect, onToggleAll, onAanpassen, onAfboeken, heeftKoppeling = false, }) {
30
- const ids = rijen.map((r) => r.id);
31
- const allesGekozen = ids.length > 0 && ids.every((id) => selected.has(id));
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
- return (_jsxs("table", { className: "w-full text-sm", children: [_jsx("thead", { className: "border-b bg-muted/40 text-xs text-muted-foreground", children: _jsxs("tr", { children: [_jsx("th", { className: "w-10 px-3 py-2", children: _jsx(Checkbox, { checked: allesGekozen, onCheckedChange: () => onToggleAll(ids), "aria-label": "Alles op deze pagina kiezen" }) }), _jsx(Kop, { kolom: "naam", label: "Artikel", sort: sort, oplopend: oplopend, onSort: onSort }), k("artikelnummer") && _jsx(Kop, { kolom: "artikelnummer", label: "Artikelnummer", sort: sort, oplopend: oplopend, onSort: onSort }), _jsx(Kop, { kolom: "liggend", label: "Liggend", sort: sort, oplopend: oplopend, onSort: onSort, rechts: true }), _jsx(Kop, { kolom: "gereserveerd", label: "Gereserveerd", sort: sort, oplopend: oplopend, onSort: onSort, rechts: true }), _jsx(Kop, { kolom: "vrij", label: "Vrij", sort: sort, oplopend: oplopend, onSort: onSort, rechts: true }), k("minimum") && _jsx("th", { className: "px-3 py-2 text-right font-medium", children: "Minimum" }), k("inkomend") && _jsx(Kop, { kolom: "inkomend", label: "Inkomend", sort: sort, oplopend: oplopend, onSort: onSort, rechts: true }), k("prijs") && _jsx(Kop, { kolom: "prijs", label: "Prijs", sort: sort, oplopend: oplopend, onSort: onSort, rechts: true }), _jsx("th", { className: "px-3 py-2 text-left font-medium", children: "Status" }), _jsx("th", { className: "px-3 py-2 text-right font-medium", children: "Acties" })] }) }), _jsxs("tbody", { children: [loading && rijen.length === 0 &&
34
- Array.from({ length: 6 }).map((_, i) => (_jsx("tr", { className: "border-b", children: _jsx("td", { colSpan: 12, className: "px-3 py-3", children: _jsx("div", { className: "h-6 animate-pulse rounded bg-muted" }) }) }, `s${i}`))), !loading && rijen.length === 0 && (_jsx("tr", { children: _jsx("td", { colSpan: 12, className: "px-3 py-10 text-center text-muted-foreground", children: "Geen artikelen gevonden." }) })), rijen.map((r) => {
35
- const rood = onderMinimum(r);
36
- const gearchiveerd = !!r.gearchiveerd_at;
37
- return (_jsxs("tr", { className: `border-b hover:bg-muted/30 ${loading ? "opacity-60" : ""}`, children: [_jsx("td", { className: "px-3 py-2", children: _jsx(Checkbox, { checked: selected.has(r.id), onCheckedChange: () => onToggleSelect(r.id), "aria-label": `Kies ${r.naam}` }) }), _jsx("td", { className: "px-3 py-2", children: _jsxs(Link, { to: "/voorraad/$id", params: { id: r.id }, className: "flex items-center gap-3 min-w-0", children: [r.foto ? (_jsx("img", { src: r.foto, alt: "", loading: "lazy", className: "h-9 w-9 shrink-0 rounded object-cover bg-muted" })) : (_jsx("span", { className: "flex h-9 w-9 shrink-0 items-center justify-center rounded bg-muted", children: _jsx(Package, { className: "h-4 w-4 text-muted-foreground" }) })), _jsxs("span", { className: "min-w-0", children: [_jsxs("span", { className: "flex items-center gap-1.5 font-medium text-foreground truncate", children: [r.naam, toonFloridayLabel(r, heeftKoppeling) && _jsx("img", { src: floridayLogo.url, alt: "Floriday", title: "Gekoppeld aan Floriday", className: "h-3.5 w-3.5 shrink-0" })] }), _jsxs("span", { className: "block text-xs text-muted-foreground truncate", children: [r.artikelnummer || "Geen artikelnummer", r.soort === "SET" ? " · Samenstelling" : r.soort === "GROUP" ? " · Productgroep" : ""] })] })] }) }), k("artikelnummer") && _jsx("td", { className: "px-3 py-2 text-muted-foreground", children: r.artikelnummer ?? "—" }), _jsx("td", { className: "px-3 py-2 text-right tabular-nums", children: aantal(r.liggend) }), _jsx("td", { className: "px-3 py-2 text-right tabular-nums text-muted-foreground", children: aantal(r.gereserveerd) }), _jsx("td", { className: `px-3 py-2 text-right tabular-nums font-medium ${rood ? "text-destructive" : ""}`, title: rood ? `Onder minimum (${aantal(r.minimum)})` : undefined, children: aantal(r.vrij) }), k("minimum") && _jsx("td", { className: "px-3 py-2 text-right tabular-nums text-muted-foreground", children: aantal(r.minimum) }), k("inkomend") && _jsx("td", { className: "px-3 py-2 text-right tabular-nums text-muted-foreground", children: aantal(r.inkomend) }), k("prijs") && _jsx("td", { className: "px-3 py-2 text-right tabular-nums", children: geld(r.prijs) }), _jsx("td", { className: "px-3 py-2", children: _jsx(StatusIcoon, { status: r.status }) }), _jsx("td", { className: "px-3 py-2", children: _jsxs("div", { className: "flex justify-end gap-1", children: [_jsx(Button, { size: "sm", variant: "ghost", 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, { size: "sm", variant: "ghost", 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" }) }), _jsx(Button, { asChild: true, size: "sm", variant: "ghost", title: "Openen", "aria-label": "Openen", children: _jsx(Link, { to: "/voorraad/$id", params: { id: r.id }, children: _jsx(ExternalLink, { className: "h-4 w-4" }) }) })] }) })] }, r.id));
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":"AAEA,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;AA0ED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,aAAa,EAAE,MAAM,GAAG,IAAI;;;;EA0B9D;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
+ {"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
- .limit(1000);
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":"AAIA,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,CAuBzC"}
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":"AAIA,OAAO,EAAsB,KAAK,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAsB5E;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI;;;;;EA2U1D"}
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
- .limit(1000)
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
- supabase
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
- supabase
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;;;;;EAgDjD"}
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("voorraad_lijst", {
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
- return { data, loading, error, reload: laad };
86
+ }, [laad, laadKpi]);
87
+ const reload = useCallback(async () => { await Promise.all([laad(), laadKpi()]); }, [laad, laadKpi]);
88
+ return { data, kpi, loading, error, reload };
71
89
  }