@flowselections/floriday-klanten-module 1.0.15 → 1.0.17

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 (102) hide show
  1. package/dist-lib/assets/floriday-logo.png.asset.json +11 -0
  2. package/dist-lib/components/klanten/BackLink.d.ts +12 -0
  3. package/dist-lib/components/klanten/BackLink.d.ts.map +1 -0
  4. package/dist-lib/components/klanten/BackLink.js +12 -0
  5. package/dist-lib/components/klanten/FloridayAssortmentTab.d.ts +4 -0
  6. package/dist-lib/components/klanten/FloridayAssortmentTab.d.ts.map +1 -0
  7. package/dist-lib/components/klanten/FloridayAssortmentTab.js +80 -0
  8. package/dist-lib/components/klanten/FloridayDeeplinkButton.d.ts +8 -0
  9. package/dist-lib/components/klanten/FloridayDeeplinkButton.d.ts.map +1 -0
  10. package/dist-lib/components/klanten/FloridayDeeplinkButton.js +9 -0
  11. package/dist-lib/components/klanten/GroupedKlantDetail.d.ts.map +1 -1
  12. package/dist-lib/components/klanten/GroupedKlantDetail.js +19 -5
  13. package/dist-lib/components/klanten/ImportKlanten.d.ts.map +1 -1
  14. package/dist-lib/components/klanten/ImportKlanten.js +3 -2
  15. package/dist-lib/components/klanten/KlantDetail.d.ts.map +1 -1
  16. package/dist-lib/components/klanten/KlantDetail.js +9 -3
  17. package/dist-lib/components/klanten/KlantenOverzicht.d.ts.map +1 -1
  18. package/dist-lib/components/klanten/KlantenOverzicht.js +9 -6
  19. package/dist-lib/components/klanten/NetwerkPagina.d.ts.map +1 -1
  20. package/dist-lib/components/klanten/NetwerkPagina.js +21 -18
  21. package/dist-lib/components/klanten/NieuweKlant.d.ts.map +1 -1
  22. package/dist-lib/components/klanten/NieuweKlant.js +23 -8
  23. package/dist-lib/components/klanten/PriceGroupBadges.d.ts +7 -0
  24. package/dist-lib/components/klanten/PriceGroupBadges.d.ts.map +1 -0
  25. package/dist-lib/components/klanten/PriceGroupBadges.js +13 -0
  26. package/dist-lib/components/klanten/SyncStatusChip.d.ts +4 -0
  27. package/dist-lib/components/klanten/SyncStatusChip.d.ts.map +1 -0
  28. package/dist-lib/components/klanten/SyncStatusChip.js +49 -0
  29. package/dist-lib/components/klanten/pricegroups/CustomerPickerDialog.d.ts +8 -0
  30. package/dist-lib/components/klanten/pricegroups/CustomerPickerDialog.d.ts.map +1 -0
  31. package/dist-lib/components/klanten/pricegroups/CustomerPickerDialog.js +75 -0
  32. package/dist-lib/components/klanten/pricegroups/PriceGroupFormPage.d.ts +4 -0
  33. package/dist-lib/components/klanten/pricegroups/PriceGroupFormPage.d.ts.map +1 -0
  34. package/dist-lib/components/klanten/pricegroups/PriceGroupFormPage.js +269 -0
  35. package/dist-lib/components/klanten/pricegroups/PriceGroupsPage.d.ts +2 -0
  36. package/dist-lib/components/klanten/pricegroups/PriceGroupsPage.d.ts.map +1 -0
  37. package/dist-lib/components/klanten/pricegroups/PriceGroupsPage.js +133 -0
  38. package/dist-lib/components/klanten/pricegroups/ProductPickerDialog.d.ts +8 -0
  39. package/dist-lib/components/klanten/pricegroups/ProductPickerDialog.d.ts.map +1 -0
  40. package/dist-lib/components/klanten/pricegroups/ProductPickerDialog.js +18 -0
  41. package/dist-lib/index.d.ts +1 -0
  42. package/dist-lib/index.d.ts.map +1 -1
  43. package/dist-lib/index.js +6 -1
  44. package/dist-lib/integrations/supabase/auth-middleware.d.ts +1684 -93
  45. package/dist-lib/integrations/supabase/auth-middleware.d.ts.map +1 -1
  46. package/dist-lib/integrations/supabase/client.d.ts +1684 -93
  47. package/dist-lib/integrations/supabase/client.d.ts.map +1 -1
  48. package/dist-lib/integrations/supabase/client.js +4 -3
  49. package/dist-lib/integrations/supabase/client.server.d.ts +1684 -93
  50. package/dist-lib/integrations/supabase/client.server.d.ts.map +1 -1
  51. package/dist-lib/integrations/supabase/types.d.ts +1712 -72
  52. package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
  53. package/dist-lib/integrations/supabase/types.js +3 -9
  54. package/dist-lib/lib/accounting/twinfield/client.server.d.ts +21 -0
  55. package/dist-lib/lib/accounting/twinfield/client.server.d.ts.map +1 -0
  56. package/dist-lib/lib/accounting/twinfield/client.server.js +171 -0
  57. package/dist-lib/lib/accounting/twinfield/mapping.d.ts +46 -0
  58. package/dist-lib/lib/accounting/twinfield/mapping.d.ts.map +1 -0
  59. package/dist-lib/lib/accounting/twinfield/mapping.js +121 -0
  60. package/dist-lib/lib/accounting/twinfield/worker.functions.d.ts +38814 -0
  61. package/dist-lib/lib/accounting/twinfield/worker.functions.d.ts.map +1 -0
  62. package/dist-lib/lib/accounting/twinfield/worker.functions.js +48 -0
  63. package/dist-lib/lib/accounting/twinfield/worker.server.d.ts +12 -0
  64. package/dist-lib/lib/accounting/twinfield/worker.server.d.ts.map +1 -0
  65. package/dist-lib/lib/accounting/twinfield/worker.server.js +124 -0
  66. package/dist-lib/lib/accounting.functions.d.ts +4806 -33
  67. package/dist-lib/lib/accounting.functions.d.ts.map +1 -1
  68. package/dist-lib/lib/customer-analytics.functions.d.ts +1684 -93
  69. package/dist-lib/lib/customer-analytics.functions.d.ts.map +1 -1
  70. package/dist-lib/lib/customer-schemas.d.ts +2 -0
  71. package/dist-lib/lib/customer-schemas.d.ts.map +1 -1
  72. package/dist-lib/lib/customer-schemas.js +1 -0
  73. package/dist-lib/lib/customers.functions.d.ts +25793 -163
  74. package/dist-lib/lib/customers.functions.d.ts.map +1 -1
  75. package/dist-lib/lib/customers.functions.js +92 -4
  76. package/dist-lib/lib/delivery-locations.functions.d.ts +6408 -44
  77. package/dist-lib/lib/delivery-locations.functions.d.ts.map +1 -1
  78. package/dist-lib/lib/external-customers.functions.d.ts +6408 -44
  79. package/dist-lib/lib/external-customers.functions.d.ts.map +1 -1
  80. package/dist-lib/lib/floriday-assortment.functions.d.ts +12959 -0
  81. package/dist-lib/lib/floriday-assortment.functions.d.ts.map +1 -0
  82. package/dist-lib/lib/floriday-assortment.functions.js +213 -0
  83. package/dist-lib/lib/floriday-discounts.functions.d.ts +3398 -216
  84. package/dist-lib/lib/floriday-discounts.functions.d.ts.map +1 -1
  85. package/dist-lib/lib/floriday-trade-items.functions.d.ts +12943 -0
  86. package/dist-lib/lib/floriday-trade-items.functions.d.ts.map +1 -0
  87. package/dist-lib/lib/floriday-trade-items.functions.js +107 -0
  88. package/dist-lib/lib/local-discounts.functions.d.ts +11216 -79
  89. package/dist-lib/lib/local-discounts.functions.d.ts.map +1 -1
  90. package/dist-lib/lib/overdue-invoices.functions.d.ts +1684 -93
  91. package/dist-lib/lib/overdue-invoices.functions.d.ts.map +1 -1
  92. package/dist-lib/lib/price-groups-local.functions.d.ts +90665 -0
  93. package/dist-lib/lib/price-groups-local.functions.d.ts.map +1 -0
  94. package/dist-lib/lib/price-groups-local.functions.js +148 -0
  95. package/dist-lib/lib/price-groups.functions.d.ts +12952 -0
  96. package/dist-lib/lib/price-groups.functions.d.ts.map +1 -0
  97. package/dist-lib/lib/price-groups.functions.js +115 -0
  98. package/dist-lib/lib/pricing.d.ts +24 -0
  99. package/dist-lib/lib/pricing.d.ts.map +1 -0
  100. package/dist-lib/lib/pricing.js +60 -0
  101. package/package.json +8 -4
  102. package/public/flowselections-assets/Klanten/floriday-logo.png +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PriceGroupsPage.d.ts","sourceRoot":"","sources":["../../../../src/components/klanten/pricegroups/PriceGroupsPage.tsx"],"names":[],"mappings":"AAiHA,wBAAgB,eAAe,gCAoO9B"}
