@flowselections/floriday-afhandeling-module 1.0.61 → 1.0.63

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/dist-lib/components/afhandeling/AutoSendStatus.d.ts +9 -2
  2. package/dist-lib/components/afhandeling/AutoSendStatus.d.ts.map +1 -1
  3. package/dist-lib/components/afhandeling/AutoSendStatus.js +70 -6
  4. package/dist-lib/components/afhandeling/DagDetailDialog.js +39 -24
  5. package/dist-lib/components/afhandeling/LeveringenPage.d.ts.map +1 -1
  6. package/dist-lib/components/afhandeling/LeveringenPage.js +3 -1
  7. package/dist-lib/components/afhandeling/NuVersturenButton.d.ts.map +1 -1
  8. package/dist-lib/components/afhandeling/NuVersturenButton.js +4 -4
  9. package/dist-lib/components/afhandeling/PickAgendaView.d.ts.map +1 -1
  10. package/dist-lib/components/afhandeling/PickAgendaView.js +9 -7
  11. package/dist-lib/components/afhandeling/ScanControleView.d.ts.map +1 -1
  12. package/dist-lib/components/afhandeling/ScanControleView.js +2 -2
  13. package/dist-lib/components/afhandeling/VerzetOrderDialog.js +3 -3
  14. package/dist-lib/components/afhandeling/WeekAgendaView.js +5 -2
  15. package/dist-lib/components/settings/AfhandelingSettingsCard.js +1 -1
  16. package/dist-lib/hooks/useOrderPlanning.d.ts.map +1 -1
  17. package/dist-lib/hooks/useOrderPlanning.js +7 -6
  18. package/dist-lib/hooks/usePickAgenda.d.ts +4 -0
  19. package/dist-lib/hooks/usePickAgenda.d.ts.map +1 -1
  20. package/dist-lib/hooks/usePickAgenda.js +36 -5
  21. package/dist-lib/integrations/supabase/types.d.ts +225 -0
  22. package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
  23. package/dist-lib/lib/cartAssignments.functions.d.ts +4 -0
  24. package/dist-lib/lib/cartAssignments.functions.d.ts.map +1 -1
  25. package/dist-lib/lib/cartAssignments.functions.js +5 -1
  26. package/dist-lib/lib/floridayLoading.functions.d.ts +7 -0
  27. package/dist-lib/lib/floridayLoading.functions.d.ts.map +1 -1
  28. package/dist-lib/lib/floridayLoading.functions.js +11 -4
  29. package/dist-lib/lib/printing.js +1 -1
  30. package/dist-lib/lib/serverRoutes/autoSend.handler.d.ts +2 -1
  31. package/dist-lib/lib/serverRoutes/autoSend.handler.d.ts.map +1 -1
  32. package/dist-lib/lib/serverRoutes/autoSend.handler.js +26 -5
  33. package/dist-lib/lib/serverRoutes/index.d.ts +1 -0
  34. package/dist-lib/lib/serverRoutes/index.d.ts.map +1 -1
  35. package/dist-lib/lib/serverRoutes/index.js +1 -0
  36. package/package.json +12 -1
@@ -1,5 +1,12 @@
1
- /** Toont de tekst uit logistics_auto_send_status letterlijk; niets bij status "geen". */
2
- export declare function AutoSendStatus({ orderId }: {
1
+ /** Tijdzone van de vestiging uit de Werktijden-instelling (alleen lezen); null = onbekend. */
2
+ export declare function useVestigingTijdzone(): import("@tanstack/react-query").UseQueryResult<string | null, Error>;
3
+ /**
4
+ * Melding over automatisch versturen in gewone taal. Is de order al gemeld bij
5
+ * Floriday (handmatig of automatisch), dan "Verstuurd om HH:MM". De letterlijke
6
+ * tekst van de planning staat onder "Details voor beheer".
7
+ */
8
+ export declare function AutoSendStatus({ orderId, orderRef }: {
3
9
  orderId: string;
10
+ orderRef?: string | null;
4
11
  }): import("react").JSX.Element | null;
5
12
  //# sourceMappingURL=AutoSendStatus.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"AutoSendStatus.d.ts","sourceRoot":"","sources":["../../../src/components/afhandeling/AutoSendStatus.tsx"],"names":[],"mappings":"AAGA,yFAAyF;AACzF,wBAAgB,cAAc,CAAC,EAAE,OAAO,EAAE,EAAE;IAAE,OAAO,EAAE,MAAM,CAAA;CAAE,sCAe9D"}
1
+ {"version":3,"file":"AutoSendStatus.d.ts","sourceRoot":"","sources":["../../../src/components/afhandeling/AutoSendStatus.tsx"],"names":[],"mappings":"AAWA,8FAA8F;AAC9F,wBAAgB,oBAAoB,yEAWnC;AAaD;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,sCAwDlG"}
@@ -1,8 +1,40 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useQuery } from "@tanstack/react-query";
3
3
  import { supabase } from "../../integrations/supabase/moduleClient";
4
- /** Toont de tekst uit logistics_auto_send_status letterlijk; niets bij status "geen". */
5
- export function AutoSendStatus({ orderId }) {
4
+ /** Tijdzone van de vestiging uit de Werktijden-instelling (alleen lezen); null = onbekend. */
5
+ export function useVestigingTijdzone() {
6
+ return useQuery({
7
+ queryKey: ["vestiging-tijdzone"],
8
+ staleTime: 10 * 60000,
9
+ retry: 1,
10
+ queryFn: async () => {
11
+ const { data, error } = await supabase.rpc("logistics_opening_timezone");
12
+ if (error)
13
+ throw new Error(error.message);
14
+ return typeof data === "string" && data ? data : null;
15
+ },
16
+ });
17
+ }
18
+ function hhmm(iso, tz) {
19
+ if (!iso || !tz)
20
+ return null;
21
+ const d = new Date(iso);
22
+ if (Number.isNaN(d.getTime()))
23
+ return null;
24
+ try {
25
+ return d.toLocaleTimeString("nl-NL", { timeZone: tz, hour: "2-digit", minute: "2-digit" });
26
+ }
27
+ catch {
28
+ return null;
29
+ }
30
+ }
31
+ /**
32
+ * Melding over automatisch versturen in gewone taal. Is de order al gemeld bij
33
+ * Floriday (handmatig of automatisch), dan "Verstuurd om HH:MM". De letterlijke
34
+ * tekst van de planning staat onder "Details voor beheer".
35
+ */
36
+ export function AutoSendStatus({ orderId, orderRef }) {
37
+ const tz = useVestigingTijdzone();
6
38
  const { data } = useQuery({
7
39
  queryKey: ["auto-send-status", orderId],
8
40
  queryFn: async () => {
@@ -15,8 +47,40 @@ export function AutoSendStatus({ orderId }) {
15
47
  refetchInterval: 30000,
16
48
  retry: 1,
17
49
  });
18
- if (!data?.tekst || !data.status || data.status === "geen")
50
+ // Eerste door Floriday geaccepteerde poging (handmatig of automatisch).
51
+ const sent = useQuery({
52
+ queryKey: ["auto-send-accepted", orderRef],
53
+ enabled: !!orderRef,
54
+ refetchInterval: 30000,
55
+ queryFn: async () => {
56
+ const { data, error } = await supabase
57
+ .from("floriday_loading_attempts").select("created_at")
58
+ .eq("step", "send").contains("order_refs", [orderRef]).in("code", ["created", "already_fulfilled"])
59
+ .order("created_at", { ascending: true }).limit(1);
60
+ if (error)
61
+ throw new Error(error.message);
62
+ return Array.isArray(data) && data[0]?.created_at ? String(data[0].created_at) : null;
63
+ },
64
+ });
65
+ const zone = tz.data ?? null;
66
+ const sentAt = sent.data ?? (data?.status === "verstuurd" ? data.verstuurdOm ?? null : null);
67
+ let tekst = null;
68
+ let tone = "text-muted-foreground";
69
+ if (sentAt) {
70
+ const t = hhmm(sentAt, zone);
71
+ tekst = t ? `Verstuurd om ${t}` : "Verstuurd";
72
+ }
73
+ else if (data?.status === "mislukt") {
74
+ const t = hhmm(data.volgendePoging, zone);
75
+ tekst = t
76
+ ? `Automatisch versturen lukt nog niet. Nieuwe poging om ${t}. Je kunt ook op ‘Nu versturen’ drukken.`
77
+ : "Automatisch versturen lukt nog niet. Je kunt op ‘Nu versturen’ drukken.";
78
+ tone = "text-destructive";
79
+ }
80
+ else if (data?.status === "wachtend" || data?.status === "bezig") {
81
+ tekst = "Wordt automatisch verstuurd.";
82
+ }
83
+ if (!tekst)
19
84
  return null;
20
- const tone = data.status === "mislukt" ? "text-destructive" : "text-muted-foreground";
21
- return _jsx("span", { className: `text-xs ${tone}`, children: data.tekst });
85
+ return (_jsxs("span", { className: `text-xs ${tone}`, children: [tekst, data?.tekst && !sentAt ? (_jsxs("details", { className: "mt-1 text-[11px] text-muted-foreground", children: [_jsx("summary", { className: "cursor-pointer", children: "Details voor beheer" }), data.tekst] })) : null] }));
22
86
  }
@@ -42,23 +42,23 @@ function useVoortgang(ref, status, opts = { floriday: null }) {
42
42
  if (opts.cancelled)
43
43
  return { label: "Geannuleerd", done: true, variant: "outline", bron: "Verkoop" };
44
44
  if (opts.archived === true)
45
- return { label: "Gearchiveerd", done: true, variant: "outline", bron: "Engine" };
45
+ return { label: "Gearchiveerd", done: true, variant: "outline", bron: "planning" };
46
46
  const archiefNote = opts.archived === null ? " · archiefstatus onbekend" : "";
47
47
  if (opts.floriday !== false && st?.floridaySent)
48
- return { label: "Verzonden naar Floriday", done: true, variant: "default", bron: "Floriday" + archiefNote };
48
+ return { label: "Gemeld bij Floriday", done: true, variant: "default", bron: "Floriday" + archiefNote };
49
49
  if (st?.approvedAll) {
50
50
  if (opts.floriday === false)
51
- return { label: "Afgemeld", done: true, variant: "default", bron: "Engine-scan" + archiefNote };
51
+ return { label: "Afgemeld", done: true, variant: "default", bron: "scan" + archiefNote };
52
52
  if (opts.floriday === true)
53
- return { label: "Klaar voor verzending", done: false, variant: "secondary", bron: "Engine-scan" + archiefNote };
54
- return { label: "Alle karren gescand", done: false, variant: "secondary", bron: "Engine-scan" + archiefNote };
53
+ return { label: "Klaar voor verzending", done: false, variant: "secondary", bron: "scan" + archiefNote };
54
+ return { label: "Alle karren gescand", done: false, variant: "secondary", bron: "scan" + archiefNote };
55
55
  }
56
56
  const v = voortgang(status);
57
57
  if (v.done && opts.floriday === false)
58
- return { ...v, label: "Afgemeld", bron: "Engine" + archiefNote };
58
+ return { ...v, label: "Afgemeld", bron: "planning" + archiefNote };
59
59
  if (v.done && opts.floriday === true && v.label === "Afgehandeld")
60
- return { ...v, label: "Afgemeld (nog niet naar Floriday)", done: false, variant: "secondary", bron: "Engine" + archiefNote };
61
- return { ...v, bron: "Engine" + archiefNote };
60
+ return { ...v, label: "Afgemeld (nog niet naar Floriday)", done: false, variant: "secondary", bron: "planning" + archiefNote };
61
+ return { ...v, bron: "planning" + archiefNote };
62
62
  }
63
63
  function isCancelled(status) {
64
64
  const s = (status ?? "").trim().toLowerCase();
@@ -146,7 +146,7 @@ function DagDetailContent({ open, date, planned, onOpenChange, }) {
146
146
  const shipState = new Map();
147
147
  for (const o of contentsQuery.data?.orders ?? []) {
148
148
  coverageByRef.set(refKey(o.orderRef), o.label);
149
- shipState.set(refKey(o.orderRef), { approvedAll: o.approvedAll, floridaySent: o.floridaySent });
149
+ shipState.set(refKey(o.orderRef), { approvedAll: o.approvedAll, floridaySent: o.floridaySent, cartIds: o.cartIds ?? [], approvedCartIds: o.approvedCartIds ?? [] });
150
150
  }
151
151
  // Eén rij per kar die op deze dag valt (Engine-dekking is leidend, ook voor bufferkarren).
152
152
  const cartRows = [];
@@ -178,7 +178,9 @@ function DagDetailContent({ open, date, planned, onOpenChange, }) {
178
178
  });
179
179
  }
180
180
  }
181
- 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 ", cartRows.length, " karren \u00B7", " ", ordersOnDay.filter((o) => voortgang(o.status).done).length, " van ", ordersOnDay.length, " ", "bestellingen afgehandeld \u00B7 ", cartRows.filter((c) => voortgang(c.status).done).length, " van", " ", cartRows.length, " karren afgehandeld"] })] }), _jsxs(ResponsiveDialogBody, { children: [_jsx(DayCapacityWarning, { date: date }), _jsx(BezettingCard, { date: date, cartsToday: cartRows.length }), _jsxs(Tabs, { defaultValue: "bestellingen", children: [_jsxs(TabsList, { children: [_jsxs(TabsTrigger, { value: "bestellingen", children: ["Bestellingen (", ordersOnDay.length, ")"] }), _jsxs(TabsTrigger, { value: "karren", children: ["Karren (", cartRows.length, ")"] })] }), _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" }) })] }) }) }));
181
+ // Tel karren op unieke kar-id (gedeelde bufferkar één keer); rijen zonder id tellen los.
182
+ 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" }) })] }) }) }));
182
184
  }
