@flowselections/floriday-voorraad 1.0.149 → 1.0.152

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
@@ -0,0 +1,5 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /** Werkbalk: zoekbalk links, knoppen (Filters, Kolommen) rechts; chips op de regel eronder. */
3
+ export function Toolbar({ children, right, chips }) {
4
+ return (_jsxs("div", { className: "flex flex-col gap-2", children: [_jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsx("div", { className: "min-w-0 flex-1 basis-60", children: children }), right && _jsx("div", { className: "flex flex-wrap items-center gap-2", children: right })] }), chips] }));
5
+ }
@@ -0,0 +1,6 @@
1
+ /** "Bijgewerkt hh:mm" in 12 px grijs; "Laden…" zolang er nog geen tijd is. */
2
+ export declare function UpdatedAt({ at, prefix }: {
3
+ at: string | Date | null | undefined;
4
+ prefix?: string;
5
+ }): import("react").JSX.Element | null;
6
+ //# sourceMappingURL=UpdatedAt.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"UpdatedAt.d.ts","sourceRoot":"","sources":["../../../src/components/fs-ui/UpdatedAt.tsx"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,wBAAgB,SAAS,CAAC,EAAE,EAAE,EAAE,MAAqB,EAAE,EAAE;IAAE,EAAE,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,GAAG,SAAS,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,sCASjH"}
@@ -0,0 +1,10 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /** "Bijgewerkt hh:mm" in 12 px grijs; "Laden…" zolang er nog geen tijd is. */
3
+ export function UpdatedAt({ at, prefix = "Bijgewerkt" }) {
4
+ if (!at)
5
+ return _jsx("span", { className: "tabular-nums", children: "Laden\u2026" });
6
+ const d = typeof at === "string" ? new Date(at) : at;
7
+ if (Number.isNaN(d.getTime()))
8
+ return null;
9
+ return (_jsxs("span", { className: "tabular-nums", children: [prefix, " ", d.toLocaleTimeString("nl-NL", { hour: "2-digit", minute: "2-digit" })] }));
10
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"ArtikelCompositionCard.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/ArtikelCompositionCard.tsx"],"names":[],"mappings":"AAMA,UAAU,KAAK;IACb,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAkHD,wBAAgB,sBAAsB,CAAC,EAAE,SAAS,EAAE,UAAU,EAAE,EAAE,KAAK,sCA6RtE"}
1
+ {"version":3,"file":"ArtikelCompositionCard.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/ArtikelCompositionCard.tsx"],"names":[],"mappings":"AAOA,UAAU,KAAK;IACb,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAkHD,wBAAgB,sBAAsB,CAAC,EAAE,SAAS,EAAE,UAAU,EAAE,EAAE,KAAK,sCA0TtE"}
@@ -1,6 +1,7 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
- import { useCallback, useEffect, useState } from "react";
3
- import { toast } from "sonner";
2
+ import { useCallback, useEffect, useRef, useState } from "react";
3
+ import { useOpslagDeelnemer } from "./artikel/opslaan-context.js";
4
+ import { dbFoutTekst } from "../../lib/db-error-message.js";
4
5
  import { Card, CardContent, CardDescription, CardHeader, CardTitle, Badge } from "@flowselections/core";
5
6
  import { ArrowDown, ArrowUp, Loader2, Package } from "lucide-react";
6
7
  import { supabase } from "../../integrations/supabase/client.js";
