@flowselections/floriday-voorraad 1.0.145 → 1.0.149

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (100) hide show
  1. package/dist-lib/components/voorraad/ArtikelWizard.d.ts +3 -1
  2. package/dist-lib/components/voorraad/ArtikelWizard.d.ts.map +1 -1
  3. package/dist-lib/components/voorraad/ArtikelWizard.js +56 -52
  4. package/dist-lib/components/voorraad/EditArtikelPage.d.ts.map +1 -1
  5. package/dist-lib/components/voorraad/EditArtikelPage.js +15 -23
  6. package/dist-lib/components/voorraad/OrderHistoryCard.d.ts +3 -1
  7. package/dist-lib/components/voorraad/OrderHistoryCard.d.ts.map +1 -1
  8. package/dist-lib/components/voorraad/OrderHistoryCard.js +7 -3
  9. package/dist-lib/components/voorraad/ProductCustomFieldsCard.d.ts +5 -1
  10. package/dist-lib/components/voorraad/ProductCustomFieldsCard.d.ts.map +1 -1
  11. package/dist-lib/components/voorraad/ProductCustomFieldsCard.js +37 -18
  12. package/dist-lib/components/voorraad/ProductSalePriceField.d.ts.map +1 -1
  13. package/dist-lib/components/voorraad/ProductSalePriceField.js +11 -8
  14. package/dist-lib/components/voorraad/StepSamenstelling.d.ts.map +1 -1
  15. package/dist-lib/components/voorraad/StepSamenstelling.js +35 -10
  16. package/dist-lib/components/voorraad/StockMutationHistoryCard.d.ts +3 -1
  17. package/dist-lib/components/voorraad/StockMutationHistoryCard.d.ts.map +1 -1
  18. package/dist-lib/components/voorraad/StockMutationHistoryCard.js +16 -10
  19. package/dist-lib/components/voorraad/TrayStickerCard.d.ts +5 -1
  20. package/dist-lib/components/voorraad/TrayStickerCard.d.ts.map +1 -1
  21. package/dist-lib/components/voorraad/TrayStickerCard.js +64 -66
  22. package/dist-lib/components/voorraad/ValidationOverviewTable.d.ts +0 -5
  23. package/dist-lib/components/voorraad/ValidationOverviewTable.d.ts.map +1 -1
  24. package/dist-lib/components/voorraad/ValidationOverviewTable.js +10 -4
  25. package/dist-lib/components/voorraad/VoorraadBeheerCard.d.ts +3 -0
  26. package/dist-lib/components/voorraad/VoorraadBeheerCard.d.ts.map +1 -0
  27. package/dist-lib/components/voorraad/VoorraadBeheerCard.js +44 -0
  28. package/dist-lib/components/voorraad/VoorraadPage.d.ts +1 -13
  29. package/dist-lib/components/voorraad/VoorraadPage.d.ts.map +1 -1
  30. package/dist-lib/components/voorraad/VoorraadPage.js +102 -140
  31. package/dist-lib/components/voorraad/VoorraadSturingCard.d.ts +6 -0
  32. package/dist-lib/components/voorraad/VoorraadSturingCard.d.ts.map +1 -0
  33. package/dist-lib/components/voorraad/VoorraadSturingCard.js +47 -0
  34. package/dist-lib/components/voorraad/artikel/ArtikelPagina.d.ts +6 -0
  35. package/dist-lib/components/voorraad/artikel/ArtikelPagina.d.ts.map +1 -0
  36. package/dist-lib/components/voorraad/artikel/ArtikelPagina.js +313 -0
  37. package/dist-lib/components/voorraad/artikel/TwijfelKiezen.d.ts +7 -0
  38. package/dist-lib/components/voorraad/artikel/TwijfelKiezen.d.ts.map +1 -0
  39. package/dist-lib/components/voorraad/artikel/TwijfelKiezen.js +54 -0
  40. package/dist-lib/components/voorraad/artikel/opslaan-context.d.ts +21 -0
  41. package/dist-lib/components/voorraad/artikel/opslaan-context.d.ts.map +1 -0
  42. package/dist-lib/components/voorraad/artikel/opslaan-context.js +15 -0
  43. package/dist-lib/components/voorraad/lijst/ImporteerKnop.d.ts +5 -0
  44. package/dist-lib/components/voorraad/lijst/ImporteerKnop.d.ts.map +1 -0
  45. package/dist-lib/components/voorraad/lijst/ImporteerKnop.js +20 -0
  46. package/dist-lib/components/voorraad/lijst/LijstExportKnop.d.ts +6 -0
  47. package/dist-lib/components/voorraad/lijst/LijstExportKnop.d.ts.map +1 -0
  48. package/dist-lib/components/voorraad/lijst/LijstExportKnop.js +43 -0
  49. package/dist-lib/components/voorraad/lijst/SnelActieDialogen.d.ts +16 -0
  50. package/dist-lib/components/voorraad/lijst/SnelActieDialogen.d.ts.map +1 -0
  51. package/dist-lib/components/voorraad/lijst/SnelActieDialogen.js +95 -0
  52. package/dist-lib/components/voorraad/lijst/VoorraadLijstTabel.d.ts +21 -0
  53. package/dist-lib/components/voorraad/lijst/VoorraadLijstTabel.d.ts.map +1 -0
  54. package/dist-lib/components/voorraad/lijst/VoorraadLijstTabel.js +39 -0
  55. package/dist-lib/hooks/useVoorraadCustomFields.d.ts +2 -1
  56. package/dist-lib/hooks/useVoorraadCustomFields.d.ts.map +1 -1
  57. package/dist-lib/hooks/useVoorraadCustomFields.js +8 -4
  58. package/dist-lib/hooks/useVoorraadLijst.d.ts +21 -0
  59. package/dist-lib/hooks/useVoorraadLijst.d.ts.map +1 -0
  60. package/dist-lib/hooks/useVoorraadLijst.js +71 -0
  61. package/dist-lib/index.d.ts.map +1 -1
  62. package/dist-lib/index.js +4 -0
  63. package/dist-lib/integrations/supabase/types.d.ts +748 -1
  64. package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
  65. package/dist-lib/lib/artikel-gewijzigd.d.ts +4 -0
  66. package/dist-lib/lib/artikel-gewijzigd.d.ts.map +1 -0
  67. package/dist-lib/lib/artikel-gewijzigd.js +7 -0
  68. package/dist-lib/lib/artikel-pagina.d.ts +68 -0
  69. package/dist-lib/lib/artikel-pagina.d.ts.map +1 -0
  70. package/dist-lib/lib/artikel-pagina.js +97 -0
  71. package/dist-lib/lib/count-stock.d.ts +17 -0
  72. package/dist-lib/lib/count-stock.d.ts.map +1 -0
  73. package/dist-lib/lib/count-stock.js +26 -0
  74. package/dist-lib/lib/floriday-import-validator.d.ts +9 -0
  75. package/dist-lib/lib/floriday-import-validator.d.ts.map +1 -1
  76. package/dist-lib/lib/floriday-import-validator.js +13 -0
  77. package/dist-lib/lib/floriday-versie-ronde.d.ts +59 -0
  78. package/dist-lib/lib/floriday-versie-ronde.d.ts.map +1 -0
  79. package/dist-lib/lib/floriday-versie-ronde.functions.d.ts +10 -0
  80. package/dist-lib/lib/floriday-versie-ronde.functions.d.ts.map +1 -0
  81. package/dist-lib/lib/floriday-versie-ronde.functions.js +22 -0
  82. package/dist-lib/lib/floriday-versie-ronde.js +106 -0
  83. package/dist-lib/lib/pending-photos.d.ts +20 -0
  84. package/dist-lib/lib/pending-photos.d.ts.map +1 -0
  85. package/dist-lib/lib/pending-photos.js +90 -0
  86. package/dist-lib/lib/voorraad-custom-fields.d.ts +16 -1
  87. package/dist-lib/lib/voorraad-custom-fields.d.ts.map +1 -1
  88. package/dist-lib/lib/voorraad-custom-fields.js +12 -6
  89. package/dist-lib/lib/voorraad-lijst.d.ts +92 -0
  90. package/dist-lib/lib/voorraad-lijst.d.ts.map +1 -0
  91. package/dist-lib/lib/voorraad-lijst.js +86 -0
  92. package/dist-lib/server-hooks/floriday-stock-reconcile.d.ts.map +1 -1
  93. package/dist-lib/server-hooks/floriday-stock-reconcile.js +5 -0
  94. package/dist-lib/server-hooks/floriday-versieronde.d.ts +2 -0
  95. package/dist-lib/server-hooks/floriday-versieronde.d.ts.map +1 -0
  96. package/dist-lib/server-hooks/floriday-versieronde.js +29 -0
  97. package/dist-lib/server-hooks/guard.js +1 -1
  98. package/package.json +1 -1
  99. package/sql/AGENTS.md +6 -0
  100. package/sql/floriday-voorraad-schema.sql +1355 -22