183
185
  function BezettingCard({ date, cartsToday }) {
184
186
  const queryClient = useQueryClient();
@@ -241,7 +243,7 @@ function BezettingCard({ date, cartsToday }) {
241
243
  await clearDayStaffPlan(date);
242
244
  await queryClient.invalidateQueries({ queryKey: ["engine-staff-day", dateKey] });
243
245
  setEditing(false);
244
- toast.success("Terug naar standaardbezetting van de Logistiek Engine");
246
+ toast.success("Terug naar de standaardbezetting");
245
247
  }
246
248
  catch (err) {
247
249
  toast.error(err instanceof Error ? err.message : "Herstellen mislukt");
@@ -250,7 +252,7 @@ function BezettingCard({ date, cartsToday }) {
250
252
  setSaving(false);
251
253
  }
252
254
  };
253
- return (_jsxs("div", { className: "mb-4 rounded-lg border bg-muted/30 p-4", children: [_jsxs("div", { className: "flex flex-wrap items-center justify-between gap-3", children: [_jsxs("div", { className: "flex flex-wrap items-center gap-6", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Users, { className: "h-4 w-4 text-muted-foreground" }), _jsxs("div", { children: [_jsx("div", { className: "text-lg font-bold leading-none", children: defaultsQuery.isLoading || dayQuery.isLoading ? "…" : (effStaff ?? "—") }), _jsxs("div", { className: "text-xs text-muted-foreground", children: ["medewerkers ", dayPlan ? "(aangepast voor deze dag)" : "(gemiddelde uit Engine)"] })] })] }), _jsxs("div", { children: [_jsxs("div", { className: "text-lg font-bold leading-none", children: [effFrom, "\u2013", effTo] }), _jsx("div", { className: "text-xs text-muted-foreground", children: "werktijden" })] }), capacity?.capacityCarts !== null && capacity !== null && (_jsxs("div", { children: [_jsxs("div", { className: "text-lg font-bold leading-none", children: [cartsToday, " / ", capacity.capacityCarts] }), _jsx("div", { className: "text-xs text-muted-foreground", children: "karren gepland / capaciteit" })] }))] }), !editing && (_jsx(Button, { size: "sm", variant: "outline", onClick: startEdit, children: "Bezetting aanpassen" }))] }), editing && (_jsxs("div", { className: "mt-4 flex flex-wrap items-end gap-3", children: [_jsxs("div", { className: "space-y-1", children: [_jsx("label", { className: "text-xs text-muted-foreground", children: "Medewerkers" }), _jsx(Input, { type: "number", min: 0, step: 1, value: staff, onChange: (e) => setStaff(e.target.value), className: "w-28" })] }), _jsxs("div", { className: "space-y-1", children: [_jsx("label", { className: "text-xs text-muted-foreground", children: "Van" }), _jsx(Input, { type: "time", value: from, onChange: (e) => setFrom(e.target.value), className: "w-32" })] }), _jsxs("div", { className: "space-y-1", children: [_jsx("label", { className: "text-xs text-muted-foreground", children: "Tot" }), _jsx(Input, { type: "time", value: to, onChange: (e) => setTo(e.target.value), className: "w-32" })] }), _jsx(Button, { size: "sm", onClick: save, disabled: saving, children: saving ? "Opslaan…" : "Opslaan" }), dayPlan && (_jsx(Button, { size: "sm", variant: "ghost", onClick: reset, disabled: saving, children: "Standaard herstellen" })), _jsx(Button, { size: "sm", variant: "ghost", onClick: () => setEditing(false), disabled: saving, children: "Annuleren" })] }))] }));
255
+ return (_jsxs("div", { className: "mb-4 rounded-lg border bg-muted/30 p-4", children: [_jsxs("div", { className: "flex flex-wrap items-center justify-between gap-3", children: [_jsxs("div", { className: "flex flex-wrap items-center gap-6", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Users, { className: "h-4 w-4 text-muted-foreground" }), _jsxs("div", { children: [_jsx("div", { className: "text-lg font-bold leading-none", children: defaultsQuery.isLoading || dayQuery.isLoading ? "…" : (effStaff ?? "—") }), _jsxs("div", { className: "text-xs text-muted-foreground", children: ["medewerkers ", dayPlan ? "(aangepast voor deze dag)" : "(gemiddelde uit de planning)"] })] })] }), _jsxs("div", { children: [_jsxs("div", { className: "text-lg font-bold leading-none", children: [effFrom, "\u2013", effTo] }), _jsx("div", { className: "text-xs text-muted-foreground", children: "werktijden" })] }), capacity?.capacityCarts !== null && capacity !== null && (_jsxs("div", { children: [_jsxs("div", { className: "text-lg font-bold leading-none", children: [cartsToday, " / ", capacity.capacityCarts] }), _jsx("div", { className: "text-xs text-muted-foreground", children: "karren gepland / capaciteit" })] }))] }), !editing && (_jsx(Button, { size: "sm", variant: "outline", onClick: startEdit, children: "Bezetting aanpassen" }))] }), editing && (_jsxs("div", { className: "mt-4 flex flex-wrap items-end gap-3", children: [_jsxs("div", { className: "space-y-1", children: [_jsx("label", { className: "text-xs text-muted-foreground", children: "Medewerkers" }), _jsx(Input, { type: "number", min: 0, step: 1, value: staff, onChange: (e) => setStaff(e.target.value), className: "w-28" })] }), _jsxs("div", { className: "space-y-1", children: [_jsx("label", { className: "text-xs text-muted-foreground", children: "Van" }), _jsx(Input, { type: "time", value: from, onChange: (e) => setFrom(e.target.value), className: "w-32" })] }), _jsxs("div", { className: "space-y-1", children: [_jsx("label", { className: "text-xs text-muted-foreground", children: "Tot" }), _jsx(Input, { type: "time", value: to, onChange: (e) => setTo(e.target.value), className: "w-32" })] }), _jsx(Button, { size: "sm", onClick: save, disabled: saving, children: saving ? "Opslaan…" : "Opslaan" }), dayPlan && (_jsx(Button, { size: "sm", variant: "ghost", onClick: reset, disabled: saving, children: "Standaard herstellen" })), _jsx(Button, { size: "sm", variant: "ghost", onClick: () => setEditing(false), disabled: saving, children: "Annuleren" })] }))] }));
254
256
  }
