@flowselections/floriday-afhandeling-module 1.0.9 → 1.0.10

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 (72) hide show
  1. package/dist-lib/components/afhandeling/DagDetailDialog.d.ts +9 -0
  2. package/dist-lib/components/afhandeling/DagDetailDialog.d.ts.map +1 -0
  3. package/dist-lib/components/afhandeling/DagDetailDialog.js +145 -0
  4. package/dist-lib/components/afhandeling/LeveringenPage.d.ts.map +1 -1
  5. package/dist-lib/components/afhandeling/LeveringenPage.js +11 -5
  6. package/dist-lib/components/afhandeling/PickAgendaView.d.ts.map +1 -1
  7. package/dist-lib/components/afhandeling/PickAgendaView.js +13 -4
  8. package/dist-lib/components/afhandeling/PlanningAgendaPage.d.ts.map +1 -1
  9. package/dist-lib/components/afhandeling/PlanningAgendaPage.js +2 -2
  10. package/dist-lib/components/afhandeling/ServiceAreaSelect.d.ts +12 -0
  11. package/dist-lib/components/afhandeling/ServiceAreaSelect.d.ts.map +1 -0
  12. package/dist-lib/components/afhandeling/ServiceAreaSelect.js +79 -0
  13. package/dist-lib/components/afhandeling/VervoerderForm.d.ts.map +1 -1
  14. package/dist-lib/components/afhandeling/VervoerderForm.js +53 -6
  15. package/dist-lib/components/afhandeling/VervoerdersPage.d.ts.map +1 -1
  16. package/dist-lib/components/afhandeling/VervoerdersPage.js +23 -5
  17. package/dist-lib/components/afhandeling/WeekAgendaView.d.ts +2 -1
  18. package/dist-lib/components/afhandeling/WeekAgendaView.d.ts.map +1 -1
  19. package/dist-lib/components/afhandeling/WeekAgendaView.js +53 -18
  20. package/dist-lib/components/pickstation/PakbonTemplate.d.ts +5 -2
  21. package/dist-lib/components/pickstation/PakbonTemplate.d.ts.map +1 -1
  22. package/dist-lib/components/pickstation/PakbonTemplate.js +12 -10
  23. package/dist-lib/components/settings/AfhandelingSettingsCard.d.ts.map +1 -1
  24. package/dist-lib/components/settings/AfhandelingSettingsCard.js +18 -6
  25. package/dist-lib/hooks/useOrderPlanning.d.ts +3 -0
  26. package/dist-lib/hooks/useOrderPlanning.d.ts.map +1 -1
  27. package/dist-lib/hooks/useOrderPlanning.js +47 -6
  28. package/dist-lib/integrations/supabase/auth-middleware.d.ts +2261 -136
  29. package/dist-lib/integrations/supabase/auth-middleware.d.ts.map +1 -1
  30. package/dist-lib/integrations/supabase/client.d.ts +2261 -136
  31. package/dist-lib/integrations/supabase/client.d.ts.map +1 -1
  32. package/dist-lib/integrations/supabase/client.server.d.ts +2261 -136
  33. package/dist-lib/integrations/supabase/client.server.d.ts.map +1 -1
  34. package/dist-lib/integrations/supabase/types.d.ts +2329 -134
  35. package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
  36. package/dist-lib/integrations/supabase/types.js +1 -0
  37. package/dist-lib/lib/afhandelingSettings.functions.d.ts +9431 -5172
  38. package/dist-lib/lib/afhandelingSettings.functions.d.ts.map +1 -1
  39. package/dist-lib/lib/afhandelingSettings.functions.js +27 -2
  40. package/dist-lib/lib/carrierSignupMail.server.d.ts +56 -0
  41. package/dist-lib/lib/carrierSignupMail.server.d.ts.map +1 -0
  42. package/dist-lib/lib/carrierSignupMail.server.js +229 -0
  43. package/dist-lib/lib/carriers.functions.d.ts +33377 -15579
  44. package/dist-lib/lib/carriers.functions.d.ts.map +1 -1
  45. package/dist-lib/lib/carriers.functions.js +28 -2
  46. package/dist-lib/lib/cartAssignments.functions.d.ts +46479 -0
  47. package/dist-lib/lib/cartAssignments.functions.d.ts.map +1 -0
  48. package/dist-lib/lib/cartAssignments.functions.js +130 -0
  49. package/dist-lib/lib/cartMoveImpact.d.ts +8 -0
  50. package/dist-lib/lib/cartMoveImpact.d.ts.map +1 -0
  51. package/dist-lib/lib/cartMoveImpact.js +26 -0
  52. package/dist-lib/lib/floriday.functions.d.ts +2265 -136
  53. package/dist-lib/lib/floriday.functions.d.ts.map +1 -1
  54. package/dist-lib/lib/floriday.functions.js +178 -98
  55. package/dist-lib/lib/geo/euServiceAreas.d.ts +30 -0
  56. package/dist-lib/lib/geo/euServiceAreas.d.ts.map +1 -0
  57. package/dist-lib/lib/geo/euServiceAreas.js +277 -0
  58. package/dist-lib/lib/invalidatePlanning.d.ts +11 -0
  59. package/dist-lib/lib/invalidatePlanning.d.ts.map +1 -0
  60. package/dist-lib/lib/invalidatePlanning.js +25 -0
  61. package/dist-lib/lib/pickstationI18n.d.ts +4 -0
  62. package/dist-lib/lib/pickstationI18n.d.ts.map +1 -1
  63. package/dist-lib/lib/pickstationI18n.js +24 -0
  64. package/dist-lib/lib/pickstationOrders.server.d.ts +1 -1
  65. package/dist-lib/lib/pickstationOrders.server.d.ts.map +1 -1
  66. package/dist-lib/lib/pickstationOrders.server.js +305 -177
  67. package/dist-lib/lib/pickstationTypes.d.ts +11 -2
  68. package/dist-lib/lib/pickstationTypes.d.ts.map +1 -1
  69. package/dist-lib/lib/planning.d.ts +18 -2
  70. package/dist-lib/lib/planning.d.ts.map +1 -1
  71. package/dist-lib/lib/planning.js +96 -33
  72. package/package.json +2 -2
