@flowselections/floriday-afhandeling-module 1.0.9 → 1.0.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. package/dist-lib/components/afhandeling/DagDetailDialog.d.ts +9 -0
  2. package/dist-lib/components/afhandeling/DagDetailDialog.d.ts.map +1 -0
  3. package/dist-lib/components/afhandeling/DagDetailDialog.js +145 -0
  4. package/dist-lib/components/afhandeling/LeveringenPage.d.ts.map +1 -1
  5. package/dist-lib/components/afhandeling/LeveringenPage.js +11 -5
  6. package/dist-lib/components/afhandeling/PickAgendaView.d.ts.map +1 -1
  7. package/dist-lib/components/afhandeling/PickAgendaView.js +13 -4
  8. package/dist-lib/components/afhandeling/PlanningAgendaPage.d.ts.map +1 -1
  9. package/dist-lib/components/afhandeling/PlanningAgendaPage.js +2 -2
  10. package/dist-lib/components/afhandeling/ServiceAreaSelect.d.ts +12 -0
  11. package/dist-lib/components/afhandeling/ServiceAreaSelect.d.ts.map +1 -0
  12. package/dist-lib/components/afhandeling/ServiceAreaSelect.js +79 -0
  13. package/dist-lib/components/afhandeling/VervoerderForm.d.ts.map +1 -1
  14. package/dist-lib/components/afhandeling/VervoerderForm.js +53 -6
  15. package/dist-lib/components/afhandeling/VervoerdersPage.d.ts.map +1 -1
  16. package/dist-lib/components/afhandeling/VervoerdersPage.js +23 -5
  17. package/dist-lib/components/afhandeling/WeekAgendaView.d.ts +2 -1
  18. package/dist-lib/components/afhandeling/WeekAgendaView.d.ts.map +1 -1
  19. package/dist-lib/components/afhandeling/WeekAgendaView.js +53 -18
  20. package/dist-lib/components/pickstation/PakbonTemplate.d.ts +5 -2
  21. package/dist-lib/components/pickstation/PakbonTemplate.d.ts.map +1 -1
  22. package/dist-lib/components/pickstation/PakbonTemplate.js +12 -10
  23. package/dist-lib/components/settings/AfhandelingSettingsCard.d.ts.map +1 -1
  24. package/dist-lib/components/settings/AfhandelingSettingsCard.js +18 -6
  25. package/dist-lib/hooks/useOrderPlanning.d.ts +3 -0
  26. package/dist-lib/hooks/useOrderPlanning.d.ts.map +1 -1
  27. package/dist-lib/hooks/useOrderPlanning.js +47 -6
  28. package/dist-lib/integrations/supabase/auth-middleware.d.ts +2261 -136
  29. package/dist-lib/integrations/supabase/auth-middleware.d.ts.map +1 -1
  30. package/dist-lib/integrations/supabase/client.d.ts +2261 -136
  31. package/dist-lib/integrations/supabase/client.d.ts.map +1 -1
  32. package/dist-lib/integrations/supabase/client.server.d.ts +2261 -136
  33. package/dist-lib/integrations/supabase/client.server.d.ts.map +1 -1
  34. package/dist-lib/integrations/supabase/types.d.ts +2329 -134
  35. package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
  36. package/dist-lib/integrations/supabase/types.js +1 -0
  37. package/dist-lib/lib/afhandelingSettings.functions.d.ts +9431 -5172
  38. package/dist-lib/lib/afhandelingSettings.functions.d.ts.map +1 -1
  39. package/dist-lib/lib/afhandelingSettings.functions.js +27 -2
  40. package/dist-lib/lib/carrierSignupMail.server.d.ts +56 -0
  41. package/dist-lib/lib/carrierSignupMail.server.d.ts.map +1 -0
  42. package/dist-lib/lib/carrierSignupMail.server.js +229 -0
  43. package/dist-lib/lib/carriers.functions.d.ts +33377 -15579
  44. package/dist-lib/lib/carriers.functions.d.ts.map +1 -1
  45. package/dist-lib/lib/carriers.functions.js +28 -2
  46. package/dist-lib/lib/cartAssignments.functions.d.ts +46479 -0
  47. package/dist-lib/lib/cartAssignments.functions.d.ts.map +1 -0
  48. package/dist-lib/lib/cartAssignments.functions.js +130 -0
  49. package/dist-lib/lib/cartMoveImpact.d.ts +8 -0
  50. package/dist-lib/lib/cartMoveImpact.d.ts.map +1 -0
  51. package/dist-lib/lib/cartMoveImpact.js +26 -0
  52. package/dist-lib/lib/floriday.functions.d.ts +2265 -136
  53. package/dist-lib/lib/floriday.functions.d.ts.map +1 -1
  54. package/dist-lib/lib/floriday.functions.js +178 -98
  55. package/dist-lib/lib/geo/euServiceAreas.d.ts +30 -0
  56. package/dist-lib/lib/geo/euServiceAreas.d.ts.map +1 -0
  57. package/dist-lib/lib/geo/euServiceAreas.js +277 -0
  58. package/dist-lib/lib/invalidatePlanning.d.ts +11 -0
  59. package/dist-lib/lib/invalidatePlanning.d.ts.map +1 -0
  60. package/dist-lib/lib/invalidatePlanning.js +25 -0
  61. package/dist-lib/lib/pickstationI18n.d.ts +4 -0
  62. package/dist-lib/lib/pickstationI18n.d.ts.map +1 -1
  63. package/dist-lib/lib/pickstationI18n.js +24 -0
  64. package/dist-lib/lib/pickstationOrders.server.d.ts +1 -1
  65. package/dist-lib/lib/pickstationOrders.server.d.ts.map +1 -1
  66. package/dist-lib/lib/pickstationOrders.server.js +305 -177
  67. package/dist-lib/lib/pickstationTypes.d.ts +11 -2
  68. package/dist-lib/lib/pickstationTypes.d.ts.map +1 -1
  69. package/dist-lib/lib/planning.d.ts +18 -2
  70. package/dist-lib/lib/planning.d.ts.map +1 -1
  71. package/dist-lib/lib/planning.js +96 -33
  72. package/package.json +2 -2