255
257
  function BestellingenTab({ orders, isLoading }) {
256
258
  const { bufferFor } = useOrderBuffers(orders.map((o) => o.ref));
@@ -362,10 +364,10 @@ async function printDocumentenVoorOrder(p, fetchLines, fetchCarts, fetchDelivery
362
364
  if (cartsRes.status === "fulfilled")
363
365
  engineCarts = cartsRes.value.carts;
364
366
  else
365
- reasons.push(cartsRes.reason instanceof Error ? cartsRes.reason.message : "Karindeling van de Engine niet opgehaald.");
367
+ reasons.push(cartsRes.reason instanceof Error ? cartsRes.reason.message : "Karindeling niet opgehaald.");
366
368
  if (engineCarts.length === 0) {
367
369
  if (!reasons.length)
368
- reasons.push(`De Engine heeft voor ${p.order.orderNumber} nog geen karren ingedeeld.`);
370
+ reasons.push(`${p.order.orderNumber} is nog niet over karren verdeeld.`);
369
371
  return { printed: 0, skipped: [], failed: 1, reasons, achtergrond: null };
370
372
  }
371
373
  let info;
@@ -395,7 +397,7 @@ async function printDocumentenVoorOrder(p, fetchLines, fetchCarts, fetchDelivery
395
397
  const ci = infoById.get(engineCart.cartId);
396
398
  if (!ci) {
397
399
  failed++;
398
- reasons.push(`Pakbon ${cartNumber}: geen kar-barcode van de Engine.`);
400
+ reasons.push(`Pakbon ${cartNumber} kan nog niet worden geprint: de kar is nog niet ingedeeld. Probeer het over een minuut opnieuw.`);
399
401
  return;
400
402
  }
401
403
  const opdracht = (isHerprint) => printLayoutDocument({
@@ -441,7 +443,7 @@ async function printDocumentenVoorOrder(p, fetchLines, fetchCarts, fetchDelivery
441
443
  else {
442
444
  const { pdf } = await fetchDeliveryNote({ data: { orderRef: p.order.orderNumber } });
443
445
  if (!pdf)
444
- return { printed: 0, failed: 1, reasons: [`geen Engine-layout en geen Floriday-brief beschikbaar voor ${p.order.orderNumber}.`] };
446
+ return { printed: 0, failed: 1, reasons: [`Verzendbrief niet geprint: er is geen ontwerp ingesteld en Floriday heeft nog geen verzendbrief. Vraag kantoor het ontwerp in te stellen.`] };
445
447
  jobId = await printPdfDocument({ documenttype: "afleverbrief", documentnaam: `AFL-${p.order.orderNumber}`, pdfBase64: pdf.base64, volgStatus: false });
446
448
  }
447
449
  return jobId ? { printed: 1, failed: 0, reasons: [] } : { printed: 0, failed: 1, reasons: ["de Printwachtrij gaf geen opdracht-id terug."] };
@@ -477,7 +479,19 @@ function BufferGroup({ bufferId, orders: dayOrders }) {
477
479
  const fetchShippingNoteLayout = useServerFn(getShippingNoteLayout);
478
480
  const fetchPakbonInfo = useServerFn(getPakbonCartInfo);
479
481
  const shipState = useContext(ShipStateCtx);
480
- const carts = orders.reduce((s, o) => s + (o.carts || 0), 0);
482
+ // Tel op unieke kar-id: een gedeelde bufferkar telt één keer.
483
+ const bufCartIds = new Set();
484
+ const bufApproved = new Set();
485
+ for (const o of orders) {
486
+ const st = shipState.get(refKey(o.ref));
487
+ for (const id of st?.cartIds ?? [])
488
+ bufCartIds.add(id);
489
+ for (const id of st?.approvedCartIds ?? [])
490
+ bufApproved.add(id);
491
+ }
492
+ const carts = bufCartIds.size || orders.reduce((s, o) => s + (o.carts || 0), 0);
493
+ const scannedCount = [...bufCartIds].filter((id) => bufApproved.has(id)).length;
494
+ const bufferKlaar = bufCartIds.size > 0 && scannedCount === bufCartIds.size;
481
495
  const klant = orders.find((o) => o.planned?.order.customerName)?.planned?.order.customerName ?? null;
482
496
  const alleAf = orders.every((o) => shipState.get(refKey(o.ref))?.floridaySent === true);
483
497
  const refs = orders.map((o) => o.ref).filter(Boolean);
@@ -549,7 +563,7 @@ function BufferGroup({ bufferId, orders: dayOrders }) {
549
563
  setPrinting(false);
550
564
  }
551
565
  };
552
- return (_jsxs("div", { className: "rounded-lg border border-primary/40 bg-primary/5", 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-primary/10", 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-center gap-2 text-sm", children: [_jsx(Badge, { variant: "secondary", children: "Buffer" }), _jsxs("span", { className: "font-medium", children: [orders.length, " bestellingen samen"] }), klant ? _jsx("span", { className: "text-xs text-muted-foreground", children: klant }) : null] }), _jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsxs(Badge, { variant: "secondary", children: [carts, " ", carts === 1 ? "kar" : "karren", " vandaag"] }), _jsx(Badge, { variant: alleAf ? "default" : "outline", children: alleAf ? "Verzonden naar Floriday" : orders.every(engineKlaar) ? "Klaar voor verzending" : orders.some(engineKlaar) ? "Deels gescand" : "Nog te picken" }), _jsx("span", { className: "text-xs text-muted-foreground", children: refs.join(", ") })] })] })] }), _jsxs("div", { className: "flex flex-wrap items-center gap-2 border-t pt-3", children: [_jsxs(Button, { size: "sm", variant: "outline", onClick: afhandelen, disabled: busy || alleAf, title: "Alle bestellingen in deze buffer in \u00E9\u00E9n keer afhandelen", children: [_jsx(CheckCircle2, { className: "mr-1 h-3.5 w-3.5" }), alleAf ? "Afgehandeld" : busy ? "Bezig…" : "Buffer afhandelen (verzonden)"] }), refs[0] && alleFloriday ? _jsx(NuVersturenButton, { orderRef: refs[0], label: "Buffer naar Floriday versturen" }) : null, _jsxs(Button, { size: "sm", variant: "outline", onClick: printAlles, disabled: printing, title: "Alle pakbonnen van deze buffer afdrukken", children: [_jsx(Printer, { className: "mr-1 h-3.5 w-3.5" }), printing ? "In de Printwachtrij…" : "Documenten afdrukken"] }), _jsxs(Button, { size: "sm", variant: "outline", onClick: () => setVerzetOpen(true), title: "Alle bestellingen van deze buffer verzetten", children: [_jsx(CalendarClock, { className: "mr-1 h-3.5 w-3.5" }), "Verzetten"] })] })] }), open && (_jsx("div", { className: "space-y-2 border-t border-primary/30 px-2 py-2", children: orders.map((o) => o.planned ? (_jsx(OrderRow, { planned: o.planned, cartsToday: o.carts, engineStatus: o.status }, o.ref)) : (_jsx(EngineOrderRow, { order: o }, o.ref))) })), _jsx(BulkVerzetDialog, { open: verzetOpen, orders: verzetOrders, cartCount: carts, onOpenChange: setVerzetOpen, onDone: () => setVerzetOpen(false) })] }));
566
+ return (_jsxs("div", { className: "rounded-lg border border-primary/40 bg-primary/5", 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-primary/10", 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-center gap-2 text-sm", children: [_jsx(Badge, { variant: "secondary", children: "Buffer" }), _jsxs("span", { className: "font-medium", children: [orders.length, " bestellingen samen"] }), klant ? _jsx("span", { className: "text-xs text-muted-foreground", children: klant }) : null] }), _jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsxs(Badge, { variant: "secondary", children: [carts, " ", carts === 1 ? "kar" : "karren", " vandaag"] }), bufCartIds.size > 0 && (_jsxs(Badge, { variant: bufferKlaar ? "default" : "outline", children: [scannedCount, " van ", bufCartIds.size, " ", bufCartIds.size === 1 ? "kar" : "karren", " gescand"] })), _jsx(Badge, { variant: alleAf ? "default" : "outline", children: alleAf ? "Gemeld bij Floriday" : (bufCartIds.size > 0 ? bufferKlaar : orders.every(engineKlaar)) ? "Klaar voor verzending" : orders.some(engineKlaar) ? "Deels gescand" : "Nog te picken" }), _jsx("span", { className: "text-xs text-muted-foreground", children: refs.join(", ") })] })] })] }), _jsxs("div", { className: "flex flex-wrap items-center gap-2 border-t pt-3", children: [_jsxs(Button, { size: "sm", variant: "outline", onClick: afhandelen, disabled: busy || alleAf, title: "Alle bestellingen in deze buffer in \u00E9\u00E9n keer afhandelen", children: [_jsx(CheckCircle2, { className: "mr-1 h-3.5 w-3.5" }), alleAf ? "Afgehandeld" : busy ? "Bezig…" : "Buffer afhandelen (verzonden)"] }), refs[0] && alleFloriday && !alleAf ? _jsx(NuVersturenButton, { orderRef: refs[0], label: "Buffer naar Floriday versturen" }) : null, _jsxs(Button, { size: "sm", variant: "outline", onClick: printAlles, disabled: printing, title: "Alle pakbonnen van deze buffer afdrukken", children: [_jsx(Printer, { className: "mr-1 h-3.5 w-3.5" }), printing ? "In de Printwachtrij…" : "Documenten afdrukken"] }), _jsxs(Button, { size: "sm", variant: "outline", onClick: () => setVerzetOpen(true), title: "Alle bestellingen van deze buffer verzetten", children: [_jsx(CalendarClock, { className: "mr-1 h-3.5 w-3.5" }), "Verzetten"] })] })] }), open && (_jsx("div", { className: "space-y-2 border-t border-primary/30 px-2 py-2", children: orders.map((o) => o.planned ? (_jsx(OrderRow, { planned: o.planned, cartsToday: o.carts, engineStatus: o.status }, o.ref)) : (_jsx(EngineOrderRow, { order: o }, o.ref))) })), _jsx(BulkVerzetDialog, { open: verzetOpen, orders: verzetOrders, cartCount: carts, onOpenChange: setVerzetOpen, onDone: () => setVerzetOpen(false) })] }));
553
567
  }
554
568
  /** Bestelling die alleen de Engine kent; verdere ordergegevens ontbreken. */
555
569
  /** Knop "Afhandelen (verzonden)" — zet de bestelling in de orderadministratie op Verzonden. */