@@ -6,7 +6,8 @@ export interface WeekAgendaViewProps {
6
6
  mode: WeekAgendaMode;
7
7
  isLoading?: boolean;
8
8
  onOrderClick?: (order: FloridayOrder) => void;
9
+ onDayClick?: (date: Date) => void;
9
10
  title?: string;
10
11
  }
11
- export declare function WeekAgendaView({ planned, mode, isLoading, onOrderClick, title, }: WeekAgendaViewProps): import("react").JSX.Element;
12
+ export declare function WeekAgendaView({ planned, mode, isLoading, onOrderClick, onDayClick, title, }: WeekAgendaViewProps): import("react").JSX.Element;
12
13
  //# sourceMappingURL=WeekAgendaView.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"WeekAgendaView.d.ts","sourceRoot":"","sources":["../../../src/components/afhandeling/WeekAgendaView.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAElE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAEjE,MAAM,MAAM,cAAc,GAAG,UAAU,GAAG,QAAQ,GAAG,oBAAoB,GAAG,MAAM,CAAC;AAEnF,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,IAAI,EAAE,cAAc,CAAC;IACrB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;IAC9C,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAmDD,wBAAgB,cAAc,CAAC,EAC7B,OAAO,EACP,IAAI,EACJ,SAAS,EACT,YAAY,EACZ,KAAK,GACN,EAAE,mBAAmB,+BA0FrB"}
