@flowselections/floriday-voorraad 1.0.86 → 1.0.90

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 (38) hide show
  1. package/dist-lib/components/voorraad/FloridayTakeoverPanel.d.ts.map +1 -1
  2. package/dist-lib/components/voorraad/FloridayTakeoverPanel.js +32 -9
  3. package/dist-lib/components/voorraad/InventoryStatusBadge.d.ts.map +1 -1
  4. package/dist-lib/components/voorraad/InventoryStatusBadge.js +7 -0
  5. package/dist-lib/hooks/useInventorySettings.d.ts +3 -0
  6. package/dist-lib/hooks/useInventorySettings.d.ts.map +1 -1
  7. package/dist-lib/hooks/useInventorySettings.js +1 -1
  8. package/dist-lib/hooks-entry.d.ts +1 -0
  9. package/dist-lib/hooks-entry.d.ts.map +1 -1
  10. package/dist-lib/hooks-entry.js +1 -0
  11. package/dist-lib/integrations/supabase/types.d.ts +278 -0
  12. package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
  13. package/dist-lib/lib/__tests__/floriday-customer-offers.test.d.ts +2 -0
  14. package/dist-lib/lib/__tests__/floriday-customer-offers.test.d.ts.map +1 -0
  15. package/dist-lib/lib/__tests__/floriday-customer-offers.test.js +36 -0
  16. package/dist-lib/lib/__tests__/floriday-fetch-status.test.d.ts +2 -0
  17. package/dist-lib/lib/__tests__/floriday-fetch-status.test.d.ts.map +1 -0
  18. package/dist-lib/lib/__tests__/floriday-fetch-status.test.js +25 -0
  19. package/dist-lib/lib/floriday-customer-offers.d.ts +25 -0
  20. package/dist-lib/lib/floriday-customer-offers.d.ts.map +1 -0
  21. package/dist-lib/lib/floriday-customer-offers.js +49 -0
  22. package/dist-lib/lib/floriday-fetch-status.d.ts +17 -0
  23. package/dist-lib/lib/floriday-fetch-status.d.ts.map +1 -0
  24. package/dist-lib/lib/floriday-fetch-status.js +14 -0
  25. package/dist-lib/lib/floriday-takeover-core.d.ts +71 -0
  26. package/dist-lib/lib/floriday-takeover-core.d.ts.map +1 -0
  27. package/dist-lib/lib/floriday-takeover-core.js +196 -0
  28. package/dist-lib/lib/floriday-takeover.functions.d.ts +7 -35
  29. package/dist-lib/lib/floriday-takeover.functions.d.ts.map +1 -1
  30. package/dist-lib/lib/floriday-takeover.functions.js +3 -173
  31. package/dist-lib/server-hooks/floriday-follow.d.ts +3 -0
  32. package/dist-lib/server-hooks/floriday-follow.d.ts.map +1 -0
  33. package/dist-lib/server-hooks/floriday-follow.js +46 -0
  34. package/dist-lib/server-hooks/guard.d.ts.map +1 -1
  35. package/dist-lib/server-hooks/guard.js +6 -1
  36. package/package.json +16 -2
  37. package/sql/AGENTS.md +3 -0
  38. package/sql/floriday-voorraad-schema.sql +8188 -0
@@ -1 +1 @@
1
- {"version":3,"file":"FloridayTakeoverPanel.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/FloridayTakeoverPanel.tsx"],"names":[],"mappings":"AAuCA,wBAAgB,sBAAsB,CAAC,EAAE,YAAY,EAAE,EAAE;IAAE,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,sCAoCvF;AAED,wBAAgB,qBAAqB,CAAC,EAAE,YAAY,EAAE,EAAE;IAAE,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,sCAwPtF"}
1
+ {"version":3,"file":"FloridayTakeoverPanel.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/FloridayTakeoverPanel.tsx"],"names":[],"mappings":"AA4CA,wBAAgB,sBAAsB,CAAC,EAAE,YAAY,EAAE,EAAE;IAAE,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,sCAoCvF;AAED,wBAAgB,qBAAqB,CAAC,EAAE,YAAY,EAAE,EAAE;IAAE,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,sCAoStF"}
@@ -11,12 +11,16 @@ import { useCallback, useEffect, useMemo, useState } from "react";
11
11
  import { useServerFn } from "@tanstack/react-start";
12
12
  import { toast } from "sonner";
13
13
  import * as XLSX from "xlsx";
14
- import { Button } from "@flowselections/core";
14
+ import { Button, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle } from "@flowselections/core";
15
15
  import { supabase } from "../../integrations/supabase/client.js";
16
16
  import { getFloridayTakeoverPreview } from "../../lib/floriday-takeover.functions.js";
17
17
  const sb = supabase;