@@ -0,0 +1,9 @@
1
+ import type { PlannedOrder } from "../../hooks/useOrderPlanning";
2
+ export interface DagDetailDialogProps {
3
+ open: boolean;
4
+ date: Date | null;
5
+ planned: PlannedOrder[];
6
+ onOpenChange: (open: boolean) => void;
7
+ }
8
+ export declare function DagDetailDialog({ open, date, planned, onOpenChange }: DagDetailDialogProps): import("react").JSX.Element | null;
9
+ //# sourceMappingURL=DagDetailDialog.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DagDetailDialog.d.ts","sourceRoot":"","sources":["../../../src/components/afhandeling/DagDetailDialog.tsx"],"names":[],"mappings":"AAwBA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAyBjE,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;AAID,wBAAgB,eAAe,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,YAAY,EAAE,EAAE,oBAAoB,sCAyE1F"}
@@ -0,0 +1,145 @@
1
+ import { jsxs as _jsxs, jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useState } from "react";
3
+ import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
4
+ import { useServerFn } from "@tanstack/react-start";
5
+ import { Badge, Button, Dialog, DialogContent, DialogHeader, DialogTitle, Input, Tabs, TabsContent, TabsList, TabsTrigger, Table, TableBody, TableCell, TableHead, TableHeader, TableRow, toast, } from "@flowselections/core";
6
+ import { ChevronDown, ChevronRight, MoveRight, Truck } from "lucide-react";
7
+ import { isSameDay } from "../../lib/pickDate";
8
+ import { getOrderLines, moveCart as moveCartFn, } from "../../lib/cartAssignments.functions";
9
+ import { evaluateCartMove } from "../../lib/cartMoveImpact";
10
+ const NL_DATE = new Intl.DateTimeFormat("nl-NL", { dateStyle: "full" });
11
+ export function DagDetailDialog({ open, date, planned, onOpenChange }) {
12
+ if (!date)
13
+ return null;
14
+ const ordersOnDay = planned.filter((p) => {
15
+ if (!p.planning)
16
+ return false;
17
+ const assigns = p.planning.pickAssignments ?? [];
18
+ return assigns.some((a) => isSameDay(a.date, date));
19
+ });
20
+ // Synthetiseer karren — één rij per kar die op deze dag valt
21
+ const cartRows = [];
22
+ for (const p of ordersOnDay) {
23
+ const assigns = p.planning.pickAssignments ?? [];
24
+ const totalCarts = p.order.cartCount ?? 0;
25
+ if (totalCarts <= 0)
26
+ continue;
27
+ let runningIdx = 0;
28
+ for (const a of assigns) {
29
+ const onThisDay = isSameDay(a.date, date);
30
+ const cartIndexes = a.cartIndexes && a.cartIndexes.length > 0
31
+ ? a.cartIndexes
32
+ : Array.from({ length: a.cartCount }, (_, i) => runningIdx + i);
33
+ for (const idx of cartIndexes) {
34
+ if (onThisDay) {
35
+ const plantsPerCart = totalCarts > 0 ? Math.round((p.order.plantCount ?? 0) / totalCarts) : 0;
36
+ cartRows.push({
37
+ key: `${p.order.id}-${idx}`,
38
+ orderId: p.order.id,
39
+ orderSource: p.order.source,
40
+ cartIndex: idx,
41
+ cartNumber: `${p.order.orderNumber}-${String(idx + 1).padStart(2, "0")}`,
42
+ order: p,
43
+ layers: 4,
44
+ crates: 0,
45
+ plants: plantsPerCart,
46
+ occupancy: 100,
47
+ });
48
+ }
49
+ }
50
+ runningIdx += a.cartCount;
51
+ }
52
+ }
53
+ return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { className: "max-w-5xl max-h-[90vh] overflow-y-auto", children: [_jsx(DialogHeader, { children: _jsxs(DialogTitle, { children: ["Dagdetails \u2014 ", NL_DATE.format(date)] }) }), _jsxs("div", { className: "flex flex-wrap gap-3 text-sm text-muted-foreground", children: [_jsxs("span", { children: [ordersOnDay.length, " bestellingen"] }), _jsx("span", { children: "\u00B7" }), _jsxs("span", { children: [cartRows.length, " karren"] })] }), _jsxs(Tabs, { defaultValue: "bestellingen", className: "mt-4", 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, date: date }) }), _jsx(TabsContent, { value: "karren", className: "mt-4", children: _jsx(KarrenTab, { carts: cartRows, currentDate: date }) })] })] }) }));
54
+ }
55
+ function BestellingenTab({ orders, date }) {
56
+ if (orders.length === 0) {
57
+ return _jsx("p", { className: "text-sm text-muted-foreground", children: "Geen bestellingen op deze dag." });
58
+ }
59
+ return (_jsx("div", { className: "space-y-3", children: orders.map((p) => {
60
+ const cartsToday = (p.planning?.pickAssignments ?? []).find((a) => isSameDay(a.date, date))?.cartCount ?? 0;
61
+ return (_jsx(OrderRow, { planned: p, cartsToday: cartsToday }, p.order.id));
62
+ }) }));
63
+ }
64
+ function OrderRow({ planned: p, cartsToday }) {
65
+ const [open, setOpen] = useState(false);
66
+ const source = p.order.source;
67
+ const fetchLines = useServerFn(getOrderLines);
68
+ const linesQuery = useQuery({
69
+ queryKey: ["order-lines", source, p.order.id],
70
+ queryFn: () => fetchLines({ data: { orderId: p.order.id, orderSource: source } }),
71
+ enabled: open,
72
+ });
73
+ return (_jsxs("div", { className: "rounded-lg border bg-card", children: [_jsxs("button", { type: "button", onClick: () => setOpen((o) => !o), className: "flex w-full items-center gap-3 px-4 py-3 text-left hover:bg-muted/50", children: [open ? _jsx(ChevronDown, { className: "h-4 w-4" }) : _jsx(ChevronRight, { className: "h-4 w-4" }), _jsxs("div", { className: "flex-1 grid grid-cols-2 md:grid-cols-4 gap-3 text-sm", children: [_jsxs("div", { children: [_jsx("div", { className: "font-medium", children: p.order.orderNumber }), _jsx("div", { className: "text-xs text-muted-foreground", children: p.order.customerName })] }), _jsxs("div", { children: [_jsx("div", { className: "text-xs text-muted-foreground", children: "Type" }), _jsx("div", { children: p.order.deliveryType })] }), _jsxs("div", { children: [_jsx("div", { className: "text-xs text-muted-foreground", children: "Leverdatum" }), _jsx("div", { children: p.order.deliveryDate
74
+ ? new Date(p.order.deliveryDate).toLocaleDateString("nl-NL")
75
+ : "—" })] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsxs(Badge, { variant: "secondary", children: [cartsToday, " karren vandaag"] }), _jsx(Badge, { variant: "outline", children: p.order.status })] })] })] }), open && (_jsxs("div", { className: "border-t px-4 py-3 space-y-3", children: [_jsxs("div", { className: "grid grid-cols-2 md:grid-cols-3 gap-3 text-sm", children: [_jsx(Field, { label: "Afleverlocatie", value: "\u2014" }), _jsx(Field, { label: "Ophaallocatie (tuin)", value: "\u2014" }), _jsx(Field, { label: "Vervoerder", value: "\u2014" })] }), _jsxs("div", { children: [_jsx("div", { className: "text-xs font-medium text-muted-foreground mb-1", children: "Orderregels" }), linesQuery.isLoading && _jsx("p", { className: "text-xs", children: "Regels laden\u2026" }), linesQuery.data && _jsx(OrderLinesTable, { lines: linesQuery.data.lines }), linesQuery.error && _jsx("p", { className: "text-xs text-destructive", children: "Kon regels niet laden" })] })] }))] }));
76
+ }
77
+ function Field({ label, value }) {
78
+ return (_jsxs("div", { children: [_jsx("div", { className: "text-xs text-muted-foreground", children: label }), _jsx("div", { children: value })] }));
79
+ }
80
+ function OrderLinesTable({ lines }) {
81
+ if (lines.length === 0) {
82
+ return _jsx("p", { className: "text-xs text-muted-foreground", children: "Geen orderregels gevonden." });
83
+ }
84
+ return (_jsxs(Table, { children: [_jsx(TableHeader, { children: _jsxs(TableRow, { children: [_jsx(TableHead, { children: "Artikel" }), _jsx(TableHead, { className: "text-right", children: "Planten" }), _jsx(TableHead, { className: "text-right", children: "Fusten" }), _jsx(TableHead, { className: "text-right", children: "Overig" })] }) }), _jsx(TableBody, { children: lines.map((l) => (_jsxs(TableRow, { children: [_jsx(TableCell, { children: l.article }), _jsx(TableCell, { className: "text-right", children: l.plants }), _jsx(TableCell, { className: "text-right", children: l.crates ?? "—" }), _jsx(TableCell, { className: "text-right", children: l.otherUnits ?? "—" })] }, l.id))) })] }));
85
+ }
86
+ function KarrenTab({ carts, currentDate }) {
87
+ const [moveCartRow, setMoveCartRow] = useState(null);
88
+ if (carts.length === 0) {
89
+ return _jsx("p", { className: "text-sm text-muted-foreground", children: "Geen karren ingepland op deze dag." });
90
+ }
91
+ return (_jsxs(_Fragment, { children: [_jsxs(Table, { children: [_jsx(TableHeader, { children: _jsxs(TableRow, { children: [_jsx(TableHead, { children: "Karnummer" }), _jsx(TableHead, { children: "Klant" }), _jsx(TableHead, { children: "Bestelling" }), _jsx(TableHead, { children: "Tuin" }), _jsx(TableHead, { children: "Vervoerder" }), _jsx(TableHead, { className: "text-right", children: "Bezetting" }), _jsx(TableHead, { className: "text-right", children: "Lagen" }), _jsx(TableHead, { className: "text-right", children: "Planten" }), _jsx(TableHead, { children: "Status" }), _jsx(TableHead, { className: "text-right", children: "Actie" })] }) }), _jsx(TableBody, { children: carts.map((c) => (_jsxs(TableRow, { children: [_jsx(TableCell, { className: "font-medium", children: c.cartNumber }), _jsx(TableCell, { children: c.order.order.customerName }), _jsx(TableCell, { children: c.order.order.orderNumber }), _jsx(TableCell, { children: "\u2014" }), _jsx(TableCell, { children: "\u2014" }), _jsxs(TableCell, { className: "text-right", children: [c.occupancy, "%"] }), _jsx(TableCell, { className: "text-right", children: c.layers }), _jsx(TableCell, { className: "text-right", children: c.plants }), _jsx(TableCell, { children: _jsx(Badge, { variant: "secondary", children: "Nog te picken" }) }), _jsx(TableCell, { className: "text-right", children: _jsxs(Button, { size: "sm", variant: "outline", onClick: () => setMoveCartRow(c), children: [_jsx(MoveRight, { className: "mr-1 h-3.5 w-3.5" }), " Verplaatsen"] }) })] }, c.key))) })] }), _jsx(VerplaatsKarDialog, { cart: moveCartRow, currentDate: currentDate, onClose: () => setMoveCartRow(null) })] }));
92
+ }
93
+ function VerplaatsKarDialog({ cart, currentDate, onClose, }) {
94
+ const [newDate, setNewDate] = useState("");
95
+ const move = useServerFn(moveCartFn);
96
+ const qc = useQueryClient();
97
+ const mutation = useMutation({
98
+ mutationFn: async () => {
99
+ if (!cart)
100
+ throw new Error("Geen kar geselecteerd");
101
+ if (!newDate)
102
+ throw new Error("Kies eerst een nieuwe pickdatum");
103
+ return move({
104
+ data: {
105
+ orderId: cart.orderId,
106
+ orderSource: cart.orderSource,
107
+ cartIndex: cart.cartIndex,
108
+ newDate,
109
+ fromDate: currentDate,
110
+ },
111
+ });
112
+ },
113
+ onSuccess: async (res) => {
114
+ const target = new Date(newDate).toLocaleDateString("nl-NL", {
115
+ weekday: "long", day: "numeric", month: "long",
116
+ });
117
+ toast.success(`Kar ${cart?.cartNumber ?? ""} verplaatst naar ${target}`);
118
+ qc.setQueryData(["cart-overrides"], (current) => {
119
+ const overrides = current?.overrides ?? [];
120
+ const next = overrides.filter((o) => !(o.order_id === res.override.order_id &&
121
+ o.order_source === res.override.order_source &&
122
+ o.cart_index === res.override.cart_index));
123
+ return { overrides: [...next, res.override] };
124
+ });
125
+ await Promise.all([
126
+ qc.invalidateQueries({ queryKey: ["floriday", "orders"] }),
127
+ qc.invalidateQueries({ queryKey: ["cart-overrides"] }),
128
+ ]);
129
+ await qc.refetchQueries({ queryKey: ["cart-overrides"], type: "active" });
130
+ setNewDate("");
131
+ onClose();
132
+ return res;
133
+ },
134
+ onError: (e) => {
135
+ const msg = e instanceof Error ? e.message : "Verplaatsen mislukt";
136
+ toast.error(msg);
137
+ console.error("[moveCart]", e);
138
+ },
139
+ });
140
+ if (!cart)
141
+ return null;
142
+ const parsed = newDate ? new Date(newDate) : null;
143
+ const impact = parsed ? evaluateCartMove(parsed, currentDate, cart.order.planning) : null;
144
+ return (_jsx(Dialog, { open: !!cart, onOpenChange: (o) => !o && onClose(), children: _jsxs(DialogContent, { className: "max-w-md", children: [_jsx(DialogHeader, { children: _jsxs(DialogTitle, { className: "flex items-center gap-2", children: [_jsx(Truck, { className: "h-4 w-4" }), " Kar verplaatsen"] }) }), _jsxs("div", { className: "space-y-3 text-sm", children: [_jsxs("div", { className: "rounded-md border bg-muted/40 p-3", children: [_jsx("div", { className: "font-medium", children: cart.cartNumber }), _jsxs("div", { className: "text-xs text-muted-foreground", children: [cart.order.order.customerName, " \u00B7 ", cart.order.order.orderNumber] })] }), _jsxs("div", { children: [_jsx("label", { className: "text-xs text-muted-foreground", children: "Nieuwe pickdatum" }), _jsx(Input, { type: "date", value: newDate, onChange: (e) => setNewDate(e.target.value) })] }), impact && (_jsxs("div", { className: "space-y-1", children: [impact.blockers.map((b, i) => (_jsxs("p", { className: "text-xs text-destructive", children: ["\u26A0 ", b] }, `b${i}`))), impact.warnings.map((w, i) => (_jsxs("p", { className: "text-xs text-amber-600", children: ["! ", w] }, `w${i}`))), impact.ok && impact.warnings.length === 0 && (_jsx("p", { className: "text-xs text-green-600", children: "\u2713 Verplaatsing is logistiek haalbaar." }))] })), _jsxs("div", { className: "flex justify-end gap-2", children: [_jsx(Button, { variant: "ghost", onClick: onClose, children: "Annuleren" }), _jsx(Button, { onClick: () => mutation.mutate(), disabled: !newDate || (impact ? !impact.ok : false) || mutation.isPending, children: mutation.isPending ? "Bezig…" : "Bevestig verplaatsing" })] })] })] }) }));
145
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"LeveringenPage.d.ts","sourceRoot":"","sources":["../../../src/components/afhandeling/LeveringenPage.tsx"],"names":[],"mappings":"AAqBA,wBAAgB,cAAc,gCA+F7B"}
1
+ {"version":3,"file":"LeveringenPage.d.ts","sourceRoot":"","sources":["../../../src/components/afhandeling/LeveringenPage.tsx"],"names":[],"mappings":"AAwBA,wBAAgB,cAAc,gCA2G7B"}
@@ -1,13 +1,19 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Link } from "@tanstack/react-router";
3
2
  import { useMemo, useState } from "react";