@@ -89,7 +90,12 @@ export function ArtikelCompositionCard({ productId, floridayId }) {
89
90
  const [loading, setLoading] = useState(true);
90
91
  const [sections, setSections] = useState([]);
91
92
  const [reloadTick, setReloadTick] = useState(0);
92
- const [savingOrder, setSavingOrder] = useState(false);
93
+ const [savingOrder] = useState(false);
94
+ // Volgorde van de groepsleden bij openen (D1): pas bij "Opslaan" bewaard, met controle tegen deze stand.
95
+ const [volgordeBij, setVolgordeBij] = useState(null);
96
+ const volgordeBijRef = useRef(null);
97
+ volgordeBijRef.current = volgordeBij;
98
+ const lokaleVolgorde = useRef(null);
93
99
  useEffect(() => {
94
100
  let cancelled = false;
95
101
  (async () => {
@@ -218,6 +224,20 @@ export function ArtikelCompositionCard({ productId, floridayId }) {
218
224
  });
219
225
  }
220
226
  if (!cancelled) {
227
+ const grp = out.find((x) => x.kind === "productgroep");
228
+ const nieuwIds = grp ? grp.rows.map((r) => r.key) : null;
229
+ const lokaal = lokaleVolgorde.current;
230
+ const vuilNu = lokaal && volgordeBijRef.current && lokaal.join("|") !== volgordeBijRef.current.join("|");
231
+ if (grp && vuilNu) {
232
+ // Niet-opgeslagen volgorde houden; nieuwe leden achteraan.
233
+ const pos = new Map(lokaal.map((id, i) => [id, i]));
234
+ grp.rows = [...grp.rows].sort((a, b) => (pos.get(a.key) ?? 1e9) - (pos.get(b.key) ?? 1e9));
235
+ lokaleVolgorde.current = grp.rows.map((r) => r.key);
236
+ }
237
+ else {
238
+ setVolgordeBij(nieuwIds);
239
+ lokaleVolgorde.current = nieuwIds;
240
+ }
221
241
  setSections(out);
222
242
  setLoading(false);
223
243
  }
@@ -238,32 +258,43 @@ export function ArtikelCompositionCard({ productId, floridayId }) {
238
258
  supabase.removeChannel(channel);
239
259
  };
240
260
  }, [groupId]);
241
- const move = useCallback(async (index, dir) => {
261
+ const move = useCallback((index, dir) => {
242
262
  const sec = sections.find((x) => x.kind === "productgroep");
243
263
  if (!sec)
244
264
  return;
245
265
  const target = index + dir;
246
266
  if (target < 0 || target >= sec.rows.length)
247
267
  return;
248
- const prev = sections;
249
268
  const rows = [...sec.rows];
250
269
  [rows[index], rows[target]] = [rows[target], rows[index]];
270
+ lokaleVolgorde.current = rows.map((r) => r.key);
251
271
  setSections(sections.map((x) => (x === sec ? { ...x, rows } : x)));
252
- setSavingOrder(true);
253
- const results = await Promise.all(rows.map((r, i) => supabase.from("product_group_items").update({ sort_order: i }).eq("id", r.key)));
254
- setSavingOrder(false);
255
- const failed = results.find((r) => r.error);
256
- if (failed?.error) {
257
- setSections(prev);
258
- toast.error(`Volgorde opslaan mislukt: ${failed.error.message}`);
259
- }
260
272
  }, [sections]);
273
+ const huidigeVolgorde = sections.find((x) => x.kind === "productgroep")?.rows.map((r) => r.key) ?? null;
274
+ const volgordeVuil = Boolean(groupId && volgordeBij && huidigeVolgorde && huidigeVolgorde.join("|") !== volgordeBij.join("|"));
275
+ useOpslagDeelnemer("groepsvolgorde", volgordeVuil, async () => {
276
+ if (!groupId || !volgordeBij || !huidigeVolgorde)
277
+ return { ok: true };
278
+ const { data, error } = await supabase.rpc("voorraad_groep_volgorde_opslaan", { p_group_id: groupId, p_oud: volgordeBij, p_nieuw: huidigeVolgorde });
279
+ if (error)
280
+ return { ok: false, melding: dbFoutTekst(error, "Opslaan") };
281
+ const r = data;
282
+ if (!r.ok) {
283
+ // Conflict: de volgorde van de ander overnemen en opnieuw laten kiezen.
284
+ lokaleVolgorde.current = null;
285
+ setReloadTick((t) => t + 1);
286
+ return { ok: false, melding: r.melding ?? "Iemand anders heeft de volgorde intussen gewijzigd. Er is niets opgeslagen." };
287
+ }
288
+ setVolgordeBij(huidigeVolgorde);
289
+ lokaleVolgorde.current = huidigeVolgorde;
290
+ return { ok: true };
291
+ });
261
292
  if (loading) {
262
293
  return (_jsx(Card, { children: _jsxs(CardContent, { className: "flex items-center gap-2 py-4 text-sm text-muted-foreground", children: [_jsx(Loader2, { className: "h-4 w-4 animate-spin" }), " Samenstelling laden\u2026"] }) }));
263
294
  }
264
295
  if (sections.length === 0)
265
296
  return null;
266
297
  return (_jsx(_Fragment, { children: sections.map((s) => (_jsxs(Card, { children: [_jsxs(CardHeader, { children: [_jsxs("div", { className: "flex items-center gap-2 flex-wrap", children: [_jsx(CardTitle, { children: s.title }), _jsx(Badge, { className: s.kind === "productgroep"
267
- ? "bg-indigo-500/15 text-indigo-700 border-indigo-500/30"
268
- : "bg-emerald-500/15 text-emerald-700 border-emerald-500/30", children: s.kind === "productgroep" ? "Productgroep" : "Samenstelling" })] }), _jsx(CardDescription, { children: s.description })] }), _jsx(CardContent, { children: s.rows.length === 0 ? (_jsx("p", { className: "text-sm text-muted-foreground py-4 text-center", children: "Nog geen onderdelen toegevoegd." })) : (_jsx("ul", { className: "divide-y", children: s.rows.map((r, idx) => (_jsxs("li", { className: "flex items-center gap-3 py-2", children: [s.kind === "productgroep" && (_jsxs("div", { className: "flex flex-col shrink-0", children: [_jsx("button", { type: "button", "aria-label": "Omhoog", disabled: idx === 0 || savingOrder, onClick: () => move(idx, -1), className: "p-0.5 text-muted-foreground hover:text-foreground disabled:opacity-30", children: _jsx(ArrowUp, { className: "h-3.5 w-3.5" }) }), _jsx("button", { type: "button", "aria-label": "Omlaag", disabled: idx === s.rows.length - 1 || savingOrder, onClick: () => move(idx, 1), className: "p-0.5 text-muted-foreground hover:text-foreground disabled:opacity-30", children: _jsx(ArrowDown, { className: "h-3.5 w-3.5" }) })] })), _jsx("div", { className: "h-8 w-8 rounded bg-muted flex items-center justify-center shrink-0", children: _jsx(Package, { className: "h-4 w-4 text-muted-foreground" }) }), _jsxs("div", { className: "flex-1 min-w-0", children: [_jsx("div", { className: "font-medium text-sm truncate", children: r.name }), r.code && (_jsx("div", { className: "text-xs text-muted-foreground truncate", children: r.code })), r.detail && (_jsx("div", { className: "text-xs text-muted-foreground truncate", children: r.detail }))] }), s.kind === "productgroep" && (_jsx("div", { className: "text-right shrink-0 w-40", children: r.stock ? (_jsxs(_Fragment, { children: [_jsxs("div", { className: "text-sm font-medium tabular-nums", children: ["Beschikbaar: ", r.stock.available] }), _jsxs("div", { className: "text-xs text-muted-foreground tabular-nums", children: ["fysiek ", r.stock.physical, " \u00B7 gereserveerd ", r.stock.reserved] })] })) : (_jsx("div", { className: "text-xs text-muted-foreground", children: "Nog geen voorraad" })) })), r.quantity !== undefined && r.quantity !== null && (_jsxs("div", { className: "text-sm tabular-nums text-muted-foreground shrink-0", title: s.kind === "productgroep" ? "Aantal in groep" : undefined, children: [r.quantity, r.unit ? ` ${r.unit}` : ""] }))] }, r.key))) })) })] }, s.kind))) }));
298
+ ? "rounded-full border-border bg-muted text-muted-foreground"
299
+ : "rounded-full border-primary/30 bg-primary/10 text-primary", children: s.kind === "productgroep" ? "Productgroep" : "Samenstelling" })] }), _jsxs(CardDescription, { children: [s.description, s.kind === "productgroep" && volgordeVuil && _jsx("span", { className: "ml-2 font-medium text-warning-soft-foreground", children: "\u00B7 Volgorde nog niet opgeslagen" })] })] }), _jsx(CardContent, { children: s.rows.length === 0 ? (_jsx("p", { className: "text-sm text-muted-foreground py-4 text-center", children: "Nog geen onderdelen toegevoegd." })) : (_jsx("ul", { className: "divide-y", children: s.rows.map((r, idx) => (_jsxs("li", { className: "flex items-center gap-3 py-2", children: [s.kind === "productgroep" && (_jsxs("div", { className: "flex flex-col shrink-0", children: [_jsx("button", { type: "button", "aria-label": "Omhoog", disabled: idx === 0 || savingOrder, onClick: () => move(idx, -1), className: "p-0.5 text-muted-foreground hover:text-foreground disabled:opacity-30", children: _jsx(ArrowUp, { className: "h-3.5 w-3.5" }) }), _jsx("button", { type: "button", "aria-label": "Omlaag", disabled: idx === s.rows.length - 1 || savingOrder, onClick: () => move(idx, 1), className: "p-0.5 text-muted-foreground hover:text-foreground disabled:opacity-30", children: _jsx(ArrowDown, { className: "h-3.5 w-3.5" }) })] })), _jsx("div", { className: "h-8 w-8 rounded bg-muted flex items-center justify-center shrink-0", children: _jsx(Package, { className: "h-4 w-4 text-muted-foreground" }) }), _jsxs("div", { className: "flex-1 min-w-0", children: [_jsx("div", { className: "font-medium text-sm truncate", children: r.name }), r.code && (_jsx("div", { className: "text-xs text-muted-foreground truncate", children: r.code })), r.detail && (_jsx("div", { className: "text-xs text-muted-foreground truncate", children: r.detail }))] }), s.kind === "productgroep" && (_jsx("div", { className: "text-right shrink-0 w-40", children: r.stock ? (_jsxs(_Fragment, { children: [_jsxs("div", { className: "text-sm font-medium tabular-nums", children: ["Beschikbaar: ", r.stock.available] }), _jsxs("div", { className: "text-xs text-muted-foreground tabular-nums", children: ["fysiek ", r.stock.physical, " \u00B7 gereserveerd ", r.stock.reserved] })] })) : (_jsx("div", { className: "text-xs text-muted-foreground", children: "Nog geen voorraad" })) })), r.quantity !== undefined && r.quantity !== null && (_jsxs("div", { className: "text-sm tabular-nums text-muted-foreground shrink-0", title: s.kind === "productgroep" ? "Aantal in groep" : undefined, children: [r.quantity, r.unit ? ` ${r.unit}` : ""] }))] }, r.key))) })) })] }, s.kind))) }));
269
300
  }
