@flowselections/floriday-afhandeling-module 1.0.58 → 1.0.63

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 (49) hide show
  1. package/dist-lib/components/afhandeling/AutoSendStatus.d.ts +12 -0
  2. package/dist-lib/components/afhandeling/AutoSendStatus.d.ts.map +1 -0
  3. package/dist-lib/components/afhandeling/AutoSendStatus.js +86 -0
  4. package/dist-lib/components/afhandeling/DagDetailDialog.d.ts.map +1 -1
  5. package/dist-lib/components/afhandeling/DagDetailDialog.js +40 -24
  6. package/dist-lib/components/afhandeling/LeveringenPage.d.ts.map +1 -1
  7. package/dist-lib/components/afhandeling/LeveringenPage.js +3 -1
  8. package/dist-lib/components/afhandeling/NuVersturenButton.d.ts.map +1 -1
  9. package/dist-lib/components/afhandeling/NuVersturenButton.js +4 -4
  10. package/dist-lib/components/afhandeling/PickAgendaView.d.ts.map +1 -1
  11. package/dist-lib/components/afhandeling/PickAgendaView.js +9 -7
  12. package/dist-lib/components/afhandeling/ScanControleView.d.ts.map +1 -1
  13. package/dist-lib/components/afhandeling/ScanControleView.js +2 -2
  14. package/dist-lib/components/afhandeling/VerzetOrderDialog.js +3 -3
  15. package/dist-lib/components/afhandeling/WeekAgendaView.d.ts.map +1 -1
  16. package/dist-lib/components/afhandeling/WeekAgendaView.js +60 -10
  17. package/dist-lib/components/settings/AfhandelingSettingsCard.js +1 -1
  18. package/dist-lib/hooks/useConsistencyCheck.d.ts +1 -1
  19. package/dist-lib/hooks/useConsistencyCheck.d.ts.map +1 -1
  20. package/dist-lib/hooks/useConsistencyCheck.js +5 -3
  21. package/dist-lib/hooks/useOrderPlanning.d.ts.map +1 -1
  22. package/dist-lib/hooks/useOrderPlanning.js +7 -6
  23. package/dist-lib/hooks/usePickAgenda.d.ts +4 -0
  24. package/dist-lib/hooks/usePickAgenda.d.ts.map +1 -1
  25. package/dist-lib/hooks/usePickAgenda.js +36 -5
  26. package/dist-lib/integrations/supabase/previewAuthStorage.d.ts.map +1 -1
  27. package/dist-lib/integrations/supabase/previewAuthStorage.js +22 -4
  28. package/dist-lib/integrations/supabase/types.d.ts +609 -2
  29. package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
  30. package/dist-lib/integrations/supabase/types.js +1 -0
  31. package/dist-lib/lib/cartAssignments.functions.d.ts +4 -0
  32. package/dist-lib/lib/cartAssignments.functions.d.ts.map +1 -1
  33. package/dist-lib/lib/cartAssignments.functions.js +5 -1
  34. package/dist-lib/lib/floriday.functions.d.ts +2 -0
  35. package/dist-lib/lib/floriday.functions.d.ts.map +1 -1
  36. package/dist-lib/lib/floriday.functions.js +1 -0
  37. package/dist-lib/lib/floridayLoading.functions.d.ts +18 -0
  38. package/dist-lib/lib/floridayLoading.functions.d.ts.map +1 -1
  39. package/dist-lib/lib/floridayLoading.functions.js +28 -9
  40. package/dist-lib/lib/printing.js +1 -1
  41. package/dist-lib/lib/serverRoutes/autoSend.handler.d.ts +3 -0
  42. package/dist-lib/lib/serverRoutes/autoSend.handler.d.ts.map +1 -0
  43. package/dist-lib/lib/serverRoutes/autoSend.handler.js +123 -0
  44. package/dist-lib/lib/serverRoutes/index.d.ts +1 -0
  45. package/dist-lib/lib/serverRoutes/index.d.ts.map +1 -1
  46. package/dist-lib/lib/serverRoutes/index.js +1 -0
  47. package/dist-lib/moduleDefinition.d.ts.map +1 -1
  48. package/dist-lib/moduleDefinition.js +5 -2
  49. package/package.json +13 -2
@@ -1,5 +1,8 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useEffect, useMemo, useRef, useState } from "react";
3
+ import { useQuery } from "@tanstack/react-query";
4
+ import { supabase } from "../../integrations/supabase/moduleClient";
5
+ import { isExcludedOrderStatus, normalizeOrderStatus } from "../../lib/orderStatus";
3
6
  import { Button, Badge } from "@flowselections/core";
4
7
  import { ChevronLeft, ChevronRight, Package, Truck, Sprout, Clock, CheckCircle2, Scale } from "lucide-react";
5
8
  import { isSameDay, startOfWeek, addDays } from "../../lib/pickDate";
@@ -8,6 +11,20 @@ import { usePickAgenda } from "../../hooks/usePickAgenda";
8
11
  import { useConsistencyCheck } from "../../hooks/useConsistencyCheck";
9
12
  import { CapacityWarning } from "./CapacityWarning";
10
13
  import { toDayKey } from "../../lib/logisticsEngine/capacity";
14
+ const SHIPPED = new Set(["verzonden", "gereed voor facturatie", "gefactureerd", "geleverd", "afgeleverd", "afgerond", "afgehandeld", "afgemeld", "shipped", "delivered", "completed", "invoiced"]);
15
+ /** Eén indeling per order. Floriday herkend op Floriday-id; afmeldstand uit de order. */
16
+ function classify(o, scanned) {
17
+ if (/annul|cancel/i.test(o.status ?? "") || isExcludedOrderStatus(o.status))
18
+ return "uit";
19
+ if (SHIPPED.has(normalizeOrderStatus(o.status))) {
20
+ if (o.salesOrderId && o.floridayFulfilled !== true)
21
+ return "niet_afgemeld";
22
+ return "afgehandeld";
23
+ }
24
+ if (scanned || isOrderReady(o))
25
+ return "verzendklaar";
26
+ return "voorbereiding";
27
+ }
11
28
  const WEEKDAY_LABELS = ["MAANDAG", "DINSDAG", "WOENSDAG", "DONDERDAG", "VRIJDAG", "ZATERDAG", "ZONDAG"];