@@ -0,0 +1,133 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useMemo, useState } from "react";
3
+ import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
4
+ import { useServerFn } from "@tanstack/react-start";
5
+ import { Button, Input, Card, CardContent, Badge, Table, TableBody, TableCell, TableHead, TableHeader, TableRow, Tabs, TabsList, TabsTrigger, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, DropdownMenuSeparator, } from "@flowselections/core";
6
+ import { Link, useNavigate } from "@tanstack/react-router";
7
+ import { Plus, Search, MoreVertical, GripVertical, EyeOff, Eye, Pencil, Users, Copy, Trash2, Tag, ChevronLeft, ChevronRight } from "lucide-react";
8
+ import { DndContext, closestCenter, KeyboardSensor, PointerSensor, useSensor, useSensors, } from "@dnd-kit/core";
9
+ import { SortableContext, sortableKeyboardCoordinates, verticalListSortingStrategy, useSortable, arrayMove, } from "@dnd-kit/sortable";
10
+ import { CSS } from "@dnd-kit/utilities";
11
+ import { listCustomerPriceGroups, deleteCustomerPriceGroup, reorderCustomerPriceGroups, updateCustomerPriceGroup, createCustomerPriceGroup, } from "../../../lib/price-groups-local.functions";
12
+ import { CustomerPickerDialog } from "./CustomerPickerDialog";
13
+ function unitLabel(u) {
14
+ switch (u) {
15
+ case "per_fust": return "Per fust";
16
+ case "per_stuk": return "Per stuk";
17
+ case "per_ladingdrager": return "Per ladingdrager";
18
+ default: return u;
19
+ }
20
+ }
21
+ function calcSummary(g) {
22
+ if (g.pricing_mode !== "calculated")
23
+ return "—";
24
+ if (!g.calc_type || !g.calc_value_type || !g.calc_value)
25
+ return "Geen berekening";
26
+ const verb = g.calc_type === "discount" ? "Korting" : "Opslag";
27
+ const val = g.calc_value_type === "percentage" ? `${g.calc_value}%` : `€ ${Number(g.calc_value).toFixed(2)}`;
28
+ return `${verb} — ${val}`;
29
+ }
30
+ function SortableRow({ g, onEdit, onMembers, onDelete, onDuplicate, onToggleActive }) {
31
+ const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({ id: g.id });
32
+ const style = { transform: CSS.Transform.toString(transform), transition, opacity: isDragging ? 0.6 : 1 };
33
+ return (_jsxs(TableRow, { ref: setNodeRef, style: style, className: isDragging ? "bg-muted/50" : undefined, children: [_jsx(TableCell, { className: "w-[36px] p-0 pl-2", children: _jsx("button", { type: "button", className: "cursor-grab touch-none text-muted-foreground hover:text-foreground p-2", "aria-label": "Verslepen", ...attributes, ...listeners, children: _jsx(GripVertical, { className: "h-4 w-4" }) }) }), _jsxs(TableCell, { children: [_jsxs("div", { className: "font-medium flex items-center gap-2", children: [g.name, !g.is_active && _jsx(Badge, { variant: "secondary", className: "text-[10px]", children: "Inactief" })] }), _jsxs("div", { className: "text-xs text-muted-foreground", children: ["Laatst gewijzigd: ", new Date(g.updated_at).toLocaleString("nl-NL")] })] }), _jsx(TableCell, { children: _jsx(Badge, { variant: g.pricing_mode === "calculated" ? "default" : "secondary", children: g.pricing_mode === "calculated" ? "Berekende prijzen" : "Handmatige prijzen" }) }), _jsxs(TableCell, { className: "tabular-nums", children: [g.member_count, " klanten"] }), _jsxs(TableCell, { className: "text-sm", children: [g.price_scope === "incl_delivery" ? "Levering en transport" : "Exclusief levering", g.include_stickers && _jsx("div", { className: "text-xs text-muted-foreground", children: "Incl. stickers" })] }), _jsx(TableCell, { className: "text-sm", children: unitLabel(g.unit) }), _jsx(TableCell, { className: "text-sm", children: g.currency }), _jsx(TableCell, { className: "text-sm text-muted-foreground", children: calcSummary(g) }), _jsx(TableCell, { className: "w-[52px] text-right pr-2", children: _jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsx(Button, { variant: "ghost", size: "icon", "aria-label": "Acties", children: _jsx(MoreVertical, { className: "h-4 w-4" }) }) }), _jsxs(DropdownMenuContent, { align: "end", children: [_jsxs(DropdownMenuItem, { onClick: onEdit, children: [_jsx(Pencil, { className: "h-4 w-4 mr-2" }), "Bewerken"] }), _jsxs(DropdownMenuItem, { onClick: onMembers, children: [_jsx(Users, { className: "h-4 w-4 mr-2" }), "Klanten toewijzen"] }), _jsxs(DropdownMenuItem, { onClick: onDuplicate, children: [_jsx(Copy, { className: "h-4 w-4 mr-2" }), "Dupliceren"] }), _jsx(DropdownMenuItem, { onClick: onToggleActive, children: g.is_active
34
+ ? (_jsxs(_Fragment, { children: [_jsx(EyeOff, { className: "h-4 w-4 mr-2" }), "Op inactief zetten"] }))
35
+ : (_jsxs(_Fragment, { children: [_jsx(Eye, { className: "h-4 w-4 mr-2" }), "Activeren"] })) }), _jsx(DropdownMenuSeparator, {}), _jsxs(DropdownMenuItem, { onClick: onDelete, className: "text-destructive focus:text-destructive", children: [_jsx(Trash2, { className: "h-4 w-4 mr-2" }), "Verwijderen"] })] })] }) })] }));
36
+ }
37
+ export function PriceGroupsPage() {
38
+ const qc = useQueryClient();
39
+ const navigate = useNavigate();
40
+ const listFn = useServerFn(listCustomerPriceGroups);
41
+ const delFn = useServerFn(deleteCustomerPriceGroup);
42
+ const reorderFn = useServerFn(reorderCustomerPriceGroups);
43
+ const updateFn = useServerFn(updateCustomerPriceGroup);
44
+ const createFn = useServerFn(createCustomerPriceGroup);
45
+ const { data: groups = [], isLoading } = useQuery({
46
+ queryKey: ["customer-price-groups"],
47
+ queryFn: () => listFn(),
48
+ });
49
+ const [search, setSearch] = useState("");
50
+ const [statusFilter, setStatusFilter] = useState("all");
51
+ const [membersFor, setMembersFor] = useState(null);
52
+ const [page, setPage] = useState(1);
53
+ const [pageSize, setPageSize] = useState(25);
54
+ const resetPage = () => setPage(1);
55
+ const sensors = useSensors(useSensor(PointerSensor, { activationConstraint: { distance: 5 } }), useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates }));
56
+ const filtered = useMemo(() => {
57
+ const q = search.trim().toLowerCase();
58
+ return groups.filter((g) => {
59
+ if (statusFilter === "active" && !g.is_active)
60
+ return false;
61
+ if (statusFilter === "inactive" && g.is_active)
62
+ return false;
63
+ if (!q)
64
+ return true;
65
+ return g.name.toLowerCase().includes(q);
66
+ });
67
+ }, [groups, search, statusFilter]);
68
+ const reorderMut = useMutation({
69
+ mutationFn: (ids) => reorderFn({ data: { ids } }),
70
+ onMutate: async (ids) => {
71
+ await qc.cancelQueries({ queryKey: ["customer-price-groups"] });
72
+ const prev = qc.getQueryData(["customer-price-groups"]);
73
+ if (prev) {
74
+ const map = new Map(prev.map((g) => [g.id, g]));
75
+ const next = ids.map((id, idx) => ({ ...map.get(id), sort_order: idx + 1 }));
76
+ qc.setQueryData(["customer-price-groups"], next);
77
+ }
78
+ return { prev };
79
+ },
80
+ onError: (_e, _v, ctx) => { if (ctx?.prev)
81
+ qc.setQueryData(["customer-price-groups"], ctx.prev); },
82
+ onSettled: () => qc.invalidateQueries({ queryKey: ["customer-price-groups"] }),
83
+ });
84
+ const deleteMut = useMutation({
85
+ mutationFn: (id) => delFn({ data: { id } }),
86
+ onSuccess: () => qc.invalidateQueries({ queryKey: ["customer-price-groups"] }),
87
+ });
88
+ const toggleActiveMut = useMutation({
89
+ mutationFn: async (g) => {
90
+ const { id, member_count, created_at, updated_at, sort_order, ...rest } = g;
91
+ return updateFn({ data: { id, input: { ...rest, is_active: !g.is_active } } });
92
+ },
93
+ onSuccess: () => qc.invalidateQueries({ queryKey: ["customer-price-groups"] }),
94
+ });
95
+ const duplicateMut = useMutation({
96
+ mutationFn: async (g) => {
97
+ const { id, member_count, created_at, updated_at, sort_order, ...rest } = g;
98
+ return createFn({ data: { ...rest, name: `${g.name} (kopie)` } });
99
+ },
100
+ onSuccess: () => qc.invalidateQueries({ queryKey: ["customer-price-groups"] }),
101
+ });
102
+ const handleDragEnd = (event) => {
103
+ const { active, over } = event;
104
+ if (!over || active.id === over.id)
105
+ return;
106
+ const ids = filtered.map((g) => g.id);
107
+ const oldIndex = ids.indexOf(String(active.id));
108
+ const newIndex = ids.indexOf(String(over.id));
109
+ if (oldIndex < 0 || newIndex < 0)
110
+ return;
111
+ // Reorder only over full list to keep global ordering stable
112
+ const fullIds = groups.map((g) => g.id);
113
+ const from = fullIds.indexOf(String(active.id));
114
+ const to = fullIds.indexOf(String(over.id));
115
+ const next = arrayMove(fullIds, from, to);
116
+ reorderMut.mutate(next);
117
+ };
118
+ const active = groups.filter((g) => g.is_active).length;
119
+ const inactive = groups.length - active;
120
+ const total = filtered.length;
121
+ const totalPages = Math.max(1, Math.ceil(total / pageSize));
122
+ const currentPage = Math.min(page, totalPages);
123
+ const startIdx = total === 0 ? 0 : (currentPage - 1) * pageSize;
124
+ const endIdx = Math.min(startIdx + pageSize, total);
125
+ const pageRows = filtered.slice(startIdx, endIdx);
126
+ return (_jsxs("main", { className: "p-6 space-y-5", children: [_jsxs("div", { className: "flex items-start justify-between gap-4 flex-wrap", children: [_jsxs("div", { children: [_jsx("h1", { className: "text-2xl font-bold tracking-tight", children: "Prijsgroepen" }), _jsx("p", { className: "text-sm text-muted-foreground mt-1", children: "Beheer prijsafspraken en wijs klanten uit elke bron toe aan een prijsgroep." })] }), _jsx(Button, { asChild: true, children: _jsxs(Link, { to: "/klanten/prijsgroepen/nieuw", children: [_jsx(Plus, { className: "h-4 w-4 mr-2" }), "Prijsgroep toevoegen"] }) })] }), _jsxs(Card, { children: [_jsx("div", { className: "border-b px-4 pt-3", children: _jsx(Tabs, { value: statusFilter, onValueChange: (v) => { setStatusFilter(v); resetPage(); }, children: _jsxs(TabsList, { className: "flex-wrap", children: [_jsxs(TabsTrigger, { value: "all", children: ["Alle ", _jsx("span", { className: "ml-1.5 text-xs text-muted-foreground", children: groups.length })] }), _jsxs(TabsTrigger, { value: "active", children: ["Actief ", _jsx("span", { className: "ml-1.5 text-xs text-muted-foreground", children: active })] }), _jsxs(TabsTrigger, { value: "inactive", children: ["Inactief ", _jsx("span", { className: "ml-1.5 text-xs text-muted-foreground", children: inactive })] })] }) }) }), _jsx("div", { className: "flex items-center gap-2 flex-wrap p-4 border-b", children: _jsxs("div", { className: "relative flex-1 min-w-[240px] max-w-md", children: [_jsx(Search, { className: "absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" }), _jsx(Input, { placeholder: "Zoek op naam\u2026", value: search, onChange: (e) => { setSearch(e.target.value); resetPage(); }, className: "pl-9" })] }) }), _jsx(CardContent, { className: "p-0", children: _jsx(DndContext, { sensors: sensors, collisionDetection: closestCenter, onDragEnd: handleDragEnd, children: _jsxs(Table, { children: [_jsx(TableHeader, { children: _jsxs(TableRow, { children: [_jsx(TableHead, { className: "w-[36px]" }), _jsx(TableHead, { children: "Naam" }), _jsx(TableHead, { children: "Type" }), _jsx(TableHead, { children: "Klanten" }), _jsx(TableHead, { children: "Diensten" }), _jsx(TableHead, { children: "Afname-eenheid" }), _jsx(TableHead, { children: "Valuta" }), _jsx(TableHead, { children: "Berekening" }), _jsx(TableHead, { className: "w-[52px]" })] }) }), _jsx(TableBody, { children: isLoading ? (_jsx(TableRow, { children: _jsx(TableCell, { colSpan: 9, className: "text-center py-8 text-muted-foreground text-sm", children: "Laden\u2026" }) })) : pageRows.length === 0 ? (_jsx(TableRow, { children: _jsx(TableCell, { colSpan: 9, children: _jsxs("div", { className: "flex flex-col items-center justify-center py-12 text-center", children: [_jsx("div", { className: "h-12 w-12 rounded-full bg-muted flex items-center justify-center mb-3", children: _jsx(Tag, { className: "h-5 w-5 text-muted-foreground" }) }), _jsx("div", { className: "font-medium", children: "Geen prijsgroepen gevonden" }), _jsx("div", { className: "text-sm text-muted-foreground mt-1", children: groups.length === 0
127
+ ? "Voeg je eerste prijsgroep toe om te beginnen."
128
+ : "Pas je filters aan of voeg een nieuwe prijsgroep toe." }), _jsx(Button, { asChild: true, className: "mt-4", children: _jsxs(Link, { to: "/klanten/prijsgroepen/nieuw", children: [_jsx(Plus, { className: "h-4 w-4 mr-2" }), "Prijsgroep toevoegen"] }) })] }) }) })) : (_jsx(SortableContext, { items: pageRows.map((g) => g.id), strategy: verticalListSortingStrategy, children: pageRows.map((g) => (_jsx(SortableRow, { g: g, onEdit: () => navigate({ to: "/klanten/prijsgroepen/$id", params: { id: g.id } }), onMembers: () => setMembersFor(g.id), onDelete: () => {
129
+ if (confirm(`Prijsgroep "${g.name}" verwijderen?`))
130
+ deleteMut.mutate(g.id);
131
+ }, onDuplicate: () => duplicateMut.mutate(g), onToggleActive: () => toggleActiveMut.mutate(g) }, g.id))) })) })] }) }) }), _jsxs("div", { className: "flex items-center justify-between gap-3 flex-wrap px-4 py-3 border-t text-sm", children: [_jsxs("div", { className: "text-muted-foreground", children: [total, " prijsgroep", total === 1 ? "" : "en"] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx("span", { className: "text-muted-foreground", children: "Per pagina" }), _jsxs(Select, { value: String(pageSize), onValueChange: (v) => { setPageSize(Number(v)); resetPage(); }, children: [_jsx(SelectTrigger, { className: "w-20", children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: [10, 25, 50, 100].map((n) => (_jsx(SelectItem, { value: String(n), children: n }, n))) })] }), _jsxs(Button, { variant: "outline", size: "sm", onClick: () => setPage((p) => Math.max(1, p - 1)), disabled: currentPage <= 1, children: [_jsx(ChevronLeft, { className: "h-4 w-4 mr-1" }), "Vorige"] }), _jsxs("span", { className: "text-muted-foreground tabular-nums", children: [currentPage, " / ", totalPages] }), _jsxs(Button, { variant: "outline", size: "sm", onClick: () => setPage((p) => Math.min(totalPages, p + 1)), disabled: currentPage >= totalPages, children: ["Volgende", _jsx(ChevronRight, { className: "h-4 w-4 ml-1" })] })] })] })] }), membersFor && (_jsx(CustomerPickerDialog, { priceGroupId: membersFor, open: !!membersFor, onOpenChange: (o) => { if (!o)
132
+ setMembersFor(null); } }))] }));
133
+ }
@@ -0,0 +1,8 @@
1
+ import type { TradeItemPreview } from "../../../lib/floriday-trade-items.functions";
2
+ export declare function ProductPickerDialog({ open, onOpenChange, items, onSelect, }: {
3
+ open: boolean;
4
+ onOpenChange: (v: boolean) => void;
5
+ items: TradeItemPreview[];
6
+ onSelect: (item: TradeItemPreview) => void;
7
+ }): import("react").JSX.Element;
8
+ //# sourceMappingURL=ProductPickerDialog.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ProductPickerDialog.d.ts","sourceRoot":"","sources":["../../../../src/components/klanten/pricegroups/ProductPickerDialog.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,6CAA6C,CAAC;AAEpF,wBAAgB,mBAAmB,CAAC,EAClC,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,QAAQ,GACpC,EAAE;IACD,IAAI,EAAE,OAAO,CAAC;IACd,YAAY,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IACnC,KAAK,EAAE,gBAAgB,EAAE,CAAC;IAC1B,QAAQ,EAAE,CAAC,IAAI,EAAE,gBAAgB,KAAK,IAAI,CAAC;CAC5C,+BA0DA"}
@@ -0,0 +1,18 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useMemo, useState } from "react";
3
+ import { Dialog, DialogContent, DialogHeader, DialogTitle, Input, } from "@flowselections/core";
4
+ import { ImageIcon, Search } from "lucide-react";
5
+ export function ProductPickerDialog({ open, onOpenChange, items, onSelect, }) {
6
+ const [q, setQ] = useState("");
7
+ const filtered = useMemo(() => {
8
+ const s = q.trim().toLowerCase();
9
+ if (!s)
10
+ return items;
11
+ return items.filter((i) => i.name.toLowerCase().includes(s) ||
12
+ (i.vbn_code ?? "").toLowerCase().includes(s));
13
+ }, [items, q]);
14
+ return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { className: "max-w-xl", children: [_jsx(DialogHeader, { children: _jsx(DialogTitle, { children: "Kies een product voor de voorbeeldberekening" }) }), _jsxs("div", { className: "relative", children: [_jsx(Search, { className: "h-4 w-4 absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground" }), _jsx(Input, { autoFocus: true, placeholder: "Zoek op naam of VBN-code", value: q, onChange: (e) => setQ(e.target.value), className: "pl-9" })] }), _jsxs("div", { className: "max-h-[420px] overflow-y-auto -mx-2 px-2 divide-y", children: [filtered.length === 0 && (_jsx("div", { className: "text-sm text-muted-foreground py-6 text-center", children: "Geen producten gevonden." })), filtered.map((i) => (_jsxs("button", { type: "button", onClick: () => { onSelect(i); onOpenChange(false); }, className: "w-full flex items-center gap-3 py-2.5 text-left hover:bg-muted/40 rounded-md px-2", children: [_jsx("div", { className: "h-10 w-10 rounded bg-muted flex items-center justify-center overflow-hidden shrink-0", children: i.image_url
15
+ ? _jsx("img", { src: i.image_url, alt: "", className: "h-full w-full object-contain" })
16
+ : _jsx(ImageIcon, { className: "h-5 w-5 text-muted-foreground" }) }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("div", { className: "text-sm font-medium truncate", children: i.name }), _jsxs("div", { className: "text-xs text-muted-foreground truncate", children: [[i.packaging_code, `${i.units_per_fust}×${i.packages_per_layer}×${i.layers_per_carrier}`, i.carrier_code]
17
+ .filter(Boolean).join(" - "), i.vbn_code ? ` · ${i.vbn_code}` : ""] })] })] }, i.id)))] })] }) }));
18
+ }
@@ -8,5 +8,6 @@ export { NieuweKlant } from "./components/klanten/NieuweKlant";
8
8
  export { ImportKlanten } from "./components/klanten/ImportKlanten";
