@flowselections/floriday-afhandeling-module 1.0.63 → 1.0.66

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 (41) hide show
  1. package/dist-lib/components/afhandeling/DagDetailDialog.d.ts +4 -0
  2. package/dist-lib/components/afhandeling/DagDetailDialog.d.ts.map +1 -1
  3. package/dist-lib/components/afhandeling/DagDetailDialog.js +27 -11
  4. package/dist-lib/components/afhandeling/LeveringenPage.d.ts.map +1 -1
  5. package/dist-lib/components/afhandeling/LeveringenPage.js +24 -2
  6. package/dist-lib/components/afhandeling/SignupMailMoments.d.ts +8 -0
  7. package/dist-lib/components/afhandeling/SignupMailMoments.d.ts.map +1 -0
  8. package/dist-lib/components/afhandeling/SignupMailMoments.js +75 -0
  9. package/dist-lib/components/afhandeling/VervoerderForm.d.ts.map +1 -1
  10. package/dist-lib/components/afhandeling/VervoerderForm.js +14 -7
  11. package/dist-lib/components/afhandeling/WeekAgendaView.d.ts.map +1 -1
  12. package/dist-lib/components/afhandeling/WeekAgendaView.js +2 -1
  13. package/dist-lib/components/settings/AfhandelingSettingsCard.d.ts.map +1 -1
  14. package/dist-lib/components/settings/AfhandelingSettingsCard.js +47 -2
  15. package/dist-lib/hooks/useOrderPlanning.d.ts.map +1 -1
  16. package/dist-lib/hooks/useOrderPlanning.js +20 -5
  17. package/dist-lib/integrations/supabase/types.d.ts +359 -0
  18. package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
  19. package/dist-lib/lib/carrierPlanning.d.ts +4 -0
  20. package/dist-lib/lib/carrierPlanning.d.ts.map +1 -1
  21. package/dist-lib/lib/carrierPlanning.js +14 -8
  22. package/dist-lib/lib/carrierSignup.functions.d.ts +102 -0
  23. package/dist-lib/lib/carrierSignup.functions.d.ts.map +1 -0
  24. package/dist-lib/lib/carrierSignup.functions.js +119 -0
  25. package/dist-lib/lib/carriers.functions.d.ts.map +1 -1
  26. package/dist-lib/lib/carriers.functions.js +13 -6
  27. package/dist-lib/lib/pickWorkdays.d.ts +24 -0
  28. package/dist-lib/lib/pickWorkdays.d.ts.map +1 -0
  29. package/dist-lib/lib/pickWorkdays.js +30 -0
  30. package/dist-lib/lib/pickstationOrders.server.d.ts.map +1 -1
  31. package/dist-lib/lib/pickstationOrders.server.js +9 -3
  32. package/dist-lib/lib/planning.d.ts +19 -0
  33. package/dist-lib/lib/planning.d.ts.map +1 -1
  34. package/dist-lib/lib/planning.js +58 -11
  35. package/dist-lib/lib/serverRoutes/dispatchCarrierSignups.handler.d.ts +1 -1
  36. package/dist-lib/lib/serverRoutes/dispatchCarrierSignups.handler.d.ts.map +1 -1
  37. package/dist-lib/lib/serverRoutes/dispatchCarrierSignups.handler.js +2 -325
  38. package/dist-lib/lib/signupMailText.d.ts +4 -0
  39. package/dist-lib/lib/signupMailText.d.ts.map +1 -0
  40. package/dist-lib/lib/signupMailText.js +37 -0
  41. package/package.json +1 -1
@@ -4,6 +4,10 @@ export interface DagDetailDialogProps {
4
4
  date: Date | null;
5
5
  planned: PlannedOrder[];
6
6
  onOpenChange: (open: boolean) => void;
7
+ /** Waar de dag op telt: leverdag (Planning en leveringen) of pickdag (Pick Agenda). */
8
+ basis?: "lever" | "pick";
9
+ /** Order (id) die uitgelicht wordt, bv. vanuit een link. */
10
+ highlightOrderId?: string | null;
7
11
  }
8
12
  export declare function DagDetailDialog(props: DagDetailDialogProps): import("react").JSX.Element | null;
9
13
  //# sourceMappingURL=DagDetailDialog.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"DagDetailDialog.d.ts","sourceRoot":"","sources":["../../../src/components/afhandeling/DagDetailDialog.tsx"],"names":[],"mappings":"AA0BA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AA4JjE,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,OAAO,CAAC;IACd,IAAI,EAAE,IAAI,GAAG,IAAI,CAAC;IAClB,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;CACvC;AAQD,wBAAgB,eAAe,CAAC,KAAK,EAAE,oBAAoB,sCAG1D"}