12
29
  const MONTHS_NL_SHORT = ["jan", "feb", "mrt", "apr", "mei", "jun", "jul", "aug", "sep", "okt", "nov", "dec"];
13
30
  function isOrderReady(o) {
@@ -87,6 +104,30 @@ export function WeekAgendaView({ planned, mode, isLoading, onOrderClick, onDayCl
87
104
  // Pickagenda komt volledig uit de Engine-RPC; deze module rekent zelf geen
88
105
  // pickdagen meer uit en doet geen datum-, werkdagen- of tijdzonelogica.
89
106
  const pickAgenda = usePickAgenda(mode === "pick" ? toDayKey(weekStart) : null, mode === "pick" ? toDayKey(weekEnd) : null);
107
+ // Orders waarvan alle karren goedgekeurd gescand zijn (op order-id).
108
+ const orderIds = useMemo(() => (mode === "pick" ? [] : planned.map((p) => p.order.id).sort()), [planned, mode]);
109
+ const scanQuery = useQuery({
110
+ queryKey: ["afhandeling-scanned-orders", orderIds],
111
+ enabled: orderIds.length > 0,
112
+ staleTime: 30000,
113
+ refetchInterval: 60000,
114
+ queryFn: async () => {
115
+ const all = new Map();
116
+ for (let i = 0; i < orderIds.length; i += 100) {
117
+ const { data, error } = await supabase
118
+ .from("logistics_control_scan").select("order_id, approved_at").in("order_id", orderIds.slice(i, i + 100));
119
+ if (error)
120
+ throw new Error(error.message);
121
+ for (const r of Array.isArray(data) ? data : []) {
122
+ if (!r.order_id)
123
+ continue;
124
+ all.set(r.order_id, (all.get(r.order_id) ?? true) && !!r.approved_at);
125
+ }
126
+ }
127
+ return [...all].filter(([, ok]) => ok).map(([id]) => id);
128
+ },
129
+ });
130
+ const scannedIds = useMemo(() => new Set(scanQuery.data ?? []), [scanQuery.data]);
90
131
  const days = useMemo(() => {
91
132
  return Array.from({ length: 7 }, (_, i) => {
92
133
  const date = addDays(weekStart, i);
@@ -104,6 +145,7 @@ export function WeekAgendaView({ planned, mode, isLoading, onOrderClick, onDayCl
104
145
  voorbereiding: row?.inPreparation ?? 0,
105
146
  // Uitsluitend uit de Engine (taak afgemeld); geannuleerde orders zijn al weggelaten.
106
147
  afgehandeld: row?.doneCount ?? 0,
148
+ nietAfgemeld: 0,
107
149
  };
108
150
  }
109
151
  const entries = [];
@@ -113,7 +155,7 @@ export function WeekAgendaView({ planned, mode, isLoading, onOrderClick, onDayCl
113
155
  const d = dateForPlanned(p, mode);
114
156
  if (!d || !isSameDay(d, date))
115
157
  continue;
116
- if (/annul|cancel/i.test(p.order.status ?? ""))
158
+ if (classify(p.order, false) === "uit")
117
159
  continue;
118
160
  entries.push({
119
161
  order: p.order,
@@ -126,8 +168,10 @@ export function WeekAgendaView({ planned, mode, isLoading, onOrderClick, onDayCl
126
168
  }
127
169
  const karren = entries.reduce((s, e) => s + e.cartCount, 0);
128
170
  const planten = entries.reduce((s, e) => s + e.plantCount, 0);
129
- const verzendklaar = entries.filter((e) => isOrderReady(e.order)).length;
130
- const voorbereiding = entries.length - verzendklaar;
171
+ const cls = entries.map((e) => classify(e.order, scannedIds.has(e.order.id)));
172
+ const cnt = (b) => cls.filter((c) => c === b).length;
173
+ const verzendklaar = cnt("verzendklaar");
174
+ const voorbereiding = cnt("voorbereiding");
131
175
  return {
132
176
  date,
133
177
  entries,
@@ -137,16 +181,19 @@ export function WeekAgendaView({ planned, mode, isLoading, onOrderClick, onDayCl
137
181
  planten,
138
182
  verzendklaar,
139
183
  voorbereiding,
140
- afgehandeld: entries.filter((e) => isDoneStatus(e.order.status)).length,
184
+ afgehandeld: cnt("afgehandeld"),
185
+ nietAfgemeld: cnt("niet_afgemeld"),
141
186
  };
142
187
  });
143
- }, [planned, weekStart, mode, pickAgenda.byDate]);
188
+ }, [planned, weekStart, mode, pickAgenda.byDate, scannedIds]);
144
189
  const totals = useMemo(() => {
145
190
  const leveringen = days.reduce((s, d) => s + d.bestellingen, 0);
146
191
  const verzendklaar = days.reduce((s, d) => s + d.verzendklaar, 0);
147
192
  const voorbereiding = days.reduce((s, d) => s + d.voorbereiding, 0);
148
193
  const totaalStuks = days.reduce((s, d) => s + d.planten, 0);
149
- return { leveringen, verzendklaar, voorbereiding, totaalStuks };
194
+ const afgehandeld = days.reduce((s, d) => s + d.afgehandeld, 0);
195
+ const nietAfgemeld = days.reduce((s, d) => s + d.nietAfgemeld, 0);
196
+ return { leveringen, verzendklaar, voorbereiding, totaalStuks, afgehandeld, nietAfgemeld };
150
197
  }, [days]);
151
198
  const { misaligned } = useConsistencyCheck();
152
199
  const capacity = useCapacityCheck(toDayKey(weekStart), toDayKey(weekEnd));
@@ -158,10 +205,10 @@ export function WeekAgendaView({ planned, mode, isLoading, onOrderClick, onDayCl
158
205
  d.setHours(0, 0, 0, 0);
159
206
  setCursor(startOfWeek(d));
160
207
  };
161
- return (_jsxs("div", { className: "flex h-full flex-col gap-4 overflow-hidden", children: [_jsxs("div", { className: "flex flex-wrap items-center justify-between gap-3", children: [_jsx("h2", { className: "text-xl font-semibold tracking-tight", children: title ?? headerLabel }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsxs(Button, { variant: "outline", size: "sm", onClick: goPrev, children: [_jsx(ChevronLeft, { className: "mr-1 h-4 w-4" }), " Vorige week"] }), _jsx(Button, { variant: "outline", size: "sm", onClick: goToday, children: "Deze week" }), _jsxs(Button, { variant: "outline", size: "sm", onClick: goNext, children: ["Volgende week ", _jsx(ChevronRight, { className: "ml-1 h-4 w-4" })] })] })] }), misaligned && (_jsx("div", { className: "rounded-md border bg-muted/40 px-3 py-2 text-xs text-muted-foreground", children: "Aantallen worden opnieuw uitgelijnd." })), _jsxs("div", { className: "grid shrink-0 grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-4", children: [_jsx(SummaryCard, { label: MODE_LABELS[mode].summary, value: totals.leveringen, tone: "default" }), _jsx(SummaryCard, { label: "Verzendklaar", value: totals.verzendklaar, tone: "success" }), _jsx(SummaryCard, { label: "In voorbereiding", value: totals.voorbereiding, tone: "muted" }), _jsx(SummaryCard, { label: "Totaal stuks", value: totals.totaalStuks, tone: "default" })] }), _jsx("div", { className: "min-h-0 flex-1 space-y-2 overflow-y-auto rounded-lg border bg-muted/20 p-2", children: isLoading || (mode === "pick" && pickAgenda.isLoading) ? (_jsx("div", { className: "rounded-lg border bg-card p-10 text-center text-sm text-muted-foreground", children: "Laden\u2026" })) : (days.map((day) => (_jsx(DayRow, { day: day, onOrderClick: onOrderClick, onDayClick: onDayClick, capacity: mode === "delivery" ? undefined : capacity.byDate.get(toDayKey(day.date)) }, day.date.toISOString())))) })] }));
208
+ return (_jsxs("div", { className: "flex h-full flex-col gap-4 overflow-hidden", children: [_jsxs("div", { className: "flex flex-wrap items-center justify-between gap-3", children: [_jsx("h2", { className: "text-xl font-semibold tracking-tight", children: title ?? headerLabel }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsxs(Button, { variant: "outline", size: "sm", onClick: goPrev, children: [_jsx(ChevronLeft, { className: "mr-1 h-4 w-4" }), " Vorige week"] }), _jsx(Button, { variant: "outline", size: "sm", onClick: goToday, children: "Deze week" }), _jsxs(Button, { variant: "outline", size: "sm", onClick: goNext, children: ["Volgende week ", _jsx(ChevronRight, { className: "ml-1 h-4 w-4" })] })] })] }), misaligned && (_jsx("div", { className: "rounded-md border bg-muted/40 px-3 py-2 text-xs text-muted-foreground", children: "De aantallen worden bijgewerkt." })), _jsxs("div", { className: "grid shrink-0 grid-cols-1 gap-3 sm:grid-cols-3 lg:grid-cols-6", children: [_jsx(SummaryCard, { label: MODE_LABELS[mode].summary, value: totals.leveringen, tone: "default" }), _jsx(SummaryCard, { label: "Verzendklaar", value: totals.verzendklaar, tone: "success" }), _jsx(SummaryCard, { label: "In voorbereiding", value: totals.voorbereiding, tone: "muted" }), _jsx(SummaryCard, { label: "Afgehandeld", value: totals.afgehandeld, tone: "success" }), _jsx(SummaryCard, { label: "Afgehandeld, nog niet gemeld bij Floriday", value: totals.nietAfgemeld, tone: "warning" }), _jsx(SummaryCard, { label: "Totaal stuks", value: totals.totaalStuks, tone: "default" })] }), _jsx("div", { className: "min-h-0 flex-1 space-y-2 overflow-y-auto rounded-lg border bg-muted/20 p-2", children: isLoading || (mode === "pick" && pickAgenda.isLoading) ? (_jsx("div", { className: "rounded-lg border bg-card p-10 text-center text-sm text-muted-foreground", children: "Laden\u2026" })) : (days.map((day) => (_jsx(DayRow, { day: day, onOrderClick: onOrderClick, onDayClick: onDayClick, capacity: mode === "delivery" ? undefined : capacity.byDate.get(toDayKey(day.date)) }, day.date.toISOString())))) })] }));
162
209
  }
163
210
  function SummaryCard({ label, value, tone, }) {
164
- const valueClass = tone === "success" ? "text-green-600" : "text-foreground";
211
+ const valueClass = tone === "success" ? "text-green-600" : tone === "warning" ? "text-amber-600" : "text-foreground";
165
212
  return (_jsxs("div", { className: "rounded-lg border bg-card p-4", children: [_jsx("div", { className: `text-3xl font-bold ${valueClass}`, children: value }), _jsx("div", { className: "mt-1 text-sm text-muted-foreground", children: label })] }));
166
213
  }
167
214
  function DayRow({ day, onOrderClick, onDayClick, capacity, }) {
@@ -170,8 +217,11 @@ function DayRow({ day, onOrderClick, onDayClick, capacity, }) {
170
217
  today.setHours(0, 0, 0, 0);
171
218
  const isToday = isSameDay(day.date, today);
172
219
  // Alles van deze dag is afgehandeld: toon een groene bevestiging.
173
- const dagAfgesloten = day.bestellingen > 0 && day.afgehandeld >= day.bestellingen;
174
- return (_jsxs("div", { className: `rounded-lg border bg-card ${isToday ? "ring-1 ring-primary" : ""}`, children: [_jsxs("div", { className: "flex flex-wrap items-center gap-6 px-4 py-4 sm:px-6", children: [_jsxs("div", { className: "w-20 shrink-0", children: [_jsx("div", { className: "text-xs font-medium tracking-wider text-muted-foreground", children: WEEKDAY_LABELS[weekdayIdx] }), _jsx("div", { className: "mt-1 text-3xl font-bold leading-none", children: day.date.getDate() }), _jsxs("div", { className: "text-sm text-muted-foreground", children: [MONTHS_NL_SHORT[day.date.getMonth()], "."] })] }), _jsxs("div", { className: "flex flex-1 flex-wrap items-center gap-6", children: [_jsx(Stat, { icon: _jsx(Package, { className: "h-4 w-4 text-slate-500" }), bg: "bg-slate-100", value: day.bestellingen, label: "bestellingen" }), _jsx(Stat, { icon: _jsx(Truck, { className: "h-4 w-4 text-amber-600" }), bg: "bg-amber-100", value: day.karren, label: "karren" }), _jsx(Stat, { icon: _jsx(Sprout, { className: "h-4 w-4 text-green-600" }), bg: "bg-green-100", value: day.planten, label: "planten" })] }), _jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [dagAfgesloten && (_jsxs("div", { className: "flex items-center gap-2 rounded-md border border-green-600/30 bg-green-500/15 px-3 py-1.5 text-sm font-medium text-green-700", children: [_jsx(CheckCircle2, { className: "h-4 w-4" }), "Dag is afgesloten"] })), day.shiftedCount > 0 && (_jsxs(Badge, { className: "bg-purple-500/10 text-purple-700 hover:bg-purple-500/10", variant: "secondary", title: "Aantal orders dat te laat is aangemeld en door de Engine is verschoven.", children: [_jsx(Scale, { className: "mr-1 h-3.5 w-3.5" }), day.shiftedCount, " te laat aangemeld"] })), day.verzendklaar > 0 && (_jsxs(Badge, { className: "bg-green-500/15 text-green-700 hover:bg-green-500/15", variant: "secondary", children: [_jsx(CheckCircle2, { className: "mr-1 h-3.5 w-3.5" }), day.verzendklaar, " verzendklaar"] })), day.voorbereiding > 0 && (_jsxs(Badge, { className: "bg-blue-500/10 text-blue-700 hover:bg-blue-500/10", variant: "secondary", children: [_jsx(Clock, { className: "mr-1 h-3.5 w-3.5" }), day.voorbereiding, " in voorbereiding"] })), onDayClick && day.bestellingen > 0 && (_jsx(Button, { size: "sm", variant: "outline", onClick: () => onDayClick(day.date), children: "Dagdetails" }))] })] }), _jsx(CapacityWarning, { day: capacity, className: "mx-4 mb-4 sm:mx-6" })] }));
220
+ const dagAfgesloten = day.bestellingen > 0 && day.afgehandeld >= day.bestellingen && day.nietAfgemeld === 0;
221
+ return (_jsxs("div", { className: `rounded-lg border bg-card ${isToday ? "ring-1 ring-primary" : ""} ${onDayClick && day.bestellingen > 0 ? "cursor-pointer transition-colors hover:bg-muted/40" : ""}`, role: onDayClick && day.bestellingen > 0 ? "button" : undefined, tabIndex: onDayClick && day.bestellingen > 0 ? 0 : undefined, onClick: onDayClick && day.bestellingen > 0 ? () => onDayClick(day.date) : undefined, onKeyDown: onDayClick && day.bestellingen > 0 ? (e) => { if (e.key === "Enter" || e.key === " ") {
222
+ e.preventDefault();
223
+ onDayClick(day.date);
224
+ } } : undefined, children: [_jsxs("div", { className: "flex flex-wrap items-center gap-6 px-4 py-4 sm:px-6", children: [_jsxs("div", { className: "w-20 shrink-0", children: [_jsx("div", { className: "text-xs font-medium tracking-wider text-muted-foreground", children: WEEKDAY_LABELS[weekdayIdx] }), _jsx("div", { className: "mt-1 text-3xl font-bold leading-none", children: day.date.getDate() }), _jsxs("div", { className: "text-sm text-muted-foreground", children: [MONTHS_NL_SHORT[day.date.getMonth()], "."] })] }), _jsxs("div", { className: "flex flex-1 flex-wrap items-center gap-6", children: [_jsx(Stat, { icon: _jsx(Package, { className: "h-4 w-4 text-slate-500" }), bg: "bg-slate-100", value: day.bestellingen, label: "bestellingen" }), _jsx(Stat, { icon: _jsx(Truck, { className: "h-4 w-4 text-amber-600" }), bg: "bg-amber-100", value: day.karren, label: "karren" }), _jsx(Stat, { icon: _jsx(Sprout, { className: "h-4 w-4 text-green-600" }), bg: "bg-green-100", value: day.planten, label: "planten" })] }), _jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [dagAfgesloten && (_jsxs("div", { className: "flex items-center gap-2 rounded-md border border-green-600/30 bg-green-500/15 px-3 py-1.5 text-sm font-medium text-green-700", children: [_jsx(CheckCircle2, { className: "h-4 w-4" }), "Dag is afgesloten"] })), day.shiftedCount > 0 && (_jsxs(Badge, { className: "bg-purple-500/10 text-purple-700 hover:bg-purple-500/10", variant: "secondary", title: "Aantal orders dat te laat is aangemeld en door de planning is verschoven.", children: [_jsx(Scale, { className: "mr-1 h-3.5 w-3.5" }), day.shiftedCount, " te laat aangemeld"] })), day.nietAfgemeld > 0 && (_jsxs(Badge, { className: "bg-amber-500/15 text-amber-700 hover:bg-amber-500/15", variant: "secondary", children: [day.nietAfgemeld, " nog niet afgemeld bij Floriday"] })), day.verzendklaar > 0 && (_jsxs(Badge, { className: "bg-green-500/15 text-green-700 hover:bg-green-500/15", variant: "secondary", children: [_jsx(CheckCircle2, { className: "mr-1 h-3.5 w-3.5" }), day.verzendklaar, " verzendklaar"] })), day.voorbereiding > 0 && (_jsxs(Badge, { className: "bg-blue-500/10 text-blue-700 hover:bg-blue-500/10", variant: "secondary", children: [_jsx(Clock, { className: "mr-1 h-3.5 w-3.5" }), day.voorbereiding, " in voorbereiding"] })), onDayClick && day.bestellingen > 0 && (_jsx(Button, { size: "sm", variant: "outline", onClick: (e) => { e.stopPropagation(); onDayClick(day.date); }, children: "Dagdetails" }))] })] }), _jsx(CapacityWarning, { day: capacity, className: "mx-4 mb-4 sm:mx-6" })] }));
175
225
  }
176
226
  function Stat({ icon, bg, value, label, }) {
177
227
  return (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: `grid h-8 w-8 place-items-center rounded-full ${bg}`, children: icon }), _jsxs("div", { children: [_jsx("div", { className: "text-lg font-bold leading-none", children: value }), _jsx("div", { className: "text-xs text-muted-foreground", children: label })] })] }));