4
- import { Button, Input, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, Tabs, TabsContent, TabsList, TabsTrigger, } from "@flowselections/core";
5
- import { Plus } from "lucide-react";
3
+ import { useQueryClient } from "@tanstack/react-query";
4
+ import { Button, Input, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, Tabs, TabsContent, TabsList, TabsTrigger, toast, } from "@flowselections/core";
5
+ import { RefreshCw } from "lucide-react";
6
6
  import { WeekAgendaView } from "./WeekAgendaView";
7
7
  import { useOrderPlanning } from "../../hooks/useOrderPlanning";
8
+ import { invalidatePlanning } from "../../lib/invalidatePlanning";
8
9
  const ALL = "__all__";
9
10
  export function LeveringenPage() {
10
- const { planned, isLoading, error, refetch } = useOrderPlanning();
11
+ const { planned, isLoading, error, warning, refetch } = useOrderPlanning();
12
+ const qc = useQueryClient();
13
+ const handleRecalc = async () => {
14
+ await invalidatePlanning(qc);
15
+ toast.success("Planning herberekend met actuele instellingen");
16
+ };
11
17
  const [customer, setCustomer] = useState(ALL);
12
18
  const [deliveryType, setDeliveryType] = useState(ALL);
13
19
  const [dateFrom, setDateFrom] = useState("");
@@ -27,5 +33,5 @@ export function LeveringenPage() {
27
33
  return true;
28
34
  });
29
35
  }, [planned, customer, deliveryType, dateFrom, dateTo]);
30
- return (_jsx("main", { className: "p-6 space-y-6", children: _jsxs(Tabs, { defaultValue: "overzicht", className: "space-y-6", children: [_jsxs(TabsList, { children: [_jsx(TabsTrigger, { value: "overzicht", children: "Overzicht" }), _jsx(TabsTrigger, { value: "veilbrieven", children: "Veilbrieven" })] }), _jsxs("div", { className: "flex flex-wrap items-end justify-between gap-3", children: [_jsxs("div", { className: "flex flex-wrap items-end gap-3", children: [_jsxs("div", { className: "space-y-1", children: [_jsx("label", { className: "text-xs text-muted-foreground", children: "Datum vanaf" }), _jsx(Input, { type: "date", value: dateFrom, onChange: (e) => setDateFrom(e.target.value), className: "w-40" })] }), _jsxs("div", { className: "space-y-1", children: [_jsx("label", { className: "text-xs text-muted-foreground", children: "Datum t/m" }), _jsx(Input, { type: "date", value: dateTo, onChange: (e) => setDateTo(e.target.value), className: "w-40" })] }), _jsxs("div", { className: "space-y-1", children: [_jsx("label", { className: "text-xs text-muted-foreground", children: "Klant" }), _jsxs(Select, { value: customer, onValueChange: setCustomer, children: [_jsx(SelectTrigger, { className: "w-56", children: _jsx(SelectValue, { placeholder: "Alle klanten" }) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: ALL, children: "Alle klanten" }), customers.map((c) => _jsx(SelectItem, { value: c, children: c }, c))] })] })] }), _jsxs("div", { className: "space-y-1", children: [_jsx("label", { className: "text-xs text-muted-foreground", children: "Type levering" }), _jsxs(Select, { value: deliveryType, onValueChange: setDeliveryType, children: [_jsx(SelectTrigger, { className: "w-56", children: _jsx(SelectValue, { placeholder: "Alle types" }) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: ALL, children: "Alle types" }), deliveryTypes.map((t) => _jsx(SelectItem, { value: t, children: t }, t))] })] })] })] }), _jsx(Button, { asChild: true, children: _jsxs(Link, { to: "/afhandeling/leveringen/nieuw", children: [_jsx(Plus, { className: "mr-2 h-4 w-4" }), "Levering aanmaken"] }) })] }), _jsx(TabsContent, { value: "overzicht", className: "space-y-3", children: 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: filtered, mode: "delivery", isLoading: isLoading })) }), _jsx(TabsContent, { value: "veilbrieven", children: _jsx("div", { className: "rounded-md border border-dashed p-10 text-center text-sm text-muted-foreground", children: "Veilbrieven volgen in een volgende iteratie." }) })] }) }));
36
+ return (_jsx("main", { className: "p-6 space-y-6", children: _jsxs(Tabs, { defaultValue: "overzicht", className: "space-y-6", children: [_jsxs(TabsList, { children: [_jsx(TabsTrigger, { value: "overzicht", children: "Overzicht" }), _jsx(TabsTrigger, { value: "veilbrieven", children: "Veilbrieven" })] }), _jsxs("div", { className: "flex flex-wrap items-end justify-between gap-3", children: [_jsxs("div", { className: "flex flex-wrap items-end gap-3", children: [_jsxs("div", { className: "space-y-1", children: [_jsx("label", { className: "text-xs text-muted-foreground", children: "Datum vanaf" }), _jsx(Input, { type: "date", value: dateFrom, onChange: (e) => setDateFrom(e.target.value), className: "w-40" })] }), _jsxs("div", { className: "space-y-1", children: [_jsx("label", { className: "text-xs text-muted-foreground", children: "Datum t/m" }), _jsx(Input, { type: "date", value: dateTo, onChange: (e) => setDateTo(e.target.value), className: "w-40" })] }), _jsxs("div", { className: "space-y-1", children: [_jsx("label", { className: "text-xs text-muted-foreground", children: "Klant" }), _jsxs(Select, { value: customer, onValueChange: setCustomer, children: [_jsx(SelectTrigger, { className: "w-56", children: _jsx(SelectValue, { placeholder: "Alle klanten" }) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: ALL, children: "Alle klanten" }), customers.map((c) => _jsx(SelectItem, { value: c, children: c }, c))] })] })] }), _jsxs("div", { className: "space-y-1", children: [_jsx("label", { className: "text-xs text-muted-foreground", children: "Type levering" }), _jsxs(Select, { value: deliveryType, onValueChange: setDeliveryType, children: [_jsx(SelectTrigger, { className: "w-56", children: _jsx(SelectValue, { placeholder: "Alle types" }) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: ALL, children: "Alle types" }), deliveryTypes.map((t) => _jsx(SelectItem, { value: t, children: t }, t))] })] })] })] }), _jsx("div", { className: "flex items-end gap-2", children: _jsxs(Button, { variant: "outline", onClick: handleRecalc, children: [_jsx(RefreshCw, { className: "mr-2 h-4 w-4" }), "Herbereken planning"] }) })] }), _jsxs(TabsContent, { value: "overzicht", className: "space-y-3", 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: filtered, mode: "delivery", isLoading: isLoading }))] }), _jsx(TabsContent, { value: "veilbrieven", children: _jsx("div", { className: "rounded-md border border-dashed p-10 text-center text-sm text-muted-foreground", children: "Veilbrieven volgen in een volgende iteratie." }) })] }) }));
31
37
  }