@@ -0,0 +1,313 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { lazy, Suspense, useCallback, useEffect, useMemo, useRef, useState } from "react";
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";
6
+ import { supabase } from "../../../integrations/supabase/client.js";
7
+ import { dbFoutTekst } from "../../../lib/db-error-message.js";
8
+ import { ARTIKEL_GEWIJZIGD } from "../../../lib/artikel-gewijzigd.js";
9
+ import { ARTIKEL_VELDEN, VELD_LABEL, VOORRAAD_LABEL, VOORRAAD_VELDEN, aantalUitInvoer, gewijzigdeVoorraad, alsLijstRij, artikelStatus, artikelTabs, floridayActie, gewijzigdeVelden, prijsUitInvoer, } from "../../../lib/artikel-pagina.js";
10
+ import { STATUS_INFO, aantal, geld } from "../../../lib/voorraad-lijst.js";
11
+ import { AanpassenDialog, AfboekenDialog } from "../lijst/SnelActieDialogen.js";
12
+ import { TwijfelKiezen } from "./TwijfelKiezen.js";
13
+ import { OpslaanContext } from "./opslaan-context.js";
14
+ // Tabbladinhoud laadt pas als je het tabblad opent.
15
+ const StockMutationHistoryCard = lazy(() => import("../StockMutationHistoryCard.js").then((m) => ({ default: m.StockMutationHistoryCard })));
16
+ const OrderHistoryCard = lazy(() => import("../OrderHistoryCard.js").then((m) => ({ default: m.OrderHistoryCard })));
17
+ const ProductStructureCard = lazy(() => import("../ProductStructureCard.js").then((m) => ({ default: m.ProductStructureCard })));
18
+ const ArtikelCompositionCard = lazy(() => import("../ArtikelCompositionCard.js").then((m) => ({ default: m.ArtikelCompositionCard })));
19
+ const TrayStickerCard = lazy(() => import("../TrayStickerCard.js").then((m) => ({ default: m.TrayStickerCard })));
20
+ const ProductCustomFieldsCard = lazy(() => import("../ProductCustomFieldsCard.js").then((m) => ({ default: m.ProductCustomFieldsCard })));
21
+ const ProductLinkCard = lazy(() => import("../ProductLinkCard.js").then((m) => ({ default: m.ProductLinkCard })));
22
+ const FloridayInfoCard = lazy(() => import("../FloridayInfoCard.js").then((m) => ({ default: m.FloridayInfoCard })));
23
+ const ArtikelWizard = lazy(() => import("../ArtikelWizard.js").then((m) => ({ default: m.ArtikelWizard })));
24
+ const TAB_LABEL = {
25
+ voorraad: "Voorraad",
26
+ verpakking: "Verpakking en samenstelling",
27
+ koppelingen: "Koppelingen",
28
+ floriday: "Floriday",
29
+ };
30
+ 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"] }));
32
+ }
33
+ function toon(v) {
34
+ if (v === null || v === undefined || v === "")
35
+ return "(leeg)";
36
+ return String(v);
37
+ }
38
+ function gelijk(a, b) {
39
+ const n = (v) => (v === null || v === undefined ? "" : String(v).trim());
40
+ return n(a) === n(b);
41
+ }
42
+ export function ArtikelPagina({ id, resumeJobId, onNietGevonden }) {
43
+ const [detail, setDetail] = useState(null);
44
+ const [fout, setFout] = useState(null);
45
+ const [bijOpenen, setBijOpenen] = useState(null);
46
+ const [invoer, setInvoer] = useState({});
47
+ const [voorraadBij, setVoorraadBij] = useState(null);
48
+ const [opslaan, setOpslaan] = useState(false);
49
+ const [conflicten, setConflicten] = useState(null);
50
+ const [tab, setTab] = useState(resumeJobId ? "floriday" : "voorraad");
51
+ const [floridayOpen, setFloridayOpen] = useState(Boolean(resumeJobId));
52
+ const [aanpassen, setAanpassen] = useState(false);
53
+ const [afboeken, setAfboeken] = useState(false);
54
+ const [archief, setArchief] = useState(null);
55
+ const [archiefKeuze, setArchiefKeuze] = useState("hier");
56
+ const [bezig, setBezig] = useState(false);
57
+ const geladen = useRef(false);
58
+ const invoerRef = useRef({});
59
+ // Onderdelen (eigen velden, traysticker) die meedoen met de ene knop "Opslaan".
60
+ const [deelnemers, setDeelnemers] = useState({});
61
+ const register = useMemo(() => ({
62
+ meld: (k, d) => setDeelnemers((m) => {
63
+ if (!d) {
64
+ if (!(k in m))
65
+ return m;
66
+ const { [k]: _weg, ...rest } = m;
67
+ return rest;
68
+ }
69
+ if (m[k]?.vuil === d.vuil && m[k])
70
+ return { ...m, [k]: d };
71
+ return { ...m, [k]: d };
72
+ }),
73
+ }), []);
74
+ // Bezochte tabbladen blijven gemonteerd, zodat niet-opgeslagen invoer niet verloren gaat.
75
+ const [bezocht, setBezocht] = useState(() => new Set([resumeJobId ? "floriday" : "voorraad"]));
76
+ useEffect(() => { setBezocht((b) => (b.has(tab) ? b : new Set(b).add(tab))); }, [tab]);
77
+ const laad = useCallback(async () => {
78
+ const { data, error } = await supabase.rpc("voorraad_artikel_detail", { p_id: id });
79
+ if (error) {
80
+ setFout(dbFoutTekst(error));
81
+ return;
82
+ }
83
+ const d = data;
84
+ if (!d?.gevonden) {
85
+ if (onNietGevonden)
86
+ onNietGevonden();
87
+ else
88
+ setFout("Dit artikel bestaat niet (meer) in de voorraad.");
89
+ return;
90
+ }
91
+ setDetail(d);
92
+ // De stand bij openen wordt maar één keer vastgelegd (en opnieuw na opslaan).
93
+ if (!geladen.current) {
94
+ geladen.current = true;
95
+ setBijOpenen(d.velden);
96
+ const vb = { minimum_stock: d.minimum, incoming_stock: d.inkomend, notes: d.notitie ?? null };
97
+ setVoorraadBij(vb);
98
+ setInvoer({
99
+ ...Object.fromEntries(ARTIKEL_VELDEN.map((k) => [k, d.velden[k] === null ? "" : String(d.velden[k])])),
100
+ ...Object.fromEntries(VOORRAAD_VELDEN.map((k) => [k, vb[k] === null ? "" : String(vb[k])])),
101
+ });
102
+ }
103
+ else {
104
+ // Velden die je niet zelf aan het wijzigen bent, volgen de nieuwe stand (ook na opslaan in een ander tabblad).
105
+ const inv = invoerRef.current;
106
+ const vb = { minimum_stock: d.minimum, incoming_stock: d.inkomend, notes: d.notitie ?? null };
107
+ const volg = {};
108
+ setBijOpenen((b) => {
109
+ if (!b)
110
+ return b;
111
+ const n = { ...b };
112
+ for (const k of ARTIKEL_VELDEN)
113
+ if (gelijk(inv[k], b[k]) && !gelijk(b[k], d.velden[k])) {
114
+ n[k] = d.velden[k];
115
+ volg[k] = d.velden[k] === null ? "" : String(d.velden[k]);
116
+ }
117
+ return n;
118
+ });
119
+ setVoorraadBij((b) => {
120
+ if (!b)
121
+ return b;
122
+ const n = { ...b };
123
+ for (const k of VOORRAAD_VELDEN)
124
+ if (gelijk(inv[k], b[k]) && !gelijk(b[k], vb[k])) {
125
+ n[k] = vb[k];
126
+ volg[k] = vb[k] === null ? "" : String(vb[k]);
127
+ }
128
+ return n;
129
+ });
130
+ setInvoer((s) => ({ ...s, ...volg }));
131
+ }
132
+ }, [id, onNietGevonden]);
133
+ useEffect(() => { invoerRef.current = invoer; }, [invoer]);
134
+ useEffect(() => { geladen.current = false; void laad(); }, [id]); // eslint-disable-line react-hooks/exhaustive-deps
135
+ // Opslag in een ander tabblad van deze pagina (prijs in het Floriday-formulier, samenstelling): kop direct bijwerken.
136
+ useEffect(() => {
137
+ const h = (e) => { if (e.detail?.id === id)
138
+ void laad(); };
139
+ window.addEventListener(ARTIKEL_GEWIJZIGD, h);
140
+ return () => window.removeEventListener(ARTIKEL_GEWIJZIGD, h);
141
+ }, [id, laad]);
142
+ // Live bijwerken alleen voor dit artikel (voorraadregel en artikelrij), gebundeld.
143
+ const heeftDetail = Boolean(detail);
144
+ const voorraadSleutel = detail?.voorraad_sleutel ?? null;
145
+ useEffect(() => {
146
+ if (!heeftDetail)
147
+ return;
148
+ let t = null;
149
+ const later = () => { if (t)
150
+ clearTimeout(t); t = setTimeout(() => void laad(), 1500); };
151
+ let ch = supabase.channel(`artikel-${id}`)
152
+ .on("postgres_changes", { event: "*", schema: "public", table: "products", filter: `id=eq.${id}` }, later);
153
+ if (voorraadSleutel) {
154
+ ch = ch.on("postgres_changes", { event: "*", schema: "public", table: "trade_item_stock", filter: `trade_item_key=eq.${voorraadSleutel}` }, later);
155
+ }
156
+ ch.subscribe();
157
+ return () => { if (t)
158
+ clearTimeout(t); supabase.removeChannel(ch); };
159
+ }, [id, voorraadSleutel, laad, heeftDetail]);
160
+ const nieuw = useMemo(() => {
161
+ if (!bijOpenen)
162
+ return null;
163
+ const p = prijsUitInvoer(invoer.sale_price ?? "");
164
+ return {
165
+ product: invoer.product ?? null,
166
+ article_number: invoer.article_number ?? null,
167
+ sale_price: p,
168
+ unit: invoer.unit ?? null,
169
+ barcode: invoer.barcode ?? null,
170
+ };
171
+ }, [invoer, bijOpenen]);
172
+ const wijzigingen = bijOpenen && nieuw ? gewijzigdeVelden(bijOpenen, nieuw) : {};
173
+ const voorraadNieuw = {
174
+ minimum_stock: aantalUitInvoer(invoer.minimum_stock ?? ""),
175
+ incoming_stock: aantalUitInvoer(invoer.incoming_stock ?? ""),
176
+ notes: invoer.notes ?? null,
177
+ };
178
+ const voorraadWijz = voorraadBij ? gewijzigdeVoorraad(voorraadBij, voorraadNieuw) : {};
179
+ const vuileDeelnemers = Object.values(deelnemers).filter((d) => d.vuil);
180
+ const vuil = Object.keys(wijzigingen).length > 0 || Object.keys(voorraadWijz).length > 0 || vuileDeelnemers.length > 0;
181
+ const prijsFout = nieuw && Number.isNaN(nieuw.sale_price) ? "Vul een geldige prijs in, bijv. 1,25." : null;
182
+ const aantalFout = Number.isNaN(voorraadNieuw.minimum_stock) || Number.isNaN(voorraadNieuw.incoming_stock)
183
+ ? "Minimum en inkomend: vul een heel aantal van 0 of meer in." : null;
184
+ // Waarschuwing bij verlaten met niet-opgeslagen wijzigingen.
185
+ useBlocker({ shouldBlockFn: () => vuil && !window.confirm("Je hebt niet-opgeslagen wijzigingen. Pagina toch verlaten?"), enableBeforeUnload: () => vuil });
186
+ async function doeOpslaan() {
187
+ if (!bijOpenen || !voorraadBij || !vuil || prijsFout || aantalFout)
188
+ return;
189
+ setOpslaan(true);
190
+ const alleConflicten = [];
191
+ let fout = null;
192
+ let opgeslagen = false;
193
+ if (Object.keys(wijzigingen).length) {
194
+ const oud = {};
195
+ for (const k of Object.keys(wijzigingen))
196
+ oud[k] = bijOpenen[k];
197
+ const { data, error } = await supabase.rpc("voorraad_artikel_opslaan", { p_id: id, p_oud: oud, p_nieuw: wijzigingen });
198
+ if (error)
199
+ fout = dbFoutTekst(error);
200
+ else {
201
+ const r = data;
202
+ if (r.ok) {
203
+ opgeslagen = true;
204
+ setBijOpenen({ ...bijOpenen, ...wijzigingen });
205
+ }
206
+ else
207
+ alleConflicten.push(...(r.conflicten ?? []));
208
+ }
209
+ }
210
+ if (!fout && Object.keys(voorraadWijz).length) {
211
+ const oud = {};
212
+ for (const k of Object.keys(voorraadWijz))
213
+ oud[k] = voorraadBij[k];
214
+ const { data, error } = await supabase.rpc("voorraad_voorraad_instellingen_opslaan", { p_product_id: id, p_oud: oud, p_nieuw: voorraadWijz });
215
+ if (error)
216
+ fout = dbFoutTekst(error);
217
+ else {
218
+ const r = data;
219
+ if (r.ok) {
220
+ opgeslagen = true;
221
+ setVoorraadBij({ ...voorraadBij, ...voorraadWijz });
222
+ }
223
+ else
224
+ alleConflicten.push(...(r.conflicten ?? []));
225
+ }
226
+ }
227
+ if (!fout) {
228
+ for (const d of vuileDeelnemers) {
229
+ const r = await d.opslaan();
230
+ if (r.ok)
231
+ opgeslagen = true;
232
+ else if (r.melding)
233
+ toast.error(r.melding);
234
+ }
235
+ }
236
+ setOpslaan(false);
237
+ if (fout)
238
+ toast.error(fout);
239
+ if (alleConflicten.length)
240
+ setConflicten(alleConflicten);
241
+ if (opgeslagen)
242
+ toast.success(alleConflicten.length ? "Deels opgeslagen; kies bij de conflicten." : "Opgeslagen (alleen lokaal).");
243
+ await laad();
244
+ }
245
+ /** Conflict: neem hun waarde over voor dat veld en leg de nieuwe stand vast. */
246
+ function neemHunOver(c) {
247
+ setInvoer((s) => ({ ...s, [c.veld]: c.hun === null ? "" : String(c.hun) }));
248
+ zetBasis(c);
249
+ }
250
+ function zetBasis(c) {
251
+ if (VOORRAAD_VELDEN.includes(c.veld))
252
+ setVoorraadBij((b) => (b ? { ...b, [c.veld]: c.hun } : b));
253
+ else
254
+ setBijOpenen((b) => (b ? { ...b, [c.veld]: c.hun } : b));
255
+ setConflicten((l) => (l ?? []).filter((x) => x.veld !== c.veld));
256
+ }
257
+ /** Conflict: houd jouw waarde; de stand bij openen wordt hun waarde, zodat opnieuw opslaan bewust overschrijft. */
258
+ function houdJouw(c) {
259
+ zetBasis(c);
260
+ }
261
+ async function archiveren(bevestigd) {
262
+ setBezig(true);
263
+ const { data, error } = await supabase.rpc("voorraad_artikel_archiveren", { p_id: id, p_archiveren: true, p_bevestigd: bevestigd, p_ook_floriday: archiefKeuze === "floriday" });
264
+ setBezig(false);
265
+ if (error) {
266
+ toast.error(dbFoutTekst(error));
267
+ return;
268
+ }
269
+ const r = data;
270
+ if (!r.ok && r.waarschuwing) {
271
+ setArchief({ waarschuwing: { gereserveerd: r.gereserveerd, open_orders: r.open_orders } });
272
+ return;
273
+ }
274
+ 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.");
276
+ await laad();
277
+ }
278
+ async function terugzetten() {
279
+ const { error } = await supabase.rpc("voorraad_artikel_archiveren", { p_id: id, p_archiveren: false });
280
+ if (error) {
281
+ toast.error(dbFoutTekst(error));
282
+ return;
283
+ }
284
+ toast.success("Artikel staat weer in de voorraad.");
285
+ await laad();
286
+ }
287
+ if (fout) {
288
+ return _jsx("main", { className: "p-6", children: _jsx(Card, { className: "p-6 text-sm", children: fout }) });
289
+ }
290
+ if (!detail || !nieuw) {
291
+ return _jsx("main", { className: "p-6", children: _jsx(Card, { className: "p-6", children: _jsx(Laden, {}) }) });
292
+ }
293
+ const status = artikelStatus({ ...detail, velden: nieuw });
294
+ const info = STATUS_INFO[status];
295
+ const actie = floridayActie(detail);
296
+ const tabs = artikelTabs(detail);
297
+ if (floridayOpen && !tabs.includes("floriday") && actie === "op_floriday")
298
+ tabs.push("floriday");
299
+ 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
309
+ ? "Floriday blijft ongewijzigd: het artikel blijft op Floriday te koop."
310
+ : "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."
312
+ : "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
+ }
@@ -0,0 +1,7 @@
1
+ /** Twijfelgevallen van de automatische koppeling, met "Dit artikel kiezen". Geen Floriday-schrijfactie. */
2
+ export declare function TwijfelKiezen({ productId, floridayId, onGekozen }: {
3
+ productId?: string;
4
+ floridayId?: string;
5
+ onGekozen?: () => void;
6
+ }): import("react").JSX.Element | null;
7
+ //# sourceMappingURL=TwijfelKiezen.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TwijfelKiezen.d.ts","sourceRoot":"","sources":["../../../../src/components/voorraad/artikel/TwijfelKiezen.tsx"],"names":[],"mappings":"AASA,2GAA2G;AAC3G,wBAAgB,aAAa,CAAC,EAAE,SAAS,EAAE,UAAU,EAAE,SAAS,EAAE,EAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,IAAI,CAAA;CAAE,sCAiEtI"}
@@ -0,0 +1,54 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useCallback, useEffect, useState } from "react";
3
+ import { Button, Card, toast } from "@flowselections/core";
4
+ import { Loader2 } from "lucide-react";
5
+ import { supabase } from "../../../integrations/supabase/client.js";
6
+ import { dbFoutTekst } from "../../../lib/db-error-message.js";
7
+ /** Twijfelgevallen van de automatische koppeling, met "Dit artikel kiezen". Geen Floriday-schrijfactie. */
8
+ export function TwijfelKiezen({ productId, floridayId, onGekozen }) {
9
+ const [lijst, setLijst] = useState(null);
10
+ const [bezig, setBezig] = useState(null);
11
+ const laad = useCallback(async () => {
12
+ const { data, error } = await supabase.rpc("voorraad_twijfel_lijst", { p_product_id: productId ?? null, p_floriday_id: floridayId ?? null });
13
+ if (error) {
14
+ toast.error(dbFoutTekst(error));
15
+ setLijst([]);
16
+ return;
17
+ }
18
+ setLijst(data ?? []);
19
+ }, [productId, floridayId]);
20
+ useEffect(() => { void laad(); }, [laad]);
21
+ async function kies(t, k) {
22
+ if (!window.confirm(`Floriday-product "${t.naam ?? t.floriday_id}" koppelen aan "${k.naam}"? Er gaat niets naar Floriday.`))
23
+ return;
24
+ setBezig(k.id);
25
+ const { error } = await supabase.rpc("voorraad_twijfel_kiezen", { p_floriday_id: t.floriday_id, p_product_id: k.id });
26
+ setBezig(null);
27
+ if (error) {
28
+ toast.error(dbFoutTekst(error));
29
+ return;
30
+ }
31
+ toast.success(`Gekoppeld aan ${k.naam}.`);
32
+ await laad();
33
+ onGekozen?.();
34
+ }
35
+ async function nieuw(t) {
36
+ if (!window.confirm(`Een nieuw eigen artikel maken uit Floriday-product "${t.naam ?? t.floriday_id}"? Er gaat niets naar Floriday.`))
37
+ return;
38
+ setBezig(`nieuw:${t.floriday_id}`);
39
+ const { error } = await supabase.rpc("voorraad_twijfel_nieuw_artikel", { p_floriday_id: t.floriday_id });
40
+ setBezig(null);
41
+ if (error) {
42
+ toast.error(dbFoutTekst(error));
43
+ return;
44
+ }
45
+ toast.success("Nieuw artikel aangemaakt.");
46
+ await laad();
47
+ onGekozen?.();
48
+ }
49
+ if (lijst === null)
50
+ return _jsxs("div", { className: "flex items-center gap-2 p-2 text-sm text-muted-foreground", children: [_jsx(Loader2, { className: "h-4 w-4 animate-spin" }), " Laden\u2026"] });
51
+ if (lijst.length === 0)
52
+ return null;
53
+ return (_jsxs(Card, { className: "p-4 space-y-3", children: [_jsx("div", { className: "font-medium", children: "Twijfel bij automatisch koppelen" }), lijst.map((t) => (_jsxs("div", { className: "rounded-md border p-3 text-sm space-y-2", children: [_jsxs("div", { children: [_jsx("strong", { children: t.naam ?? "Floriday-product" }), " \u00B7 ", _jsx("span", { className: "text-muted-foreground", children: t.reden })] }), _jsx("ul", { className: "space-y-1", children: t.kandidaten.map((k) => (_jsxs("li", { className: "flex flex-wrap items-center justify-between gap-2", children: [_jsxs("span", { children: [k.naam, k.artikelnummer && _jsxs("span", { className: "text-muted-foreground", children: [" \u00B7 ", k.artikelnummer] }), k.floriday_id && _jsx("span", { className: "text-muted-foreground", children: " \u00B7 al gekoppeld" }), k.gearchiveerd && _jsx("span", { className: "text-muted-foreground", children: " \u00B7 gearchiveerd" })] }), _jsxs(Button, { size: "sm", variant: "outline", disabled: bezig !== null || Boolean(k.floriday_id), onClick: () => kies(t, k), children: [bezig === k.id && _jsx(Loader2, { className: "mr-1 h-3 w-3 animate-spin" }), "Dit artikel kiezen"] })] }, k.id))) }), _jsx("div", { className: "flex justify-end border-t pt-2", children: _jsxs(Button, { size: "sm", variant: "secondary", disabled: bezig !== null, onClick: () => nieuw(t), children: [bezig === `nieuw:${t.floriday_id}` && _jsx(Loader2, { className: "mr-1 h-3 w-3 animate-spin" }), "Nieuw artikel aanmaken"] }) })] }, t.floriday_id)))] }));
54
+ }
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Onderdelen van de artikelpagina die meedoen met de ene knop "Opslaan".
3
+ * Een onderdeel meldt of het niet-opgeslagen wijzigingen heeft en levert een
4
+ * opslagfunctie met eigen conflictcontrole. Er zijn geen eigen opslagknoppen.
5
+ */
6
+ export interface OpslagUitkomst {
7
+ ok: boolean;
8
+ /** Tekst bij een conflict of fout, in gewone taal. */
9
+ melding?: string;
10
+ }
11
+ export interface OpslagDeelnemer {
12
+ vuil: boolean;
13
+ opslaan: () => Promise<OpslagUitkomst>;
14
+ }
15
+ export interface OpslagRegister {
16
+ meld: (sleutel: string, d: OpslagDeelnemer | null) => void;
17
+ }
18
+ export declare const OpslaanContext: import("react").Context<OpslagRegister | null>;
19
+ /** Registreert een onderdeel bij de opslagknop van de pagina (buiten de pagina: geen effect). */
20
+ export declare function useOpslagDeelnemer(sleutel: string, vuil: boolean, opslaan: () => Promise<OpslagUitkomst>): boolean;
21
+ //# sourceMappingURL=opslaan-context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"opslaan-context.d.ts","sourceRoot":"","sources":["../../../../src/components/voorraad/artikel/opslaan-context.tsx"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,OAAO,CAAC;IACZ,sDAAsD;IACtD,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,MAAM,OAAO,CAAC,cAAc,CAAC,CAAC;CACxC;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC,EAAE,eAAe,GAAG,IAAI,KAAK,IAAI,CAAC;CAC5D;AAED,eAAO,MAAM,cAAc,gDAA6C,CAAC;AAEzE,iGAAiG;AACjG,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC,cAAc,CAAC,WAUxG"}
@@ -0,0 +1,15 @@
1
+ import { createContext, useContext, useEffect, useRef } from "react";
2
+ export const OpslaanContext = createContext(null);
3
+ /** Registreert een onderdeel bij de opslagknop van de pagina (buiten de pagina: geen effect). */
4
+ export function useOpslagDeelnemer(sleutel, vuil, opslaan) {
5
+ const reg = useContext(OpslaanContext);
6
+ const fn = useRef(opslaan);
7
+ fn.current = opslaan;
8
+ useEffect(() => {
9
+ if (!reg)
10
+ return;
11
+ reg.meld(sleutel, { vuil, opslaan: () => fn.current() });
12
+ }, [reg, sleutel, vuil]);
13
+ useEffect(() => () => reg?.meld(sleutel, null), [reg, sleutel]);
14
+ return Boolean(reg);
15
+ }
@@ -0,0 +1,5 @@
1
+ export declare function ImporteerKnop({ connectionId, onImported }: {
2
+ connectionId: string | null;
3
+ onImported: () => void;
4
+ }): import("react").JSX.Element;
5
+ //# sourceMappingURL=ImporteerKnop.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ImporteerKnop.d.ts","sourceRoot":"","sources":["../../../../src/components/voorraad/lijst/ImporteerKnop.tsx"],"names":[],"mappings":"AAwBA,wBAAgB,aAAa,CAAC,EAAE,YAAY,EAAE,UAAU,EAAE,EAAE;IAAE,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,UAAU,EAAE,MAAM,IAAI,CAAA;CAAE,+BAmBlH"}
@@ -0,0 +1,20 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useState } from "react";
3
+ import { Button, Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from "@flowselections/core";
4
+ import { Upload } from "lucide-react";
5
+ import { useVoorraadData } from "../../../hooks/useVoorraadData.js";
6
+ import { StockBulkButtons } from "../StockBulkButtons.js";
7
+ /** Artikelgegevens voor importeren worden pas geladen als je op "Importeren" klikt. */
8
+ function ImportInhoud({ connectionId, onImported }) {
9
+ const { items, loading, reload } = useVoorraadData(connectionId);
10
+ if (loading && items.length === 0)
11
+ return _jsx("p", { className: "text-sm text-muted-foreground", children: "Artikelen laden\u2026" });
12
+ return (_jsx("div", { className: "flex flex-wrap gap-2", children: _jsx(StockBulkButtons, { items: items, activeConnectionId: connectionId, onImported: async () => {
13
+ await reload();
14
+ onImported();
15
+ } }) }));
16
+ }
17
+ export function ImporteerKnop({ connectionId, onImported }) {
18
+ const [open, setOpen] = useState(false);
19
+ return (_jsxs(_Fragment, { children: [_jsxs(Button, { variant: "outline", size: "sm", onClick: () => setOpen(true), children: [_jsx(Upload, { className: "mr-2 h-4 w-4" }), "Importeren"] }), _jsx(Dialog, { open: open, onOpenChange: setOpen, children: _jsxs(DialogContent, { children: [_jsxs(DialogHeader, { children: [_jsx(DialogTitle, { children: "Importeren" }), _jsx(DialogDescription, { children: "Download het sjabloon of kies een bestand. Je krijgt eerst een proefcontrole." })] }), open && _jsx(ImportInhoud, { connectionId: connectionId, onImported: onImported })] }) })] }));
20
+ }
@@ -0,0 +1,6 @@
1
+ import { type LijstVraag } from "../../../hooks/useVoorraadLijst.js";
2
+ /** Excel-export van het huidige filter en de zoekopdracht, in porties van de server (geen limiet). */
3
+ export declare function LijstExportKnop({ vraag }: {
4
+ vraag: Omit<LijstVraag, "pagina" | "grootte">;
5
+ }): import("react").JSX.Element;
6
+ //# sourceMappingURL=LijstExportKnop.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LijstExportKnop.d.ts","sourceRoot":"","sources":["../../../../src/components/voorraad/lijst/LijstExportKnop.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAqB,KAAK,UAAU,EAAE,MAAM,iCAAiC,CAAC;AAGrF,sGAAsG;AACtG,wBAAgB,eAAe,CAAC,EAAE,KAAK,EAAE,EAAE;IAAE,KAAK,EAAE,IAAI,CAAC,UAAU,EAAE,QAAQ,GAAG,SAAS,CAAC,CAAA;CAAE,+BAuC3F"}
@@ -0,0 +1,43 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useState } from "react";
3
+ import * as XLSX from "xlsx";
4
+ import { Button, toast } from "@flowselections/core";
5
+ import { Download } from "lucide-react";
6
+ import { haalVoorraadLijst } from "../../../hooks/useVoorraadLijst.js";
7
+ import { allePorties } from "../../../lib/voorraad-lijst.js";
8
+ /** Excel-export van het huidige filter en de zoekopdracht, in porties van de server (geen limiet). */
9
+ export function LijstExportKnop({ vraag }) {
10
+ const [bezig, setBezig] = useState(null);
11
+ const exporteer = async () => {
12
+ setBezig("Bezig…");
13
+ try {
14
+ const rijen = await allePorties(async (pagina, grootte) => {
15
+ const a = await haalVoorraadLijst({ ...vraag, pagina, grootte });
16
+ setBezig(`${Math.min(pagina * grootte, a.totaal)} / ${a.totaal}`);
17
+ return a;
18
+ });
19
+ const ws = XLSX.utils.json_to_sheet(rijen.map((r) => ({
20
+ Artikelnummer: r.artikelnummer ?? "",
21
+ Artikel: r.naam,
22
+ Liggend: r.liggend,
23
+ Minimum: r.minimum,
24
+ Gereserveerd: r.gereserveerd,
25
+ "Vrij beschikbaar": r.vrij,
26
+ Inkomend: r.inkomend,
27
+ "Verkoopprijs per stuk": r.prijs ?? "",
28
+ "Gekoppeld aan Floriday": r.floriday_id ? "ja" : "nee",
29
+ })));
30
+ const wb = XLSX.utils.book_new();
31
+ XLSX.utils.book_append_sheet(wb, ws, "Voorraad");
32
+ XLSX.writeFile(wb, `voorraad-${new Date().toISOString().slice(0, 10)}.xlsx`);
33
+ toast.success(`${rijen.length} artikelen geëxporteerd`);
34
+ }
35
+ catch (e) {
36
+ toast.error(`Exporteren mislukt: ${e.message}`);
37
+ }
38
+ finally {
39
+ setBezig(null);
40
+ }
41
+ };
42
+ return (_jsxs(Button, { variant: "outline", size: "sm", onClick: exporteer, disabled: !!bezig, children: [_jsx(Download, { className: "mr-2 h-4 w-4" }), bezig ?? "Exporteren"] }));
43
+ }
@@ -0,0 +1,16 @@
1
+ import { type LijstRij } from "../../../lib/voorraad-lijst.js";
2
+ /** Controle vóór "Voorraad aanpassen": aantal ≠ 0, reden verplicht, niet onder 0. */
3
+ export declare function aanpassenFout(delta: number, reden: string, liggend: number): string | null;
4
+ /** Directe boeking +/− met reden; los van "Opslaan" zodat een orderafboeking nooit wordt overschreven. */
5
+ export declare function AanpassenDialog({ rij, onClose, onDone }: {
6
+ rij: LijstRij | null;
7
+ onClose: () => void;
8
+ onDone: () => void;
9
+ }): import("react").JSX.Element | null;
10
+ export declare function AfboekenDialog({ rij, connectionId, onClose, onDone, }: {
11
+ rij: LijstRij | null;
12
+ connectionId: string | null;
13
+ onClose: () => void;
14
+ onDone: () => void;
15
+ }): import("react").JSX.Element | null;
16
+ //# sourceMappingURL=SnelActieDialogen.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SnelActieDialogen.d.ts","sourceRoot":"","sources":["../../../../src/components/voorraad/lijst/SnelActieDialogen.tsx"],"names":[],"mappings":"AAsBA,OAAO,EAAU,KAAK,QAAQ,EAAE,MAAM,6BAA6B,CAAC;AAEpE,qFAAqF;AACrF,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAK1F;AAED,0GAA0G;AAC1G,wBAAgB,eAAe,CAAC,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE;IAAE,GAAG,EAAE,QAAQ,GAAG,IAAI,CAAC;IAAC,OAAO,EAAE,MAAM,IAAI,CAAC;IAAC,MAAM,EAAE,MAAM,IAAI,CAAA;CAAE,sCAyD1H;AAED,wBAAgB,cAAc,CAAC,EAC7B,GAAG,EACH,YAAY,EACZ,OAAO,EACP,MAAM,GACP,EAAE;IACD,GAAG,EAAE,QAAQ,GAAG,IAAI,CAAC;IACrB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,MAAM,EAAE,MAAM,IAAI,CAAC;CACpB,sCAwEA"}