@flowselections/floriday-verkoop-module 1.0.42 → 1.0.44
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/SalesOrdersTable.d.ts +5 -2
- package/dist-lib/components/verkoop/SalesOrdersTable.d.ts.map +1 -1
- package/dist-lib/components/verkoop/SalesOrdersTable.js +27 -13
- package/dist-lib/components/verkoop/VerkoopOrdersPage.d.ts.map +1 -1
- package/dist-lib/components/verkoop/VerkoopOrdersPage.js +114 -35
- package/dist-lib/integrations/supabase/auth-middleware.d.ts +571 -12
- package/dist-lib/integrations/supabase/auth-middleware.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/client.d.ts +571 -12
- package/dist-lib/integrations/supabase/client.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/client.server.d.ts +571 -12
- package/dist-lib/integrations/supabase/client.server.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/types.d.ts +594 -13
- package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/types.js +3 -0
- package/dist-lib/lib/cart-lock.functions.d.ts +571 -12
- package/dist-lib/lib/cart-lock.functions.d.ts.map +1 -1
- package/dist-lib/lib/cart-planning-settings.functions.d.ts +6281 -132
- package/dist-lib/lib/cart-planning-settings.functions.d.ts.map +1 -1
- package/dist-lib/lib/cart-replan.functions.d.ts +1713 -36
- package/dist-lib/lib/cart-replan.functions.d.ts.map +1 -1
- package/dist-lib/lib/create-order.functions.d.ts +31355 -193
- package/dist-lib/lib/create-order.functions.d.ts.map +1 -1
- package/dist-lib/lib/create-order.functions.js +18 -1
- package/dist-lib/lib/floriday-auto-fulfillment.functions.d.ts +1713 -36
- package/dist-lib/lib/floriday-auto-fulfillment.functions.d.ts.map +1 -1
- package/dist-lib/lib/floriday-logistic-labels.functions.d.ts +571 -12
- package/dist-lib/lib/floriday-logistic-labels.functions.d.ts.map +1 -1
- package/dist-lib/lib/floriday-mirror.functions.d.ts +571 -12
- package/dist-lib/lib/floriday-mirror.functions.d.ts.map +1 -1
- package/dist-lib/lib/floriday-mirror.functions.js +6 -1
- package/dist-lib/lib/floriday-public.functions.d.ts +3997 -84
- package/dist-lib/lib/floriday-public.functions.d.ts.map +1 -1
- package/dist-lib/lib/floriday-writes.functions.d.ts +2855 -60
- package/dist-lib/lib/floriday-writes.functions.d.ts.map +1 -1
- package/dist-lib/lib/invoice-timing.functions.d.ts +2284 -48
- package/dist-lib/lib/invoice-timing.functions.d.ts.map +1 -1
- package/dist-lib/lib/invoice-timing.server.d.ts.map +1 -1
- package/dist-lib/lib/invoice-timing.server.js +1 -0
- package/dist-lib/lib/load-carriers.functions.d.ts +571 -12
- package/dist-lib/lib/load-carriers.functions.d.ts.map +1 -1
- package/dist-lib/lib/logistics-engine.functions.d.ts +2855 -60
- package/dist-lib/lib/logistics-engine.functions.d.ts.map +1 -1
- package/dist-lib/lib/logistics-stock.functions.d.ts +1142 -24
- package/dist-lib/lib/logistics-stock.functions.d.ts.map +1 -1
- package/dist-lib/lib/order-addresses.functions.d.ts +571 -12
- package/dist-lib/lib/order-addresses.functions.d.ts.map +1 -1
- package/dist-lib/lib/order-buffers.functions.d.ts +1713 -36
- package/dist-lib/lib/order-buffers.functions.d.ts.map +1 -1
- package/dist-lib/lib/order-cancel.server.d.ts +24 -0
- package/dist-lib/lib/order-cancel.server.d.ts.map +1 -0
- package/dist-lib/lib/order-cancel.server.js +130 -0
- package/dist-lib/lib/order-carrier.functions.d.ts +1142 -24
- package/dist-lib/lib/order-carrier.functions.d.ts.map +1 -1
- package/dist-lib/lib/order-pickup.functions.d.ts +1142 -24
- package/dist-lib/lib/order-pickup.functions.d.ts.map +1 -1
- package/dist-lib/lib/order-shipments.functions.d.ts +2284 -48
- package/dist-lib/lib/order-shipments.functions.d.ts.map +1 -1
- package/dist-lib/lib/salesorders-list.functions.d.ts +571 -12
- package/dist-lib/lib/salesorders-list.functions.d.ts.map +1 -1
- package/dist-lib/lib/salesorders-list.server.d.ts.map +1 -1
- package/dist-lib/lib/salesorders-list.server.js +4 -60
- package/dist-lib/lib/salesorders.functions.d.ts +2855 -60
- package/dist-lib/lib/salesorders.functions.d.ts.map +1 -1
- package/dist-lib/lib/tradeitems.functions.d.ts +571 -12
- package/dist-lib/lib/tradeitems.functions.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import type { SalesOrderDTO } from "../../lib/salesorders-mapping";
|
|
3
|
-
|
|
3
|
+
/** Stabiele rij-id: buffers zijn serverside al samengevouwen tot één rij. */
|
|
4
|
+
export declare function salesOrderRowId(row: SalesOrderDTO): string;
|
|
5
|
+
export declare function SalesOrdersTable({ orders, loading, onSelectOrder, onDeleteOrder, onEditOrder, selectedIds, onSelectionChange, }: {
|
|
4
6
|
orders: SalesOrderDTO[];
|
|
5
7
|
loading?: boolean;
|
|
6
8
|
onSelectOrder?: (order: SalesOrderDTO) => void;
|
|
7
9
|
onDeleteOrder?: (order: SalesOrderDTO) => void;
|
|
8
10
|
onEditOrder?: (order: SalesOrderDTO) => void;
|
|
9
|
-
|
|
11
|
+
selectedIds?: string[];
|
|
12
|
+
onSelectionChange?: (ids: string[]) => void;
|
|
10
13
|
}): React.JSX.Element;
|
|
11
14
|
//# sourceMappingURL=SalesOrdersTable.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SalesOrdersTable.d.ts","sourceRoot":"","sources":["../../../src/components/verkoop/SalesOrdersTable.tsx"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"SalesOrdersTable.d.ts","sourceRoot":"","sources":["../../../src/components/verkoop/SalesOrdersTable.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA4B,MAAM,OAAO,CAAC;AAuBjD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAmCnE,6EAA6E;AAC7E,wBAAgB,eAAe,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,CAG1D;AAED,wBAAgB,gBAAgB,CAAC,EAC/B,MAAM,EACN,OAAO,EACP,aAAa,EACb,aAAa,EACb,WAAW,EACX,WAAW,EACX,iBAAiB,GAClB,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,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,iBAAiB,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;CAC7C,qBAkPA"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useState } from "react";
|
|
2
|
+
import { useMemo, useState } from "react";
|
|
3
3
|
import { Badge, Checkbox, Skeleton, Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "@flowselections/core";
|
|
4
4
|
import { formatLoadUnits } from "../../lib/load-carrier-labels";
|
|
5
5
|
import { useAllowedLoadCarriers } from "../../lib/useAllowedLoadCarriers";
|
|
@@ -36,19 +36,33 @@ function StatusTag({ order }) {
|
|
|
36
36
|
const stage = order.stage ?? "incoming";
|
|
37
37
|
return (_jsx(Badge, { variant: "outline", className: `border-transparent ${orderStageBadgeClass(stage)}`, children: orderStageLabel(stage) }));
|
|
38
38
|
}
|
|
39
|
-
|
|
40
|
-
|
|
39
|
+
/** Stabiele rij-id: buffers zijn serverside al samengevouwen tot één rij. */
|
|
40
|
+
export function salesOrderRowId(row) {
|
|
41
|
+
const bufferSize = row.bufferSize ?? 1;
|
|
42
|
+
return row.bufferId && bufferSize > 1 ? `buf-${row.bufferId}` : row.id;
|
|
43
|
+
}
|
|
44
|
+
export function SalesOrdersTable({ orders, loading, onSelectOrder, onDeleteOrder, onEditOrder, selectedIds, onSelectionChange, }) {
|
|
45
|
+
const [internalSelected, setInternalSelected] = useState([]);
|
|
46
|
+
const selected = useMemo(() => new Set(selectedIds ?? internalSelected), [selectedIds, internalSelected]);
|
|
41
47
|
const allowedCarrierCodes = useAllowedLoadCarriers().allowed;
|
|
42
|
-
const
|
|
48
|
+
const rowIds = useMemo(() => orders.map(salesOrderRowId), [orders]);
|
|
49
|
+
const allChecked = rowIds.length > 0 && rowIds.every((id) => selected.has(id));
|
|
50
|
+
const someChecked = !allChecked && rowIds.some((id) => selected.has(id));
|
|
43
51
|
// Omzetkolom alleen tonen wanneer er daadwerkelijk bedragen bekend zijn.
|
|
44
52
|
const showRevenue = orders.some((o) => (o.revenue ?? 0) > 0);
|
|
45
53
|
const columnCount = showRevenue ? 10 : 9;
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
54
|
+
const applySelection = (ids) => {
|
|
55
|
+
if (onSelectionChange)
|
|
56
|
+
onSelectionChange(ids);
|
|
57
|
+
else
|
|
58
|
+
setInternalSelected(ids);
|
|
59
|
+
};
|
|
60
|
+
const toggle = (id) => {
|
|
61
|
+
const next = new Set(selected);
|
|
62
|
+
next.has(id) ? next.delete(id) : next.add(id);
|
|
63
|
+
applySelection(Array.from(next));
|
|
64
|
+
};
|
|
65
|
+
return (_jsx("div", { className: "rounded-lg border border-border bg-card", children: _jsx("div", { className: "max-h-[70vh] overflow-y-auto overflow-x-auto overscroll-contain [scrollbar-gutter:stable]", style: { WebkitOverflowScrolling: "touch" }, children: _jsxs(Table, { className: "min-w-[950px] [&_thead_th]:sticky [&_thead_th]:top-0 [&_thead_th]:z-10 [&_thead_th]:bg-card", children: [_jsx(TableHeader, { children: _jsxs(TableRow, { className: "hover:bg-transparent", children: [_jsx(TableHead, { className: "w-10", children: _jsx(Checkbox, { "aria-label": "Alle bestellingen selecteren", checked: allChecked ? true : someChecked ? "indeterminate" : false, onCheckedChange: (v) => applySelection(v === true ? rowIds : []) }) }), _jsx(TableHead, { children: "Levering" }), _jsx(TableHead, { children: "Klant" }), _jsx(TableHead, { children: "Product" }), _jsx(TableHead, { children: "Karren" }), _jsx(TableHead, { children: "Fusten" }), _jsx(TableHead, { children: "Vervoerder" }), _jsx(TableHead, { children: "Status" }), showRevenue && _jsx(TableHead, { children: "Omzet" }), _jsx(TableHead, { children: _jsxs("span", { className: "inline-flex items-center gap-1", children: ["Besteld ", _jsx(ArrowDown, { className: "h-3 w-3" })] }) }), _jsx(TableHead, { className: "w-10" })] }) }), _jsxs(TableBody, { children: [loading &&
|
|
52
66
|
Array.from({ length: 6 }).map((_, i) => (_jsx(TableRow, { children: Array.from({ length: columnCount + 1 }).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: columnCount + 1, className: "py-12 text-center text-muted-foreground", children: "Geen verkooporders gevonden." }) })), !loading &&
|
|
53
67
|
orders.map((row) => {
|
|
54
68
|
// Buffers zijn serverside al samengevouwen tot één rij.
|
|
@@ -70,8 +84,8 @@ export function SalesOrdersTable({ orders, loading, onSelectOrder, onDeleteOrder
|
|
|
70
84
|
? "Buffer-orders kunnen niet gebundeld bewerkt worden."
|
|
71
85
|
: row.isLocal
|
|
72
86
|
? undefined
|
|
73
|
-
: "FO-orders kunnen niet bewerkt worden.", children: "Bewerken" }), _jsx(DropdownMenuItem, { onClick: () => onSelectOrder?.(row), children: "Details bekijken" }),
|
|
74
|
-
? "Buffer-orders kunnen niet gebundeld
|
|
75
|
-
: "FO-orders
|
|
87
|
+
: "FO-orders kunnen niet bewerkt worden.", children: "Bewerken" }), _jsx(DropdownMenuItem, { onClick: () => onSelectOrder?.(row), children: "Details bekijken" }), !isBuffer && row.isLocal ? (_jsx(DropdownMenuItem, { className: "text-destructive focus:text-destructive", onClick: () => onDeleteOrder?.(row), children: row.stage === "cancelled" ? "Definitief verwijderen" : "Annuleren" })) : (_jsxs(DropdownMenuItem, { disabled: true, title: isBuffer
|
|
88
|
+
? "Buffer-orders kunnen niet gebundeld geannuleerd worden."
|
|
89
|
+
: "FO-orders annuleer je in Floriday.", children: ["Annuleren", isBuffer ? "" : " (FO)"] }))] })] }) })] }, rowId));
|
|
76
90
|
})] })] }) }) }));
|
|
77
91
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VerkoopOrdersPage.d.ts","sourceRoot":"","sources":["../../../src/components/verkoop/VerkoopOrdersPage.tsx"],"names":[],"mappings":"AA6DA,wBAAgB,iBAAiB,
|
|
1
|
+
{"version":3,"file":"VerkoopOrdersPage.d.ts","sourceRoot":"","sources":["../../../src/components/verkoop/VerkoopOrdersPage.tsx"],"names":[],"mappings":"AA6DA,wBAAgB,iBAAiB,gCA4mBhC"}
|
|
@@ -1,4 +1,4 @@
|
|
|
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 { useEffect, useState } from "react";
|
|
3
3
|
import { Button, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, toast, } from "@flowselections/core";
|
|
4
4
|
import { Download, Plus, AlertTriangle, RefreshCw, ChevronLeft, ChevronRight, Filter, } from "lucide-react";
|
|
@@ -7,7 +7,7 @@ import { useNavigate } from "@tanstack/react-router";
|
|
|
7
7
|
import { useQueryClient } from "@tanstack/react-query";
|
|
8
8
|
import { supabase } from "../../integrations/supabase/client";
|
|
9
9
|
import { runSalesOrdersSync } from "../../lib/salesorders.functions";
|
|
10
|
-
import {
|
|
10
|
+
import { cancelLocalOrder, deleteLocalOrder } from "../../lib/create-order.functions";
|
|
11
11
|
import { runTradeItemsSync } from "../../lib/tradeitems.functions";
|
|
12
12
|
import { ordersToCsv, parseOrderRowId, } from "../../lib/salesorders-mapping";
|
|
13
13
|
import { listSalesOrders } from "../../lib/salesorders-list.functions";
|
|
@@ -15,7 +15,7 @@ import { derivePeriodWindow, useSalesOrdersQuery } from "../../lib/useSalesOrder
|
|
|
15
15
|
import { AnalyticsCards } from "./AnalyticsCards";
|
|
16
16
|
import { SalesOrdersFilters } from "./SalesOrdersFilters";
|
|
17
17
|
import { SalesOrdersFilterPanel, countActiveFilters, } from "./SalesOrdersFilterPanel";
|
|
18
|
-
import { SalesOrdersTable } from "./SalesOrdersTable";
|
|
18
|
+
import { SalesOrdersTable, salesOrderRowId } from "./SalesOrdersTable";
|
|
19
19
|
import { EditLocalOrderDialog } from "./EditLocalOrderDialog";
|
|
20
20
|
import { ORDER_STAGES } from "../../lib/order-status";
|
|
21
21
|
const STATUS_TABS = [
|
|
@@ -50,11 +50,14 @@ export function VerkoopOrdersPage() {
|
|
|
50
50
|
const triggerSync = useServerFn(runSalesOrdersSync);
|
|
51
51
|
const triggerTradeItemsSync = useServerFn(runTradeItemsSync);
|
|
52
52
|
const triggerDeleteOrder = useServerFn(deleteLocalOrder);
|
|
53
|
-
const
|
|
54
|
-
const [
|
|
53
|
+
const triggerCancelOrder = useServerFn(cancelLocalOrder);
|
|
54
|
+
const [pendingAction, setPendingAction] = useState(null);
|
|
55
|
+
const [cancelReason, setCancelReason] = useState("");
|
|
55
56
|
const [deleting, setDeleting] = useState(false);
|
|
57
|
+
const [deleteProgress, setDeleteProgress] = useState(0);
|
|
58
|
+
const [selectedRowIds, setSelectedRowIds] = useState([]);
|
|
56
59
|
const [editOrderId, setEditOrderId] = useState(null);
|
|
57
|
-
// Rijen die daadwerkelijk
|
|
60
|
+
// Rijen die daadwerkelijk geraakt worden: bij een samengevouwen buffer alle
|
|
58
61
|
// onderliggende orders, anders alleen de rij zelf.
|
|
59
62
|
const deleteTargets = (row) => {
|
|
60
63
|
const ids = (row.bufferSize ?? 1) > 1 && row.bufferOrderIds?.length
|
|
@@ -62,50 +65,93 @@ export function VerkoopOrdersPage() {
|
|
|
62
65
|
: [row.id];
|
|
63
66
|
return Array.from(new Set(ids));
|
|
64
67
|
};
|
|
65
|
-
|
|
68
|
+
// Alle order-ids achter een selectie, gesplitst in bruikbaar en overgeslagen
|
|
69
|
+
// (Floriday-orders kunnen hier niet geannuleerd of verwijderd worden).
|
|
70
|
+
const collectDeletePlan = (rows) => {
|
|
71
|
+
const deletable = [];
|
|
72
|
+
let skipped = 0;
|
|
73
|
+
const seen = new Set();
|
|
74
|
+
for (const row of rows) {
|
|
75
|
+
for (const target of deleteTargets(row)) {
|
|
76
|
+
if (seen.has(target))
|
|
77
|
+
continue;
|
|
78
|
+
seen.add(target);
|
|
79
|
+
const parsed = parseOrderRowId(target);
|
|
80
|
+
if (!parsed || parsed.kind !== "order") {
|
|
81
|
+
skipped += 1;
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
deletable.push({ id: parsed.uuid, kind: parsed.kind });
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return { deletable, skipped, total: seen.size };
|
|
88
|
+
};
|
|
89
|
+
const readableError = (e, mode) => {
|
|
66
90
|
const msg = typeof e?.message === "string" ? e.message.trim() : "";
|
|
67
91
|
if (!msg || msg.startsWith("[") || msg.startsWith("{"))
|
|
68
|
-
return "Verwijderen mislukt";
|
|
92
|
+
return mode === "cancel" ? "Annuleren mislukt" : "Verwijderen mislukt";
|
|
69
93
|
return msg;
|
|
70
94
|
};
|
|
71
|
-
const
|
|
72
|
-
if (!
|
|
95
|
+
const handleConfirmAction = async () => {
|
|
96
|
+
if (!pendingAction)
|
|
97
|
+
return;
|
|
98
|
+
const mode = pendingAction.mode;
|
|
99
|
+
const { deletable, skipped } = collectDeletePlan(pendingAction.rows);
|
|
100
|
+
if (deletable.length === 0) {
|
|
101
|
+
toast.error(mode === "cancel"
|
|
102
|
+
? "Geen van de geselecteerde bestellingen kan geannuleerd worden."
|
|
103
|
+
: "Geen van de geselecteerde bestellingen kan verwijderd worden.");
|
|
104
|
+
setPendingAction(null);
|
|
73
105
|
return;
|
|
74
|
-
|
|
106
|
+
}
|
|
75
107
|
setDeleting(true);
|
|
76
|
-
|
|
108
|
+
setDeleteProgress(0);
|
|
109
|
+
let done = 0;
|
|
77
110
|
let reversals = 0;
|
|
78
111
|
const failed = [];
|
|
79
|
-
for (const target of
|
|
80
|
-
const parsed = parseOrderRowId(target);
|
|
112
|
+
for (const target of deletable) {
|
|
81
113
|
try {
|
|
82
|
-
if (
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
await triggerDeleteProposal({ data: { proposalId: parsed.uuid } });
|
|
114
|
+
if (mode === "cancel") {
|
|
115
|
+
await triggerCancelOrder({
|
|
116
|
+
data: { orderId: target.id, reason: cancelReason.trim() || undefined },
|
|
117
|
+
});
|
|
87
118
|
}
|
|
88
119
|
else {
|
|
89
|
-
const res = await triggerDeleteOrder({ data: { orderId:
|
|
120
|
+
const res = await triggerDeleteOrder({ data: { orderId: target.id } });
|
|
90
121
|
reversals += res.reversalEntries;
|
|
91
122
|
}
|
|
92
|
-
|
|
123
|
+
done += 1;
|
|
93
124
|
}
|
|
94
125
|
catch (e) {
|
|
95
|
-
failed.push(readableError(e));
|
|
126
|
+
failed.push(readableError(e, mode));
|
|
96
127
|
}
|
|
128
|
+
setDeleteProgress((p) => p + 1);
|
|
97
129
|
}
|
|
98
130
|
setDeleting(false);
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
131
|
+
setDeleteProgress(0);
|
|
132
|
+
if (done > 0) {
|
|
133
|
+
const extra = [];
|
|
134
|
+
if (mode === "cancel")
|
|
135
|
+
extra.push("voorraad teruggeboekt");
|
|
136
|
+
else
|
|
137
|
+
extra.push(`${reversals} voorraadmutatie(s) teruggedraaid`);
|
|
138
|
+
if (skipped > 0)
|
|
139
|
+
extra.push(`${skipped} overgeslagen`);
|
|
140
|
+
if (failed.length > 0)
|
|
141
|
+
extra.push(`${failed.length} mislukt`);
|
|
142
|
+
const verb = mode === "cancel" ? "geannuleerd" : "verwijderd";
|
|
143
|
+
toast.success(deletable.length > 1
|
|
144
|
+
? `${done} van ${deletable.length} bestelling(en) ${verb} — ${extra.join(", ")}`
|
|
145
|
+
: `Bestelling ${verb} — ${extra.join(", ")}`);
|
|
103
146
|
}
|
|
104
147
|
if (failed.length > 0) {
|
|
105
148
|
toast.error(Array.from(new Set(failed)).join(" · "));
|
|
106
149
|
}
|
|
107
|
-
if (
|
|
108
|
-
|
|
150
|
+
if (done > 0) {
|
|
151
|
+
setPendingAction(null);
|
|
152
|
+
setCancelReason("");
|
|
153
|
+
setSelectedRowIds([]);
|
|
154
|
+
}
|
|
109
155
|
await refetch();
|
|
110
156
|
};
|
|
111
157
|
// Serverside gefilterd, gesorteerd en gepagineerd: de browser krijgt alleen
|
|
@@ -116,6 +162,13 @@ export function VerkoopOrdersPage() {
|
|
|
116
162
|
pageSize,
|
|
117
163
|
});
|
|
118
164
|
const paged = data?.orders ?? [];
|
|
165
|
+
// Selectie voor bulkacties: alleen rijen die nu zichtbaar zijn.
|
|
166
|
+
const selectedRows = paged.filter((o) => selectedRowIds.includes(salesOrderRowId(o)));
|
|
167
|
+
const selectedPlan = collectDeletePlan(selectedRows);
|
|
168
|
+
// Selectie leegmaken bij wisselen van tabblad, pagina of filters.
|
|
169
|
+
useEffect(() => {
|
|
170
|
+
setSelectedRowIds([]);
|
|
171
|
+
}, [tab, page, filters]);
|
|
119
172
|
const activeEnvironment = data?.environment ?? "staging";
|
|
120
173
|
const totals = data?.totals ?? {
|
|
121
174
|
revenue: 0,
|
|
@@ -217,15 +270,24 @@ export function VerkoopOrdersPage() {
|
|
|
217
270
|
(active
|
|
218
271
|
? "bg-primary text-primary-foreground"
|
|
219
272
|
: "bg-muted text-muted-foreground"), children: count })] }, t.id));
|
|
220
|
-
}) }) }), _jsxs("div", { className: "flex flex-wrap items-center justify-between gap-3", children: [_jsx(SalesOrdersFilters, { value: filters, onChange: setFilters }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsxs(Button, { variant: "outline", size: "sm", onClick: () => setFilterPanelOpen(true), "aria-label": "Filters", children: [_jsx(Filter, { className: "mr-2 h-4 w-4" }), "Filters", activeFilterCount > 0 && (_jsx("span", { className: "ml-2 inline-flex h-5 min-w-5 items-center justify-center rounded-full bg-primary px-1.5 text-[11px] font-semibold text-primary-foreground", children: activeFilterCount }))] }), _jsx(Button, { variant: "outline", size: "icon", onClick: handleSync, disabled: syncing, "aria-label": "Synchroniseren", title: "Synchroniseren", children: _jsx(RefreshCw, { className: "h-4 w-4 " + (syncing ? "animate-spin" : "") }) }), _jsxs(Button, { variant: "outline", size: "sm", onClick: handleExport, children: [_jsx(Download, { className: "mr-2 h-4 w-4" }), " Exporteren"] }), _jsxs(Button, { size: "sm", onClick: () => navigate({ to: "/floriday-verkoop/verkooporders/nieuw" }), children: [_jsx(Plus, { className: "mr-2 h-4 w-4" }), " Bestelling aanmaken"] })] })] }), _jsx(AnalyticsCards, { totals: totals, loading: isLoading }), error && (_jsxs("div", { className: "flex items-start gap-3 rounded-lg border border-destructive/30 bg-destructive/5 p-4 text-sm", children: [_jsx(AlertTriangle, { className: "mt-0.5 h-5 w-5 shrink-0 text-destructive" }), _jsxs("div", { className: "flex-1", children: [_jsx("div", { className: "font-medium text-destructive", children: "Floriday gegevens konden niet geladen worden" }), _jsx("div", { className: "mt-1 text-muted-foreground", children: error.message })] }), _jsx(Button, { size: "sm", variant: "outline", onClick: () => refetch(), disabled: isFetching, children: "Opnieuw proberen" })] })), (warnings.length > 0 || truncated) && (_jsxs("div", { className: "flex items-start gap-3 rounded-lg border border-amber-500/30 bg-amber-500/5 p-3 text-sm", children: [_jsx(AlertTriangle, { className: "mt-0.5 h-4 w-4 shrink-0 text-amber-600" }), _jsxs("div", { className: "space-y-1 text-muted-foreground", children: [warnings.map((w) => (_jsx("div", { children: w }, w))), truncated && (_jsx("div", { children: "Niet alle orders konden worden meegenomen. Kies een kortere periode voor een volledig overzicht." }))] })] })), _jsx(
|
|
273
|
+
}) }) }), _jsxs("div", { className: "flex flex-wrap items-center justify-between gap-3", children: [_jsx(SalesOrdersFilters, { value: filters, onChange: setFilters }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsxs(Button, { variant: "outline", size: "sm", onClick: () => setFilterPanelOpen(true), "aria-label": "Filters", children: [_jsx(Filter, { className: "mr-2 h-4 w-4" }), "Filters", activeFilterCount > 0 && (_jsx("span", { className: "ml-2 inline-flex h-5 min-w-5 items-center justify-center rounded-full bg-primary px-1.5 text-[11px] font-semibold text-primary-foreground", children: activeFilterCount }))] }), _jsx(Button, { variant: "outline", size: "icon", onClick: handleSync, disabled: syncing, "aria-label": "Synchroniseren", title: "Synchroniseren", children: _jsx(RefreshCw, { className: "h-4 w-4 " + (syncing ? "animate-spin" : "") }) }), _jsxs(Button, { variant: "outline", size: "sm", onClick: handleExport, children: [_jsx(Download, { className: "mr-2 h-4 w-4" }), " Exporteren"] }), _jsxs(Button, { size: "sm", onClick: () => navigate({ to: "/floriday-verkoop/verkooporders/nieuw" }), children: [_jsx(Plus, { className: "mr-2 h-4 w-4" }), " Bestelling aanmaken"] })] })] }), _jsx(AnalyticsCards, { totals: totals, loading: isLoading }), error && (_jsxs("div", { className: "flex items-start gap-3 rounded-lg border border-destructive/30 bg-destructive/5 p-4 text-sm", children: [_jsx(AlertTriangle, { className: "mt-0.5 h-5 w-5 shrink-0 text-destructive" }), _jsxs("div", { className: "flex-1", children: [_jsx("div", { className: "font-medium text-destructive", children: "Floriday gegevens konden niet geladen worden" }), _jsx("div", { className: "mt-1 text-muted-foreground", children: error.message })] }), _jsx(Button, { size: "sm", variant: "outline", onClick: () => refetch(), disabled: isFetching, children: "Opnieuw proberen" })] })), (warnings.length > 0 || truncated) && (_jsxs("div", { className: "flex items-start gap-3 rounded-lg border border-amber-500/30 bg-amber-500/5 p-3 text-sm", children: [_jsx(AlertTriangle, { className: "mt-0.5 h-4 w-4 shrink-0 text-amber-600" }), _jsxs("div", { className: "space-y-1 text-muted-foreground", children: [warnings.map((w) => (_jsx("div", { children: w }, w))), truncated && (_jsx("div", { children: "Niet alle orders konden worden meegenomen. Kies een kortere periode voor een volledig overzicht." }))] })] })), selectedRows.length > 0 && (_jsxs("div", { className: "flex flex-wrap items-center justify-between gap-3 rounded-lg border border-primary/30 bg-primary/5 p-3", children: [_jsxs("div", { className: "text-sm text-foreground", children: [_jsx("span", { className: "font-medium", children: selectedRows.length }), " geselecteerd", selectedPlan.total !== selectedRows.length && (_jsxs("span", { className: "text-muted-foreground", children: [" ", "\u00B7 ", selectedPlan.total, " onderliggende bestelling(en)"] })), selectedPlan.skipped > 0 && (_jsxs("span", { className: "text-muted-foreground", children: [" ", "\u00B7 ", selectedPlan.skipped, " Floriday-order(s) worden overgeslagen"] }))] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Button, { variant: "ghost", size: "sm", onClick: () => setSelectedRowIds([]), children: "Selectie wissen" }), _jsx(Button, { size: "sm", className: "bg-destructive text-destructive-foreground hover:bg-destructive/90", disabled: selectedPlan.deletable.length === 0, onClick: () => {
|
|
274
|
+
setCancelReason("");
|
|
275
|
+
setPendingAction({
|
|
276
|
+
rows: selectedRows,
|
|
277
|
+
mode: selectedRows.every((r) => r.stage === "cancelled") ? "delete" : "cancel",
|
|
278
|
+
});
|
|
279
|
+
}, children: selectedRows.every((r) => r.stage === "cancelled")
|
|
280
|
+
? "Selectie definitief verwijderen"
|
|
281
|
+
: "Selectie annuleren" })] })] })), _jsx(SalesOrdersTable, { orders: paged, loading: isLoading, selectedIds: selectedRowIds, onSelectionChange: setSelectedRowIds, onSelectOrder: (o) => navigate({
|
|
221
282
|
to: "/floriday-verkoop/verkooporders/$salesOrderId",
|
|
222
283
|
params: { salesOrderId: o.id },
|
|
223
284
|
}), onDeleteOrder: (o) => {
|
|
224
285
|
if (!o.isLocal) {
|
|
225
|
-
toast.error("FO-orders kunnen niet
|
|
286
|
+
toast.error("FO-orders kunnen hier niet geannuleerd worden. Annuleer deze in Floriday.");
|
|
226
287
|
return;
|
|
227
288
|
}
|
|
228
|
-
|
|
289
|
+
setCancelReason("");
|
|
290
|
+
setPendingAction({ rows: [o], mode: o.stage === "cancelled" ? "delete" : "cancel" });
|
|
229
291
|
}, onEditOrder: (o) => {
|
|
230
292
|
if (!o.isLocal) {
|
|
231
293
|
toast.error("FO-orders kunnen niet bewerkt worden.");
|
|
@@ -233,10 +295,27 @@ export function VerkoopOrdersPage() {
|
|
|
233
295
|
}
|
|
234
296
|
const rawId = o.id.startsWith("local:") ? o.id.slice("local:".length) : o.id;
|
|
235
297
|
setEditOrderId(rawId);
|
|
236
|
-
},
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
298
|
+
} }), pendingAction && (_jsx("div", { className: "fixed inset-0 z-50 flex items-center justify-center bg-black/50 p-4", onClick: () => !deleting && setPendingAction(null), children: _jsxs("div", { className: "w-full max-w-md rounded-lg border border-border bg-card p-6 shadow-lg", onClick: (e) => e.stopPropagation(), children: [(() => {
|
|
299
|
+
const rows = pendingAction.rows;
|
|
300
|
+
const isCancel = pendingAction.mode === "cancel";
|
|
301
|
+
const plan = collectDeletePlan(rows);
|
|
302
|
+
const single = rows[0];
|
|
303
|
+
const isBulk = rows.length > 1;
|
|
304
|
+
const verb = isCancel ? "annuleren" : "definitief verwijderen";
|
|
305
|
+
return (_jsxs(_Fragment, { children: [_jsx("h2", { className: "text-lg font-semibold text-foreground", children: isBulk
|
|
306
|
+
? `${rows.length} regels ${verb}?`
|
|
307
|
+
: plan.total > 1
|
|
308
|
+
? `${plan.total} bestellingen ${verb}?`
|
|
309
|
+
: `Bestelling ${verb}?` }), _jsxs("p", { className: "mt-2 text-sm text-muted-foreground", children: [isBulk ? (_jsxs(_Fragment, { children: ["Weet je zeker dat je", " ", _jsxs("span", { className: "font-medium text-foreground", children: [rows.length, " geselecteerde regels"] }), " ", "wilt ", verb, "? Dit gaat om ", plan.total, " bestelling(en) in totaal."] })) : (_jsxs(_Fragment, { children: ["Weet je zeker dat je ", plan.total > 1 ? "deze buffer met " : "", _jsx("span", { className: "font-medium text-foreground", children: plan.total > 1
|
|
310
|
+
? `${plan.total} bestellingen`
|
|
311
|
+
: single?.orderNumber || single?.id }), " ", "wilt ", verb, "?"] })), isCancel
|
|
312
|
+
? " De bestelling blijft bewaard onder Geannuleerd, gaat uit de planning en facturatie, en de voorraad wordt teruggeboekt."
|
|
313
|
+
: " De bestelling wordt definitief gewist; resterende voorraadmutaties worden teruggedraaid."] }), isCancel && (_jsxs("label", { className: "mt-4 block text-sm text-foreground", children: ["Reden (optioneel)", _jsx("textarea", { value: cancelReason, onChange: (e) => setCancelReason(e.target.value), rows: 2, maxLength: 500, placeholder: "Bijv. klant heeft geannuleerd", className: "mt-1 w-full rounded-md border border-input bg-background p-2 text-sm text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring" })] })), plan.skipped > 0 && (_jsxs("p", { className: "mt-2 text-sm text-amber-600", children: [plan.skipped, " van de ", plan.total, " zijn Floriday-orders en worden overgeslagen."] })), plan.deletable.length === 0 && (_jsxs("p", { className: "mt-2 text-sm text-destructive", children: ["Geen van deze regels kan hier ", verb, "."] }))] }));
|
|
314
|
+
})(), _jsxs("div", { className: "mt-5 flex justify-end gap-2", children: [_jsx(Button, { variant: "outline", size: "sm", onClick: () => setPendingAction(null), disabled: deleting, children: "Sluiten" }), _jsx(Button, { size: "sm", onClick: handleConfirmAction, disabled: deleting || collectDeletePlan(pendingAction.rows).deletable.length === 0, className: "bg-destructive text-destructive-foreground hover:bg-destructive/90", children: deleting
|
|
315
|
+
? `Bezig… ${deleteProgress}/${collectDeletePlan(pendingAction.rows).deletable.length}`
|
|
316
|
+
: pendingAction.mode === "cancel"
|
|
317
|
+
? "Order annuleren"
|
|
318
|
+
: "Definitief verwijderen" })] })] }) })), editOrderId && (_jsx(EditLocalOrderDialog, { orderId: editOrderId, open: !!editOrderId, onOpenChange: (o) => !o && setEditOrderId(null) })), _jsxs("div", { className: "flex flex-wrap items-center justify-between gap-3 text-sm text-muted-foreground", children: [_jsx("div", { children: totalCount === 0
|
|
240
319
|
? "Geen resultaten"
|
|
241
320
|
: totalCount === 1
|
|
242
321
|
? "1 resultaat"
|