@flowselections/floriday-klanten-module 1.0.11 → 1.0.13

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 (43) hide show
  1. package/dist-lib/components/klanten/DiscountsTab.d.ts +4 -0
  2. package/dist-lib/components/klanten/DiscountsTab.d.ts.map +1 -0
  3. package/dist-lib/components/klanten/DiscountsTab.js +92 -0
  4. package/dist-lib/components/klanten/GroupedKlantDetail.d.ts.map +1 -1
  5. package/dist-lib/components/klanten/GroupedKlantDetail.js +5 -2
  6. package/dist-lib/components/klanten/KlantDetail.d.ts.map +1 -1
  7. package/dist-lib/components/klanten/KlantDetail.js +3 -2
  8. package/dist-lib/components/klanten/KlantenOverzicht.d.ts.map +1 -1
  9. package/dist-lib/components/klanten/KlantenOverzicht.js +83 -23
  10. package/dist-lib/components/klanten/LocalDiscountFormDialog.d.ts +10 -0
  11. package/dist-lib/components/klanten/LocalDiscountFormDialog.d.ts.map +1 -0
  12. package/dist-lib/components/klanten/LocalDiscountFormDialog.js +107 -0
  13. package/dist-lib/components/klanten/LocalDiscountsTab.d.ts +4 -0
  14. package/dist-lib/components/klanten/LocalDiscountsTab.d.ts.map +1 -0
  15. package/dist-lib/components/klanten/LocalDiscountsTab.js +101 -0
  16. package/dist-lib/integrations/supabase/auth-middleware.d.ts +1788 -107
  17. package/dist-lib/integrations/supabase/auth-middleware.d.ts.map +1 -1
  18. package/dist-lib/integrations/supabase/client.d.ts +1788 -107
  19. package/dist-lib/integrations/supabase/client.d.ts.map +1 -1
  20. package/dist-lib/integrations/supabase/client.server.d.ts +1788 -107
  21. package/dist-lib/integrations/supabase/client.server.d.ts.map +1 -1
  22. package/dist-lib/integrations/supabase/types.d.ts +1819 -104
  23. package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
  24. package/dist-lib/lib/accounting.functions.d.ts +5362 -319
  25. package/dist-lib/lib/accounting.functions.d.ts.map +1 -1
  26. package/dist-lib/lib/customer-analytics.functions.d.ts +1788 -107
  27. package/dist-lib/lib/customer-analytics.functions.d.ts.map +1 -1
  28. package/dist-lib/lib/customers.functions.d.ts +11767 -0
  29. package/dist-lib/lib/customers.functions.d.ts.map +1 -1
  30. package/dist-lib/lib/delivery-locations.functions.d.ts +6724 -0
  31. package/dist-lib/lib/delivery-locations.functions.d.ts.map +1 -1
  32. package/dist-lib/lib/external-customers.functions.d.ts +6724 -0
  33. package/dist-lib/lib/external-customers.functions.d.ts.map +1 -1
  34. package/dist-lib/lib/floriday-discounts.functions.d.ts +21974 -0
  35. package/dist-lib/lib/floriday-discounts.functions.d.ts.map +1 -0
  36. package/dist-lib/lib/floriday-discounts.functions.js +83 -0
  37. package/dist-lib/lib/local-discounts.functions.d.ts +76898 -0
  38. package/dist-lib/lib/local-discounts.functions.d.ts.map +1 -0
  39. package/dist-lib/lib/local-discounts.functions.js +185 -0
  40. package/dist-lib/lib/overdue-invoices.functions.d.ts +10984 -0
  41. package/dist-lib/lib/overdue-invoices.functions.d.ts.map +1 -0
  42. package/dist-lib/lib/overdue-invoices.functions.js +72 -0
  43. package/package.json +1 -1
