@flowselections/floriday-klanten-module 1.0.45 → 1.0.46
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist-lib/components/klanten/KlantenOverzicht.d.ts.map +1 -1
- package/dist-lib/components/klanten/KlantenOverzicht.js +6 -5
- package/dist-lib/components/klanten/NieuweKlant.d.ts.map +1 -1
- package/dist-lib/components/klanten/NieuweKlant.js +10 -7
- package/dist-lib/integrations/supabase/types.d.ts +1344 -0
- package/dist-lib/integrations/supabase/types.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 +2 -0
- package/dist-lib/lib/customers.functions.d.ts +34 -0
- package/dist-lib/lib/customers.functions.d.ts.map +1 -1
- package/dist-lib/lib/customers.functions.js +43 -2
- package/dist-lib/lib/twinfield-adopt.functions.d.ts +2 -0
- package/dist-lib/lib/twinfield-adopt.functions.d.ts.map +1 -1
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"KlantenOverzicht.d.ts","sourceRoot":"","sources":["../../../src/components/klanten/KlantenOverzicht.tsx"],"names":[],"mappings":"AAgEA,wBAAgB,gBAAgB,
|
|
1
|
+
{"version":3,"file":"KlantenOverzicht.d.ts","sourceRoot":"","sources":["../../../src/components/klanten/KlantenOverzicht.tsx"],"names":[],"mappings":"AAgEA,wBAAgB,gBAAgB,gCAw2B/B"}
|
|
@@ -472,7 +472,7 @@ export function KlantenOverzicht() {
|
|
|
472
472
|
});
|
|
473
473
|
const toggleSort = (col) => setSort((s) => (s.col === col ? { col, dir: s.dir === "asc" ? "desc" : "asc" } : { col, dir: "asc" }));
|
|
474
474
|
const lastUpdated = externalData?.last_fetched_at ?? null;
|
|
475
|
-
return (_jsx(TooltipPrimitive.Provider, { delayDuration: 200, children: _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: "Klanten" }), _jsx("p", { className: "text-sm text-muted-foreground mt-1", children: "Beheer en doorzoek al je klanten." })] }), _jsxs("div", { className: "flex gap-2", children: [_jsxs(Button, { variant: "outline", onClick: () => goTo({ to: "/klanten/import" }), children: [_jsx(Upload, { className: "h-4 w-4 mr-2" }), " Importeren"] }), _jsxs(Button, { onClick: () => goTo({ to: "/klanten/nieuw" }), children: [_jsx(Plus, { className: "h-4 w-4 mr-2" }), " Nieuwe klant"] })] })] }), loadError && (_jsxs("div", { className: "flex flex-wrap items-center gap-3 rounded-lg border border-destructive/30 bg-destructive/5 px-4 py-3", children: [_jsx(AlertCircle, { className: "h-5 w-5 text-destructive shrink-0" }), _jsxs("div", { className: "text-sm", children: [_jsx("div", { className: "font-medium", children: isBackendUnavailable(loadError)
|
|
475
|
+
return (_jsx(TooltipPrimitive.Provider, { delayDuration: 200, children: _jsxs("main", { className: "p-4 sm:p-6 space-y-5 min-w-0 max-w-full overflow-x-hidden", children: [_jsxs("div", { className: "flex items-start justify-between gap-4 flex-wrap", children: [_jsxs("div", { className: "min-w-0", children: [_jsx("h1", { className: "text-2xl font-bold tracking-tight", children: "Klanten" }), _jsx("p", { className: "text-sm text-muted-foreground mt-1", children: "Beheer en doorzoek al je klanten." })] }), _jsxs("div", { className: "flex flex-wrap gap-2", children: [_jsxs(Button, { variant: "outline", onClick: () => goTo({ to: "/klanten/import" }), children: [_jsx(Upload, { className: "h-4 w-4 mr-2" }), " Importeren"] }), _jsxs(Button, { onClick: () => goTo({ to: "/klanten/nieuw" }), children: [_jsx(Plus, { className: "h-4 w-4 mr-2" }), " Nieuwe klant"] })] })] }), loadError && (_jsxs("div", { className: "flex flex-wrap items-center gap-3 rounded-lg border border-destructive/30 bg-destructive/5 px-4 py-3", children: [_jsx(AlertCircle, { className: "h-5 w-5 text-destructive shrink-0" }), _jsxs("div", { className: "text-sm", children: [_jsx("div", { className: "font-medium", children: isBackendUnavailable(loadError)
|
|
476
476
|
? "De database is tijdelijk niet bereikbaar"
|
|
477
477
|
: "De klanten konden niet geladen worden" }), _jsx("div", { className: "text-muted-foreground", children: isBackendUnavailable(loadError)
|
|
478
478
|
? "Je bent niet uitgelogd. Probeer het over een paar minuten opnieuw."
|
|
@@ -504,15 +504,16 @@ export function KlantenOverzicht() {
|
|
|
504
504
|
setView("all");
|
|
505
505
|
setPage(1);
|
|
506
506
|
} }) }) })) : pageRows.map((c) => {
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
507
|
+
// Handelsnaam voorop (zoals Floriday en de verzendbrief); officiële naam klein eronder.
|
|
508
|
+
const hasTrade = !!c.commercial_name && c.commercial_name !== c.company_name;
|
|
509
|
+
const primaryName = hasTrade ? c.commercial_name : c.company_name;
|
|
510
|
+
const secondary = hasTrade ? c.company_name : c.city ?? c.email ?? null;
|
|
510
511
|
return (_jsxs(TableRow, { className: "cursor-pointer hover:bg-muted/50", onClick: () => c.id
|
|
511
512
|
? goTo({ to: "/klanten/$id", params: { id: c.id } })
|
|
512
513
|
: goTo({
|
|
513
514
|
to: "/klanten/groep/$groupKey",
|
|
514
515
|
params: { groupKey: c.group_key },
|
|
515
|
-
}), children: [_jsx(TableCell, { className: "py-3", children: _jsxs("div", { className: "flex items-center gap-3", children: [_jsx(CustomerAvatar, { logoUrl: c.logo_url, name: c.company_name, size: "sm" }), _jsxs("div", { className: "min-w-0", children: [_jsxs("div", { className: "font-medium truncate flex items-center gap-2", children: [_jsx("span", { className: "truncate", children:
|
|
516
|
+
}), children: [_jsx(TableCell, { className: "py-3", children: _jsxs("div", { className: "flex items-center gap-3", children: [_jsx(CustomerAvatar, { logoUrl: c.logo_url, name: c.company_name, size: "sm" }), _jsxs("div", { className: "min-w-0", children: [_jsxs("div", { className: "font-medium truncate flex items-center gap-2", children: [_jsx("span", { className: "truncate", children: primaryName }), c.overdue && c.overdue.count > 0 && (_jsxs("span", { title: `${c.overdue.count} openstaande factu${c.overdue.count === 1 ? "ur" : "ren"} · oudste vervallen op ${c.overdue.oldest_due}`, className: "inline-flex items-center gap-1 rounded-full bg-destructive/10 text-destructive px-2 py-0.5 text-[11px] font-medium shrink-0", children: [_jsx(AlertCircle, { className: "h-3 w-3" }), " ", formatEuro(c.overdue.total), " achterstallig"] }))] }), secondary && _jsx("div", { className: "text-xs text-muted-foreground truncate", children: secondary })] })] }) }), _jsx(TableCell, { className: "py-3", children: c.contact_person || c.email ? (_jsxs("div", { className: "min-w-0", children: [c.contact_person && _jsx("div", { className: "text-sm truncate", children: c.contact_person }), c.email && (_jsxs("a", { href: `mailto:${c.email}`, onClick: (e) => e.stopPropagation(), className: "text-xs text-muted-foreground inline-flex items-center gap-1 hover:underline truncate", children: [_jsx(Mail, { className: "h-3 w-3" }), " ", c.email] }))] })) : _jsx("span", { className: "text-muted-foreground", children: "\u2014" }) }), _jsx(TableCell, { className: "py-3", children: _jsx(PriceGroupBadges, { groups: c.price_groups }) }), _jsx(TableCell, { className: "py-3", children: _jsx(ChannelBadges, { providers: c.providers }) }), _jsx(TableCell, { className: "py-3", children: _jsx("span", { className: `inline-flex items-center rounded-full border px-2 py-0.5 text-xs font-medium ${paymentToneClasses(c.payment_method.tone)}`, children: c.payment_method.label }) }), _jsx(TableCell, { className: "py-3 text-sm whitespace-nowrap", children: c.revenue && c.revenue.count > 0 ? (_jsxs("span", { title: `${c.revenue.count} transactie${c.revenue.count === 1 ? "" : "s"} · laatste op ${formatDateNl(c.revenue.last_date)}`, children: [_jsx("span", { className: "font-medium", children: formatEuro(c.revenue.amount) }), _jsxs("span", { className: "block text-xs text-muted-foreground", children: ["laatst ", formatDateNl(c.revenue.last_date)] })] })) : (_jsx("span", { className: "text-muted-foreground", children: "Geen omzet" })) }), _jsx(TableCell, { className: "py-3 text-sm", children: c.country ? (_jsxs("span", { className: "inline-flex items-center gap-1.5", children: [_jsx("span", { className: "text-base", "aria-hidden": "true", children: countryNameToFlag(c.country) ?? "🌐" }), _jsx("span", { children: formatCountry(c.country) })] })) : _jsx("span", { className: "text-muted-foreground", children: "\u2014" }) }), _jsx(TableCell, { className: "py-3", children: _jsxs("div", { className: "flex items-center justify-between gap-2", children: [_jsx(StatusPill, { active: c.is_active, missing: c.missing }), c.id ? (_jsxs(Button, { variant: "ghost", size: "sm", className: "h-8 w-8 p-0", title: "Klant bewerken", onClick: (e) => {
|
|
516
517
|
e.stopPropagation();
|
|
517
518
|
setEditTarget({ customerId: c.id });
|
|
518
519
|
}, children: [_jsx(Pencil, { className: "h-4 w-4" }), _jsx("span", { className: "sr-only", children: "Bewerken" })] })) : c.twinfield_code ? (_jsxs(Button, { variant: "ghost", size: "sm", className: "h-8 w-8 p-0", title: "Twinfield-klant overnemen en bewerken", onClick: (e) => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NieuweKlant.d.ts","sourceRoot":"","sources":["../../../src/components/klanten/NieuweKlant.tsx"],"names":[],"mappings":"AAoBA,wBAAgB,WAAW,
|
|
1
|
+
{"version":3,"file":"NieuweKlant.d.ts","sourceRoot":"","sources":["../../../src/components/klanten/NieuweKlant.tsx"],"names":[],"mappings":"AAoBA,wBAAgB,WAAW,gCAgJ1B"}
|
|
@@ -1,11 +1,11 @@
|
|
|
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 { friendlyError } from "../../lib/errors";
|
|
3
3
|
import { useNavigate } from "@tanstack/react-router";
|
|
4
4
|
import { useState } from "react";
|
|
5
|
-
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
|
5
|
+
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
|
6
6
|
import { useServerFn } from "@tanstack/react-start";
|
|
7
7
|
import { toast, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, } from "@flowselections/core";
|
|
8
|
-
import { createCustomer } from "../../lib/customers.functions.js";
|
|
8
|
+
import { createCustomer, getActiveTwinfieldAdministration } from "../../lib/customers.functions.js";
|
|
9
9
|
import { upsertDeliveryLocation } from "../../lib/delivery-locations.functions.js";
|
|
10
10
|
import { KlantFormulier, emptyKlantFormValues, } from "./KlantFormulier.js";
|
|
11
11
|
import { useCustomerDefaults, MISSING_DEFAULT_MESSAGE } from "./useCustomerDefaults.js";
|
|
@@ -19,6 +19,9 @@ export function NieuweKlant() {
|
|
|
19
19
|
const [pendingData, setPendingData] = useState(null);
|
|
20
20
|
const [locations, setLocations] = useState([]);
|
|
21
21
|
const cd = useCustomerDefaults();
|
|
22
|
+
const [twCreate, setTwCreate] = useState(true);
|
|
23
|
+
const getAdmin = useServerFn(getActiveTwinfieldAdministration);
|
|
24
|
+
const admin = useQuery({ queryKey: ["twinfield-active-admin"], queryFn: () => getAdmin() });
|
|
22
25
|
const mut = useMutation({
|
|
23
26
|
mutationFn: async (data) => {
|
|
24
27
|
const r = await create({ data });
|
|
@@ -62,9 +65,9 @@ export function NieuweKlant() {
|
|
|
62
65
|
onError: (e) => toast.error(friendlyError(e, "Klant aanmaken mislukt")),
|
|
63
66
|
});
|
|
64
67
|
const onSubmit = (values) => {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
mut.mutate(
|
|
68
|
+
const v = { ...values, twinfield_create: twCreate };
|
|
69
|
+
setPendingData(v);
|
|
70
|
+
mut.mutate(v);
|
|
68
71
|
};
|
|
69
72
|
const forceCreate = () => {
|
|
70
73
|
if (!pendingData)
|
|
@@ -86,5 +89,5 @@ export function NieuweKlant() {
|
|
|
86
89
|
const notice = cd.isAdmin && d && d.missing.length > 0
|
|
87
90
|
? `${MISSING_DEFAULT_MESSAGE}: ${d.missing.map((m) => LABELS[m]).join(", ")}. Leeg gelaten velden gaan niet naar Twinfield; Twinfield gebruikt dan zijn eigen standaard.`
|
|
88
91
|
: undefined;
|
|
89
|
-
return (_jsx(KlantFormulier, { notice: notice, title: "Nieuwe klant", description: "Vul de gegevens in om een nieuwe klant aan te maken. Het debiteurnummer wordt automatisch gegenereerd.", submitLabel: "Klant aanmaken", submitting: mut.isPending, defaultValues: defaultValues, onSubmit: onSubmit, onCancel: () => navigate({ to: "/klanten" }), extraSections: _jsx(NieuweKlantLocaties, { locations: locations, onChange: setLocations, defaultCountry: d?.country ?? "" }), extra: _jsx(AlertDialog, { open: dupes.length > 0, onOpenChange: (o) => !o && setDupes([]), children: _jsxs(AlertDialogContent, { children: [_jsxs(AlertDialogHeader, { children: [_jsx(AlertDialogTitle, { children: "Mogelijke duplicaten gevonden" }), _jsx(AlertDialogDescription, { children: _jsx("ul", { className: "list-disc pl-5 space-y-1 mt-2", children: dupes.map((d) => (_jsxs("li", { children: [_jsx("strong", { children: d.company_name }), " \u2014 ", d.reason] }, d.id + d.reason))) }) })] }), _jsxs(AlertDialogFooter, { children: [_jsx(AlertDialogCancel, { children: "Annuleren" }), _jsx(AlertDialogAction, { onClick: forceCreate, children: "Toch aanmaken" })] })] }) }) }));
|
|
92
|
+
return (_jsx(KlantFormulier, { notice: notice, title: "Nieuwe klant", description: "Vul de gegevens in om een nieuwe klant aan te maken. Het debiteurnummer wordt automatisch gegenereerd.", submitLabel: "Klant aanmaken", submitting: mut.isPending, defaultValues: defaultValues, onSubmit: onSubmit, onCancel: () => navigate({ to: "/klanten" }), extraSections: _jsxs(_Fragment, { children: [_jsx(NieuweKlantLocaties, { locations: locations, onChange: setLocations, defaultCountry: d?.country ?? "" }), _jsx("div", { className: "rounded-md border px-4 py-3 text-sm", children: admin.data?.active ? (_jsxs("label", { className: "flex items-start gap-2 cursor-pointer", children: [_jsx("input", { type: "checkbox", className: "mt-1", checked: twCreate, onChange: (e) => setTwCreate(e.target.checked) }), _jsxs("span", { children: ["Ook als debiteur aanmaken in Twinfield", _jsxs("span", { className: "block text-xs text-muted-foreground", children: ["Administratie: ", admin.data.name ? `${admin.data.name} (${admin.data.code})` : admin.data.code] })] })] })) : admin.isLoading ? (_jsx("span", { className: "text-muted-foreground", children: "Twinfield-koppeling controleren\u2026" })) : (_jsx("span", { className: "text-muted-foreground", children: "Er is geen actieve Twinfield-koppeling. De klant wordt alleen in het ERP aangemaakt; er gaat niets naar Twinfield." })) })] }), extra: _jsx(AlertDialog, { open: dupes.length > 0, onOpenChange: (o) => !o && setDupes([]), children: _jsxs(AlertDialogContent, { children: [_jsxs(AlertDialogHeader, { children: [_jsx(AlertDialogTitle, { children: "Mogelijke duplicaten gevonden" }), _jsx(AlertDialogDescription, { children: _jsx("ul", { className: "list-disc pl-5 space-y-1 mt-2", children: dupes.map((d) => (_jsxs("li", { children: [_jsx("strong", { children: d.company_name }), " \u2014 ", d.reason] }, d.id + d.reason))) }) })] }), _jsxs(AlertDialogFooter, { children: [_jsx(AlertDialogCancel, { children: "Annuleren" }), _jsx(AlertDialogAction, { onClick: forceCreate, children: "Toch aanmaken" })] })] }) }) }));
|
|
90
93
|
}
|