@@ -47,5 +47,5 @@ export function AfhandelingSettingsCard() {
47
47
  toast.error(e instanceof Error ? e.message : "Opslaan mislukt");
48
48
  },
49
49
  });
50
- return (_jsxs(Card, { children: [_jsxs(CardHeader, { children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(CalendarClock, { className: "h-5 w-5 text-primary" }), _jsx(CardTitle, { children: "Afhandeling \u2014 Planning" })] }), _jsx(CardDescription, { children: "De pickplanning (pickdagen, picktijd en verdeling over de week) wordt volledig bepaald door de Logistics Engine. Hieronder staan alleen de instellingen van deze module zelf." })] }), _jsxs(CardContent, { className: "space-y-5", children: [_jsxs("div", { className: "rounded-md border p-4 space-y-4", children: [_jsxs("div", { className: "flex items-start justify-between gap-4", children: [_jsxs("div", { className: "space-y-1", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Mail, { className: "h-4 w-4 text-primary" }), _jsx(Label, { htmlFor: "mailEnabled", className: "font-semibold", children: "Aanmeldmail vervoerders" })] }), _jsx("p", { className: "text-xs text-muted-foreground max-w-prose", children: "Verstuurt automatisch een mail met de te verwachten karren naar de vervoerder v\u00F3\u00F3r de aanmelddeadline van het ophaalmoment. E\u00E9n mail per ophaaldag/-moment per vervoerder." })] }), _jsx(Switch, { id: "mailEnabled", checked: mailEnabled, onCheckedChange: setMailEnabled })] }), _jsxs("div", { className: "space-y-2", children: [_jsx(Label, { htmlFor: "mailLead", children: "Minuten v\u00F3\u00F3r aanmelddeadline versturen" }), _jsx(Input, { id: "mailLead", type: "number", min: 0, max: 1440, value: mailLead, disabled: !mailEnabled, onChange: (e) => setMailLead(Math.max(0, Math.min(1440, Math.floor(Number(e.target.value)) || 0))) })] }), _jsxs("div", { className: "space-y-2", children: [_jsx(Label, { htmlFor: "mailPurpose", children: "E-mail-doeleinde (purpose)" }), _jsx(Input, { id: "mailPurpose", value: mailPurpose, disabled: !mailEnabled, onChange: (e) => setMailPurpose(e.target.value) }), _jsxs("p", { className: "text-xs text-muted-foreground", children: ["Koppel dit doeleinde in de module ", _jsx("strong", { children: "E-mailkoppeling" }), " aan een actief send-adres. Standaard: ", _jsx("code", { children: "afhandeling_carrier_signup" }), "."] })] })] }), _jsxs("div", { className: "rounded-md bg-muted/50 p-3 text-xs text-muted-foreground", children: [_jsx("strong", { children: "Ophaaltijd" }), " en ", _jsx("strong", { children: "Aanmeldtijd" }), " stel je per vervoerder in (werkdagen v\u00F3\u00F3r bezorgdag). De vervoerder die als ", _jsx("strong", { children: "standaard" }), " is gemarkeerd, wordt gebruikt voor orders zonder eigen vervoerder."] }), _jsx(Button, { onClick: () => mutation.mutate(), disabled: mutation.isPending, children: mutation.isPending ? "Opslaan…" : "Opslaan" })] })] }));
50
+ return (_jsxs(Card, { children: [_jsxs(CardHeader, { children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(CalendarClock, { className: "h-5 w-5 text-primary" }), _jsx(CardTitle, { children: "Afhandeling \u2014 Planning" })] }), _jsx(CardDescription, { children: "De pickplanning (pickdagen, picktijd en verdeling over de week) wordt volledig bepaald door de planning. Hieronder staan alleen de instellingen van deze module zelf." })] }), _jsxs(CardContent, { className: "space-y-5", children: [_jsxs("div", { className: "rounded-md border p-4 space-y-4", children: [_jsxs("div", { className: "flex items-start justify-between gap-4", children: [_jsxs("div", { className: "space-y-1", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Mail, { className: "h-4 w-4 text-primary" }), _jsx(Label, { htmlFor: "mailEnabled", className: "font-semibold", children: "Aanmeldmail vervoerders" })] }), _jsx("p", { className: "text-xs text-muted-foreground max-w-prose", children: "Verstuurt automatisch een mail met de te verwachten karren naar de vervoerder v\u00F3\u00F3r de aanmelddeadline van het ophaalmoment. E\u00E9n mail per ophaaldag/-moment per vervoerder." })] }), _jsx(Switch, { id: "mailEnabled", checked: mailEnabled, onCheckedChange: setMailEnabled })] }), _jsxs("div", { className: "space-y-2", children: [_jsx(Label, { htmlFor: "mailLead", children: "Minuten v\u00F3\u00F3r aanmelddeadline versturen" }), _jsx(Input, { id: "mailLead", type: "number", min: 0, max: 1440, value: mailLead, disabled: !mailEnabled, onChange: (e) => setMailLead(Math.max(0, Math.min(1440, Math.floor(Number(e.target.value)) || 0))) })] }), _jsxs("div", { className: "space-y-2", children: [_jsx(Label, { htmlFor: "mailPurpose", children: "E-mail-doeleinde (purpose)" }), _jsx(Input, { id: "mailPurpose", value: mailPurpose, disabled: !mailEnabled, onChange: (e) => setMailPurpose(e.target.value) }), _jsxs("p", { className: "text-xs text-muted-foreground", children: ["Koppel dit doeleinde in de module ", _jsx("strong", { children: "E-mailkoppeling" }), " aan een actief send-adres. Standaard: ", _jsx("code", { children: "afhandeling_carrier_signup" }), "."] })] })] }), _jsxs("div", { className: "rounded-md bg-muted/50 p-3 text-xs text-muted-foreground", children: [_jsx("strong", { children: "Ophaaltijd" }), " en ", _jsx("strong", { children: "Aanmeldtijd" }), " stel je per vervoerder in (werkdagen v\u00F3\u00F3r bezorgdag). De vervoerder die als ", _jsx("strong", { children: "standaard" }), " is gemarkeerd, wordt gebruikt voor orders zonder eigen vervoerder."] }), _jsx(Button, { onClick: () => mutation.mutate(), disabled: mutation.isPending, children: mutation.isPending ? "Opslaan…" : "Opslaan" })] })] }));
51
51
  }
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Eenmalige controle bij het laden van de agenda. Deze module corrigeert niets
2
+ * Controle van de Engine, elke 2 minuten opnieuw opgevraagd. Deze module corrigeert niets
3
3
  * zelf: bij een afwijking tonen we alleen een discrete melding.