1
+ {"version":3,"file":"WeekAgendaView.d.ts","sourceRoot":"","sources":["../../../src/components/afhandeling/WeekAgendaView.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAElE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAEjE,MAAM,MAAM,cAAc,GAAG,UAAU,GAAG,QAAQ,GAAG,oBAAoB,GAAG,MAAM,CAAC;AAEnF,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,IAAI,EAAE,cAAc,CAAC;IACrB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;IAC9C,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,CAAC;IAClC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AA4DD,wBAAgB,cAAc,CAAC,EAC7B,OAAO,EACP,IAAI,EACJ,SAAS,EACT,YAAY,EACZ,UAAU,EACV,KAAK,GACN,EAAE,mBAAmB,+BAwHrB"}
@@ -37,7 +37,7 @@ const MODE_LABELS = {
37
37
  "transport-register": { summary: "Aanmeldingen deze week" },
38
38
  pick: { summary: "Te picken deze week" },
39
39
  };
40
- export function WeekAgendaView({ planned, mode, isLoading, onOrderClick, title, }) {
40
+ export function WeekAgendaView({ planned, mode, isLoading, onOrderClick, onDayClick, title, }) {
41
41
  const [cursor, setCursor] = useState(() => {
42
42
  const d = new Date();
43
43
  d.setHours(0, 0, 0, 0);
@@ -49,21 +49,51 @@ export function WeekAgendaView({ planned, mode, isLoading, onOrderClick, title,
49
49
  const days = useMemo(() => {
50
50
  return Array.from({ length: 7 }, (_, i) => {
51
51
  const date = addDays(weekStart, i);
52
- const dayPlanned = planned.filter((p) => {
53
- const d = dateForPlanned(p, mode);
54
- return d ? isSameDay(d, date) : false;
55
- });
56
- const dayOrders = dayPlanned.map((p) => p.order);
57
- const karren = dayOrders.reduce((s, o) => s + (o.lineCount || 0), 0);
58
- const planten = dayOrders.reduce((s, o) => s + (o.lineCount || 0) * 10, 0);
59
- const verzendklaar = dayOrders.filter(isOrderReady).length;
60
- const voorbereiding = dayOrders.length - verzendklaar;
61
- const movedEarlierOrderIds = new Set(dayPlanned.filter((p) => isMovedEarlier(p, mode)).map((p) => p.order.id));
52
+ const entries = [];
53
+ for (const p of planned) {
54
+ if (!p.planning)
55
+ continue;
56
+ if (mode === "pick") {
57
+ const assigns = p.planning.pickAssignments ?? [
58
+ { date: p.planning.scheduledPickDate ?? p.planning.pickStartDate, cartCount: p.order.cartCount ?? 0 },
59
+ ];
60
+ const total = assigns.length;
61
+ assigns.forEach((a, idx) => {
62
+ if (!isSameDay(a.date, date))
63
+ return;
64
+ const totalCarts = p.order.cartCount ?? 0;
65
+ const ratio = totalCarts > 0 ? a.cartCount / totalCarts : 1 / Math.max(total, 1);
66
+ entries.push({
67
+ order: p.order,
68
+ cartCount: a.cartCount,
69
+ plantCount: Math.round((p.order.plantCount ?? 0) * ratio),
70
+ movedEarlier: a.date.getTime() !== p.planning.pickStartDate.getTime(),
71
+ splitPart: total > 1 ? { index: idx + 1, total } : undefined,
72
+ });
73
+ });
74
+ }
75
+ else {
76
+ const d = dateForPlanned(p, mode);
77
+ if (!d || !isSameDay(d, date))
78
+ continue;
79
+ entries.push({
80
+ order: p.order,
81
+ cartCount: p.order.cartCount ?? 0,
82
+ plantCount: p.order.plantCount ?? 0,
83
+ movedEarlier: false,
84
+ });
85
+ }
86
+ }
87
+ const karren = entries.reduce((s, e) => s + e.cartCount, 0);
88
+ const planten = entries.reduce((s, e) => s + e.plantCount, 0);
89
+ const verzendklaar = entries.filter((e) => isOrderReady(e.order)).length;
90
+ const voorbereiding = entries.length - verzendklaar;
91
+ const movedEarlierOrderIds = new Set(entries.filter((e) => e.movedEarlier).map((e) => e.order.id));
62
92
  return {
63
93
  date,
64
- orders: dayOrders,
94
+ entries,
65
95
  movedEarlierOrderIds,
66
- bestellingen: dayOrders.length,
96
+ bestellingen: entries.length,
67
97
  karren,
68
98
  planten,
69
99
  verzendklaar,
@@ -86,20 +116,25 @@ export function WeekAgendaView({ planned, mode, isLoading, onOrderClick, title,
86
116
  d.setHours(0, 0, 0, 0);
87
117
  setCursor(startOfWeek(d));
88
118
  };
89
- return (_jsxs("div", { className: "space-y-4", 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" })] })] })] }), _jsxs("div", { className: "grid 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: "space-y-2", children: 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 }, day.date.toISOString()))) })] }));
119
+ return (_jsxs("div", { className: "space-y-4", 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" })] })] })] }), _jsxs("div", { className: "grid 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: "space-y-2", children: 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 }, day.date.toISOString()))) })] }));
90
120
  }
91
121
  function SummaryCard({ label, value, tone, }) {
92
122
  const valueClass = tone === "success" ? "text-green-600" : "text-foreground";
93
123
  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 })] }));
94
124
  }