@@ -1 +1 @@
1
- {"version":3,"file":"PickAgendaView.d.ts","sourceRoot":"","sources":["../../../src/components/afhandeling/PickAgendaView.tsx"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAIlE,MAAM,WAAW,mBAAmB;IAClC,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;CAC/C;AAED,wBAAgB,cAAc,CAAC,EAAE,YAAY,EAAE,EAAE,mBAAmB,+BA6FnE"}
1
+ {"version":3,"file":"PickAgendaView.d.ts","sourceRoot":"","sources":["../../../src/components/afhandeling/PickAgendaView.tsx"],"names":[],"mappings":"AAsBA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAIlE,MAAM,WAAW,mBAAmB;IAClC,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;CAC/C;AAED,wBAAgB,cAAc,CAAC,EAAE,YAAY,EAAE,EAAE,mBAAmB,+BAkHnE"}
@@ -1,13 +1,22 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Link } from "@tanstack/react-router";
3
2
  import { useMemo, useState } from "react";
3
+ import { useQueryClient } from "@tanstack/react-query";
4
4
  import { Button, Input, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, Tabs, TabsContent, TabsList, TabsTrigger, } from "@flowselections/core";
5
- import { Plus } from "lucide-react";
5
+ import { RefreshCw } from "lucide-react";
6
+ import { toast } from "@flowselections/core";
6
7
  import { WeekAgendaView } from "./WeekAgendaView";
8
+ import { DagDetailDialog } from "./DagDetailDialog";
7
9
  import { useOrderPlanning } from "../../hooks/useOrderPlanning";
10
+ import { invalidatePlanning } from "../../lib/invalidatePlanning";
8
11
  const ALL = "__all__";
9
12
  export function PickAgendaView({ onOrderClick }) {
10
- const { planned, isLoading, error, refetch } = useOrderPlanning();
13
+ const { planned, isLoading, error, warning, refetch } = useOrderPlanning();
14
+ const qc = useQueryClient();
15
+ const [dayDetailDate, setDayDetailDate] = useState(null);
16
+ const handleRecalc = async () => {
17
+ await invalidatePlanning(qc);
18
+ toast.success("Planning herberekend met actuele instellingen");
19
+ };
11
20
  const [customer, setCustomer] = useState(ALL);
12
21
  const [deliveryType, setDeliveryType] = useState(ALL);
13
22
  const [dateFrom, setDateFrom] = useState("");
@@ -27,5 +36,5 @@ export function PickAgendaView({ onOrderClick }) {
27
36
  return true;
28
37
  });
29
38
  }, [planned, customer, deliveryType, dateFrom, dateTo]);
30
- return (_jsxs(Tabs, { defaultValue: "overzicht", className: "space-y-6", children: [_jsxs(TabsList, { children: [_jsx(TabsTrigger, { value: "overzicht", children: "Overzicht" }), _jsx(TabsTrigger, { value: "veilbrieven", children: "Veilbrieven" })] }), _jsxs("div", { className: "flex flex-wrap items-end justify-between gap-3", children: [_jsxs("div", { className: "flex flex-wrap items-end gap-3", children: [_jsxs("div", { className: "space-y-1", children: [_jsx("label", { className: "text-xs text-muted-foreground", children: "Datum vanaf" }), _jsx(Input, { type: "date", value: dateFrom, onChange: (e) => setDateFrom(e.target.value), className: "w-40" })] }), _jsxs("div", { className: "space-y-1", children: [_jsx("label", { className: "text-xs text-muted-foreground", children: "Datum t/m" }), _jsx(Input, { type: "date", value: dateTo, onChange: (e) => setDateTo(e.target.value), className: "w-40" })] }), _jsxs("div", { className: "space-y-1", children: [_jsx("label", { className: "text-xs text-muted-foreground", children: "Klant" }), _jsxs(Select, { value: customer, onValueChange: setCustomer, children: [_jsx(SelectTrigger, { className: "w-56", children: _jsx(SelectValue, { placeholder: "Alle klanten" }) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: ALL, children: "Alle klanten" }), customers.map((c) => _jsx(SelectItem, { value: c, children: c }, c))] })] })] }), _jsxs("div", { className: "space-y-1", children: [_jsx("label", { className: "text-xs text-muted-foreground", children: "Type levering" }), _jsxs(Select, { value: deliveryType, onValueChange: setDeliveryType, children: [_jsx(SelectTrigger, { className: "w-56", children: _jsx(SelectValue, { placeholder: "Alle types" }) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: ALL, children: "Alle types" }), deliveryTypes.map((t) => _jsx(SelectItem, { value: t, children: t }, t))] })] })] })] }), _jsx(Button, { asChild: true, children: _jsxs(Link, { to: "/afhandeling/leveringen/nieuw", children: [_jsx(Plus, { className: "mr-2 h-4 w-4" }), "Levering aanmaken"] }) })] }), _jsx(TabsContent, { value: "overzicht", className: "space-y-3", children: 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: filtered, mode: "pick", isLoading: isLoading, onOrderClick: onOrderClick })) }), _jsx(TabsContent, { value: "veilbrieven", children: _jsx("div", { className: "rounded-md border border-dashed p-10 text-center text-sm text-muted-foreground", children: "Veilbrieven volgen in een volgende iteratie." }) })] }));
39
+ return (_jsxs(Tabs, { defaultValue: "overzicht", className: "space-y-6", children: [_jsxs(TabsList, { children: [_jsx(TabsTrigger, { value: "overzicht", children: "Overzicht" }), _jsx(TabsTrigger, { value: "veilbrieven", children: "Veilbrieven" })] }), _jsxs("div", { className: "flex flex-wrap items-end justify-between gap-3", children: [_jsxs("div", { className: "flex flex-wrap items-end gap-3", children: [_jsxs("div", { className: "space-y-1", children: [_jsx("label", { className: "text-xs text-muted-foreground", children: "Datum vanaf" }), _jsx(Input, { type: "date", value: dateFrom, onChange: (e) => setDateFrom(e.target.value), className: "w-40" })] }), _jsxs("div", { className: "space-y-1", children: [_jsx("label", { className: "text-xs text-muted-foreground", children: "Datum t/m" }), _jsx(Input, { type: "date", value: dateTo, onChange: (e) => setDateTo(e.target.value), className: "w-40" })] }), _jsxs("div", { className: "space-y-1", children: [_jsx("label", { className: "text-xs text-muted-foreground", children: "Klant" }), _jsxs(Select, { value: customer, onValueChange: setCustomer, children: [_jsx(SelectTrigger, { className: "w-56", children: _jsx(SelectValue, { placeholder: "Alle klanten" }) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: ALL, children: "Alle klanten" }), customers.map((c) => _jsx(SelectItem, { value: c, children: c }, c))] })] })] }), _jsxs("div", { className: "space-y-1", children: [_jsx("label", { className: "text-xs text-muted-foreground", children: "Type levering" }), _jsxs(Select, { value: deliveryType, onValueChange: setDeliveryType, children: [_jsx(SelectTrigger, { className: "w-56", children: _jsx(SelectValue, { placeholder: "Alle types" }) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: ALL, children: "Alle types" }), deliveryTypes.map((t) => _jsx(SelectItem, { value: t, children: t }, t))] })] })] })] }), _jsx("div", { className: "flex items-end gap-2", children: _jsxs(Button, { variant: "outline", onClick: handleRecalc, children: [_jsx(RefreshCw, { className: "mr-2 h-4 w-4" }), "Herbereken planning"] }) })] }), _jsxs(TabsContent, { value: "overzicht", className: "space-y-3", 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: filtered, mode: "pick", isLoading: isLoading, onOrderClick: onOrderClick, onDayClick: setDayDetailDate }))] }), _jsx(TabsContent, { value: "veilbrieven", children: _jsx("div", { className: "rounded-md border border-dashed p-10 text-center text-sm text-muted-foreground", children: "Veilbrieven volgen in een volgende iteratie." }) }), _jsx(DagDetailDialog, { open: !!dayDetailDate, date: dayDetailDate, planned: filtered, onOpenChange: (o) => !o && setDayDetailDate(null) })] }));
31
40
  }
