@flowselections/floriday-voorraad 1.0.12 → 1.0.14

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 (123) hide show
  1. package/dist-lib/assets/floriday-logo.png.asset.json +11 -0
  2. package/dist-lib/components/voorraad/ArtikelWizard.d.ts +5 -0
  3. package/dist-lib/components/voorraad/ArtikelWizard.d.ts.map +1 -0
  4. package/dist-lib/components/voorraad/ArtikelWizard.js +1229 -0
  5. package/dist-lib/components/voorraad/ConnectionBadge.d.ts +6 -0
  6. package/dist-lib/components/voorraad/ConnectionBadge.d.ts.map +1 -0
  7. package/dist-lib/components/voorraad/ConnectionBadge.js +12 -0
  8. package/dist-lib/components/voorraad/EditArtikelPage.d.ts +4 -0
  9. package/dist-lib/components/voorraad/EditArtikelPage.d.ts.map +1 -0
  10. package/dist-lib/components/voorraad/EditArtikelPage.js +51 -0
  11. package/dist-lib/components/voorraad/NieuwArtikelPage.d.ts +2 -0
  12. package/dist-lib/components/voorraad/NieuwArtikelPage.d.ts.map +1 -0
  13. package/dist-lib/components/voorraad/NieuwArtikelPage.js +5 -0
  14. package/dist-lib/components/voorraad/StockBulkButtons.d.ts +7 -0
  15. package/dist-lib/components/voorraad/StockBulkButtons.d.ts.map +1 -0
  16. package/dist-lib/components/voorraad/StockBulkButtons.js +356 -0
  17. package/dist-lib/components/voorraad/ValidationOverviewTable.d.ts +7 -0
  18. package/dist-lib/components/voorraad/ValidationOverviewTable.d.ts.map +1 -0
  19. package/dist-lib/components/voorraad/ValidationOverviewTable.js +87 -0
  20. package/dist-lib/components/voorraad/ValidationPage.d.ts +2 -0
  21. package/dist-lib/components/voorraad/ValidationPage.d.ts.map +1 -0
  22. package/dist-lib/components/voorraad/ValidationPage.js +12 -0
  23. package/dist-lib/components/voorraad/VoorraadDashboard.d.ts +6 -0
  24. package/dist-lib/components/voorraad/VoorraadDashboard.d.ts.map +1 -0
  25. package/dist-lib/components/voorraad/VoorraadDashboard.js +42 -0
  26. package/dist-lib/components/voorraad/VoorraadPage.d.ts.map +1 -1
  27. package/dist-lib/components/voorraad/VoorraadPage.js +78 -11
  28. package/dist-lib/components/voorraad/VoorraadPagination.d.ts +10 -0
  29. package/dist-lib/components/voorraad/VoorraadPagination.d.ts.map +1 -0
  30. package/dist-lib/components/voorraad/VoorraadPagination.js +12 -0
  31. package/dist-lib/components/voorraad/VoorraadStockCard.d.ts +8 -0
  32. package/dist-lib/components/voorraad/VoorraadStockCard.d.ts.map +1 -0
  33. package/dist-lib/components/voorraad/VoorraadStockCard.js +33 -0
  34. package/dist-lib/components/voorraad/VoorraadTable.d.ts.map +1 -1
  35. package/dist-lib/components/voorraad/VoorraadTable.js +21 -2
  36. package/dist-lib/hooks/useFloriday.d.ts +55 -0
  37. package/dist-lib/hooks/useFloriday.d.ts.map +1 -0
  38. package/dist-lib/hooks/useFloriday.js +298 -0
  39. package/dist-lib/hooks/useFloridayConnection.d.ts +9 -4
  40. package/dist-lib/hooks/useFloridayConnection.d.ts.map +1 -1
  41. package/dist-lib/hooks/useFloridayConnection.js +23 -23
  42. package/dist-lib/hooks/useFlorydayCustomers.d.ts +17 -0
  43. package/dist-lib/hooks/useFlorydayCustomers.d.ts.map +1 -0
  44. package/dist-lib/hooks/useFlorydayCustomers.js +65 -0
  45. package/dist-lib/hooks/useLocalTradeItems.d.ts +38 -0
  46. package/dist-lib/hooks/useLocalTradeItems.d.ts.map +1 -0
  47. package/dist-lib/hooks/useLocalTradeItems.js +144 -0
  48. package/dist-lib/hooks/useTradeItemSpec.d.ts +42 -0
  49. package/dist-lib/hooks/useTradeItemSpec.d.ts.map +1 -0
  50. package/dist-lib/hooks/useTradeItemSpec.js +448 -0
  51. package/dist-lib/hooks/useTradeItemStock.d.ts +19 -0
  52. package/dist-lib/hooks/useTradeItemStock.d.ts.map +1 -0
  53. package/dist-lib/hooks/useTradeItemStock.js +78 -0
  54. package/dist-lib/hooks/useVbnRegulatoryFeatures.d.ts +3 -0
  55. package/dist-lib/hooks/useVbnRegulatoryFeatures.d.ts.map +1 -0
  56. package/dist-lib/hooks/useVbnRegulatoryFeatures.js +19 -0
  57. package/dist-lib/hooks/useVoorraadData.d.ts +6 -0
  58. package/dist-lib/hooks/useVoorraadData.d.ts.map +1 -1
  59. package/dist-lib/hooks/useVoorraadData.js +65 -10
  60. package/dist-lib/index.d.ts +4 -0
  61. package/dist-lib/index.d.ts.map +1 -1
  62. package/dist-lib/index.js +6 -5
  63. package/dist-lib/integrations/supabase/auth-middleware.d.ts +9085 -1645
  64. package/dist-lib/integrations/supabase/auth-middleware.d.ts.map +1 -1
  65. package/dist-lib/integrations/supabase/client.d.ts +9085 -1645
  66. package/dist-lib/integrations/supabase/client.d.ts.map +1 -1
  67. package/dist-lib/integrations/supabase/client.server.d.ts +9085 -1645
  68. package/dist-lib/integrations/supabase/client.server.d.ts.map +1 -1
  69. package/dist-lib/integrations/supabase/types.d.ts +9298 -1655
  70. package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
  71. package/dist-lib/integrations/supabase/types.js +20 -1
  72. package/dist-lib/lib/__tests__/floricode-required-features.test.d.ts +2 -0
  73. package/dist-lib/lib/__tests__/floricode-required-features.test.d.ts.map +1 -0
  74. package/dist-lib/lib/__tests__/floricode-required-features.test.js +186 -0
  75. package/dist-lib/lib/floricode-client.d.ts +15 -1
  76. package/dist-lib/lib/floricode-client.d.ts.map +1 -1
  77. package/dist-lib/lib/floricode-client.js +42 -4
  78. package/dist-lib/lib/floricode-required-features.functions.d.ts +10026 -0
  79. package/dist-lib/lib/floricode-required-features.functions.d.ts.map +1 -0
  80. package/dist-lib/lib/floricode-required-features.functions.js +170 -0
  81. package/dist-lib/lib/floriday-client.d.ts +45 -19
  82. package/dist-lib/lib/floriday-client.d.ts.map +1 -1
  83. package/dist-lib/lib/floriday-client.js +236 -38
  84. package/dist-lib/lib/floriday-env.d.ts +29 -0
  85. package/dist-lib/lib/floriday-env.d.ts.map +1 -0
  86. package/dist-lib/lib/floriday-env.js +42 -0
  87. package/dist-lib/lib/floriday-env.server.d.ts +15 -0
  88. package/dist-lib/lib/floriday-env.server.d.ts.map +1 -0
  89. package/dist-lib/lib/floriday-env.server.js +13 -0
  90. package/dist-lib/lib/floriday-field-sources.d.ts +18 -0
  91. package/dist-lib/lib/floriday-field-sources.d.ts.map +1 -0
  92. package/dist-lib/lib/floriday-field-sources.js +45 -0
  93. package/dist-lib/lib/floriday-gateway-entities.d.ts +44 -0
  94. package/dist-lib/lib/floriday-gateway-entities.d.ts.map +1 -0
  95. package/dist-lib/lib/floriday-gateway-entities.js +51 -0
  96. package/dist-lib/lib/floriday-gateway.functions.d.ts +39999 -0
  97. package/dist-lib/lib/floriday-gateway.functions.d.ts.map +1 -0
  98. package/dist-lib/lib/floriday-gateway.functions.js +68 -0
  99. package/dist-lib/lib/floriday-gateway.server.d.ts +122 -0
  100. package/dist-lib/lib/floriday-gateway.server.d.ts.map +1 -0
  101. package/dist-lib/lib/floriday-gateway.server.js +230 -0
  102. package/dist-lib/lib/floriday-import-validator.d.ts +26 -0
  103. package/dist-lib/lib/floriday-import-validator.d.ts.map +1 -0
  104. package/dist-lib/lib/floriday-import-validator.js +41 -0
  105. package/dist-lib/lib/floriday-payload.d.ts +47 -3
  106. package/dist-lib/lib/floriday-payload.d.ts.map +1 -1
  107. package/dist-lib/lib/floriday-payload.js +179 -7
  108. package/dist-lib/lib/floriday-schema-to-zod.d.ts +9 -0
  109. package/dist-lib/lib/floriday-schema-to-zod.d.ts.map +1 -0
  110. package/dist-lib/lib/floriday-schema-to-zod.js +53 -0
  111. package/dist-lib/lib/floriday-writes.functions.d.ts +19998 -0
  112. package/dist-lib/lib/floriday-writes.functions.d.ts.map +1 -0
  113. package/dist-lib/lib/floriday-writes.functions.js +74 -0
  114. package/dist-lib/lib/image-resize.d.ts +43 -0
  115. package/dist-lib/lib/image-resize.d.ts.map +1 -0
  116. package/dist-lib/lib/image-resize.js +212 -0
  117. package/dist-lib/lib/normalize-errors-middleware.d.ts +2 -0
  118. package/dist-lib/lib/normalize-errors-middleware.d.ts.map +1 -0
  119. package/dist-lib/lib/normalize-errors-middleware.js +24 -0
  120. package/dist-lib/lib/wizard-step-fields.d.ts +15 -0
  121. package/dist-lib/lib/wizard-step-fields.d.ts.map +1 -0
  122. package/dist-lib/lib/wizard-step-fields.js +51 -0
  123. package/package.json +29 -14
