@flowselections/floriday-verkoop-module 1.0.17 → 1.0.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/dist-lib/components/verkoop/CreateOrderPage.d.ts.map +1 -1
  2. package/dist-lib/components/verkoop/CreateOrderPage.js +6 -4
  3. package/dist-lib/components/verkoop/EditLocalOrderDialog.d.ts +6 -0
  4. package/dist-lib/components/verkoop/EditLocalOrderDialog.d.ts.map +1 -0
  5. package/dist-lib/components/verkoop/EditLocalOrderDialog.js +102 -0
  6. package/dist-lib/components/verkoop/OrderKarindelingCard.d.ts.map +1 -1
  7. package/dist-lib/components/verkoop/OrderKarindelingCard.js +26 -28
  8. package/dist-lib/components/verkoop/OrderShipmentsCard.d.ts +5 -0
  9. package/dist-lib/components/verkoop/OrderShipmentsCard.d.ts.map +1 -0
  10. package/dist-lib/components/verkoop/OrderShipmentsCard.js +137 -0
  11. package/dist-lib/components/verkoop/SalesOrderDetailPage.d.ts.map +1 -1
  12. package/dist-lib/components/verkoop/SalesOrderDetailPage.js +8 -2
  13. package/dist-lib/components/verkoop/SalesOrderDetailView.d.ts.map +1 -1
  14. package/dist-lib/components/verkoop/SalesOrderDetailView.js +2 -1
  15. package/dist-lib/components/verkoop/SalesOrdersTable.d.ts +4 -1
  16. package/dist-lib/components/verkoop/SalesOrdersTable.d.ts.map +1 -1
  17. package/dist-lib/components/verkoop/SalesOrdersTable.js +2 -2
  18. package/dist-lib/components/verkoop/VerkoopOrdersPage.d.ts.map +1 -1
  19. package/dist-lib/components/verkoop/VerkoopOrdersPage.js +43 -1
  20. package/dist-lib/integrations/supabase/auth-middleware.d.ts +436 -149
  21. package/dist-lib/integrations/supabase/auth-middleware.d.ts.map +1 -1
  22. package/dist-lib/integrations/supabase/client.d.ts +436 -149
  23. package/dist-lib/integrations/supabase/client.d.ts.map +1 -1
  24. package/dist-lib/integrations/supabase/client.server.d.ts +436 -149
  25. package/dist-lib/integrations/supabase/client.server.d.ts.map +1 -1
  26. package/dist-lib/integrations/supabase/types.d.ts +452 -153
  27. package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
  28. package/dist-lib/lib/cart-lock.functions.d.ts +436 -149
  29. package/dist-lib/lib/cart-lock.functions.d.ts.map +1 -1
  30. package/dist-lib/lib/cart-replan.functions.d.ts +884 -310
  31. package/dist-lib/lib/cart-replan.functions.d.ts.map +1 -1
  32. package/dist-lib/lib/create-order.functions.d.ts +39491 -1379
  33. package/dist-lib/lib/create-order.functions.d.ts.map +1 -1
  34. package/dist-lib/lib/create-order.functions.js +814 -64
  35. package/dist-lib/lib/expand-group-order-items.d.ts +13 -0
  36. package/dist-lib/lib/expand-group-order-items.d.ts.map +1 -0
  37. package/dist-lib/lib/expand-group-order-items.js +216 -0
  38. package/dist-lib/lib/floriday-auto-fulfillment.functions.d.ts +1321 -460
  39. package/dist-lib/lib/floriday-auto-fulfillment.functions.d.ts.map +1 -1
  40. package/dist-lib/lib/floriday-logistic-labels.functions.d.ts +436 -149
  41. package/dist-lib/lib/floriday-logistic-labels.functions.d.ts.map +1 -1
  42. package/dist-lib/lib/floriday-public.functions.d.ts +3052 -1043
  43. package/dist-lib/lib/floriday-public.functions.d.ts.map +1 -1
  44. package/dist-lib/lib/floriday-writes.functions.d.ts +2180 -745
  45. package/dist-lib/lib/floriday-writes.functions.d.ts.map +1 -1
  46. package/dist-lib/lib/order-shipments.functions.d.ts +47382 -0
  47. package/dist-lib/lib/order-shipments.functions.d.ts.map +1 -0
  48. package/dist-lib/lib/order-shipments.functions.js +178 -0
  49. package/dist-lib/lib/salesorders-mapping.d.ts +1 -0
  50. package/dist-lib/lib/salesorders-mapping.d.ts.map +1 -1
  51. package/dist-lib/lib/salesorders.functions.d.ts +1321 -460
  52. package/dist-lib/lib/salesorders.functions.d.ts.map +1 -1
  53. package/dist-lib/lib/tradeitems.functions.d.ts +436 -149
  54. package/dist-lib/lib/tradeitems.functions.d.ts.map +1 -1
  55. package/dist-lib/lib/useSalesOrdersQuery.d.ts.map +1 -1
  56. package/dist-lib/lib/useSalesOrdersQuery.js +39 -2
  57. package/package.json +2 -2