18
18
  const eur = (v) => v == null ? "—" : `€ ${Number(v).toLocaleString("nl-NL", { minimumFractionDigits: 2, maximumFractionDigits: 4 })}`;
19
19
  const num = (v) => (v == null ? "—" : Number(v).toLocaleString("nl-NL"));
20
+ function offersText(o) {
21
+ return o.map((x) => [x.customer ?? "Klant onbekend", x.title ? `“${x.title}”` : null, x.price != null ? eur(x.price) : null, x.pieces != null ? `${num(x.pieces)} st.` : null,
22
+ x.from || x.to ? `${(x.from ?? "").slice(0, 10)} – ${(x.to ?? "").slice(0, 10)}` : null].filter(Boolean).join(" ")).join("; ");
23
+ }
20
24
  function formsText(f) {
21
25
  const parts = [];
22
26
  if (f.continuous)
@@ -55,8 +59,11 @@ export function FloridayTakeoverPanel({ connectionId }) {
55
59
  const [loading, setLoading] = useState(false);
56
60
  const [rows, setRows] = useState(null);
57
61
  const [weekError, setWeekError] = useState(null);
62
+ const [fetchStatus, setFetchStatus] = useState(null);
63
+ const itemsBlocked = fetchStatus != null && fetchStatus.tradeItems !== "compleet";
58
64
  const [selected, setSelected] = useState(new Set());
59
65
  const [busy, setBusy] = useState(false);
66
+ const [ask, setAsk] = useState(null);
60
67
  const [runs, setRuns] = useState([]);
61
68
  const [leadRows, setLeadRows] = useState([]);
62
69
  const [leadSel, setLeadSel] = useState(new Set());
@@ -88,6 +95,7 @@ export function FloridayTakeoverPanel({ connectionId }) {
88
95
  const res = await preview();
89
96
  setRows(res.rows);
90
97
  setWeekError(res.weekError);
98
+ setFetchStatus(res.fetchStatus);
91
99
  setSelected(new Set(res.rows.filter((r) => r.action !== "niet").map((r) => r.tradeItemId)));
92
100
  }
93
101
  catch (e) {
@@ -107,17 +115,25 @@ export function FloridayTakeoverPanel({ connectionId }) {
107
115
  "Floriday aantal": r.floridayStock ?? "", "Bron aantal": r.stockSource ?? "",
108
116
  "Prijs aanbodregel": r.supplyLinePrice ?? "", Catalogusprijs: r.catalogPrice ?? "", "Gekozen prijs": r.floridayPrice ?? "", "Bron prijs": r.priceSource ?? "",
109
117
  "Eigen voorraad": r.erpStock ?? "", "Eigen prijs": r.erpPrice ?? "", Aanbodvorm: formsText(r.forms),
118
+ Klantaanbiedingen: offersText(r.customerOffers),
110
119
  }));
111
120
  const wb = XLSX.utils.book_new();
112
121
  XLSX.utils.book_append_sheet(wb, XLSX.utils.json_to_sheet(data), "Proefrapport");
113
122
  XLSX.writeFile(wb, `overnemen-uit-floriday-${new Date().toISOString().slice(0, 10)}.xlsx`);
114
123
  };