@@ -1 +1 @@
1
- {"version":3,"file":"PlanningAgendaPage.d.ts","sourceRoot":"","sources":["../../../src/components/afhandeling/PlanningAgendaPage.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAkB,KAAK,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAGvE,UAAU,KAAK;IACb,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,cAAc,CAAC;CACtB;AAED,wBAAgB,kBAAkB,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,EAAE,KAAK,+BAgBrE"}
1
+ {"version":3,"file":"PlanningAgendaPage.d.ts","sourceRoot":"","sources":["../../../src/components/afhandeling/PlanningAgendaPage.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAkB,KAAK,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAGvE,UAAU,KAAK;IACb,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,cAAc,CAAC;CACtB;AAED,wBAAgB,kBAAkB,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,EAAE,KAAK,+BAqBrE"}
@@ -2,6 +2,6 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { WeekAgendaView } from "./WeekAgendaView";
3
3
  import { useOrderPlanning } from "../../hooks/useOrderPlanning";
4
4
  export function PlanningAgendaPage({ title, description, mode }) {
5
- const { planned, isLoading, error } = useOrderPlanning();
6
- return (_jsxs("main", { className: "p-6 space-y-6", children: [_jsxs("div", { children: [_jsx("h1", { className: "text-2xl font-semibold", children: title }), _jsx("p", { className: "text-sm text-muted-foreground", children: description })] }), error && (_jsx("div", { className: "rounded-md border border-destructive/30 bg-destructive/5 p-3 text-sm text-destructive", children: error })), _jsx(WeekAgendaView, { planned: planned, mode: mode, isLoading: isLoading })] }));
5
+ const { planned, isLoading, error, warning } = useOrderPlanning();
6
+ return (_jsxs("main", { className: "p-6 space-y-6", children: [_jsxs("div", { children: [_jsx("h1", { className: "text-2xl font-semibold", children: title }), _jsx("p", { className: "text-sm text-muted-foreground", children: description })] }), error && (_jsx("div", { className: "rounded-md border border-destructive/30 bg-destructive/5 p-3 text-sm text-destructive", children: error })), warning && !error && (_jsxs("div", { className: "rounded-md border border-amber-300 bg-amber-50 p-3 text-sm text-amber-900", children: [warning, " De beschikbare orders blijven zichtbaar."] })), _jsx(WeekAgendaView, { planned: planned, mode: mode, isLoading: isLoading })] }));
7
7
  }
@@ -0,0 +1,12 @@
1
+ export interface ServiceAreaValue {
2
+ countries: string[];
3
+ provinces: string[];
4
+ cities: string[];
5
+ }
6
+ interface Props {
7
+ value: ServiceAreaValue;
8
+ onChange: (next: ServiceAreaValue) => void;
9
+ }
10
+ export declare function ServiceAreaSelect({ value, onChange }: Props): import("react").JSX.Element;
11
+ export {};
12
+ //# sourceMappingURL=ServiceAreaSelect.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ServiceAreaSelect.d.ts","sourceRoot":"","sources":["../../../src/components/afhandeling/ServiceAreaSelect.tsx"],"names":[],"mappings":"AAKA,MAAM,WAAW,gBAAgB;IAC/B,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED,UAAU,KAAK;IACb,KAAK,EAAE,gBAAgB,CAAC;IACxB,QAAQ,EAAE,CAAC,IAAI,EAAE,gBAAgB,KAAK,IAAI,CAAC;CAC5C;AAiCD,wBAAgB,iBAAiB,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE,KAAK,+BAwH3D"}
@@ -0,0 +1,79 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useMemo, useState } from "react";
3
+ import { Popover, PopoverContent, PopoverTrigger, Badge, cn } from "@flowselections/core";
4
+ import { ChevronDown, Search, X, Check, Globe, MapPin, Building2 } from "lucide-react";
5
+ import { GEO_ITEMS, getGeoItem } from "../../lib/geo/euServiceAreas";
6
+ const LEVEL_META = {
7
+ country: { label: "Land", icon: Globe, tone: "bg-blue-100 text-blue-700" },
8
+ province: { label: "Provincie", icon: MapPin, tone: "bg-emerald-100 text-emerald-700" },
9
+ city: { label: "Plaats", icon: Building2, tone: "bg-amber-100 text-amber-700" },
10
+ };
11
+ function isSelected(value, item) {
12
+ if (item.level === "country")
13
+ return value.countries.includes(item.code);
14
+ if (item.level === "province")
15
+ return value.provinces.includes(item.code);
16
+ return value.cities.includes(item.code);
17
+ }
18
+ function toggle(value, item) {
19
+ const has = isSelected(value, item);
20
+ if (item.level === "country") {
21
+ return { ...value, countries: has ? value.countries.filter((c) => c !== item.code) : [...value.countries, item.code] };
22
+ }
23
+ if (item.level === "province") {
24
+ return { ...value, provinces: has ? value.provinces.filter((c) => c !== item.code) : [...value.provinces, item.code] };
25
+ }
26
+ return { ...value, cities: has ? value.cities.filter((c) => c !== item.code) : [...value.cities, item.code] };
27
+ }
28
+ function removeCode(value, code) {
29
+ return {
30
+ countries: value.countries.filter((c) => c !== code),
31
+ provinces: value.provinces.filter((c) => c !== code),
32
+ cities: value.cities.filter((c) => c !== code),
33
+ };
34
+ }
35
+ export function ServiceAreaSelect({ value, onChange }) {
36
+ const [open, setOpen] = useState(false);
37
+ const [query, setQuery] = useState("");
38
+ const filtered = useMemo(() => {
39
+ const q = query.trim().toLowerCase();
40
+ const items = q
41
+ ? GEO_ITEMS.filter((i) => i.label.toLowerCase().includes(q) || i.code.toLowerCase().includes(q))
42
+ : GEO_ITEMS;
43
+ const byLevel = { country: [], province: [], city: [] };
44
+ for (const i of items)
45
+ byLevel[i.level].push(i);
46
+ for (const k of Object.keys(byLevel)) {
47
+ byLevel[k].sort((a, b) => a.label.localeCompare(b.label, "nl"));
48
+ }
49
+ // cap city list to keep the popover snappy when no query
50
+ if (!q)
51
+ byLevel.city = byLevel.city.slice(0, 60);
52
+ return byLevel;
53
+ }, [query]);
54
+ const selectedItems = useMemo(() => {
55
+ const all = [...value.countries, ...value.provinces, ...value.cities];
56
+ return all
57
+ .map((c) => getGeoItem(c) ?? { code: c, label: c, level: "country", country: c })
58
+ .sort((a, b) => {
59
+ const order = { country: 0, province: 1, city: 2 };
60
+ return order[a.level] - order[b.level] || a.label.localeCompare(b.label, "nl");
61
+ });
62
+ }, [value]);
63
+ const total = selectedItems.length;
64
+ return (_jsxs("div", { className: "space-y-2", children: [_jsxs(Popover, { open: open, onOpenChange: setOpen, children: [_jsx(PopoverTrigger, { asChild: true, children: _jsxs("button", { type: "button", className: "flex items-center justify-between w-full min-h-10 px-3 py-2 rounded-md border bg-background text-sm", children: [_jsx("span", { className: total === 0 ? "text-muted-foreground" : "", children: total === 0 ? "Selecteer landen, provincies of plaatsen…" : `${total} gebied${total === 1 ? "" : "en"} geselecteerd` }), _jsx(ChevronDown, { className: "w-4 h-4 text-muted-foreground" })] }) }), _jsxs(PopoverContent, { className: "p-0 w-[360px]", align: "start", children: [_jsxs("div", { className: "flex items-center gap-2 px-3 py-2 border-b", children: [_jsx(Search, { className: "w-4 h-4 text-muted-foreground" }), _jsx("input", { autoFocus: true, value: query, onChange: (e) => setQuery(e.target.value), placeholder: "Zoek land, provincie of plaats\u2026", className: "flex-1 bg-transparent text-sm outline-none" })] }), _jsxs("div", { className: "max-h-[340px] overflow-y-auto py-1", children: [Object.keys(filtered).map((level) => {
65
+ const items = filtered[level];
66
+ if (items.length === 0)
67
+ return null;
68
+ const Meta = LEVEL_META[level];
69
+ return (_jsxs("div", { children: [_jsxs("div", { className: "px-3 pt-2 pb-1 text-[11px] font-semibold uppercase tracking-wider text-muted-foreground", children: [Meta.label, "en"] }), items.map((item) => {
70
+ const sel = isSelected(value, item);
71
+ const Icon = LEVEL_META[item.level].icon;
72
+ return (_jsxs("button", { type: "button", onClick: () => onChange(toggle(value, item)), className: cn("flex w-full items-center gap-2 px-3 py-1.5 text-left text-sm hover:bg-muted", sel && "bg-muted/60"), children: [_jsx(Icon, { className: "h-3.5 w-3.5 text-muted-foreground shrink-0" }), _jsx("span", { className: "flex-1 truncate", children: item.label }), _jsx("span", { className: "text-[10px] text-muted-foreground", children: item.code }), sel ? _jsx(Check, { className: "h-4 w-4 text-primary" }) : null] }, item.code));
73
+ })] }, level));
74
+ }), filtered.country.length === 0 && filtered.province.length === 0 && filtered.city.length === 0 && (_jsx("div", { className: "px-3 py-6 text-center text-sm text-muted-foreground", children: "Geen resultaten" }))] })] })] }), selectedItems.length > 0 && (_jsx("div", { className: "flex flex-wrap gap-1.5", children: selectedItems.map((item) => {
75
+ const meta = LEVEL_META[item.level];
76
+ const Icon = meta.icon;
77
+ return (_jsxs(Badge, { variant: "secondary", className: cn("gap-1 pr-1", meta.tone), children: [_jsx(Icon, { className: "h-3 w-3" }), _jsx("span", { children: item.label }), _jsx("button", { type: "button", onClick: () => onChange(removeCode(value, item.code)), className: "ml-1 rounded-sm hover:bg-black/10 p-0.5", "aria-label": `Verwijder ${item.label}`, children: _jsx(X, { className: "h-3 w-3" }) })] }, item.code));
78
+ }) }))] }));
79
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"VervoerderForm.d.ts","sourceRoot":"","sources":["../../../src/components/afhandeling/VervoerderForm.tsx"],"names":[],"mappings":"AA6CA,UAAU,KAAK;IACb,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAiND,wBAAgB,cAAc,CAAC,EAAE,SAAS,EAAE,EAAE,KAAK,+BA0VlD"}
1
+ {"version":3,"file":"VervoerderForm.d.ts","sourceRoot":"","sources":["../../../src/components/afhandeling/VervoerderForm.tsx"],"names":[],"mappings":"AA8CA,UAAU,KAAK;IACb,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAyPD,wBAAgB,cAAc,CAAC,EAAE,SAAS,EAAE,EAAE,KAAK,+BA4XlD"}
@@ -8,6 +8,7 @@ import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
8
8
  import { getCarrier, upsertCarrier, } from "../../lib/carriers.functions";
9
9
  import { DEFAULT_PICKUP_MOMENT } from "../../lib/planning";
10
10
  import { COUNTRIES, countryName } from "../../lib/countries";
11
+ import { ServiceAreaSelect } from "./ServiceAreaSelect";
11
12
  const EMPTY = {
12
13
  name: "",
13
14
  contact_person: "",
@@ -17,6 +18,9 @@ const EMPTY = {
17
18
  notes: "",
18
19
  is_default: false,
19
20
  pickup_moments: [DEFAULT_PICKUP_MOMENT],
21
+ service_countries: [],
22
+ service_provinces: [],
23
+ service_cities: [],
20
24
  };
21
25
  function FieldIcon({ icon: Icon }) {
22
26
  return (_jsx("div", { className: "flex items-center justify-center w-10 h-10 rounded-md bg-primary/10 text-primary shrink-0", children: _jsx(Icon, { className: "w-4 h-4" }) }));
@@ -38,11 +42,41 @@ function CountryPicker({ value, onChange, excluded, }) {
38
42
  setQuery("");
39
43
  }, 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)))) })] })] }));