@@ -0,0 +1,42 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Link } from "@tanstack/react-router";
3
+ import { Card } from "@flowselections/core";
4
+ function Stat({ label, value, tone = "default", }) {
5
+ const toneCls = tone === "danger"
6
+ ? "text-destructive"
7
+ : tone === "warn"
8
+ ? "text-amber-600"
9
+ : tone === "good"
10
+ ? "text-emerald-600"
11
+ : "text-foreground";
12
+ return (_jsxs("div", { children: [_jsx("div", { className: "text-xs text-muted-foreground", children: label }), _jsx("div", { className: `text-2xl font-semibold tabular-nums ${toneCls}`, children: value })] }));
13
+ }
14
+ export function VoorraadDashboard({ items, connectionId, }) {
15
+ const total = items.length;
16
+ const linked = items.filter((i) => i.published).length +
17
+ items.filter((i) => !i.published && i.linkStatus === "linked").length;
18
+ const waiting = items.filter((i) => !i.published && i.linkStatus === "waiting_for_data").length;
19
+ const validationErrors = items.filter((i) => !i.published && (i.missingFields?.length ?? 0) > 0 && i.linkStatus !== "linked").length;
20
+ const publishErrors = items.filter((i) => !i.published && i.linkStatus === "publish_failed").length;
21
+ // Nieuw sinds laatste import
22
+ let lastImport = null;
23
+ try {
24
+ if (connectionId) {
25
+ const s = localStorage.getItem(`voorraad_last_import_${connectionId}`);
26
+ if (s)
27
+ lastImport = new Date(s);
28
+ }
29
+ }
30
+ catch {
31
+ /* ignore */
32
+ }
33
+ const newSinceImport = lastImport
34
+ ? items.filter((i) => {
35
+ const raw = i.raw;
36
+ const src = raw?.source ?? raw?.data?.source;
37
+ const created = raw?.created_at ? new Date(raw.created_at) : null;
38
+ return src === "excel_import" && created && created >= lastImport;
39
+ }).length
40
+ : 0;
41
+ return (_jsxs(Card, { className: "p-4", children: [_jsxs("div", { className: "flex items-start justify-between gap-3 mb-3", children: [_jsxs("div", { children: [_jsx("div", { className: "text-sm font-medium", children: "Floriday-koppeling overzicht" }), _jsx("div", { className: "text-xs text-muted-foreground", children: "Status van alle artikelen ten opzichte van Floriday" })] }), _jsx(Link, { to: "/voorraad/validatie", className: "text-xs text-primary hover:underline", children: "Openstaande artikelen bekijken \u2192" })] }), _jsxs("div", { className: "grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-6 gap-4", children: [_jsx(Stat, { label: "Artikelen", value: total }), _jsx(Stat, { label: "Gekoppeld", value: linked, tone: "good" }), _jsx(Stat, { label: "Wacht op koppeling", value: waiting, tone: "warn" }), _jsx(Stat, { label: "Validatiefouten", value: validationErrors, tone: "warn" }), _jsx(Stat, { label: "Publicatiefouten", value: publishErrors, tone: "danger" }), _jsx(Stat, { label: "Nieuw sinds import", value: newSinceImport })] })] }));
42
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"VoorraadPage.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/VoorraadPage.tsx"],"names":[],"mappings":"AAUA,wBAAgB,YAAY,4CAuE3B"}
1
+ {"version":3,"file":"VoorraadPage.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/VoorraadPage.tsx"],"names":[],"mappings":"AA6EA,wBAAgB,YAAY,4CAiM3B"}
@@ -1,23 +1,90 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useMemo, useState } from "react";
2
+ import { useEffect, useMemo, useState } from "react";
3
3
  import { Link } from "@tanstack/react-router";