115
124
  const apply = async () => {
116
- if (!connectionId || chosen.length === 0)
125
+ if (!connectionId || chosen.length === 0 || itemsBlocked)
117
126
  return;
118
127
  const created = chosen.filter((r) => r.action === "aanmaken").length;
119
- if (!window.confirm(`${chosen.length} artikelen overnemen uit Floriday${created ? `, waarvan ${created} nieuw aanmaken` : ""}? ` +
120
- "Voorraad en prijs worden overgenomen. Er gaat niets naar Floriday; deze artikelen blijven Floriday-leidend."))
128
+ setAsk({
129
+ title: `${chosen.length} artikelen overnemen uit Floriday?`,
130
+ text: `${created ? `Hiervan maak je er ${created} nieuw aan. ` : ""}Voorraad en prijs worden overgenomen. Er gaat niets naar Floriday; deze artikelen blijven Floriday-leidend en volgen Floriday daarna elk uur. Je kunt dit terugdraaien.`,
131
+ label: `Overnemen (${chosen.length})`,
132
+ run: doApply,
133
+ });
134
+ };
135
+ const doApply = async () => {
136
+ if (!connectionId)
121
137
  return;
122
138
  setBusy(true);
123
139
  const items = chosen.map((r) => ({
@@ -137,9 +153,13 @@ export function FloridayTakeoverPanel({ connectionId }) {
137
153
  loadRuns();
138
154
  loadLeadRows();
139
155
  };
140
- const undo = async (runId) => {
141
- if (!window.confirm("Deze overname terugdraaien? Artikelen die sindsdien zijn gewijzigd blijven staan. Nieuw aangemaakte artikelen blijven bestaan (op Uit)."))
142
- return;
156
+ const undo = (runId) => setAsk({
157
+ title: "Deze overname terugdraaien?",
158
+ text: "Artikelen die sindsdien zijn gewijzigd blijven staan. Nieuw aangemaakte artikelen blijven bestaan (op Uit).",
159
+ label: "Terugdraaien",
160
+ run: () => doUndo(runId),
161
+ });
162
+ const doUndo = async (runId) => {
143
163
  const { data, error } = await sb.rpc("floriday_takeover_undo", { p_run_id: runId });
144
164
  if (error) {
145
165
  toast.error("Terugdraaien is niet gelukt.", { description: error.message });
@@ -179,8 +199,11 @@ export function FloridayTakeoverPanel({ connectionId }) {
179
199
  };
180
200
  if (!connectionId)
181
201
  return null;
182
- return (_jsxs("section", { className: "rounded-md border p-3 space-y-3", children: [_jsxs("button", { type: "button", className: "text-sm font-medium", onClick: () => setOpen((v) => !v), children: [open ? "▾" : "▸", " Overnemen uit Floriday en eigen voorraad leidend maken"] }), open && (_jsxs("div", { className: "space-y-6", children: [_jsxs("div", { className: "space-y-2", children: [_jsx("p", { className: "text-sm text-muted-foreground", children: "Neem voorraad en prijs over uit Floriday zonder iets terug te sturen. Maak eerst een proefrapport; er wordt dan nog niets gewijzigd." }), _jsxs("div", { className: "flex flex-wrap gap-2", children: [_jsx(Button, { size: "sm", onClick: runPreview, disabled: loading, children: loading ? "Floriday lezen…" : "Proefrapport maken" }), rows && _jsx(Button, { size: "sm", variant: "outline", onClick: download, children: "Download als Excel" }), rows && _jsx(Button, { size: "sm", onClick: apply, disabled: busy || chosen.length === 0, children: busy ? "Bezig…" : `Overnemen (${chosen.length})` })] }), weekError && (_jsxs("p", { className: "text-sm text-destructive", children: ["Het weekaanbod kon niet worden gelezen; het rapport gebruikt alleen de overige gegevens.", " ", _jsxs("details", { className: "inline", children: [_jsx("summary", { className: "inline cursor-pointer", children: "Details voor beheer" }), weekError] })] })), rows && (_jsx("div", { className: "max-h-[28rem] overflow-auto rounded border", children: _jsxs("table", { className: "w-full text-xs", children: [_jsx("thead", { className: "sticky top-0 bg-muted", children: _jsxs("tr", { className: "text-left", children: [_jsx("th", { className: "p-1" }), _jsx("th", { className: "p-1", children: "Artikel" }), _jsx("th", { className: "p-1", children: "Actie" }), _jsx("th", { className: "p-1", children: "Floriday aantal" }), _jsx("th", { className: "p-1", children: "Floriday prijs" }), _jsx("th", { className: "p-1", children: "Nu in je voorraad" }), _jsx("th", { className: "p-1", children: "Aanbodvorm" })] }) }), _jsx("tbody", { children: rows.map((r) => (_jsxs("tr", { className: "border-t align-top", children: [_jsx("td", { className: "p-1", children: _jsx("input", { type: "checkbox", disabled: r.action === "niet", checked: selected.has(r.tradeItemId), onChange: (e) => setSelected((s) => { const n = new Set(s); e.target.checked ? n.add(r.tradeItemId) : n.delete(r.tradeItemId); return n; }) }) }), _jsxs("td", { className: "p-1", children: [_jsx("div", { className: "font-medium", children: r.name }), _jsx("div", { className: "text-muted-foreground", children: [r.articleCode, r.gtin, r.vbnCode && `VBN ${r.vbnCode}`].filter(Boolean).join(" · ") }), r.suggestion && _jsx("div", { className: "text-amber-700", children: r.suggestion })] }), _jsxs("td", { className: "p-1", children: [r.action === "aanmaken" ? "Nieuw aanmaken (Uit)" : r.action === "bijwerken" ? "Bijwerken" : "Niet over te nemen", r.reason && _jsx("div", { className: "text-muted-foreground", children: r.reason })] }), _jsxs("td", { className: "p-1", children: [num(r.floridayStock), _jsx("div", { className: "text-muted-foreground", children: r.stockSource ?? "" })] }), _jsxs("td", { className: "p-1", children: [eur(r.floridayPrice), _jsx("div", { className: "text-muted-foreground", children: r.priceSource ?? "" }), r.priceDiffers && _jsxs("div", { className: "text-amber-700", children: ["Aanbodregel ", eur(r.supplyLinePrice), " \u00B7 catalogus ", eur(r.catalogPrice)] })] }), _jsxs("td", { className: "p-1", children: [num(r.erpStock), " \u00B7 ", eur(r.erpPrice)] }), _jsx("td", { className: "p-1", children: formsText(r.forms) })] }, r.tradeItemId))) })] }) })), runs.length > 0 && (_jsxs("div", { className: "text-sm space-y-1", children: [_jsx("div", { className: "font-medium", children: "Laatste overnames" }), runs.map((r) => (_jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsxs("span", { children: [new Date(r.created_at).toLocaleString("nl-NL"), " \u2014 ", r.total_items, " artikelen", r.created_products ? `, ${r.created_products} nieuw` : "", " \u2014 ", r.status] }), r.status !== "ongedaan" && _jsx(Button, { size: "sm", variant: "outline", onClick: () => undo(r.id), children: "Terugdraaien" })] }, r.id)))] }))] }), _jsxs("div", { className: "space-y-2", children: [_jsx("div", { className: "text-sm font-medium", children: "Eigen voorraad leidend maken" }), _jsx("p", { className: "text-sm text-muted-foreground", children: "Kies artikelen die nu Floriday-leidend zijn. Je ziet eerst wat er naar Floriday zou gaan; pas na bevestigen verandert er iets." }), leadRows.length === 0 ? (_jsx("p", { className: "text-sm text-muted-foreground", children: "Er zijn geen Floriday-leidende artikelen." })) : (_jsxs("div", { className: "max-h-48 overflow-auto rounded border p-2 text-sm space-y-1", children: [_jsxs("label", { className: "flex items-center gap-2 font-medium", children: [_jsx("input", { type: "checkbox", checked: leadSel.size === leadRows.length, onChange: (e) => setLeadSel(e.target.checked ? new Set(leadRows.map((r) => r.product_id)) : new Set()) }), "Alles kiezen"] }), leadRows.map((r) => (_jsxs("label", { className: "flex items-center gap-2", children: [_jsx("input", { type: "checkbox", checked: leadSel.has(r.product_id), onChange: (e) => setLeadSel((s) => { const n = new Set(s); e.target.checked ? n.add(r.product_id) : n.delete(r.product_id); return n; }) }), r.name] }, r.product_id)))] })), _jsxs(Button, { size: "sm", variant: "outline", disabled: leadSel.size === 0, onClick: showErpPreview, children: ["Voorbeeld tonen (", leadSel.size, ")"] }), erpPreview && (_jsxs("div", { className: "space-y-2", children: [_jsxs("p", { className: "text-xs text-muted-foreground", children: ["Alleen de huidige week en de komende ", erpPreview[0]?.horizon_weeks ?? "?", " weken worden aangepast; weken in het verleden nooit."] }), _jsxs("table", { className: "w-full text-xs rounded border", children: [_jsx("thead", { className: "bg-muted", children: _jsxs("tr", { className: "text-left", children: [_jsx("th", { className: "p-1", children: "Artikel" }), _jsx("th", { className: "p-1", children: "Week" }), _jsx("th", { className: "p-1", children: "Vorm" }), _jsx("th", { className: "p-1", children: "Aantal" }), _jsx("th", { className: "p-1", children: "Prijs per stuk" }), _jsx("th", { className: "p-1", children: "Nu in Floriday" }), _jsx("th", { className: "p-1", children: "Let op" })] }) }), _jsx("tbody", { children: erpPreview.flatMap((r) => {
202
+ return (_jsxs("section", { className: "rounded-md border p-3 space-y-3", children: [_jsxs("button", { type: "button", className: "text-sm font-medium", onClick: () => setOpen((v) => !v), children: [open ? "▾" : "▸", " Overnemen uit Floriday en eigen voorraad leidend maken"] }), open && (_jsxs("div", { className: "space-y-6", children: [_jsxs("div", { className: "space-y-2", children: [_jsx("p", { className: "text-sm text-muted-foreground", children: "Neem voorraad en prijs over uit Floriday zonder iets terug te sturen. Maak eerst een proefrapport; er wordt dan nog niets gewijzigd." }), _jsxs("div", { className: "flex flex-wrap gap-2", children: [_jsx(Button, { size: "sm", onClick: runPreview, disabled: loading, children: loading ? "Floriday lezen…" : "Proefrapport maken" }), rows && _jsx(Button, { size: "sm", variant: "outline", onClick: download, children: "Download als Excel" }), rows && _jsx(Button, { size: "sm", onClick: apply, disabled: busy || chosen.length === 0 || itemsBlocked, children: busy ? "Bezig…" : `Overnemen (${chosen.length})` })] }), fetchStatus && fetchStatus.tradeItems !== "compleet" && (_jsx("p", { className: "text-sm text-destructive", children: fetchStatus.tradeItems === "bezig"
203
+ ? "Artikelen worden nog opgehaald – wacht tot dit compleet is. Overnemen kan daarna."
204
+ : "De artikelen uit Floriday zijn nog niet opgehaald – wacht tot dit compleet is. Overnemen kan daarna." })), fetchStatus && fetchStatus.tradeItems === "compleet" && (fetchStatus.catalogPrices === "bezig" || fetchStatus.customerOffers === "bezig") && (_jsx("p", { className: "text-sm text-muted-foreground", children: "Het aanbod wordt nog opgehaald \u2013 wacht tot dit compleet is voor de juiste prijzen. Maak daarna het proefrapport opnieuw." })), weekError && (_jsxs("p", { className: "text-sm text-destructive", children: ["Het weekaanbod kon niet worden gelezen; het rapport gebruikt alleen de overige gegevens.", " ", _jsxs("details", { className: "inline", children: [_jsx("summary", { className: "inline cursor-pointer", children: "Details voor beheer" }), weekError] })] })), rows && (_jsx("div", { className: "max-h-[28rem] overflow-auto rounded border", children: _jsxs("table", { className: "w-full text-xs", children: [_jsx("thead", { className: "sticky top-0 bg-muted", children: _jsxs("tr", { className: "text-left", children: [_jsx("th", { className: "p-1" }), _jsx("th", { className: "p-1", children: "Artikel" }), _jsx("th", { className: "p-1", children: "Actie" }), _jsx("th", { className: "p-1", children: "Floriday aantal" }), _jsx("th", { className: "p-1", children: "Floriday prijs" }), _jsx("th", { className: "p-1", children: "Nu in je voorraad" }), _jsx("th", { className: "p-1", children: "Aanbodvorm" })] }) }), _jsx("tbody", { children: rows.map((r) => (_jsxs("tr", { className: "border-t align-top", children: [_jsx("td", { className: "p-1", children: _jsx("input", { type: "checkbox", disabled: r.action === "niet", checked: selected.has(r.tradeItemId), onChange: (e) => setSelected((s) => { const n = new Set(s); e.target.checked ? n.add(r.tradeItemId) : n.delete(r.tradeItemId); return n; }) }) }), _jsxs("td", { className: "p-1", children: [_jsx("div", { className: "font-medium", children: r.name }), _jsx("div", { className: "text-muted-foreground", children: [r.articleCode, r.gtin, r.vbnCode && `VBN ${r.vbnCode}`].filter(Boolean).join(" · ") }), r.suggestion && _jsx("div", { className: "text-amber-700", children: r.suggestion })] }), _jsxs("td", { className: "p-1", children: [r.action === "aanmaken" ? "Nieuw aanmaken (Uit)" : r.action === "bijwerken" ? "Bijwerken" : "Niet over te nemen", r.reason && _jsx("div", { className: "text-muted-foreground", children: r.reason })] }), _jsxs("td", { className: "p-1", children: [num(r.floridayStock), _jsx("div", { className: "text-muted-foreground", children: r.stockSource ?? "" })] }), _jsxs("td", { className: "p-1", children: [eur(r.floridayPrice), _jsx("div", { className: "text-muted-foreground", children: r.priceSource ?? "" }), r.priceDiffers && _jsxs("div", { className: "text-amber-700", children: ["Aanbodregel ", eur(r.supplyLinePrice), " \u00B7 catalogus ", eur(r.catalogPrice)] })] }), _jsxs("td", { className: "p-1", children: [num(r.erpStock), " \u00B7 ", eur(r.erpPrice)] }), _jsxs("td", { className: "p-1", children: [formsText(r.forms), r.customerOffers.length > 0 && (_jsxs("details", { children: [_jsxs("summary", { className: "cursor-pointer", children: ["Klantaanbiedingen (", r.customerOffers.length, ", alleen getoond)"] }), _jsx("ul", { className: "list-disc pl-4", children: r.customerOffers.map((o) => _jsx("li", { children: offersText([o]) }, o.id)) })] }))] })] }, r.tradeItemId))) })] }) })), runs.length > 0 && (_jsxs("div", { className: "text-sm space-y-1", children: [_jsx("div", { className: "font-medium", children: "Laatste overnames" }), runs.map((r) => (_jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsxs("span", { children: [new Date(r.created_at).toLocaleString("nl-NL"), " \u2014 ", r.total_items, " artikelen", r.created_products ? `, ${r.created_products} nieuw` : "", " \u2014 ", r.status] }), r.status !== "ongedaan" && _jsx(Button, { size: "sm", variant: "outline", onClick: () => undo(r.id), children: "Terugdraaien" })] }, r.id)))] }))] }), _jsxs("div", { className: "space-y-2", children: [_jsx("div", { className: "text-sm font-medium", children: "Eigen voorraad leidend maken" }), _jsx("p", { className: "text-sm text-muted-foreground", children: "Kies artikelen die nu Floriday-leidend zijn. Je ziet eerst wat er naar Floriday zou gaan; pas na bevestigen verandert er iets." }), leadRows.length === 0 ? (_jsx("p", { className: "text-sm text-muted-foreground", children: "Er zijn geen Floriday-leidende artikelen." })) : (_jsxs("div", { className: "max-h-48 overflow-auto rounded border p-2 text-sm space-y-1", children: [_jsxs("label", { className: "flex items-center gap-2 font-medium", children: [_jsx("input", { type: "checkbox", checked: leadSel.size === leadRows.length, onChange: (e) => setLeadSel(e.target.checked ? new Set(leadRows.map((r) => r.product_id)) : new Set()) }), "Alles kiezen"] }), leadRows.map((r) => (_jsxs("label", { className: "flex items-center gap-2", children: [_jsx("input", { type: "checkbox", checked: leadSel.has(r.product_id), onChange: (e) => setLeadSel((s) => { const n = new Set(s); e.target.checked ? n.add(r.product_id) : n.delete(r.product_id); return n; }) }), r.name] }, r.product_id)))] })), _jsxs(Button, { size: "sm", variant: "outline", disabled: leadSel.size === 0, onClick: showErpPreview, children: ["Voorbeeld tonen (", leadSel.size, ")"] }), erpPreview && (_jsxs("div", { className: "space-y-2", children: [_jsxs("p", { className: "text-xs text-muted-foreground", children: ["Alleen de huidige week en de komende ", erpPreview[0]?.horizon_weeks ?? "?", " weken worden aangepast; weken in het verleden nooit."] }), _jsxs("table", { className: "w-full text-xs rounded border", children: [_jsx("thead", { className: "bg-muted", children: _jsxs("tr", { className: "text-left", children: [_jsx("th", { className: "p-1", children: "Artikel" }), _jsx("th", { className: "p-1", children: "Week" }), _jsx("th", { className: "p-1", children: "Vorm" }), _jsx("th", { className: "p-1", children: "Aantal" }), _jsx("th", { className: "p-1", children: "Prijs per stuk" }), _jsx("th", { className: "p-1", children: "Nu in Floriday" }), _jsx("th", { className: "p-1", children: "Let op" })] }) }), _jsx("tbody", { children: erpPreview.flatMap((r) => {
183
205
  const ws = r.weeks?.length ? r.weeks : [null];
184
206
  return ws.map((w, i) => (_jsxs("tr", { className: "border-t align-top", children: [_jsx("td", { className: "p-1", children: i === 0 ? r.product_name : "" }), _jsx("td", { className: "p-1", children: w ? (w.week != null ? `${w.year} wk ${w.week}` : "Doorlopend") : "—" }), _jsx("td", { className: "p-1", children: w ? (w.form === "continuous" ? "Doorlopende voorraad" : "Weekaanbod") : "—" }), _jsx("td", { className: "p-1", children: w ? _jsxs(_Fragment, { children: [num(w.quantity), w.available === false ? " (niet beschikbaar)" : ""] }) : "—" }), _jsx("td", { className: "p-1", children: w ? eur(w.price) : eur(r.sale_price) }), _jsx("td", { className: "p-1", children: w && w.floriday_pieces != null ? num(w.floriday_pieces) : "onbekend" }), _jsx("td", { className: "p-1 text-amber-700", children: i === 0 ? r.blocker ?? "" : "" })] }, `${r.product_id}-${i}`)));
185
- }) })] }), _jsxs("div", { className: "flex gap-2", children: [_jsx(Button, { size: "sm", onClick: confirmErp, children: "Bevestigen" }), _jsx(Button, { size: "sm", variant: "outline", onClick: () => setErpPreview(null), children: "Annuleren" })] })] }))] })] }))] }));
207
+ }) })] }), _jsxs("div", { className: "flex gap-2", children: [_jsx(Button, { size: "sm", onClick: confirmErp, children: "Bevestigen" }), _jsx(Button, { size: "sm", variant: "outline", onClick: () => setErpPreview(null), children: "Annuleren" })] })] }))] })] })), _jsx(AlertDialog, { open: ask != null, onOpenChange: (o) => { if (!o)
208
+ setAsk(null); }, children: _jsxs(AlertDialogContent, { children: [_jsxs(AlertDialogHeader, { children: [_jsx(AlertDialogTitle, { children: ask?.title }), _jsx(AlertDialogDescription, { children: ask?.text })] }), _jsxs(AlertDialogFooter, { children: [_jsx(AlertDialogCancel, { children: "Annuleren" }), _jsx(AlertDialogAction, { onClick: () => { const r = ask?.run; setAsk(null); r?.(); }, children: ask?.label })] })] }) })] }));
186
209
  }