@@ -590,7 +604,7 @@ function AfhandelenButton({ orderRef, done }) {
590
604
  }
591
605
  function EngineOrderRow({ order }) {
592
606
  const vg = useVoortgang(order.ref, order.status);
593
- return (_jsx("div", { className: "rounded-lg border bg-card px-4 py-3", children: _jsxs("div", { className: "grid grid-cols-2 gap-3 text-sm md:grid-cols-4", children: [_jsxs("div", { children: [_jsx("div", { className: "font-medium", children: order.ref }), _jsx("div", { className: "text-xs text-muted-foreground", children: "Alleen bekend bij de Engine" })] }), _jsxs("div", { children: [_jsx("div", { className: "text-xs text-muted-foreground", children: "Karren vandaag" }), _jsx("div", { children: order.carts })] }), _jsxs("div", { children: [_jsx("div", { className: "text-xs text-muted-foreground", children: "Planten" }), _jsx("div", { children: order.plants })] }), _jsxs("div", { className: "flex items-center gap-2 flex-wrap", children: [_jsx(StatusBadge, { vg: vg }), _jsx(AfhandelenButton, { orderRef: order.ref, done: vg.done })] })] }) }));
607
+ return (_jsx("div", { className: "rounded-lg border bg-card px-4 py-3", children: _jsxs("div", { className: "grid grid-cols-2 gap-3 text-sm md:grid-cols-4", children: [_jsxs("div", { children: [_jsx("div", { className: "font-medium", children: order.ref }), _jsx("div", { className: "text-xs text-muted-foreground", children: "Alleen bekend in de planning" })] }), _jsxs("div", { children: [_jsx("div", { className: "text-xs text-muted-foreground", children: "Karren vandaag" }), _jsx("div", { children: order.carts })] }), _jsxs("div", { children: [_jsx("div", { className: "text-xs text-muted-foreground", children: "Planten" }), _jsx("div", { children: order.plants })] }), _jsxs("div", { className: "flex items-center gap-2 flex-wrap", children: [_jsx(StatusBadge, { vg: vg }), _jsx(AfhandelenButton, { orderRef: order.ref, done: vg.done })] })] }) }));
594
608
  }