40
44
  }
41
- function PickupMomentRow({ label, days, time, onDaysChange, onTimeChange, }) {
42
- 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, 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) })] })] }));
45
+ function PickupMomentRow({ label, days, time, maxDays, onDaysChange, onTimeChange, }) {
46
+ 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) })] })] }));
47
+ }
48
+ // Normaliseer een ophaalmoment: aanmelden moet altijd vóór (of op) ophalen
49
+ // liggen, ophalen vóór (of op) bezorgen. Werkdagen-waardes zijn afstanden tot
50
+ // de bezorgdag, dus geldt: signup_days >= pickup_days >= delivery_days.
51
+ function normalizeMoment(next, prev) {
52
+ const m = { ...next };
53
+ if (next.signup_days !== prev.signup_days) {
54
+ // Aanmelden verlaagd → ophalen mag niet eerder zijn dan aanmelden
55
+ if (m.pickup_days > m.signup_days)
56
+ m.pickup_days = m.signup_days;
57
+ if (m.delivery_days > m.pickup_days)
58
+ m.delivery_days = m.pickup_days;
59
+ }
60
+ if (next.pickup_days !== prev.pickup_days) {
61
+ // Ophalen mag nooit vóór aanmelden plaatsvinden
62
+ if (m.pickup_days > m.signup_days)
63
+ m.pickup_days = m.signup_days;
64
+ if (m.delivery_days > m.pickup_days)
65
+ m.delivery_days = m.pickup_days;
66
+ }
67
+ if (next.delivery_days !== prev.delivery_days) {
68
+ if (m.delivery_days > m.pickup_days)
69
+ m.delivery_days = m.pickup_days;
70
+ }
71
+ return m;
43
72
  }
44
73
  function PickupMomentEditor({ index, value, canDelete, onChange, onDelete, }) {
45
- 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) => onChange({ ...value, signup_days: n }), onTimeChange: (v) => onChange({ ...value, signup_time: v }) }), _jsx(PickupMomentRow, { label: "Ophalen", days: value.pickup_days, time: value.pickup_time, onDaysChange: (n) => onChange({ ...value, pickup_days: n }), onTimeChange: (v) => onChange({ ...value, pickup_time: v }) }), _jsx(PickupMomentRow, { label: "Bezorgen", days: value.delivery_days, time: value.delivery_time, onDaysChange: (n) => onChange({ ...value, delivery_days: n }), onTimeChange: (v) => onChange({ ...value, delivery_time: v }) })] })] }));
74
+ const update = (patch) => {
75
+ const next = { ...value, ...patch };
76
+ onChange(normalizeMoment(next, value));
77
+ };
78
+ const pickupInvalid = value.pickup_days > value.signup_days;
79
+ 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 }) })] })] }));
46
80
  }
47
81
  export function VervoerderForm({ carrierId }) {
48
82
  const isEdit = !!carrierId;
@@ -74,6 +108,9 @@ export function VervoerderForm({ carrierId }) {
74
108
  pickup_moments: c.pickup_moments && c.pickup_moments.length > 0
75
109
  ? c.pickup_moments
76
110
  : [DEFAULT_PICKUP_MOMENT],
111
+ service_countries: c.service_countries ?? [],
112
+ service_provinces: c.service_provinces ?? [],
113
+ service_cities: c.service_cities ?? [],
77
114
  });
78
115
  setRates(c.rates);
79
116
  }
@@ -92,9 +129,10 @@ export function VervoerderForm({ carrierId }) {
92
129
  throw e;
93
130
  }
94
131
  },