1
+ {"version":3,"file":"DagDetailDialog.d.ts","sourceRoot":"","sources":["../../../src/components/afhandeling/DagDetailDialog.tsx"],"names":[],"mappings":"AA0BA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AA8JjE,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,OAAO,CAAC;IACd,IAAI,EAAE,IAAI,GAAG,IAAI,CAAC;IAClB,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACtC,uFAAuF;IACvF,KAAK,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;IACzB,4DAA4D;IAC5D,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAClC;AAUD,wBAAgB,eAAe,CAAC,KAAK,EAAE,oBAAoB,sCAG1D"}
@@ -9,10 +9,12 @@ import { ResponsiveDialogBody, ResponsiveDialogContent, ResponsiveDialogFooter,
9
9
  import { usePickAgenda } from "../../hooks/usePickAgenda";
10
10
  import { useOrderBuffers, useBufferMembers } from "../../hooks/useOrderBuffers";
11
11
  import { isSameDay } from "../../lib/pickDate";
12
+ import { isExcludedOrderStatus } from "../../lib/orderStatus";
12
13
  import { getOrderLines, getDayCartContents, getOrderCartIds, getPakbonCartInfo, } from "../../lib/cartAssignments.functions";
13
14
  import { printLayoutDocument, printPdfDocument, printFoutMelding } from "../../lib/printing";
14
15
  import { NuVersturenButton } from "./NuVersturenButton";
15
16
  import { AutoSendStatus } from "./AutoSendStatus";
17
+ import { SignupMailMoments } from "./SignupMailMoments";
16
18
  import { CapacityWarning } from "./CapacityWarning";
17
19
  import { useCapacityCheck } from "../../hooks/useCapacityCheck";
18
20
  import { toDayKey } from "../../lib/logisticsEngine/capacity";
@@ -78,6 +80,7 @@ function StickerMeldingen({ orderId }) {
78
80
  return null;
79
81
  return (_jsx("ul", { className: "space-y-1", children: q.data.map((m, i) => (_jsxs("li", { className: "flex items-start gap-1 text-xs text-destructive", children: [_jsx(AlertTriangle, { className: "mt-0.5 h-3 w-3 shrink-0" }), _jsx("span", { children: m.melding })] }, (m.stickerId ?? "") + i))) }));
80
82
  }
83
+ const HighlightCtx = createContext(null);
81
84
  const NL_DATE = new Intl.DateTimeFormat("nl-NL", { dateStyle: "full" });
82
85
  function refKey(value) {
83
86
  return (value ?? "").trim().toUpperCase();
@@ -87,7 +90,7 @@ export function DagDetailDialog(props) {
87
90
  return null;
88
91
  return _jsx(DagDetailContent, { ...props, date: props.date });
89
92
  }
90
- function DagDetailContent({ open, date, planned, onOpenChange, }) {
93
+ function DagDetailContent({ open, date, planned, onOpenChange, basis = "pick", highlightOrderId, }) {
91
94
  const dayKey = toDayKey(date);
92
95
  const agenda = usePickAgenda(dayKey, dayKey);
93
96
  const row = agenda.byDate.get(dayKey);
@@ -101,16 +104,28 @@ function DagDetailContent({ open, date, planned, onOpenChange, }) {
101
104
  plannedByRef.set(k, p);
102
105
  }
103
106
  }
104
- const ordersOnDay = (row?.orders ?? []).map((o) => ({
105
- ref: o.orderRef,
106
- carts: o.carts,
107
- plants: o.plants,
108
- status: o.status,
109
- planned: plannedByRef.get(refKey(o.orderRef)) ?? null,
110
- }));
107
+ // Leverdag: dezelfde orders als de dagkaart in Planning en leveringen.
108
+ const ordersOnDay = basis === "lever"
109
+ ? planned
110
+ .filter((p) => p.planning?.deliveryDate && isSameDay(p.planning.deliveryDate, date))
111
+ .filter((p) => !/annul|cancel/i.test(p.order.status ?? "") && !isExcludedOrderStatus(p.order.status))
112
+ .map((p) => ({
113
+ ref: p.order.orderNumber || p.order.id,
114
+ carts: (p.planning?.pickAssignments ?? []).reduce((s, a) => s + a.cartCount, 0),
115
+ plants: p.order.plantCount ?? 0,
116
+ status: p.order.status ?? "",
117
+ planned: p,
118
+ }))
119
+ : (row?.orders ?? []).map((o) => ({
120
+ ref: o.orderRef,
121
+ carts: o.carts,
122
+ plants: o.plants,
123
+ status: o.status,
124
+ planned: plannedByRef.get(refKey(o.orderRef)) ?? null,
125
+ }));
111
126
  // Aanvullen met lokaal ingeplande orders die de Engine-rij (nog) niet noemt.
112
127
  const seen = new Set(ordersOnDay.map((o) => refKey(o.ref)));
113
- for (const p of planned) {
128
+ for (const p of basis === "lever" ? [] : planned) {
114
129
  const assigns = p.planning?.pickAssignments ?? [];
115
130
  const today = assigns.find((a) => isSameDay(a.date, date));
116
131
  if (!today)
@@ -180,7 +195,7 @@ function DagDetailContent({ open, date, planned, onOpenChange, }) {
180
195
  }
181
196
  // Tel karren op unieke kar-id (gedeelde bufferkar één keer); rijen zonder id tellen los.
182
197
  const uniekeKarren = new Set(cartRows.map((c) => c.cartId ?? `zonder-id:${c.key}`)).size;
183
- return (_jsx(ShipStateCtx.Provider, { value: shipState, children: _jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(ResponsiveDialogContent, { size: "xl", children: [_jsxs(ResponsiveDialogHeader, { children: [_jsxs(DialogTitle, { children: ["Dagdetails \u2014 ", NL_DATE.format(date)] }), _jsxs(DialogDescription, { children: [ordersOnDay.length, " bestellingen \u00B7 ", uniekeKarren, " ", uniekeKarren === 1 ? "kar" : "karren", " \u00B7", " ", ordersOnDay.filter((o) => voortgang(o.status).done).length, " van ", ordersOnDay.length, " ", "bestellingen afgehandeld \u00B7 ", new Set(cartRows.filter((c) => voortgang(c.status).done).map((c) => c.cartId ?? `zonder-id:${c.key}`)).size, " van", " ", uniekeKarren, " karren afgehandeld"] })] }), _jsxs(ResponsiveDialogBody, { children: [_jsx(DayCapacityWarning, { date: date }), _jsx(BezettingCard, { date: date, cartsToday: uniekeKarren }), _jsxs(Tabs, { defaultValue: "bestellingen", children: [_jsxs(TabsList, { children: [_jsxs(TabsTrigger, { value: "bestellingen", children: ["Bestellingen (", ordersOnDay.length, ")"] }), _jsxs(TabsTrigger, { value: "karren", children: ["Karren (", uniekeKarren, ")"] })] }), _jsx(TabsContent, { value: "bestellingen", className: "mt-4", children: _jsx(BestellingenTab, { orders: ordersOnDay, isLoading: agenda.isLoading }) }), _jsx(TabsContent, { value: "karren", className: "mt-4", children: _jsx(KarrenTab, { carts: cartRows }) })] })] }), _jsx(ResponsiveDialogFooter, { children: _jsx(Button, { variant: "outline", onClick: () => onOpenChange(false), children: "Sluiten" }) })] }) }) }));
198
+ return (_jsx(HighlightCtx.Provider, { value: highlightOrderId ?? null, children: _jsx(ShipStateCtx.Provider, { value: shipState, children: _jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(ResponsiveDialogContent, { size: "xl", children: [_jsxs(ResponsiveDialogHeader, { children: [_jsxs(DialogTitle, { children: ["Dagdetails \u2014 ", NL_DATE.format(date), " (", basis === "lever" ? "leverdag" : "pickdag", ")"] }), _jsxs(DialogDescription, { children: [basis === "lever" ? "Bestellingen die op deze dag worden geleverd. " : "Bestellingen die op deze dag worden gepickt. ", ordersOnDay.length, " ", ordersOnDay.length === 1 ? "bestelling" : "bestellingen", " \u00B7 ", uniekeKarren, " ", uniekeKarren === 1 ? "kar" : "karren", " \u00B7", " ", ordersOnDay.filter((o) => voortgang(o.status).done).length, " van ", ordersOnDay.length, " ", ordersOnDay.length === 1 ? "bestelling" : "bestellingen", " afgehandeld \u00B7 ", new Set(cartRows.filter((c) => voortgang(c.status).done).map((c) => c.cartId ?? `zonder-id:${c.key}`)).size, " van", " ", uniekeKarren, " ", uniekeKarren === 1 ? "kar" : "karren", " afgehandeld"] })] }), _jsxs(ResponsiveDialogBody, { children: [_jsx(DayCapacityWarning, { date: date }), _jsx(BezettingCard, { date: date, cartsToday: uniekeKarren }), _jsx(SignupMailMoments, { from: dayKey, to: dayKey }), _jsxs(Tabs, { defaultValue: "bestellingen", children: [_jsxs(TabsList, { children: [_jsxs(TabsTrigger, { value: "bestellingen", children: ["Bestellingen (", ordersOnDay.length, ")"] }), _jsxs(TabsTrigger, { value: "karren", children: ["Karren (", uniekeKarren, ")"] })] }), _jsx(TabsContent, { value: "bestellingen", className: "mt-4", children: _jsx(BestellingenTab, { orders: ordersOnDay, isLoading: agenda.isLoading }) }), _jsx(TabsContent, { value: "karren", className: "mt-4", children: _jsx(KarrenTab, { carts: cartRows }) })] })] }), _jsx(ResponsiveDialogFooter, { children: _jsx(Button, { variant: "outline", onClick: () => onOpenChange(false), children: "Sluiten" }) })] }) }) }) }));
184
199
  }
185
200
  function BezettingCard({ date, cartsToday }) {
186
201
  const queryClient = useQueryClient();
@@ -646,7 +661,8 @@ function OrderRow({ planned: p, cartsToday, engineStatus, }) {
646
661
  setPrinting(false);
647
662
  }
648
663
  };