@@ -0,0 +1,107 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useState } from "react";
3
+ import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
4
+ import { useServerFn } from "@tanstack/react-start";
5
+ import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle, Button, Input, Label, toast, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@flowselections/core";
6
+ import { upsertLocalAgreement, upsertLocalSalesStrategy, upsertLocalPriceGroup, listLocalProducts, } from "../../lib/local-discounts.functions";
7
+ export function LocalDiscountFormDialog({ open, onOpenChange, customerId, editing }) {
8
+ const qc = useQueryClient();
9
+ const listProducts = useServerFn(listLocalProducts);
10
+ const saveAgreement = useServerFn(upsertLocalAgreement);
11
+ const saveSalesStrategy = useServerFn(upsertLocalSalesStrategy);
12
+ const savePriceGroup = useServerFn(upsertLocalPriceGroup);
13
+ const { data: prodData } = useQuery({
14
+ queryKey: ["local-products"],
15
+ queryFn: () => listProducts(),
16
+ enabled: open,
17
+ });
18
+ const products = prodData?.products ?? [];
19
+ const [source, setSource] = useState("agreement");
20
+ const [productId, setProductId] = useState("");
21
+ const [basePrice, setBasePrice] = useState("");
22
+ const [netPrice, setNetPrice] = useState("");
23
+ const [discountType, setDiscountType] = useState("percentage");
24
+ const [discountValue, setDiscountValue] = useState("");
25
+ const [minQuantity, setMinQuantity] = useState("0");
26
+ const [validFrom, setValidFrom] = useState("");
27
+ const [validUntil, setValidUntil] = useState("");
28
+ const [notes, setNotes] = useState("");
29
+ const [currency] = useState("EUR");
30
+ useEffect(() => {
31
+ if (!open)
32
+ return;
33
+ if (editing) {
34
+ setSource(editing.source);
35
+ setProductId(editing.product_id ?? "");
36
+ setBasePrice(editing.base_price != null ? String(editing.base_price) : "");
37
+ setNetPrice(editing.net_price != null ? String(editing.net_price) : "");
38
+ setDiscountType(editing.discount_type ?? "percentage");
39
+ setDiscountValue(editing.discount_value != null ? String(editing.discount_value) : "");
40
+ setMinQuantity(String(editing.min_quantity ?? 0));
41
+ setValidFrom(editing.valid_from ?? "");
42
+ setValidUntil(editing.valid_until ?? "");
43
+ setNotes("");
44
+ }
45
+ else {
46
+ setSource("agreement");
47
+ setProductId("");
48
+ setBasePrice("");
49
+ setNetPrice("");
50
+ setDiscountValue("");
51
+ setDiscountType("percentage");
52
+ setMinQuantity("0");
53
+ setValidFrom("");
54
+ setValidUntil("");
55
+ setNotes("");
56
+ }
57
+ }, [open, editing]);
58
+ const save = useMutation({
59
+ mutationFn: async () => {
60
+ const common = {
61
+ id: editing?.source_id,
62
+ customer_id: customerId,
63
+ product_id: productId || null,
64
+ currency,
65
+ valid_from: validFrom || null,
66
+ valid_until: validUntil || null,
67
+ notes: notes || null,
68
+ };
69
+ if (source === "agreement") {
70
+ return saveAgreement({
71
+ data: {
72
+ ...common,
73
+ base_price: basePrice ? Number(basePrice) : null,
74
+ net_price: Number(netPrice),
75
+ min_quantity: Number(minQuantity || 0),
76
+ },
77
+ });
78
+ }
79
+ if (source === "sales_strategy") {
80
+ return saveSalesStrategy({
81
+ data: {
82
+ ...common,
83
+ discount_type: discountType,
84
+ discount_value: Number(discountValue),
85
+ min_quantity: Number(minQuantity || 0),
86
+ },
87
+ });
88
+ }
89
+ return savePriceGroup({
90
+ data: {
91
+ ...common,
92
+ discount_type: discountType,
93
+ discount_value: Number(discountValue),
94
+ min_quantity: Number(minQuantity || 0),
95
+ },
96
+ });
97
+ },
98
+ onSuccess: () => {
99
+ toast.success(editing ? "Korting bijgewerkt" : "Korting toegevoegd");
100
+ qc.invalidateQueries({ queryKey: ["local-discounts", customerId] });
101
+ onOpenChange(false);
102
+ },
103
+ onError: (err) => toast.error(err.message),
104
+ });
105
+ const canSave = (source === "agreement" ? !!netPrice : !!discountValue) && !save.isPending;
106
+ return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { className: "max-w-lg", children: [_jsx(DialogHeader, { children: _jsx(DialogTitle, { children: editing ? "Korting bewerken" : "Nieuwe korting" }) }), _jsxs("div", { className: "space-y-3", children: [_jsxs("div", { className: "space-y-1", children: [_jsx(Label, { children: "Bron" }), _jsxs(Select, { value: source, onValueChange: (v) => setSource(v), disabled: !!editing, children: [_jsx(SelectTrigger, { children: _jsx(SelectValue, {}) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "agreement", children: "Contract (vaste nettoprijs)" }), _jsx(SelectItem, { value: "sales_strategy", children: "Kortingsregel (%/bedrag)" }), _jsx(SelectItem, { value: "price_group", children: "Staffelkorting (o.b.v. hoeveelheid)" })] })] })] }), _jsxs("div", { className: "space-y-1", children: [_jsx(Label, { children: "Product" }), _jsxs(Select, { value: productId, onValueChange: setProductId, children: [_jsx(SelectTrigger, { children: _jsx(SelectValue, { placeholder: "Kies een product" }) }), _jsx(SelectContent, { children: products.map((p) => (_jsxs(SelectItem, { value: p.id, children: [p.product, p.sale_price != null ? ` — € ${Number(p.sale_price).toFixed(2)}` : ""] }, p.id))) })] })] }), source === "agreement" ? (_jsxs("div", { className: "grid grid-cols-2 gap-3", children: [_jsxs("div", { className: "space-y-1", children: [_jsx(Label, { children: "Basisprijs (optioneel)" }), _jsx(Input, { type: "number", step: "0.01", value: basePrice, onChange: (e) => setBasePrice(e.target.value) })] }), _jsxs("div", { className: "space-y-1", children: [_jsx(Label, { children: "Nettoprijs *" }), _jsx(Input, { type: "number", step: "0.01", value: netPrice, onChange: (e) => setNetPrice(e.target.value) })] })] })) : (_jsxs("div", { className: "grid grid-cols-2 gap-3", children: [_jsxs("div", { className: "space-y-1", children: [_jsx(Label, { children: "Type" }), _jsxs(Select, { value: discountType, onValueChange: (v) => setDiscountType(v), children: [_jsx(SelectTrigger, { children: _jsx(SelectValue, {}) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "percentage", children: "Percentage (%)" }), _jsx(SelectItem, { value: "amount", children: "Vast bedrag" })] })] })] }), _jsxs("div", { className: "space-y-1", children: [_jsx(Label, { children: "Waarde *" }), _jsx(Input, { type: "number", step: "0.01", value: discountValue, onChange: (e) => setDiscountValue(e.target.value) })] })] })), _jsxs("div", { className: "grid grid-cols-3 gap-3", children: [_jsxs("div", { className: "space-y-1", children: [_jsx(Label, { children: "Min. hoeveelheid" }), _jsx(Input, { type: "number", step: "1", value: minQuantity, onChange: (e) => setMinQuantity(e.target.value) })] }), _jsxs("div", { className: "space-y-1", children: [_jsx(Label, { children: "Geldig van" }), _jsx(Input, { type: "date", value: validFrom, onChange: (e) => setValidFrom(e.target.value) })] }), _jsxs("div", { className: "space-y-1", children: [_jsx(Label, { children: "Geldig tot" }), _jsx(Input, { type: "date", value: validUntil, onChange: (e) => setValidUntil(e.target.value) })] })] }), _jsxs("div", { className: "space-y-1", children: [_jsx(Label, { children: "Notities" }), _jsx(Input, { value: notes, onChange: (e) => setNotes(e.target.value) })] })] }), _jsxs(DialogFooter, { children: [_jsx(Button, { variant: "outline", onClick: () => onOpenChange(false), children: "Annuleren" }), _jsx(Button, { disabled: !canSave, onClick: () => save.mutate(), children: save.isPending ? "Opslaan…" : "Opslaan" })] })] }) }));
107
+ }
@@ -0,0 +1,4 @@
1
+ export declare function LocalDiscountsTab({ customerId }: {
2
+ customerId: string;
3
+ }): import("react").JSX.Element;
4
+ //# sourceMappingURL=LocalDiscountsTab.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LocalDiscountsTab.d.ts","sourceRoot":"","sources":["../../../src/components/klanten/LocalDiscountsTab.tsx"],"names":[],"mappings":"AAiDA,wBAAgB,iBAAiB,CAAC,EAAE,UAAU,EAAE,EAAE;IAAE,UAAU,EAAE,MAAM,CAAA;CAAE,+BA2JvE"}
@@ -0,0 +1,101 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useMemo, useState } from "react";
3
+ import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
4
+ import { useServerFn } from "@tanstack/react-start";
5
+ import { Card, CardContent, Badge, Input, Button, toast, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "@flowselections/core";
6
+ import { Percent, FileText, Users, Plus, Pencil, Trash2 } from "lucide-react";
7
+ import { listLocalCustomerDiscounts, deleteLocalDiscount, } from "../../lib/local-discounts.functions";
8
+ import { LocalDiscountFormDialog } from "./LocalDiscountFormDialog";
9
+ const SOURCE_LABEL = {
10
+ agreement: "Contract",
11
+ sales_strategy: "Kortingsregel",
12
+ price_group: "Staffel",
13
+ };
14
+ const SOURCE_ICON = {
15
+ agreement: _jsx(FileText, { className: "h-3 w-3" }),
16
+ sales_strategy: _jsx(Percent, { className: "h-3 w-3" }),
17
+ price_group: _jsx(Users, { className: "h-3 w-3" }),
18
+ };
19
+ function fmtMoney(v, currency = "EUR") {
20
+ if (v == null)
21
+ return "—";
22
+ try {
23
+ return new Intl.NumberFormat("nl-NL", { style: "currency", currency }).format(v);
24
+ }
25
+ catch {
26
+ return `${v.toFixed(2)} ${currency}`;
27
+ }
28
+ }
29
+ function fmtDiscount(row) {
30
+ if (row.discount_type == null || row.discount_value == null)
31
+ return "—";
32
+ return row.discount_type === "percentage"
33
+ ? `${row.discount_value}%`
34
+ : fmtMoney(row.discount_value, row.currency);
35
+ }
36
+ function fmtDate(v) {
37
+ if (!v)
38
+ return "—";
39
+ try {
40
+ return new Date(v).toLocaleDateString("nl-NL");
41
+ }
42
+ catch {
43
+ return v;
44
+ }
45
+ }
46
+ function isActiveOn(row, date) {
47
+ if (row.valid_from && row.valid_from > date)
48
+ return false;
49
+ if (row.valid_until && row.valid_until < date)
50
+ return false;
51
+ return true;
52
+ }
53
+ export function LocalDiscountsTab({ customerId }) {
54
+ const qc = useQueryClient();
55
+ const fn = useServerFn(listLocalCustomerDiscounts);
56
+ const del = useServerFn(deleteLocalDiscount);
57
+ const { data, isLoading, error } = useQuery({
58
+ queryKey: ["local-discounts", customerId],
59
+ queryFn: () => fn({ data: { customerId } }),
60
+ });
61
+ const [source, setSource] = useState("all");
62
+ const [search, setSearch] = useState("");
63
+ const [onlyActive, setOnlyActive] = useState(true);
64
+ const [dialogOpen, setDialogOpen] = useState(false);
65
+ const [editing, setEditing] = useState(null);
66
+ const today = new Date().toISOString().slice(0, 10);
67
+ const delMut = useMutation({
68
+ mutationFn: (row) => del({ data: { source: row.source, id: row.source_id } }),
69
+ onSuccess: () => {
70
+ toast.success("Korting verwijderd");
71
+ qc.invalidateQueries({ queryKey: ["local-discounts", customerId] });
72
+ },
73
+ onError: (err) => toast.error(err.message),
74
+ });
75
+ const filtered = useMemo(() => {
76
+ const rows = data?.rows ?? [];
77
+ return rows.filter((r) => {
78
+ if (source !== "all" && r.source !== source)
79
+ return false;
80
+ if (onlyActive && !isActiveOn(r, today))
81
+ return false;
82
+ if (search) {
83
+ const q = search.toLowerCase();
84
+ const hay = [r.product_name, r.product_id].filter(Boolean).join(" ").toLowerCase();
85
+ if (!hay.includes(q))
86
+ return false;
87
+ }
88
+ return true;
89
+ });
90
+ }, [data, source, search, onlyActive, today]);
91
+ if (isLoading)
92
+ return _jsx(Card, { children: _jsx(CardContent, { className: "pt-6", children: "Kortingen laden\u2026" }) });
93
+ if (error) {
94
+ return (_jsx(Card, { children: _jsxs(CardContent, { className: "pt-6 text-sm text-destructive", children: ["Kortingen konden niet worden geladen: ", error.message] }) }));
95
+ }
96
+ const s = data?.summary;
97
+ return (_jsxs("div", { className: "space-y-4", children: [_jsxs("div", { className: "grid gap-3 md:grid-cols-3", children: [_jsx(SummaryCard, { label: "Contracten", value: s?.agreements ?? 0, icon: _jsx(FileText, { className: "h-4 w-4" }) }), _jsx(SummaryCard, { label: "Kortingsregels", value: s?.sales_strategies ?? 0, icon: _jsx(Percent, { className: "h-4 w-4" }) }), _jsx(SummaryCard, { label: "Staffels", value: s?.price_groups ?? 0, icon: _jsx(Users, { className: "h-4 w-4" }) })] }), _jsx(Card, { children: _jsxs(CardContent, { className: "pt-6 space-y-4", children: [_jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsx(Input, { placeholder: "Zoek op product\u2026", value: search, onChange: (e) => setSearch(e.target.value), className: "max-w-xs" }), _jsxs(Select, { value: source, onValueChange: (v) => setSource(v), children: [_jsx(SelectTrigger, { className: "w-44", children: _jsx(SelectValue, { placeholder: "Bron" }) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "all", children: "Alle bronnen" }), _jsx(SelectItem, { value: "agreement", children: "Contract" }), _jsx(SelectItem, { value: "sales_strategy", children: "Kortingsregel" }), _jsx(SelectItem, { value: "price_group", children: "Staffel" })] })] }), _jsxs("label", { className: "flex items-center gap-2 text-sm text-muted-foreground", children: [_jsx("input", { type: "checkbox", checked: onlyActive, onChange: (e) => setOnlyActive(e.target.checked) }), "Alleen actief vandaag"] }), _jsxs(Button, { className: "ml-auto", size: "sm", onClick: () => { setEditing(null); setDialogOpen(true); }, children: [_jsx(Plus, { className: "h-4 w-4 mr-1" }), " Nieuwe korting"] })] }), _jsx("div", { className: "rounded-md border overflow-x-auto", children: _jsxs(Table, { children: [_jsx(TableHeader, { children: _jsxs(TableRow, { children: [_jsx(TableHead, { children: "Product" }), _jsx(TableHead, { children: "Bron" }), _jsx(TableHead, { className: "text-right", children: "Basisprijs" }), _jsx(TableHead, { className: "text-right", children: "Korting" }), _jsx(TableHead, { className: "text-right", children: "Netto" }), _jsx(TableHead, { children: "Geldig" }), _jsx(TableHead, { className: "text-right", children: "Min. hvh" }), _jsx(TableHead, { className: "w-20" })] }) }), _jsx(TableBody, { children: filtered.length === 0 ? (_jsx(TableRow, { children: _jsx(TableCell, { colSpan: 8, className: "text-center text-muted-foreground py-6", children: "Nog geen kortingen voor deze klant." }) })) : filtered.map((r) => (_jsxs(TableRow, { children: [_jsx(TableCell, { className: "font-medium", children: r.product_name ?? "—" }), _jsx(TableCell, { children: _jsxs(Badge, { variant: "secondary", className: "gap-1", children: [SOURCE_ICON[r.source], SOURCE_LABEL[r.source]] }) }), _jsx(TableCell, { className: "text-right tabular-nums", children: fmtMoney(r.base_price, r.currency) }), _jsx(TableCell, { className: "text-right tabular-nums", children: fmtDiscount(r) }), _jsx(TableCell, { className: "text-right tabular-nums font-medium", children: fmtMoney(r.net_price, r.currency) }), _jsxs(TableCell, { className: "text-sm text-muted-foreground", children: [fmtDate(r.valid_from), " \u2013 ", fmtDate(r.valid_until)] }), _jsx(TableCell, { className: "text-right tabular-nums", children: r.min_quantity || "—" }), _jsx(TableCell, { className: "text-right", children: _jsxs("div", { className: "flex gap-1 justify-end", children: [_jsx(Button, { variant: "ghost", size: "icon", onClick: () => { setEditing(r); setDialogOpen(true); }, children: _jsx(Pencil, { className: "h-3.5 w-3.5" }) }), _jsx(Button, { variant: "ghost", size: "icon", onClick: () => delMut.mutate(r), children: _jsx(Trash2, { className: "h-3.5 w-3.5" }) })] }) })] }, `${r.source}-${r.source_id}`))) })] }) })] }) }), _jsx(LocalDiscountFormDialog, { open: dialogOpen, onOpenChange: setDialogOpen, customerId: customerId, editing: editing })] }));
98
+ }
99
+ function SummaryCard({ label, value, icon, }) {
100
+ return (_jsx(Card, { children: _jsxs(CardContent, { className: "p-4", children: [_jsxs("div", { className: "flex items-center gap-2 text-xs uppercase text-muted-foreground", children: [icon, label] }), _jsx("div", { className: "text-2xl font-semibold mt-1", children: value })] }) }));
101
+ }