4
4
  */
5
5
  export declare function useConsistencyCheck(): {
@@ -1 +1 @@
1
- {"version":3,"file":"useConsistencyCheck.d.ts","sourceRoot":"","sources":["../../src/hooks/useConsistencyCheck.ts"],"names":[],"mappings":"AAGA;;;GAGG;AACH,wBAAgB,mBAAmB;;EAelC"}
1
+ {"version":3,"file":"useConsistencyCheck.d.ts","sourceRoot":"","sources":["../../src/hooks/useConsistencyCheck.ts"],"names":[],"mappings":"AAGA;;;GAGG;AACH,wBAAgB,mBAAmB;;EAiBlC"}
@@ -1,15 +1,17 @@
1
1
  import { useQuery } from "@tanstack/react-query";
2
2
  import { supabase } from "../integrations/supabase/moduleClient";
3
3
  /**
4
- * Eenmalige controle bij het laden van de agenda. Deze module corrigeert niets
4
+ * Controle van de Engine, elke 2 minuten opnieuw opgevraagd. Deze module corrigeert niets
5
5
  * zelf: bij een afwijking tonen we alleen een discrete melding.
6
6
  */
7
7
  export function useConsistencyCheck() {
8
8
  const query = useQuery({
9
9
  queryKey: ["logistics-consistency-check"],
10
- staleTime: Infinity,
10
+ staleTime: 60000,
11
+ refetchInterval: 120000,
12
+ refetchIntervalInBackground: false,
11
13
  retry: false,
12
- refetchOnWindowFocus: false,
14
+ refetchOnWindowFocus: true,
13
15
  queryFn: async () => {
14
16
  const { data, error } = await supabase.rpc("logistics_consistency_check");
15
17
  if (error)
@@ -1 +1 @@
1
- {"version":3,"file":"useOrderPlanning.d.ts","sourceRoot":"","sources":["../../src/hooks/useOrderPlanning.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAK/D,OAAO,EAGL,KAAK,aAAa,EAEnB,MAAM,iBAAiB,CAAC;AAGzB,OAAO,EAAwC,KAAK,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAYtG;;;;;GAKG;AACH,MAAM,MAAM,aAAa,GACrB,SAAS,GACT,iBAAiB,GACjB,gBAAgB,GAChB,oBAAoB,CAAC;AAEzB,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,aAAa,CAAC;IACrB,6EAA6E;IAC7E,QAAQ,EAAE,aAAa,GAAG,IAAI,CAAC;IAC/B,qEAAqE;IACrE,UAAU,EAAE,UAAU,GAAG,IAAI,CAAC;IAC9B,gFAAgF;IAChF,cAAc,EAAE,OAAO,CAAC;IACxB,iDAAiD;IACjD,KAAK,EAAE,aAAa,CAAC;IACrB,uEAAuE;IACvE,cAAc,EAAE,OAAO,CAAC;CACzB;AAED,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,iFAAiF;IACjF,mBAAmB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,6EAA6E;IAC7E,QAAQ,EAAE,YAAY,EAAE,CAAC;IACzB,wEAAwE;IACxE,aAAa,EAAE,YAAY,EAAE,CAAC;IAC9B,wDAAwD;IACxD,iBAAiB,EAAE,YAAY,EAAE,CAAC;IAClC,4EAA4E;IAC5E,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,mEAAmE;IACnE,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAGD,MAAM,WAAW,uBAAuB;IACtC,8EAA8E;IAC9E,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,EAAE,CAAC,EAAE,MAAM,CAAC;CACb;AAED,wBAAgB,gBAAgB,CAAC,KAAK,CAAC,EAAE,uBAAuB,GAAG,sBAAsB,CAkTxF"}
1
+ {"version":3,"file":"useOrderPlanning.d.ts","sourceRoot":"","sources":["../../src/hooks/useOrderPlanning.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAK/D,OAAO,EAGL,KAAK,aAAa,EAEnB,MAAM,iBAAiB,CAAC;AAGzB,OAAO,EAAwC,KAAK,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAYtG;;;;;GAKG;AACH,MAAM,MAAM,aAAa,GACrB,SAAS,GACT,iBAAiB,GACjB,gBAAgB,GAChB,oBAAoB,CAAC;AAEzB,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,aAAa,CAAC;IACrB,6EAA6E;IAC7E,QAAQ,EAAE,aAAa,GAAG,IAAI,CAAC;IAC/B,qEAAqE;IACrE,UAAU,EAAE,UAAU,GAAG,IAAI,CAAC;IAC9B,gFAAgF;IAChF,cAAc,EAAE,OAAO,CAAC;IACxB,iDAAiD;IACjD,KAAK,EAAE,aAAa,CAAC;IACrB,uEAAuE;IACvE,cAAc,EAAE,OAAO,CAAC;CACzB;AAED,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,iFAAiF;IACjF,mBAAmB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,6EAA6E;IAC7E,QAAQ,EAAE,YAAY,EAAE,CAAC;IACzB,wEAAwE;IACxE,aAAa,EAAE,YAAY,EAAE,CAAC;IAC9B,wDAAwD;IACxD,iBAAiB,EAAE,YAAY,EAAE,CAAC;IAClC,4EAA4E;IAC5E,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,mEAAmE;IACnE,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAGD,MAAM,WAAW,uBAAuB;IACtC,8EAA8E;IAC9E,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,EAAE,CAAC,EAAE,MAAM,CAAC;CACb;AAED,wBAAgB,gBAAgB,CAAC,KAAK,CAAC,EAAE,uBAAuB,GAAG,sBAAsB,CAmTxF"}
@@ -11,7 +11,7 @@ import { useCartIndex } from "./useCartIndex";
11
11
  import { emitEngineEvent, listComposeTaskRefs } from "../lib/logisticsEngine/engine";
12
12
  import { orderRefsFor } from "../lib/orderRefs";
13
13
  import { isOpenOrderStatus } from "../lib/orderStatus";
14
- import { MAX_ATTEMPTS, emitQueueStatus, markInFlight, markResult, resetEmitQueue, selectEmitBatch, } from "../lib/logisticsEngine/emitQueue";
14
+ import { emitQueueStatus, markInFlight, markResult, resetEmitQueue, selectEmitBatch, } from "../lib/logisticsEngine/emitQueue";
15
15
  export function useOrderPlanning(range) {
16
16
  const from = range?.from ?? null;
17
17
  const to = range?.to ?? null;
@@ -34,7 +34,8 @@ export function useOrderPlanning(range) {
34
34
  let message = "Onbekende fout";
35
35
  if (e instanceof Response) {
36
36
  const text = await e.clone().text().catch(() => "");
37
- message = `Floriday-aanvraag mislukt (${e.status}): ${text.slice(0, 200) || e.statusText}`;
37
+ message = "Floriday reageert nu niet goed. Probeer het over een paar minuten opnieuw.";
38
+ console.warn(`Floriday-aanvraag mislukt (${e.status}): ${text.slice(0, 200) || e.statusText}`);
38
39
  }
39
40
  else if (e instanceof Error) {
40
41
  message = e.message;
@@ -182,7 +183,7 @@ export function useOrderPlanning(range) {
182
183
  cartIndexes: Array.from({ length: carrierCount }, (_, i) => i),
183
184
  },
184
185
  ],
185
- reason: `Ingepland door de Logistics Engine (taak ${(pickTask ?? task).task_type}, regelversie ${(pickTask ?? task).rule_version ?? "-"}).`,
186
+ reason: `Ingepland door de planning (taak ${(pickTask ?? task).task_type}, regelversie ${(pickTask ?? task).rule_version ?? "-"}).`,
186
187
  carrierName: carrier?.name ?? base.carrierName,
187
188
  };
188
189
  return { order, planning, engineTask: task, awaitingEngine: false, state: "planned", hasComposeTask: true };
@@ -223,7 +224,7 @@ export function useOrderPlanning(range) {
223
224
  if (!batch.length) {
224
225
  const status = emitQueueStatus(refs);
225
226
  if (status.gaveUp.length) {
226
- setSignalError(`Melden bij de Logistics Engine is na ${MAX_ATTEMPTS} pogingen gestopt voor ${status.gaveUp.length} order(s): ${status.gaveUp.slice(0, 5).join(", ")}${status.gaveUp.length > 5 ? "…" : ""}. ${status.lastError ?? ""}`.trim());
227
+ setSignalError(`${status.gaveUp.length} order(s) konden niet automatisch worden ingepland. Open de order en kies ‘Opnieuw inplannen’.`);
227
228
  }
228
229
  return;
229
230
  }
@@ -257,8 +258,8 @@ export function useOrderPlanning(range) {
257
258
  else {
258
259
  const status = emitQueueStatus(refs);
259
260
  setSignalError(status.gaveUp.length
260
- ? `Melden bij de Logistics Engine is na ${MAX_ATTEMPTS} pogingen gestopt voor ${status.gaveUp.length} order(s): ${status.gaveUp.slice(0, 5).join(", ")}${status.gaveUp.length > 5 ? "…" : ""}. ${status.lastError ?? ""}`.trim()
261
- : `Melding aan de Logistics Engine is mislukt (nieuwe poging volgt automatisch). ${result.error ?? ""}`.trim());
261
+ ? `${status.gaveUp.length} order(s) konden niet automatisch worden ingepland. Open de order en kies ‘Opnieuw inplannen’.`
262
+ : "Inplannen lukt nog niet; er volgt automatisch een nieuwe poging.");
262
263
  }
263
264
  }
264
265
  })();
@@ -6,6 +6,10 @@ export interface PickAgendaOrder {
6
6
  carts: number;
7
7
  plants: number;
8
8
  shifted: boolean;
9
+ /** Orders in deze (buffer)taak; leeg = alleen orderRef. */
10
+ bufferOrders: string[];
11
+ /** Unieke kar-id's van deze taak. */
12
+ cartIds: string[];
9
13
  /** Verkoop-module zegt: deze order is afgehandeld (verzonden/geleverd). */
10
14
  fulfilled?: boolean;
11
15
  }
@@ -1 +1 @@
1
- {"version":3,"file":"usePickAgenda.d.ts","sourceRoot":"","sources":["../../src/hooks/usePickAgenda.ts"],"names":[],"mappings":"AAQA,kDAAkD;AAClD,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;IACjB,2EAA2E;IAC3E,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,gDAAgD;AAChD,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,MAAM,CAAC;IACrB,wDAAwD;IACxD,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,eAAe,EAAE,CAAC;CAC3B;AAwCD,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,EAAE,EAAE,EAAE,MAAM,GAAG,IAAI;;;;;EA8GnE"}
1
+ {"version":3,"file":"usePickAgenda.d.ts","sourceRoot":"","sources":["../../src/hooks/usePickAgenda.ts"],"names":[],"mappings":"AAQA,kDAAkD;AAClD,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;IACjB,2DAA2D;IAC3D,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,qCAAqC;IACrC,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,2EAA2E;IAC3E,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,gDAAgD;AAChD,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,MAAM,CAAC;IACrB,wDAAwD;IACxD,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,eAAe,EAAE,CAAC;CAC3B;AA4CD,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,EAAE,EAAE,EAAE,MAAM,GAAG,IAAI;;;;;EAoInE"}
@@ -16,6 +16,10 @@ function toOrders(value) {
16
16
  carts: Number(r.carts ?? 0),
17
17
  plants: Number(r.plants ?? 0),
18
18
  shifted: Boolean(r.shifted),
19
+ bufferOrders: Array.isArray(r.bufferOrders) ? r.bufferOrders.map(String).filter(Boolean) : [],
20
+ cartIds: Array.isArray(r.cartList)
21
+ ? r.cartList.map((c) => c?.cartId).filter((x) => typeof x === "string" && !!x)
22
+ : [],
19
23
  };
20
24
  });
21
25
  }
@@ -110,16 +114,43 @@ export function usePickAgenda(from, to) {
110
114
  if (to && pickDate > to)
111
115
  continue;
112
116
  // "Afgehandeld" volgt uitsluitend de Engine (taak afgemeld), niet de Verkoop-status.
113
- const doneCount = orders.filter((o) => o.status === "done" || o.status === "completed").length;
114
- const ready = orders.filter((o) => o.status === "ready" || o.status === "done").length;
117
+ // Tellen per losse order (ook elke order in een buffer) en per unieke kar-id,
118
+ // gelijk aan Dagdetails. "Afgehandeld" = taak afgemeld of Verkoop-status afgehandeld,
119
+ // gelijk aan Planning en leveringen.
120
+ const refsOf = (o) => (o.bufferOrders.length ? o.bufferOrders : [o.orderRef]);
121
+ const allRefs = new Set();
122
+ const doneRefs = new Set();
123
+ const readyRefs = new Set();
124
+ for (const o of orders) {
125
+ for (const r of refsOf(o)) {
126
+ if (isCancelledRef(r))
127
+ continue;
128
+ allRefs.add(r);
129
+ const engineDone = o.status === "done" || o.status === "completed";
130
+ if (engineDone || isFulfilledRef(r))
131
+ doneRefs.add(r);
132
+ if (o.status === "ready" || engineDone || isFulfilledRef(r))
133
+ readyRefs.add(r);
134
+ }
135
+ }
136
+ const cartIds = new Set();
137
+ let cartsWithoutId = 0;
138
+ for (const o of orders) {
139
+ if (o.cartIds.length)
140
+ o.cartIds.forEach((c) => cartIds.add(c));
141
+ else
142
+ cartsWithoutId += o.carts || 0;
143
+ }
144
+ const doneCount = doneRefs.size;
145
+ const ready = readyRefs.size;
115
146
  out.push({
116
147
  pickDate,
117
148
  orders,
118
- orderCount: orders.length,
119
- cartCount: orders.reduce((s, o) => s + (o.carts || 0), 0),
149
+ orderCount: allRefs.size,
150
+ cartCount: cartIds.size + cartsWithoutId,
120
151
  plantCount: orders.reduce((s, o) => s + (o.plants || 0), 0),
121
152
  ready,
122
- inPreparation: Math.max(0, orders.length - ready),
153
+ inPreparation: Math.max(0, allRefs.size - ready),
123
154
  shiftedCount: orders.filter((o) => o.shifted).length,
124
155
  doneCount,
125
156
  });
@@ -1 +1 @@
1
- {"version":3,"file":"previewAuthStorage.d.ts","sourceRoot":"","sources":["../../../src/integrations/supabase/previewAuthStorage.ts"],"names":[],"mappings":"AAIA,wBAAgB,sBAAsB;mBA2Db,MAAM;mBAeZ,MAAM,SAAS,MAAM;sBASlB,MAAM;cAK3B"}
1
+ {"version":3,"file":"previewAuthStorage.d.ts","sourceRoot":"","sources":["../../../src/integrations/supabase/previewAuthStorage.ts"],"names":[],"mappings":"AAIA,wBAAgB,sBAAsB;mBA6Db,MAAM;mBAwBZ,MAAM,SAAS,MAAM;sBASlB,MAAM;cAM3B"}
@@ -61,21 +61,38 @@ export function brokeredPreviewStorage() {
61
61
  // The editor may not be listening yet at the first getItem, so retry once.
62
62
  let firstGet = true;
63
63
  const RETRY_DELAY = 250;
64
+ // Blocked third-party storage throws here; seeding must not break broker reads.
65
+ const readLocal = (key) => { try {
66
+ return localStorage.getItem(key);
67
+ }
68
+ catch {
69
+ return undefined;
70
+ } };
64
71
  return {
65
72
  getItem: async (key) => {
66
- let res = await request('lovable-preview-auth:get', key);
73
+ // Sent with GET and REMOVE so an editor that never cached this session can check or revoke it.
74
+ const local = readLocal(key);
75
+ let res = await request('lovable-preview-auth:get', key, local ?? undefined);
67
76
  if (!res && firstGet) {
68
77
  await new Promise((r) => setTimeout(r, RETRY_DELAY));
69
- res = await request('lovable-preview-auth:get', key);
78
+ res = await request('lovable-preview-auth:get', key, local ?? undefined);
70
79
  }
71
80
  firstGet = false;
72
81
  // '' is the logout tombstone: clear the local copy too so it can't resurrect if
73
82
  // the broker later goes silent. A null reply means never-synced -> keep local.
74
83
  if (res && res.ok && typeof res.value === 'string') {
75
84
  if (res.value === '') {
76
- localStorage.removeItem(key);
85
+ if (local !== undefined && readLocal(key) === local)
86
+ localStorage.removeItem(key);
77
87
  return null;
78
88
  }
89
+ // Seed this origin so a new tab, whose editor holds no session, stays signed in.
90
+ if (local === null && readLocal(key) === null) {
91
+ try {
92
+ localStorage.setItem(key, res.value);
93
+ }
94
+ catch { /* storage full: still return the editor's session */ }
95
+ }
79
96
  return res.value;
80
97
  }
81
98
  return localStorage.getItem(key);
@@ -92,8 +109,9 @@ export function brokeredPreviewStorage() {
92
109
  });
93
110
  },
94
111
  removeItem: (key) => {
112
+ const local = readLocal(key);
95
113
  localStorage.removeItem(key);
96
- return request('lovable-preview-auth:remove', key).then(() => undefined);
114
+ return request('lovable-preview-auth:remove', key, local ?? undefined).then(() => undefined);
97
115
  },
98
116
  };
99
117
  }