95
- function DayRow({ day, onOrderClick }) {
125
+ function DayRow({ day, onOrderClick, onDayClick }) {
96
126
  const weekdayIdx = (day.date.getDay() + 6) % 7;
97
127
  const today = new Date();
98
128
  today.setHours(0, 0, 0, 0);
99
129
  const isToday = isSameDay(day.date, today);
100
- 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: [day.movedEarlierOrderIds.size > 0 && (_jsxs(Badge, { className: "bg-purple-500/10 text-purple-700 hover:bg-purple-500/10", variant: "secondary", title: "Aantal orders dat vervroegd is ingepland door Pickverdeling.", children: [_jsx(Scale, { className: "mr-1 h-3.5 w-3.5" }), day.movedEarlierOrderIds.size, " vervroegd"] })), 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"] }))] })] }), onOrderClick && day.orders.length > 0 && (_jsx("div", { className: "border-t px-4 py-2 sm:px-6", children: _jsx("div", { className: "flex flex-wrap gap-2", children: day.orders.map((o) => {
101
- const moved = day.movedEarlierOrderIds.has(o.id);
102
- return (_jsxs("button", { onClick: () => onOrderClick(o), title: moved ? "Deze order is eerder ingepland vanwege Pickverdeling." : undefined, className: `flex items-center gap-1 rounded border px-2 py-1 text-xs hover:bg-muted ${moved ? "border-purple-300 bg-purple-50" : ""}`, children: [moved && _jsx(Scale, { className: "h-3 w-3 text-purple-600" }), _jsx("span", { className: "font-medium", children: o.orderNumber }), _jsxs("span", { className: "ml-1 text-muted-foreground", children: ["\u00B7 ", o.customerName] })] }, o.id));
130
+ 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: [day.movedEarlierOrderIds.size > 0 && (_jsxs(Badge, { className: "bg-purple-500/10 text-purple-700 hover:bg-purple-500/10", variant: "secondary", title: "Aantal orders dat vervroegd is ingepland door Pickverdeling.", children: [_jsx(Scale, { className: "mr-1 h-3.5 w-3.5" }), day.movedEarlierOrderIds.size, " vervroegd"] })), 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" }))] })] }), onOrderClick && day.entries.length > 0 && (_jsx("div", { className: "border-t px-4 py-2 sm:px-6", children: _jsx("div", { className: "flex flex-wrap gap-2", children: day.entries.map((e, idx) => {
131
+ const moved = e.movedEarlier;
132
+ const split = e.splitPart;
133
+ return (_jsxs("button", { onClick: () => onOrderClick(e.order), title: split
134
+ ? `Deel ${split.index}/${split.total} — ${e.cartCount} karren deze dag (verdeeld picken).`
135
+ : moved
136
+ ? "Deze order is eerder ingepland vanwege Pickverdeling."
137
+ : undefined, className: `flex items-center gap-1 rounded border px-2 py-1 text-xs hover:bg-muted ${moved || split ? "border-purple-300 bg-purple-50" : ""}`, children: [(moved || split) && _jsx(Scale, { className: "h-3 w-3 text-purple-600" }), _jsx("span", { className: "font-medium", children: e.order.orderNumber }), _jsxs("span", { className: "ml-1 text-muted-foreground", children: ["\u00B7 ", e.order.customerName] }), _jsxs("span", { className: "ml-1 text-purple-700", children: ["\u00B7 ", e.cartCount, " karren", split ? ` (deel ${split.index}/${split.total})` : ""] })] }, `${e.order.id}-${idx}`));
103
138
  }) }) }))] }));
104
139
  }
