@flowselections/floriday-verkoop-module 1.0.11 → 1.0.12
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/verkoop/CreateOrderPage.d.ts.map +1 -1
- package/dist-lib/components/verkoop/CreateOrderPage.js +217 -189
- package/dist-lib/components/verkoop/SalesOrdersTable.d.ts.map +1 -1
- package/dist-lib/components/verkoop/SalesOrdersTable.js +3 -4
- package/dist-lib/components/verkoop/SearchableSelect.d.ts +19 -0
- package/dist-lib/components/verkoop/SearchableSelect.d.ts.map +1 -0
- package/dist-lib/components/verkoop/SearchableSelect.js +21 -0
- package/dist-lib/integrations/supabase/auth-middleware.d.ts +885 -60
- package/dist-lib/integrations/supabase/auth-middleware.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/client.d.ts +885 -60
- package/dist-lib/integrations/supabase/client.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/client.server.d.ts +885 -60
- package/dist-lib/integrations/supabase/client.server.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/types.d.ts +907 -62
- package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
- package/dist-lib/lib/create-order.functions.d.ts +75223 -0
- package/dist-lib/lib/create-order.functions.d.ts.map +1 -0
- package/dist-lib/lib/create-order.functions.js +188 -0
- package/dist-lib/lib/floriday-public.functions.d.ts +5775 -0
- package/dist-lib/lib/floriday-public.functions.d.ts.map +1 -1
- package/dist-lib/lib/floriday-writes.functions.d.ts +4125 -0
- package/dist-lib/lib/floriday-writes.functions.d.ts.map +1 -1
- package/dist-lib/lib/salesorders.functions.d.ts +2760 -285
- package/dist-lib/lib/salesorders.functions.d.ts.map +1 -1
- package/dist-lib/lib/tradeitems.functions.d.ts +885 -60
- package/dist-lib/lib/tradeitems.functions.d.ts.map +1 -1
- package/dist-lib/lib/useSalesOrdersQuery.d.ts.map +1 -1
- package/dist-lib/lib/useSalesOrdersQuery.js +9 -2
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CreateOrderPage.d.ts","sourceRoot":"","sources":["../../../src/components/verkoop/CreateOrderPage.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"CreateOrderPage.d.ts","sourceRoot":"","sources":["../../../src/components/verkoop/CreateOrderPage.tsx"],"names":[],"mappings":"AA4CA,wBAAgB,eAAe,4CAib9B"}
|
|
@@ -1,218 +1,246 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
// Handmatig aanmaken van een bestelling — gebruikt exact dezelfde workflow,
|
|
3
|
+
// server-fns en validaties als de Mailbox-module. Beide modules leunen op
|
|
4
|
+
// gedeelde queries (klanten, producten met voorraad, afleverlocaties,
|
|
5
|
+
// afhaallocaties, vervoerders, orderpicktijd) en dezelfde eerstmogelijke-
|
|
6
|
+
// leverdatum-berekening. Zie src/lib/create-order.functions.ts.
|
|
7
|
+
import { useEffect, useMemo, useRef, useState } from "react";
|
|
8
|
+
import { Button, Card, CardContent, CardHeader, CardTitle, Input, Label, Textarea, toast, } from "@flowselections/core";
|
|
4
9
|
import { Link, useNavigate } from "@tanstack/react-router";
|
|
5
10
|
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
|
6
|
-
import { ArrowLeft,
|
|
7
|
-
import { supabase } from "../../integrations/supabase/client";
|
|
11
|
+
import { ArrowLeft, Plus, Trash2 } from "lucide-react";
|
|
8
12
|
import { useServerFn } from "@tanstack/react-start";
|
|
9
|
-
import {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
});
|
|
14
|
-
function pickStr(v) {
|
|
15
|
-
if (v == null)
|
|
16
|
-
return null;
|
|
17
|
-
if (typeof v === "string")
|
|
18
|
-
return v;
|
|
19
|
-
if (typeof v === "number")
|
|
20
|
-
return String(v);
|
|
21
|
-
if (Array.isArray(v)) {
|
|
22
|
-
for (const x of v) {
|
|
23
|
-
const s = pickStr(x?.text ?? x?.value ?? x);
|
|
24
|
-
if (s)
|
|
25
|
-
return s;
|
|
26
|
-
}
|
|
27
|
-
return null;
|
|
28
|
-
}
|
|
29
|
-
if (typeof v === "object") {
|
|
30
|
-
return (v.nl ?? v.NL ?? v.en ?? v.EN ?? v.text ?? v.value ??
|
|
31
|
-
Object.values(v).find((x) => typeof x === "string") ??
|
|
32
|
-
null);
|
|
33
|
-
}
|
|
34
|
-
return null;
|
|
13
|
+
import { SearchableSelect } from "./SearchableSelect";
|
|
14
|
+
import { listCustomersForCreateOrder, listProductsForCreateOrder, listDeliveryLocationsForCustomerCO, listPickupLocationsCO, listCarriersForCreateOrder, getOrderPickDaysCO, createManualOrder, } from "../../lib/create-order.functions";
|
|
15
|
+
function ReadOnlyField({ label, value }) {
|
|
16
|
+
return (_jsxs("div", { className: "space-y-0.5", children: [_jsx("div", { className: "text-[11px] uppercase tracking-wide text-muted-foreground", children: label }), _jsx("div", { className: "text-sm", children: value && String(value).trim() !== "" ? value : _jsx("span", { className: "text-muted-foreground", children: "\u2014" }) })] }));
|
|
35
17
|
}
|
|
36
|
-
function
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
return (
|
|
49
|
-
}
|
|
50
|
-
|
|
18
|
+
export function CreateOrderPage() {
|
|
19
|
+
const navigate = useNavigate();
|
|
20
|
+
const qc = useQueryClient();
|
|
21
|
+
const fetchCustomers = useServerFn(listCustomersForCreateOrder);
|
|
22
|
+
const fetchProducts = useServerFn(listProductsForCreateOrder);
|
|
23
|
+
const fetchDeliveryLocations = useServerFn(listDeliveryLocationsForCustomerCO);
|
|
24
|
+
const fetchPickupLocations = useServerFn(listPickupLocationsCO);
|
|
25
|
+
const fetchCarriers = useServerFn(listCarriersForCreateOrder);
|
|
26
|
+
const fetchPickDays = useServerFn(getOrderPickDaysCO);
|
|
27
|
+
const fetchCreate = useServerFn(createManualOrder);
|
|
28
|
+
const safe = async (fn) => {
|
|
29
|
+
try {
|
|
30
|
+
return await fn();
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return { items: [] };
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
const customersQ = useQuery({ queryKey: ["co-customers"], queryFn: () => safe(() => fetchCustomers()) });
|
|
37
|
+
const productsQ = useQuery({ queryKey: ["co-products"], queryFn: () => safe(() => fetchProducts()) });
|
|
38
|
+
const pickupQ = useQuery({ queryKey: ["co-pickup"], queryFn: () => safe(() => fetchPickupLocations()) });
|
|
39
|
+
const carriersQ = useQuery({ queryKey: ["co-carriers"], queryFn: () => safe(() => fetchCarriers()) });
|
|
40
|
+
const pickDaysQ = useQuery({
|
|
41
|
+
queryKey: ["co-pickdays"],
|
|
42
|
+
queryFn: async () => { try {
|
|
43
|
+
return await fetchPickDays();
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
return { pickDays: 0 };
|
|
47
|
+
} },
|
|
51
48
|
});
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
const
|
|
55
|
-
|
|
56
|
-
|
|
49
|
+
const customers = customersQ.data?.items ?? [];
|
|
50
|
+
const products = productsQ.data?.items ?? [];
|
|
51
|
+
const pickupLocations = pickupQ.data?.items ?? [];
|
|
52
|
+
const carriers = carriersQ.data?.items ?? [];
|
|
53
|
+
const pickDays = Math.max(0, Number(pickDaysQ.data?.pickDays ?? 0));
|
|
54
|
+
const [customerId, setCustomerId] = useState(null);
|
|
55
|
+
const [deliveryLocationId, setDeliveryLocationId] = useState(null);
|
|
56
|
+
const [pickupLocationId, setPickupLocationId] = useState(null);
|
|
57
|
+
const [carrierId, setCarrierId] = useState(null);
|
|
58
|
+
const [deliveryDate, setDeliveryDate] = useState("");
|
|
59
|
+
const [notes, setNotes] = useState("");
|
|
60
|
+
const [lines, setLines] = useState([
|
|
61
|
+
{ product_id: null, product_name: "", quantity: null, unit: "stuks", unit_price: null },
|
|
62
|
+
]);
|
|
63
|
+
// Afleverlocaties van gekozen klant
|
|
64
|
+
const deliveryLocQ = useQuery({
|
|
65
|
+
queryKey: ["co-delivery-locations", customerId],
|
|
66
|
+
enabled: !!customerId,
|
|
57
67
|
queryFn: async () => {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
.from("products")
|
|
61
|
-
.select("id, product, sale_price, unit")
|
|
62
|
-
.order("product", { ascending: true })
|
|
63
|
-
.limit(2000),
|
|
64
|
-
fetchCachedList({ data: { environment: "staging", entity: "trade-items", limit: 5000 } }),
|
|
65
|
-
]);
|
|
66
|
-
if (localRes.error)
|
|
67
|
-
throw localRes.error;
|
|
68
|
-
const out = [];
|
|
69
|
-
for (const r of localRes.data ?? []) {
|
|
70
|
-
out.push({
|
|
71
|
-
id: `local:${r.id}`,
|
|
72
|
-
name: r.product ?? "Onbekend product",
|
|
73
|
-
source: "local",
|
|
74
|
-
price: r.sale_price != null ? Number(r.sale_price) : null,
|
|
75
|
-
unit: r.unit ?? null,
|
|
76
|
-
});
|
|
68
|
+
try {
|
|
69
|
+
return await fetchDeliveryLocations({ data: { customerId: customerId } });
|
|
77
70
|
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
const name = pickStr(d.name) ??
|
|
81
|
-
pickStr(d.productName) ??
|
|
82
|
-
pickStr(d.tradeItemName) ??
|
|
83
|
-
pickStr(d.tradeItemTexts) ??
|
|
84
|
-
"Onbekend artikel";
|
|
85
|
-
out.push({
|
|
86
|
-
id: `floriday:${r.floriday_id}`,
|
|
87
|
-
name,
|
|
88
|
-
source: "floriday",
|
|
89
|
-
price: null,
|
|
90
|
-
});
|
|
71
|
+
catch {
|
|
72
|
+
return { items: [] };
|
|
91
73
|
}
|
|
92
|
-
return out;
|
|
93
74
|
},
|
|
94
|
-
staleTime: 60000,
|
|
95
75
|
});
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
const
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
const base = q
|
|
116
|
-
? list.filter((p) => p.name.toLowerCase().includes(q))
|
|
117
|
-
: list;
|
|
118
|
-
return base.slice(0, 100);
|
|
119
|
-
}, [productsQ.data, productQuery]);
|
|
120
|
-
const total = lines.reduce((s, l) => s + l.quantity * l.unitPrice, 0);
|
|
121
|
-
const addLine = () => {
|
|
122
|
-
if (!selectedProduct)
|
|
123
|
-
return;
|
|
124
|
-
const qty = Number(quantity);
|
|
125
|
-
const price = Number(unitPrice);
|
|
126
|
-
if (!Number.isFinite(qty) || qty <= 0) {
|
|
127
|
-
toast.error("Vul een geldig aantal in");
|
|
128
|
-
return;
|
|
76
|
+
const deliveryLocations = deliveryLocQ.data?.items ?? [];
|
|
77
|
+
const selectedLocation = deliveryLocations.find((l) => l.id === deliveryLocationId) ?? null;
|
|
78
|
+
// Reset delivery_location bij klantwissel
|
|
79
|
+
useEffect(() => {
|
|
80
|
+
if (deliveryLocationId && !deliveryLocations.some((l) => l.id === deliveryLocationId)) {
|
|
81
|
+
setDeliveryLocationId(null);
|
|
82
|
+
}
|
|
83
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
84
|
+
}, [customerId, deliveryLocQ.data]);
|
|
85
|
+
// Auto-fill vervoerder op basis van gekozen afleverlocatie (één keer per wissel)
|
|
86
|
+
const lastLocRef = useRef(null);
|
|
87
|
+
useEffect(() => {
|
|
88
|
+
if (deliveryLocationId !== lastLocRef.current) {
|
|
89
|
+
lastLocRef.current = deliveryLocationId;
|
|
90
|
+
const loc = deliveryLocations.find((l) => l.id === deliveryLocationId);
|
|
91
|
+
if (loc?.carrier_id)
|
|
92
|
+
setCarrierId(loc.carrier_id);
|
|
93
|
+
else if (deliveryLocationId === null)
|
|
94
|
+
setCarrierId(null);
|
|
129
95
|
}
|
|
130
|
-
|
|
131
|
-
|
|
96
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
97
|
+
}, [deliveryLocationId, deliveryLocations.length]);
|
|
98
|
+
// Eerst mogelijke leverdatum
|
|
99
|
+
const selectedCarrier = carriers.find((c) => c.id === carrierId) ?? null;
|
|
100
|
+
const carrierDeliveryDays = Math.max(0, Number(selectedCarrier?.delivery_days ?? 0));
|
|
101
|
+
const canPickDate = !!customerId && !!deliveryLocationId && !!pickupLocationId && !!carrierId;
|
|
102
|
+
const fmtISO = (d) => `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
|
|
103
|
+
const earliestDate = useMemo(() => {
|
|
104
|
+
const base = new Date();
|
|
105
|
+
base.setHours(0, 0, 0, 0);
|
|
106
|
+
base.setDate(base.getDate() + pickDays + carrierDeliveryDays);
|
|
107
|
+
return base;
|
|
108
|
+
}, [pickDays, carrierDeliveryDays]);
|
|
109
|
+
const earliestISO = fmtISO(earliestDate);
|
|
110
|
+
const earliestLabel = earliestDate.toLocaleDateString("nl-NL", {
|
|
111
|
+
day: "numeric", month: "long", year: "numeric",
|
|
112
|
+
});
|
|
113
|
+
useEffect(() => {
|
|
114
|
+
if (!canPickDate)
|
|
132
115
|
return;
|
|
116
|
+
if (!deliveryDate || deliveryDate < earliestISO) {
|
|
117
|
+
setDeliveryDate(earliestISO);
|
|
133
118
|
}
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
quantity: qty,
|
|
140
|
-
unitPrice: price,
|
|
141
|
-
},
|
|
142
|
-
]);
|
|
143
|
-
setSelectedProduct(null);
|
|
144
|
-
setQuantity("");
|
|
145
|
-
setUnitPrice("");
|
|
146
|
-
setProductQuery("");
|
|
119
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
120
|
+
}, [canPickDate, earliestISO]);
|
|
121
|
+
const blockDecimal = (e) => {
|
|
122
|
+
if ([".", ",", "e", "E", "+", "-"].includes(e.key))
|
|
123
|
+
e.preventDefault();
|
|
147
124
|
};
|
|
148
|
-
const
|
|
149
|
-
const createMutation = useMutation({
|
|
125
|
+
const createMut = useMutation({
|
|
150
126
|
mutationFn: async () => {
|
|
151
127
|
if (!customerId)
|
|
152
128
|
throw new Error("Selecteer een klant");
|
|
129
|
+
if (!deliveryLocationId)
|
|
130
|
+
throw new Error("Selecteer een afleverlocatie");
|
|
131
|
+
if (!pickupLocationId)
|
|
132
|
+
throw new Error("Selecteer een afhaallocatie (tuin)");
|
|
133
|
+
if (!carrierId)
|
|
134
|
+
throw new Error("Selecteer een vervoerder");
|
|
153
135
|
if (!deliveryDate)
|
|
154
136
|
throw new Error("Selecteer een leverdatum");
|
|
155
|
-
|
|
137
|
+
const validLines = lines.filter((l) => l.product_id && (l.quantity ?? 0) > 0);
|
|
138
|
+
if (validLines.length === 0)
|
|
156
139
|
throw new Error("Voeg minimaal één product toe");
|
|
157
|
-
const
|
|
158
|
-
|
|
159
|
-
.
|
|
160
|
-
.
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
: undefined,
|
|
187
|
-
product_name: l.product.name,
|
|
188
|
-
quantity: Math.round(l.quantity),
|
|
189
|
-
unit: l.product.unit ?? undefined,
|
|
190
|
-
unit_price: l.unitPrice,
|
|
191
|
-
}));
|
|
192
|
-
const { error: itemsErr } = await supabase
|
|
193
|
-
.from("order_items")
|
|
194
|
-
.insert(itemsPayload);
|
|
195
|
-
if (itemsErr)
|
|
196
|
-
throw itemsErr;
|
|
140
|
+
for (const l of validLines) {
|
|
141
|
+
const p = products.find((x) => x.id === l.product_id);
|
|
142
|
+
const ppc = Math.max(1, Number(p?.plants_per_crate ?? 1));
|
|
143
|
+
const q = l.quantity ?? 0;
|
|
144
|
+
if (q < ppc || q % ppc !== 0) {
|
|
145
|
+
throw new Error(`"${p?.product ?? l.product_name}" moet per hele fust besteld worden (min. 1 fust = ${ppc} planten).`);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
const customer = customers.find((c) => c.id === customerId);
|
|
149
|
+
const payload = {
|
|
150
|
+
customerId,
|
|
151
|
+
customerName: customer?.company_name ?? "Onbekende klant",
|
|
152
|
+
deliveryLocationId,
|
|
153
|
+
pickupLocationId,
|
|
154
|
+
carrierId,
|
|
155
|
+
deliveryDate,
|
|
156
|
+
notes: notes.trim() ? notes.trim() : null,
|
|
157
|
+
lines: validLines.map((l) => {
|
|
158
|
+
const p = products.find((x) => x.id === l.product_id);
|
|
159
|
+
return {
|
|
160
|
+
product_id: l.product_id,
|
|
161
|
+
product_name: p?.product ?? l.product_name ?? "Onbekend product",
|
|
162
|
+
quantity: l.quantity,
|
|
163
|
+
unit: l.unit ?? p?.unit ?? "stuks",
|
|
164
|
+
unit_price: l.unit_price ?? undefined,
|
|
165
|
+
};
|
|
166
|
+
}),
|
|
167
|
+
};
|
|
168
|
+
return await fetchCreate({ data: payload });
|
|
197
169
|
},
|
|
198
170
|
onSuccess: () => {
|
|
199
171
|
toast.success("Bestelling aangemaakt");
|
|
200
|
-
|
|
172
|
+
qc.invalidateQueries({ queryKey: ["floriday", "salesorders-cache"] });
|
|
201
173
|
navigate({ to: "/floriday-verkoop/verkooporders" });
|
|
202
174
|
},
|
|
203
175
|
onError: (e) => toast.error(e?.message ?? "Aanmaken mislukt"),
|
|
204
176
|
});
|
|
205
|
-
return (_jsxs("main", { className: "space-y-5 p-6", children: [_jsx("div", { children: _jsx(Button, { asChild: true, variant: "ghost", size: "sm", children: _jsxs(Link, { to: "/floriday-verkoop/verkooporders", children: [_jsx(ArrowLeft, { className: "mr-2 h-4 w-4" }), "Terug naar verkooporders"] }) }) }), _jsxs(
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
177
|
+
return (_jsxs("main", { className: "space-y-5 p-6", children: [_jsx("div", { children: _jsx(Button, { asChild: true, variant: "ghost", size: "sm", children: _jsxs(Link, { to: "/floriday-verkoop/verkooporders", children: [_jsx(ArrowLeft, { className: "mr-2 h-4 w-4" }), "Terug naar verkooporders"] }) }) }), _jsxs(Card, { children: [_jsxs(CardHeader, { children: [_jsx(CardTitle, { children: "Nieuwe Bestelling" }), _jsx("p", { className: "text-sm text-muted-foreground", children: "Doorloop de stappen: klant \u2192 afleverlocatie \u2192 afhaallocatie \u2192 vervoerder \u2192 leverdatum \u2192 producten." })] }), _jsxs(CardContent, { className: "space-y-5", children: [_jsxs("div", { className: "space-y-1", children: [_jsxs(Label, { className: "flex items-center gap-2", children: ["Klant ", _jsx("span", { className: "text-destructive", children: "*" })] }), _jsx(SearchableSelect, { value: customerId, placeholder: customersQ.isLoading ? "Laden..." : "Kies klant...", options: customers.map((c) => ({ value: c.id, label: c.company_name ?? "Onbekend", hint: c.email ?? undefined })), onChange: (v) => { setCustomerId(v); setDeliveryLocationId(null); }, allowClear: false })] }), _jsxs("div", { className: "space-y-1", children: [_jsxs(Label, { className: "flex items-center gap-2", children: ["Afleverlocatie ", _jsx("span", { className: "text-destructive", children: "*" })] }), _jsx(SearchableSelect, { value: deliveryLocationId, placeholder: !customerId ? "Selecteer eerst een klant"
|
|
178
|
+
: deliveryLocations.length ? "Kies locatie..."
|
|
179
|
+
: "Geen locaties bekend voor deze klant", disabled: !customerId || deliveryLocations.length === 0, allowClear: false, options: deliveryLocations.map((l) => {
|
|
180
|
+
const addr = [
|
|
181
|
+
l.address_line || [l.street, l.house_number].filter(Boolean).join(" "),
|
|
182
|
+
l.postal_code, l.city,
|
|
183
|
+
].filter(Boolean).join(", ");
|
|
184
|
+
return {
|
|
185
|
+
value: l.id,
|
|
186
|
+
label: (l.name || addr || "Locatie") + (l.is_primary ? " ★" : ""),
|
|
187
|
+
hint: l.name && addr ? addr : undefined,
|
|
188
|
+
};
|
|
189
|
+
}), onChange: setDeliveryLocationId })] }), selectedLocation && (_jsxs("div", { className: "rounded-md border bg-muted/30 p-3 space-y-2", children: [_jsx("div", { className: "text-xs font-medium text-muted-foreground", children: "Adresgegevens" }), _jsxs("div", { className: "grid grid-cols-2 gap-2", children: [_jsx(ReadOnlyField, { label: "Straat", value: selectedLocation.street }), _jsx(ReadOnlyField, { label: "Huisnummer", value: selectedLocation.house_number }), _jsx(ReadOnlyField, { label: "Postcode", value: selectedLocation.postal_code }), _jsx(ReadOnlyField, { label: "Plaats", value: selectedLocation.city }), _jsx(ReadOnlyField, { label: "Provincie", value: selectedLocation.province }), _jsx(ReadOnlyField, { label: "Land", value: selectedLocation.country })] })] })), _jsxs("div", { className: "space-y-1", children: [_jsxs(Label, { className: "flex items-center gap-2", children: ["Afhaallocatie / Tuin ", _jsx("span", { className: "text-destructive", children: "*" })] }), _jsx(SearchableSelect, { value: pickupLocationId, placeholder: pickupLocations.length ? "Kies eigen locatie..." : "Geen eigen locaties bekend", disabled: pickupLocations.length === 0, allowClear: false, options: pickupLocations.map((l) => ({ value: l.id, label: l.name })), onChange: setPickupLocationId }), _jsx("div", { className: "text-xs text-muted-foreground", children: "Eigen locatie (tuin) van waaruit de bestelling wordt geleverd of opgehaald." })] }), _jsxs("div", { className: "space-y-1", children: [_jsxs(Label, { children: ["Vervoerder ", _jsx("span", { className: "text-destructive", children: "*" })] }), _jsx(SearchableSelect, { value: carrierId, placeholder: !customerId ? "Selecteer eerst een klant"
|
|
190
|
+
: !deliveryLocationId ? "Selecteer eerst een afleverlocatie"
|
|
191
|
+
: "Kies vervoerder...", disabled: !deliveryLocationId, allowClear: false, options: carriers.map((c) => ({
|
|
192
|
+
value: c.id,
|
|
193
|
+
label: c.name + (c.is_default ? " (standaard)" : ""),
|
|
194
|
+
})), onChange: setCarrierId }), selectedLocation?.carrier_id && carrierId && carrierId !== selectedLocation.carrier_id && (_jsx("div", { className: "text-xs text-muted-foreground", children: "Wijkt af van standaard vervoerder van deze locatie \u2014 geldt alleen voor deze bestelling." }))] }), _jsxs("div", { className: "space-y-1", children: [_jsxs(Label, { className: "flex items-center gap-2", children: ["Leverdatum ", _jsx("span", { className: "text-destructive", children: "*" })] }), _jsx(Input, { type: "date", value: canPickDate ? deliveryDate : "", min: canPickDate ? earliestISO : undefined, disabled: !canPickDate, onChange: (e) => {
|
|
195
|
+
const v = e.target.value || "";
|
|
196
|
+
if (v && v < earliestISO) {
|
|
197
|
+
toast.error(`Eerst mogelijke leverdatum is ${earliestLabel}.`);
|
|
198
|
+
setDeliveryDate(earliestISO);
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
setDeliveryDate(v);
|
|
202
|
+
} }), !canPickDate ? (_jsx("div", { className: "text-xs text-muted-foreground", children: "Selecteer eerst klant, afleverlocatie, afhaallocatie en vervoerder." })) : (_jsxs("div", { className: "text-xs text-muted-foreground", children: ["Eerst beschikbare leverdatum is ", earliestLabel, ", o.b.v. orderpicktijd (", pickDays, "d) en levertijd vervoerder (", carrierDeliveryDays, "d)."] }))] }), _jsxs("div", { className: "space-y-2", children: [_jsx(Label, { children: "Bestelregels" }), _jsxs("div", { className: "grid grid-cols-12 gap-2 text-xs text-muted-foreground px-1", children: [_jsx("div", { className: "col-span-5", children: "Product" }), _jsx("div", { className: "col-span-2", children: "Planten" }), _jsx("div", { className: "col-span-2", children: "Fusten" }), _jsx("div", { className: "col-span-2", children: "Prijs" }), _jsx("div", { className: "col-span-1" })] }), lines.map((line, idx) => {
|
|
203
|
+
const product = line.product_id ? products.find((p) => p.id === line.product_id) : null;
|
|
204
|
+
const ppc = Math.max(1, Number(product?.plants_per_crate ?? 1));
|
|
205
|
+
const plants = line.quantity ?? null;
|
|
206
|
+
const cratesDisplay = plants === null ? "" : String(Math.ceil(plants / ppc));
|
|
207
|
+
return (_jsxs("div", { className: "grid grid-cols-12 gap-2 items-start", children: [_jsx("div", { className: "col-span-5", children: _jsx(SearchableSelect, { value: line.product_id, placeholder: "Kies product...", clearLabel: "\u2014 Handmatig \u2014", options: products.map((p) => ({ value: p.id, label: p.product, hint: p.unit })), onChange: (v) => {
|
|
208
|
+
const next = [...lines];
|
|
209
|
+
if (v === null)
|
|
210
|
+
next[idx] = { ...next[idx], product_id: null };
|
|
211
|
+
else {
|
|
212
|
+
const p = products.find((x) => x.id === v);
|
|
213
|
+
const newPpc = Math.max(1, Number(p?.plants_per_crate ?? 1));
|
|
214
|
+
const currentQ = Number(next[idx].quantity ?? 0);
|
|
215
|
+
const normalizedQ = currentQ > 0 ? Math.ceil(currentQ / newPpc) * newPpc : newPpc;
|
|
216
|
+
next[idx] = {
|
|
217
|
+
...next[idx],
|
|
218
|
+
product_id: v,
|
|
219
|
+
product_name: p?.product ?? "",
|
|
220
|
+
unit: next[idx].unit ?? p?.unit ?? "stuks",
|
|
221
|
+
quantity: normalizedQ,
|
|
222
|
+
unit_price: next[idx].unit_price ?? (p?.sale_price != null ? Number(p.sale_price) : null),
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
setLines(next);
|
|
226
|
+
} }) }), _jsx(Input, { className: "col-span-2", type: "number", min: ppc, step: ppc, inputMode: "numeric", onKeyDown: blockDecimal, value: plants ?? "", onChange: (e) => {
|
|
227
|
+
const next = [...lines];
|
|
228
|
+
const raw = e.target.value.replace(/[.,]/g, "");
|
|
229
|
+
const v = raw ? Number(raw) : null;
|
|
230
|
+
const rounded = v === null ? null : Math.max(1, Math.ceil(v / ppc)) * ppc;
|
|
231
|
+
next[idx] = { ...next[idx], quantity: rounded };
|
|
232
|
+
setLines(next);
|
|
233
|
+
}, placeholder: "Planten", title: `Verkoop per hele fust van ${ppc} planten` }), _jsx(Input, { className: "col-span-2", type: "number", min: 1, step: 1, inputMode: "numeric", onKeyDown: blockDecimal, value: cratesDisplay, onChange: (e) => {
|
|
234
|
+
const next = [...lines];
|
|
235
|
+
const raw = e.target.value.replace(/[.,]/g, "");
|
|
236
|
+
const cr = raw ? Math.max(1, Math.floor(Number(raw))) : null;
|
|
237
|
+
next[idx] = { ...next[idx], quantity: cr === null ? null : cr * ppc };
|
|
238
|
+
setLines(next);
|
|
239
|
+
}, placeholder: "Fusten", title: `${ppc} planten per fust` }), _jsx(Input, { className: "col-span-2", type: "number", min: 0, step: "0.01", value: line.unit_price ?? "", onChange: (e) => {
|
|
240
|
+
const next = [...lines];
|
|
241
|
+
const v = e.target.value === "" ? null : Number(e.target.value);
|
|
242
|
+
next[idx] = { ...next[idx], unit_price: v };
|
|
243
|
+
setLines(next);
|
|
244
|
+
}, placeholder: "\u20AC prijs" }), _jsx(Button, { variant: "ghost", size: "icon", className: "col-span-1", onClick: () => setLines(lines.filter((_, i) => i !== idx)), "aria-label": "Regel verwijderen", children: _jsx(Trash2, { className: "h-4 w-4" }) })] }, idx));
|
|
245
|
+
}), _jsxs(Button, { variant: "outline", size: "sm", onClick: () => setLines([...lines, { product_id: null, product_name: "", quantity: null, unit: "stuks", unit_price: null }]), children: [_jsx(Plus, { className: "h-3 w-3 mr-1" }), " Regel toevoegen"] })] }), _jsxs("div", { className: "space-y-1", children: [_jsx(Label, { children: "Opmerkingen" }), _jsx(Textarea, { value: notes, onChange: (e) => setNotes(e.target.value), placeholder: "Optionele notities bij deze bestelling", rows: 3 })] }), _jsxs("div", { className: "flex items-center justify-end gap-2 pt-2", children: [_jsx(Button, { variant: "outline", onClick: () => navigate({ to: "/floriday-verkoop/verkooporders" }), disabled: createMut.isPending, children: "Annuleren" }), _jsx(Button, { onClick: () => createMut.mutate(), disabled: createMut.isPending, children: createMut.isPending ? "Bezig..." : "Bestelling Aanmaken" })] })] })] })] }));
|
|
218
246
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SalesOrdersTable.d.ts","sourceRoot":"","sources":["../../../src/components/verkoop/SalesOrdersTable.tsx"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;
|
|
1
|
+
{"version":3,"file":"SalesOrdersTable.d.ts","sourceRoot":"","sources":["../../../src/components/verkoop/SalesOrdersTable.tsx"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAsEnE,wBAAgB,gBAAgB,CAAC,EAC/B,MAAM,EACN,OAAO,EACP,aAAa,GACd,EAAE;IACD,MAAM,EAAE,aAAa,EAAE,CAAC;IACxB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;CAChD,2CAyIA"}
|
|
@@ -3,7 +3,6 @@ import { useState } from "react";
|
|
|
3
3
|
import { Badge, Checkbox, Skeleton, Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "@flowselections/core";
|
|
4
4
|
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, } from "@flowselections/core";
|
|
5
5
|
import { MoreVertical, Package, ArrowDown } from "lucide-react";
|
|
6
|
-
const eur = new Intl.NumberFormat("nl-NL", { style: "currency", currency: "EUR" });
|
|
7
6
|
function formatDateShort(iso) {
|
|
8
7
|
if (!iso)
|
|
9
8
|
return { date: "—", time: "" };
|
|
@@ -44,7 +43,7 @@ function StatusTag({ status, rawOrder, }) {
|
|
|
44
43
|
if (rawStatus === "COMMITTED" || status.isConfirmed) {
|
|
45
44
|
return (_jsx(Badge, { variant: "outline", className: "border-amber-500/30 bg-amber-500/10 text-amber-600 dark:text-amber-400", children: "Verzend klaar" }));
|
|
46
45
|
}
|
|
47
|
-
return (_jsx(Badge, { variant: "outline", className: "border-muted-foreground/30 bg-muted text-muted-foreground", children: "Nog
|
|
46
|
+
return (_jsx(Badge, { variant: "outline", className: "border-muted-foreground/30 bg-muted text-muted-foreground", children: "Nog picken" }));
|
|
48
47
|
}
|
|
49
48
|
export function SalesOrdersTable({ orders, loading, onSelectOrder, }) {
|
|
50
49
|
const [selected, setSelected] = useState(new Set());
|
|
@@ -54,10 +53,10 @@ export function SalesOrdersTable({ orders, loading, onSelectOrder, }) {
|
|
|
54
53
|
n.has(id) ? n.delete(id) : n.add(id);
|
|
55
54
|
return n;
|
|
56
55
|
});
|
|
57
|
-
return (_jsx("div", { className: "rounded-lg border border-border bg-card", children: _jsxs(Table, { children: [_jsx(TableHeader, { children: _jsxs(TableRow, { className: "hover:bg-transparent", children: [_jsx(TableHead, { className: "w-10", children: _jsx(Checkbox, { checked: allChecked, onCheckedChange: (v) => setSelected(v === true ? new Set(orders.map((o) => o.id)) : new Set()) }) }), _jsx(TableHead, { children: "
|
|
56
|
+
return (_jsx("div", { className: "rounded-lg border border-border bg-card", children: _jsxs(Table, { children: [_jsx(TableHeader, { children: _jsxs(TableRow, { className: "hover:bg-transparent", children: [_jsx(TableHead, { className: "w-10", children: _jsx(Checkbox, { checked: allChecked, onCheckedChange: (v) => setSelected(v === true ? new Set(orders.map((o) => o.id)) : new Set()) }) }), _jsx(TableHead, { children: "Omzet" }), _jsx(TableHead, { children: _jsxs("span", { className: "inline-flex items-center gap-1", children: ["Bestelmoment ", _jsx(ArrowDown, { className: "h-3 w-3" })] }) }), _jsx(TableHead, { children: "Karren" }), _jsx(TableHead, { children: "Fusten" }), _jsx(TableHead, { children: "Klant" }), _jsx(TableHead, { children: "Levering" }), _jsx(TableHead, { children: "Status" }), _jsx(TableHead, { className: "w-10" })] }) }), _jsxs(TableBody, { children: [loading &&
|
|
58
57
|
Array.from({ length: 6 }).map((_, i) => (_jsx(TableRow, { children: Array.from({ length: 9 }).map((__, j) => (_jsx(TableCell, { children: _jsx(Skeleton, { className: "h-6 w-full" }) }, j))) }, `s-${i}`))), !loading && orders.length === 0 && (_jsx(TableRow, { children: _jsx(TableCell, { colSpan: 9, className: "py-12 text-center text-muted-foreground", children: "Geen verkooporders gevonden." }) })), !loading &&
|
|
59
58
|
orders.map((o) => {
|
|
60
59
|
const ordered = formatDateShort(o.orderedAt);
|
|
61
|
-
return (_jsxs(TableRow, { className: "align-top cursor-pointer", onClick: () => onSelectOrder?.(o), children: [_jsx(TableCell, { onClick: (e) => e.stopPropagation(), children: _jsx(Checkbox, { checked: selected.has(o.id), onCheckedChange: () => toggle(o.id) }) }),
|
|
60
|
+
return (_jsxs(TableRow, { className: "align-top cursor-pointer", onClick: () => onSelectOrder?.(o), children: [_jsx(TableCell, { onClick: (e) => e.stopPropagation(), children: _jsx(Checkbox, { checked: selected.has(o.id), onCheckedChange: () => toggle(o.id) }) }), _jsxs(TableCell, { children: [_jsxs("div", { className: "font-medium text-foreground", children: ["\u20AC ", o.revenue.toFixed(2)] }), o.orderNumber && (_jsx("div", { className: "text-xs text-muted-foreground", children: o.orderNumber }))] }), _jsx(TableCell, { children: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Package, { className: "h-4 w-4 text-primary" }), _jsxs("div", { children: [_jsx("div", { className: "font-medium text-foreground", children: ordered.date }), _jsx("div", { className: "text-xs text-muted-foreground", children: ordered.time })] })] }) }), _jsx(TableCell, { children: _jsx("div", { className: "font-medium text-foreground", children: o.karCount }) }), _jsx(TableCell, { children: _jsx("div", { className: "font-medium text-foreground", children: o.fustCount }) }), _jsxs(TableCell, { children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("span", { className: "font-medium text-foreground", children: o.customer.name }), o.isLocal && (_jsx(Badge, { variant: "outline", className: "border-primary/30 text-primary", children: "Lokaal" }))] }), _jsx("div", { className: "text-xs text-muted-foreground", children: [o.customer.postalCode, o.customer.city].filter(Boolean).join(" ") })] }), _jsx(TableCell, { className: "text-sm text-foreground", children: formatDateLong(o.deliveryAt) }), _jsx(TableCell, { children: _jsx(StatusTag, { status: o.status, rawOrder: o.raw?.order }) }), _jsx(TableCell, { onClick: (e) => e.stopPropagation(), children: _jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { className: "inline-flex h-8 w-8 items-center justify-center rounded-md text-muted-foreground hover:bg-accent hover:text-foreground", children: _jsx(MoreVertical, { className: "h-4 w-4" }) }), _jsxs(DropdownMenuContent, { align: "end", children: [_jsx(DropdownMenuItem, { children: "Bewerken" }), _jsx(DropdownMenuItem, { children: "Details bekijken" }), _jsx(DropdownMenuItem, { children: "Bevestigen" }), _jsx(DropdownMenuItem, { className: "text-destructive focus:text-destructive", children: "Annuleren" })] })] }) })] }, o.id));
|
|
62
61
|
})] })] }) }));
|
|
63
62
|
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export interface SearchableOption {
|
|
2
|
+
value: string;
|
|
3
|
+
label: string;
|
|
4
|
+
hint?: string | null;
|
|
5
|
+
}
|
|
6
|
+
interface SearchableSelectProps {
|
|
7
|
+
value: string | null;
|
|
8
|
+
options: SearchableOption[];
|
|
9
|
+
placeholder?: string;
|
|
10
|
+
emptyLabel?: string;
|
|
11
|
+
allowClear?: boolean;
|
|
12
|
+
clearLabel?: string;
|
|
13
|
+
onChange: (value: string | null) => void;
|
|
14
|
+
className?: string;
|
|
15
|
+
disabled?: boolean;
|
|
16
|
+
}
|
|
17
|
+
export declare function SearchableSelect({ value, options, placeholder, emptyLabel, allowClear, clearLabel, onChange, className, disabled, }: SearchableSelectProps): import("react/jsx-runtime").JSX.Element;
|
|
18
|
+
export {};
|
|
19
|
+
//# sourceMappingURL=SearchableSelect.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SearchableSelect.d.ts","sourceRoot":"","sources":["../../../src/components/verkoop/SearchableSelect.tsx"],"names":[],"mappings":"AAMA,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB;AAED,UAAU,qBAAqB;IAC7B,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,OAAO,EAAE,gBAAgB,EAAE,CAAC;IAC5B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IACzC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,wBAAgB,gBAAgB,CAAC,EAC/B,KAAK,EAAE,OAAO,EAAE,WAAuB,EAAE,UAA6B,EACtE,UAAiB,EAAE,UAAiC,EACpD,QAAQ,EAAE,SAAS,EAAE,QAAgB,GACtC,EAAE,qBAAqB,2CAuFvB"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useMemo, useState } from "react";
|
|
3
|
+
import { Button, Input, Popover, PopoverContent, PopoverTrigger, } from "@flowselections/core";
|
|
4
|
+
import { Check, ChevronsUpDown, Search } from "lucide-react";
|
|
5
|
+
export function SearchableSelect({ value, options, placeholder = "Kies...", emptyLabel = "Geen resultaat", allowClear = true, clearLabel = "— Geen koppeling —", onChange, className, disabled = false, }) {
|
|
6
|
+
const [open, setOpen] = useState(false);
|
|
7
|
+
const [q, setQ] = useState("");
|
|
8
|
+
const selected = useMemo(() => options.find((o) => o.value === value) ?? null, [options, value]);
|
|
9
|
+
const filtered = useMemo(() => {
|
|
10
|
+
const needle = q.trim().toLowerCase();
|
|
11
|
+
if (!needle)
|
|
12
|
+
return options.slice(0, 200);
|
|
13
|
+
return options
|
|
14
|
+
.filter((o) => o.label.toLowerCase().includes(needle) ||
|
|
15
|
+
(o.hint ?? "").toLowerCase().includes(needle))
|
|
16
|
+
.slice(0, 200);
|
|
17
|
+
}, [options, q]);
|
|
18
|
+
return (_jsxs(Popover, { open: open, onOpenChange: (o) => { if (disabled)
|
|
19
|
+
return; setOpen(o); if (!o)
|
|
20
|
+
setQ(""); }, children: [_jsx(PopoverTrigger, { asChild: true, children: _jsxs(Button, { type: "button", variant: "outline", role: "combobox", "aria-expanded": open, disabled: disabled, className: `w-full justify-between font-normal ${className ?? ""}`, children: [_jsx("span", { className: `truncate ${selected ? "" : "text-muted-foreground"}`, children: selected?.label ?? placeholder }), _jsx(ChevronsUpDown, { className: "ml-2 h-4 w-4 shrink-0 opacity-50" })] }) }), _jsxs(PopoverContent, { className: "w-[--radix-popover-trigger-width] p-0", align: "start", children: [_jsx("div", { className: "p-2 border-b", children: _jsxs("div", { className: "relative", children: [_jsx(Search, { className: "absolute left-2 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" }), _jsx(Input, { autoFocus: true, value: q, onChange: (e) => setQ(e.target.value), placeholder: `Zoek in ${options.length} items...`, className: "pl-8 h-9" })] }) }), _jsxs("div", { className: "max-h-64 overflow-y-auto py-1", children: [allowClear && (_jsxs("button", { type: "button", className: "w-full text-left text-sm px-3 py-2 hover:bg-accent flex items-center gap-2", onClick: () => { onChange(null); setOpen(false); }, children: [value === null && _jsx(Check, { className: "h-3 w-3" }), _jsx("span", { className: "text-muted-foreground italic", children: clearLabel })] })), filtered.length === 0 ? (_jsx("div", { className: "px-3 py-6 text-sm text-muted-foreground text-center", children: emptyLabel })) : (filtered.map((o) => (_jsxs("button", { type: "button", className: "w-full text-left text-sm px-3 py-2 hover:bg-accent flex items-start gap-2", onClick: () => { onChange(o.value); setOpen(false); }, children: [_jsx(Check, { className: `h-3 w-3 mt-1 shrink-0 ${value === o.value ? "" : "invisible"}` }), _jsxs("div", { className: "flex-1 min-w-0", children: [_jsx("div", { className: "truncate", children: o.label }), o.hint && _jsx("div", { className: "text-xs text-muted-foreground truncate", children: o.hint })] })] }, o.value)))), filtered.length === 200 && (_jsx("div", { className: "px-3 py-2 text-xs text-muted-foreground text-center border-t", children: "Toon eerste 200 resultaten \u2014 verfijn je zoekopdracht" }))] })] })] }));
|
|
21
|
+
}
|