@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.
- package/dist-lib/components/afhandeling/DagDetailDialog.d.ts +9 -0
- package/dist-lib/components/afhandeling/DagDetailDialog.d.ts.map +1 -0
- package/dist-lib/components/afhandeling/DagDetailDialog.js +145 -0
- package/dist-lib/components/afhandeling/LeveringenPage.d.ts.map +1 -1
- package/dist-lib/components/afhandeling/LeveringenPage.js +11 -5
- package/dist-lib/components/afhandeling/PickAgendaView.d.ts.map +1 -1
- package/dist-lib/components/afhandeling/PickAgendaView.js +13 -4
- package/dist-lib/components/afhandeling/PlanningAgendaPage.d.ts.map +1 -1
- package/dist-lib/components/afhandeling/PlanningAgendaPage.js +2 -2
- package/dist-lib/components/afhandeling/ServiceAreaSelect.d.ts +12 -0
- package/dist-lib/components/afhandeling/ServiceAreaSelect.d.ts.map +1 -0
- package/dist-lib/components/afhandeling/ServiceAreaSelect.js +79 -0
- package/dist-lib/components/afhandeling/VervoerderForm.d.ts.map +1 -1
- package/dist-lib/components/afhandeling/VervoerderForm.js +53 -6
- package/dist-lib/components/afhandeling/VervoerdersPage.d.ts.map +1 -1
- package/dist-lib/components/afhandeling/VervoerdersPage.js +23 -5
- package/dist-lib/components/afhandeling/WeekAgendaView.d.ts +2 -1
- package/dist-lib/components/afhandeling/WeekAgendaView.d.ts.map +1 -1
- package/dist-lib/components/afhandeling/WeekAgendaView.js +53 -18
- package/dist-lib/components/pickstation/PakbonTemplate.d.ts +5 -2
- package/dist-lib/components/pickstation/PakbonTemplate.d.ts.map +1 -1
- package/dist-lib/components/pickstation/PakbonTemplate.js +12 -10
- package/dist-lib/components/settings/AfhandelingSettingsCard.d.ts.map +1 -1
- package/dist-lib/components/settings/AfhandelingSettingsCard.js +18 -6
- package/dist-lib/hooks/useOrderPlanning.d.ts +3 -0
- package/dist-lib/hooks/useOrderPlanning.d.ts.map +1 -1
- package/dist-lib/hooks/useOrderPlanning.js +47 -6
- package/dist-lib/integrations/supabase/auth-middleware.d.ts +2261 -136
- package/dist-lib/integrations/supabase/auth-middleware.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/client.d.ts +2261 -136
- package/dist-lib/integrations/supabase/client.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/client.server.d.ts +2261 -136
- package/dist-lib/integrations/supabase/client.server.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/types.d.ts +2329 -134
- package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/types.js +1 -0
- package/dist-lib/lib/afhandelingSettings.functions.d.ts +9431 -5172
- package/dist-lib/lib/afhandelingSettings.functions.d.ts.map +1 -1
- package/dist-lib/lib/afhandelingSettings.functions.js +27 -2
- package/dist-lib/lib/carrierSignupMail.server.d.ts +56 -0
- package/dist-lib/lib/carrierSignupMail.server.d.ts.map +1 -0
- package/dist-lib/lib/carrierSignupMail.server.js +229 -0
- package/dist-lib/lib/carriers.functions.d.ts +33377 -15579
- package/dist-lib/lib/carriers.functions.d.ts.map +1 -1
- package/dist-lib/lib/carriers.functions.js +28 -2
- package/dist-lib/lib/cartAssignments.functions.d.ts +46479 -0
- package/dist-lib/lib/cartAssignments.functions.d.ts.map +1 -0
- package/dist-lib/lib/cartAssignments.functions.js +130 -0
- package/dist-lib/lib/cartMoveImpact.d.ts +8 -0
- package/dist-lib/lib/cartMoveImpact.d.ts.map +1 -0
- package/dist-lib/lib/cartMoveImpact.js +26 -0
- package/dist-lib/lib/floriday.functions.d.ts +2265 -136
- package/dist-lib/lib/floriday.functions.d.ts.map +1 -1
- package/dist-lib/lib/floriday.functions.js +178 -98
- package/dist-lib/lib/geo/euServiceAreas.d.ts +30 -0
- package/dist-lib/lib/geo/euServiceAreas.d.ts.map +1 -0
- package/dist-lib/lib/geo/euServiceAreas.js +277 -0
- package/dist-lib/lib/invalidatePlanning.d.ts +11 -0
- package/dist-lib/lib/invalidatePlanning.d.ts.map +1 -0
- package/dist-lib/lib/invalidatePlanning.js +25 -0
- package/dist-lib/lib/pickstationI18n.d.ts +4 -0
- package/dist-lib/lib/pickstationI18n.d.ts.map +1 -1
- package/dist-lib/lib/pickstationI18n.js +24 -0
- package/dist-lib/lib/pickstationOrders.server.d.ts +1 -1
- package/dist-lib/lib/pickstationOrders.server.d.ts.map +1 -1
- package/dist-lib/lib/pickstationOrders.server.js +305 -177
- package/dist-lib/lib/pickstationTypes.d.ts +11 -2
- package/dist-lib/lib/pickstationTypes.d.ts.map +1 -1
- package/dist-lib/lib/planning.d.ts +18 -2
- package/dist-lib/lib/planning.d.ts.map +1 -1
- package/dist-lib/lib/planning.js +96 -33
- 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":"
|
|
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 {
|
|
5
|
-
import {
|
|
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(
|
|
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":"
|
|
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 {
|
|
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(
|
|
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,+
|
|
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":"
|
|
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
|
-
|
|
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
|
-
|
|
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: "
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
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
|
}
|