@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.
Files changed (66) hide show
  1. package/dist-lib/components/verkoop/SalesOrdersTable.d.ts +5 -2
  2. package/dist-lib/components/verkoop/SalesOrdersTable.d.ts.map +1 -1
  3. package/dist-lib/components/verkoop/SalesOrdersTable.js +27 -13
  4. package/dist-lib/components/verkoop/VerkoopOrdersPage.d.ts.map +1 -1
  5. package/dist-lib/components/verkoop/VerkoopOrdersPage.js +114 -35
  6. package/dist-lib/integrations/supabase/auth-middleware.d.ts +571 -12
  7. package/dist-lib/integrations/supabase/auth-middleware.d.ts.map +1 -1
  8. package/dist-lib/integrations/supabase/client.d.ts +571 -12
  9. package/dist-lib/integrations/supabase/client.d.ts.map +1 -1
  10. package/dist-lib/integrations/supabase/client.server.d.ts +571 -12
  11. package/dist-lib/integrations/supabase/client.server.d.ts.map +1 -1
  12. package/dist-lib/integrations/supabase/types.d.ts +594 -13
  13. package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
  14. package/dist-lib/integrations/supabase/types.js +3 -0
  15. package/dist-lib/lib/cart-lock.functions.d.ts +571 -12
  16. package/dist-lib/lib/cart-lock.functions.d.ts.map +1 -1
  17. package/dist-lib/lib/cart-planning-settings.functions.d.ts +6281 -132
  18. package/dist-lib/lib/cart-planning-settings.functions.d.ts.map +1 -1
  19. package/dist-lib/lib/cart-replan.functions.d.ts +1713 -36
  20. package/dist-lib/lib/cart-replan.functions.d.ts.map +1 -1
  21. package/dist-lib/lib/create-order.functions.d.ts +31355 -193
  22. package/dist-lib/lib/create-order.functions.d.ts.map +1 -1
  23. package/dist-lib/lib/create-order.functions.js +18 -1
  24. package/dist-lib/lib/floriday-auto-fulfillment.functions.d.ts +1713 -36
  25. package/dist-lib/lib/floriday-auto-fulfillment.functions.d.ts.map +1 -1
  26. package/dist-lib/lib/floriday-logistic-labels.functions.d.ts +571 -12
  27. package/dist-lib/lib/floriday-logistic-labels.functions.d.ts.map +1 -1
  28. package/dist-lib/lib/floriday-mirror.functions.d.ts +571 -12
  29. package/dist-lib/lib/floriday-mirror.functions.d.ts.map +1 -1
  30. package/dist-lib/lib/floriday-mirror.functions.js +6 -1
  31. package/dist-lib/lib/floriday-public.functions.d.ts +3997 -84
  32. package/dist-lib/lib/floriday-public.functions.d.ts.map +1 -1
  33. package/dist-lib/lib/floriday-writes.functions.d.ts +2855 -60
  34. package/dist-lib/lib/floriday-writes.functions.d.ts.map +1 -1
  35. package/dist-lib/lib/invoice-timing.functions.d.ts +2284 -48
  36. package/dist-lib/lib/invoice-timing.functions.d.ts.map +1 -1
  37. package/dist-lib/lib/invoice-timing.server.d.ts.map +1 -1
  38. package/dist-lib/lib/invoice-timing.server.js +1 -0
  39. package/dist-lib/lib/load-carriers.functions.d.ts +571 -12
  40. package/dist-lib/lib/load-carriers.functions.d.ts.map +1 -1
  41. package/dist-lib/lib/logistics-engine.functions.d.ts +2855 -60
  42. package/dist-lib/lib/logistics-engine.functions.d.ts.map +1 -1
  43. package/dist-lib/lib/logistics-stock.functions.d.ts +1142 -24
  44. package/dist-lib/lib/logistics-stock.functions.d.ts.map +1 -1
  45. package/dist-lib/lib/order-addresses.functions.d.ts +571 -12
  46. package/dist-lib/lib/order-addresses.functions.d.ts.map +1 -1
  47. package/dist-lib/lib/order-buffers.functions.d.ts +1713 -36
  48. package/dist-lib/lib/order-buffers.functions.d.ts.map +1 -1
  49. package/dist-lib/lib/order-cancel.server.d.ts +24 -0
  50. package/dist-lib/lib/order-cancel.server.d.ts.map +1 -0
  51. package/dist-lib/lib/order-cancel.server.js +130 -0
  52. package/dist-lib/lib/order-carrier.functions.d.ts +1142 -24
  53. package/dist-lib/lib/order-carrier.functions.d.ts.map +1 -1
  54. package/dist-lib/lib/order-pickup.functions.d.ts +1142 -24
  55. package/dist-lib/lib/order-pickup.functions.d.ts.map +1 -1
  56. package/dist-lib/lib/order-shipments.functions.d.ts +2284 -48
  57. package/dist-lib/lib/order-shipments.functions.d.ts.map +1 -1
  58. package/dist-lib/lib/salesorders-list.functions.d.ts +571 -12
  59. package/dist-lib/lib/salesorders-list.functions.d.ts.map +1 -1
  60. package/dist-lib/lib/salesorders-list.server.d.ts.map +1 -1
  61. package/dist-lib/lib/salesorders-list.server.js +4 -60
  62. package/dist-lib/lib/salesorders.functions.d.ts +2855 -60
  63. package/dist-lib/lib/salesorders.functions.d.ts.map +1 -1
  64. package/dist-lib/lib/tradeitems.functions.d.ts +571 -12
  65. package/dist-lib/lib/tradeitems.functions.d.ts.map +1 -1
  66. 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