@@ -1 +1 @@
1
- {"version":3,"file":"InventoryStatusBadge.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/InventoryStatusBadge.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;AACzE,OAAO,EAAE,WAAW,EAAwC,MAAM,uBAAuB,CAAC;AAC1F,OAAO,EAAE,WAAW,EAAE,CAAC;AAiCvB,wBAAgB,oBAAoB,CAAC,EAAE,OAAO,EAAE,EAAE;IAAE,OAAO,EAAE,gBAAgB,GAAG,SAAS,CAAA;CAAE,+BAuC1F"}
1
+ {"version":3,"file":"InventoryStatusBadge.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/InventoryStatusBadge.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;AACzE,OAAO,EAAE,WAAW,EAAwC,MAAM,uBAAuB,CAAC;AAC1F,OAAO,EAAE,WAAW,EAAE,CAAC;AAiCvB,wBAAgB,oBAAoB,CAAC,EAAE,OAAO,EAAE,EAAE;IAAE,OAAO,EAAE,gBAAgB,GAAG,SAAS,CAAA;CAAE,+BAqD1F"}
@@ -39,6 +39,13 @@ export function InventoryStatusBadge({ setting }) {
39
39
  ? "UNKNOWN"
40
40
  : (setting.sync_status ?? "UNKNOWN");
41
41
  const dot = syncDot(status);
42
+ if (setting.erp_leading === false) {
43
+ const at = setting.follow_floriday_at
44
+ ? new Date(setting.follow_floriday_at).toLocaleString("nl-NL", { day: "numeric", month: "short", hour: "2-digit", minute: "2-digit" })
45
+ : "nog niet";
46
+ const failed = setting.follow_floriday_result?.startsWith("fout");
47
+ return (_jsxs("span", { className: `text-xs ${failed ? "text-destructive" : "text-muted-foreground"}`, title: failed ? setting.follow_floriday_result ?? undefined : "Aantal en prijs komen elk uur uit Floriday; er gaat niets naar Floriday.", children: ["Volgt Floriday \u00B7 laatst bijgewerkt ", at, failed ? " (niet gelukt)" : ""] }));
48
+ }
42
49
  return (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Badge, { className: MODE_CLASSES[mode] ?? MODE_CLASSES.FOLLOW_ERP, children: MODE_LABELS[mode] ?? mode }), _jsx("span", { className: `h-2 w-2 shrink-0 rounded-full ${dot.cls}`, title: setting.action_reason
43
50
  ? `${dot.title}: ${ACTION_REASON_LABELS[setting.action_reason] ?? setting.action_reason}`
44
51
  : setting.last_sync_error
@@ -22,6 +22,9 @@ export type InventorySetting = {
22
22
  action_reason: string | null;
23
23
  packing_size: number | null;
24
24
  product_type: string | null;
25
+ erp_leading: boolean | null;
26
+ follow_floriday_at: string | null;
27
+ follow_floriday_result: string | null;
25
28
  };
26
29
  export declare function useInventorySettings(connectionId: string | null): {
27
30
  rows: InventorySetting[];
@@ -1 +1 @@
1
- {"version":3,"file":"useInventorySettings.d.ts","sourceRoot":"","sources":["../../src/hooks/useInventorySettings.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AACpD,YAAY,EAAE,SAAS,EAAE,CAAC;AAE1B,MAAM,MAAM,gBAAgB,GAAG;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,sBAAsB,EAAE,MAAM,GAAG,IAAI,CAAC;IACtC,UAAU,EAAE,SAAS,CAAC;IACtB,eAAe,EAAE,MAAM,CAAC;IACxB,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,gBAAgB,EAAE,MAAM,CAAC;IACzB,oBAAoB,EAAE,OAAO,CAAC;IAC9B,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,gBAAgB,EAAE,OAAO,GAAG,IAAI,CAAC;IACjC,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B,CAAC;AAKF,wBAAgB,oBAAoB,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI;;;;;;EA0D/D"}
1
+ {"version":3,"file":"useInventorySettings.d.ts","sourceRoot":"","sources":["../../src/hooks/useInventorySettings.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AACpD,YAAY,EAAE,SAAS,EAAE,CAAC;AAE1B,MAAM,MAAM,gBAAgB,GAAG;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,sBAAsB,EAAE,MAAM,GAAG,IAAI,CAAC;IACtC,UAAU,EAAE,SAAS,CAAC;IACtB,eAAe,EAAE,MAAM,CAAC;IACxB,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,gBAAgB,EAAE,MAAM,CAAC;IACzB,oBAAoB,EAAE,OAAO,CAAC;IAC9B,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,gBAAgB,EAAE,OAAO,GAAG,IAAI,CAAC;IACjC,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,WAAW,EAAE,OAAO,GAAG,IAAI,CAAC;IAC5B,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,sBAAsB,EAAE,MAAM,GAAG,IAAI,CAAC;CACvC,CAAC;AAKF,wBAAgB,oBAAoB,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI;;;;;;EA0D/D"}
@@ -5,7 +5,7 @@
5
5
  */
6
6
  import { useCallback, useEffect, useState } from "react";
7
7
  import { supabase } from "../integrations/supabase/client.js";
8
- const SELECT = "id, connection_id, product_id, floriday_trade_item_id, stock_mode, buffer_quantity, maximum_quantity, fixed_quantity, manual_quantity, desired_quantity, desired_availability, remote_quantity, remote_available, remote_checked_at, sync_status, last_sync_error, last_published_at, last_confirmed_at, action_reason, packing_size, product_type";
8
+ const SELECT = "id, connection_id, product_id, floriday_trade_item_id, stock_mode, buffer_quantity, maximum_quantity, fixed_quantity, manual_quantity, desired_quantity, desired_availability, remote_quantity, remote_available, remote_checked_at, sync_status, last_sync_error, last_published_at, last_confirmed_at, action_reason, packing_size, product_type, erp_leading, follow_floriday_at, follow_floriday_result";
9
9
  export function useInventorySettings(connectionId) {
10
10
  const [rows, setRows] = useState([]);
11
11
  const [loading, setLoading] = useState(false);
@@ -5,6 +5,7 @@
5
5
  */
6
6
  export { handleFloridayBaseSupply } from "./server-hooks/floriday-base-supply.js";
7
7
  export { handleFloridayContinuousStock } from "./server-hooks/floriday-continuous-stock.js";
8
+ export { handleFloridayFollow } from "./server-hooks/floriday-follow.js";
8
9
  export { handleFloridayContinuousStockPull } from "./server-hooks/floriday-continuous-stock-pull.js";
9
10
  export { handleFloridayStockReconcile } from "./server-hooks/floriday-stock-reconcile.js";
10
11
  export { handleLogisticsTasksRun } from "./server-hooks/logistics-tasks-run.js";
@@ -1 +1 @@
1
- {"version":3,"file":"hooks-entry.d.ts","sourceRoot":"","sources":["../src/hooks-entry.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,wBAAwB,EAAE,MAAM,qCAAqC,CAAC;AAC/E,OAAO,EAAE,6BAA6B,EAAE,MAAM,0CAA0C,CAAC;AACzF,OAAO,EAAE,iCAAiC,EAAE,MAAM,+CAA+C,CAAC;AAClG,OAAO,EAAE,4BAA4B,EAAE,MAAM,yCAAyC,CAAC;AACvF,OAAO,EAAE,uBAAuB,EAAE,MAAM,oCAAoC,CAAC;AAC7E,OAAO,EAAE,oBAAoB,EAAE,MAAM,iCAAiC,CAAC"}
1
+ {"version":3,"file":"hooks-entry.d.ts","sourceRoot":"","sources":["../src/hooks-entry.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,wBAAwB,EAAE,MAAM,qCAAqC,CAAC;AAC/E,OAAO,EAAE,6BAA6B,EAAE,MAAM,0CAA0C,CAAC;AACzF,OAAO,EAAE,oBAAoB,EAAE,MAAM,gCAAgC,CAAC;AACtE,OAAO,EAAE,iCAAiC,EAAE,MAAM,+CAA+C,CAAC;AAClG,OAAO,EAAE,4BAA4B,EAAE,MAAM,yCAAyC,CAAC;AACvF,OAAO,EAAE,uBAAuB,EAAE,MAAM,oCAAoC,CAAC;AAC7E,OAAO,EAAE,oBAAoB,EAAE,MAAM,iCAAiC,CAAC"}
@@ -5,6 +5,7 @@
5
5
  */
6
6
  export { handleFloridayBaseSupply } from "./server-hooks/floriday-base-supply.js";
7
7
  export { handleFloridayContinuousStock } from "./server-hooks/floriday-continuous-stock.js";
8
+ export { handleFloridayFollow } from "./server-hooks/floriday-follow.js";
8
9
  export { handleFloridayContinuousStockPull } from "./server-hooks/floriday-continuous-stock-pull.js";
9
10
  export { handleFloridayStockReconcile } from "./server-hooks/floriday-stock-reconcile.js";
10
11
  export { handleLogisticsTasksRun } from "./server-hooks/logistics-tasks-run.js";