@flowselections/floriday-verkoop-module 1.0.41 → 1.0.43
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 +141 -27
- package/dist-lib/integrations/supabase/auth-middleware.d.ts +1428 -172
- package/dist-lib/integrations/supabase/auth-middleware.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/client.d.ts +1428 -172
- package/dist-lib/integrations/supabase/client.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/client.server.d.ts +1428 -172
- package/dist-lib/integrations/supabase/client.server.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/types.d.ts +1453 -170
- 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 +1428 -172
- package/dist-lib/lib/cart-lock.functions.d.ts.map +1 -1
- package/dist-lib/lib/cart-planning-settings.functions.d.ts +15224 -1408
- package/dist-lib/lib/cart-planning-settings.functions.d.ts.map +1 -1
- package/dist-lib/lib/cart-replan.functions.d.ts +4152 -384
- package/dist-lib/lib/cart-replan.functions.d.ts.map +1 -1
- package/dist-lib/lib/create-order.functions.d.ts +65255 -1978
- package/dist-lib/lib/create-order.functions.d.ts.map +1 -1
- package/dist-lib/lib/create-order.functions.js +48 -1
- package/dist-lib/lib/floriday-auto-fulfillment.functions.d.ts +4152 -384
- package/dist-lib/lib/floriday-auto-fulfillment.functions.d.ts.map +1 -1
- package/dist-lib/lib/floriday-logistic-labels.functions.d.ts +1428 -172
- package/dist-lib/lib/floriday-logistic-labels.functions.d.ts.map +1 -1
- package/dist-lib/lib/floriday-mirror.functions.d.ts +1428 -172
- 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 +9688 -896
- package/dist-lib/lib/floriday-public.functions.d.ts.map +1 -1
- package/dist-lib/lib/floriday-writes.functions.d.ts +6920 -640
- package/dist-lib/lib/floriday-writes.functions.d.ts.map +1 -1
- package/dist-lib/lib/invoice-timing.functions.d.ts +5536 -512
- 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 +1428 -172
- package/dist-lib/lib/load-carriers.functions.d.ts.map +1 -1
- package/dist-lib/lib/logistics-engine.functions.d.ts +6920 -640
- package/dist-lib/lib/logistics-engine.functions.d.ts.map +1 -1
- package/dist-lib/lib/logistics-stock.functions.d.ts +2768 -256
- package/dist-lib/lib/logistics-stock.functions.d.ts.map +1 -1
- package/dist-lib/lib/order-addresses.functions.d.ts +1428 -172
- package/dist-lib/lib/order-addresses.functions.d.ts.map +1 -1
- package/dist-lib/lib/order-buffers.functions.d.ts +4152 -384
- 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 +2768 -256
- package/dist-lib/lib/order-carrier.functions.d.ts.map +1 -1
- package/dist-lib/lib/order-pickup.functions.d.ts +2768 -256
- package/dist-lib/lib/order-pickup.functions.d.ts.map +1 -1
- package/dist-lib/lib/order-shipments.functions.d.ts +5536 -512
- package/dist-lib/lib/order-shipments.functions.d.ts.map +1 -1
- package/dist-lib/lib/salesorders-list.functions.d.ts +1428 -172
- 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-mapping.d.ts +6 -0
- package/dist-lib/lib/salesorders-mapping.d.ts.map +1 -1
- package/dist-lib/lib/salesorders-mapping.js +17 -0
- package/dist-lib/lib/salesorders.functions.d.ts +6920 -640
- package/dist-lib/lib/salesorders.functions.d.ts.map +1 -1
- package/dist-lib/lib/tradeitems.functions.d.ts +1428 -172
- 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":"
|
|
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,15 +7,15 @@ 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 { deleteLocalOrder } from "../../lib/create-order.functions";
|
|
10
|
+
import { cancelLocalOrder, deleteLocalOrder } from "../../lib/create-order.functions";
|
|
11
11
|
import { runTradeItemsSync } from "../../lib/tradeitems.functions";
|
|
12
|
-
import { ordersToCsv, } from "../../lib/salesorders-mapping";
|
|
12
|
+
import { ordersToCsv, parseOrderRowId, } from "../../lib/salesorders-mapping";
|
|
13
13
|
import { listSalesOrders } from "../../lib/salesorders-list.functions";
|
|
14
14
|
import { derivePeriodWindow, useSalesOrdersQuery } from "../../lib/useSalesOrdersQuery";
|
|
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,28 +50,109 @@ export function VerkoopOrdersPage() {
|
|
|
50
50
|
const triggerSync = useServerFn(runSalesOrdersSync);
|
|
51
51
|
const triggerTradeItemsSync = useServerFn(runTradeItemsSync);
|
|
52
52
|
const triggerDeleteOrder = useServerFn(deleteLocalOrder);
|
|
53
|
-
const
|
|
53
|
+
const triggerCancelOrder = useServerFn(cancelLocalOrder);
|
|
54
|
+
const [pendingAction, setPendingAction] = useState(null);
|
|
55
|
+
const [cancelReason, setCancelReason] = useState("");
|
|
54
56
|
const [deleting, setDeleting] = useState(false);
|
|
57
|
+
const [deleteProgress, setDeleteProgress] = useState(0);
|
|
58
|
+
const [selectedRowIds, setSelectedRowIds] = useState([]);
|
|
55
59
|
const [editOrderId, setEditOrderId] = useState(null);
|
|
56
|
-
|
|
57
|
-
|
|
60
|
+
// Rijen die daadwerkelijk geraakt worden: bij een samengevouwen buffer alle
|
|
61
|
+
// onderliggende orders, anders alleen de rij zelf.
|
|
62
|
+
const deleteTargets = (row) => {
|
|
63
|
+
const ids = (row.bufferSize ?? 1) > 1 && row.bufferOrderIds?.length
|
|
64
|
+
? row.bufferOrderIds
|
|
65
|
+
: [row.id];
|
|
66
|
+
return Array.from(new Set(ids));
|
|
67
|
+
};
|
|
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) => {
|
|
90
|
+
const msg = typeof e?.message === "string" ? e.message.trim() : "";
|
|
91
|
+
if (!msg || msg.startsWith("[") || msg.startsWith("{"))
|
|
92
|
+
return mode === "cancel" ? "Annuleren mislukt" : "Verwijderen mislukt";
|
|
93
|
+
return msg;
|
|
94
|
+
};
|
|
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);
|
|
58
105
|
return;
|
|
59
|
-
const rawId = orderPendingDelete.id.startsWith("local:")
|
|
60
|
-
? orderPendingDelete.id.slice("local:".length)
|
|
61
|
-
: orderPendingDelete.id;
|
|
62
|
-
try {
|
|
63
|
-
setDeleting(true);
|
|
64
|
-
const res = await triggerDeleteOrder({ data: { orderId: rawId } });
|
|
65
|
-
toast.success(`Bestelling verwijderd — ${res.reversalEntries} voorraadmutatie(s) teruggedraaid`);
|
|
66
|
-
setOrderPendingDelete(null);
|
|
67
|
-
await refetch();
|
|
68
106
|
}
|
|
69
|
-
|
|
70
|
-
|
|
107
|
+
setDeleting(true);
|
|
108
|
+
setDeleteProgress(0);
|
|
109
|
+
let done = 0;
|
|
110
|
+
let reversals = 0;
|
|
111
|
+
const failed = [];
|
|
112
|
+
for (const target of deletable) {
|
|
113
|
+
try {
|
|
114
|
+
if (mode === "cancel") {
|
|
115
|
+
await triggerCancelOrder({
|
|
116
|
+
data: { orderId: target.id, reason: cancelReason.trim() || undefined },
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
const res = await triggerDeleteOrder({ data: { orderId: target.id } });
|
|
121
|
+
reversals += res.reversalEntries;
|
|
122
|
+
}
|
|
123
|
+
done += 1;
|
|
124
|
+
}
|
|
125
|
+
catch (e) {
|
|
126
|
+
failed.push(readableError(e, mode));
|
|
127
|
+
}
|
|
128
|
+
setDeleteProgress((p) => p + 1);
|
|
71
129
|
}
|
|
72
|
-
|
|
73
|
-
|
|
130
|
+
setDeleting(false);
|
|
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(", ")}`);
|
|
146
|
+
}
|
|
147
|
+
if (failed.length > 0) {
|
|
148
|
+
toast.error(Array.from(new Set(failed)).join(" · "));
|
|
149
|
+
}
|
|
150
|
+
if (done > 0) {
|
|
151
|
+
setPendingAction(null);
|
|
152
|
+
setCancelReason("");
|
|
153
|
+
setSelectedRowIds([]);
|
|
74
154
|
}
|
|
155
|
+
await refetch();
|
|
75
156
|
};
|
|
76
157
|
// Serverside gefilterd, gesorteerd en gepagineerd: de browser krijgt alleen
|
|
77
158
|
// de zichtbare pagina plus totalen/tellingen.
|
|
@@ -81,6 +162,13 @@ export function VerkoopOrdersPage() {
|
|
|
81
162
|
pageSize,
|
|
82
163
|
});
|
|
83
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]);
|
|
84
172
|
const activeEnvironment = data?.environment ?? "staging";
|
|
85
173
|
const totals = data?.totals ?? {
|
|
86
174
|
revenue: 0,
|
|
@@ -182,15 +270,24 @@ export function VerkoopOrdersPage() {
|
|
|
182
270
|
(active
|
|
183
271
|
? "bg-primary text-primary-foreground"
|
|
184
272
|
: "bg-muted text-muted-foreground"), children: count })] }, t.id));
|
|
185
|
-
}) }) }), _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({
|
|
186
282
|
to: "/floriday-verkoop/verkooporders/$salesOrderId",
|
|
187
283
|
params: { salesOrderId: o.id },
|
|
188
284
|
}), onDeleteOrder: (o) => {
|
|
189
285
|
if (!o.isLocal) {
|
|
190
|
-
toast.error("FO-orders kunnen niet
|
|
286
|
+
toast.error("FO-orders kunnen hier niet geannuleerd worden. Annuleer deze in Floriday.");
|
|
191
287
|
return;
|
|
192
288
|
}
|
|
193
|
-
|
|
289
|
+
setCancelReason("");
|
|
290
|
+
setPendingAction({ rows: [o], mode: o.stage === "cancelled" ? "delete" : "cancel" });
|
|
194
291
|
}, onEditOrder: (o) => {
|
|
195
292
|
if (!o.isLocal) {
|
|
196
293
|
toast.error("FO-orders kunnen niet bewerkt worden.");
|
|
@@ -198,10 +295,27 @@ export function VerkoopOrdersPage() {
|
|
|
198
295
|
}
|
|
199
296
|
const rawId = o.id.startsWith("local:") ? o.id.slice("local:".length) : o.id;
|
|
200
297
|
setEditOrderId(rawId);
|
|
201
|
-
},
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
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
|
|
205
319
|
? "Geen resultaten"
|
|
206
320
|
: totalCount === 1
|
|
207
321
|
? "1 resultaat"
|