@flowselections/floriday-klanten-module 1.0.15 → 1.0.16
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/assets/floriday-logo.png.asset.json +11 -0
- package/dist-lib/components/klanten/BackLink.d.ts +12 -0
- package/dist-lib/components/klanten/BackLink.d.ts.map +1 -0
- package/dist-lib/components/klanten/BackLink.js +12 -0
- package/dist-lib/components/klanten/FloridayAssortmentTab.d.ts +4 -0
- package/dist-lib/components/klanten/FloridayAssortmentTab.d.ts.map +1 -0
- package/dist-lib/components/klanten/FloridayAssortmentTab.js +80 -0
- package/dist-lib/components/klanten/FloridayDeeplinkButton.d.ts +8 -0
- package/dist-lib/components/klanten/FloridayDeeplinkButton.d.ts.map +1 -0
- package/dist-lib/components/klanten/FloridayDeeplinkButton.js +9 -0
- package/dist-lib/components/klanten/GroupedKlantDetail.d.ts.map +1 -1
- package/dist-lib/components/klanten/GroupedKlantDetail.js +19 -5
- package/dist-lib/components/klanten/ImportKlanten.d.ts.map +1 -1
- package/dist-lib/components/klanten/ImportKlanten.js +3 -2
- package/dist-lib/components/klanten/KlantDetail.d.ts.map +1 -1
- package/dist-lib/components/klanten/KlantDetail.js +9 -3
- package/dist-lib/components/klanten/KlantenOverzicht.d.ts.map +1 -1
- package/dist-lib/components/klanten/KlantenOverzicht.js +6 -3
- package/dist-lib/components/klanten/NetwerkPagina.d.ts.map +1 -1
- package/dist-lib/components/klanten/NetwerkPagina.js +21 -18
- package/dist-lib/components/klanten/NieuweKlant.d.ts.map +1 -1
- package/dist-lib/components/klanten/NieuweKlant.js +23 -8
- package/dist-lib/components/klanten/PriceGroupBadges.d.ts +7 -0
- package/dist-lib/components/klanten/PriceGroupBadges.d.ts.map +1 -0
- package/dist-lib/components/klanten/PriceGroupBadges.js +13 -0
- package/dist-lib/components/klanten/SyncStatusChip.d.ts +4 -0
- package/dist-lib/components/klanten/SyncStatusChip.d.ts.map +1 -0
- package/dist-lib/components/klanten/SyncStatusChip.js +49 -0
- package/dist-lib/components/klanten/pricegroups/CustomerPickerDialog.d.ts +8 -0
- package/dist-lib/components/klanten/pricegroups/CustomerPickerDialog.d.ts.map +1 -0
- package/dist-lib/components/klanten/pricegroups/CustomerPickerDialog.js +75 -0
- package/dist-lib/components/klanten/pricegroups/PriceGroupFormPage.d.ts +4 -0
- package/dist-lib/components/klanten/pricegroups/PriceGroupFormPage.d.ts.map +1 -0
- package/dist-lib/components/klanten/pricegroups/PriceGroupFormPage.js +269 -0
- package/dist-lib/components/klanten/pricegroups/PriceGroupsPage.d.ts +2 -0
- package/dist-lib/components/klanten/pricegroups/PriceGroupsPage.d.ts.map +1 -0
- package/dist-lib/components/klanten/pricegroups/PriceGroupsPage.js +133 -0
- package/dist-lib/components/klanten/pricegroups/ProductPickerDialog.d.ts +8 -0
- package/dist-lib/components/klanten/pricegroups/ProductPickerDialog.d.ts.map +1 -0
- package/dist-lib/components/klanten/pricegroups/ProductPickerDialog.js +18 -0
- package/dist-lib/index.d.ts +1 -0
- package/dist-lib/index.d.ts.map +1 -1
- package/dist-lib/index.js +6 -1
- package/dist-lib/integrations/supabase/auth-middleware.d.ts +762 -3
- package/dist-lib/integrations/supabase/auth-middleware.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/client.d.ts +762 -3
- package/dist-lib/integrations/supabase/client.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/client.js +4 -3
- package/dist-lib/integrations/supabase/client.server.d.ts +762 -3
- package/dist-lib/integrations/supabase/client.server.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/types.d.ts +789 -8
- package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
- package/dist-lib/lib/accounting/twinfield/client.server.d.ts +21 -0
- package/dist-lib/lib/accounting/twinfield/client.server.d.ts.map +1 -0
- package/dist-lib/lib/accounting/twinfield/client.server.js +171 -0
- package/dist-lib/lib/accounting/twinfield/mapping.d.ts +46 -0
- package/dist-lib/lib/accounting/twinfield/mapping.d.ts.map +1 -0
- package/dist-lib/lib/accounting/twinfield/mapping.js +109 -0
- package/dist-lib/lib/accounting/twinfield/worker.functions.d.ts +36318 -0
- package/dist-lib/lib/accounting/twinfield/worker.functions.d.ts.map +1 -0
- package/dist-lib/lib/accounting/twinfield/worker.functions.js +48 -0
- package/dist-lib/lib/accounting/twinfield/worker.server.d.ts +12 -0
- package/dist-lib/lib/accounting/twinfield/worker.server.d.ts.map +1 -0
- package/dist-lib/lib/accounting/twinfield/worker.server.js +124 -0
- package/dist-lib/lib/accounting.functions.d.ts +2286 -9
- package/dist-lib/lib/accounting.functions.d.ts.map +1 -1
- package/dist-lib/lib/customer-analytics.functions.d.ts +762 -3
- package/dist-lib/lib/customer-analytics.functions.d.ts.map +1 -1
- package/dist-lib/lib/customer-schemas.d.ts +2 -0
- package/dist-lib/lib/customer-schemas.d.ts.map +1 -1
- package/dist-lib/lib/customer-schemas.js +1 -0
- package/dist-lib/lib/customers.functions.d.ts +18241 -99
- package/dist-lib/lib/customers.functions.d.ts.map +1 -1
- package/dist-lib/lib/customers.functions.js +92 -4
- package/dist-lib/lib/delivery-locations.functions.d.ts +3048 -12
- package/dist-lib/lib/delivery-locations.functions.d.ts.map +1 -1
- package/dist-lib/lib/external-customers.functions.d.ts +3048 -12
- package/dist-lib/lib/external-customers.functions.d.ts.map +1 -1
- package/dist-lib/lib/floriday-assortment.functions.d.ts +12127 -0
- package/dist-lib/lib/floriday-assortment.functions.d.ts.map +1 -0
- package/dist-lib/lib/floriday-assortment.functions.js +213 -0
- package/dist-lib/lib/floriday-discounts.functions.d.ts +1800 -282
- package/dist-lib/lib/floriday-discounts.functions.d.ts.map +1 -1
- package/dist-lib/lib/floriday-trade-items.functions.d.ts +12111 -0
- package/dist-lib/lib/floriday-trade-items.functions.d.ts.map +1 -0
- package/dist-lib/lib/floriday-trade-items.functions.js +107 -0
- package/dist-lib/lib/local-discounts.functions.d.ts +5336 -23
- package/dist-lib/lib/local-discounts.functions.d.ts.map +1 -1
- package/dist-lib/lib/overdue-invoices.functions.d.ts +762 -3
- package/dist-lib/lib/overdue-invoices.functions.d.ts.map +1 -1
- package/dist-lib/lib/price-groups-local.functions.d.ts +84841 -0
- package/dist-lib/lib/price-groups-local.functions.d.ts.map +1 -0
- package/dist-lib/lib/price-groups-local.functions.js +148 -0
- package/dist-lib/lib/price-groups.functions.d.ts +12120 -0
- package/dist-lib/lib/price-groups.functions.d.ts.map +1 -0
- package/dist-lib/lib/price-groups.functions.js +115 -0
- package/dist-lib/lib/pricing.d.ts +24 -0
- package/dist-lib/lib/pricing.d.ts.map +1 -0
- package/dist-lib/lib/pricing.js +60 -0
- package/package.json +6 -3
|
@@ -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
|
+
}
|
package/dist-lib/index.d.ts
CHANGED
|
@@ -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
|
package/dist-lib/index.d.ts.map
CHANGED
|
@@ -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;
|
|
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 },
|