@@ -634,7 +634,7 @@ export function ArtikelWizard({ draftId: initialDraftId, tradeItemId, productId,
634
634
  return;
635
635
  }
636
636
  if (!row) {
637
- toast.error("Artikel niet gevonden in de cache");
637
+ toast.error("Artikel niet gevonden bij de Floriday-artikelen");
638
638
  return;
639
639
  }
640
640
  // Lokale rij met nog niet doorgegeven wijzigingen? Die is leidend.
@@ -916,7 +916,7 @@ export function ArtikelWizard({ draftId: initialDraftId, tradeItemId, productId,
916
916
  if (hasRegulatoryLookup && regulatoryQuery.isLoading) {
917
917
  return {
918
918
  ok: false,
919
- errors: ["Verplichte Floricode-karakteristieken worden nog geladen"],
919
+ errors: ["Verplichte kenmerken worden nog geladen"],
920
920
  };
921
921
  }
922
922
  if (hasRegulatoryLookup && regulatoryQuery.isError) {
@@ -1852,7 +1852,7 @@ export function ArtikelWizard({ draftId: initialDraftId, tradeItemId, productId,
1852
1852
  const payload = buildUpdateTradeItemPayload(snapshot);
1853
1853
  const res = await floriday.updateTradeItem(activeId, pushTargetId, payload);
1854
1854
  if (!res?.attemptId) {
1855
- throw new Error("Gateway gaf geen attemptId terug");
1855
+ throw new Error("Floriday gaf geen bevestiging terug");
1856
1856
  }
1857
1857
  // Geen eigen kopie in floriday_trade_items_cache: Floriday-auth vult die.
1858
1858
  if (localId) {
@@ -2916,7 +2916,7 @@ function PackingConfigDialog({ open, onOpenChange, initial, isFirst, packagingTy
2916
2916
  packagingSupplierId: v,
2917
2917
  packagingSupplierName: sup?.name,
2918
2918
  }));
2919
- }, children: [_jsx(SelectTrigger, { children: _jsx(SelectValue, { placeholder: "Kies leverancier..." }) }), _jsx(SelectContent, { children: PACKAGING_SUPPLIERS.map((s) => (_jsx(SelectItem, { value: s.id, children: s.name }, s.id))) })] }))] }), _jsxs("div", { className: "space-y-2", children: [_jsx(Label, { children: "Floricode VRS-materiaal UUID (optioneel)" }), _jsx(Input, { value: draft.packagingMaterialId ?? "", onChange: (e) => patch("packagingMaterialId", e.target.value.trim()), placeholder: "00000000-0000-0000-0000-000000000000", className: materialInvalid ? "border-destructive" : "" }), materialInvalid ? (_jsx("p", { className: "text-xs text-destructive", children: "Deze code klopt niet. Laat het veld leeg of kopieer de materiaalcode uit Floricode." })) : (_jsxs("p", { className: "text-xs text-muted-foreground", children: ["Wanneer ingevuld gaat het als ", _jsx("code", { children: "customPackageId" }), "naar Floriday en vervangt het de VBN-fust."] }))] })] }), hasCustomPkg && (_jsx("div", { className: "rounded-md border border-primary/30 bg-primary/5 px-3 py-2 text-xs", children: "Eigen verpakking gekozen; het VBN-fust hieronder wordt niet naar Floriday gestuurd." }))] })), _jsxs("div", { className: "grid grid-cols-2 gap-3", children: [_jsxs("div", { className: "space-y-2", children: [_jsx(Label, { children: hasCustomPkg
2919
+ }, children: [_jsx(SelectTrigger, { children: _jsx(SelectValue, { placeholder: "Kies leverancier..." }) }), _jsx(SelectContent, { children: PACKAGING_SUPPLIERS.map((s) => (_jsx(SelectItem, { value: s.id, children: s.name }, s.id))) })] }))] }), _jsxs("div", { className: "space-y-2", children: [_jsx(Label, { children: "Materiaalcode (optioneel)" }), _jsx(Input, { value: draft.packagingMaterialId ?? "", onChange: (e) => patch("packagingMaterialId", e.target.value.trim()), placeholder: "00000000-0000-0000-0000-000000000000", className: materialInvalid ? "border-destructive" : "" }), materialInvalid ? (_jsx("p", { className: "text-xs text-destructive", children: "Deze code klopt niet. Laat het veld leeg of kopieer de materiaalcode uit Floricode." })) : (_jsxs("p", { className: "text-xs text-muted-foreground", children: ["Wanneer ingevuld gaat het als ", _jsx("code", { children: "customPackageId" }), "naar Floriday en vervangt het de VBN-fust."] }))] })] }), hasCustomPkg && (_jsx("div", { className: "rounded-md border border-primary/30 bg-primary/5 px-3 py-2 text-xs", children: "Eigen verpakking gekozen; het VBN-fust hieronder wordt niet naar Floriday gestuurd." }))] })), _jsxs("div", { className: "grid grid-cols-2 gap-3", children: [_jsxs("div", { className: "space-y-2", children: [_jsx(Label, { children: hasCustomPkg
2920
2920
  ? "VBN-fust (genegeerd — custom package actief)"
2921
2921
  : draft.source === "SUPPLIER"
2922
2922
  ? "Fallback VBN-fust"
@@ -1,12 +1,12 @@
1
- import { type ConnectionStatus } from "../../lib/stock-scope.js";
1
+ import type { ConnectionStatus } from "../../lib/stock-scope.js";
2
2
  /**
3
- * Meldt in gewone taal waarom Floriday-acties ontbreken. Geen link: de route
4
- * naar de Floriday-koppeling hoort bij de Shell/Floriday-auth en is hier niet
5
- * aantoonbaar bekend.
3
+ * Werkpagina's tonen geen uitnodiging om een koppeling in te stellen (dat hoort bij Instellingen).
4
+ * Alleen als een bestaande koppeling niet geladen kan worden: één rustige melding.
5
+ * De technische reden gaat naar de console.
6
6
  */
7
- export declare function FloridayConnectionNotice({ status, environment, error, }: {
7
+ export declare function FloridayConnectionNotice({ status, error, }: {
8
8
  status: ConnectionStatus;
9
- environment: string | null;
9
+ environment?: string | null;
10
10
  error: string | null;
11
11
  }): import("react").JSX.Element | null;
12
12
  //# sourceMappingURL=FloridayConnectionNotice.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"FloridayConnectionNotice.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/FloridayConnectionNotice.tsx"],"names":[],"mappings":"AACA,OAAO,EAAoB,KAAK,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAEhF;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,EACvC,MAAM,EACN,WAAW,EACX,KAAK,GACN,EAAE;IACD,MAAM,EAAE,gBAAgB,CAAC;IACzB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB,sCAsBA"}
1
+ {"version":3,"file":"FloridayConnectionNotice.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/FloridayConnectionNotice.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAG9D;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,EACvC,MAAM,EACN,KAAK,GACN,EAAE;IACD,MAAM,EAAE,gBAAgB,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB,sCAQA"}
@@ -1,16 +1,14 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { AlertTriangle } from "lucide-react";
3
- import { noConnectionText } from "../../lib/stock-scope.js";
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { InlineAlert } from "../fs-ui/InlineAlert.js";
4
3
  /**
5
- * Meldt in gewone taal waarom Floriday-acties ontbreken. Geen link: de route
6
- * naar de Floriday-koppeling hoort bij de Shell/Floriday-auth en is hier niet
7
- * aantoonbaar bekend.
4
+ * Werkpagina's tonen geen uitnodiging om een koppeling in te stellen (dat hoort bij Instellingen).
5
+ * Alleen als een bestaande koppeling niet geladen kan worden: één rustige melding.
6
+ * De technische reden gaat naar de console.
8
7
  */
9
- export function FloridayConnectionNotice({ status, environment, error, }) {
10
- if (status === "fout") {
11
- return (_jsxs("div", { role: "alert", className: "flex items-start gap-2 rounded-md border border-destructive/40 bg-destructive/10 p-3 text-sm text-destructive", children: [_jsx(AlertTriangle, { className: "mt-0.5 h-4 w-4 shrink-0" }), _jsxs("div", { children: [_jsx("p", { className: "font-medium", children: "Floriday-koppeling laden mislukt \u2013 alleen eigen artikelen mogelijk." }), _jsxs("p", { className: "mt-1 break-words", children: ["Melding: ", error] })] })] }));
12
- }
13
- if (status !== "geen_koppeling")
8
+ export function FloridayConnectionNotice({ status, error, }) {
9
+ if (status !== "fout")
14
10
  return null;
15
- return (_jsxs("div", { role: "status", className: "flex items-start gap-2 rounded-md border border-amber-500/30 bg-amber-500/10 p-3 text-sm text-amber-800", children: [_jsx(AlertTriangle, { className: "mt-0.5 h-4 w-4 shrink-0" }), _jsxs("div", { children: [_jsx("p", { className: "font-medium", children: noConnectionText(environment) }), _jsx("p", { className: "mt-1", children: "Een Floriday-koppeling kies je bij de Floriday-instellingen van de Shell." })] })] }));
11
+ if (error)
12
+ console.warn("Floriday-koppeling laden mislukt:", error);
13
+ return (_jsx(InlineAlert, { tone: "let_op", children: "De koppeling met Floriday kon niet geladen worden. Je eigen artikelen werken gewoon; probeer het later opnieuw." }));
16
14
  }
@@ -1 +1 @@
1
- {"version":3,"file":"FustenPage.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/FustenPage.tsx"],"names":[],"mappings":"AAmLA,wBAAgB,UAAU,gCA0PzB"}
1
+ {"version":3,"file":"FustenPage.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/FustenPage.tsx"],"names":[],"mappings":"AAqLA,wBAAgB,UAAU,gCAoPzB"}
@@ -7,6 +7,8 @@ import { fustNaam, fustTekorten, saldoBijhouden } from "../../lib/fust-administr
7
7
  import { useFloridayConnection } from "../../hooks/useFloridayConnection.js";
8
8
  import { useFusten } from "../../hooks/useFusten.js";
9
9
  import { fetchFloricodeFustMap, fetchFloridayCustomPackageMap, fetchFustMutations, formatFustDimensions, resolveFustDimensions, } from "../../lib/fusten.js";
10
+ import { PageHeader } from "../fs-ui/PageHeader.js";
11
+ import { InlineAlert } from "../fs-ui/InlineAlert.js";
10
12
  /** Afmetingen tonen en handmatig aanvullen wanneer de catalogus ze niet kent. */
11
13
  function DimensionCell({ fust, floricode, customPackages, onSave, }) {
12
14
  const [editing, setEditing] = useState(false);
@@ -22,7 +24,7 @@ function DimensionCell({ fust, floricode, customPackages, onSave, }) {
22
24
  return Number.isFinite(n) && n > 0 ? n : null;
23
25
  };
24
26
  if (!editing) {
25
- return (_jsxs("div", { children: [_jsx("div", { className: label ? "tabular-nums" : "text-muted-foreground", children: label ?? "Onbekend" }), resolved.source !== "onbekend" && (_jsx("div", { className: "text-xs text-muted-foreground", children: resolved.source === "floricode" ? "Floricode" : resolved.source === "floriday" ? "Floriday eigen fust" : "handmatig" })), _jsx("button", { type: "button", className: "text-xs text-primary hover:underline", onClick: () => {
27
+ return (_jsxs("div", { children: [_jsx("div", { className: label ? "tabular-nums" : "text-muted-foreground", children: label ?? "Onbekend" }), resolved.source !== "onbekend" && (_jsx("div", { className: "text-xs text-muted-foreground", children: resolved.source === "floricode" ? "Standaardmaat" : resolved.source === "floriday" ? "Floriday eigen fust" : "handmatig" })), _jsx("button", { type: "button", className: "text-xs text-primary hover:underline", onClick: () => {
26
28
  setDraft({
27
29
  length_mm: fust.length_mm?.toString() ?? "",
28
30
  width_mm: fust.width_mm?.toString() ?? "",
@@ -114,7 +116,7 @@ export function FustenPage() {
114
116
  setBusyId(null);
115
117
  }
116
118
  };
117
- 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: "Fusten" }), _jsx("p", { className: "text-sm text-muted-foreground mt-1", children: "Alle fusttypen worden automatisch afgeleid uit de beladingen van je producten." })] }), _jsxs(Button, { variant: "outline", size: "sm", onClick: () => void sync(), disabled: syncing, children: [_jsx(RefreshCw, { className: `mr-2 h-4 w-4 ${syncing ? "animate-spin" : ""}` }), "Synchroniseren"] })] }), error && (_jsxs("div", { className: "rounded-md border border-destructive/40 bg-destructive/10 text-destructive p-3 text-sm", children: ["Fusten laden mislukt: ", error] })), floricodeError && (_jsxs("div", { className: "rounded-md border border-amber-500/40 bg-amber-500/10 p-3 text-sm", children: ["Floricode-fustcodes niet leesbaar: ", floricodeError, ". Alleen handmatig ingevoerde maten worden getoond."] })), negative.length > 0 && (_jsxs("div", { className: "rounded-md border border-destructive/40 bg-destructive/10 p-3 text-sm text-destructive space-y-1", children: [_jsxs("p", { className: "font-medium", children: [negative.length, " fusttype", negative.length === 1 ? " heeft" : "n hebben", " een tekort."] }), _jsx("p", { className: "text-foreground/80", children: "Er is sinds het startmoment meer uitgeleverd dan ingeboekt. Alleen fusten waarvan je het saldo bijhoudt staan hier. Controleer ontvangsten en retouren, of boek de werkelijke telling bij." }), _jsx("ul", { className: "text-xs text-foreground/80", children: negative.map((n) => (_jsxs("li", { children: [fustNaam(n), ": tekort van ", Math.abs(n.stock).toLocaleString("nl-NL"), " fusten"] }, n.id))) })] })), _jsx(FustStartmomentCard, { items: items, onSaved: () => void sync() }), _jsxs("div", { className: "grid gap-4 sm:grid-cols-3", children: [_jsxs(Card, { className: "p-4", children: [_jsx("p", { className: "text-sm text-muted-foreground", children: "Fusttypen" }), _jsx("p", { className: "mt-2 text-3xl font-bold tabular-nums", children: totals.types })] }), _jsxs(Card, { className: "p-4", children: [_jsx("p", { className: "text-sm text-muted-foreground", children: "Totale fustvoorraad" }), _jsx("p", { className: "mt-2 text-3xl font-bold tabular-nums", children: totals.stock })] }), _jsxs(Card, { className: "p-4", children: [_jsx("p", { className: "text-sm text-muted-foreground", children: "Onder minimum" }), _jsx("p", { className: "mt-2 text-3xl font-bold tabular-nums", children: totals.belowMin })] })] }), _jsxs("div", { className: "relative max-w-sm", children: [_jsx(Search, { className: "absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" }), _jsx(Input, { value: search, onChange: (e) => setSearch(e.target.value), placeholder: "Zoek fust\u2026", className: "pl-9" })] }), _jsx(Card, { className: "overflow-hidden", children: loading ? (_jsx("p", { className: "p-6 text-sm text-muted-foreground", children: "Fusten laden\u2026" })) : filtered.length === 0 ? (_jsxs("div", { className: "p-6 text-sm text-muted-foreground flex items-center gap-2", children: [_jsx(Boxes, { className: "h-4 w-4" }), "Nog geen fusttypen gevonden. Voeg een belading toe aan een product."] })) : (_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", children: "Fust" }), _jsx("th", { className: "p-3", children: "Bron" }), _jsx("th", { className: "p-3", children: "Afmeting (L \u00D7 B \u00D7 H)" }), _jsx("th", { className: "p-3 text-right", children: "Gebruikt door" }), _jsx("th", { className: "p-3 text-right", children: "Voorraad" }), _jsx("th", { className: "p-3 text-right", children: "Minimum" }), _jsx("th", { className: "p-3 text-right", children: "Aanpassen" })] }) }), _jsx("tbody", { children: filtered.map((f) => (_jsxs("tr", { className: "border-t border-border align-top", children: [_jsxs("td", { className: "p-3", children: [_jsx("div", { className: "font-medium", children: fustNaam(f) }), f.eigen_naam?.trim() && _jsx("div", { className: "text-xs text-muted-foreground", children: f.name }), _jsx("div", { className: "text-xs text-muted-foreground", children: f.vbn_code ? `VBN ${f.vbn_code}` : f.fust_key }), !f.in_use && (_jsx(Badge, { variant: "outline", className: "mt-1", children: "Niet meer in gebruik" })), _jsx(FustInstellingenCell, { fust: f, onSaved: () => void sync() }), _jsx("div", { className: "mt-1", children: _jsx(MutationHistory, { fust: f }) })] }), _jsx("td", { className: "p-3", children: f.source === "SUPPLIER" ? "Leverancier" : "VBN" }), _jsx("td", { className: "p-3 text-sm", children: _jsx(DimensionCell, { fust: f, floricode: floricode, customPackages: customPackages, onSave: (v) => patch(f.id, v) }) }), _jsx("td", { className: "p-3 text-right tabular-nums", children: f.used_by_count }), _jsx("td", { className: "p-3 text-right", children: f.stock < 0 && saldoBijhouden(f) ? (_jsxs("span", { className: "tabular-nums font-semibold text-rose-600", title: "Er is meer uitgeleverd dan ingeboekt. Boek ontvangen of geretourneerde fusten bij.", children: ["Tekort van ", Math.abs(f.stock).toLocaleString("nl-NL"), " fusten"] })) : (_jsx("span", { className: `tabular-nums font-semibold ${f.minimum_stock > 0 && f.stock < f.minimum_stock
119
+ return (_jsxs("main", { className: "flex flex-col gap-4 p-4 pb-24 sm:p-6 sm:pb-24", children: [_jsx(PageHeader, { title: "Fusten", meta: _jsx("span", { children: "Fusttypen komen vanzelf uit de beladingen van je artikelen." }), actions: _jsxs(Button, { variant: "outline", size: "sm", className: "h-9", onClick: () => void sync(), disabled: syncing, children: [_jsx(RefreshCw, { className: `mr-2 h-4 w-4 ${syncing ? "animate-spin motion-reduce:animate-none" : ""}` }), "Bijwerken"] }) }), error && (_jsx(InlineAlert, { tone: "fout", children: "Fusten laden is niet gelukt. Probeer het opnieuw." })), floricodeError && (_jsx(InlineAlert, { tone: "let_op", children: "De standaardmaten van fusten konden niet geladen worden. Alleen zelf ingevoerde maten worden getoond." })), negative.length > 0 && (_jsxs("div", { className: "rounded-md border border-destructive/40 bg-destructive/10 p-3 text-sm text-destructive space-y-1", children: [_jsxs("p", { className: "font-medium", children: [negative.length, " fusttype", negative.length === 1 ? " heeft" : "n hebben", " een tekort."] }), _jsx("p", { className: "text-foreground/80", children: "Er is sinds het startmoment meer uitgeleverd dan ingeboekt. Alleen fusten waarvan je het saldo bijhoudt staan hier. Controleer ontvangsten en retouren, of boek de werkelijke telling bij." }), _jsx("ul", { className: "text-xs text-foreground/80", children: negative.map((n) => (_jsxs("li", { children: [fustNaam(n), ": tekort van ", Math.abs(n.stock).toLocaleString("nl-NL"), " fusten"] }, n.id))) })] })), _jsx(FustStartmomentCard, { items: items, onSaved: () => void sync() }), _jsxs("div", { className: "grid gap-4 sm:grid-cols-3", children: [_jsxs(Card, { className: "p-4", children: [_jsx("p", { className: "text-sm text-muted-foreground", children: "Fusttypen" }), _jsx("p", { className: "mt-2 text-3xl font-bold tabular-nums", children: totals.types })] }), _jsxs(Card, { className: "p-4", children: [_jsx("p", { className: "text-sm text-muted-foreground", children: "Totale fustvoorraad" }), _jsx("p", { className: "mt-2 text-3xl font-bold tabular-nums", children: totals.stock })] }), _jsxs(Card, { className: "p-4", children: [_jsx("p", { className: "text-sm text-muted-foreground", children: "Onder minimum" }), _jsx("p", { className: "mt-2 text-3xl font-bold tabular-nums", children: totals.belowMin })] })] }), _jsxs("div", { className: "relative max-w-sm", children: [_jsx(Search, { className: "absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" }), _jsx(Input, { value: search, onChange: (e) => setSearch(e.target.value), placeholder: "Zoek fust\u2026", className: "pl-9" })] }), _jsx(Card, { className: "overflow-hidden", children: loading ? (_jsx("p", { className: "p-6 text-sm text-muted-foreground", children: "Fusten laden\u2026" })) : filtered.length === 0 ? (_jsxs("div", { className: "p-6 text-sm text-muted-foreground flex items-center gap-2", children: [_jsx(Boxes, { className: "h-4 w-4" }), "Nog geen fusttypen gevonden. Voeg een belading toe aan een product."] })) : (_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", children: "Fust" }), _jsx("th", { className: "p-3", children: "Bron" }), _jsx("th", { className: "p-3", children: "Afmeting (L \u00D7 B \u00D7 H)" }), _jsx("th", { className: "p-3 text-right", children: "Gebruikt door" }), _jsx("th", { className: "p-3 text-right", children: "Voorraad" }), _jsx("th", { className: "p-3 text-right", children: "Minimum" }), _jsx("th", { className: "p-3 text-right", children: "Aanpassen" })] }) }), _jsx("tbody", { children: filtered.map((f) => (_jsxs("tr", { className: "border-t border-border align-top", children: [_jsxs("td", { className: "p-3", children: [_jsx("div", { className: "font-medium", children: fustNaam(f) }), f.eigen_naam?.trim() && _jsx("div", { className: "text-xs text-muted-foreground", children: f.name }), _jsx("div", { className: "text-xs text-muted-foreground", children: f.vbn_code ? `VBN ${f.vbn_code}` : f.fust_key }), !f.in_use && (_jsx(Badge, { variant: "outline", className: "mt-1", children: "Niet meer in gebruik" })), _jsx(FustInstellingenCell, { fust: f, onSaved: () => void sync() }), _jsx("div", { className: "mt-1", children: _jsx(MutationHistory, { fust: f }) })] }), _jsx("td", { className: "p-3", children: f.source === "SUPPLIER" ? "Leverancier" : "VBN" }), _jsx("td", { className: "p-3 text-sm", children: _jsx(DimensionCell, { fust: f, floricode: floricode, customPackages: customPackages, onSave: (v) => patch(f.id, v) }) }), _jsx("td", { className: "p-3 text-right tabular-nums", children: f.used_by_count }), _jsx("td", { className: "p-3 text-right", children: f.stock < 0 && saldoBijhouden(f) ? (_jsxs("span", { className: "tabular-nums font-semibold text-rose-600", title: "Er is meer uitgeleverd dan ingeboekt. Boek ontvangen of geretourneerde fusten bij.", children: ["Tekort van ", Math.abs(f.stock).toLocaleString("nl-NL"), " fusten"] })) : (_jsx("span", { className: `tabular-nums font-semibold ${f.minimum_stock > 0 && f.stock < f.minimum_stock
118
120
  ? "text-rose-600"
119
121
  : ""}`, children: f.stock })) }), _jsx("td", { className: "p-3 text-right", children: _jsx(Input, { type: "number", className: "h-8 w-28 text-right", defaultValue: f.minimum_stock, onBlur: (e) => {
120
122
  const v = Math.max(0, Math.trunc(Number(e.target.value) || 0));
@@ -1 +1 @@
1
- {"version":3,"file":"NieuwArtikelPage.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/NieuwArtikelPage.tsx"],"names":[],"mappings":"AA8BA,wBAAgB,gBAAgB,gCA4Y/B"}
1
+ {"version":3,"file":"NieuwArtikelPage.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/NieuwArtikelPage.tsx"],"names":[],"mappings":"AA+BA,wBAAgB,gBAAgB,gCAoY/B"}
@@ -20,6 +20,7 @@ import { saveOwnPackings } from "../../lib/product-packings.js";
20
20
  import { assertArticleCode } from "../../lib/article-code.js";
21
21
  import { EMPTY_SIMPLE_PACKING, engineOordeelRegel, missingPackingFields, simplePackingToRow, } from "../../lib/simple-packing.js";
22
22
  import { resolveEngineCarrier } from "../../lib/engine-carrier.js";
23
+ import { PageHeader } from "../fs-ui/PageHeader.js";
23
24
  const STEPS = ["Naam en code", "Voorraad", "Prijs", "Belading"];
24
25
  export function NieuwArtikelPage() {
25
26
  const carriers = useLoadCarriers();
@@ -186,5 +187,5 @@ export function NieuwArtikelPage() {
186
187
  ? "rounded-md border p-3 text-sm"
187
188
  : "rounded-md border border-destructive/40 bg-destructive/10 p-3 text-sm text-destructive", children: [_jsxs("p", { className: "font-medium", children: ["Controle karindeling: ", oordeel.tekst] }), oordeel.regels.length > 0 && (_jsx("ul", { className: "list-disc pl-5", children: oordeel.regels.map((r) => (_jsx("li", { children: r }, r))) }))] })), hasFloriday ? (_jsxs(_Fragment, { children: [_jsx("p", { className: "text-sm text-muted-foreground", children: "Wil je het artikel ook op Floriday zetten? Daarvoor is nog nodig:" }), _jsxs("ul", { className: "list-disc pl-5 text-sm", children: [!hasPrice && _jsx("li", { children: "Prijs per stuk (excl. btw)" }), missing.map((m) => (_jsx("li", { children: m }, m)))] })] })) : (_jsx("p", { className: "text-sm text-muted-foreground", children: "De voorraad is opgeslagen als eigen voorraad. Die gaat niet naar Floriday." })), _jsxs("div", { className: "flex flex-wrap gap-2", children: [hasFloriday && (_jsxs(Button, { onClick: () => navigate({ to: "/voorraad/$id", params: { id: createdId } }), children: [_jsx(Send, { className: "mr-2 h-4 w-4" }), "Op Floriday zetten"] })), _jsx(Button, { variant: "outline", asChild: true, children: _jsx(Link, { to: "/voorraad", children: "Terug naar voorraad" }) })] })] }) }));
188
189
  }
189
- return (_jsxs("main", { className: "p-6 space-y-4 max-w-2xl", 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"] }), _jsxs("div", { children: [_jsx("h1", { className: "text-xl font-semibold", children: "Artikel toevoegen" }), _jsxs("p", { className: "text-sm text-muted-foreground", children: ["Stap ", step + 1, " van ", STEPS.length, ": ", STEPS[step]] })] }), _jsx(FloridayConnectionNotice, { status: conn.status, environment: conn.selectedEnvironment, error: conn.error }), _jsxs(Card, { className: "p-5 space-y-4", children: [step === 0 && (_jsxs(_Fragment, { children: [_jsxs("div", { className: "space-y-1", children: [_jsx(Label, { htmlFor: "na-name", children: "Naam" }), _jsx(Input, { id: "na-name", value: name, onChange: (e) => setName(e.target.value), autoFocus: true })] }), _jsxs("div", { className: "space-y-1", children: [_jsx(Label, { htmlFor: "na-code", children: "Artikelcode (optioneel)" }), _jsx(Input, { id: "na-code", value: code, onChange: (e) => setCode(e.target.value) }), _jsx("p", { className: "text-xs text-muted-foreground", children: "Leeg laten: er wordt automatisch een code gemaakt." })] })] })), step === 1 && (_jsxs("div", { className: "space-y-1", children: [_jsx(Label, { htmlFor: "na-stock", children: "Voorraad (stuks)" }), _jsx(Input, { id: "na-stock", type: "number", min: 0, value: canSaveStock ? stock : "", onChange: (e) => setStock(e.target.value), disabled: !canSaveStock, "aria-describedby": "na-stock-help", autoFocus: true }), !canSaveStock && (_jsx("p", { id: "na-stock-help", className: "text-xs text-muted-foreground", children: "Koppeling wordt gecontroleerd\u2026" })), canSaveStock && !hasFloriday && (_jsx("p", { id: "na-stock-help", className: "text-xs text-muted-foreground", children: LOCAL_STOCK_TEXT }))] })), step === 2 && (_jsxs("div", { className: "space-y-1", children: [_jsx(Label, { htmlFor: "na-price", children: "Prijs per stuk (EUR, excl. btw)" }), _jsx(Input, { id: "na-price", inputMode: "decimal", value: price, onChange: (e) => setPrice(e.target.value), placeholder: "Bijv. 1,10", autoFocus: true }), _jsx("p", { className: "text-xs text-muted-foreground", children: "Zonder prijs komt het artikel niet in Floriday (\"Prijs ontbreekt\")." })] })), step === 3 && (_jsxs("div", { className: "space-y-3", children: [_jsx("p", { className: "text-sm text-muted-foreground", children: "Zonder volledige belading kan de karindeling dit artikel niet plannen. Alle vijf velden zijn verplicht." }), _jsxs("div", { className: "space-y-1", children: [_jsx(Label, { htmlFor: "na-fust", children: "Fust (VBN-code)" }), _jsxs("select", { id: "na-fust", className: "w-full rounded-md border bg-background px-3 py-2 text-sm", value: packing.vbnPackageCode, onChange: (e) => setPack("vbnPackageCode", e.target.value), children: [_jsx("option", { value: "", children: "Kies een fust\u2026" }), fusts.map((f) => (_jsxs("option", { value: String(f.vbn), children: [f.vbn, " \u2013 ", f.name] }, f.vbn)))] }), _jsx(Input, { "aria-label": "Andere VBN-fustcode", placeholder: "Of typ een VBN-fustcode, bijv. 412", inputMode: "numeric", value: packing.vbnPackageCode, onChange: (e) => setPack("vbnPackageCode", e.target.value) })] }), _jsxs("div", { className: "grid grid-cols-1 gap-3 sm:grid-cols-3", children: [_jsxs("div", { className: "space-y-1", children: [_jsx(Label, { htmlFor: "na-ppc", children: "Planten per fust" }), _jsx(Input, { id: "na-ppc", inputMode: "numeric", value: packing.piecesPerPackage, onChange: (e) => setPack("piecesPerPackage", e.target.value) })] }), _jsxs("div", { className: "space-y-1", children: [_jsx(Label, { htmlFor: "na-cpl", children: "Fusten per laag" }), _jsx(Input, { id: "na-cpl", inputMode: "numeric", value: packing.packagesPerLayer, onChange: (e) => setPack("packagesPerLayer", e.target.value) })] }), _jsxs("div", { className: "space-y-1", children: [_jsx(Label, { htmlFor: "na-lpc", children: "Lagen per kar" }), _jsx(Input, { id: "na-lpc", inputMode: "numeric", value: packing.layersPerLoadCarrier, onChange: (e) => setPack("layersPerLoadCarrier", e.target.value) })] })] }), _jsxs("div", { className: "space-y-1", children: [_jsx(Label, { htmlFor: "na-lc", children: "Ladingdrager" }), _jsxs("select", { id: "na-lc", className: "w-full rounded-md border bg-background px-3 py-2 text-sm", value: packing.loadCarrier, onChange: (e) => setPack("loadCarrier", e.target.value), children: [_jsx("option", { value: "", children: "Kies een ladingdrager\u2026" }), carriers.items.map((c) => (_jsx("option", { value: c.code, children: c.naam }, c.code)))] }), carriers.error && _jsx("p", { className: "text-xs text-destructive", children: carriers.error }), engineCarrier.melding && _jsx("p", { className: "text-xs text-destructive", children: engineCarrier.melding })] }), missingPacking.length > 0 && (_jsxs("p", { className: "text-xs text-muted-foreground", children: ["Nog invullen: ", missingPacking.join(", "), "."] }))] })), saveError && (_jsx("div", { role: "alert", className: "rounded-md border border-destructive/40 bg-destructive/10 p-3 text-sm text-destructive", children: saveError })), _jsxs("div", { className: "flex flex-wrap justify-between gap-2", children: [_jsx(Button, { variant: "outline", disabled: step === 0 || saving, onClick: () => setStep((s) => s - 1), children: "Vorige" }), step < STEPS.length - 1 ? (_jsx(Button, { disabled: !canNext, onClick: () => setStep((s) => s + 1), children: "Volgende" })) : (_jsxs(Button, { disabled: saving || !name.trim() || !packingReady, onClick: () => void save(), children: [saving ? _jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }) : _jsx(Check, { className: "mr-2 h-4 w-4" }), saving ? "Bezig met opslaan…" : "Artikel opslaan"] }))] })] }), hasFloriday && (_jsx("button", { type: "button", className: "text-sm text-muted-foreground underline hover:text-foreground", onClick: () => setFull(true), children: "Meteen alle Floriday-gegevens invullen" }))] }));
190
+ return (_jsxs("main", { className: "flex max-w-3xl 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"] }), title: "Artikel toevoegen", meta: _jsxs("span", { children: ["Stap ", step + 1, " van ", STEPS.length, ": ", STEPS[step]] }) }), _jsx(FloridayConnectionNotice, { status: conn.status, environment: conn.selectedEnvironment, error: conn.error }), _jsxs(Card, { className: "space-y-4 rounded-xl p-4 shadow-sm sm:p-5", children: [step === 0 && (_jsxs(_Fragment, { children: [_jsxs("div", { className: "space-y-1", children: [_jsx(Label, { htmlFor: "na-name", children: "Naam" }), _jsx(Input, { id: "na-name", value: name, onChange: (e) => setName(e.target.value), autoFocus: true })] }), _jsxs("div", { className: "space-y-1", children: [_jsx(Label, { htmlFor: "na-code", children: "Artikelcode (optioneel)" }), _jsx(Input, { id: "na-code", value: code, onChange: (e) => setCode(e.target.value) }), _jsx("p", { className: "text-xs text-muted-foreground", children: "Leeg laten: er wordt automatisch een code gemaakt." })] })] })), step === 1 && (_jsxs("div", { className: "space-y-1", children: [_jsx(Label, { htmlFor: "na-stock", children: "Voorraad (stuks)" }), _jsx(Input, { id: "na-stock", type: "number", min: 0, value: canSaveStock ? stock : "", onChange: (e) => setStock(e.target.value), disabled: !canSaveStock, "aria-describedby": "na-stock-help", autoFocus: true }), !canSaveStock && (_jsx("p", { id: "na-stock-help", className: "text-xs text-muted-foreground", children: "Koppeling wordt gecontroleerd\u2026" })), canSaveStock && !hasFloriday && (_jsx("p", { id: "na-stock-help", className: "text-xs text-muted-foreground", children: LOCAL_STOCK_TEXT }))] })), step === 2 && (_jsxs("div", { className: "space-y-1", children: [_jsx(Label, { htmlFor: "na-price", children: "Prijs per stuk (EUR, excl. btw)" }), _jsx(Input, { id: "na-price", inputMode: "decimal", value: price, onChange: (e) => setPrice(e.target.value), placeholder: "Bijv. 1,10", autoFocus: true }), _jsx("p", { className: "text-xs text-muted-foreground", children: "Zonder prijs komt het artikel niet in Floriday (\"Prijs ontbreekt\")." })] })), step === 3 && (_jsxs("div", { className: "space-y-3", children: [_jsx("p", { className: "text-sm text-muted-foreground", children: "Zonder volledige belading kan de karindeling dit artikel niet plannen. Alle vijf velden zijn verplicht." }), _jsxs("div", { className: "space-y-1", children: [_jsx(Label, { htmlFor: "na-fust", children: "Fust (VBN-code)" }), _jsxs("select", { id: "na-fust", className: "w-full rounded-md border bg-background px-3 py-2 text-sm", value: packing.vbnPackageCode, onChange: (e) => setPack("vbnPackageCode", e.target.value), children: [_jsx("option", { value: "", children: "Kies een fust\u2026" }), fusts.map((f) => (_jsxs("option", { value: String(f.vbn), children: [f.vbn, " \u2013 ", f.name] }, f.vbn)))] }), _jsx(Input, { "aria-label": "Andere VBN-fustcode", placeholder: "Of typ een VBN-fustcode, bijv. 412", inputMode: "numeric", value: packing.vbnPackageCode, onChange: (e) => setPack("vbnPackageCode", e.target.value) })] }), _jsxs("div", { className: "grid grid-cols-1 gap-3 sm:grid-cols-3", children: [_jsxs("div", { className: "space-y-1", children: [_jsx(Label, { htmlFor: "na-ppc", children: "Planten per fust" }), _jsx(Input, { id: "na-ppc", inputMode: "numeric", value: packing.piecesPerPackage, onChange: (e) => setPack("piecesPerPackage", e.target.value) })] }), _jsxs("div", { className: "space-y-1", children: [_jsx(Label, { htmlFor: "na-cpl", children: "Fusten per laag" }), _jsx(Input, { id: "na-cpl", inputMode: "numeric", value: packing.packagesPerLayer, onChange: (e) => setPack("packagesPerLayer", e.target.value) })] }), _jsxs("div", { className: "space-y-1", children: [_jsx(Label, { htmlFor: "na-lpc", children: "Lagen per kar" }), _jsx(Input, { id: "na-lpc", inputMode: "numeric", value: packing.layersPerLoadCarrier, onChange: (e) => setPack("layersPerLoadCarrier", e.target.value) })] })] }), _jsxs("div", { className: "space-y-1", children: [_jsx(Label, { htmlFor: "na-lc", children: "Ladingdrager" }), _jsxs("select", { id: "na-lc", className: "w-full rounded-md border bg-background px-3 py-2 text-sm", value: packing.loadCarrier, onChange: (e) => setPack("loadCarrier", e.target.value), children: [_jsx("option", { value: "", children: "Kies een ladingdrager\u2026" }), carriers.items.map((c) => (_jsx("option", { value: c.code, children: c.naam }, c.code)))] }), carriers.error && _jsx("p", { className: "text-xs text-destructive", children: carriers.error }), engineCarrier.melding && _jsx("p", { className: "text-xs text-destructive", children: engineCarrier.melding })] }), missingPacking.length > 0 && (_jsxs("p", { className: "text-xs text-muted-foreground", children: ["Nog invullen: ", missingPacking.join(", "), "."] }))] })), saveError && (_jsx("div", { role: "alert", className: "rounded-md border border-destructive/40 bg-destructive/10 p-3 text-sm text-destructive", children: saveError })), _jsxs("div", { className: "flex flex-wrap justify-between gap-2", children: [_jsx(Button, { variant: "outline", disabled: step === 0 || saving, onClick: () => setStep((s) => s - 1), children: "Vorige" }), step < STEPS.length - 1 ? (_jsx(Button, { disabled: !canNext, onClick: () => setStep((s) => s + 1), children: "Volgende" })) : (_jsxs(Button, { disabled: saving || !name.trim() || !packingReady, onClick: () => void save(), children: [saving ? _jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }) : _jsx(Check, { className: "mr-2 h-4 w-4" }), saving ? "Bezig met opslaan…" : "Artikel opslaan"] }))] })] }), hasFloriday && (_jsx("button", { type: "button", className: "text-sm text-muted-foreground underline hover:text-foreground", onClick: () => setFull(true), children: "Meteen alle Floriday-gegevens invullen" }))] }));
190
191
  }
@@ -1 +1 @@
1
- {"version":3,"file":"PublicatiesPage.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/PublicatiesPage.tsx"],"names":[],"mappings":"AA+BA,wBAAgB,eAAe,gCAwJ9B"}
1
+ {"version":3,"file":"PublicatiesPage.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/PublicatiesPage.tsx"],"names":[],"mappings":"AAgCA,wBAAgB,eAAe,gCA8I9B"}
@@ -5,6 +5,7 @@ import { ArrowLeft, Check, AlertTriangle, Loader2, RefreshCw } from "lucide-reac
5
5
  import { useEffect, useState } from "react";
6
6
  import { usePublishJobs } from "../../hooks/usePublishJob.js";
7
7
  import { supabase } from "../../integrations/supabase/client.js";
8
+ import { PageHeader } from "../fs-ui/PageHeader.js";
8
9
  const STATUS_LABEL = {
9
10
  running: "Bezig",
10
11
  succeeded: "Gepubliceerd",
@@ -47,7 +48,7 @@ export function PublicatiesPage() {
47
48
  cancelled = true;
48
49
  };
49
50
  }, [jobs]);
50
- return (_jsxs("main", { className: "p-6 space-y-4", children: [_jsxs("div", { className: "flex items-center justify-between", 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"] }), _jsxs(Button, { variant: "outline", size: "sm", onClick: () => void reload(), children: [_jsx(RefreshCw, { className: "mr-1 h-3 w-3" }), "Vernieuwen"] })] }), _jsxs("div", { children: [_jsx("h1", { className: "text-xl font-semibold", children: "Publicaties" }), _jsx("p", { className: "text-sm text-muted-foreground", children: "Alle producten die je hebt gepubliceerd, met wat er eventueel nog moet gebeuren." })] }), loading ? (_jsxs(Card, { className: "p-6 flex items-center gap-2 text-sm text-muted-foreground", children: [_jsx(Loader2, { className: "h-4 w-4 animate-spin" }), "Laden\u2026"] })) : jobs.length === 0 ? (_jsx(Card, { className: "p-6 text-sm text-muted-foreground", children: "Er zijn nog geen publicaties." })) : (_jsx("div", { className: "space-y-3", children: jobs.map((job) => {
51
+ 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"] }), title: "Naar Floriday gestuurd", meta: _jsx("span", { children: "Artikelen die je naar Floriday hebt gestuurd, met wat er eventueel nog moet gebeuren." }), actions: _jsxs(Button, { variant: "outline", size: "sm", className: "h-9", onClick: () => void reload(), children: [_jsx(RefreshCw, { className: "mr-2 h-4 w-4" }), "Vernieuwen"] }) }), loading ? (_jsxs(Card, { className: "p-6 flex items-center gap-2 text-sm text-muted-foreground", children: [_jsx(Loader2, { className: "h-4 w-4 animate-spin" }), "Laden\u2026"] })) : jobs.length === 0 ? (_jsx(Card, { className: "p-6 text-sm text-muted-foreground", children: "Er zijn nog geen publicaties." })) : (_jsx("div", { className: "space-y-3", children: jobs.map((job) => {
51
52
  const target = job.product_id ?? job.trade_item_id;
52
53
  // Oude "Actie nodig" sluit vanzelf zodra een latere publicatie van
53
54
  // hetzelfde artikel (op id) wel gelukt is.
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Sneltoetsen zoals in Verkooporders: N = nieuw (alleen als er een maak-actie is),
3
+ * ? = overzicht. "/" (zoeken), J/K/Enter (rijen) en Esc zitten in SearchInput en DataTable.
4
+ * Nooit tijdens typen in een invoerveld en niet terwijl er een dialoog open is.
5
+ */
6
+ export declare function Sneltoetsen({ onNieuw, nieuwLabel }: {
7
+ onNieuw?: () => void;
8
+ nieuwLabel?: string;
9
+ }): import("react").JSX.Element;
10
+ /** Kleine sneltoets-hint in een knop; alleen vanaf tablet. */
11
+ export declare function Kbd({ children }: {
12
+ children: string;
13
+ }): import("react").JSX.Element;
14
+ //# sourceMappingURL=Sneltoetsen.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Sneltoetsen.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/Sneltoetsen.tsx"],"names":[],"mappings":"AAIA;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,EAAE,OAAO,EAAE,UAA4B,EAAE,EAAE;IAAE,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,+BAwCnH;AAED,8DAA8D;AAC9D,wBAAgB,GAAG,CAAC,EAAE,QAAQ,EAAE,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,+BAErD"}
@@ -0,0 +1,45 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useState } from "react";
3
+ import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from "@flowselections/core";
4
+ import { isTypen } from "../fs-ui/SearchInput.js";
5
+ /**
6
+ * Sneltoetsen zoals in Verkooporders: N = nieuw (alleen als er een maak-actie is),
7
+ * ? = overzicht. "/" (zoeken), J/K/Enter (rijen) en Esc zitten in SearchInput en DataTable.
8
+ * Nooit tijdens typen in een invoerveld en niet terwijl er een dialoog open is.
9
+ */
10
+ export function Sneltoetsen({ onNieuw, nieuwLabel = "Nieuw artikel" }) {
11
+ const [open, setOpen] = useState(false);
12
+ useEffect(() => {
13
+ const h = (e) => {
14
+ if (e.metaKey || e.ctrlKey || e.altKey || isTypen(e.target))
15
+ return;
16
+ if (e.key === "?") {
17
+ e.preventDefault();
18
+ setOpen(true);
19
+ return;
20
+ }
21
+ if (document.querySelector("[role=dialog]"))
22
+ return;
23
+ if ((e.key === "n" || e.key === "N") && onNieuw) {
24
+ e.preventDefault();
25
+ onNieuw();
26
+ }
27
+ };
28
+ window.addEventListener("keydown", h);
29
+ return () => window.removeEventListener("keydown", h);
30
+ }, [onNieuw]);
31
+ const regels = [
32
+ ...(onNieuw ? [["N", nieuwLabel]] : []),
33
+ ["/", "Zoeken"],
34
+ ["J", "Volgende rij"],
35
+ ["K", "Vorige rij"],
36
+ ["Enter", "Openen"],
37
+ ["Esc", "Sluiten of wissen"],
38
+ ["?", "Dit overzicht"],
39
+ ];
40
+ return (_jsx(Dialog, { open: open, onOpenChange: setOpen, children: _jsxs(DialogContent, { className: "max-w-sm rounded-lg", children: [_jsxs(DialogHeader, { children: [_jsx(DialogTitle, { children: "Sneltoetsen" }), _jsx(DialogDescription, { children: "Werken niet terwijl je in een veld typt." })] }), _jsx("dl", { className: "divide-y divide-border text-sm", children: regels.map(([t, l]) => (_jsxs("div", { className: "flex items-center justify-between py-2", children: [_jsx("dt", { children: l }), _jsx("dd", { children: _jsx("kbd", { className: "rounded-sm border bg-muted px-1.5 py-0.5 text-xs", children: t }) })] }, t))) })] }) }));
41
+ }
42
+ /** Kleine sneltoets-hint in een knop; alleen vanaf tablet. */
43
+ export function Kbd({ children }) {
44
+ return _jsx("kbd", { className: "ml-2 hidden rounded-sm border border-current/30 px-1 text-[11px] leading-4 opacity-80 md:inline", children: children });
45
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"StockIntegrityAlert.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/StockIntegrityAlert.tsx"],"names":[],"mappings":"AAqCA,wBAAgB,mBAAmB,uCAiIlC"}
1
+ {"version":3,"file":"StockIntegrityAlert.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/StockIntegrityAlert.tsx"],"names":[],"mappings":"AA4CA,wBAAgB,mBAAmB,uCAiIlC"}
@@ -8,6 +8,12 @@ import { useCallback, useEffect, useState } from "react";
8
8
  import { AlertOctagon } from "lucide-react";
9
9
  import { toast } from "sonner";
10
10
  import { supabase } from "../../integrations/supabase/client.js";
11
+ /** Bron van een voorraadwijziging in gewone woorden; onbekende codes niet tonen. */
12
+ const BRON = {
13
+ MANUAL_STOCK: "handmatig aangepast", IMPORT: "import", ORDER: "order", CORRECTIE: "correctie",
14
+ FLORIDAY: "Floriday", PRODUCTION: "productie", unknown: "onbekend",
15
+ };
16
+ const bronTekst = (b) => (b ? (BRON[b] ? ` (${BRON[b]})` : "") : "");
11
17
  const keyToProductId = (k) => k.replace(/^product:/, "");
12
18
  function label(it, names) {
13
19
  const found = names.get(it.trade_item_key);
@@ -76,12 +82,13 @@ export function StockIntegrityAlert() {
76
82
  })
77
83
  .eq("id", id);
78
84
  if (error) {
79
- toast.error(`Markeren als gezien mislukt: ${error.message}`);
85
+ console.warn(error);
86
+ toast.error("Markeren als gezien lukt niet. Probeer opnieuw.");
80
87
  return;
81
88
  }
82
89
  void load();
83
90
  };
84
91
  if (alerts.length === 0)
85
92
  return null;
86
- return (_jsx("div", { className: "space-y-2", children: alerts.map((a) => (_jsxs("div", { className: "flex items-start gap-3 rounded-md border border-destructive/40 bg-destructive/10 p-3 text-sm text-destructive", children: [_jsx(AlertOctagon, { className: "mt-0.5 h-4 w-4 shrink-0" }), _jsx("div", { className: "flex-1 space-y-1", children: a.alert_kind === "FLORIDAY_NOT_UPDATED" ? (_jsxs(_Fragment, { children: [_jsxs("p", { className: "font-medium", children: ["Verkoop niet in Floriday verwerkt \u2013 pas de Floriday-voorraad aan (", new Date(a.created_at).toLocaleString("nl-NL"), ")."] }), _jsx("p", { className: "text-xs text-foreground/80", children: "Floriday leidt deze artikelen. Deze verkoop kwam niet via Floriday, dus Floriday weet er niets van." }), _jsx("ul", { className: "text-xs text-foreground/80", children: (Array.isArray(a.items) ? a.items : []).slice(0, 10).map((it, i) => (_jsxs("li", { children: [label(it, names), ": ", it.qty ?? "?", " stuks", it.order_number ? ` (order ${it.order_number})` : ""] }, `${it.trade_item_key}-${i}`))) })] })) : (_jsxs(_Fragment, { children: [_jsxs("p", { className: "font-medium", children: [a.item_count, " artikelen gingen tegelijk naar voorraad 0 (", new Date(a.created_at).toLocaleString("nl-NL"), "). Controleer of dit klopt."] }), _jsx("ul", { className: "text-xs text-foreground/80", children: (Array.isArray(a.items) ? a.items : []).slice(0, 10).map((it, i) => (_jsxs("li", { children: [label(it, names), ": was ", it.physical_before, it.change_source ? ` (bron ${it.change_source})` : " (bron onbekend)"] }, `${it.trade_item_key}-${i}`))) })] })) }), _jsx("button", { type: "button", className: "rounded-md border border-destructive/40 bg-background px-3 py-1 text-xs font-medium text-foreground hover:bg-muted", onClick: () => void acknowledge(a.id), children: "Gezien" })] }, a.id))) }));
93
+ return (_jsx("div", { className: "space-y-2", children: alerts.map((a) => (_jsxs("div", { role: "alert", className: "flex items-start gap-3 rounded-lg border border-destructive/30 bg-destructive/10 px-3 py-2.5 text-sm text-foreground", children: [_jsx(AlertOctagon, { className: "mt-0.5 h-4 w-4 shrink-0 text-destructive", "aria-hidden": true }), _jsx("div", { className: "flex-1 space-y-1", children: a.alert_kind === "FLORIDAY_NOT_UPDATED" ? (_jsxs(_Fragment, { children: [_jsxs("p", { className: "font-medium", children: ["Verkoop niet in Floriday verwerkt \u2013 pas de Floriday-voorraad aan (", new Date(a.created_at).toLocaleString("nl-NL"), ")."] }), _jsx("p", { className: "text-xs text-foreground/80", children: "Floriday leidt deze artikelen. Deze verkoop kwam niet via Floriday, dus Floriday weet er niets van." }), _jsx("ul", { className: "text-xs text-foreground/80", children: (Array.isArray(a.items) ? a.items : []).slice(0, 10).map((it, i) => (_jsxs("li", { children: [label(it, names), ": ", it.qty ?? "?", " stuks", it.order_number ? ` (order ${it.order_number})` : ""] }, `${it.trade_item_key}-${i}`))) })] })) : (_jsxs(_Fragment, { children: [_jsxs("p", { className: "font-medium", children: [a.item_count, " artikelen gingen tegelijk naar voorraad 0 (", new Date(a.created_at).toLocaleString("nl-NL"), "). Controleer of dit klopt."] }), _jsx("ul", { className: "text-xs text-foreground/80", children: (Array.isArray(a.items) ? a.items : []).slice(0, 10).map((it, i) => (_jsxs("li", { children: [label(it, names), ": was ", it.physical_before, bronTekst(it.change_source)] }, `${it.trade_item_key}-${i}`))) })] })) }), _jsx("button", { type: "button", className: "h-8 rounded-lg border border-border bg-card px-3 text-sm font-medium text-foreground hover:bg-muted focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring", onClick: () => void acknowledge(a.id), children: "Gezien" })] }, a.id))) }));
87
94
  }
@@ -20,7 +20,7 @@ const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/
20
20
  /** Een verwerker houdt een regel maximaal 5 minuten vast. */
21
21
  const LOCK_STALE_MS = 5 * 60000;
22
22
  const STATUS_LABEL = {
23
- pending: "In wachtrij",
23
+ pending: "Wacht op versturen",
24
24
  processing: "Bezig",
25
25
  sent: "Verstuurd",
26
26
  completed: "Verstuurd",
@@ -74,7 +74,7 @@ function explainError(raw) {
74
74
  return { text: raw, action: "Vul een prijs in bij dit artikel en probeer opnieuw." };
75
75
  }
76
76
  if (m.includes("cache")) {
77
- return { text: raw, action: "Synchroniseer de Floriday-artikelen en probeer opnieuw." };
77
+ return { text: raw, action: "Werk de Floriday-artikelen bij en probeer opnieuw." };
78
78
  }
79
79
  if (m.includes("429") || m.includes("rate limit") || m.includes("snelheidslimiet")) {
80
80
  return { text: "Floriday liet even geen nieuwe berichten toe.", action: "Dit gaat vanzelf; de volgende ronde pakt het op." };
@@ -2,7 +2,11 @@
2
2
  * Telmoment vastleggen: orders vóór de teldag en orders die vóór het telmoment vertrokken
3
3
  * zijn tellen niet mee; orders van de teldag die al weg zijn vink je aan.
4
4
  */
5
- export declare function TelmomentButton({ onSaved }: {
5
+ export declare function TelmomentButton({ onSaved, open: openVan, onOpenChange, verbergKnop }: {
6
6
  onSaved?: () => void;
7
+ /** Optioneel van buitenaf openen (bijv. vanuit het menu "Meer"). */
8
+ open?: boolean;
9
+ onOpenChange?: (o: boolean) => void;
10
+ verbergKnop?: boolean;
7
11
  }): import("react").JSX.Element;
8
12
  //# sourceMappingURL=TelmomentButton.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"TelmomentButton.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/TelmomentButton.tsx"],"names":[],"mappings":"AA6BA;;;GAGG;AACH,wBAAgB,eAAe,CAAC,EAAE,OAAO,EAAE,EAAE;IAAE,OAAO,CAAC,EAAE,MAAM,IAAI,CAAA;CAAE,+BAkGpE"}
1
+ {"version":3,"file":"TelmomentButton.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/TelmomentButton.tsx"],"names":[],"mappings":"AA6BA;;;GAGG;AACH,wBAAgB,eAAe,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,EAAE;IACrF,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,oEAAoE;IACpE,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,YAAY,CAAC,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IACpC,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB,+BAyGA"}