4
4
  import { Button, Card, Input } from "@flowselections/core";
5
- import { Plus, RefreshCw, Search } from "lucide-react";
5
+ import { AlertTriangle, CheckCircle2, FileText, PackageX, Plus, Search, } from "lucide-react";
6
6
  import { useFloridayConnection } from "../../hooks/useFloridayConnection";
7
7
  import { useVoorraadData } from "../../hooks/useVoorraadData";
8
- import { ConnectionPicker } from "./ConnectionPicker";
9
8
  import { ExcelExportButton } from "./ExcelExportButton";
9
+ import { StockBulkButtons } from "./StockBulkButtons";
10
10
  import { VoorraadTable } from "./VoorraadTable";
11
+ import { VoorraadPagination } from "./VoorraadPagination";
12
+ function toneClasses(tone) {
13
+ switch (tone) {
14
+ case "emerald":
15
+ return { icon: "bg-emerald-100 text-emerald-700", value: "text-foreground" };
16
+ case "rose":
17
+ return { icon: "bg-rose-100 text-rose-700", value: "text-foreground" };
18
+ case "amber":
19
+ return { icon: "bg-amber-100 text-amber-700", value: "text-foreground" };
20
+ case "primary":
21
+ default:
22
+ return { icon: "bg-primary/10 text-primary", value: "text-foreground" };
23
+ }
24
+ }
25
+ function KpiCard({ icon, label, count, tone, active, onClick, }) {
26
+ const cls = toneClasses(tone);
27
+ return (_jsxs("button", { type: "button", onClick: onClick, className: `text-left rounded-lg border bg-card p-4 transition-colors hover:border-primary/50 ${active ? "border-primary ring-1 ring-primary" : "border-border"}`, children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("span", { className: `inline-flex h-8 w-8 items-center justify-center rounded-md ${cls.icon}`, children: icon }), _jsx("span", { className: "text-sm font-medium text-foreground", children: label })] }), _jsx("div", { className: `mt-3 text-3xl font-bold tabular-nums ${cls.value}`, children: count })] }));
28
+ }
29
+ function availableOf(i) {
30
+ return i.availableQuantity ?? i.quantity ?? 0;
31
+ }
11
32
  export function VoorraadPage() {
12
- const { connections, activeId, setActiveId, loading: connLoading, error: connError, } = useFloridayConnection();
33
+ const { activeId, error: connError } = useFloridayConnection();
13
34
  const { items, loading, error, reload } = useVoorraadData(activeId);
14
35
  const [search, setSearch] = useState("");
36
+ const [stockFilter, setStockFilter] = useState("all");
37
+ const [page, setPage] = useState(1);
38
+ const [pageSize, setPageSize] = useState(25);
39
+ const counts = useMemo(() => {
40
+ let inStock = 0;
41
+ let outOfStock = 0;
42
+ let belowMin = 0;
43
+ for (const i of items) {
44
+ const avail = availableOf(i);
45
+ if (avail > 0)
46
+ inStock++;
47
+ else
48
+ outOfStock++;
49
+ if ((i.minQuantity ?? 0) > 0 && avail < i.minQuantity)
50
+ belowMin++;
51
+ }
52
+ return { total: items.length, inStock, outOfStock, belowMin };
53
+ }, [items]);
15
54
  const filtered = useMemo(() => {
16
- if (!search.trim())
17
- return items;
18
- const q = search.toLowerCase();
19
- return items.filter((i) => i.name.toLowerCase().includes(q) ||
20
- i.articleNumber.toLowerCase().includes(q));
21
- }, [items, search]);
22
- return (_jsxs("main", { className: "p-6 space-y-4", children: [_jsxs("div", { className: "flex flex-wrap items-center gap-3", children: [_jsxs("div", { className: "relative flex-1 min-w-[240px] max-w-md", children: [_jsx(Search, { className: "absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" }), _jsx(Input, { value: search, onChange: (e) => setSearch(e.target.value), placeholder: "Zoek op product of artikelnummer...", className: "pl-9" })] }), _jsx(ConnectionPicker, { connections: connections, activeId: activeId, onChange: setActiveId, disabled: connLoading }), _jsxs(Button, { variant: "outline", onClick: reload, disabled: loading || !activeId, children: [_jsx(RefreshCw, { className: `mr-2 h-4 w-4 ${loading ? "animate-spin" : ""}` }), "Vernieuwen"] }), _jsx(ExcelExportButton, { items: filtered }), _jsx(Button, { asChild: true, children: _jsxs(Link, { to: "/voorraad/nieuw", children: [_jsx(Plus, { className: "mr-2 h-4 w-4" }), "Artikel toevoegen"] }) })] }), connError && (_jsxs(Card, { className: "border-destructive/40 bg-destructive/5 p-4 text-sm", children: ["Floriday-connecties laden mislukt: ", connError] })), error && (_jsxs(Card, { className: "border-destructive/40 bg-destructive/5 p-4 text-sm", children: ["Voorraad laden mislukt: ", error] })), _jsxs(Card, { className: "overflow-hidden", children: [_jsxs("div", { className: "px-4 py-2 border-b text-xs text-muted-foreground", children: [filtered.length, " producten ", search && `(gefilterd uit ${items.length})`] }), _jsx(VoorraadTable, { items: filtered, loading: loading })] })] }));
55
+ const q = search.trim().toLowerCase();
56
+ return items.filter((i) => {
57
+ const avail = availableOf(i);
58
+ if (stockFilter === "in_stock" && avail <= 0)
59
+ return false;
60
+ if (stockFilter === "out_of_stock" && avail > 0)
61
+ return false;
62
+ if (stockFilter === "below_min" &&
63
+ !((i.minQuantity ?? 0) > 0 && avail < i.minQuantity))
64
+ return false;
65
+ if (!q)
66
+ return true;
67
+ return (i.name.toLowerCase().includes(q) ||
68
+ (i.articleNumber ?? "").toLowerCase().includes(q));
69
+ });
70
+ }, [items, search, stockFilter]);
71
+ const totalPages = Math.max(1, Math.ceil(filtered.length / pageSize));
72
+ useEffect(() => {
73
+ if (page > totalPages)
74
+ setPage(1);
75
+ }, [page, totalPages]);
76
+ const paged = useMemo(() => filtered.slice((page - 1) * pageSize, page * pageSize), [filtered, page, pageSize]);
77
+ const toggle = (id) => setStockFilter((cur) => (cur === id && id !== "all" ? "all" : id));
78
+ const TABS = [
79
+ { id: "all", label: "Alle", count: counts.total },
80
+ { id: "in_stock", label: "Op voorraad", count: counts.inStock },
81
+ { id: "out_of_stock", label: "Niet op voorraad", count: counts.outOfStock },
82
+ { id: "below_min", label: "Onder minimum", count: counts.belowMin },
83
+ ];
84
+ return (_jsxs("main", { className: "p-6 space-y-6", children: [_jsxs("header", { className: "grid grid-cols-[minmax(0,1fr)_auto] items-start gap-4", children: [_jsxs("div", { className: "min-w-0", children: [_jsx("h1", { className: "text-2xl font-bold tracking-tight truncate", children: "Voorraad" }), _jsx("p", { className: "text-sm text-muted-foreground mt-1", children: "Beheer je artikelen en voorraadniveaus." })] }), _jsx(Button, { asChild: true, disabled: !activeId, size: "sm", className: "shrink-0", children: _jsxs(Link, { to: "/voorraad/nieuw", children: [_jsx(Plus, { className: "mr-2 h-4 w-4" }), "Artikel toevoegen"] }) })] }), connError && (_jsxs("div", { className: "rounded-md border border-destructive/40 bg-destructive/10 text-destructive p-3 text-sm", children: ["Floriday-connectie laden mislukt: ", connError] })), error && (_jsxs("div", { className: "rounded-md border border-destructive/40 bg-destructive/10 text-destructive p-3 text-sm", children: ["Voorraad laden mislukt: ", error] })), _jsxs("div", { className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4", children: [_jsx(KpiCard, { icon: _jsx(FileText, { className: "h-4 w-4" }), tone: "primary", label: "Artikelen", count: counts.total, active: stockFilter === "all", onClick: () => toggle("all") }), _jsx(KpiCard, { icon: _jsx(CheckCircle2, { className: "h-4 w-4" }), tone: "emerald", label: "Op voorraad", count: counts.inStock, active: stockFilter === "in_stock", onClick: () => toggle("in_stock") }), _jsx(KpiCard, { icon: _jsx(PackageX, { className: "h-4 w-4" }), tone: "rose", label: "Niet op voorraad", count: counts.outOfStock, active: stockFilter === "out_of_stock", onClick: () => toggle("out_of_stock") }), _jsx(KpiCard, { icon: _jsx(AlertTriangle, { className: "h-4 w-4" }), tone: "amber", label: "Onder minimum", count: counts.belowMin, active: stockFilter === "below_min", onClick: () => toggle("below_min") })] }), _jsxs(Card, { className: "overflow-hidden p-0", children: [_jsx("div", { className: "flex flex-wrap items-center gap-1 border-b px-4", children: TABS.map((t) => {
85
+ const active = stockFilter === t.id;
86
+ return (_jsxs("button", { type: "button", onClick: () => setStockFilter(t.id), className: `relative py-3 px-3 text-sm font-medium whitespace-nowrap transition-colors ${active
87
+ ? "text-foreground"
88
+ : "text-muted-foreground hover:text-foreground"}`, children: [t.label, _jsx("span", { className: `ml-2 text-xs ${active ? "text-primary" : "text-muted-foreground/70"}`, children: t.count }), active && (_jsx("span", { className: "absolute left-2 right-2 -bottom-px h-0.5 bg-primary rounded-full" }))] }, t.id));
89
+ }) }), _jsxs("div", { className: "flex flex-wrap items-center gap-2 p-4 border-b", children: [_jsxs("div", { className: "relative flex-1 min-w-[220px] max-w-md", children: [_jsx(Search, { className: "w-4 h-4 absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground" }), _jsx(Input, { placeholder: "Zoek op product of artikelnummer...", className: "pl-9", value: search, onChange: (e) => setSearch(e.target.value) })] }), _jsxs("div", { className: "ml-auto flex flex-wrap items-center gap-2", children: [_jsx(ExcelExportButton, { items: filtered }), _jsx(StockBulkButtons, { items: items, activeConnectionId: activeId, onImported: reload })] })] }), _jsx("div", { className: "overflow-x-auto", children: _jsx(VoorraadTable, { items: paged, loading: loading }) }), _jsx(VoorraadPagination, { total: filtered.length, page: page, pageSize: pageSize, onPageChange: setPage, onPageSizeChange: setPageSize })] })] }));
23
90
  }