- export declare function SalesOrdersTable({ orders, loading, onSelectOrder, onDeleteOrder, onEditOrder, onConfirmOrder, }: {
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
- onConfirmOrder?: (order: SalesOrderDTO) => void;
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,KAAmB,MAAM,OAAO,CAAC;AAuBxC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAmCnE,wBAAgB,gBAAgB,CAAC,EAC/B,MAAM,EACN,OAAO,EACP,aAAa,EACb,aAAa,EACb,WAAW,EACX,cAAc,GACf,EAAE;IACD,MAAM,EAAE,aAAa,EAAE,CAAC;IACxB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;IAC/C,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;IAC/C,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;IAC7C,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;CACjD,qBAwOA"}
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
- export function SalesOrdersTable({ orders, loading, onSelectOrder, onDeleteOrder, onEditOrder, onConfirmOrder, }) {
40
- const [selected, setSelected] = useState(new Set());
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 allChecked = orders.length > 0 && selected.size === orders.length;
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 toggle = (id) => setSelected((s) => {
47
- const n = new Set(s);
48
- n.has(id) ? n.delete(id) : n.add(id);
49
- return n;
50
- });
51
- 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, { checked: allChecked, onCheckedChange: (v) => setSelected(v === true ? new Set(orders.map((o) => o.id)) : new Set()) }) }), _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 &&
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" }), _jsx(DropdownMenuItem, { onClick: () => onConfirmOrder?.(row), children: "Bevestigen" }), !isBuffer && row.isLocal ? (_jsx(DropdownMenuItem, { className: "text-destructive focus:text-destructive", onClick: () => onDeleteOrder?.(row), children: "Verwijderen" })) : (_jsxs(DropdownMenuItem, { disabled: true, title: isBuffer
74
- ? "Buffer-orders kunnen niet gebundeld verwijderd worden."
75
- : "FO-orders kunnen niet verwijderd worden.", children: ["Verwijderen", isBuffer ? "" : " (FO)"] }))] })] }) })] }, rowId));
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,gCAychC"}
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 { deleteLocalOrder, deleteMailboxProposal } from "../../lib/create-order.functions";
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 triggerDeleteProposal = useServerFn(deleteMailboxProposal);
54
- const [orderPendingDelete, setOrderPendingDelete] = useState(null);
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 verwijderd worden: bij een samengevouwen buffer alle
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
- const readableError = (e) => {
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 handleDeleteOrder = async () => {
72
- if (!orderPendingDelete)
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
- const targets = deleteTargets(orderPendingDelete);
106
+ }
75
107
  setDeleting(true);
76
- let deleted = 0;
108
+ setDeleteProgress(0);
109
+ let done = 0;
77
110
  let reversals = 0;
78
111
  const failed = [];
79
- for (const target of targets) {
80
- const parsed = parseOrderRowId(target);
112
+ for (const target of deletable) {
81
113
  try {
82
- if (!parsed || parsed.kind === "floriday") {
83
- throw new Error("Deze regel kan niet verwijderd worden.");
84
- }
85
- if (parsed.kind === "proposal") {
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: parsed.uuid } });
120
+ const res = await triggerDeleteOrder({ data: { orderId: target.id } });
90
121
  reversals += res.reversalEntries;
91
122
  }
92
- deleted += 1;
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
- if (deleted > 0) {
100
- toast.success(targets.length > 1
101
- ? `${deleted} van ${targets.length} bestelling(en) verwijderd — ${reversals} voorraadmutatie(s) teruggedraaid`
102
- : `Bestelling verwijderd — ${reversals} voorraadmutatie(s) teruggedraaid`);
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 (deleted > 0)
108
- setOrderPendingDelete(null);
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(SalesOrdersTable, { orders: paged, loading: isLoading, onSelectOrder: (o) => navigate({
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 verwijderd worden.");
286
+ toast.error("FO-orders kunnen hier niet geannuleerd worden. Annuleer deze in Floriday.");
226
287
  return;
227
288
  }
228
- setOrderPendingDelete(o);
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
- }, onConfirmOrder: (o) => navigate({
237
- to: "/floriday-verkoop/verkooporders/$salesOrderId",
238
- params: { salesOrderId: o.id },
239
- }) }), orderPendingDelete && (_jsx("div", { className: "fixed inset-0 z-50 flex items-center justify-center bg-black/50 p-4", onClick: () => !deleting && setOrderPendingDelete(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: [_jsx("h2", { className: "text-lg font-semibold text-foreground", children: "Bestelling verwijderen?" }), _jsxs("p", { className: "mt-2 text-sm text-muted-foreground", children: ["Weet je zeker dat je bestelling", " ", _jsx("span", { className: "font-medium text-foreground", children: orderPendingDelete.orderNumber || orderPendingDelete.id }), " ", "wilt verwijderen? Alle voorraadmutaties van deze bestelling worden automatisch teruggedraaid."] }), _jsxs("div", { className: "mt-5 flex justify-end gap-2", children: [_jsx(Button, { variant: "outline", size: "sm", onClick: () => setOrderPendingDelete(null), disabled: deleting, children: "Annuleren" }), _jsx(Button, { size: "sm", onClick: handleDeleteOrder, disabled: deleting, className: "bg-destructive text-destructive-foreground hover:bg-destructive/90", children: deleting ? "Bezig…" : "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
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"