9
9
  export { KlantDetailRoute } from "./components/klanten/KlantDetailRoute";
10
10
  export { GroupedKlantDetailRoute } from "./components/klanten/GroupedKlantDetailRoute";
11
+ export { PriceGroupsPage } from "./components/klanten/pricegroups/PriceGroupsPage";
11
12
  export declare const floridayKlantenModule: FlowModule;
12
13
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAGvD,cAAc,kBAAkB,CAAC;AACjC,OAAO,EAAE,gBAAgB,EAAE,MAAM,uCAAuC,CAAC;AACzE,OAAO,EAAE,WAAW,EAAE,MAAM,kCAAkC,CAAC;AAC/D,OAAO,EAAE,kBAAkB,EAAE,MAAM,yCAAyC,CAAC;AAC7E,OAAO,EAAE,aAAa,EAAE,MAAM,oCAAoC,CAAC;AACnE,OAAO,EAAE,WAAW,EAAE,MAAM,kCAAkC,CAAC;AAC/D,OAAO,EAAE,aAAa,EAAE,MAAM,oCAAoC,CAAC;AACnE,OAAO,EAAE,gBAAgB,EAAE,MAAM,uCAAuC,CAAC;AACzE,OAAO,EAAE,uBAAuB,EAAE,MAAM,8CAA8C,CAAC;AAEvF,eAAO,MAAM,qBAAqB,EAAE,UAYnC,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAGvD,cAAc,kBAAkB,CAAC;AACjC,OAAO,EAAE,gBAAgB,EAAE,MAAM,uCAAuC,CAAC;AACzE,OAAO,EAAE,WAAW,EAAE,MAAM,kCAAkC,CAAC;AAC/D,OAAO,EAAE,kBAAkB,EAAE,MAAM,yCAAyC,CAAC;AAC7E,OAAO,EAAE,aAAa,EAAE,MAAM,oCAAoC,CAAC;AACnE,OAAO,EAAE,WAAW,EAAE,MAAM,kCAAkC,CAAC;AAC/D,OAAO,EAAE,aAAa,EAAE,MAAM,oCAAoC,CAAC;AACnE,OAAO,EAAE,gBAAgB,EAAE,MAAM,uCAAuC,CAAC;AACzE,OAAO,EAAE,uBAAuB,EAAE,MAAM,8CAA8C,CAAC;AACvF,OAAO,EAAE,eAAe,EAAE,MAAM,kDAAkD,CAAC;AAEnF,eAAO,MAAM,qBAAqB,EAAE,UAgBnC,CAAC"}
package/dist-lib/index.js CHANGED
@@ -13,7 +13,7 @@
13
13
  // 3. Add your nav item — use `children` if you need a dropdown