595
609
  function OrderRow({ planned: p, cartsToday, engineStatus, }) {
596
610
  const [open, setOpen] = useState(false);
@@ -602,6 +616,7 @@ function OrderRow({ planned: p, cartsToday, engineStatus, }) {
602
616
  const fetchDeliveryNote = useServerFn(getDeliveryNotePdf);
603
617
  const fetchShippingNoteLayout = useServerFn(getShippingNoteLayout);
604
618
  const archivedQ = useOrderArchived(p.order.id);
619
+ const shipStateRow = useContext(ShipStateCtx).get(refKey(p.order.orderNumber));
605
620
  const fetchPakbonInfo = useServerFn(getPakbonCartInfo);
606
621
  const vg = useVoortgang(p.order.orderNumber, engineStatus ?? p.order.status, {
607
622
  floriday: !!p.order.salesOrderId,
@@ -633,7 +648,7 @@ function OrderRow({ planned: p, cartsToday, engineStatus, }) {
633
648
  };
634
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
635
650
  ? new Date(p.order.deliveryDate).toLocaleDateString("nl-NL")
636
- : "—" })] })] }), _jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsxs(Badge, { variant: "secondary", children: [cartsToday, " 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 ? _jsx(NuVersturenButton, { orderRef: p.order.orderNumber }) : null, p.order.salesOrderId ? _jsx(AutoSendStatus, { orderId: p.order.id }) : 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) => {
651
+ : "—" })] })] }), _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) => {
637
652
  e.stopPropagation();
638
653
  setVerzetOpen(true);
639
654
  }, title: "Ophaaldag of ophaaltijd van deze order wijzigen", children: [_jsx(CalendarClock, { className: "mr-1 h-3.5 w-3.5" }), "Verzetten"] })] })] }), _jsx(VerzetOrderDialog, { open: verzetOpen, orderRef: p.order.orderNumber, carrierTarget: {
@@ -670,7 +685,7 @@ function KarrenTab({ carts }) {
670
685
  const toggleAll = () => setSelected(allSelected ? new Set() : new Set(carts.map((c) => c.key)));
671
686
  const selectedCarts = carts.filter((c) => selected.has(c.key));
672
687
  const selectedOrders = Array.from(new Map(selectedCarts.map((c) => [c.orderRef, { ref: c.orderRef, customerName: c.customerName }])).values());
673
- return (_jsxs(_Fragment, { children: [_jsx("p", { className: "mb-3 text-xs text-muted-foreground", children: "De indeling komt uit de Logistics Engine. Verzetten gebeurt per bestelling: alle karren van die bestelling schuiven mee." }), _jsxs("div", { className: "mb-3 flex flex-wrap items-center justify-between gap-3 rounded-md border bg-muted/30 px-3 py-2", children: [_jsxs("label", { className: "flex items-center gap-2 text-sm", children: [_jsx(Checkbox, { checked: allSelected, onCheckedChange: toggleAll }), _jsx("span", { children: selected.size > 0
688
+ return (_jsxs(_Fragment, { children: [_jsx("p", { className: "mb-3 text-xs text-muted-foreground", children: "De indeling komt uit de planning. Verzetten gebeurt per bestelling: alle karren van die bestelling schuiven mee." }), _jsxs("div", { className: "mb-3 flex flex-wrap items-center justify-between gap-3 rounded-md border bg-muted/30 px-3 py-2", children: [_jsxs("label", { className: "flex items-center gap-2 text-sm", children: [_jsx(Checkbox, { checked: allSelected, onCheckedChange: toggleAll }), _jsx("span", { children: selected.size > 0
674
689
  ? `${selected.size} kar${selected.size === 1 ? "" : "ren"} geselecteerd · ${selectedOrders.length} bestelling${selectedOrders.length === 1 ? "" : "en"}`
675
690
  : "Alles selecteren" })] }), _jsxs("div", { className: "flex items-center gap-2", children: [selected.size > 0 && (_jsx(Button, { size: "sm", variant: "ghost", onClick: () => setSelected(new Set()), children: "Selectie wissen" })), _jsxs(Button, { size: "sm", disabled: selected.size === 0, onClick: () => setBulkOpen(true), children: [_jsx(CalendarClock, { className: "mr-1 h-3.5 w-3.5" }), "Geselecteerde verzetten"] })] })] }), _jsx("div", { className: "grid gap-3", children: carts.map((c) => (_jsx(KarRow, { cart: c, cartTotal: carts.filter((x) => x.orderRef === c.orderRef).length, selected: selected.has(c.key), onToggle: () => toggle(c.key) }, c.key))) }), _jsx(BulkVerzetDialog, { open: bulkOpen, orders: selectedOrders, cartCount: selected.size, onOpenChange: setBulkOpen, onDone: () => setSelected(new Set()) })] }));
676
691
  }
@@ -738,10 +753,10 @@ function KarRow({ cart: c, cartTotal, selected, onToggle, }) {
738
753
  }
739
754
  }
740
755
  if (!c.cartId)
741
- throw new Error(`Pakbon ${c.cartNumber}: de Engine heeft voor deze kar nog geen kar-id.`);
756
+ throw new Error(`Pakbon ${c.cartNumber} kan nog niet worden geprint: de kar is nog niet ingedeeld. Probeer het over een minuut opnieuw.`);
742
757
  const [ci] = (await fetchPakbonInfo({ data: { cartIds: [c.cartId] } })).carts;
743
758
  if (!ci)
744
- throw new Error(`Pakbon ${c.cartNumber}: geen kar-barcode van de Engine.`);
759
+ throw new Error(`Pakbon ${c.cartNumber} kan nog niet worden geprint: de kar is nog niet ingedeeld. Probeer het over een minuut opnieuw.`);
745
760
  const nr = String(c.cartIndex + 1).padStart(2, "0");
746
761
  await printLayoutDocument({
747
762
  documenttype: "pakbon",
@@ -807,7 +822,7 @@ function KarRow({ cart: c, cartTotal, selected, onToggle, }) {
807
822
  // 2) Geen eigen layout ingesteld: dan de Floriday-brief, als die er is.
808
823
  const { pdf } = await fetchDeliveryNote({ data: { orderRef: c.orderRef } });
809
824
  if (!pdf) {
810
- toast.error("Er is geen verzendbrief-layout gekoppeld in de Logistieke Engine en geen Floriday-brief beschikbaar.");
825
+ toast.error("Verzendbrief niet geprint: er is geen ontwerp ingesteld en Floriday heeft nog geen verzendbrief. Vraag kantoor het ontwerp in te stellen.");
811
826
  return;
812
827
  }
813
828
  await printPdfDocument({
@@ -1 +1 @@
1
- {"version":3,"file":"LeveringenPage.d.ts","sourceRoot":"","sources":["../../../src/components/afhandeling/LeveringenPage.tsx"],"names":[],"mappings":"AAMA,wBAAgB,cAAc,gCA6B7B"}
1
+ {"version":3,"file":"LeveringenPage.d.ts","sourceRoot":"","sources":["../../../src/components/afhandeling/LeveringenPage.tsx"],"names":[],"mappings":"AAOA,wBAAgB,cAAc,gCAqC7B"}
@@ -2,11 +2,13 @@ import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
2
2
  import { useMemo, useState } from "react";
3
3
  import { Button } from "@flowselections/core";
4
4
  import { WeekAgendaView } from "./WeekAgendaView";
5
+ import { DagDetailDialog } from "./DagDetailDialog";
5
6
  import { useOrderPlanning } from "../../hooks/useOrderPlanning";
6
7
  import { agendaRange, currentWeekStart } from "../../lib/agendaRange";
7
8
  export function LeveringenPage() {
8
9
  const [weekStart, setWeekStart] = useState(() => currentWeekStart());
9
10
  const range = useMemo(() => agendaRange(weekStart), [weekStart]);
10
11
  const { planned, nearestDeliveryDate, isLoading, error, warning, refetch } = useOrderPlanning(range);
11
- 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, jumpToDate: nearestDeliveryDate }))] }));
12
+ 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) })] }));
12
14
  }
@@ -1 +1 @@
1
- {"version":3,"file":"NuVersturenButton.d.ts","sourceRoot":"","sources":["../../../src/components/afhandeling/NuVersturenButton.tsx"],"names":[],"mappings":"AAqBA,wBAAgB,iBAAiB,CAAC,EAAE,QAAQ,EAAE,KAAsB,EAAE,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,+BAkI3G"}
1
+ {"version":3,"file":"NuVersturenButton.d.ts","sourceRoot":"","sources":["../../../src/components/afhandeling/NuVersturenButton.tsx"],"names":[],"mappings":"AAqBA,wBAAgB,iBAAiB,CAAC,EAAE,QAAQ,EAAE,KAAsB,EAAE,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,+BAiI3G"}
@@ -7,7 +7,7 @@ import { Send, Loader2, CheckCircle2, XCircle } from "lucide-react";
7
7
  import { prepareFloridayLoading, sendFloridayLoading, } from "../../lib/floridayLoading.functions";
8
8
  /** "Nu versturen": toont eerst een samenvatting met controles en vraagt bevestiging. */
9
9
  const PICK_LABEL = {
10
- geen_pickrij: "geen pickrij bij de Engine",
10
+ geen_pickrij: "nog niet gepickt",
11
11
  open: "nog te picken",
12
12
  bezig: "wordt gepickt",
13
13
  gepickt: "gepickt",
@@ -70,11 +70,11 @@ export function NuVersturenButton({ orderRef, label = "Nu versturen" }) {
70
70
  setSending(false);
71
71
  }
72
72
  };
73
- return (_jsxs(_Fragment, { children: [_jsxs(Button, { size: "sm", variant: "outline", onClick: open, disabled: loading, children: [loading ? _jsx(Loader2, { className: "h-3 w-3 mr-1 animate-spin" }) : _jsx(Send, { className: "h-3 w-3 mr-1" }), label] }), _jsx(AlertDialog, { open: !!preview, onOpenChange: (o) => !o && !sending && setPreview(null), children: _jsxs(AlertDialogContent, { children: [_jsxs(AlertDialogHeader, { children: [_jsx(AlertDialogTitle, { children: "Belading naar Floriday versturen?" }), _jsx(AlertDialogDescription, { children: "Een geaccepteerde opdracht start de verzendbrief, vooraankondiging, fust-administratie en afrekening, en is op productie niet terug te draaien." })] }), preview && (_jsxs("div", { className: "space-y-2 text-sm", children: [_jsxs("div", { children: [_jsx("span", { className: "text-muted-foreground", children: "Orders:" }), " ", preview.orderRefs.join(", ")] }), _jsxs("div", { children: [_jsx("span", { className: "text-muted-foreground", children: "Leveringen:" }), " ", preview.shipments.length] }), _jsxs("div", { children: [_jsx("span", { className: "text-muted-foreground", children: "Karren:" }), " ", preview.cartCount, preview.carriers.some((c) => c.shared) ? " (waarvan gedeeld)" : ""] }), Array.isArray(preview.carriers) && preview.carriers.length > 0 && (_jsxs("div", { children: [_jsx("span", { className: "text-muted-foreground", children: "Karkenmerk naar Floriday:" }), " ", _jsx("span", { className: "font-mono", children: preview.carriers.map((c) => c.loadCarrierReference || "—").join(", ") })] })), _jsxs("div", { children: [_jsx("span", { className: "text-muted-foreground", children: "Regels:" }), " ", preview.lineCount] }), _jsx("ul", { className: "space-y-1", children: preview.orders.map((o) => (_jsxs("li", { className: "flex items-center gap-2", children: [o.controlled
73
+ return (_jsxs(_Fragment, { children: [_jsxs(Button, { size: "sm", variant: "outline", onClick: open, disabled: loading, children: [loading ? _jsx(Loader2, { className: "h-3 w-3 mr-1 animate-spin" }) : _jsx(Send, { className: "h-3 w-3 mr-1" }), label] }), _jsx(AlertDialog, { open: !!preview, onOpenChange: (o) => !o && !sending && setPreview(null), children: _jsxs(AlertDialogContent, { children: [_jsxs(AlertDialogHeader, { children: [_jsx(AlertDialogTitle, { children: "Belading naar Floriday versturen?" }), _jsx(AlertDialogDescription, { children: "Na versturen maakt Floriday de verzendbrief, vooraanmelding en fustafrekening. Dit kun je niet terugdraaien." })] }), preview && (_jsxs("div", { className: "space-y-2 text-sm", children: [_jsxs("div", { children: [_jsx("span", { className: "text-muted-foreground", children: "Orders:" }), " ", preview.orderRefs.join(", ")] }), _jsxs("div", { children: [_jsx("span", { className: "text-muted-foreground", children: "Leveringen:" }), " ", preview.shipments.length] }), _jsxs("div", { children: [_jsx("span", { className: "text-muted-foreground", children: "Karren:" }), " ", preview.cartCount, preview.carriers.some((c) => c.shared) ? " (waarvan gedeeld)" : ""] }), Array.isArray(preview.carriers) && preview.carriers.length > 0 && (_jsxs("div", { children: [_jsx("span", { className: "text-muted-foreground", children: "Karkenmerk naar Floriday:" }), " ", _jsx("span", { className: "font-mono", children: preview.carriers.map((c) => c.loadCarrierReference || "—").join(", ") })] })), _jsxs("div", { children: [_jsx("span", { className: "text-muted-foreground", children: "Regels:" }), " ", preview.lineCount] }), _jsx("ul", { className: "space-y-1", children: preview.orders.map((o) => (_jsxs("li", { className: "flex items-center gap-2", children: [o.controlled
74
74
  ? _jsx(CheckCircle2, { className: "h-3.5 w-3.5 text-primary" })
75
- : _jsx(XCircle, { className: "h-3.5 w-3.5 text-destructive" }), _jsxs("span", { children: [o.orderRef, ": ", o.cartsControlled, " van ", o.cartsTotal, " karren goedgekeurd op de Scan-controle", o.controlled ? " — klaar voor verzending" : "", !o.controlled && o.pickStatus && o.pickStatus !== "geen_pickrij" ? ` · pickstatus: ${PICK_LABEL[o.pickStatus] ?? o.pickStatus}` : "", o.autoStatus ? ` · auto-verwerking: ${o.autoStatus}` : ""] })] }, o.orderRef))) }), preview.shipments.filter((sh) => sh.floridayStatus || preview.lastAttempts.some((a) => a.fulfillmentOrderId === sh.fulfillmentOrderId && (a.code === "created" || a.code === "already_fulfilled"))).map((sh) => (_jsxs("div", { className: "rounded border border-border p-2", children: ["Verzonden naar Floriday", sh.floridayStatus ? ` (Floriday: ${sh.floridayStatus})` : "", " \u00B7", " ", sh.deliveryNoteReady === true ? "verzendbrief klaar" : "verzendbrief volgt via de Engine"] }, sh.fulfillmentOrderId))), preview.lastAttempts.length > 0 && (_jsxs("div", { className: "rounded border border-border p-2", children: [_jsx("div", { className: "text-muted-foreground mb-1", children: "Laatste pogingen" }), preview.lastAttempts.slice(0, 4).map((a) => (_jsxs("div", { children: [new Date(a.createdAt).toLocaleString("nl-NL"), " \u00B7 versturen \u00B7 ", a.code === "created" || a.code === "already_fulfilled" ? "geaccepteerd" : (a.message ?? a.code)?.replace(/\s*\[[^\]]+\]$/, "")] }, a.createdAt + a.step)))] })), preview.blockers.length > 0 && (_jsx("ul", { className: "list-disc pl-5 text-destructive", children: preview.blockers.map((b) => {
75
+ : _jsx(XCircle, { className: "h-3.5 w-3.5 text-destructive" }), _jsxs("span", { children: [o.orderRef, ": ", o.cartsControlled, " van ", o.cartsTotal, " karren goedgekeurd op de Scan-controle", o.controlled ? " — klaar voor verzending" : "", !o.controlled && o.pickStatus && o.pickStatus !== "geen_pickrij" ? ` · pickstatus: ${PICK_LABEL[o.pickStatus] ?? o.pickStatus}` : "", o.autoStatus ? ` · auto-verwerking: ${o.autoStatus}` : ""] })] }, o.orderRef))) }), preview.shipments.filter((sh) => sh.floridayStatus || preview.lastAttempts.some((a) => a.fulfillmentOrderId === sh.fulfillmentOrderId && (a.code === "created" || a.code === "already_fulfilled"))).map((sh) => (_jsxs("div", { className: "rounded border border-border p-2", children: ["Gemeld bij Floriday", sh.floridayStatus ? ` (Floriday: ${sh.floridayStatus})` : "", " \u00B7", " ", sh.deliveryNoteReady === true ? "verzendbrief klaar" : "verzendbrief wordt automatisch geprint"] }, sh.fulfillmentOrderId))), preview.lastAttempts.length > 0 && (_jsxs("div", { className: "rounded border border-border p-2", children: [_jsx("div", { className: "text-muted-foreground mb-1", children: "Laatste pogingen" }), preview.lastAttempts.slice(0, 4).map((a) => (_jsxs("div", { children: [new Date(a.createdAt).toLocaleString("nl-NL"), " \u00B7 versturen \u00B7 ", a.code === "created" || a.code === "already_fulfilled" ? "geaccepteerd" : (a.message ?? a.code)?.replace(/\s*\[[^\]]+\]$/, "")] }, a.createdAt + a.step)))] })), preview.blockers.length > 0 && (_jsx("ul", { className: "list-disc pl-5 text-destructive", children: preview.blockers.map((b) => {
76
76
  // Begrijpelijke zin, technische code klein eronder.
77
77
  const m = /^(.*?)\s*\[([^\]]+)\]$/s.exec(b);
78
78
  return (_jsxs("li", { children: [m ? m[1] : b, m ? _jsx("div", { className: "text-xs text-muted-foreground", children: m[2] }) : null] }, b));
79
- }) }))] })), _jsxs(AlertDialogFooter, { children: [_jsx(AlertDialogCancel, { disabled: sending, children: "Annuleren" }), _jsxs(Button, { onClick: doSend, disabled: !preview?.canSend || sending || sent, children: [sending ? _jsx(Loader2, { className: "h-3 w-3 mr-1 animate-spin" }) : null, sent ? "Verzonden naar Floriday" : "Versturen"] })] })] }) })] }));
79
+ }) }))] })), _jsxs(AlertDialogFooter, { children: [_jsx(AlertDialogCancel, { disabled: sending, children: "Annuleren" }), _jsxs(Button, { onClick: doSend, disabled: !preview?.canSend || sending || sent, children: [sending ? _jsx(Loader2, { className: "h-3 w-3 mr-1 animate-spin" }) : null, sent ? "Gemeld bij Floriday" : "Versturen"] })] })] }) })] }));
80
80
  }
@@ -1 +1 @@
1
- {"version":3,"file":"PickAgendaView.d.ts","sourceRoot":"","sources":["../../../src/components/afhandeling/PickAgendaView.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAElE,MAAM,WAAW,mBAAmB;IAClC,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;CAC/C;AA2BD,wBAAgB,cAAc,CAAC,EAAE,YAAY,EAAE,EAAE,mBAAmB,+BAwFnE"}
1
+ {"version":3,"file":"PickAgendaView.d.ts","sourceRoot":"","sources":["../../../src/components/afhandeling/PickAgendaView.tsx"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAElE,MAAM,WAAW,mBAAmB;IAClC,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;CAC/C;AA2BD,wBAAgB,cAAc,CAAC,EAAE,YAAY,EAAE,EAAE,mBAAmB,+BAyFnE"}
@@ -3,6 +3,7 @@ import { useMemo, useState } from "react";
3
3
  import { Button } from "@flowselections/core";
4
4
  import { WeekAgendaView } from "./WeekAgendaView";
5
5
  import { DagDetailDialog } from "./DagDetailDialog";
6
+ import { useVestigingTijdzone } from "./AutoSendStatus";
6
7
  import { useOrderPlanning } from "../../hooks/useOrderPlanning";
7
8
  import { agendaRange, currentWeekStart } from "../../lib/agendaRange";
8
9
  /** Toon hooguit dit aantal ordernummers per categorie; de rest telt als "meer". */
@@ -23,19 +24,20 @@ export function PickAgendaView({ onOrderClick }) {
23
24
  const { planned, awaiting, tasksWithoutStart, nearestDeliveryDate, signalError, lastEngineRun, isLoading, error, warning, refetch, } = useOrderPlanning(range);
24
25
  const awaitingCount = awaiting.length;
25
26
  const anyIssue = awaitingCount + tasksWithoutStart.length > 0;
26
- const lastRunLabel = lastEngineRun
27
+ const vestigingTz = useVestigingTijdzone().data ?? null;
28
+ const lastRunLabel = lastEngineRun && vestigingTz
27
29
  ? new Date(lastEngineRun).toLocaleString("nl-NL", {
28
- timeZone: "Europe/Amsterdam",
30
+ timeZone: vestigingTz,
29
31
  dateStyle: "short",
30
32
  timeStyle: "short",
31
33
  })
32
34
  : null;
33
35
  const [dayDetailDate, setDayDetailDate] = useState(null);
34
- return (_jsxs("div", { className: "flex h-full flex-col gap-6 overflow-hidden", children: [anyIssue && (_jsxs("div", { className: "shrink-0 space-y-1 rounded-md border border-sky-300 bg-sky-50 p-4 text-sm text-sky-900", children: [_jsx(CategoryLine, { items: awaiting, label: (n) => `${n} order${n === 1 ? "" : "s"} zonder indeling van de Logistics Engine, daarom nog niet in de agenda` }), _jsx(CategoryLine, { items: tasksWithoutStart, label: (n) => `${n} order${n === 1 ? "" : "s"} met een Engine-taak zonder geldige starttijd` }), _jsxs("div", { className: "text-xs", children: [lastRunLabel
35
- ? `Laatste indeling door de Engine: ${lastRunLabel}.`
36
- : "Er is nog geen indeling van de Engine bekend voor deze periode.", " ", awaitingCount === 0
36
+ return (_jsxs("div", { className: "flex h-full flex-col gap-6 overflow-hidden", children: [anyIssue && (_jsxs("div", { className: "shrink-0 space-y-1 rounded-md border border-sky-300 bg-sky-50 p-4 text-sm text-sky-900", children: [_jsx(CategoryLine, { items: awaiting, label: (n) => `${n} order${n === 1 ? "" : "s"} ${n === 1 ? "is" : "zijn"} nog niet over karren verdeeld en ${n === 1 ? "staat" : "staan"} daarom nog niet in de agenda` }), _jsx(CategoryLine, { items: tasksWithoutStart, label: (n) => `${n} order${n === 1 ? " heeft" : "s hebben"} nog geen starttijd voor het picken. Controleer de werktijden` }), _jsxs("div", { className: "text-xs", children: [lastRunLabel
37
+ ? `Laatste indeling door de planning: ${lastRunLabel}.`
38
+ : "Er is nog geen indeling bekend voor deze periode.", " ", awaitingCount === 0
37
39
  ? "Orders die buiten deze week zijn ingedeeld staan hier niet bij."
38
40
  : signalError
39
- ? `Melding vanuit deze module is mislukt: ${signalError}`
40
- : "Melding vanuit deze module is verstuurd; het inplannen gebeurt door de Logistics Engine."] })] })), warning && !error && (_jsxs("div", { className: "shrink-0 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: "shrink-0 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("div", { className: "min-h-0 flex-1", children: _jsx(WeekAgendaView, { planned: planned, mode: "pick", isLoading: isLoading, onOrderClick: onOrderClick, onDayClick: setDayDetailDate, onWeekChange: setWeekStart, jumpToDate: nearestDeliveryDate }) })), _jsx(DagDetailDialog, { open: !!dayDetailDate, date: dayDetailDate, planned: planned, onOpenChange: (o) => !o && setDayDetailDate(null) })] }));
41
+ ? signalError
42
+ : "De orders worden automatisch ingepland."] })] })), warning && !error && (_jsxs("div", { className: "shrink-0 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: "shrink-0 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("div", { className: "min-h-0 flex-1", children: _jsx(WeekAgendaView, { planned: planned, mode: "pick", isLoading: isLoading, onOrderClick: onOrderClick, onDayClick: setDayDetailDate, onWeekChange: setWeekStart, jumpToDate: nearestDeliveryDate }) })), _jsx(DagDetailDialog, { open: !!dayDetailDate, date: dayDetailDate, planned: planned, onOpenChange: (o) => !o && setDayDetailDate(null) })] }));
41
43
  }
@@ -1 +1 @@
1
- {"version":3,"file":"ScanControleView.d.ts","sourceRoot":"","sources":["../../../src/components/afhandeling/ScanControleView.tsx"],"names":[],"mappings":"AA4FA,wBAAgB,gBAAgB,gCAqV/B"}
1
+ {"version":3,"file":"ScanControleView.d.ts","sourceRoot":"","sources":["../../../src/components/afhandeling/ScanControleView.tsx"],"names":[],"mappings":"AA4FA,wBAAgB,gBAAgB,gCAiV/B"}
@@ -101,7 +101,7 @@ export function ScanControleView() {
101
101
  return;
102
102
  }
103
103
  toast.success(res.isFloriday
104
- ? "Goedgekeurd — de Floriday-afleverbrief wordt opgehaald en geprint."
104
+ ? "Goedgekeurd — de Floriday-verzendbrief wordt geprint."
105
105
  : "Goedgekeurd — de afleverbrief staat klaar op de printer.");
106
106
  setLookup(null);
107
107
  setBarcode("");
@@ -144,7 +144,7 @@ export function ScanControleView() {
144
144
  return _jsx("div", { className: "text-sm text-muted-foreground", children: "Controlepost laden\u2026" });
145
145
  }
146
146
  if (!postKlaar) {
147
- return (_jsx(Card, { className: "p-4 border-destructive/40 bg-destructive/10", children: _jsxs("div", { className: "flex items-start gap-2 text-destructive", children: [_jsx(AlertTriangle, { className: "mt-0.5 h-4 w-4 shrink-0" }), _jsxs("div", { className: "text-sm", children: [_jsx("div", { className: "font-medium", children: "Controlepost nog niet ingesteld" }), _jsxs("p", { children: ["Er is nog geen ", scannerId ? "printer" : printerId ? "scanner" : "scanner en printer", " ", "gekozen bij Logistiek Engine \u2192 stap 6 \u201CControle\u201D \u2192 Scancontrole. Zolang dat ontbreekt kan er niet gescand worden."] })] })] }) }));
147
+ return (_jsx(Card, { className: "p-4 border-destructive/40 bg-destructive/10", children: _jsxs("div", { className: "flex items-start gap-2 text-destructive", children: [_jsx(AlertTriangle, { className: "mt-0.5 h-4 w-4 shrink-0" }), _jsxs("div", { className: "text-sm", children: [_jsx("div", { className: "font-medium", children: "Controlepost nog niet ingesteld" }), _jsx("p", { children: "De karrencontrole is nog niet ingesteld (scanner en printer). Vraag kantoor dit te regelen." })] })] }) }));
148
148
  }
149
149
  const cart = lookup?.cart ?? null;
150
150
  return (_jsxs("div", { className: "space-y-4", children: [_jsxs(Card, { className: "p-4 space-y-3", children: [_jsxs("form", { className: "flex gap-2", onSubmit: (e) => {
@@ -171,7 +171,7 @@ export function VerzetOrderDialog({ open, orderRef, carrierTarget, onOpenChange
171
171
  `(${result.next?.pickupFrom ?? "—"}–${result.next?.pickupTo ?? "—"}), ` +
172
172
  `leverdatum ${fmtDate(result.next?.deliveryDate)}. ` +
173
173
  (result.customerNotified
174
- ? "De Logistiek Engine heeft de klant per e-mail geïnformeerd."
174
+ ? "De klant is per e-mail geïnformeerd."
175
175
  : "Klant is niet geïnformeerd."));
176
176
  onOpenChange(false);
177
177
  }
@@ -193,11 +193,11 @@ export function VerzetOrderDialog({ open, orderRef, carrierTarget, onOpenChange
193
193
  }
194
194
  void submit(false);
195
195
  };
196
- return (_jsxs(Dialog, { open: open, onOpenChange: onOpenChange, children: [_jsxs(ResponsiveDialogContent, { size: "lg", children: [_jsxs(ResponsiveDialogHeader, { children: [_jsxs(DialogTitle, { children: ["Order verzetten \u2014 ", orderRef] }), _jsx(DialogDescription, { children: "De Logistics Engine bepaalt de gevolgen; deze module past niets zelf aan." })] }), _jsxs(ResponsiveDialogBody, { className: "space-y-4", children: [_jsxs("div", { className: "rounded-md border p-3 space-y-2", children: [_jsx("p", { className: "text-xs font-medium text-muted-foreground", children: "Pickmoment (dag waarop de order gemaakt wordt)" }), pickMoments.length === 0 ? (_jsx("p", { className: "text-sm text-muted-foreground", children: "Nog geen pickmoment van de Engine." })) : (_jsxs(_Fragment, { children: [_jsxs("p", { className: "text-sm", children: ["Nu ingepland: ", fmtDateTime(pickMoments[0]?.plannedStart)] }), _jsxs("div", { className: "space-y-1", children: [_jsx(Label, { children: "Nieuwe pickdag" }), _jsx(Input, { type: "date", value: pickDay, onChange: (e) => setPickDayValue(e.target.value), className: "sm:w-56" })] }), pickDayChanged && (_jsxs("p", { className: "text-xs text-muted-foreground", children: [pickMoments.length, " pick-taak(en) worden naar deze dag verzet; het tijdstip blijft gelijk."] }))] }))] }), _jsxs("div", { className: "rounded-md border p-3 space-y-2", children: [_jsxs("p", { className: "text-xs font-medium text-muted-foreground", children: ["Vervoerder", carrier ? `: ${carrier.name}` : ""] }), _jsxs("div", { className: "space-y-1", children: [_jsx(Label, { children: "Vervoersdatum" }), _jsx(Input, { type: "date", value: pickupDate, onChange: (e) => setPickupDate(e.target.value), className: "sm:w-56" })] }), carrierMoments.length === 0 ? (_jsx("p", { className: "text-sm text-muted-foreground", children: "Geen vervoerder gekoppeld \u2014 aanmeldtijden zijn niet bekend." })) : (_jsxs(_Fragment, { children: [_jsxs("p", { className: "text-sm", children: ["Huidige aanmeldtijd: ", pickupFrom || "—", pickupTo && pickupTo !== pickupFrom ? `–${pickupTo}` : ""] }), _jsx("div", { className: "flex flex-wrap gap-2", children: carrierMoments.map((m, i) => {
196
+ return (_jsxs(Dialog, { open: open, onOpenChange: onOpenChange, children: [_jsxs(ResponsiveDialogContent, { size: "lg", children: [_jsxs(ResponsiveDialogHeader, { children: [_jsxs(DialogTitle, { children: ["Order verzetten \u2014 ", orderRef] }), _jsx(DialogDescription, { children: "De planning wordt automatisch aangepast." })] }), _jsxs(ResponsiveDialogBody, { className: "space-y-4", children: [_jsxs("div", { className: "rounded-md border p-3 space-y-2", children: [_jsx("p", { className: "text-xs font-medium text-muted-foreground", children: "Pickmoment (dag waarop de order gemaakt wordt)" }), pickMoments.length === 0 ? (_jsx("p", { className: "text-sm text-muted-foreground", children: "Nog geen pickmoment bekend." })) : (_jsxs(_Fragment, { children: [_jsxs("p", { className: "text-sm", children: ["Nu ingepland: ", fmtDateTime(pickMoments[0]?.plannedStart)] }), _jsxs("div", { className: "space-y-1", children: [_jsx(Label, { children: "Nieuwe pickdag" }), _jsx(Input, { type: "date", value: pickDay, onChange: (e) => setPickDayValue(e.target.value), className: "sm:w-56" })] }), pickDayChanged && (_jsxs("p", { className: "text-xs text-muted-foreground", children: [pickMoments.length, " pick-taak(en) worden naar deze dag verzet; het tijdstip blijft gelijk."] }))] }))] }), _jsxs("div", { className: "rounded-md border p-3 space-y-2", children: [_jsxs("p", { className: "text-xs font-medium text-muted-foreground", children: ["Vervoerder", carrier ? `: ${carrier.name}` : ""] }), _jsxs("div", { className: "space-y-1", children: [_jsx(Label, { children: "Vervoersdatum" }), _jsx(Input, { type: "date", value: pickupDate, onChange: (e) => setPickupDate(e.target.value), className: "sm:w-56" })] }), carrierMoments.length === 0 ? (_jsx("p", { className: "text-sm text-muted-foreground", children: "Geen vervoerder gekoppeld \u2014 aanmeldtijden zijn niet bekend." })) : (_jsxs(_Fragment, { children: [_jsxs("p", { className: "text-sm", children: ["Huidige aanmeldtijd: ", pickupFrom || "—", pickupTo && pickupTo !== pickupFrom ? `–${pickupTo}` : ""] }), _jsx("div", { className: "flex flex-wrap gap-2", children: carrierMoments.map((m, i) => {
197
197
  const active = pickupFrom === m.signup_time;
198
198
  return (_jsxs(Button, { type: "button", size: "sm", variant: active ? "default" : "outline", onClick: () => {
199
199
  setPickupFrom(m.signup_time);
200
200
  setPickupTo(m.signup_time);
201
201
  }, children: [m.signup_time, _jsxs("span", { className: "ml-1 text-xs opacity-70", children: ["(", m.signup_days, " wd vooraf \u00B7 ophalen ", m.pickup_time, ")"] })] }, `${m.signup_time}-${i}`));
202
- }) }), _jsx("p", { className: "text-xs text-muted-foreground", children: "Kies een aanmeldtijd van deze vervoerder." })] }))] }), preview?.current && (_jsxs("p", { className: "text-xs text-muted-foreground", children: ["Huidig aanmeldmoment: ", fmtDate(preview.current.pickupDate), " ", preview.current.pickupFrom ?? "—", "\u2013", preview.current.pickupTo ?? "—", " \u00B7 leverdatum ", fmtDate(preview.current.deliveryDate)] })), loadingPreview && _jsx("p", { className: "text-sm text-muted-foreground", children: "Voorvertoning ophalen\u2026" }), preview && preview.ok === false && (_jsx("div", { className: "rounded-md border border-destructive/30 bg-destructive/5 p-3 text-sm text-destructive", children: preview.error ?? "De Engine kan deze wijziging niet verwerken." })), preview && preview.ok !== false && (_jsxs("div", { className: "rounded-md border bg-muted/40 p-3 text-sm space-y-2", children: [_jsx("p", { children: summary }), _jsxs("p", { className: "text-xs text-muted-foreground", children: [preview.affectedTasks ?? 0, " taken worden opnieuw ingepland."] }), preview.planLocked && (_jsx(Badge, { variant: "outline", children: "Planning is vergrendeld door de Engine" }))] })), needsCustomerChoice && (_jsxs("div", { className: "space-y-1 rounded-md border p-3", children: [_jsx(Label, { children: "Reden voor de klantmelding (optioneel)" }), _jsx(Textarea, { value: reason, onChange: (e) => setReason(e.target.value), maxLength: 500, placeholder: "Bijv. Aanvoer bloemen vertraagd" })] })), history.length > 0 && (_jsxs("div", { className: "space-y-1", children: [_jsx("p", { className: "text-xs font-medium text-muted-foreground", children: "Verzetgeschiedenis" }), _jsx("ul", { className: "space-y-1 text-xs text-muted-foreground", children: history.map((h) => (_jsxs("li", { children: [new Date(h.created_at).toLocaleString("nl-NL"), " \u2014", " ", String(h.direction ?? ""), " ", String(h.reason ?? "")] }, h.id))) })] }))] }), _jsxs(ResponsiveDialogFooter, { children: [_jsx(Button, { variant: "outline", onClick: () => onOpenChange(false), children: "Annuleren" }), _jsx(Button, { onClick: handleVerzetten, disabled: saving || loadingPreview || (!pickDayChanged && (!transportChanged || blocked)), children: saving ? "Opslaan…" : "Verzetten" })] })] }), _jsx(AlertDialog, { open: confirmOpen, onOpenChange: setConfirmOpen, children: _jsxs(AlertDialogContent, { children: [_jsxs(AlertDialogHeader, { children: [_jsx(AlertDialogTitle, { children: "Klant informeren?" }), _jsx(AlertDialogDescription, { children: "De leverdatum is gewijzigd naar een latere datum. Wilt u de klant hierover informeren?" })] }), _jsxs(AlertDialogFooter, { children: [_jsx(AlertDialogCancel, { onClick: () => void submit(false), children: "Nee" }), _jsx(AlertDialogAction, { onClick: () => void submit(true), children: "Ja" })] })] }) })] }));
202
+ }) }), _jsx("p", { className: "text-xs text-muted-foreground", children: "Kies een aanmeldtijd van deze vervoerder." })] }))] }), preview?.current && (_jsxs("p", { className: "text-xs text-muted-foreground", children: ["Huidig aanmeldmoment: ", fmtDate(preview.current.pickupDate), " ", preview.current.pickupFrom ?? "—", "\u2013", preview.current.pickupTo ?? "—", " \u00B7 leverdatum ", fmtDate(preview.current.deliveryDate)] })), loadingPreview && _jsx("p", { className: "text-sm text-muted-foreground", children: "Voorvertoning ophalen\u2026" }), preview && preview.ok === false && (_jsx("div", { className: "rounded-md border border-destructive/30 bg-destructive/5 p-3 text-sm text-destructive", children: preview.error ?? "Deze wijziging kan nu niet worden verwerkt." })), preview && preview.ok !== false && (_jsxs("div", { className: "rounded-md border bg-muted/40 p-3 text-sm space-y-2", children: [_jsx("p", { children: summary }), _jsxs("p", { className: "text-xs text-muted-foreground", children: [preview.affectedTasks ?? 0, " taken worden opnieuw ingepland."] }), preview.planLocked && (_jsx(Badge, { variant: "outline", children: "Deze order ligt vast en kan nu niet worden verzet." }))] })), needsCustomerChoice && (_jsxs("div", { className: "space-y-1 rounded-md border p-3", children: [_jsx(Label, { children: "Reden voor de klantmelding (optioneel)" }), _jsx(Textarea, { value: reason, onChange: (e) => setReason(e.target.value), maxLength: 500, placeholder: "Bijv. Aanvoer bloemen vertraagd" })] })), history.length > 0 && (_jsxs("div", { className: "space-y-1", children: [_jsx("p", { className: "text-xs font-medium text-muted-foreground", children: "Verzetgeschiedenis" }), _jsx("ul", { className: "space-y-1 text-xs text-muted-foreground", children: history.map((h) => (_jsxs("li", { children: [new Date(h.created_at).toLocaleString("nl-NL"), " \u2014", " ", String(h.direction ?? ""), " ", String(h.reason ?? "")] }, h.id))) })] }))] }), _jsxs(ResponsiveDialogFooter, { children: [_jsx(Button, { variant: "outline", onClick: () => onOpenChange(false), children: "Annuleren" }), _jsx(Button, { onClick: handleVerzetten, disabled: saving || loadingPreview || (!pickDayChanged && (!transportChanged || blocked)), children: saving ? "Opslaan…" : "Verzetten" })] })] }), _jsx(AlertDialog, { open: confirmOpen, onOpenChange: setConfirmOpen, children: _jsxs(AlertDialogContent, { children: [_jsxs(AlertDialogHeader, { children: [_jsx(AlertDialogTitle, { children: "Klant informeren?" }), _jsx(AlertDialogDescription, { children: "De leverdatum is gewijzigd naar een latere datum. Wilt u de klant hierover informeren?" })] }), _jsxs(AlertDialogFooter, { children: [_jsx(AlertDialogCancel, { onClick: () => void submit(false), children: "Nee" }), _jsx(AlertDialogAction, { onClick: () => void submit(true), children: "Ja" })] })] }) })] }));
203
203
  }
@@ -205,7 +205,7 @@ export function WeekAgendaView({ planned, mode, isLoading, onOrderClick, onDayCl
205
205
  d.setHours(0, 0, 0, 0);
206
206
  setCursor(startOfWeek(d));
207
207
  };
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: "Aantallen bij de Engine wijken af; de Engine lijnt ze uit." })), _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: "Verzonden, nog niet afgemeld 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())))) })] }));
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
209
  }
210
210
  function SummaryCard({ label, value, tone, }) {
211
211
  const valueClass = tone === "success" ? "text-green-600" : tone === "warning" ? "text-amber-600" : "text-foreground";
@@ -218,7 +218,10 @@ function DayRow({ day, onOrderClick, onDayClick, capacity, }) {
218
218
  const isToday = isSameDay(day.date, today);
219
219
  // Alles van deze dag is afgehandeld: toon een groene bevestiging.
220
220
  const dagAfgesloten = day.bestellingen > 0 && day.afgehandeld >= day.bestellingen && day.nietAfgemeld === 0;
221
- return (_jsxs("div", { className: `rounded-lg border bg-card ${isToday ? "ring-1 ring-primary" : ""}`, children: [_jsxs("div", { className: "flex flex-wrap items-center gap-6 px-4 py-4 sm:px-6", children: [_jsxs("div", { className: "w-20 shrink-0", children: [_jsx("div", { className: "text-xs font-medium tracking-wider text-muted-foreground", children: WEEKDAY_LABELS[weekdayIdx] }), _jsx("div", { className: "mt-1 text-3xl font-bold leading-none", children: day.date.getDate() }), _jsxs("div", { className: "text-sm text-muted-foreground", children: [MONTHS_NL_SHORT[day.date.getMonth()], "."] })] }), _jsxs("div", { className: "flex flex-1 flex-wrap items-center gap-6", children: [_jsx(Stat, { icon: _jsx(Package, { className: "h-4 w-4 text-slate-500" }), bg: "bg-slate-100", value: day.bestellingen, label: "bestellingen" }), _jsx(Stat, { icon: _jsx(Truck, { className: "h-4 w-4 text-amber-600" }), bg: "bg-amber-100", value: day.karren, label: "karren" }), _jsx(Stat, { icon: _jsx(Sprout, { className: "h-4 w-4 text-green-600" }), bg: "bg-green-100", value: day.planten, label: "planten" })] }), _jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [dagAfgesloten && (_jsxs("div", { className: "flex items-center gap-2 rounded-md border border-green-600/30 bg-green-500/15 px-3 py-1.5 text-sm font-medium text-green-700", children: [_jsx(CheckCircle2, { className: "h-4 w-4" }), "Dag is afgesloten"] })), day.shiftedCount > 0 && (_jsxs(Badge, { className: "bg-purple-500/10 text-purple-700 hover:bg-purple-500/10", variant: "secondary", title: "Aantal orders dat te laat is aangemeld en door de Engine is verschoven.", children: [_jsx(Scale, { className: "mr-1 h-3.5 w-3.5" }), day.shiftedCount, " te laat aangemeld"] })), day.nietAfgemeld > 0 && (_jsxs(Badge, { className: "bg-amber-500/15 text-amber-700 hover:bg-amber-500/15", variant: "secondary", children: [day.nietAfgemeld, " nog niet afgemeld bij Floriday"] })), day.verzendklaar > 0 && (_jsxs(Badge, { className: "bg-green-500/15 text-green-700 hover:bg-green-500/15", variant: "secondary", children: [_jsx(CheckCircle2, { className: "mr-1 h-3.5 w-3.5" }), day.verzendklaar, " verzendklaar"] })), day.voorbereiding > 0 && (_jsxs(Badge, { className: "bg-blue-500/10 text-blue-700 hover:bg-blue-500/10", variant: "secondary", children: [_jsx(Clock, { className: "mr-1 h-3.5 w-3.5" }), day.voorbereiding, " in voorbereiding"] })), onDayClick && day.bestellingen > 0 && (_jsx(Button, { size: "sm", variant: "outline", onClick: () => onDayClick(day.date), children: "Dagdetails" }))] })] }), _jsx(CapacityWarning, { day: capacity, className: "mx-4 mb-4 sm:mx-6" })] }));
221
+ return (_jsxs("div", { className: `rounded-lg border bg-card ${isToday ? "ring-1 ring-primary" : ""} ${onDayClick && day.bestellingen > 0 ? "cursor-pointer transition-colors hover:bg-muted/40" : ""}`, role: onDayClick && day.bestellingen > 0 ? "button" : undefined, tabIndex: onDayClick && day.bestellingen > 0 ? 0 : undefined, onClick: onDayClick && day.bestellingen > 0 ? () => onDayClick(day.date) : undefined, onKeyDown: onDayClick && day.bestellingen > 0 ? (e) => { if (e.key === "Enter" || e.key === " ") {
222
+ e.preventDefault();
223
+ onDayClick(day.date);
224
+ } } : undefined, children: [_jsxs("div", { className: "flex flex-wrap items-center gap-6 px-4 py-4 sm:px-6", children: [_jsxs("div", { className: "w-20 shrink-0", children: [_jsx("div", { className: "text-xs font-medium tracking-wider text-muted-foreground", children: WEEKDAY_LABELS[weekdayIdx] }), _jsx("div", { className: "mt-1 text-3xl font-bold leading-none", children: day.date.getDate() }), _jsxs("div", { className: "text-sm text-muted-foreground", children: [MONTHS_NL_SHORT[day.date.getMonth()], "."] })] }), _jsxs("div", { className: "flex flex-1 flex-wrap items-center gap-6", children: [_jsx(Stat, { icon: _jsx(Package, { className: "h-4 w-4 text-slate-500" }), bg: "bg-slate-100", value: day.bestellingen, label: "bestellingen" }), _jsx(Stat, { icon: _jsx(Truck, { className: "h-4 w-4 text-amber-600" }), bg: "bg-amber-100", value: day.karren, label: "karren" }), _jsx(Stat, { icon: _jsx(Sprout, { className: "h-4 w-4 text-green-600" }), bg: "bg-green-100", value: day.planten, label: "planten" })] }), _jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [dagAfgesloten && (_jsxs("div", { className: "flex items-center gap-2 rounded-md border border-green-600/30 bg-green-500/15 px-3 py-1.5 text-sm font-medium text-green-700", children: [_jsx(CheckCircle2, { className: "h-4 w-4" }), "Dag is afgesloten"] })), day.shiftedCount > 0 && (_jsxs(Badge, { className: "bg-purple-500/10 text-purple-700 hover:bg-purple-500/10", variant: "secondary", title: "Aantal orders dat te laat is aangemeld en door de planning is verschoven.", children: [_jsx(Scale, { className: "mr-1 h-3.5 w-3.5" }), day.shiftedCount, " te laat aangemeld"] })), day.nietAfgemeld > 0 && (_jsxs(Badge, { className: "bg-amber-500/15 text-amber-700 hover:bg-amber-500/15", variant: "secondary", children: [day.nietAfgemeld, " nog niet afgemeld bij Floriday"] })), day.verzendklaar > 0 && (_jsxs(Badge, { className: "bg-green-500/15 text-green-700 hover:bg-green-500/15", variant: "secondary", children: [_jsx(CheckCircle2, { className: "mr-1 h-3.5 w-3.5" }), day.verzendklaar, " verzendklaar"] })), day.voorbereiding > 0 && (_jsxs(Badge, { className: "bg-blue-500/10 text-blue-700 hover:bg-blue-500/10", variant: "secondary", children: [_jsx(Clock, { className: "mr-1 h-3.5 w-3.5" }), day.voorbereiding, " in voorbereiding"] })), onDayClick && day.bestellingen > 0 && (_jsx(Button, { size: "sm", variant: "outline", onClick: (e) => { e.stopPropagation(); onDayClick(day.date); }, children: "Dagdetails" }))] })] }), _jsx(CapacityWarning, { day: capacity, className: "mx-4 mb-4 sm:mx-6" })] }));
222
225
  }
223
226
  function Stat({ icon, bg, value, label, }) {
224
227
  return (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: `grid h-8 w-8 place-items-center rounded-full ${bg}`, children: icon }), _jsxs("div", { children: [_jsx("div", { className: "text-lg font-bold leading-none", children: value }), _jsx("div", { className: "text-xs text-muted-foreground", children: label })] })] }));