@flowselections/floriday-verkoop-module 1.0.11 → 1.0.13
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/AutoFulfillmentStatusCard.d.ts +4 -0
- package/dist-lib/components/verkoop/AutoFulfillmentStatusCard.d.ts.map +1 -0
- package/dist-lib/components/verkoop/AutoFulfillmentStatusCard.js +95 -0
- package/dist-lib/components/verkoop/CartAssignmentWizard.d.ts +6 -0
- package/dist-lib/components/verkoop/CartAssignmentWizard.d.ts.map +1 -0
- package/dist-lib/components/verkoop/CartAssignmentWizard.js +194 -0
- package/dist-lib/components/verkoop/CreateOrderPage.d.ts.map +1 -1
- package/dist-lib/components/verkoop/CreateOrderPage.js +230 -192
- package/dist-lib/components/verkoop/OrderAfleverbrievenCard.d.ts +4 -0
- package/dist-lib/components/verkoop/OrderAfleverbrievenCard.d.ts.map +1 -0
- package/dist-lib/components/verkoop/OrderAfleverbrievenCard.js +44 -0
- package/dist-lib/components/verkoop/OrderKarindelingCard.d.ts +5 -0
- package/dist-lib/components/verkoop/OrderKarindelingCard.d.ts.map +1 -0
- package/dist-lib/components/verkoop/OrderKarindelingCard.js +160 -0
- package/dist-lib/components/verkoop/SalesOrderDetailView.d.ts.map +1 -1
- package/dist-lib/components/verkoop/SalesOrderDetailView.js +6 -3
- 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/components/verkoop/VerkoopOrdersPage.d.ts.map +1 -1
- package/dist-lib/components/verkoop/VerkoopOrdersPage.js +39 -3
- package/dist-lib/integrations/supabase/auth-middleware.d.ts +1381 -86
- package/dist-lib/integrations/supabase/auth-middleware.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/client.d.ts +1381 -86
- package/dist-lib/integrations/supabase/client.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/client.server.d.ts +1381 -86
- package/dist-lib/integrations/supabase/client.server.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/types.d.ts +1402 -79
- package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
- package/dist-lib/lib/cart-lock.functions.d.ts +11225 -0
- package/dist-lib/lib/cart-lock.functions.d.ts.map +1 -0
- package/dist-lib/lib/cart-lock.functions.js +47 -0
- package/dist-lib/lib/cart-planner.d.ts +38 -0
- package/dist-lib/lib/cart-planner.d.ts.map +1 -0
- package/dist-lib/lib/cart-planner.js +217 -0
- package/dist-lib/lib/cart-replan.functions.d.ts +22467 -0
- package/dist-lib/lib/cart-replan.functions.d.ts.map +1 -0
- package/dist-lib/lib/cart-replan.functions.js +203 -0
- package/dist-lib/lib/create-order.functions.d.ts +100967 -0
- package/dist-lib/lib/create-order.functions.d.ts.map +1 -0
- package/dist-lib/lib/create-order.functions.js +343 -0
- package/dist-lib/lib/floriday-auto-fulfillment.functions.d.ts +33640 -0
- package/dist-lib/lib/floriday-auto-fulfillment.functions.d.ts.map +1 -0
- package/dist-lib/lib/floriday-auto-fulfillment.functions.js +65 -0
- package/dist-lib/lib/floriday-auto-fulfillment.server.d.ts +21 -0
- package/dist-lib/lib/floriday-auto-fulfillment.server.d.ts.map +1 -0
- package/dist-lib/lib/floriday-auto-fulfillment.server.js +352 -0
- package/dist-lib/lib/floriday-logistic-labels.functions.d.ts +11220 -0
- package/dist-lib/lib/floriday-logistic-labels.functions.d.ts.map +1 -0
- package/dist-lib/lib/floriday-logistic-labels.functions.js +171 -0
- package/dist-lib/lib/floriday-public.functions.d.ts +9067 -16
- package/dist-lib/lib/floriday-public.functions.d.ts.map +1 -1
- package/dist-lib/lib/floriday-public.functions.js +45 -47
- package/dist-lib/lib/floriday-writes.functions.d.ts +6475 -0
- package/dist-lib/lib/floriday-writes.functions.d.ts.map +1 -1
- package/dist-lib/lib/salesorders-mapping.d.ts +2 -0
- package/dist-lib/lib/salesorders-mapping.d.ts.map +1 -1
- package/dist-lib/lib/salesorders-mapping.js +46 -20
- package/dist-lib/lib/salesorders.functions.d.ts +4273 -388
- package/dist-lib/lib/salesorders.functions.d.ts.map +1 -1
- package/dist-lib/lib/salesorders.functions.js +8 -0
- package/dist-lib/lib/tradeitems.functions.d.ts +1381 -86
- package/dist-lib/lib/tradeitems.functions.d.ts.map +1 -1
- package/dist-lib/lib/useSalesOrdersQuery.d.ts +1 -0
- package/dist-lib/lib/useSalesOrdersQuery.d.ts.map +1 -1
- package/dist-lib/lib/useSalesOrdersQuery.js +130 -41
- package/package.json +1 -1
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
// Toont karindeling per order. De belading (planten×fusten×lagen) komt
|
|
3
|
+
// ALTIJD uit de producten in Voorraad (tabel `products`) — nooit uit de
|
|
4
|
+
// Floriday-packingConfiguration — zodat verkoop en voorraad synchroon zijn.
|
|
5
|
+
// Alleen wanneer de order geen `product_id`-koppeling heeft, valt de kaart
|
|
6
|
+
// terug op de Floriday packingConfiguration.
|
|
7
|
+
import { useEffect, useMemo, useState } from "react";
|
|
8
|
+
import { Badge, Card, CardContent, CardHeader, CardTitle, Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@flowselections/core";
|
|
9
|
+
import { CheckCircle2, Lock } from "lucide-react";
|
|
10
|
+
import { supabase } from "../../integrations/supabase/client";
|
|
11
|
+
import { planCarts } from "../../lib/cart-planner";
|
|
12
|
+
function deriveCartsFromFloriday(order) {
|
|
13
|
+
const o = order.raw.order ?? {};
|
|
14
|
+
const pack = o?.packingConfiguration ?? {};
|
|
15
|
+
const ppc = Math.max(1, Number(pack.piecesPerPackage ?? order.quantity.count ?? 1));
|
|
16
|
+
const cpl = Math.max(1, Number(pack.packagesPerLayer ?? 1));
|
|
17
|
+
const lpc = Math.max(1, Number(pack.layersPerLoadCarrier ?? 1));
|
|
18
|
+
const totalPieces = Math.max(0, Number(o.numberOfPieces ?? order.totalPieces ?? 0));
|
|
19
|
+
if (totalPieces <= 0)
|
|
20
|
+
return [];
|
|
21
|
+
const totalCrates = Math.ceil(totalPieces / ppc);
|
|
22
|
+
const cratesPerCart = cpl * lpc;
|
|
23
|
+
const fullCarts = Math.floor(totalCrates / cratesPerCart);
|
|
24
|
+
const remainderCrates = totalCrates - fullCarts * cratesPerCart;
|
|
25
|
+
const carts = [];
|
|
26
|
+
let n = 1;
|
|
27
|
+
for (let i = 0; i < fullCarts; i++) {
|
|
28
|
+
carts.push({
|
|
29
|
+
cart_number: n++,
|
|
30
|
+
is_complete: true,
|
|
31
|
+
occupancy_pct: 100,
|
|
32
|
+
total_layers_used: lpc,
|
|
33
|
+
items: [{
|
|
34
|
+
product_name: order.productName,
|
|
35
|
+
crates: cratesPerCart,
|
|
36
|
+
plants: cratesPerCart * ppc,
|
|
37
|
+
plants_per_crate: ppc,
|
|
38
|
+
crates_per_layer: cpl,
|
|
39
|
+
layers_per_cart: lpc,
|
|
40
|
+
layer_share_pct: 100,
|
|
41
|
+
}],
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
if (remainderCrates > 0) {
|
|
45
|
+
const layersUsed = Math.ceil(remainderCrates / cpl);
|
|
46
|
+
const occ = Math.round(((layersUsed / lpc) * 100) * 100) / 100;
|
|
47
|
+
carts.push({
|
|
48
|
+
cart_number: n,
|
|
49
|
+
is_complete: false,
|
|
50
|
+
occupancy_pct: occ,
|
|
51
|
+
total_layers_used: layersUsed,
|
|
52
|
+
items: [{
|
|
53
|
+
product_name: order.productName,
|
|
54
|
+
crates: remainderCrates,
|
|
55
|
+
plants: remainderCrates * ppc,
|
|
56
|
+
plants_per_crate: ppc,
|
|
57
|
+
crates_per_layer: cpl,
|
|
58
|
+
layers_per_cart: lpc,
|
|
59
|
+
layer_share_pct: occ,
|
|
60
|
+
}],
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
return carts;
|
|
64
|
+
}
|
|
65
|
+
function belading(i) {
|
|
66
|
+
return `${i.plants_per_crate}×${i.crates_per_layer}×${i.layers_per_cart}`;
|
|
67
|
+
}
|
|
68
|
+
export function OrderKarindelingCard({ order }) {
|
|
69
|
+
// Lokale/mailbox-orders hebben een prefix (`local:`, `mailbox-proposal:`) in
|
|
70
|
+
// hun DTO-id. De DB-tabellen (`orders`, `order_items`) gebruiken de kale UUID,
|
|
71
|
+
// dus die prefix moet eraf voor de query — anders faalt de UUID-cast en valt
|
|
72
|
+
// de kaart terug op de Floriday-modus met slechts één artikel.
|
|
73
|
+
const rawId = order?.id ?? order.raw?.order?.id ?? null;
|
|
74
|
+
const orderId = typeof rawId === "string" ? rawId.replace(/^(local:|mailbox-proposal:)/, "") : rawId;
|
|
75
|
+
const [lockedAt, setLockedAt] = useState(null);
|
|
76
|
+
const [dbItems, setDbItems] = useState(null);
|
|
77
|
+
// Laadt order_items + gekoppelde products (voorraad-belading) en
|
|
78
|
+
// ververst live bij mutaties op order_items, orders of products.
|
|
79
|
+
useEffect(() => {
|
|
80
|
+
if (!orderId)
|
|
81
|
+
return;
|
|
82
|
+
let active = true;
|
|
83
|
+
const load = async () => {
|
|
84
|
+
const [{ data: itemRows }, { data: orderRow }] = await Promise.all([
|
|
85
|
+
supabase
|
|
86
|
+
.from("order_items")
|
|
87
|
+
.select("id, product_id, product_name, quantity, products(plants_per_crate, crates_per_layer, layers_per_cart)")
|
|
88
|
+
.eq("order_id", orderId),
|
|
89
|
+
supabase.from("orders").select("plan_locked_at").eq("id", orderId).maybeSingle(),
|
|
90
|
+
]);
|
|
91
|
+
if (!active)
|
|
92
|
+
return;
|
|
93
|
+
setDbItems(itemRows ?? []);
|
|
94
|
+
setLockedAt(orderRow?.plan_locked_at ?? null);
|
|
95
|
+
};
|
|
96
|
+
void load();
|
|
97
|
+
const channel = supabase
|
|
98
|
+
.channel(`order-karindeling-${orderId}`)
|
|
99
|
+
.on("postgres_changes", { event: "UPDATE", schema: "public", table: "orders", filter: `id=eq.${orderId}` }, (payload) => setLockedAt(payload.new?.plan_locked_at ?? null))
|
|
100
|
+
.on("postgres_changes", { event: "*", schema: "public", table: "order_items", filter: `order_id=eq.${orderId}` }, () => void load())
|
|
101
|
+
.on("postgres_changes", { event: "UPDATE", schema: "public", table: "products" }, () => void load())
|
|
102
|
+
.subscribe();
|
|
103
|
+
return () => {
|
|
104
|
+
active = false;
|
|
105
|
+
void supabase.removeChannel(channel);
|
|
106
|
+
};
|
|
107
|
+
}, [orderId]);
|
|
108
|
+
// Bouw plannerinput uit voorraad-belading. Alleen items met een gekoppeld
|
|
109
|
+
// product (product_id + belading in products-tabel) worden meegenomen.
|
|
110
|
+
const plannerCarts = useMemo(() => {
|
|
111
|
+
if (!dbItems || dbItems.length === 0)
|
|
112
|
+
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
|
+
}
|
|
134
|
+
if (inputs.length === 0)
|
|
135
|
+
return null;
|
|
136
|
+
return planCarts(inputs).carts;
|
|
137
|
+
}, [dbItems]);
|
|
138
|
+
const floridayCarts = useMemo(() => deriveCartsFromFloriday(order), [order]);
|
|
139
|
+
const usingVoorraad = plannerCarts !== null;
|
|
140
|
+
const carts = usingVoorraad
|
|
141
|
+
? plannerCarts.map((c) => ({
|
|
142
|
+
cart_number: c.cart_number,
|
|
143
|
+
is_complete: c.is_complete,
|
|
144
|
+
occupancy_pct: c.occupancy_pct,
|
|
145
|
+
total_layers_used: c.total_layers_used,
|
|
146
|
+
items: c.items.map((i) => ({
|
|
147
|
+
product_name: i.product_name,
|
|
148
|
+
crates: i.crates,
|
|
149
|
+
plants: i.plants,
|
|
150
|
+
plants_per_crate: i.plants_per_crate,
|
|
151
|
+
crates_per_layer: i.crates_per_layer,
|
|
152
|
+
layers_per_cart: i.layers_per_cart,
|
|
153
|
+
layer_share_pct: i.layer_share_pct,
|
|
154
|
+
})),
|
|
155
|
+
}))
|
|
156
|
+
: floridayCarts;
|
|
157
|
+
const source = usingVoorraad ? "voorraad" : "floriday";
|
|
158
|
+
const lockedDate = lockedAt ? new Date(lockedAt).toLocaleString("nl-NL") : null;
|
|
159
|
+
return (_jsxs(Card, { children: [_jsxs(CardHeader, { className: "flex flex-row items-center justify-between", children: [_jsx(CardTitle, { className: "text-base", children: "Karindeling" }), _jsxs("div", { className: "flex items-center gap-2", children: [lockedAt && (_jsxs(Badge, { variant: "outline", className: "gap-1", children: [_jsx(Lock, { className: "h-3 w-3" }), "Vergrendeld \u2014 pakbon geprint ", lockedDate] })), _jsxs(Badge, { variant: "secondary", children: [carts.length, " kar(ren) \u00B7 bron: ", source === "voorraad" ? "Voorraad" : "Floriday"] })] })] }), _jsx(CardContent, { children: carts.length === 0 ? (_jsx("p", { className: "text-sm text-muted-foreground", children: "Nog geen karindeling beschikbaar voor deze order." })) : (_jsx("div", { className: "space-y-4", children: carts.map((c) => (_jsxs("div", { className: "rounded-md border border-border", children: [_jsxs("div", { className: "flex items-center justify-between 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: ["Kar ", c.cart_number] }), _jsxs(Badge, { variant: c.is_complete ? "default" : "secondary", className: "gap-1", children: [c.is_complete && _jsx(CheckCircle2, { className: "h-3 w-3" }), c.is_complete ? "Volledig" : "Deels"] })] }), _jsxs("div", { className: "text-muted-foreground", children: [c.total_layers_used, " lagen \u00B7 benutting ", c.occupancy_pct, "%"] })] }), _jsxs(Table, { children: [_jsx(TableHeader, { children: _jsxs(TableRow, { children: [_jsx(TableHead, { children: "Artikel" }), _jsx(TableHead, { className: "text-right", children: "Planten" }), _jsx(TableHead, { className: "text-right", children: "Fusten" }), _jsx(TableHead, { children: "Belading" }), _jsx(TableHead, { className: "text-right", children: "Laag-aandeel" })] }) }), _jsx(TableBody, { children: c.items.map((i, idx) => (_jsxs(TableRow, { children: [_jsx(TableCell, { children: i.product_name }), _jsx(TableCell, { className: "text-right", children: i.plants }), _jsx(TableCell, { className: "text-right", children: i.crates }), _jsx(TableCell, { className: "font-mono text-xs", children: belading(i) }), _jsxs(TableCell, { className: "text-right", children: [Math.round(i.layer_share_pct * 10) / 10, "%"] })] }, idx))) })] })] }, c.cart_number))) })) })] }));
|
|
160
|
+
}
|
|
@@ -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;
|
|
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,5 +1,8 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { CheckCircle2, ImageIcon } from "lucide-react";
|
|
3
|
+
import { OrderKarindelingCard } from "./OrderKarindelingCard";
|
|
4
|
+
import { OrderAfleverbrievenCard } from "./OrderAfleverbrievenCard";
|
|
5
|
+
import { AutoFulfillmentStatusCard } from "./AutoFulfillmentStatusCard";
|
|
3
6
|
const eurFlex = new Intl.NumberFormat("nl-NL", {
|
|
4
7
|
style: "currency",
|
|
5
8
|
currency: "EUR",
|
|
@@ -123,7 +126,7 @@ export function SalesOrderDetailView({ order, sellerFallback, }) {
|
|
|
123
126
|
if (vbnCode)
|
|
124
127
|
beladingParts.push(String(vbnCode));
|
|
125
128
|
if (layersPerLoadCarrier && packagesPerLayer && piecesPerPackage) {
|
|
126
|
-
beladingParts.push(`${
|
|
129
|
+
beladingParts.push(`${piecesPerPackage}×${packagesPerLayer}×${layersPerLoadCarrier}`);
|
|
127
130
|
}
|
|
128
131
|
if (lcShort)
|
|
129
132
|
beladingParts.push(lcShort);
|
|
@@ -209,5 +212,5 @@ export function SalesOrderDetailView({ order, sellerFallback, }) {
|
|
|
209
212
|
const bestellingLabel = order.status.isCancelled
|
|
210
213
|
? "Geannuleerd"
|
|
211
214
|
: STATUS_LABELS[statusKey] ?? (order.status.isShipped ? "Verzonden" : order.status.isConfirmed ? "Order bevestigd" : "Open");
|
|
212
|
-
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:
|
|
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] }));
|
|
213
216
|
}
|
|
@@ -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
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VerkoopOrdersPage.d.ts","sourceRoot":"","sources":["../../../src/components/verkoop/VerkoopOrdersPage.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"VerkoopOrdersPage.d.ts","sourceRoot":"","sources":["../../../src/components/verkoop/VerkoopOrdersPage.tsx"],"names":[],"mappings":"AAgEA,wBAAgB,iBAAiB,4CA2UhC"}
|
|
@@ -4,6 +4,8 @@ import { Button, Select, SelectContent, SelectItem, SelectTrigger, SelectValue,
|
|
|
4
4
|
import { Download, Plus, AlertTriangle, RefreshCw, ChevronLeft, ChevronRight, Filter, } from "lucide-react";
|
|
5
5
|
import { useServerFn } from "@tanstack/react-start";
|
|
6
6
|
import { useNavigate } from "@tanstack/react-router";
|
|
7
|
+
import { useQueryClient } from "@tanstack/react-query";
|
|
8
|
+
import { supabase } from "../../integrations/supabase/client";
|
|
7
9
|
import { runSalesOrdersSync } from "../../lib/salesorders.functions";
|
|
8
10
|
import { runTradeItemsSync } from "../../lib/tradeitems.functions";
|
|
9
11
|
import { applyFilters, computeTotals, ordersToCsv, } from "../../lib/salesorders-mapping";
|
|
@@ -12,7 +14,6 @@ import { AnalyticsCards } from "./AnalyticsCards";
|
|
|
12
14
|
import { SalesOrdersFilters } from "./SalesOrdersFilters";
|
|
13
15
|
import { SalesOrdersFilterPanel, countActiveFilters, } from "./SalesOrdersFilterPanel";
|
|
14
16
|
import { SalesOrdersTable } from "./SalesOrdersTable";
|
|
15
|
-
import { FlorydaySyncHealthBanner } from "../../components/floriday/FlorydaySyncHealthBanner";
|
|
16
17
|
const STATUS_TABS = [
|
|
17
18
|
{ id: "all", label: "Alle" },
|
|
18
19
|
{ id: "open", label: "Open" },
|
|
@@ -53,10 +54,45 @@ export function VerkoopOrdersPage() {
|
|
|
53
54
|
const [page, setPage] = useState(1);
|
|
54
55
|
const [pageSize, setPageSize] = useState(20);
|
|
55
56
|
const navigate = useNavigate();
|
|
57
|
+
const queryClient = useQueryClient();
|
|
56
58
|
const triggerSync = useServerFn(runSalesOrdersSync);
|
|
57
59
|
const triggerTradeItemsSync = useServerFn(runTradeItemsSync);
|
|
58
60
|
const { data, isLoading, error, refetch, isFetching } = useSalesOrdersQuery();
|
|
59
61
|
const orders = data?.orders ?? [];
|
|
62
|
+
const activeEnvironment = data?.environment ?? "staging";
|
|
63
|
+
// Automatisch omschakelen wanneer de Authenticatie-module de omgeving wijzigt.
|
|
64
|
+
useEffect(() => {
|
|
65
|
+
const channel = supabase
|
|
66
|
+
.channel("floriday-settings-env")
|
|
67
|
+
.on("postgres_changes", { event: "*", schema: "public", table: "floriday_settings" }, () => {
|
|
68
|
+
queryClient.invalidateQueries({ queryKey: ["floriday", "salesorders-cache"] });
|
|
69
|
+
})
|
|
70
|
+
.subscribe();
|
|
71
|
+
return () => {
|
|
72
|
+
supabase.removeChannel(channel);
|
|
73
|
+
};
|
|
74
|
+
}, [queryClient]);
|
|
75
|
+
// Realtime — lijst live bijwerken zodra cache-regels binnenkomen/wijzigen.
|
|
76
|
+
useEffect(() => {
|
|
77
|
+
let timer = null;
|
|
78
|
+
const invalidate = () => {
|
|
79
|
+
if (timer)
|
|
80
|
+
clearTimeout(timer);
|
|
81
|
+
timer = setTimeout(() => {
|
|
82
|
+
queryClient.invalidateQueries({ queryKey: ["floriday", "salesorders-cache"] });
|
|
83
|
+
}, 500);
|
|
84
|
+
};
|
|
85
|
+
const channel = supabase
|
|
86
|
+
.channel("floriday-salesorders-cache")
|
|
87
|
+
.on("postgres_changes", { event: "*", schema: "public", table: "floriday_salesorders_cache" }, invalidate)
|
|
88
|
+
.on("postgres_changes", { event: "*", schema: "public", table: "floriday_fulfillment_orders_cache" }, invalidate)
|
|
89
|
+
.subscribe();
|
|
90
|
+
return () => {
|
|
91
|
+
if (timer)
|
|
92
|
+
clearTimeout(timer);
|
|
93
|
+
supabase.removeChannel(channel);
|
|
94
|
+
};
|
|
95
|
+
}, [queryClient]);
|
|
60
96
|
const statusFiltered = useMemo(() => orders.filter((o) => matchesStatus(o, tab)), [orders, tab]);
|
|
61
97
|
const filtered = useMemo(() => applyFilters(statusFiltered, filters), [statusFiltered, filters]);
|
|
62
98
|
const totals = useMemo(() => computeTotals(filtered), [filtered]);
|
|
@@ -118,7 +154,7 @@ export function VerkoopOrdersPage() {
|
|
|
118
154
|
]);
|
|
119
155
|
const syncedOrders = orders.results.reduce((s, x) => s + x.synced, 0);
|
|
120
156
|
const syncedItems = items.results.reduce((s, x) => s + x.synced, 0);
|
|
121
|
-
toast.success(`Sync klaar — ${syncedOrders} order(s), ${syncedItems} artikel(en) bijgewerkt`);
|
|
157
|
+
toast.success(`Sync klaar (${activeEnvironment === "live" ? "productie" : "staging"}) — ${syncedOrders} order(s), ${syncedItems} artikel(en) bijgewerkt`);
|
|
122
158
|
await refetch();
|
|
123
159
|
}
|
|
124
160
|
catch (e) {
|
|
@@ -128,7 +164,7 @@ export function VerkoopOrdersPage() {
|
|
|
128
164
|
setSyncing(false);
|
|
129
165
|
}
|
|
130
166
|
};
|
|
131
|
-
return (_jsxs("main", { className: "space-y-5 p-6", children: [_jsx(
|
|
167
|
+
return (_jsxs("main", { className: "space-y-5 p-6", children: [_jsx("div", { className: "flex items-center justify-between border-b border-border", children: _jsx("div", { className: "flex items-center gap-6 overflow-x-auto", children: STATUS_TABS.map((t) => {
|
|
132
168
|
const active = tab === t.id;
|
|
133
169
|
const count = statusCounts[t.id];
|
|
134
170
|
return (_jsxs("button", { type: "button", onClick: () => setTab(t.id), className: "relative -mb-px flex items-center gap-2 whitespace-nowrap border-b-2 px-1 pb-3 pt-1 text-sm transition-colors " +
|