14
14
  // 4. Done! You don't need to touch any other file for registration.
15
15
  // ============================================================================
16
- import { Users } from "lucide-react";
16
+ import { Users, Tag } from "lucide-react";
17
17
  import { CustomerFieldsCard } from "./components/settings/CustomerFieldsCard";
18
18
  export * from "./_core-safelist";
19
19
  export { KlantenOverzicht } from "./components/klanten/KlantenOverzicht";
@@ -24,6 +24,7 @@ export { NieuweKlant } from "./components/klanten/NieuweKlant";
24
24
  export { ImportKlanten } from "./components/klanten/ImportKlanten";
25
25
  export { KlantDetailRoute } from "./components/klanten/KlantDetailRoute";
26
26
  export { GroupedKlantDetailRoute } from "./components/klanten/GroupedKlantDetailRoute";
27
+ export { PriceGroupsPage } from "./components/klanten/pricegroups/PriceGroupsPage";
27
28
  export const floridayKlantenModule = {
28
29
  id: "customers",
29
30
  name: "Klanten",
@@ -32,6 +33,10 @@ export const floridayKlantenModule = {
32
33
  label: "Klanten",
33
34
  href: "/klanten",
34
35
  icon: Users,
36
+ children: [
37
+ { label: "Overzicht", href: "/klanten", icon: Users },
38
+ { label: "Prijsgroepen", href: "/klanten/prijsgroepen", icon: Tag },
39
+ ],
35
40
  },
36
41
  settingsCards: [
37
42
  { component: CustomerFieldsCard, order: 10 },