95
- onSuccess: () => {
132
+ onSuccess: async () => {
96
133
  toast.success(isEdit ? "Vervoerder bijgewerkt" : "Vervoerder aangemaakt");
97
- queryClient.invalidateQueries({ queryKey: ["carriers"] });
134
+ const { invalidatePlanning } = await import("../../lib/invalidatePlanning");
135
+ await invalidatePlanning(queryClient);
98
136
  if (carrierId)
99
137
  queryClient.invalidateQueries({ queryKey: ["carrier", carrierId] });
100
138
  goBack();
@@ -143,7 +181,16 @@ export function VervoerderForm({ carrierId }) {
143
181
  } }, idx))) }), _jsxs(Button, { type: "button", variant: "outline", size: "sm", onClick: () => setForm({
144
182
  ...form,
145
183
  pickup_moments: [...form.pickup_moments, { ...DEFAULT_PICKUP_MOMENT }],
146
- }), children: [_jsx(Plus, { className: "w-4 h-4 mr-1" }), "Ophaalmoment toevoegen"] })] }), _jsxs("label", { className: "flex items-center gap-2 text-sm", children: [_jsx("input", { type: "checkbox", checked: form.is_default ?? false, onChange: (e) => setForm({ ...form, is_default: e.target.checked }), className: "h-4 w-4 rounded border" }), _jsxs("span", { children: ["Gebruik als ", _jsx("strong", { children: "standaard vervoerder" }), " voor de automatische planning (voor orders zonder eigen vervoerder)."] })] }), _jsxs("div", { className: "space-y-2", children: [_jsx(Label, { children: "Opmerkingen" }), _jsxs("div", { className: "flex gap-2", children: [_jsx(FieldIcon, { icon: FileText }), _jsx(Textarea, { value: form.notes ?? "", onChange: (e) => setForm({ ...form, notes: e.target.value }), placeholder: "Extra informatie over deze vervoerder...", rows: 3, className: "flex-1" })] })] })] })] }), _jsxs(Card, { children: [_jsxs(CardHeader, { children: [_jsx(CardTitle, { className: "text-base", children: "Verzendtarieven" }), _jsxs("p", { className: "text-sm text-muted-foreground", children: ["Stel de verzendkosten ", _jsx("strong", { children: "per kwekerskar" }), " in voor elk land waar deze vervoerder naartoe verzendt."] })] }), _jsxs(CardContent, { className: "space-y-4", children: [_jsxs("div", { className: "grid grid-cols-1 md:grid-cols-[1fr,160px,auto] gap-3", children: [_jsx(CountryPicker, { value: pendingCountry, onChange: setPendingCountry, excluded: rates.map((r) => r.country_code) }), _jsxs("div", { className: "flex items-stretch gap-2 rounded-md border bg-background h-10", children: [_jsx("div", { className: "flex items-center justify-center w-10 bg-primary/10 text-primary rounded-l-md text-sm font-medium", children: "\u20AC" }), _jsx("input", { type: "number", step: "0.01", min: 0, value: pendingPrice, onChange: (e) => setPendingPrice(e.target.value), placeholder: "Prijs", className: "flex-1 bg-transparent text-sm outline-none pr-3" })] }), _jsxs(Button, { type: "button", onClick: addRate, children: [_jsx(Plus, { className: "w-4 h-4 mr-1" }), "Toevoegen"] })] }), rates.length === 0 ? (_jsxs("div", { className: "border rounded-md py-10 text-center space-y-2", children: [_jsx(Globe, { className: "w-8 h-8 mx-auto text-muted-foreground" }), _jsx("div", { className: "font-medium", children: "Nog geen landen toegevoegd" }), _jsx("div", { className: "text-sm text-muted-foreground", children: "Voeg een land toe om verzendtarieven in te stellen." })] })) : (_jsx("div", { className: "border rounded-md divide-y", children: rates
184
+ }), children: [_jsx(Plus, { className: "w-4 h-4 mr-1" }), "Ophaalmoment toevoegen"] })] }), _jsxs("label", { className: "flex items-center gap-2 text-sm", children: [_jsx("input", { type: "checkbox", checked: form.is_default ?? false, onChange: (e) => setForm({ ...form, is_default: e.target.checked }), className: "h-4 w-4 rounded border" }), _jsxs("span", { children: ["Gebruik als ", _jsx("strong", { children: "standaard vervoerder" }), " voor de automatische planning (voor orders zonder eigen vervoerder)."] })] }), _jsxs("div", { className: "space-y-2", children: [_jsx(Label, { children: "Opmerkingen" }), _jsxs("div", { className: "flex gap-2", children: [_jsx(FieldIcon, { icon: FileText }), _jsx(Textarea, { value: form.notes ?? "", onChange: (e) => setForm({ ...form, notes: e.target.value }), placeholder: "Extra informatie over deze vervoerder...", rows: 3, className: "flex-1" })] })] })] })] }), _jsxs(Card, { children: [_jsxs(CardHeader, { children: [_jsx(CardTitle, { className: "text-base", children: "Bedieningsgebied" }), _jsx("p", { className: "text-sm text-muted-foreground", children: "Selecteer de landen, provincies en/of plaatsen waar deze vervoerder actief is. Een land dekt automatisch al zijn provincies en plaatsen." })] }), _jsx(CardContent, { children: _jsx(ServiceAreaSelect, { value: {
185
+ countries: form.service_countries ?? [],
186
+ provinces: form.service_provinces ?? [],
187
+ cities: form.service_cities ?? [],
188
+ }, onChange: (next) => setForm({
189
+ ...form,
190
+ service_countries: next.countries,
191
+ service_provinces: next.provinces,
192
+ service_cities: next.cities,
193
+ }) }) })] }), _jsxs(Card, { children: [_jsxs(CardHeader, { children: [_jsx(CardTitle, { className: "text-base", children: "Verzendtarieven" }), _jsxs("p", { className: "text-sm text-muted-foreground", children: ["Stel de verzendkosten ", _jsx("strong", { children: "per kwekerskar" }), " in voor elk land waar deze vervoerder naartoe verzendt."] })] }), _jsxs(CardContent, { className: "space-y-4", children: [_jsxs("div", { className: "grid grid-cols-1 md:grid-cols-[1fr,160px,auto] gap-3", children: [_jsx(CountryPicker, { value: pendingCountry, onChange: setPendingCountry, excluded: rates.map((r) => r.country_code) }), _jsxs("div", { className: "flex items-stretch gap-2 rounded-md border bg-background h-10", children: [_jsx("div", { className: "flex items-center justify-center w-10 bg-primary/10 text-primary rounded-l-md text-sm font-medium", children: "\u20AC" }), _jsx("input", { type: "number", step: "0.01", min: 0, value: pendingPrice, onChange: (e) => setPendingPrice(e.target.value), placeholder: "Prijs", className: "flex-1 bg-transparent text-sm outline-none pr-3" })] }), _jsxs(Button, { type: "button", onClick: addRate, children: [_jsx(Plus, { className: "w-4 h-4 mr-1" }), "Toevoegen"] })] }), rates.length === 0 ? (_jsxs("div", { className: "border rounded-md py-10 text-center space-y-2", children: [_jsx(Globe, { className: "w-8 h-8 mx-auto text-muted-foreground" }), _jsx("div", { className: "font-medium", children: "Nog geen landen toegevoegd" }), _jsx("div", { className: "text-sm text-muted-foreground", children: "Voeg een land toe om verzendtarieven in te stellen." })] })) : (_jsx("div", { className: "border rounded-md divide-y", children: rates
147
194
  .slice()
148
195
  .sort((a, b) => countryName(a.country_code).localeCompare(countryName(b.country_code), "nl"))
149
196
  .map((r) => (_jsxs("div", { className: "flex items-center gap-3 px-3 py-2", children: [_jsx(Globe, { className: "w-4 h-4 text-muted-foreground" }), _jsxs("div", { className: "flex-1", children: [_jsx("div", { className: "text-sm font-medium", children: countryName(r.country_code) }), _jsx("div", { className: "text-xs text-muted-foreground", children: r.country_code })] }), _jsxs("div", { className: "text-sm font-medium", children: ["\u20AC ", r.price_per_trolley.toFixed(2), " ", _jsx("span", { className: "text-muted-foreground font-normal", children: "/ kar" })] }), _jsx("button", { type: "button", onClick: () => removeRate(r.country_code), className: "text-muted-foreground hover:text-destructive", "aria-label": "Verwijderen", children: _jsx(Trash2, { className: "w-4 h-4" }) })] }, r.country_code))) })), _jsxs("p", { className: "text-sm text-muted-foreground", children: ["De prijs per kwekerskar wordt automatisch berekend", " ", _jsx("strong", { children: "bij het aanmaken van een bestelling" }), "."] })] })] }), _jsxs("div", { className: "flex justify-end gap-2", children: [_jsxs(Button, { type: "button", variant: "outline", onClick: goBack, children: [_jsx(X, { className: "w-4 h-4 mr-1" }), "Annuleren"] }), _jsx(Button, { type: "submit", disabled: mutation.isPending, children: mutation.isPending ? "Opslaan..." : "Opslaan" })] })] })] }));
@@ -1 +1 @@
1
- {"version":3,"file":"VervoerdersPage.d.ts","sourceRoot":"","sources":["../../../src/components/afhandeling/VervoerdersPage.tsx"],"names":[],"mappings":"AAyBA,wBAAgB,eAAe,gCAgJ9B"}
1
+ {"version":3,"file":"VervoerdersPage.d.ts","sourceRoot":"","sources":["../../../src/components/afhandeling/VervoerdersPage.tsx"],"names":[],"mappings":"AAyBA,wBAAgB,eAAe,gCA6L9B"}
@@ -1,15 +1,16 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useState } from "react";
3
3
  import { Button, Table, TableBody, TableCell, TableHead, TableHeader, TableRow, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, toast, } from "@flowselections/core";
4
- import { Plus, Pencil, Trash2, Truck } from "lucide-react";
4
+ import { Plus, Pencil, Trash2, Truck, ArrowUp, ArrowDown } from "lucide-react";
5
5
  import { Link } from "@tanstack/react-router";
6
6
  import { useServerFn } from "@tanstack/react-start";
7
7
  import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