@@ -0,0 +1,10 @@
1
+ export interface VoorraadPaginationProps {
2
+ total: number;
3
+ page: number;
4
+ pageSize: number;
5
+ pageSizeOptions?: number[];
6
+ onPageChange: (page: number) => void;
7
+ onPageSizeChange: (size: number) => void;
8
+ }
9
+ export declare function VoorraadPagination({ total, page, pageSize, pageSizeOptions, onPageChange, onPageSizeChange, }: VoorraadPaginationProps): import("react/jsx-runtime").JSX.Element;
10
+ //# sourceMappingURL=VoorraadPagination.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"VoorraadPagination.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/VoorraadPagination.tsx"],"names":[],"mappings":"AASA,MAAM,WAAW,uBAAuB;IACtC,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,YAAY,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC,gBAAgB,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CAC1C;AAED,wBAAgB,kBAAkB,CAAC,EACjC,KAAK,EACL,IAAI,EACJ,QAAQ,EACR,eAAmC,EACnC,YAAY,EACZ,gBAAgB,GACjB,EAAE,uBAAuB,2CAuDzB"}
@@ -0,0 +1,12 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Button, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@flowselections/core";
3
+ export function VoorraadPagination({ total, page, pageSize, pageSizeOptions = [10, 25, 50, 100], onPageChange, onPageSizeChange, }) {
4
+ const totalPages = Math.max(1, Math.ceil(total / pageSize));
5
+ const safePage = Math.min(Math.max(1, page), totalPages);
6
+ const from = total === 0 ? 0 : (safePage - 1) * pageSize + 1;
7
+ const to = Math.min(total, safePage * pageSize);
8
+ return (_jsxs("div", { className: "flex flex-wrap items-center justify-between gap-3 border-t px-4 py-2 text-sm text-muted-foreground", children: [_jsx("div", { children: total === 0 ? "0 van 0" : `${from}–${to} van ${total}` }), _jsxs("div", { className: "flex items-center gap-3", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("span", { children: "Per pagina" }), _jsxs(Select, { value: String(pageSize), onValueChange: (v) => {
9
+ onPageSizeChange(Number(v));
10
+ onPageChange(1);
11
+ }, children: [_jsx(SelectTrigger, { className: "h-8 w-[72px]", children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: pageSizeOptions.map((opt) => (_jsx(SelectItem, { value: String(opt), children: opt }, opt))) })] })] }), _jsx(Button, { variant: "outline", size: "sm", disabled: safePage <= 1, onClick: () => onPageChange(safePage - 1), children: "Vorige" }), _jsxs("span", { className: "tabular-nums", children: [safePage, " / ", totalPages] }), _jsx(Button, { variant: "outline", size: "sm", disabled: safePage >= totalPages, onClick: () => onPageChange(safePage + 1), children: "Volgende" })] })] }));
12
+ }
@@ -0,0 +1,8 @@
1
+ interface Props {
2
+ connectionId: string | null;
3
+ tradeItemId?: string | null;
4
+ draftId?: string | null;
5
+ }
6
+ export declare function VoorraadStockCard({ connectionId, tradeItemId, draftId }: Props): import("react/jsx-runtime").JSX.Element;
7
+ export {};
8
+ //# sourceMappingURL=VoorraadStockCard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"VoorraadStockCard.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/VoorraadStockCard.tsx"],"names":[],"mappings":"AAaA,UAAU,KAAK;IACb,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AAED,wBAAgB,iBAAiB,CAAC,EAAE,YAAY,EAAE,WAAW,EAAE,OAAO,EAAE,EAAE,KAAK,2CAoG9E"}
@@ -0,0 +1,33 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useEffect, useState } from "react";
3
+ import { Button, Card, CardContent, Input, Label, Textarea, toast, } from "@flowselections/core";
4
+ import { Loader2, Package, Check } from "lucide-react";
5
+ import { useTradeItemStock, stockKeyFor } from "../../hooks/useTradeItemStock";
6
+ export function VoorraadStockCard({ connectionId, tradeItemId, draftId }) {
7
+ const key = stockKeyFor({ tradeItemId, draftId });
8
+ const { row, loading, save } = useTradeItemStock(connectionId, key);
9
+ const [values, setValues] = useState(row);
10
+ const [saving, setSaving] = useState(false);
11
+ useEffect(() => {
12
+ setValues(row);
13
+ }, [row.updated_at, row.physical_stock, row.minimum_stock, row.reserved_stock, row.incoming_stock, row.notes]);
14
+ if (!key) {
15
+ return (_jsx(Card, { className: "p-4 text-sm text-muted-foreground", children: "Voorraad kan pas worden ingesteld nadat het artikel voor het eerst is opgeslagen." }));
16
+ }
17
+ const available = Math.max(0, values.physical_stock - values.reserved_stock);
18
+ const handleSave = async () => {
19
+ setSaving(true);
20
+ try {
21
+ await save(values);
22
+ toast.success("Voorraad bijgewerkt");
23
+ }
24
+ catch (e) {
25
+ toast.error(`Opslaan mislukt: ${e.message}`);
26
+ }
27
+ finally {
28
+ setSaving(false);
29
+ }
30
+ };
31
+ const numField = (id, label) => (_jsxs("div", { className: "space-y-1", children: [_jsx(Label, { htmlFor: id, children: label }), _jsx(Input, { id: id, type: "number", min: 0, value: String(values[id] ?? 0), onChange: (e) => setValues((v) => ({ ...v, [id]: Number(e.target.value) || 0 })) })] }));
32
+ return (_jsxs(Card, { children: [_jsxs("div", { className: "grid grid-cols-[auto_minmax(0,1fr)_auto] items-center gap-3 px-6 py-4", children: [_jsx("span", { className: "flex h-7 w-7 items-center justify-center rounded-full border border-primary/40 bg-primary/10 text-primary", children: _jsx(Package, { className: "h-4 w-4" }) }), _jsx("span", { className: "font-medium", children: "Voorraad" }), _jsxs("span", { className: "text-xs text-muted-foreground", children: ["Beschikbaar: ", _jsx("span", { className: "tabular-nums font-medium text-foreground", children: available })] })] }), _jsx(CardContent, { className: "pt-0 space-y-4", children: loading ? (_jsxs("div", { className: "flex items-center gap-2 text-sm text-muted-foreground", children: [_jsx(Loader2, { className: "h-4 w-4 animate-spin" }), " Voorraad laden\u2026"] })) : (_jsxs(_Fragment, { children: [_jsxs("div", { className: "grid grid-cols-2 gap-4 sm:grid-cols-4", children: [numField("physical_stock", "Liggend"), numField("minimum_stock", "Minimum"), numField("reserved_stock", "Gereserveerd"), numField("incoming_stock", "Inkomend")] }), _jsxs("div", { className: "space-y-1", children: [_jsx(Label, { htmlFor: "stock-notes", children: "Notitie (optioneel)" }), _jsx(Textarea, { id: "stock-notes", rows: 2, value: values.notes ?? "", onChange: (e) => setValues((v) => ({ ...v, notes: e.target.value })), placeholder: "Bijv. locatie of opmerking" })] }), _jsx("div", { className: "flex justify-end", children: _jsxs(Button, { onClick: handleSave, disabled: saving, children: [saving ? (_jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" })) : (_jsx(Check, { className: "mr-2 h-4 w-4" })), "Voorraad opslaan"] }) })] })) })] }));
33
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"VoorraadTable.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/VoorraadTable.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAW3D,wBAAgB,aAAa,CAAC,EAC5B,KAAK,EACL,OAAO,GACR,EAAE;IACD,KAAK,EAAE,SAAS,EAAE,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;CAClB,2CAoEA"}
1
+ {"version":3,"file":"VoorraadTable.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/VoorraadTable.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAW3D,wBAAgB,aAAa,CAAC,EAC5B,KAAK,EACL,OAAO,GACR,EAAE;IACD,KAAK,EAAE,SAAS,EAAE,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;CAClB,2CA8GA"}
@@ -1,4 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useNavigate } from "@tanstack/react-router";
3
+ import { Badge } from "@flowselections/core";
2
4
  import { Package } from "lucide-react";
3
5
  const columns = [
4
6
  { key: "quantity", label: "Liggend" },
@@ -9,6 +11,7 @@ const columns = [
9
11
  { key: "economicQuantity", label: "Economisch" },
10
12
  ];
11
13
  export function VoorraadTable({ items, loading, }) {
14
+ const navigate = useNavigate();
12
15
  if (loading) {
13
16
  return _jsx("p", { className: "text-sm text-muted-foreground p-6", children: "Voorraad laden..." });
14
17
  }
@@ -16,7 +19,23 @@ export function VoorraadTable({ items, loading, }) {
16
19
  return (_jsx("p", { className: "text-sm text-muted-foreground p-6", children: "Geen producten gevonden voor deze connectie." }));
17
20
  }
18
21
  return (_jsx("div", { className: "overflow-x-auto", children: _jsxs("table", { className: "w-full text-sm", children: [_jsx("thead", { className: "bg-muted/50 text-left", children: _jsxs("tr", { children: [_jsx("th", { className: "p-3 w-14" }), _jsx("th", { className: "p-3", children: "Product" }), _jsx("th", { className: "p-3", children: "Artikelnummer" }), columns.map((c) => (_jsx("th", { className: "p-3 text-right whitespace-nowrap", children: c.label }, c.key)))] }) }), _jsx("tbody", { children: items.map((item) => {
19
- const low = item.minQuantity > 0 && item.availableQuantity < item.minQuantity;
20
- return (_jsxs("tr", { className: "border-t hover:bg-muted/30", children: [_jsx("td", { className: "p-2", children: item.imageUrl ? (_jsx("img", { src: item.imageUrl, alt: item.name, className: "h-10 w-10 rounded object-cover", loading: "lazy" })) : (_jsx("div", { className: "h-10 w-10 rounded bg-muted flex items-center justify-center", children: _jsx(Package, { className: "h-4 w-4 text-muted-foreground" }) })) }), _jsxs("td", { className: "p-3", children: [_jsx("div", { className: "font-medium text-foreground", children: item.name }), low && (_jsx("div", { className: "text-xs text-destructive", children: "onder minimumvoorraad" }))] }), _jsx("td", { className: "p-3 font-mono text-xs", children: item.articleNumber }), columns.map((c) => (_jsx("td", { className: "p-3 text-right tabular-nums", children: Number(item[c.key] ?? 0) }, c.key)))] }, item.id));
22
+ const isLocal = !item.published;
23
+ const low = !isLocal &&
24
+ item.minQuantity > 0 &&
25
+ item.availableQuantity < item.minQuantity;
26
+ const linkId = isLocal ? item.localId : item.id;
27
+ const clickable = Boolean(linkId);
28
+ const open = () => {
29
+ if (linkId)
30
+ navigate({ to: "/voorraad/$id", params: { id: linkId } });
31
+ };
32
+ return (_jsxs("tr", { className: `border-t hover:bg-muted/30 ${clickable ? "cursor-pointer" : ""}`, onClick: clickable ? open : undefined, role: clickable ? "button" : undefined, tabIndex: clickable ? 0 : undefined, onKeyDown: clickable
33
+ ? (e) => {
34
+ if (e.key === "Enter" || e.key === " ") {
35
+ e.preventDefault();
36
+ open();
37
+ }
38
+ }
39
+ : undefined, "aria-label": clickable ? `Bewerk ${item.name}` : undefined, children: [_jsx("td", { className: "p-2", children: item.imageUrl ? (_jsx("img", { src: item.imageUrl, alt: item.name, className: "h-10 w-10 rounded object-cover", loading: "lazy" })) : (_jsx("div", { className: "h-10 w-10 rounded bg-muted flex items-center justify-center", children: _jsx(Package, { className: "h-4 w-4 text-muted-foreground" }) })) }), _jsxs("td", { className: "p-3", children: [_jsxs("div", { className: "flex items-center gap-2 flex-wrap", children: [_jsx("span", { className: "font-medium text-foreground", children: item.name }), isLocal && (_jsx(Badge, { variant: "outline", className: "text-[10px] font-normal text-muted-foreground border-amber-500/40 bg-amber-500/10", children: "Niet gepubliceerd" }))] }), low && (_jsx("div", { className: "text-xs text-destructive", children: "onder minimumvoorraad" }))] }), _jsx("td", { className: "p-3 font-mono text-xs", children: item.articleNumber || "—" }), columns.map((c) => (_jsx("td", { className: "p-3 text-right tabular-nums text-muted-foreground", children: Number(item[c.key] ?? 0) }, c.key)))] }, item.id));
21
40
  }) })] }) }));
22
41
  }
@@ -0,0 +1,55 @@
1
+ export interface FloridaySchemaField {
2
+ name: string;
3
+ type: string;
4
+ required?: boolean;
5
+ enum?: string[];
6
+ description?: string;
7
+ minLength?: number;
8
+ maxLength?: number;
9
+ minimum?: number;
10
+ maximum?: number;
11
+ pattern?: string;
12
+ nullable?: boolean;
13
+ }
14
+ export interface FloridaySchema {
15
+ ok: boolean;
16
+ resource: string;
17
+ version: string;
18
+ requiredFields: string[];
19
+ fields: FloridaySchemaField[];
20
+ enums?: Record<string, string[]>;
21
+ schema?: unknown;
22
+ /** §15.2 — code-lists die in dezelfde call zijn meegeleverd. */
23
+ codeLists?: Record<string, Array<{
24
+ code: string;
25
+ label?: string | null;
26
+ description?: string | null;
27
+ payload?: unknown;
28
+ }>>;
29
+ }
30
+ export declare function useFloridaySchema(resource: string, params?: Record<string, string>): import("@tanstack/react-query").UseQueryResult<FloridaySchema, Error>;
31
+ export declare function useTradeItemFormSchema(): import("@tanstack/react-query").UseQueryResult<FloridaySchema, Error>;
32
+ export declare function useCodeList(listName: string | null | undefined): import("@tanstack/react-query").UseQueryResult<{
33
+ ok: boolean;
34
+ listName: string;
35
+ values: {
36
+ code: string;
37
+ description?: string;
38
+ }[];
39
+ }, Error>;
40
+ import type { GatewayEntity } from "../lib/floriday-gateway-entities";
41
+ export declare function useCachedList(entity: GatewayEntity | null | undefined, limit?: number): import("@tanstack/react-query").UseQueryResult<{
42
+ ok: boolean;
43
+ entity: string;
44
+ rows: {
45
+ id: string;
46
+ data: Record<string, any>;
47
+ }[];
48
+ }, Error>;
49
+ /**
50
+ * Invalideert alle floriday-* queries wanneer de actieve connectie
51
+ * (en dus mogelijk de environment) wijzigt. Voorkomt cache-bleed
52
+ * tussen staging en live bij een toekomstige switcher.
53
+ */
54
+ export declare function useInvalidateFloridayOnConnectionChange(): void;
55
+ //# sourceMappingURL=useFloriday.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useFloriday.d.ts","sourceRoot":"","sources":["../../src/hooks/useFloriday.ts"],"names":[],"mappings":"AAWA,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,OAAO,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,MAAM,EAAE,mBAAmB,EAAE,CAAC;IAC9B,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IACjC,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,gEAAgE;IAChE,SAAS,CAAC,EAAE,MAAM,CAChB,MAAM,EACN,KAAK,CAAC;QACJ,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QACtB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAC5B,OAAO,CAAC,EAAE,OAAO,CAAC;KACnB,CAAC,CACH,CAAC;CACH;AAoMD,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,MAAM,EAChB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,yEA0BhC;AAqBD,wBAAgB,sBAAsB,0EA0CrC;AAED,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS;QAMnD,OAAO;cACD,MAAM;YACR;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,EAAE;UAKvD;AAED,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kCAAkC,CAAC;AAEtE,wBAAgB,aAAa,CAC3B,MAAM,EAAE,aAAa,GAAG,IAAI,GAAG,SAAS,EACxC,KAAK,SAAO;QAOF,OAAO;YACH,MAAM;UACR;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;KAAE,EAAE;UAKxD;AAED;;;;GAIG;AACH,wBAAgB,uCAAuC,SAStD"}