@@ -1 +1 @@
1
- {"version":3,"file":"CreateOrderPage.d.ts","sourceRoot":"","sources":["../../../src/components/verkoop/CreateOrderPage.tsx"],"names":[],"mappings":"AA6CA,wBAAgB,eAAe,4CA6c9B"}
1
+ {"version":3,"file":"CreateOrderPage.d.ts","sourceRoot":"","sources":["../../../src/components/verkoop/CreateOrderPage.tsx"],"names":[],"mappings":"AA6CA,wBAAgB,eAAe,4CA6d9B"}
@@ -56,6 +56,7 @@ export function CreateOrderPage() {
56
56
  const [pickupLocationId, setPickupLocationId] = useState(null);
57
57
  const [carrierId, setCarrierId] = useState(null);
58
58
  const [deliveryDate, setDeliveryDate] = useState("");
59
+ const [isReservation, setIsReservation] = useState(false);
59
60
  const [notes, setNotes] = useState("");
60
61
  const [lines, setLines] = useState([
61
62
  { product_id: null, product_name: "", quantity: null, unit: "stuks", unit_price: null },
@@ -132,7 +133,7 @@ export function CreateOrderPage() {
132
133
  throw new Error("Selecteer een afhaallocatie (tuin)");
133
134
  if (!carrierId)
134
135
  throw new Error("Selecteer een vervoerder");
135
- if (!deliveryDate)
136
+ if (!isReservation && !deliveryDate)
136
137
  throw new Error("Selecteer een leverdatum");
137
138
  const validLines = lines.filter((l) => l.product_id && (l.quantity ?? 0) > 0);
138
139
  if (validLines.length === 0)
@@ -152,7 +153,8 @@ export function CreateOrderPage() {
152
153
  deliveryLocationId,
153
154
  pickupLocationId,
154
155
  carrierId,
155
- deliveryDate,
156
+ deliveryDate: isReservation ? null : deliveryDate,
157
+ isReservation,
156
158
  notes: notes.trim() ? notes.trim() : null,
157
159
  lines: validLines.map((l) => {
158
160
  const p = products.find((x) => x.id === l.product_id);
@@ -203,7 +205,7 @@ export function CreateOrderPage() {
203
205
  : "Kies vervoerder...", disabled: !deliveryLocationId, allowClear: false, options: carriers.map((c) => ({
204
206
  value: c.id,
205
207
  label: c.name + (c.is_default ? " (standaard)" : ""),
206
- })), 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) => {
208
+ })), 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 ", !isReservation && _jsx("span", { className: "text-destructive", children: "*" })] }), _jsx(Input, { type: "date", value: !isReservation && canPickDate ? deliveryDate : "", min: canPickDate ? earliestISO : undefined, disabled: isReservation || !canPickDate, onChange: (e) => {
207
209
  const v = e.target.value || "";
208
210
  if (v && v < earliestISO) {
209
211
  toast.error(`Eerst mogelijke leverdatum is ${earliestLabel}.`);
@@ -211,7 +213,7 @@ export function CreateOrderPage() {
211
213
  return;
212
214
  }
213
215
  setDeliveryDate(v);
214
- } }), !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) => {
216
+ } }), _jsxs("label", { className: "mt-1 flex items-center gap-2 text-sm text-foreground", children: [_jsx("input", { type: "checkbox", checked: isReservation, onChange: (e) => setIsReservation(e.target.checked), className: "h-4 w-4 rounded border-input" }), "Reservering (nog geen vaste leverdatum)"] }), isReservation ? (_jsx("div", { className: "text-xs text-muted-foreground", children: "De bestelling wordt als reservering aangemaakt zonder leverdatumvalidatie." })) : !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) => {
215
217
  const product = line.product_id ? products.find((p) => p.id === line.product_id) : null;
216
218
  const ppc = Math.max(1, Number(product?.plants_per_crate ?? 1));
217
219
  const plants = line.quantity ?? null;
@@ -0,0 +1,6 @@
1
+ export declare function EditLocalOrderDialog({ orderId, open, onOpenChange, }: {
2
+ orderId: string;
3
+ open: boolean;
4
+ onOpenChange: (o: boolean) => void;
5
+ }): import("react/jsx-runtime").JSX.Element | null;
6
+ //# sourceMappingURL=EditLocalOrderDialog.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EditLocalOrderDialog.d.ts","sourceRoot":"","sources":["../../../src/components/verkoop/EditLocalOrderDialog.tsx"],"names":[],"mappings":"AAoBA,wBAAgB,oBAAoB,CAAC,EACnC,OAAO,EACP,IAAI,EACJ,YAAY,GACb,EAAE;IACD,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,OAAO,CAAC;IACd,YAAY,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;CACpC,kDAyMA"}
@@ -0,0 +1,102 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useState } from "react";
3
+ import { Button, Input, toast } from "@flowselections/core";
4
+ import { useServerFn } from "@tanstack/react-start";
5
+ import { useQueryClient } from "@tanstack/react-query";
6
+ import { getLocalOrderForEdit, updateLocalOrder, } from "../../lib/create-order.functions";
7
+ export function EditLocalOrderDialog({ orderId, open, onOpenChange, }) {
8
+ const load = useServerFn(getLocalOrderForEdit);
9
+ const save = useServerFn(updateLocalOrder);
10
+ const queryClient = useQueryClient();
11
+ const [loading, setLoading] = useState(false);
12
+ const [saving, setSaving] = useState(false);
13
+ const [error, setError] = useState(null);
14
+ const [deliveryDate, setDeliveryDate] = useState("");
15
+ const [isReservation, setIsReservation] = useState(false);
16
+ const [items, setItems] = useState([]);
17
+ useEffect(() => {
18
+ if (!open)
19
+ return;
20
+ let cancelled = false;
21
+ setLoading(true);
22
+ setError(null);
23
+ load({ data: { orderId } })
24
+ .then((r) => {
25
+ if (cancelled)
26
+ return;
27
+ setDeliveryDate((r.deliveryDate ?? "").slice(0, 10));
28
+ setIsReservation(!!r.isReservation);
29
+ setItems(r.items.map((it) => ({
30
+ id: it.id,
31
+ productName: it.productName,
32
+ quantity: it.quantity,
33
+ unit: it.unit,
34
+ plantsPerCrate: it.plantsPerCrate ?? 1,
35
+ minQuantity: Number(it.minQuantity ?? 0),
36
+ shippedQuantity: Number(it.shippedQuantity ?? 0),
37
+ plannedQuantity: Number(it.plannedQuantity ?? 0),
38
+ })));
39
+ })
40
+ .catch((e) => !cancelled && setError(e?.message ?? "Laden mislukt"))
41
+ .finally(() => !cancelled && setLoading(false));
42
+ return () => {
43
+ cancelled = true;
44
+ };
45
+ }, [open, orderId, load]);
46
+ if (!open)
47
+ return null;
48
+ const setPlants = (idx, plants) => {
49
+ setItems((prev) => prev.map((p, i) => (i === idx ? { ...p, quantity: Number.isFinite(plants) ? Math.max(0, plants) : 0 } : p)));
50
+ };
51
+ const setFusten = (idx, fusten) => {
52
+ setItems((prev) => prev.map((p, i) => {
53
+ if (i !== idx)
54
+ return p;
55
+ const ppc = Math.max(1, p.plantsPerCrate);
56
+ const f = Number.isFinite(fusten) ? Math.max(0, fusten) : 0;
57
+ return { ...p, quantity: f * ppc };
58
+ }));
59
+ };
60
+ const handleSave = async () => {
61
+ for (const it of items) {
62
+ const ppc = Math.max(1, it.plantsPerCrate);
63
+ if (it.quantity < ppc || it.quantity % ppc !== 0) {
64
+ toast.error(`"${it.productName}" moet per hele fust besteld worden (min. 1 fust = ${ppc} planten).`);
65
+ return;
66
+ }
67
+ if (it.quantity < it.minQuantity) {
68
+ toast.error(`"${it.productName}" kan niet lager dan ${it.minQuantity} — er is al ${it.shippedQuantity} geleverd en ${it.plannedQuantity} gepland op bezorgmomenten.`);
69
+ return;
70
+ }
71
+ }
72
+ try {
73
+ setSaving(true);
74
+ await save({
75
+ data: {
76
+ orderId,
77
+ deliveryDate: !isReservation && deliveryDate ? deliveryDate : undefined,
78
+ items: items.map((i) => ({ itemId: i.id, quantity: Math.round(i.quantity) })),
79
+ },
80
+ });
81
+ toast.success("Bestelling bijgewerkt");
82
+ await Promise.all([
83
+ queryClient.invalidateQueries({ queryKey: ["floriday", "salesorders-cache"] }),
84
+ queryClient.invalidateQueries({ queryKey: ["order-shipments", orderId] }),
85
+ queryClient.invalidateQueries({ queryKey: ["order-carts", orderId] }),
86
+ queryClient.invalidateQueries({ queryKey: ["salesorder-detail", orderId] }),
87
+ ]);
88
+ onOpenChange(false);
89
+ }
90
+ catch (e) {
91
+ toast.error(e?.message ?? "Bijwerken mislukt");
92
+ }
93
+ finally {
94
+ setSaving(false);
95
+ }
96
+ };
97
+ return (_jsx("div", { className: "fixed inset-0 z-50 flex items-center justify-center bg-black/50 p-4", onClick: () => !saving && onOpenChange(false), children: _jsxs("div", { className: "w-full max-w-xl rounded-lg border border-border bg-card p-6 shadow-lg", onClick: (e) => e.stopPropagation(), children: [_jsx("h2", { className: "text-lg font-semibold text-foreground", children: "Bestelling bewerken" }), _jsx("p", { className: "mt-1 text-sm text-muted-foreground", children: "Pas de leverdatum en het aantal planten of fusten per regel aan. Voorraad en karindeling worden automatisch bijgewerkt." }), loading ? (_jsx("div", { className: "mt-6 text-sm text-muted-foreground", children: "Laden\u2026" })) : error ? (_jsx("div", { className: "mt-6 rounded border border-destructive/30 bg-destructive/5 p-3 text-sm text-destructive", children: error })) : (_jsxs("div", { className: "mt-5 space-y-4", children: [!isReservation && (_jsxs("div", { children: [_jsx("label", { className: "mb-1 block text-sm font-medium text-foreground", children: "Leverdatum" }), _jsx(Input, { type: "date", value: deliveryDate, onChange: (e) => setDeliveryDate(e.target.value) })] })), isReservation && (_jsx("div", { className: "rounded border border-border bg-muted/40 px-3 py-2 text-xs text-muted-foreground", children: "Dit is een reservering \u2014 er is nog geen leverdatum. Voeg een bezorgmoment toe om (deels) uit te leveren." })), _jsxs("div", { children: [_jsx("div", { className: "mb-2 text-sm font-medium text-foreground", children: "Regels" }), _jsxs("div", { className: "grid grid-cols-12 gap-2 px-1 text-xs text-muted-foreground", children: [_jsx("div", { className: "col-span-6", children: "Product" }), _jsx("div", { className: "col-span-3", children: "Planten" }), _jsx("div", { className: "col-span-3", children: "Fusten" })] }), _jsxs("div", { className: "mt-1 space-y-2", children: [items.map((it, idx) => {
98
+ const ppc = Math.max(1, it.plantsPerCrate);
99
+ const fusten = ppc > 0 ? Math.ceil(it.quantity / ppc) : 0;
100
+ return (_jsxs("div", { className: "grid grid-cols-12 items-start gap-2 rounded border border-border p-2", children: [_jsxs("div", { className: "col-span-6 min-w-0 text-sm", children: [_jsx("div", { className: "truncate font-medium text-foreground", children: it.productName }), _jsxs("div", { className: "text-xs text-muted-foreground", children: [ppc, " planten per fust"] }), it.minQuantity > 0 && (_jsxs("div", { className: "text-xs text-primary", children: ["min. ", it.minQuantity, " (reeds ", it.shippedQuantity, " geleverd, ", it.plannedQuantity, " gepland)"] }))] }), _jsx("div", { className: "col-span-3", children: _jsx(Input, { type: "number", min: 0, step: ppc, value: it.quantity, onChange: (e) => setPlants(idx, Number(e.target.value)) }) }), _jsx("div", { className: "col-span-3", children: _jsx(Input, { type: "number", min: 0, step: 1, value: fusten, onChange: (e) => setFusten(idx, Number(e.target.value)) }) })] }, it.id));
101
+ }), items.length === 0 && (_jsx("div", { className: "text-sm text-muted-foreground", children: "Geen regels op deze bestelling." }))] })] })] })), _jsxs("div", { className: "mt-6 flex justify-end gap-2", children: [_jsx(Button, { variant: "outline", size: "sm", onClick: () => onOpenChange(false), disabled: saving, children: "Annuleren" }), _jsx(Button, { size: "sm", onClick: handleSave, disabled: saving || loading || !!error, children: saving ? "Bezig…" : "Opslaan" })] })] }) }));
102
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"OrderKarindelingCard.d.ts","sourceRoot":"","sources":["../../../src/components/verkoop/OrderKarindelingCard.tsx"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AA6F/D,wBAAgB,oBAAoB,CAAC,EAAE,KAAK,EAAE,EAAE;IAAE,KAAK,EAAE,aAAa,CAAA;CAAE,2CA+KvE"}
1
+ {"version":3,"file":"OrderKarindelingCard.d.ts","sourceRoot":"","sources":["../../../src/components/verkoop/OrderKarindelingCard.tsx"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AA8F/D,wBAAgB,oBAAoB,CAAC,EAAE,KAAK,EAAE,EAAE;IAAE,KAAK,EAAE,aAAa,CAAA;CAAE,2CAgLvE"}
@@ -9,6 +9,7 @@ import { Badge, Card, CardContent, CardHeader, CardTitle, Table, TableBody, Tabl
9
9
  import { CheckCircle2, Lock } from "lucide-react";
10
10
  import { supabase } from "../../integrations/supabase/client";
11
11
  import { planCarts } from "../../lib/cart-planner";
12
+ import { expandOrderItemsForPlanner } from "../../lib/expand-group-order-items";
12
13
  function deriveCartsFromFloriday(order) {
13
14
  const o = order.raw.order ?? {};
14
15
  const pack = o?.packingConfiguration ?? {};
@@ -74,6 +75,7 @@ export function OrderKarindelingCard({ order }) {
74
75
  const orderId = typeof rawId === "string" ? rawId.replace(/^(local:|mailbox-proposal:)/, "") : rawId;
75
76
  const [lockedAt, setLockedAt] = useState(null);
76
77
  const [dbItems, setDbItems] = useState(null);
78
+ const [plannerInputs, setPlannerInputs] = useState(null);
77
79
  // Laadt order_items + gekoppelde products (voorraad-belading) en
78
80
  // ververst live bij mutaties op order_items, orders of products.
79
81
  useEffect(() => {
@@ -90,8 +92,24 @@ export function OrderKarindelingCard({ order }) {
90
92
  ]);
91
93
  if (!active)
92
94
  return;
93
- setDbItems(itemRows ?? []);
95
+ const items = itemRows ?? [];
96
+ setDbItems(items);
94
97
  setLockedAt(orderRow?.plan_locked_at ?? null);
98
+ // Alle order_items doorgeven — expand-helper resolveert zelf groepen
99
+ // (via product_id óf via naamprefix "[Groep] ...") en losse artikelen.
100
+ const raws = items.map((it) => ({
101
+ id: it.id,
102
+ product_id: it.product_id,
103
+ product_name: it.product_name,
104
+ quantity: Number(it.quantity ?? 0),
105
+ plants_per_crate: Math.max(1, Number(it.products?.plants_per_crate ?? 1)),
106
+ crates_per_layer: Math.max(1, Number(it.products?.crates_per_layer ?? 1)),
107
+ layers_per_cart: Math.max(1, Number(it.products?.layers_per_cart ?? 1)),
108
+ }));
109
+ const expanded = await expandOrderItemsForPlanner(supabase, raws);
110
+ if (!active)
111
+ return;
112
+ setPlannerInputs(expanded);
95
113
  };
96
114
  void load();
97
115
  const channel = supabase
@@ -105,36 +123,16 @@ export function OrderKarindelingCard({ order }) {
105
123
  void supabase.removeChannel(channel);
106
124
  };
107
125
  }, [orderId]);
108
- // Bouw plannerinput uit voorraad-belading. Alleen items met een gekoppeld
109
- // product (product_id + belading in products-tabel) worden meegenomen.
126
+ // Bouw plannerinput. Bij een groep-order_item zijn de sub-items al
127
+ // opgesplitst met member-belading; anders is er één regel per item.
110
128
  const plannerCarts = useMemo(() => {
111
- if (!dbItems || dbItems.length === 0)
129
+ if (!plannerInputs || plannerInputs.length === 0) {
130
+ if (dbItems && dbItems.length > 0)
131
+ return null;
112
132
  return null;
113
- const inputs = [];
114
- for (const it of dbItems) {
115
- const p = it.products;
116
- if (!it.product_id || !p)
117
- continue;
118
- const ppc = Math.max(1, Number(p.plants_per_crate ?? 0));
119
- const cpl = Math.max(1, Number(p.crates_per_layer ?? 0));
120
- const lpc = Math.max(1, Number(p.layers_per_cart ?? 0));
121
- const plants = Math.max(0, Number(it.quantity ?? 0));
122
- if (plants <= 0)
123
- continue;
124
- inputs.push({
125
- order_item_id: it.id,
126
- product_id: it.product_id,
127
- product_name: it.product_name,
128
- plants,
129
- plants_per_crate: ppc,
130
- crates_per_layer: cpl,
131
- layers_per_cart: lpc,
132
- });
133
133
  }
134
- if (inputs.length === 0)
135
- return null;
136
- return planCarts(inputs).carts;
137
- }, [dbItems]);
134
+ return planCarts(plannerInputs).carts;
135
+ }, [plannerInputs, dbItems]);
138
136
  const floridayCarts = useMemo(() => deriveCartsFromFloriday(order), [order]);
139
137
  const usingVoorraad = plannerCarts !== null;
140
138
  const carts = usingVoorraad
@@ -0,0 +1,5 @@
1
+ import type { SalesOrderDTO } from "../../lib/salesorders-mapping";
2
+ export declare function OrderShipmentsCard({ order }: {
3
+ order: SalesOrderDTO;
4
+ }): import("react/jsx-runtime").JSX.Element | null;
5
+ //# sourceMappingURL=OrderShipmentsCard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"OrderShipmentsCard.d.ts","sourceRoot":"","sources":["../../../src/components/verkoop/OrderShipmentsCard.tsx"],"names":[],"mappings":"AAuBA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AA4C/D,wBAAgB,kBAAkB,CAAC,EAAE,KAAK,EAAE,EAAE;IAAE,KAAK,EAAE,aAAa,CAAA;CAAE,kDASrE"}
@@ -0,0 +1,137 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ // Bezorgmomenten (deelleveringen) voor een bestelling.
3
+ // Toont totalen (besteld / gepland / geleverd / openstaand), lijst met shipments
4
+ // en knoppen om nieuwe bezorgmomenten aan te maken en te markeren als geleverd.
5
+ import { useMemo, useState } from "react";
6
+ import { Badge, Button, Card, CardContent, CardHeader, CardTitle, Input, Table, TableBody, TableCell, TableHead, TableHeader, TableRow, toast, } from "@flowselections/core";
7
+ import { CalendarPlus, CheckCircle2, Trash2, Truck } from "lucide-react";
8
+ import { useQuery, useQueryClient } from "@tanstack/react-query";
9
+ import { useServerFn } from "@tanstack/react-start";
10
+ import { cancelShipment, createShipment, listOrderShipments, markShipmentShipped, } from "../../lib/order-shipments.functions";
11
+ function formatDate(iso) {
12
+ if (!iso)
13
+ return "—";
14
+ const d = new Date(iso);
15
+ if (Number.isNaN(d.getTime()))
16
+ return iso;
17
+ return d.toLocaleDateString("nl-NL", { day: "numeric", month: "short", year: "numeric" });
18
+ }
19
+ export function OrderShipmentsCard({ order }) {
20
+ // Alleen lokale (en dus reserveer-bare) orders ondersteunen deelleveringen.
21
+ const rawId = order.isLocal && order.id.startsWith("local:")
22
+ ? order.id.slice("local:".length)
23
+ : null;
24
+ if (!rawId)
25
+ return null;
26
+ return _jsx(OrderShipmentsInner, { orderId: rawId });
27
+ }
28
+ function OrderShipmentsInner({ orderId }) {
29
+ const list = useServerFn(listOrderShipments);
30
+ const create = useServerFn(createShipment);
31
+ const markShipped = useServerFn(markShipmentShipped);
32
+ const cancel = useServerFn(cancelShipment);
33
+ const queryClient = useQueryClient();
34
+ const [openCreate, setOpenCreate] = useState(false);
35
+ const { data, isLoading } = useQuery({
36
+ queryKey: ["order-shipments", orderId],
37
+ queryFn: () => list({ data: { orderId } }),
38
+ });
39
+ const totals = data?.totals ?? [];
40
+ const shipments = data?.shipments ?? [];
41
+ const summed = useMemo(() => {
42
+ return totals.reduce((a, t) => ({
43
+ ordered: a.ordered + t.ordered,
44
+ shipped: a.shipped + t.shipped,
45
+ planned: a.planned + t.planned,
46
+ outstanding: a.outstanding + t.outstanding,
47
+ }), { ordered: 0, shipped: 0, planned: 0, outstanding: 0 });
48
+ }, [totals]);
49
+ const refresh = () => {
50
+ queryClient.invalidateQueries({ queryKey: ["order-shipments", orderId] });
51
+ queryClient.invalidateQueries({ queryKey: ["floriday", "salesorders-cache"] });
52
+ };
53
+ const hasOpen = totals.some((t) => t.remaining > 0);
54
+ const allShipped = totals.length > 0 && summed.outstanding === 0;
55
+ return (_jsxs(_Fragment, { children: [_jsxs(Card, { children: [_jsxs(CardHeader, { className: "flex flex-row items-center justify-between", children: [_jsx(CardTitle, { className: "text-base", children: "Bezorgmomenten & deelleveringen" }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsxs(Badge, { variant: allShipped ? "default" : "secondary", children: [summed.shipped, " / ", summed.ordered, " geleverd"] }), _jsxs(Button, { size: "sm", onClick: () => setOpenCreate(true), disabled: !hasOpen, title: hasOpen ? "" : "Er zijn geen openstaande aantallen meer.", children: [_jsx(CalendarPlus, { className: "mr-2 h-4 w-4" }), "Nieuw bezorgmoment"] })] })] }), _jsx(CardContent, { className: "space-y-6", children: isLoading ? (_jsx("p", { className: "text-sm text-muted-foreground", children: "Laden\u2026" })) : (_jsxs(_Fragment, { children: [_jsxs("div", { children: [_jsx("div", { className: "mb-2 text-sm font-medium", children: "Voortgang per artikel" }), _jsxs(Table, { children: [_jsx(TableHeader, { children: _jsxs(TableRow, { children: [_jsx(TableHead, { children: "Artikel" }), _jsx(TableHead, { className: "text-right", children: "Besteld" }), _jsx(TableHead, { className: "text-right", children: "Gepland" }), _jsx(TableHead, { className: "text-right", children: "Geleverd" }), _jsx(TableHead, { className: "text-right", children: "Nog te leveren" })] }) }), _jsx(TableBody, { children: totals.map((t) => (_jsxs(TableRow, { children: [_jsx(TableCell, { className: "font-medium", children: t.product_name }), _jsx(TableCell, { className: "text-right", children: t.ordered }), _jsx(TableCell, { className: "text-right", children: t.planned }), _jsx(TableCell, { className: "text-right", children: t.shipped }), _jsx(TableCell, { className: "text-right", children: _jsx("span", { className: t.outstanding > 0 ? "font-semibold text-primary" : "text-muted-foreground", children: t.outstanding }) })] }, t.order_item_id))) })] })] }), _jsxs("div", { children: [_jsx("div", { className: "mb-2 text-sm font-medium", children: "Bezorgmomenten" }), shipments.length === 0 ? (_jsx("p", { className: "text-sm text-muted-foreground", children: "Nog geen bezorgmomenten aangemaakt. Maak er \u00E9\u00E9n aan om (deels) uit te leveren." })) : (_jsx("div", { className: "space-y-3", children: shipments.map((s) => (_jsxs("div", { className: "rounded-md border border-border", children: [_jsxs("div", { className: "flex flex-wrap items-center justify-between gap-2 border-b border-border bg-muted/30 px-3 py-2 text-sm", children: [_jsxs("div", { className: "flex items-center gap-3", children: [_jsxs("span", { className: "font-mono font-medium", children: ["Levering #", s.shipment_number] }), _jsx("span", { className: "text-muted-foreground", children: formatDate(s.delivery_date) }), _jsxs(Badge, { variant: s.status === "shipped" ? "default" : "secondary", className: "gap-1", children: [s.status === "shipped" && _jsx(CheckCircle2, { className: "h-3 w-3" }), s.status === "shipped"
56
+ ? "Geleverd"
57
+ : s.status === "cancelled"
58
+ ? "Geannuleerd"
59
+ : "Gepland"] })] }), _jsx("div", { className: "flex gap-2", children: s.status === "planned" && (_jsxs(_Fragment, { children: [_jsxs(Button, { size: "sm", variant: "default", onClick: async () => {
60
+ try {
61
+ await markShipped({ data: { shipmentId: s.id } });
62
+ toast.success("Bezorgmoment geleverd");
63
+ refresh();
64
+ }
65
+ catch (e) {
66
+ toast.error(e?.message ?? "Markeren mislukt");
67
+ }
68
+ }, children: [_jsx(Truck, { className: "mr-2 h-4 w-4" }), "Markeer geleverd"] }), _jsx(Button, { size: "sm", variant: "outline", onClick: async () => {
69
+ if (!confirm("Bezorgmoment verwijderen?"))
70
+ return;
71
+ try {
72
+ await cancel({ data: { shipmentId: s.id } });
73
+ toast.success("Bezorgmoment verwijderd");
74
+ refresh();
75
+ }
76
+ catch (e) {
77
+ toast.error(e?.message ?? "Verwijderen mislukt");
78
+ }
79
+ }, children: _jsx(Trash2, { className: "h-4 w-4" }) })] })) })] }), _jsxs(Table, { children: [_jsx(TableHeader, { children: _jsxs(TableRow, { children: [_jsx(TableHead, { children: "Artikel" }), _jsx(TableHead, { className: "text-right", children: "Aantal" })] }) }), _jsx(TableBody, { children: s.order_shipment_items.map((i) => (_jsxs(TableRow, { children: [_jsx(TableCell, { children: i.product_name }), _jsx(TableCell, { className: "text-right", children: i.quantity })] }, i.id))) })] })] }, s.id))) }))] })] })) })] }), openCreate && (_jsx(CreateShipmentDialog, { orderId: orderId, totals: totals.filter((t) => t.remaining > 0), onClose: () => setOpenCreate(false), onCreated: () => {
80
+ setOpenCreate(false);
81
+ refresh();
82
+ }, create: create }))] }));
83
+ }
84
+ function CreateShipmentDialog({ orderId, totals, onClose, onCreated, create, }) {
85
+ const [deliveryDate, setDeliveryDate] = useState(() => {
86
+ const d = new Date();
87
+ d.setDate(d.getDate() + 1);
88
+ return d.toISOString().slice(0, 10);
89
+ });
90
+ const [qtyByItem, setQtyByItem] = useState(() => {
91
+ const init = {};
92
+ for (const t of totals)
93
+ init[t.order_item_id] = 0;
94
+ return init;
95
+ });
96
+ const [saving, setSaving] = useState(false);
97
+ const items = totals.map((t) => ({
98
+ total: t,
99
+ qty: qtyByItem[t.order_item_id] ?? 0,
100
+ }));
101
+ const canSave = items.some((i) => i.qty > 0) && !!deliveryDate;
102
+ const handleSave = async () => {
103
+ setSaving(true);
104
+ try {
105
+ const selected = items.filter((i) => i.qty > 0);
106
+ await create({
107
+ data: {
108
+ orderId,
109
+ deliveryDate,
110
+ items: selected.map((i) => ({
111
+ orderItemId: i.total.order_item_id,
112
+ productId: i.total.product_id,
113
+ productName: i.total.product_name,
114
+ quantity: i.qty,
115
+ })),
116
+ },
117
+ });
118
+ toast.success("Bezorgmoment aangemaakt");
119
+ onCreated();
120
+ }
121
+ catch (e) {
122
+ toast.error(e?.message ?? "Aanmaken mislukt");
123
+ }
124
+ finally {
125
+ setSaving(false);
126
+ }
127
+ };
128
+ return (_jsx("div", { className: "fixed inset-0 z-50 flex items-center justify-center bg-black/50 p-4", onClick: () => !saving && onClose(), children: _jsxs("div", { className: "w-full max-w-xl rounded-lg border border-border bg-card p-6 shadow-lg", onClick: (e) => e.stopPropagation(), children: [_jsx("h2", { className: "text-lg font-semibold text-foreground", children: "Nieuw bezorgmoment" }), _jsx("p", { className: "mt-1 text-sm text-muted-foreground", children: "Geef aan wat er op deze datum wordt geleverd. Aantallen mogen niet hoger zijn dan het openstaande aantal." }), _jsxs("div", { className: "mt-5 space-y-4", children: [_jsxs("div", { children: [_jsx("label", { className: "mb-1 block text-sm font-medium", children: "Bezorgdatum" }), _jsx(Input, { type: "date", value: deliveryDate, onChange: (e) => setDeliveryDate(e.target.value) })] }), _jsxs("div", { children: [_jsx("div", { className: "mb-2 text-sm font-medium", children: "Regels" }), _jsxs("div", { className: "space-y-2", children: [items.map((i) => {
129
+ const ppc = Math.max(1, i.total.plants_per_crate ?? 1);
130
+ const remainingFusten = Math.floor(i.total.remaining / ppc);
131
+ const qtyFusten = Math.floor(i.qty / ppc);
132
+ return (_jsxs("div", { className: "grid grid-cols-12 items-center gap-2 rounded border border-border p-2", children: [_jsxs("div", { className: "col-span-6 min-w-0", children: [_jsx("div", { className: "truncate text-sm font-medium", children: i.total.product_name }), _jsxs("div", { className: "text-xs text-muted-foreground", children: ["openstaand: ", remainingFusten, " fusten (", i.total.remaining, " planten) \u00B7 ", ppc, "/fust"] })] }), _jsx("div", { className: "col-span-3", children: _jsx(Input, { type: "number", min: 0, max: remainingFusten, step: 1, value: qtyFusten, onChange: (e) => {
133
+ const f = Math.max(0, Math.min(remainingFusten, Number(e.target.value) || 0));
134
+ setQtyByItem((prev) => ({ ...prev, [i.total.order_item_id]: f * ppc }));
135
+ } }) }), _jsx("div", { className: "col-span-1 text-xs text-muted-foreground", children: "fusten" }), _jsx("div", { className: "col-span-2 flex gap-1", children: _jsx(Button, { variant: "ghost", size: "sm", onClick: () => setQtyByItem((prev) => ({ ...prev, [i.total.order_item_id]: remainingFusten * ppc })), children: "Alles" }) })] }, i.total.order_item_id));
136
+ }), items.length === 0 && (_jsx("div", { className: "text-sm text-muted-foreground", children: "Geen openstaande aantallen." }))] })] })] }), _jsxs("div", { className: "mt-6 flex justify-end gap-2", children: [_jsx(Button, { variant: "outline", size: "sm", onClick: onClose, disabled: saving, children: "Annuleren" }), _jsx(Button, { size: "sm", onClick: handleSave, disabled: !canSave || saving, children: saving ? "Bezig…" : "Aanmaken" })] })] }) }));
137
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"SalesOrderDetailPage.d.ts","sourceRoot":"","sources":["../../../src/components/verkoop/SalesOrderDetailPage.tsx"],"names":[],"mappings":"AAMA,wBAAgB,oBAAoB,4CAsCnC"}
1
+ {"version":3,"file":"SalesOrderDetailPage.d.ts","sourceRoot":"","sources":["../../../src/components/verkoop/SalesOrderDetailPage.tsx"],"names":[],"mappings":"AAQA,wBAAgB,oBAAoB,4CA0DnC"}
@@ -1,8 +1,10 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useState } from "react";
2
3
  import { Link, useParams } from "@tanstack/react-router";
3
4
  import { Button } from "@flowselections/core";
4
- import { ArrowLeft } from "lucide-react";
5
+ import { ArrowLeft, Pencil } from "lucide-react";
5
6
  import { SalesOrderDetailView } from "../../components/verkoop/SalesOrderDetailView";
7
+ import { EditLocalOrderDialog } from "../../components/verkoop/EditLocalOrderDialog";
6
8
  import { useSalesOrdersQuery } from "../../lib/useSalesOrdersQuery";
7
9
  export function SalesOrderDetailPage() {
8
10
  const { salesOrderId } = useParams({
@@ -10,5 +12,9 @@ export function SalesOrderDetailPage() {
10
12
  });
11
13
  const { data, isLoading, error } = useSalesOrdersQuery();
12
14
  const order = data?.orders.find((o) => o.id === salesOrderId) ?? null;
13
- return (_jsxs("main", { className: "space-y-5 p-6", children: [_jsx("div", { className: "flex items-center justify-between gap-3", 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"] }) }) }), isLoading && (_jsx("div", { className: "rounded-lg border border-border bg-card p-8 text-sm text-muted-foreground", children: "Laden\u2026" })), error && (_jsx("div", { className: "rounded-lg border border-destructive/30 bg-destructive/5 p-4 text-sm text-destructive", children: error.message })), !isLoading && !error && !order && (_jsx("div", { className: "rounded-lg border border-border bg-card p-8 text-sm text-muted-foreground", children: "Order niet gevonden." })), order && (_jsx(SalesOrderDetailView, { order: order, sellerFallback: data?.sellerFallback ?? null }))] }));
15
+ const [editOpen, setEditOpen] = useState(false);
16
+ const rawLocalId = order?.isLocal && order.id.startsWith("local:")
17
+ ? order.id.slice("local:".length)
18
+ : null;
19
+ return (_jsxs("main", { className: "space-y-5 p-6", children: [_jsxs("div", { className: "flex items-center justify-between gap-3", 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"] }) }), rawLocalId && (_jsxs(Button, { variant: "outline", size: "sm", onClick: () => setEditOpen(true), children: [_jsx(Pencil, { className: "mr-2 h-4 w-4" }), "Bestelling bewerken"] }))] }), isLoading && (_jsx("div", { className: "rounded-lg border border-border bg-card p-8 text-sm text-muted-foreground", children: "Laden\u2026" })), error && (_jsx("div", { className: "rounded-lg border border-destructive/30 bg-destructive/5 p-4 text-sm text-destructive", children: error.message })), !isLoading && !error && !order && (_jsx("div", { className: "rounded-lg border border-border bg-card p-8 text-sm text-muted-foreground", children: "Order niet gevonden." })), order && (_jsx(SalesOrderDetailView, { order: order, sellerFallback: data?.sellerFallback ?? null })), rawLocalId && (_jsx(EditLocalOrderDialog, { orderId: rawLocalId, open: editOpen, onOpenChange: setEditOpen }))] }));
14
20
  }
@@ -1 +1 @@
1
- {"version":3,"file":"SalesOrderDetailView.d.ts","sourceRoot":"","sources":["../../../src/components/verkoop/SalesOrderDetailView.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AA+HnE,wBAAgB,oBAAoB,CAAC,EACnC,KAAK,EACL,cAAc,GACf,EAAE;IACD,KAAK,EAAE,aAAa,CAAC;IACrB,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAChC,2CA8RA"}
1
+ {"version":3,"file":"SalesOrderDetailView.d.ts","sourceRoot":"","sources":["../../../src/components/verkoop/SalesOrderDetailView.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAiInE,wBAAgB,oBAAoB,CAAC,EACnC,KAAK,EACL,cAAc,GACf,EAAE;IACD,KAAK,EAAE,aAAa,CAAC;IACrB,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAChC,2CAgSA"}
@@ -3,6 +3,7 @@ import { CheckCircle2, ImageIcon } from "lucide-react";
3
3
  import { OrderKarindelingCard } from "./OrderKarindelingCard";
4
4
  import { OrderAfleverbrievenCard } from "./OrderAfleverbrievenCard";
5
5
  import { AutoFulfillmentStatusCard } from "./AutoFulfillmentStatusCard";
6
+ import { OrderShipmentsCard } from "./OrderShipmentsCard";
6
7
  const eurFlex = new Intl.NumberFormat("nl-NL", {
7
8
  style: "currency",
8
9
  currency: "EUR",
@@ -212,5 +213,5 @@ export function SalesOrderDetailView({ order, sellerFallback, }) {
212
213
  const bestellingLabel = order.status.isCancelled
213
214
  ? "Geannuleerd"
214
215
  : STATUS_LABELS[statusKey] ?? (order.status.isShipped ? "Verzonden" : order.status.isConfirmed ? "Order bevestigd" : "Open");
215
- return (_jsxs("div", { className: "space-y-6", children: [_jsxs("div", { className: "flex items-center gap-3 rounded-md border border-border bg-muted/30 p-3", children: [_jsx("div", { className: "flex h-12 w-12 shrink-0 items-center justify-center overflow-hidden rounded-md bg-muted", children: order.imageUrl ? (_jsx("img", { src: order.imageUrl, alt: "", className: "h-full w-full object-cover" })) : (_jsx(ImageIcon, { className: "h-6 w-6 text-muted-foreground" })) }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsxs("div", { className: "truncate text-base font-semibold text-foreground", children: [order.productName, o.tradeItemVersion ? (_jsxs("span", { className: "ml-2 text-xs font-normal text-muted-foreground", children: ["\u00B7 v", o.tradeItemVersion] })) : null] }), _jsx("div", { className: "truncate text-xs text-muted-foreground", children: order.orderNumber || order.sku || "—" })] })] }), _jsxs("div", { className: "grid grid-cols-1 gap-6 lg:grid-cols-[1fr_360px]", children: [_jsxs("div", { className: "space-y-6", children: [_jsxs("div", { children: [_jsx(SectionTitle, { children: "Verkooporder" }), _jsx(Row, { label: "Ordernummer", children: order.orderNumber || "—" }), _jsx(Row, { label: "Handelsvorm", children: tradingType }), _jsx(Row, { label: "Bestelmoment", children: fmtDateTime(order.orderedAt) }), _jsx(Row, { label: "Aantal", children: _jsxs("div", { className: "flex flex-wrap gap-4", children: [_jsxs("div", { children: [order.fustCount, " fusten"] }), _jsxs("div", { children: [order.karCount, " karren"] })] }) }), _jsx(Row, { label: "Belading", children: belading }), _jsx(Row, { label: "Klant referentie", children: klantReferentie }), _jsx(Row, { label: "Floriday-referentie", children: floridayRef }), _jsx(Row, { label: "Verkoopkanaal", children: verkoopkanaal }), _jsx(Row, { label: "Financi\u00EBle afhandeling", children: financial }), _jsx(Row, { label: "Verkoper", children: sellerName ? (_jsxs("span", { className: "inline-flex items-center gap-2", children: [_jsx("span", { className: "inline-flex h-6 w-6 items-center justify-center rounded-full bg-muted text-[10px] font-semibold text-muted-foreground", children: initials(sellerName) }), sellerName] })) : ("—") })] }), _jsxs("div", { children: [_jsx(SectionTitle, { children: "Levering" }), _jsx(Row, { label: "Afleverdatum", children: fmtDateTime(order.deliveryAt) }), _jsx(Row, { label: "Incoterm", children: incoterm }), _jsx(Row, { label: "Levering", children: _jsxs("div", { className: "inline-flex max-w-xs items-start gap-3 rounded-md border border-border bg-card p-3", children: [_jsx("div", { className: "flex h-9 w-9 items-center justify-center rounded-md bg-primary/10 text-primary", children: _jsxs("svg", { viewBox: "0 0 24 24", className: "h-5 w-5", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [_jsx("path", { d: "M3 11l9-8 9 8" }), _jsx("path", { d: "M5 10v10h14V10" }), _jsx("path", { d: "M10 20v-6h4v6" })] }) }), _jsxs("div", { className: "text-sm", children: [_jsx("div", { children: addr.line || "—" }), _jsx("div", { children: [addr.postal, addr.city].filter(Boolean).join(" ") || "—" }), _jsx("div", { children: countryLabel ?? "" }), addr.gln && (_jsx("div", { className: "mt-1 text-xs text-muted-foreground", children: addr.gln }))] })] }) })] }), _jsxs("div", { children: [_jsx(SectionTitle, { children: "Klant" }), _jsx(Row, { label: "Naam", children: _jsx("span", { className: "font-medium", children: customerName }) }), _jsx(Row, { label: "GLN", children: customerGln }), _jsx(Row, { label: "Administratienummer", children: customerAdmin }), _jsx(Row, { label: "Telefoonnummer", children: customerPhone })] })] }), _jsxs("div", { className: "space-y-6", children: [_jsxs("div", { className: "rounded-md border border-border p-4", children: [_jsx("div", { className: "mb-3 text-sm font-semibold", children: "Prijs per stuk" }), _jsxs("div", { className: "space-y-2 text-sm", children: [_jsxs("div", { className: "flex justify-between", children: [_jsx("span", { className: "text-muted-foreground", children: "Product" }), _jsxs("span", { children: [oldProductPrice !== null && (_jsx("span", { className: "mr-2 text-destructive line-through", children: eurFlex.format(oldProductPrice) })), eurFlex.format(productUnit)] })] }), _jsxs("div", { className: "flex justify-between", children: [_jsx("span", { className: "text-muted-foreground", children: "Transportkosten" }), _jsx("span", { children: eurFlex.format(transportPerPiece) })] }), _jsxs("div", { className: "flex justify-between", children: [_jsx("span", { className: "text-muted-foreground", children: "Inkoopprijs" }), _jsx("span", { children: eurFlex.format(purchaseUnit) })] }), _jsxs("div", { className: "mt-2 flex justify-between border-t border-border pt-2 font-semibold", children: [_jsx("span", { children: "Totaal" }), _jsxs("span", { children: [_jsxs("span", { className: "mr-2 text-xs font-normal text-muted-foreground", children: ["(", packages, " \u00D7 ", piecesPerPackage, " \u00D7 ", eurFlex.format(productUnit), ")"] }), eurFlex.format(totalAmount)] })] })] })] }), _jsxs("div", { className: "rounded-md border border-border p-4", children: [_jsx("div", { className: "mb-3 text-sm font-semibold", children: "Status" }), _jsxs("div", { className: "space-y-3 text-sm", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsx("div", { className: "text-muted-foreground", children: "Bestelling" }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(CheckCircle2, { className: "h-4 w-4 text-emerald-600" }), bestellingLabel] })] }), hasCorrection && (_jsxs("div", { className: "flex items-center justify-between", children: [_jsx("div", { className: "text-muted-foreground", children: "Correctie" }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(CheckCircle2, { className: "h-4 w-4 text-emerald-600" }), "Correctie is verwerkt"] })] }))] })] })] })] }), _jsx(OrderKarindelingCard, { order: order }), o?.salesOrderId ? (_jsxs(_Fragment, { children: [_jsx(AutoFulfillmentStatusCard, { salesOrderId: String(o.salesOrderId) }), _jsx(OrderAfleverbrievenCard, { salesOrderId: String(o.salesOrderId) })] })) : null] }));
216
+ return (_jsxs("div", { className: "space-y-6", children: [_jsxs("div", { className: "flex items-center gap-3 rounded-md border border-border bg-muted/30 p-3", children: [_jsx("div", { className: "flex h-12 w-12 shrink-0 items-center justify-center overflow-hidden rounded-md bg-muted", children: order.imageUrl ? (_jsx("img", { src: order.imageUrl, alt: "", className: "h-full w-full object-cover" })) : (_jsx(ImageIcon, { className: "h-6 w-6 text-muted-foreground" })) }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsxs("div", { className: "truncate text-base font-semibold text-foreground", children: [order.productName, o.tradeItemVersion ? (_jsxs("span", { className: "ml-2 text-xs font-normal text-muted-foreground", children: ["\u00B7 v", o.tradeItemVersion] })) : null] }), _jsx("div", { className: "truncate text-xs text-muted-foreground", children: order.orderNumber || order.sku || "—" })] })] }), _jsxs("div", { className: "grid grid-cols-1 gap-6 lg:grid-cols-[1fr_360px]", children: [_jsxs("div", { className: "space-y-6", children: [_jsxs("div", { children: [_jsx(SectionTitle, { children: "Verkooporder" }), _jsx(Row, { label: "Ordernummer", children: order.orderNumber || "—" }), _jsx(Row, { label: "Handelsvorm", children: tradingType }), _jsx(Row, { label: "Bestelmoment", children: fmtDateTime(order.orderedAt) }), _jsx(Row, { label: "Aantal", children: _jsxs("div", { className: "flex flex-wrap gap-4", children: [_jsxs("div", { children: [order.fustCount, " fusten"] }), _jsxs("div", { children: [order.karCount, " karren"] })] }) }), _jsx(Row, { label: "Belading", children: belading }), _jsx(Row, { label: "Klant referentie", children: klantReferentie }), _jsx(Row, { label: "Floriday-referentie", children: floridayRef }), _jsx(Row, { label: "Verkoopkanaal", children: verkoopkanaal }), _jsx(Row, { label: "Financi\u00EBle afhandeling", children: financial }), _jsx(Row, { label: "Verkoper", children: sellerName ? (_jsxs("span", { className: "inline-flex items-center gap-2", children: [_jsx("span", { className: "inline-flex h-6 w-6 items-center justify-center rounded-full bg-muted text-[10px] font-semibold text-muted-foreground", children: initials(sellerName) }), sellerName] })) : ("—") })] }), _jsxs("div", { children: [_jsx(SectionTitle, { children: "Levering" }), _jsx(Row, { label: "Afleverdatum", children: fmtDateTime(order.deliveryAt) }), _jsx(Row, { label: "Incoterm", children: incoterm }), _jsx(Row, { label: "Levering", children: _jsxs("div", { className: "inline-flex max-w-xs items-start gap-3 rounded-md border border-border bg-card p-3", children: [_jsx("div", { className: "flex h-9 w-9 items-center justify-center rounded-md bg-primary/10 text-primary", children: _jsxs("svg", { viewBox: "0 0 24 24", className: "h-5 w-5", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [_jsx("path", { d: "M3 11l9-8 9 8" }), _jsx("path", { d: "M5 10v10h14V10" }), _jsx("path", { d: "M10 20v-6h4v6" })] }) }), _jsxs("div", { className: "text-sm", children: [_jsx("div", { children: addr.line || "—" }), _jsx("div", { children: [addr.postal, addr.city].filter(Boolean).join(" ") || "—" }), _jsx("div", { children: countryLabel ?? "" }), addr.gln && (_jsx("div", { className: "mt-1 text-xs text-muted-foreground", children: addr.gln }))] })] }) })] }), _jsxs("div", { children: [_jsx(SectionTitle, { children: "Klant" }), _jsx(Row, { label: "Naam", children: _jsx("span", { className: "font-medium", children: customerName }) }), _jsx(Row, { label: "GLN", children: customerGln }), _jsx(Row, { label: "Administratienummer", children: customerAdmin }), _jsx(Row, { label: "Telefoonnummer", children: customerPhone })] })] }), _jsxs("div", { className: "space-y-6", children: [_jsxs("div", { className: "rounded-md border border-border p-4", children: [_jsx("div", { className: "mb-3 text-sm font-semibold", children: "Prijs per stuk" }), _jsxs("div", { className: "space-y-2 text-sm", children: [_jsxs("div", { className: "flex justify-between", children: [_jsx("span", { className: "text-muted-foreground", children: "Product" }), _jsxs("span", { children: [oldProductPrice !== null && (_jsx("span", { className: "mr-2 text-destructive line-through", children: eurFlex.format(oldProductPrice) })), eurFlex.format(productUnit)] })] }), _jsxs("div", { className: "flex justify-between", children: [_jsx("span", { className: "text-muted-foreground", children: "Transportkosten" }), _jsx("span", { children: eurFlex.format(transportPerPiece) })] }), _jsxs("div", { className: "flex justify-between", children: [_jsx("span", { className: "text-muted-foreground", children: "Inkoopprijs" }), _jsx("span", { children: eurFlex.format(purchaseUnit) })] }), _jsxs("div", { className: "mt-2 flex justify-between border-t border-border pt-2 font-semibold", children: [_jsx("span", { children: "Totaal" }), _jsxs("span", { children: [_jsxs("span", { className: "mr-2 text-xs font-normal text-muted-foreground", children: ["(", packages, " \u00D7 ", piecesPerPackage, " \u00D7 ", eurFlex.format(productUnit), ")"] }), eurFlex.format(totalAmount)] })] })] })] }), _jsxs("div", { className: "rounded-md border border-border p-4", children: [_jsx("div", { className: "mb-3 text-sm font-semibold", children: "Status" }), _jsxs("div", { className: "space-y-3 text-sm", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsx("div", { className: "text-muted-foreground", children: "Bestelling" }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(CheckCircle2, { className: "h-4 w-4 text-emerald-600" }), bestellingLabel] })] }), hasCorrection && (_jsxs("div", { className: "flex items-center justify-between", children: [_jsx("div", { className: "text-muted-foreground", children: "Correctie" }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(CheckCircle2, { className: "h-4 w-4 text-emerald-600" }), "Correctie is verwerkt"] })] }))] })] })] })] }), _jsx(OrderShipmentsCard, { order: order }), _jsx(OrderKarindelingCard, { order: order }), o?.salesOrderId ? (_jsxs(_Fragment, { children: [_jsx(AutoFulfillmentStatusCard, { salesOrderId: String(o.salesOrderId) }), _jsx(OrderAfleverbrievenCard, { salesOrderId: String(o.salesOrderId) })] })) : null] }));
216
217
  }