8
- import { listCarriers, deleteCarrier } from "../../lib/carriers.functions";
8
+ import { listCarriers, deleteCarrier, reorderCarriers } from "../../lib/carriers.functions";
9
9
  export function VervoerdersPage() {
10
10
  const queryClient = useQueryClient();
11
11
  const fetchList = useServerFn(listCarriers);
12
12
  const removeCarrier = useServerFn(deleteCarrier);
13
+ const reorder = useServerFn(reorderCarriers);
13
14
  const [deletingId, setDeletingId] = useState(null);
14
15
  const { data, isLoading, error, refetch } = useQuery({
15
16
  queryKey: ["carriers"],
@@ -29,13 +30,30 @@ export function VervoerdersPage() {
29
30
  });
30
31
  const deleteMutation = useMutation({
31
32
  mutationFn: (id) => removeCarrier({ data: { id } }),
32
- onSuccess: () => {
33
+ onSuccess: async () => {
33
34
  toast.success("Vervoerder verwijderd");
34
- queryClient.invalidateQueries({ queryKey: ["carriers"] });
35
+ const { invalidatePlanning } = await import("../../lib/invalidatePlanning");
36
+ await invalidatePlanning(queryClient);
35
37
  setDeletingId(null);
36
38
  },
37
39
  onError: (err) => toast.error(err.message),
38
40
  });
41
+ const reorderMutation = useMutation({
42
+ mutationFn: (orderedIds) => reorder({ data: { orderedIds } }),
43
+ onSuccess: async () => {
44
+ const { invalidatePlanning } = await import("../../lib/invalidatePlanning");
45
+ await invalidatePlanning(queryClient);
46
+ },
47
+ onError: (err) => toast.error(err.message),
48
+ });
39
49
  const carriers = data?.carriers ?? [];
40
- return (_jsxs("div", { className: "p-6 space-y-6", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { children: [_jsx("h1", { className: "text-2xl font-semibold", children: "Vervoerders" }), _jsx("p", { className: "text-sm text-muted-foreground", children: "Beheer vervoerders en hun verzendtarieven per land." })] }), _jsx(Button, { asChild: true, children: _jsxs(Link, { to: "/afhandeling/vervoerders/nieuw", children: [_jsx(Plus, { className: "w-4 h-4 mr-1" }), "Vervoerder toevoegen"] }) })] }), _jsx("div", { className: "border rounded-md bg-card", children: isLoading ? (_jsx("div", { className: "p-12 text-center text-muted-foreground", children: "Laden..." })) : error ? (_jsxs("div", { className: "p-12 text-center space-y-3", children: [_jsxs("div", { className: "text-destructive", children: ["Fout bij laden: ", error.message] }), _jsx(Button, { variant: "outline", onClick: () => refetch(), children: "Opnieuw proberen" })] })) : carriers.length === 0 ? (_jsxs("div", { className: "p-12 text-center space-y-3", children: [_jsx(Truck, { className: "w-10 h-10 mx-auto text-muted-foreground" }), _jsx("div", { className: "font-medium", children: "Nog geen vervoerders" }), _jsx("div", { className: "text-sm text-muted-foreground", children: "Voeg een vervoerder toe om verzendtarieven in te stellen." }), _jsx(Button, { asChild: true, children: _jsxs(Link, { to: "/afhandeling/vervoerders/nieuw", children: [_jsx(Plus, { className: "w-4 h-4 mr-1" }), "Vervoerder toevoegen"] }) })] })) : (_jsxs(Table, { children: [_jsx(TableHeader, { children: _jsxs(TableRow, { children: [_jsx(TableHead, { children: "Bedrijfsnaam" }), _jsx(TableHead, { children: "Contactpersoon" }), _jsx(TableHead, { children: "E-mail" }), _jsx(TableHead, { children: "Telefoon" }), _jsx(TableHead, { children: "Levertijd" }), _jsx(TableHead, { children: "Landen" }), _jsx(TableHead, { className: "text-right", children: "Acties" })] }) }), _jsx(TableBody, { children: carriers.map((c) => (_jsxs(TableRow, { children: [_jsx(TableCell, { className: "font-medium", children: c.name }), _jsx(TableCell, { children: c.contact_person ?? "—" }), _jsx(TableCell, { children: c.email ?? "—" }), _jsx(TableCell, { children: c.phone ?? "—" }), _jsx(TableCell, { children: c.delivery_days != null ? `${c.delivery_days} dagen` : "—" }), _jsx(TableCell, { children: c.rate_count }), _jsx(TableCell, { className: "text-right", children: _jsxs("div", { className: "flex justify-end gap-1", children: [_jsx(Button, { asChild: true, variant: "ghost", size: "sm", children: _jsx(Link, { to: "/afhandeling/vervoerders/$id/bewerken", params: { id: c.id }, children: _jsx(Pencil, { className: "w-4 h-4" }) }) }), _jsx(Button, { variant: "ghost", size: "sm", onClick: () => setDeletingId(c.id), className: "text-destructive hover:text-destructive", children: _jsx(Trash2, { className: "w-4 h-4" }) })] }) })] }, c.id))) })] })) }), _jsx(AlertDialog, { open: !!deletingId, onOpenChange: (o) => !o && setDeletingId(null), children: _jsxs(AlertDialogContent, { children: [_jsxs(AlertDialogHeader, { children: [_jsx(AlertDialogTitle, { children: "Vervoerder verwijderen?" }), _jsx(AlertDialogDescription, { children: "Deze actie kan niet ongedaan gemaakt worden. Alle verzendtarieven worden ook verwijderd." })] }), _jsxs(AlertDialogFooter, { children: [_jsx(AlertDialogCancel, { children: "Annuleren" }), _jsx(AlertDialogAction, { onClick: () => deletingId && deleteMutation.mutate(deletingId), className: "bg-destructive text-destructive-foreground hover:bg-destructive/90", children: "Verwijderen" })] })] }) })] }));
50
+ const move = (index, direction) => {
51
+ const target = index + direction;
52
+ if (target < 0 || target >= carriers.length)
53
+ return;
54
+ const ids = carriers.map((c) => c.id);
55
+ [ids[index], ids[target]] = [ids[target], ids[index]];
56
+ reorderMutation.mutate(ids);
57
+ };
58
+ return (_jsxs("div", { className: "p-6 space-y-6", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { children: [_jsx("h1", { className: "text-2xl font-semibold", children: "Vervoerders" }), _jsx("p", { className: "text-sm text-muted-foreground", children: "Beheer vervoerders en hun verzendtarieven per land." })] }), _jsx(Button, { asChild: true, children: _jsxs(Link, { to: "/afhandeling/vervoerders/nieuw", children: [_jsx(Plus, { className: "w-4 h-4 mr-1" }), "Vervoerder toevoegen"] }) })] }), _jsx("div", { className: "border rounded-md bg-card", children: isLoading ? (_jsx("div", { className: "p-12 text-center text-muted-foreground", children: "Laden..." })) : error ? (_jsxs("div", { className: "p-12 text-center space-y-3", children: [_jsxs("div", { className: "text-destructive", children: ["Fout bij laden: ", error.message] }), _jsx(Button, { variant: "outline", onClick: () => refetch(), children: "Opnieuw proberen" })] })) : carriers.length === 0 ? (_jsxs("div", { className: "p-12 text-center space-y-3", children: [_jsx(Truck, { className: "w-10 h-10 mx-auto text-muted-foreground" }), _jsx("div", { className: "font-medium", children: "Nog geen vervoerders" }), _jsx("div", { className: "text-sm text-muted-foreground", children: "Voeg een vervoerder toe om verzendtarieven in te stellen." }), _jsx(Button, { asChild: true, children: _jsxs(Link, { to: "/afhandeling/vervoerders/nieuw", children: [_jsx(Plus, { className: "w-4 h-4 mr-1" }), "Vervoerder toevoegen"] }) })] })) : (_jsxs(Table, { children: [_jsx(TableHeader, { children: _jsxs(TableRow, { children: [_jsx(TableHead, { className: "w-32", children: "Prioriteit" }), _jsx(TableHead, { children: "Bedrijfsnaam" }), _jsx(TableHead, { children: "Contactpersoon" }), _jsx(TableHead, { children: "E-mail" }), _jsx(TableHead, { children: "Telefoon" }), _jsx(TableHead, { children: "Levertijd" }), _jsx(TableHead, { children: "Landen" }), _jsx(TableHead, { className: "text-right", children: "Acties" })] }) }), _jsx(TableBody, { children: carriers.map((c, index) => (_jsxs(TableRow, { children: [_jsx(TableCell, { children: _jsxs("div", { className: "flex items-center gap-1", children: [_jsx("span", { className: "w-6 text-sm font-medium tabular-nums text-muted-foreground", children: index + 1 }), _jsx(Button, { variant: "ghost", size: "sm", onClick: () => move(index, -1), disabled: index === 0 || reorderMutation.isPending, "aria-label": "Hoger", children: _jsx(ArrowUp, { className: "w-4 h-4" }) }), _jsx(Button, { variant: "ghost", size: "sm", onClick: () => move(index, 1), disabled: index === carriers.length - 1 || reorderMutation.isPending, "aria-label": "Lager", children: _jsx(ArrowDown, { className: "w-4 h-4" }) })] }) }), _jsx(TableCell, { className: "font-medium", children: c.name }), _jsx(TableCell, { children: c.contact_person ?? "—" }), _jsx(TableCell, { children: c.email ?? "—" }), _jsx(TableCell, { children: c.phone ?? "—" }), _jsx(TableCell, { children: c.delivery_days != null ? `${c.delivery_days} dagen` : "—" }), _jsx(TableCell, { children: c.rate_count }), _jsx(TableCell, { className: "text-right", children: _jsxs("div", { className: "flex justify-end gap-1", children: [_jsx(Button, { asChild: true, variant: "ghost", size: "sm", children: _jsx(Link, { to: "/afhandeling/vervoerders/$id/bewerken", params: { id: c.id }, children: _jsx(Pencil, { className: "w-4 h-4" }) }) }), _jsx(Button, { variant: "ghost", size: "sm", onClick: () => setDeletingId(c.id), className: "text-destructive hover:text-destructive", children: _jsx(Trash2, { className: "w-4 h-4" }) })] }) })] }, c.id))) })] })) }), _jsx(AlertDialog, { open: !!deletingId, onOpenChange: (o) => !o && setDeletingId(null), children: _jsxs(AlertDialogContent, { children: [_jsxs(AlertDialogHeader, { children: [_jsx(AlertDialogTitle, { children: "Vervoerder verwijderen?" }), _jsx(AlertDialogDescription, { children: "Deze actie kan niet ongedaan gemaakt worden. Alle verzendtarieven worden ook verwijderd." })] }), _jsxs(AlertDialogFooter, { children: [_jsx(AlertDialogCancel, { children: "Annuleren" }), _jsx(AlertDialogAction, { onClick: () => deletingId && deleteMutation.mutate(deletingId), className: "bg-destructive text-destructive-foreground hover:bg-destructive/90", children: "Verwijderen" })] })] }) })] }));
41
59
  }