105
140
  function Stat({ icon, bg, value, label, }) {
@@ -1,5 +1,5 @@
1
1
  import type { PickstationOrder } from "../../lib/pickstationTypes";
2
- /** Returns a fully self-contained printable HTML document for one packing slip. */
2
+ /** Returns a fully self-contained printable HTML document for one packing slip per cart. */
3
3
  export declare function renderPakbonHtml(order: {
4
4
  id: string;
5
5
  orderNumber: string;
@@ -7,9 +7,12 @@ export declare function renderPakbonHtml(order: {
7
7
  country: string | null;
8
8
  deliveryDate: string | null;
9
9
  pickDate: string;
10
+ cartIndex: number;
11
+ cartTotal: number;
12
+ cratesOnCart: number | null;
10
13
  lines: {
11
14
  description: string;
12
- quantity: number | null;
15
+ crates: number | null;
13
16
  unit: string | null;
14
17
  }[];
15
18
  }): string;
@@ -1 +1 @@
1
- {"version":3,"file":"PakbonTemplate.d.ts","sourceRoot":"","sources":["../../../src/components/pickstation/PakbonTemplate.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AAEnE,mFAAmF;AACnF,wBAAgB,gBAAgB,CAAC,KAAK,EAAE;IACtC,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,EAAE,CAAC;CAChF,GAAG,MAAM,CAkET;AAYD,YAAY,EAAE,gBAAgB,EAAE,CAAC"}
1
+ {"version":3,"file":"PakbonTemplate.d.ts","sourceRoot":"","sources":["../../../src/components/pickstation/PakbonTemplate.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AAEnE,4FAA4F;AAC5F,wBAAgB,gBAAgB,CAAC,KAAK,EAAE;IACtC,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,KAAK,EAAE;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,EAAE,CAAC;CAC9E,GAAG,MAAM,CAqET;AAWD,YAAY,EAAE,gBAAgB,EAAE,CAAC"}
@@ -1,4 +1,4 @@
1
- /** Returns a fully self-contained printable HTML document for one packing slip. */
1
+ /** Returns a fully self-contained printable HTML document for one packing slip per cart. */
2
2
  export function renderPakbonHtml(order) {
3
3
  const fmt = (d) => {
4
4
  if (!d)
@@ -13,21 +13,24 @@ export function renderPakbonHtml(order) {
13
13
  .map((l) => `
14
14
  <tr>
15
15
  <td>${escapeHtml(l.description)}</td>
16
- <td style="text-align:right">${l.quantity ?? ""}</td>
16
+ <td style="text-align:right">${l.crates ?? ""}</td>
17
17
  <td>${escapeHtml(l.unit ?? "")}</td>
18
18
  </tr>`)
19
19
  .join("")
20
20
  : `<tr><td colspan="3" style="text-align:center;color:#666">Geen regels</td></tr>`;
21
+ const cartLabel = `Kar ${order.cartIndex} / ${order.cartTotal}`;
22
+ const fustenLabel = order.cratesOnCart != null ? `${order.cratesOnCart}` : "—";
21
23
  return `<!doctype html>
22
24
  <html lang="nl">
23
25
  <head>
24
26
  <meta charset="utf-8">
25
- <title>Pakbon ${escapeHtml(order.orderNumber)}</title>
27
+ <title>Pakbon ${escapeHtml(order.orderNumber)} · ${escapeHtml(cartLabel)}</title>
26
28
  <style>
27
29
  @page { size: A4; margin: 16mm; }
28
30
  body { font-family: -apple-system, "Segoe UI", Helvetica, Arial, sans-serif; color:#111; }
29
31
  h1 { margin: 0 0 4px; font-size: 26px; }
30
32
  .sub { color:#444; margin-bottom: 18px; }
33
+ .cart-badge { display:inline-block; margin-left:8px; padding:2px 10px; border-radius:999px; background:#111; color:#fff; font-size:14px; vertical-align:middle; }
31
34
  .grid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px 24px; margin: 18px 0 22px; }
32
35
  .grid div { font-size: 14px; }
33
36
  .label { color:#666; font-size: 11px; text-transform: uppercase; letter-spacing: .04em; }
@@ -35,13 +38,12 @@ export function renderPakbonHtml(order) {
35
38
  table { width:100%; border-collapse: collapse; font-size: 14px; }
36
39
  th, td { padding: 8px 6px; border-bottom: 1px solid #ddd; vertical-align: top; }
37
40
  th { text-align: left; background:#f5f5f5; }
38
- .barcode { margin-top: 24px; font-family: "Libre Barcode 128", monospace; font-size: 56px; line-height: 1; }
39
- .barcode-fallback { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 18px; letter-spacing: 4px; padding: 8px 12px; border: 2px solid #111; display: inline-block; }
41
+ .barcode-fallback { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 18px; letter-spacing: 4px; padding: 8px 12px; border: 2px solid #111; display: inline-block; margin-top: 18px; }
40
42
  .footer { margin-top: 28px; font-size: 11px; color:#666; }
41
43
  </style>
42
44
  </head>
43
45
  <body>
44
- <h1>Pakbon</h1>
46
+ <h1>Pakbon <span class="cart-badge">${escapeHtml(cartLabel)}</span></h1>
45
47
  <div class="sub">Order ${escapeHtml(order.orderNumber)}</div>
46
48
 
47
49
  <div class="grid">
@@ -49,18 +51,18 @@ export function renderPakbonHtml(order) {
49
51
  <div><div class="label">Land</div><div class="value">${escapeHtml(order.country ?? "—")}</div></div>
50
52
  <div><div class="label">Leverdatum</div><div class="value">${fmt(order.deliveryDate)}</div></div>
51
53
  <div><div class="label">Pickdatum</div><div class="value">${fmt(order.pickDate)}</div></div>
54
+ <div><div class="label">Kar</div><div class="value">${escapeHtml(cartLabel)}</div></div>
55
+ <div><div class="label">Fusten op deze kar</div><div class="value">${escapeHtml(fustenLabel)}</div></div>
52
56
  </div>
53
57
 
54
58
  <table>
55
- <thead><tr><th>Omschrijving</th><th style="text-align:right;width:90px">Aantal</th><th style="width:90px">Eenheid</th></tr></thead>
59
+ <thead><tr><th>Omschrijving</th><th style="text-align:right;width:90px">Fusten</th><th style="width:90px">Eenheid</th></tr></thead>
56
60
  <tbody>${rows}</tbody>
57
61
  </table>
58
62
 
59
- <div class="barcode-fallback">*${escapeHtml(order.orderNumber)}*</div>
63
+ <div class="barcode-fallback">*${escapeHtml(order.orderNumber)}-${order.cartIndex}*</div>
60
64
 
61
65
  <div class="footer">Geprint via Pickstation · ${new Date().toLocaleString("nl-NL")}</div>
62
-
63
- <script>window.addEventListener("load", function(){ setTimeout(function(){ try{ window.focus(); window.print(); }catch(e){} }, 100); });</script>
64
66
  </body>
65
67
  </html>`;
66
68
  }
@@ -1 +1 @@
1
- {"version":3,"file":"AfhandelingSettingsCard.d.ts","sourceRoot":"","sources":["../../../src/components/settings/AfhandelingSettingsCard.tsx"],"names":[],"mappings":"AAqBA,wBAAgB,uBAAuB,gCAsItC"}
1
+ {"version":3,"file":"AfhandelingSettingsCard.d.ts","sourceRoot":"","sources":["../../../src/components/settings/AfhandelingSettingsCard.tsx"],"names":[],"mappings":"AAqBA,wBAAgB,uBAAuB,gCAoMtC"}
@@ -3,7 +3,7 @@ import { useEffect, useState } from "react";
3
3
  import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
4
4
  import { useServerFn } from "@tanstack/react-start";
5
5
  import { Card, CardContent, CardDescription, CardHeader, CardTitle, Button, Input, Label, Switch, toast, } from "@flowselections/core";
6
- import { CalendarClock, Scale } from "lucide-react";
6
+ import { CalendarClock, Mail, Scale } from "lucide-react";
7
7
  import { getAfhandelingSettings, updateAfhandelingSettings, } from "../../lib/afhandelingSettings.functions";
8
8
  export function AfhandelingSettingsCard() {
9
9
  const qc = useQueryClient();
@@ -21,6 +21,9 @@ export function AfhandelingSettingsCard() {
21
21
  transportRegisterDaysBefore: 1,
22
22
  pickDistributionEnabled: false,
23
23
  maxWorkdaysEarlierPick: 2,
24
+ signupMailEnabled: false,
25
+ signupMailLeadMinutes: 60,
26
+ signupMailPurpose: "afhandeling_carrier_signup",
24
27
  };
25
28
  }
26
29
  },
@@ -29,12 +32,18 @@ export function AfhandelingSettingsCard() {
29
32
  const [registerDays, setRegisterDays] = useState(1);
30
33
  const [distributionEnabled, setDistributionEnabled] = useState(false);
31
34
  const [maxEarlier, setMaxEarlier] = useState(2);
35
+ const [mailEnabled, setMailEnabled] = useState(false);
36
+ const [mailLead, setMailLead] = useState(60);
37
+ const [mailPurpose, setMailPurpose] = useState("afhandeling_carrier_signup");
32
38
  useEffect(() => {
33
39
  if (data) {
34
40
  setPickDays(data.pickDaysBeforeTransport);
35
41
  setRegisterDays(data.transportRegisterDaysBefore);
36
42
  setDistributionEnabled(Boolean(data.pickDistributionEnabled));
37
43
  setMaxEarlier(data.maxWorkdaysEarlierPick ?? 2);
44
+ setMailEnabled(Boolean(data.signupMailEnabled));
45
+ setMailLead(data.signupMailLeadMinutes ?? 60);
46
+ setMailPurpose(data.signupMailPurpose ?? "afhandeling_carrier_signup");
38
47
  }
39
48
  }, [data]);
40
49
  const mutation = useMutation({
@@ -44,17 +53,20 @@ export function AfhandelingSettingsCard() {
44
53
  transportRegisterDaysBefore: registerDays,
45
54
  pickDistributionEnabled: distributionEnabled,
46
55
  maxWorkdaysEarlierPick: maxEarlier,
56
+ signupMailEnabled: mailEnabled,
57
+ signupMailLeadMinutes: mailLead,
58
+ signupMailPurpose: mailPurpose,
47
59
  },
48
60
  }),
49
- onSuccess: () => {
50
- qc.invalidateQueries({ queryKey: ["afhandeling", "settings"] });
51
- qc.invalidateQueries({ queryKey: ["floriday", "orders"] });
52
- toast.success("Instellingen opgeslagen");
61
+ onSuccess: async () => {
62
+ const { invalidatePlanning } = await import("../../lib/invalidatePlanning");
63
+ await invalidatePlanning(qc);
64
+ toast.success("Instellingen opgeslagen — planning herberekend");
53
65
  },
54
66
  onError: (e) => {
55
67
  toast.error(e instanceof Error ? e.message : "Opslaan mislukt");
56
68
  },
57
69
  });
58
70
  const clamp = (n) => Math.max(0, Math.min(30, Math.floor(n) || 0));
59
- 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: "Centrale werkdag-instellingen voor de automatische order\u00ADplanning. Alle berekeningen tellen alleen werkdagen (ma\u2013vr)." })] }), _jsxs(CardContent, { className: "space-y-5", children: [_jsxs("div", { className: "space-y-2", children: [_jsx(Label, { htmlFor: "pickDays", children: "Aantal pickdagen (werkdagen v\u00F3\u00F3r ophaaldag)" }), _jsx(Input, { id: "pickDays", type: "number", min: 0, max: 30, value: pickDays, onChange: (e) => setPickDays(clamp(Number(e.target.value))) }), _jsx("p", { className: "text-xs text-muted-foreground", children: "Wordt vanaf de berekende ophaaldag teruggeteld om de uiterste pickdatum te bepalen." })] }), _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(Scale, { className: "h-4 w-4 text-primary" }), _jsx(Label, { htmlFor: "pickDistribution", className: "font-semibold", children: "Pickverdeling" })] }), _jsx("p", { className: "text-xs text-muted-foreground max-w-prose", children: "Verdeelt de pickbelasting automatisch over werkdagen. Orders mogen eerder ingepland worden, maar nooit later dan de berekende uiterste pickdatum \u2014 leverbetrouwbaarheid blijft gegarandeerd." })] }), _jsx(Switch, { id: "pickDistribution", checked: distributionEnabled, onCheckedChange: setDistributionEnabled })] }), _jsxs("div", { className: "space-y-2", children: [_jsx(Label, { htmlFor: "maxEarlier", children: "Maximaal aantal werkdagen eerder picken" }), _jsx(Input, { id: "maxEarlier", type: "number", min: 0, max: 30, value: maxEarlier, disabled: !distributionEnabled, onChange: (e) => setMaxEarlier(clamp(Number(e.target.value))) }), _jsx("p", { className: "text-xs text-muted-foreground", children: "0 = geen vervroeging. Bijv. 2 = orders mogen maximaal 2 werkdagen voor hun uiterste pickdatum ingepland worden." })] })] }), _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" })] })] }));
71
+ 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: "Centrale werkdag-instellingen voor de automatische order\u00ADplanning. Alle berekeningen tellen alleen werkdagen (ma\u2013vr)." })] }), _jsxs(CardContent, { className: "space-y-5", children: [_jsxs("div", { className: "space-y-2", children: [_jsx(Label, { htmlFor: "pickDays", children: "Aantal pickdagen (werkdagen v\u00F3\u00F3r ophaaldag)" }), _jsx(Input, { id: "pickDays", type: "number", min: 0, max: 30, value: pickDays, onChange: (e) => setPickDays(clamp(Number(e.target.value))) }), _jsx("p", { className: "text-xs text-muted-foreground", children: "Wordt vanaf de berekende ophaaldag teruggeteld om de uiterste pickdatum te bepalen." })] }), _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(Scale, { className: "h-4 w-4 text-primary" }), _jsx(Label, { htmlFor: "pickDistribution", className: "font-semibold", children: "Pickverdeling" })] }), _jsx("p", { className: "text-xs text-muted-foreground max-w-prose", children: "Verdeelt de pickbelasting automatisch over werkdagen. Orders mogen eerder ingepland worden, maar nooit later dan de berekende uiterste pickdatum \u2014 leverbetrouwbaarheid blijft gegarandeerd." })] }), _jsx(Switch, { id: "pickDistribution", checked: distributionEnabled, onCheckedChange: setDistributionEnabled })] }), _jsxs("div", { className: "space-y-2", children: [_jsx(Label, { htmlFor: "maxEarlier", children: "Maximaal aantal werkdagen eerder picken" }), _jsx(Input, { id: "maxEarlier", type: "number", min: 0, max: 30, value: maxEarlier, disabled: !distributionEnabled, onChange: (e) => setMaxEarlier(clamp(Number(e.target.value))) }), _jsx("p", { className: "text-xs text-muted-foreground", children: "0 = geen vervroeging. Bijv. 2 = orders mogen maximaal 2 werkdagen voor hun uiterste pickdatum ingepland worden." })] })] }), _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" })] })] }));
60
72
  }