@@ -1,7 +1,10 @@
1
1
  import type { SalesOrderDTO } from "../../lib/salesorders-mapping";
2
- export declare function SalesOrdersTable({ orders, loading, onSelectOrder, }: {
2
+ export declare function SalesOrdersTable({ orders, loading, onSelectOrder, onDeleteOrder, onEditOrder, onConfirmOrder, }: {
3
3
  orders: SalesOrderDTO[];
4
4
  loading?: boolean;
5
5
  onSelectOrder?: (order: SalesOrderDTO) => void;
6
+ onDeleteOrder?: (order: SalesOrderDTO) => void;
7
+ onEditOrder?: (order: SalesOrderDTO) => void;
8
+ onConfirmOrder?: (order: SalesOrderDTO) => void;
6
9
  }): import("react/jsx-runtime").JSX.Element;
7
10
  //# sourceMappingURL=SalesOrdersTable.d.ts.map
@@ -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;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"}
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,EACb,aAAa,EACb,WAAW,EACX,cAAc,GACf,EAAE;IACD,MAAM,EAAE,aAAa,EAAE,CAAC;IACxB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;IAC/C,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;IAC/C,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;IAC7C,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;CACjD,2CAsKA"}
@@ -45,7 +45,7 @@ function StatusTag({ status, rawOrder, }) {
45
45
  }
46
46
  return (_jsx(Badge, { variant: "outline", className: "border-muted-foreground/30 bg-muted text-muted-foreground", children: "Nog picken" }));
47
47
  }
48
- export function SalesOrdersTable({ orders, loading, onSelectOrder, }) {
48
+ export function SalesOrdersTable({ orders, loading, onSelectOrder, onDeleteOrder, onEditOrder, onConfirmOrder, }) {
49
49
  const [selected, setSelected] = useState(new Set());
50
50
  const allChecked = orders.length > 0 && selected.size === orders.length;
51
51
  const toggle = (id) => setSelected((s) => {
@@ -57,6 +57,6 @@ export function SalesOrdersTable({ orders, loading, onSelectOrder, }) {
57
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 &&
58
58
  orders.map((o) => {
59
59
  const ordered = formatDateShort(o.orderedAt);
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));
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" })), o.isReservation && (_jsx(Badge, { variant: "outline", className: "border-amber-400/40 text-amber-600", children: "Reservering" }))] }), _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, { disabled: !o.isLocal, onClick: () => o.isLocal && onEditOrder?.(o), title: o.isLocal ? undefined : "FO-orders kunnen niet bewerkt worden.", children: "Bewerken" }), _jsx(DropdownMenuItem, { onClick: () => onSelectOrder?.(o), children: "Details bekijken" }), _jsx(DropdownMenuItem, { onClick: () => onConfirmOrder?.(o), children: "Bevestigen" }), o.isLocal ? (_jsx(DropdownMenuItem, { className: "text-destructive focus:text-destructive", onClick: () => onDeleteOrder?.(o), children: "Verwijderen" })) : (_jsx(DropdownMenuItem, { disabled: true, title: "FO-orders kunnen niet verwijderd worden.", children: "Verwijderen (FO)" }))] })] }) })] }, o.id));
61
61
  })] })] }) }));
62
62
  }
@@ -1 +1 @@
1
- {"version":3,"file":"VerkoopOrdersPage.d.ts","sourceRoot":"","sources":["../../../src/components/verkoop/VerkoopOrdersPage.tsx"],"names":[],"mappings":"AAgEA,wBAAgB,iBAAiB,4CA2UhC"}
1
+ {"version":3,"file":"VerkoopOrdersPage.d.ts","sourceRoot":"","sources":["../../../src/components/verkoop/VerkoopOrdersPage.tsx"],"names":[],"mappings":"AAkEA,wBAAgB,iBAAiB,4CAyahC"}