649
- return (_jsxs("div", { className: "rounded-lg border bg-card", children: [_jsxs("div", { className: "flex flex-col gap-3 p-3", children: [_jsxs("button", { type: "button", onClick: () => setOpen((o) => !o), className: "-m-1 flex items-start gap-3 rounded-md p-1 text-left hover:bg-muted/50", children: [open ? (_jsx(ChevronDown, { className: "mt-1 h-4 w-4 shrink-0" })) : (_jsx(ChevronRight, { className: "mt-1 h-4 w-4 shrink-0" })), _jsxs("div", { className: "min-w-0 flex-1 space-y-2", children: [_jsxs("div", { className: "flex flex-wrap items-baseline gap-x-6 gap-y-1 text-sm", children: [_jsxs("div", { className: "min-w-0", children: [_jsx("div", { className: "font-medium", children: p.order.orderNumber }), _jsx("div", { className: "truncate text-xs text-muted-foreground", children: p.order.customerName })] }), _jsxs("div", { className: "text-xs text-muted-foreground", children: ["Type ", _jsx("span", { className: "text-foreground", children: p.order.deliveryType })] }), _jsxs("div", { className: "text-xs text-muted-foreground", children: ["Leverdatum", " ", _jsx("span", { className: "text-foreground", children: p.order.deliveryDate
664
+ const highlighted = useContext(HighlightCtx) === p.order.id;
665
+ return (_jsxs("div", { className: `rounded-lg border bg-card ${highlighted ? "ring-2 ring-primary" : ""}`, ref: highlighted ? (el) => el?.scrollIntoView({ block: "center" }) : undefined, children: [_jsxs("div", { className: "flex flex-col gap-3 p-3", children: [_jsxs("button", { type: "button", onClick: () => setOpen((o) => !o), className: "-m-1 flex items-start gap-3 rounded-md p-1 text-left hover:bg-muted/50", children: [open ? (_jsx(ChevronDown, { className: "mt-1 h-4 w-4 shrink-0" })) : (_jsx(ChevronRight, { className: "mt-1 h-4 w-4 shrink-0" })), _jsxs("div", { className: "min-w-0 flex-1 space-y-2", children: [_jsxs("div", { className: "flex flex-wrap items-baseline gap-x-6 gap-y-1 text-sm", children: [_jsxs("div", { className: "min-w-0", children: [_jsx("div", { className: "font-medium", children: p.order.orderNumber }), _jsx("div", { className: "truncate text-xs text-muted-foreground", children: p.order.customerName })] }), _jsxs("div", { className: "text-xs text-muted-foreground", children: ["Type ", _jsx("span", { className: "text-foreground", children: p.order.deliveryType })] }), _jsxs("div", { className: "text-xs text-muted-foreground", children: ["Leverdatum", " ", _jsx("span", { className: "text-foreground", children: p.order.deliveryDate
650
666
  ? new Date(p.order.deliveryDate).toLocaleDateString("nl-NL")
651
667
  : "—" })] })] }), _jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsxs(Badge, { variant: "secondary", children: [cartsToday, " ", cartsToday === 1 ? "kar" : "karren", " vandaag"] }), _jsx(StatusBadge, { vg: vg })] }), _jsx(StickerMeldingen, { orderId: p.order.id })] })] }), _jsxs("div", { className: "flex flex-wrap items-center gap-2 border-t pt-3", children: [p.order.orderNumber && p.order.salesOrderId && !shipStateRow?.floridaySent ? _jsx(NuVersturenButton, { orderRef: p.order.orderNumber }) : null, p.order.salesOrderId ? _jsx(AutoSendStatus, { orderId: p.order.id, orderRef: p.order.orderNumber }) : null, _jsx(AfhandelenButton, { orderRef: p.order.orderNumber, done: vg.done }), _jsxs(Button, { size: "sm", variant: "outline", onClick: handlePrintAll, disabled: printing, title: "Alle documenten van deze bestelling afdrukken (pakbonnen per kar en de verzendbrief)", children: [_jsx(Printer, { className: "mr-1 h-3.5 w-3.5" }), printing ? "In de Printwachtrij…" : "Alle documenten afdrukken"] }), _jsxs(Button, { size: "sm", variant: "outline", onClick: (e) => {
652
668
  e.stopPropagation();
@@ -1 +1 @@
1
- {"version":3,"file":"LeveringenPage.d.ts","sourceRoot":"","sources":["../../../src/components/afhandeling/LeveringenPage.tsx"],"names":[],"mappings":"AAOA,wBAAgB,cAAc,gCAqC7B"}
1
+ {"version":3,"file":"LeveringenPage.d.ts","sourceRoot":"","sources":["../../../src/components/afhandeling/LeveringenPage.tsx"],"names":[],"mappings":"AAmBA,wBAAgB,cAAc,gCAmD7B"}
@@ -1,14 +1,36 @@
1
1
  import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
2
- import { useMemo, useState } from "react";
2
+ import { useEffect, useMemo, useState } from "react";
3
3
  import { Button } from "@flowselections/core";
4
4
  import { WeekAgendaView } from "./WeekAgendaView";
5
5
  import { DagDetailDialog } from "./DagDetailDialog";
6
6
  import { useOrderPlanning } from "../../hooks/useOrderPlanning";
7
7
  import { agendaRange, currentWeekStart } from "../../lib/agendaRange";
8
+ import { startOfWeek } from "../../lib/pickDate";
9
+ /** Leest ?dag=JJJJ-MM-DD&order=<order_id> (link vanuit bv. Dashboards). */
10
+ function readLinkParams() {
11
+ const sp = new URLSearchParams(window.location.search);
12
+ const dag = sp.get("dag");
13
+ const order = sp.get("order");
14
+ const m = dag && /^(\d{4})-(\d{2})-(\d{2})$/.exec(dag);
15
+ const day = m ? new Date(Number(m[1]), Number(m[2]) - 1, Number(m[3])) : null;
16
+ const orderId = order && /^[0-9a-f-]{36}$/i.test(order) ? order : null;
17
+ return { day: day && !Number.isNaN(day.getTime()) ? day : null, orderId };
18
+ }
8
19
  export function LeveringenPage() {
9
20
  const [weekStart, setWeekStart] = useState(() => currentWeekStart());
10
21
  const range = useMemo(() => agendaRange(weekStart), [weekStart]);
11
22
  const { planned, nearestDeliveryDate, isLoading, error, warning, refetch } = useOrderPlanning(range);
12
23
  const [dayDetailDate, setDayDetailDate] = useState(null);
13
- return (_jsxs("main", { className: "p-6 space-y-6", children: [warning && !error && (_jsxs("div", { className: "rounded-md border border-amber-300 bg-amber-50 p-4 text-sm text-amber-900", children: [warning, " De beschikbare orders blijven zichtbaar."] })), error ? (_jsxs("div", { className: "rounded-md border border-destructive/30 bg-destructive/5 p-4 text-sm text-destructive", children: [error, _jsx(Button, { size: "sm", variant: "outline", className: "ml-3", onClick: () => refetch(), children: "Opnieuw proberen" })] })) : (_jsx(WeekAgendaView, { planned: planned, mode: "delivery", isLoading: isLoading, onWeekChange: setWeekStart, onDayClick: setDayDetailDate, jumpToDate: nearestDeliveryDate })), _jsx(DagDetailDialog, { open: !!dayDetailDate, date: dayDetailDate, planned: planned, onOpenChange: (o) => !o && setDayDetailDate(null) })] }));
24
+ const [highlightOrderId, setHighlightOrderId] = useState(null);
25
+ const [linkDay, setLinkDay] = useState(null);
26
+ useEffect(() => {
27
+ const { day, orderId } = readLinkParams();
28
+ if (day) {
29
+ setLinkDay(new URLSearchParams(window.location.search).get("dag"));
30
+ setWeekStart(startOfWeek(day));
31
+ setDayDetailDate(day);
32
+ setHighlightOrderId(orderId);
33
+ }
34
+ }, []);
35
+ return (_jsxs("main", { className: "p-6 space-y-6", children: [warning && !error && (_jsxs("div", { className: "rounded-md border border-amber-300 bg-amber-50 p-4 text-sm text-amber-900", children: [warning, " De beschikbare orders blijven zichtbaar."] })), error ? (_jsxs("div", { className: "rounded-md border border-destructive/30 bg-destructive/5 p-4 text-sm text-destructive", children: [error, _jsx(Button, { size: "sm", variant: "outline", className: "ml-3", onClick: () => refetch(), children: "Opnieuw proberen" })] })) : (_jsx(WeekAgendaView, { planned: planned, mode: "delivery", isLoading: isLoading, onWeekChange: setWeekStart, onDayClick: (d) => { setHighlightOrderId(null); setDayDetailDate(d); }, jumpToDate: linkDay ?? nearestDeliveryDate })), _jsx(DagDetailDialog, { open: !!dayDetailDate, date: dayDetailDate, planned: planned, basis: "lever", highlightOrderId: highlightOrderId, onOpenChange: (o) => !o && setDayDetailDate(null) })] }));
14
36
  }
@@ -0,0 +1,8 @@
1
+ interface Props {
2
+ from: string;
3
+ to: string;
4
+ compact?: boolean;
5
+ }
6
+ export declare function SignupMailMoments({ from, to, compact }: Props): import("react").JSX.Element | null;
7
+ export {};
8
+ //# sourceMappingURL=SignupMailMoments.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SignupMailMoments.d.ts","sourceRoot":"","sources":["../../../src/components/afhandeling/SignupMailMoments.tsx"],"names":[],"mappings":"AAqBA,UAAU,KAAK;IACb,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,wBAAgB,iBAAiB,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,KAAK,sCAyC7D"}
@@ -0,0 +1,75 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useState } from "react";
3
+ import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
4
+ import { useServerFn } from "@tanstack/react-start";
5
+ import { Button, Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, toast, } from "@flowselections/core";
6
+ import { Mail } from "lucide-react";
7
+ import { getSignupOverview, previewSignupMail, sendSignupMailNow, } from "../../lib/carrierSignup.functions";
8
+ import { fmtDayTime, fmtTime, signupReasonText } from "../../lib/signupMailText";
9
+ export function SignupMailMoments({ from, to, compact }) {
10
+ const fetchOverview = useServerFn(getSignupOverview);
11
+ const q = useQuery({
12
+ queryKey: ["afhandeling", "signup-overview", from, to],
13
+ queryFn: () => fetchOverview({ data: { from, to } }),
14
+ refetchInterval: 60000,
15
+ });
16
+ const [preview, setPreview] = useState(null);
17
+ if (q.isLoading)
18
+ return null;
19
+ if (q.isError) {
20
+ return _jsx("div", { className: "rounded-md border px-3 py-2 text-xs text-muted-foreground", children: "Aanmeldmail: stand onbekend." });
21
+ }
22
+ const ov = q.data;
23
+ if (!ov)
24
+ return null;
25
+ const tz = ov.timezone;
26
+ const reason = ov.ok ? (ov.enabled ? signupReasonText(ov.reason) : null) : signupReasonText(ov.reason);
27
+ const unassigned = ov.unassigned.length;
28
+ if (ov.groups.length === 0 && unassigned === 0 && !reason)
29
+ return null;
30
+ return (_jsxs("div", { className: "space-y-2 rounded-md border p-3", children: [_jsxs("div", { className: "flex items-center gap-2 text-sm font-medium", children: [_jsx(Mail, { className: "h-4 w-4 text-primary" }), " Aanmeldmail vervoerders"] }), !ov.enabled ? _jsx("p", { className: "text-xs text-muted-foreground", children: "De aanmeldmail staat uit." }) : null, reason && ov.enabled ? _jsx("p", { className: "text-xs text-destructive", children: reason }) : null, unassigned > 0 ? (_jsxs("p", { className: "text-xs text-destructive", children: [unassigned, " ", unassigned === 1 ? "order heeft" : "orders hebben", " geen vervoerder. Stel een standaardvervoerder in bij Vervoerders."] })) : null, _jsx("ul", { className: "space-y-1", children: ov.groups.map((g) => (_jsx(MomentRow, { g: g, tz: tz, compact: compact, enabled: ov.enabled, onPreview: () => setPreview(g) }, `${g.carrier_id}-${g.pickup_date}-${g.pickup_moment_key}`))) }), preview ? _jsx(PreviewDialog, { g: preview, onClose: () => setPreview(null) }) : null] }));
31
+ }
32
+ const kar = (n) => `${n} ${n === 1 ? "kar" : "karren"}`;
33
+ function statusLine(g, tz, enabled) {
34
+ const moment = compactMoment(g, tz);
35
+ if (g.last) {
36
+ if (g.last.fingerprint_match) {
37
+ return `${moment} — Al verstuurd om ${fmtTime(g.last.sent_at, tz)} naar ${g.carrier_name} · ${kar(g.last.cart_count)} — er is niets veranderd`;
38
+ }
39
+ return `${moment} — Aanmeldmail verstuurd om ${fmtTime(g.last.sent_at, tz)} naar ${g.carrier_name} · ${kar(g.last.cart_count)} · wijziging volgt: nu ${kar(g.cart_count)}`;
40
+ }
41
+ if (!enabled)
42
+ return `${moment} — ${g.carrier_name} · ${kar(g.cart_count)}`;
43
+ return `${moment} — Aanmeldmail volgt om ${fmtTime(g.send_at, tz)} naar ${g.carrier_name} · ${kar(g.cart_count)}`;
44
+ }
45
+ function compactMoment(g, tz) {
46
+ return `Ophalen ${fmtDayTime(g.pickup_at, tz)}`;
47
+ }
48
+ function MomentRow({ g, tz, compact, enabled, onPreview }) {
49
+ const qc = useQueryClient();
50
+ const send = useServerFn(sendSignupMailNow);
51
+ const m = useMutation({
52
+ mutationFn: () => send({ data: { carrierId: g.carrier_id, pickupDate: g.pickup_date, momentKey: g.pickup_moment_key } }),
53
+ onSuccess: (r) => {
54
+ if (r.code === "sent")
55
+ toast.success("Aanmeldmail verstuurd");
56
+ else if (r.code === "already_sent")
57
+ toast.info("Deze stand is al verstuurd");
58
+ else
59
+ toast.error(signupReasonText(r.code) ?? "Versturen lukte niet");
60
+ void qc.invalidateQueries({ queryKey: ["afhandeling", "signup-overview"] });
61
+ void qc.invalidateQueries({ queryKey: ["afhandeling", "signup-status"] });
62
+ },
63
+ onError: (e) => toast.error(e instanceof Error ? e.message : "Versturen lukte niet"),
64
+ });
65
+ const upToDate = g.last?.fingerprint_match === true;
66
+ return (_jsxs("li", { className: "flex flex-wrap items-center justify-between gap-2 text-xs", children: [_jsx("span", { children: statusLine(g, tz, enabled) }), compact ? null : (_jsxs("span", { className: "flex gap-2", children: [_jsx(Button, { size: "sm", variant: "outline", onClick: onPreview, children: "Voorbeeld bekijken" }), _jsx(Button, { size: "sm", disabled: !enabled || upToDate || m.isPending, onClick: () => m.mutate(), children: m.isPending ? "Versturen…" : "Nu versturen" })] }))] }));
67
+ }
68
+ function PreviewDialog({ g, onClose }) {
69
+ const fetchPreview = useServerFn(previewSignupMail);
70
+ const q = useQuery({
71
+ queryKey: ["afhandeling", "signup-preview", g.carrier_id, g.pickup_date, g.pickup_moment_key],
72
+ queryFn: () => fetchPreview({ data: { carrierId: g.carrier_id, pickupDate: g.pickup_date, momentKey: g.pickup_moment_key } }),
73
+ });
74
+ return (_jsx(Dialog, { open: true, onOpenChange: (o) => (!o ? onClose() : undefined), children: _jsxs(DialogContent, { className: "max-w-2xl", children: [_jsxs(DialogHeader, { children: [_jsx(DialogTitle, { children: "Voorbeeld aanmeldmail" }), _jsxs(DialogDescription, { children: [q.data?.to ? _jsxs(_Fragment, { children: ["Gaat naar ", _jsx("strong", { children: q.data.to }), "."] }) : "Ontvanger wordt geladen…", q.data?.already_sent ? " Deze stand is al verstuurd." : null] })] }), q.isLoading ? _jsx("p", { className: "text-sm text-muted-foreground", children: "Laden\u2026" }) : null, q.data && !q.data.ok ? _jsx("p", { className: "text-sm text-destructive", children: signupReasonText(q.data.reason) }) : null, q.data?.ok ? (_jsxs("div", { className: "space-y-2", children: [_jsx("p", { className: "text-sm font-medium", children: q.data.subject }), _jsx("pre", { className: "max-h-[50vh] overflow-auto whitespace-pre-wrap rounded-md bg-muted p-3 text-xs", children: q.data.text })] })) : null] }) }));
75
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"VervoerderForm.d.ts","sourceRoot":"","sources":["../../../src/components/afhandeling/VervoerderForm.tsx"],"names":[],"mappings":"AA+CA,UAAU,KAAK;IACb,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AA2PD,wBAAgB,cAAc,CAAC,EAAE,SAAS,EAAE,EAAE,KAAK,+BAyalD"}
1
+ {"version":3,"file":"VervoerderForm.d.ts","sourceRoot":"","sources":["../../../src/components/afhandeling/VervoerderForm.tsx"],"names":[],"mappings":"AA+CA,UAAU,KAAK;IACb,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAqQD,wBAAgB,cAAc,CAAC,EAAE,SAAS,EAAE,EAAE,KAAK,+BAiblD"}
@@ -18,7 +18,7 @@ const EMPTY = {
18
18
  address: "",
19
19
  notes: "",
20
20
  is_default: false,
21
- pickup_moments: [DEFAULT_PICKUP_MOMENT],
21
+ pickup_moments: [{ ...DEFAULT_PICKUP_MOMENT }],
22
22
  service_countries: [],
23
23
  service_provinces: [],
24
24
  service_cities: [],
@@ -45,8 +45,8 @@ function CountryPicker({ value, onChange, excluded, }) {
45
45
  setQuery("");
46
46
  }, className: "flex items-center justify-between w-full px-3 py-2 text-sm text-left hover:bg-muted", children: [_jsx("span", { children: c.name }), _jsx("span", { className: "text-xs text-muted-foreground", children: c.code })] }, c.code)))) })] })] }));
47
47
  }
48
- function PickupMomentRow({ label, days, time, maxDays, onDaysChange, onTimeChange, }) {
49
- return (_jsxs("div", { className: "grid grid-cols-[110px,1fr,1fr] items-center gap-3", children: [_jsx("div", { className: "text-sm font-medium", children: label }), _jsxs("div", { className: "space-y-1", children: [_jsx(Label, { className: "text-xs text-muted-foreground", children: "Werkdagen v\u00F3\u00F3r levering" }), _jsx(IconInput, { icon: Calendar, type: "number", min: 0, max: maxDays, value: days, onChange: (e) => onDaysChange(Number(e.target.value) || 0) })] }), _jsxs("div", { className: "space-y-1", children: [_jsx(Label, { className: "text-xs text-muted-foreground", children: "Tijdstip" }), _jsx(IconInput, { icon: Clock, type: "time", value: time, onChange: (e) => onTimeChange(e.target.value) })] })] }));
48
+ function PickupMomentRow({ label, days, time, maxDays, onDaysChange, onTimeChange, showError, }) {
49
+ return (_jsxs("div", { className: "grid grid-cols-[110px,1fr,1fr] items-center gap-3", children: [_jsx("div", { className: "text-sm font-medium", children: label }), _jsxs("div", { className: "space-y-1", children: [_jsx(Label, { className: "text-xs text-muted-foreground", children: "Werkdagen v\u00F3\u00F3r levering" }), _jsx(IconInput, { icon: Calendar, type: "number", min: 0, max: maxDays, value: days, onChange: (e) => onDaysChange(Number(e.target.value) || 0) })] }), _jsxs("div", { className: "space-y-1", children: [_jsx(Label, { className: "text-xs text-muted-foreground", children: "Tijdstip" }), _jsx(IconInput, { icon: Clock, type: "time", value: time, required: true, "aria-invalid": showError && !time ? true : undefined, onChange: (e) => onTimeChange(e.target.value) }), showError && !time ? _jsx("p", { className: "text-xs text-destructive", children: "Vul een tijd in." }) : null] })] }));
50
50
  }
51
51
  // Normaliseer een ophaalmoment: aanmelden moet altijd vóór (of op) ophalen
52
52
  // liggen, ophalen vóór (of op) bezorgen. Werkdagen-waardes zijn afstanden tot
@@ -73,13 +73,13 @@ function normalizeMoment(next, prev) {
73
73
  }
74
74
  return m;
75
75
  }
76
- function PickupMomentEditor({ index, value, canDelete, onChange, onDelete, }) {
76
+ function PickupMomentEditor({ index, value, canDelete, onChange, onDelete, showErrors, }) {
77
77
  const update = (patch) => {
78
78
  const next = { ...value, ...patch };
79
79
  onChange(normalizeMoment(next, value));
80
80
  };
81
81
  const pickupInvalid = value.pickup_days > value.signup_days;
82
- return (_jsxs("div", { className: "rounded-md border bg-background p-4 space-y-3", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { className: "text-sm font-semibold", children: ["Ophaalmoment ", index + 1] }), canDelete ? (_jsxs("button", { type: "button", onClick: onDelete, className: "text-xs text-muted-foreground hover:text-destructive inline-flex items-center gap-1", "aria-label": "Ophaalmoment verwijderen", children: [_jsx(Trash2, { className: "w-3.5 h-3.5" }), "Verwijderen"] })) : null] }), _jsxs("div", { className: "space-y-3", children: [_jsx(PickupMomentRow, { label: "Aanmelden", days: value.signup_days, time: value.signup_time, onDaysChange: (n) => update({ signup_days: n }), onTimeChange: (v) => update({ signup_time: v }) }), _jsx(PickupMomentRow, { label: "Ophalen", days: value.pickup_days, time: value.pickup_time, maxDays: value.signup_days, onDaysChange: (n) => update({ pickup_days: Math.min(n, value.signup_days) }), onTimeChange: (v) => update({ pickup_time: v }) }), pickupInvalid ? (_jsx("p", { className: "text-xs text-destructive -mt-1 pl-[122px]", children: "Ophalen kan niet v\u00F3\u00F3r aanmelden plaatsvinden." })) : null, _jsx(PickupMomentRow, { label: "Bezorgen", days: value.delivery_days, time: value.delivery_time, maxDays: value.pickup_days, onDaysChange: (n) => update({ delivery_days: Math.min(n, value.pickup_days) }), onTimeChange: (v) => update({ delivery_time: v }) })] })] }));
82
+ return (_jsxs("div", { className: "rounded-md border bg-background p-4 space-y-3", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { className: "text-sm font-semibold", children: ["Ophaalmoment ", index + 1] }), canDelete ? (_jsxs("button", { type: "button", onClick: onDelete, className: "text-xs text-muted-foreground hover:text-destructive inline-flex items-center gap-1", "aria-label": "Ophaalmoment verwijderen", children: [_jsx(Trash2, { className: "w-3.5 h-3.5" }), "Verwijderen"] })) : null] }), _jsxs("div", { className: "space-y-3", children: [_jsx(PickupMomentRow, { label: "Aanmelden", days: value.signup_days, time: value.signup_time, onDaysChange: (n) => update({ signup_days: n }), onTimeChange: (v) => update({ signup_time: v }), showError: showErrors }), _jsx(PickupMomentRow, { label: "Ophalen", days: value.pickup_days, time: value.pickup_time, maxDays: value.signup_days, onDaysChange: (n) => update({ pickup_days: Math.min(n, value.signup_days) }), onTimeChange: (v) => update({ pickup_time: v }), showError: showErrors }), pickupInvalid ? (_jsx("p", { className: "text-xs text-destructive -mt-1 pl-[122px]", children: "Ophalen kan niet v\u00F3\u00F3r aanmelden plaatsvinden." })) : null, _jsx(PickupMomentRow, { label: "Bezorgen", days: value.delivery_days, time: value.delivery_time, maxDays: value.pickup_days, onDaysChange: (n) => update({ delivery_days: Math.min(n, value.pickup_days) }), onTimeChange: (v) => update({ delivery_time: v }), showError: showErrors })] })] }));
83
83
  }
84
84
  export function VervoerderForm({ carrierId }) {
85
85
  const isEdit = !!carrierId;
@@ -89,6 +89,7 @@ export function VervoerderForm({ carrierId }) {
89
89
  const saveCarrier = useServerFn(upsertCarrier);
90
90
  const [form, setForm] = useState(EMPTY);
91
91
  const [rates, setRates] = useState([]);
92
+ const [showTimeErrors, setShowTimeErrors] = useState(false);
92
93
  const [pendingCountry, setPendingCountry] = useState("");
93
94
  const [pendingPrice, setPendingPrice] = useState("");
94
95
  const { data, isLoading } = useQuery({
@@ -110,7 +111,7 @@ export function VervoerderForm({ carrierId }) {
110
111
  is_default: c.is_default ?? false,
111
112
  pickup_moments: c.pickup_moments && c.pickup_moments.length > 0
112
113
  ? c.pickup_moments
113
- : [DEFAULT_PICKUP_MOMENT],
114
+ : [{ ...DEFAULT_PICKUP_MOMENT }],
114
115
  service_countries: c.service_countries ?? [],
115
116
  service_provinces: c.service_provinces ?? [],
116
117
  service_cities: c.service_cities ?? [],
@@ -173,6 +174,12 @@ export function VervoerderForm({ carrierId }) {
173
174
  toast.error("Bedrijfsnaam is verplicht");
174
175
  return;
175
176
  }
177
+ // Tijden zijn verplicht: er wordt nooit stil een tijd ingevuld.
178
+ if (form.pickup_moments.some((m) => !m.signup_time || !m.pickup_time || !m.delivery_time)) {
179
+ setShowTimeErrors(true);
180
+ toast.error("Vul bij elk ophaalmoment alle tijden in.");
181
+ return;
182
+ }
176
183
  mutation.mutate();
177
184
  };
178
185
  if (isEdit && isLoading) {
@@ -180,7 +187,7 @@ export function VervoerderForm({ carrierId }) {
180
187
  }
181
188
  return (_jsxs("main", { className: "p-6 space-y-6 max-w-4xl", children: [_jsx(Button, { asChild: true, variant: "ghost", size: "sm", children: _jsxs(Link, { to: "/afhandeling/vervoerders", children: [_jsx(ArrowLeft, { className: "mr-2 h-4 w-4" }), "Terug naar vervoerders"] }) }), _jsxs("div", { className: "flex items-start gap-3", children: [_jsx(FieldIcon, { icon: Truck }), _jsxs("div", { children: [_jsx("h1", { className: "text-2xl font-semibold", children: isEdit ? "Vervoerder bewerken" : "Vervoerder aanmaken" }), _jsx("p", { className: "text-sm text-muted-foreground", children: isEdit
182
189
  ? "Wijzig de gegevens van de vervoerder."
183
- : "Voeg een nieuwe vervoerder toe en stel verzendtarieven in." })] })] }), _jsxs("form", { onSubmit: handleSubmit, className: "space-y-6", children: [_jsxs(Card, { children: [_jsx(CardHeader, { children: _jsx(CardTitle, { className: "text-base", children: "Algemene gegevens" }) }), _jsxs(CardContent, { className: "space-y-4", children: [_jsxs("div", { className: "space-y-2", children: [_jsxs(Label, { children: ["Bedrijfsnaam ", _jsx("span", { className: "text-destructive", children: "*" })] }), _jsx(IconInput, { icon: Truck, value: form.name, onChange: (e) => setForm({ ...form, name: e.target.value }), placeholder: "Bijv. DHL Freight", required: true })] }), _jsxs("div", { className: "space-y-2", children: [_jsx(Label, { children: "Contactpersoon" }), _jsx(IconInput, { icon: User, value: form.contact_person ?? "", onChange: (e) => setForm({ ...form, contact_person: e.target.value }) })] }), _jsxs("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4", children: [_jsxs("div", { className: "space-y-2", children: [_jsx(Label, { children: "E-mail" }), _jsx(IconInput, { icon: Mail, type: "email", value: form.email ?? "", onChange: (e) => setForm({ ...form, email: e.target.value }) })] }), _jsxs("div", { className: "space-y-2", children: [_jsx(Label, { children: "Telefoonnummer" }), _jsx(IconInput, { icon: Phone, value: form.phone ?? "", onChange: (e) => setForm({ ...form, phone: e.target.value }) })] })] }), _jsxs("div", { className: "space-y-2", children: [_jsx(Label, { children: "Adres" }), _jsx(IconInput, { icon: MapPin, value: form.address ?? "", onChange: (e) => setForm({ ...form, address: e.target.value }), placeholder: "Straat, postcode, plaats" })] }), _jsxs("div", { className: "rounded-md border bg-muted/30 p-4 space-y-4", children: [_jsxs("div", { children: [_jsx("div", { className: "text-sm font-medium", children: "Ophaalmomenten (transportplanning)" }), _jsxs("p", { className: "text-xs text-muted-foreground mt-1", children: ["De bezorgdag is ", _jsx("strong", { children: "dag 0" }), ". Per ophaalmoment stel je in op hoeveel werkdagen v\u00F3\u00F3r bezorging \u00E9n op welk tijdstip elke stap plaatsvindt. Bij een nieuwe order kiest het systeem automatisch het eerstvolgende moment waarvan de aanmelddeadline nog niet is verstreken."] })] }), _jsx("div", { className: "space-y-3", children: form.pickup_moments.map((moment, idx) => (_jsx(PickupMomentEditor, { index: idx, value: moment, canDelete: form.pickup_moments.length > 1, onChange: (next) => {
190
+ : "Voeg een nieuwe vervoerder toe en stel verzendtarieven in." })] })] }), _jsxs("form", { onSubmit: handleSubmit, className: "space-y-6", children: [_jsxs(Card, { children: [_jsx(CardHeader, { children: _jsx(CardTitle, { className: "text-base", children: "Algemene gegevens" }) }), _jsxs(CardContent, { className: "space-y-4", children: [_jsxs("div", { className: "space-y-2", children: [_jsxs(Label, { children: ["Bedrijfsnaam ", _jsx("span", { className: "text-destructive", children: "*" })] }), _jsx(IconInput, { icon: Truck, value: form.name, onChange: (e) => setForm({ ...form, name: e.target.value }), placeholder: "Bijv. DHL Freight", required: true })] }), _jsxs("div", { className: "space-y-2", children: [_jsx(Label, { children: "Contactpersoon" }), _jsx(IconInput, { icon: User, value: form.contact_person ?? "", onChange: (e) => setForm({ ...form, contact_person: e.target.value }) })] }), _jsxs("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4", children: [_jsxs("div", { className: "space-y-2", children: [_jsx(Label, { children: "E-mail" }), _jsx(IconInput, { icon: Mail, type: "email", value: form.email ?? "", onChange: (e) => setForm({ ...form, email: e.target.value }) })] }), _jsxs("div", { className: "space-y-2", children: [_jsx(Label, { children: "Telefoonnummer" }), _jsx(IconInput, { icon: Phone, value: form.phone ?? "", onChange: (e) => setForm({ ...form, phone: e.target.value }) })] })] }), _jsxs("div", { className: "space-y-2", children: [_jsx(Label, { children: "Adres" }), _jsx(IconInput, { icon: MapPin, value: form.address ?? "", onChange: (e) => setForm({ ...form, address: e.target.value }), placeholder: "Straat, postcode, plaats" })] }), _jsxs("div", { className: "rounded-md border bg-muted/30 p-4 space-y-4", children: [_jsxs("div", { children: [_jsx("div", { className: "text-sm font-medium", children: "Ophaalmomenten (transportplanning)" }), _jsxs("p", { className: "text-xs text-muted-foreground mt-1", children: ["De bezorgdag is ", _jsx("strong", { children: "dag 0" }), ". Per ophaalmoment stel je in op hoeveel werkdagen v\u00F3\u00F3r bezorging \u00E9n op welk tijdstip elke stap plaatsvindt. Bij een nieuwe order kiest het systeem automatisch het eerstvolgende moment waarvan de aanmelddeadline nog niet is verstreken."] })] }), _jsx("div", { className: "space-y-3", children: form.pickup_moments.map((moment, idx) => (_jsx(PickupMomentEditor, { showErrors: showTimeErrors, index: idx, value: moment, canDelete: form.pickup_moments.length > 1, onChange: (next) => {
184
191
  const copy = [...form.pickup_moments];
185
192
  copy[idx] = next;
186
193
  setForm({ ...form, pickup_moments: copy });
@@ -1 +1 @@
1
- {"version":3,"file":"WeekAgendaView.d.ts","sourceRoot":"","sources":["../../../src/components/afhandeling/WeekAgendaView.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAElE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAOjE,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;IACf,yEAAyE;IACzE,YAAY,CAAC,EAAE,CAAC,SAAS,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,KAAK,IAAI,CAAC;IACxD,4EAA4E;IAC5E,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAiGD,wBAAgB,cAAc,CAAC,EAC7B,OAAO,EACP,IAAI,EACJ,SAAS,EACT,YAAY,EACZ,UAAU,EACV,KAAK,EACL,YAAY,EACZ,UAAU,GACX,EAAE,mBAAmB,+BAoMrB"}
1
+ {"version":3,"file":"WeekAgendaView.d.ts","sourceRoot":"","sources":["../../../src/components/afhandeling/WeekAgendaView.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAElE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAQjE,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;IACf,yEAAyE;IACzE,YAAY,CAAC,EAAE,CAAC,SAAS,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,KAAK,IAAI,CAAC;IACxD,4EAA4E;IAC5E,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAiGD,wBAAgB,cAAc,CAAC,EAC7B,OAAO,EACP,IAAI,EACJ,SAAS,EACT,YAAY,EACZ,UAAU,EACV,KAAK,EACL,YAAY,EACZ,UAAU,GACX,EAAE,mBAAmB,+BAsMrB"}
@@ -10,6 +10,7 @@ import { useCapacityCheck } from "../../hooks/useCapacityCheck";
10
10
  import { usePickAgenda } from "../../hooks/usePickAgenda";
11
11
  import { useConsistencyCheck } from "../../hooks/useConsistencyCheck";
12
12
  import { CapacityWarning } from "./CapacityWarning";
13
+ import { SignupMailMoments } from "./SignupMailMoments";
13
14
  import { toDayKey } from "../../lib/logisticsEngine/capacity";
14
15
  const SHIPPED = new Set(["verzonden", "gereed voor facturatie", "gefactureerd", "geleverd", "afgeleverd", "afgerond", "afgehandeld", "afgemeld", "shipped", "delivered", "completed", "invoiced"]);
15
16
  /** Eén indeling per order. Floriday herkend op Floriday-id; afmeldstand uit de order. */
@@ -205,7 +206,7 @@ export function WeekAgendaView({ planned, mode, isLoading, onOrderClick, onDayCl
205
206
  d.setHours(0, 0, 0, 0);
206
207
  setCursor(startOfWeek(d));
207
208
  };
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())))) })] }));
209
+ 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" })] }), mode !== "pick" ? _jsx(SignupMailMoments, { from: toDayKey(weekStart), to: toDayKey(weekEnd), compact: true }) : null, _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())))) })] }));
209
210
  }
210
211
  function SummaryCard({ label, value, tone, }) {
211
212
  const valueClass = tone === "success" ? "text-green-600" : tone === "warning" ? "text-amber-600" : "text-foreground";
@@ -1 +1 @@
1
- {"version":3,"file":"AfhandelingSettingsCard.d.ts","sourceRoot":"","sources":["../../../src/components/settings/AfhandelingSettingsCard.tsx"],"names":[],"mappings":"AAqBA,wBAAgB,uBAAuB,gCAyHtC"}
1
+ {"version":3,"file":"AfhandelingSettingsCard.d.ts","sourceRoot":"","sources":["../../../src/components/settings/AfhandelingSettingsCard.tsx"],"names":[],"mappings":"AAuBA,wBAAgB,uBAAuB,gCAiKtC"}
@@ -1,14 +1,30 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useEffect, useState } from "react";
2
+ import { useEffect, useRef, 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
6
  import { CalendarClock, Mail } from "lucide-react";
7
7
  import { getAfhandelingSettings, updateAfhandelingSettings, } from "../../lib/afhandelingSettings.functions";
8
+ import { getSignupCronStatus } from "../../lib/carrierSignup.functions";
9
+ import { fmtTime, signupReasonText } from "../../lib/signupMailText";
8
10
  export function AfhandelingSettingsCard() {
9
11
  const qc = useQueryClient();
10
12
  const fetchSettings = useServerFn(getAfhandelingSettings);
11
13
  const saveSettings = useServerFn(updateAfhandelingSettings);
14
+ const fetchCronStatus = useServerFn(getSignupCronStatus);
15
+ // Openen van de kaart leest de status en herstelt de taak als hij ontbreekt.
16
+ const cron = useQuery({
17
+ queryKey: ["afhandeling", "signup-status"],
18
+ queryFn: () => fetchCronStatus({ data: { sync: false } }),
19
+ refetchInterval: 60000,
20
+ });
21
+ const healedShown = useRef(false);
22
+ useEffect(() => {
23
+ if (cron.data?.healed && !healedShown.current) {
24
+ healedShown.current = true;
25
+ toast.info("De automatische controle ontbrak en is opnieuw aangezet.");
26
+ }
27
+ }, [cron.data?.healed]);
12
28
  const { data } = useQuery({
13
29
  queryKey: ["afhandeling", "settings"],
14
30
  queryFn: async () => {
@@ -41,11 +57,40 @@ export function AfhandelingSettingsCard() {
41
57
  onSuccess: async () => {
42
58
  const { invalidatePlanning } = await import("../../lib/invalidatePlanning");
43
59
  await invalidatePlanning(qc);
60
+ try {
61
+ const st = await fetchCronStatus({ data: { sync: true } });
62
+ qc.setQueryData(["afhandeling", "signup-status"], st);
63
+ if (!st.cronAvailable) {
64
+ toast.error("Automatisch versturen is in deze omgeving niet beschikbaar. Vraag je beheerder om dit aan te zetten.");
65
+ return;
66
+ }
67
+ if (st.syncCode === null && st.enabled !== st.active) {
68
+ toast.error("Alleen een beheerder kan de automatische controle aan- of uitzetten.");
69
+ return;
70
+ }
71
+ }
72
+ catch (e) {
73
+ toast.error(e instanceof Error ? e.message : "Automatische controle kon niet worden ingesteld");
74
+ return;
75
+ }
44
76
  toast.success("Instellingen opgeslagen");
45
77
  },
46
78
  onError: (e) => {
47
79
  toast.error(e instanceof Error ? e.message : "Opslaan mislukt");
48
80
  },
49
81
  });
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" })] })] }));
82
+ 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))) })] }), _jsx("div", { className: "space-y-1 text-sm", children: _jsxs("div", { children: [_jsx("strong", { children: "Verzendadres:" }), " ", cron.data?.senderEmail ? cron.data.senderEmail : _jsx("span", { className: "text-muted-foreground", children: "nog geen verbonden adres" }), " ", _jsx("span", { className: "text-xs text-muted-foreground", children: "(instellen bij E-mailkoppeling)" })] }) }), _jsxs("details", { className: "text-xs text-muted-foreground", children: [_jsx("summary", { className: "cursor-pointer", children: "Details voor beheer" }), _jsxs("div", { className: "mt-2 space-y-2", children: [_jsx(Label, { htmlFor: "mailPurpose", children: "Doeleinde in E-mailkoppeling" }), _jsx(Input, { id: "mailPurpose", value: mailPurpose, disabled: !mailEnabled, onChange: (e) => setMailPurpose(e.target.value) }), _jsxs("p", { children: ["Standaard: ", _jsx("code", { children: "afhandeling_carrier_signup" }), "."] })] })] })] }), _jsx(SignupStatusBlock, { status: cron.data ?? null, loading: cron.isLoading, failed: cron.isError }), _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" })] })] }));
83
+ }
84
+ function SignupStatusBlock({ status, loading, failed, }) {
85
+ if (loading)
86
+ return null;
87
+ if (failed || !status) {
88
+ return _jsx("div", { className: "rounded-md border p-3 text-xs text-muted-foreground", children: "Automatische controle: stand onbekend." });
89
+ }
90
+ const tz = status.timezone;
91
+ const run = status.lastRun;
92
+ const reason = status.enabled
93
+ ? signupReasonText(status.reason) ?? (run && !run.ok ? signupReasonText(run.reason) : null)
94
+ : null;
95
+ return (_jsxs("div", { className: "space-y-1 rounded-md border p-3 text-xs", children: [_jsxs("div", { children: [_jsx("strong", { children: "Automatische controle:" }), " ", !status.cronAvailable ? (_jsx("span", { className: "text-destructive", children: "niet beschikbaar in deze omgeving. Vraag je beheerder om automatische taken aan te zetten." })) : status.active ? (_jsx("span", { className: "text-primary", children: "actief" })) : (_jsx("span", { className: "text-muted-foreground", children: "niet actief" }))] }), run ? (_jsxs("div", { className: "text-muted-foreground", children: ["Laatst gecontroleerd om ", fmtTime(run.started_at, tz), " \u00B7 ", status.sentToday, " ", status.sentToday === 1 ? "mail" : "mails", " verstuurd vandaag"] })) : (_jsx("div", { className: "text-muted-foreground", children: "Nog niet gecontroleerd." })), reason ? _jsx("div", { className: "text-destructive", children: reason }) : null] }));
51
96
  }
@@ -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,CAmTxF"}
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,EAEL,KAAK,aAAa,EAEnB,MAAM,iBAAiB,CAAC;AAGzB,OAAO,EAAwC,KAAK,UAAU,EAAE,MAAM,+BAA+B,CAAC;AActG;;;;;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,CAkUxF"}
@@ -5,12 +5,14 @@ import { getFloridayOrders } from "../lib/floriday.functions";
5
5
  import { listCarriers } from "../lib/carriers.functions";
6
6
  import { safeServerCall } from "../lib/serverFnError";
7
7
  import { normalizePickupMoments, sanitizeWeekdays, selectCarrierForTarget } from "../lib/carrierPlanning";
8
- import { calculateOrderPlanning, DEFAULT_PICKUP_MOMENT, } from "../lib/planning";
8
+ import { calculateOrderPlanning, } from "../lib/planning";
9
9
  import { useEngineTasks } from "./useEngineTasks";
10
10
  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 { loadPickWorkdays } from "../lib/pickWorkdays";
15
+ import { supabase as moduleSupabase } from "../integrations/supabase/moduleClient";
14
16
  import { emitQueueStatus, markInFlight, markResult, resetEmitQueue, selectEmitBatch, } from "../lib/logisticsEngine/emitQueue";
15
17
  export function useOrderPlanning(range) {
16
18
  const from = range?.from ?? null;
@@ -57,6 +59,15 @@ export function useOrderPlanning(range) {
57
59
  refetchIntervalInBackground: false,
58
60
  refetchOnWindowFocus: true,
59
61
  });
62
+ // Werkdagen komen uitsluitend uit de Werktijden; zonder die kalender plannen we niet.
63
+ const workdaysQuery = useQuery({
64
+ queryKey: ["afhandeling", "pick-workdays"],
65
+ queryFn: () => loadPickWorkdays(moduleSupabase),
66
+ retry: 1,
67
+ staleTime: 5 * 60000,
68
+ refetchInterval: 10 * 60000,
69
+ refetchIntervalInBackground: false,
70
+ });
60
71
  const carriersQuery = useQuery({
61
72
  queryKey: ["carriers"],
62
73
  queryFn: () => safeServerCall(() => fetchCarriers(), { carriers: [] }, "listCarriers"),
@@ -122,7 +133,7 @@ export function useOrderPlanning(range) {
122
133
  });
123
134
  const moments = carrier
124
135
  ? normalizePickupMoments(carrier.pickup_moments)
125
- : [DEFAULT_PICKUP_MOMENT];
136
+ : [];
126
137
  // Transport-/leverdata blijven uit de vervoerdersagenda komen; het
127
138
  // pickmoment en de karindeling komen uitsluitend uit de Engine-taak.
128
139
  const base = calculateOrderPlanning(order.deliveryDate, {
@@ -188,7 +199,8 @@ export function useOrderPlanning(range) {
188
199
  };
189
200
  return { order, planning, engineTask: task, awaitingEngine: false, state: "planned", hasComposeTask: true };
190
201
  });
191
- }, [orderList, carriersQuery.data, composeByRef, pickByRef, composeExists, cartsForOrder]);
202
+ // workdaysQuery.data: herberekenen zodra de werkdagkalender (opnieuw) geladen is.
203
+ }, [orderList, carriersQuery.data, composeByRef, pickByRef, composeExists, cartsForOrder, workdaysQuery.data]);
192
204
  // Alleen echt open orders mogen bij de Logistics Engine worden aangemeld;
193
205
  // afgehandelde orders blijven wel zichtbaar maar krijgen geen melding meer.
194
206
  const openPlanned = useMemo(() => planned.filter((p) => isOpenOrderStatus(p.order.status)), [planned]);
@@ -285,12 +297,15 @@ export function useOrderPlanning(range) {
285
297
  tasksWithoutStart: openPlanned.filter((p) => p.state === "task_without_start"),
286
298
  signalError,
287
299
  lastEngineRun,
288
- isLoading: ordersQuery.isLoading || carriersQuery.isLoading || tasksLoading,
300
+ isLoading: ordersQuery.isLoading || carriersQuery.isLoading || tasksLoading || workdaysQuery.isLoading,
289
301
  error: ordersQuery.data?.orders?.length ? undefined : ordersQuery.data?.error,
290
- warning: ordersQuery.data?.orders?.length ? ordersQuery.data?.error : undefined,
302
+ warning: workdaysQuery.isError || (workdaysQuery.data && !workdaysQuery.data.available) || workdaysQuery.data?.count === 0
303
+ ? "De werkdagen uit Werktijden zijn niet beschikbaar. Ophaal- en aanmelddagen worden pas getoond als de werkdagen zijn ingesteld."
304
+ : ordersQuery.data?.orders?.length ? ordersQuery.data?.error : undefined,
291
305
  refetch: () => {
292
306
  void ordersQuery.refetch();
293
307
  void carriersQuery.refetch();
308
+ void workdaysQuery.refetch();
294
309
  refetchTasks();
295
310
  },
296
311
  };