@@ -1,4 +1,5 @@
1
1
  import type { FloridayOrder } from "../lib/floriday.functions";
2
+ import { type CartOverride } from "../lib/cartAssignments.functions";
2
3
  import { type OrderPlanning } from "../lib/planning";
3
4
  export interface PlannedOrder {
4
5
  order: FloridayOrder;
@@ -6,8 +7,10 @@ export interface PlannedOrder {
6
7
  }
7
8
  export interface UseOrderPlanningResult {
8
9
  planned: PlannedOrder[];
10
+ overrides: CartOverride[];
9
11
  isLoading: boolean;
10
12
  error?: string;
13
+ warning?: string;
11
14
  refetch: () => void;
12
15
  }
13
16
  export declare function useOrderPlanning(): UseOrderPlanningResult;
@@ -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;AAI/D,OAAO,EAIL,KAAK,aAAa,EAEnB,MAAM,iBAAiB,CAAC;AAEzB,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,aAAa,CAAC;IACrB,QAAQ,EAAE,aAAa,GAAG,IAAI,CAAC;CAChC;AAED,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAED,wBAAgB,gBAAgB,IAAI,sBAAsB,CA+EzD"}
1
+ {"version":3,"file":"useOrderPlanning.d.ts","sourceRoot":"","sources":["../../src/hooks/useOrderPlanning.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAI/D,OAAO,EAAqB,KAAK,YAAY,EAAE,MAAM,kCAAkC,CAAC;AACxF,OAAO,EAIL,KAAK,aAAa,EAEnB,MAAM,iBAAiB,CAAC;AAEzB,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,aAAa,CAAC;IACrB,QAAQ,EAAE,aAAa,GAAG,IAAI,CAAC;CAChC;AAED,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,SAAS,EAAE,YAAY,EAAE,CAAC;IAC1B,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAED,wBAAgB,gBAAgB,IAAI,sBAAsB,CAuHzD"}
@@ -4,16 +4,25 @@ import { useServerFn } from "@tanstack/react-start";
4
4
  import { getFloridayOrders } from "../lib/floriday.functions";
5
5
  import { getAfhandelingSettings } from "../lib/afhandelingSettings.functions";
6
6
  import { listCarriers } from "../lib/carriers.functions";
7
+ import { listCartOverrides } from "../lib/cartAssignments.functions";
7
8
  import { calculateOrderPlanning, distributePickDates, DEFAULT_PICKUP_MOMENT, } from "../lib/planning";
8
9
  export function useOrderPlanning() {
9
10
  const fetchOrders = useServerFn(getFloridayOrders);
10
11
  const fetchSettings = useServerFn(getAfhandelingSettings);
11
12
  const fetchCarriers = useServerFn(listCarriers);
13
+ const fetchOverrides = useServerFn(listCartOverrides);
12
14
  const ordersQuery = useQuery({
13
15
  queryKey: ["floriday", "orders"],
14
16
  queryFn: async () => {
15
17
  try {
16
- return await fetchOrders();
18
+ const result = await fetchOrders();
19
+ if (result.error) {
20
+ console.warn("[useOrderPlanning] orders loaded with warning", {
21
+ message: result.error,
22
+ orderCount: result.orders.length,
23
+ });
24
+ }
25
+ return result;
17
26
  }
18
27
  catch (e) {
19
28
  let message = "Onbekende fout";
@@ -27,6 +36,10 @@ export function useOrderPlanning() {
27
36
  else {
28
37
  message = String(e);
29
38
  }
39
+ console.error("[useOrderPlanning] orders load failed", {
40
+ message,
41
+ action: "fetch planning orders",
42
+ });
30
43
  return { orders: [], error: message };
31
44
  }
32
45
  },
@@ -55,6 +68,18 @@ export function useOrderPlanning() {
55
68
  },
56
69
  retry: false,
57
70
  });
71
+ const overridesQuery = useQuery({
72
+ queryKey: ["cart-overrides"],
73
+ queryFn: async () => {
74
+ try {
75
+ return await fetchOverrides();
76
+ }
77
+ catch {
78
+ return { overrides: [] };
79
+ }
80
+ },
81
+ retry: false,
82
+ });
58
83
  const planned = useMemo(() => {
59
84
  const orders = ordersQuery.data?.orders ?? [];
60
85
  const settings = settingsQuery.data ?? {
@@ -64,10 +89,18 @@ export function useOrderPlanning() {
64
89
  maxWorkdaysEarlierPick: 2,
65
90
  };
66
91
  const carriers = carriersQuery.data?.carriers ?? [];
92
+ const overrides = overridesQuery.data?.overrides ?? [];
67
93
  const defaultCarrier = carriers.find((c) => c.is_default);
68
94
  const moments = defaultCarrier && defaultCarrier.pickup_moments.length > 0
69
95
  ? defaultCarrier.pickup_moments
70
96
  : [DEFAULT_PICKUP_MOMENT];
97
+ const overrideMap = new Map();
98
+ for (const o of overrides) {
99
+ overrideMap.set(`${o.order_source}#${o.order_id}#${o.cart_index}`, {
100
+ date: new Date(o.pick_date),
101
+ movedFromDate: o.moved_from_date ? new Date(o.moved_from_date) : null,
102
+ });
103
+ }
71
104
  const initial = orders.map((order) => ({
72
105
  order,
73
106
  planning: calculateOrderPlanning(order.deliveryDate, {
@@ -76,15 +109,23 @@ export function useOrderPlanning() {
76
109
  orderCreatedAt: order.createdAt,
77
110
  }),
78
111
  }));
79
- return distributePickDates(initial, {
112
+ const distributed = distributePickDates(initial, {
80
113
  enabled: Boolean(settings.pickDistributionEnabled),
81
114
  maxWorkdaysEarlier: settings.maxWorkdaysEarlierPick ?? 0,
115
+ getCartCount: (it) => it.order.cartCount ?? 1,
116
+ getCartOverride: (it, idx) => {
117
+ const src = it.order.source;
118
+ return overrideMap.get(`${src}#${it.order.id}#${idx}`) ?? null;
119
+ },
82
120
  });
83
- }, [ordersQuery.data, settingsQuery.data, carriersQuery.data]);
121
+ return distributed;
122
+ }, [ordersQuery.data, settingsQuery.data, carriersQuery.data, overridesQuery.data]);
84
123
  return {
85
124
  planned,
86
- isLoading: ordersQuery.isLoading || settingsQuery.isLoading || carriersQuery.isLoading,
87
- error: ordersQuery.data?.error,
88
- refetch: () => { void ordersQuery.refetch(); },
125
+ overrides: overridesQuery.data?.overrides ?? [],
126
+ isLoading: ordersQuery.isLoading || settingsQuery.isLoading || carriersQuery.isLoading || overridesQuery.isLoading,
127
+ error: ordersQuery.data?.orders?.length ? undefined : ordersQuery.data?.error,
128
+ warning: ordersQuery.data?.orders?.length ? ordersQuery.data?.error : undefined,
129
+ refetch: () => { void ordersQuery.refetch(); void overridesQuery.refetch(); },
89
130
  };
90
131
  }