@flowselections/klanten-installatie-techniek-planner 1.0.29 → 1.0.31
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/KlantenDetailPage.d.ts.map +1 -1
- package/dist-lib/components/KlantenDetailPage.js +1 -1
- package/dist-lib/components/KlantenListPage.d.ts.map +1 -1
- package/dist-lib/components/KlantenListPage.js +1 -1
- package/dist-lib/components/klanten/KlantFormSections.d.ts.map +1 -1
- package/dist-lib/components/klanten/KlantFormSections.js +2 -2
- package/dist-lib/components/klanten/useKlantForm.d.ts.map +1 -1
- package/dist-lib/components/klanten/useKlantForm.js +12 -1
- package/dist-lib/integrations/supabase/types.d.ts +557 -0
- package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
- package/dist-lib/lib/itkClient.d.ts +2 -0
- package/dist-lib/lib/itkClient.d.ts.map +1 -1
- package/dist-lib/lib/klantRules.d.ts +6 -0
- package/dist-lib/lib/klantRules.d.ts.map +1 -1
- package/dist-lib/lib/klantRules.js +10 -0
- package/dist-lib/lib/klantRules.test.js +17 -0
- package/dist-lib/lib/statusConfig.d.ts +6 -0
- package/dist-lib/lib/statusConfig.d.ts.map +1 -1
- package/dist-lib/lib/statusConfig.js +3 -0
- package/package.json +10 -5
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"KlantenDetailPage.d.ts","sourceRoot":"","sources":["../../src/components/KlantenDetailPage.tsx"],"names":[],"mappings":"AAwBA,wBAAgB,iBAAiB,
|
|
1
|
+
{"version":3,"file":"KlantenDetailPage.d.ts","sourceRoot":"","sources":["../../src/components/KlantenDetailPage.tsx"],"names":[],"mappings":"AAwBA,wBAAgB,iBAAiB,gCAwLhC"}
|
|
@@ -29,5 +29,5 @@ export function KlantenDetailPage() {
|
|
|
29
29
|
if (loading) {
|
|
30
30
|
return (_jsx("main", { className: "p-6", children: _jsx("p", { className: "text-muted-foreground", children: "Laden\u2026" }) }));
|
|
31
31
|
}
|
|
32
|
-
return (_jsxs("main", { className: "p-6 space-y-6 max-w-5xl", children: [_jsxs("div", { className: "flex items-center justify-between gap-3", children: [_jsxs("div", { className: "flex items-center gap-3", children: [_jsx(Link, { to: "/installatietechniek-klanten-planner", children: _jsxs(Button, { variant: "ghost", size: "sm", className: "gap-2", children: [_jsx(ArrowLeft, { className: "w-4 h-4" }), "Terug"] }) }), _jsx("h1", { className: "text-2xl font-bold text-foreground", children: isNew ? "Nieuwe klant" : form.name || "Klant" }), customerNumber && (_jsx("span", { className: "font-mono text-xs text-muted-foreground bg-muted px-2 py-1 rounded", children: customerNumber }))] }), _jsxs("div", { className: "flex gap-2", children: [!isNew && (_jsxs(Button, { variant: "outline", onClick: remove, className: "gap-2 text-destructive", children: [_jsx(Trash2, { className: "w-4 h-4" }), "Verwijderen"] })), _jsxs(Button, { onClick: save, disabled: saving, className: "gap-2", children: [_jsx(Save, { className: "w-4 h-4" }), saving ? "Opslaan…" : "Opslaan"] })] })] }), !isNew && (_jsxs("nav", { className: "flex flex-wrap gap-3 text-xs text-muted-foreground border-b pb-3", children: [_jsx("a", { href: "#gegevens", className: "hover:text-foreground transition-colors", children: "Gegevens" }), form.segment === "Business" && (_jsx("a", { href: "#contactpersonen", className: "hover:text-foreground transition-colors", children: "Contactpersonen" })), _jsx("a", { href: "#werklocaties", className: "hover:text-foreground transition-colors", children: "Werklocaties" }), fields.length > 0 && (_jsx("a", { href: "#aanvullend", className: "hover:text-foreground transition-colors", children: "Aanvullend" })), _jsx("a", { href: "#werkbonnen", className: "hover:text-foreground transition-colors", children: "Werkbonnen" }), _jsx("a", { href: "#notities", className: "hover:text-foreground transition-colors", children: "Notities" })] })), !isNew && _jsx(KlantRisicoBannerById, { customerId: id, refreshKey: riskKey }), !isNew && _jsx(KlantStatsCard, { customerId: id }), _jsxs(Card, { id: "gegevens", children: [_jsx(CardHeader, { children: _jsx(CardTitle, { className: "text-base", children: "Segment" }) }), _jsx(CardContent, { children: _jsx(SegmentToggle, { segment: form.segment, onChange: (s) => setForm((f) => ({ ...f, segment: s })) }) })] }), _jsxs(Card, { children: [_jsx(CardHeader, { children: _jsx(CardTitle, { className: "text-base", children: "Algemene gegevens" }) }), _jsx(CardContent, { children: _jsx(AlgemeneGegevensFields, { form: form, setForm: setForm, isNew: isNew }) })] }), form.segment === "Business" && (_jsxs(Card, { id: "contactpersonen", children: [_jsx(CardHeader, { children: _jsx(CardTitle, { className: "text-base", children: "Contactpersonen" }) }), _jsx(CardContent, { children: _jsx(ContactenFields, { contacts: contacts, addContact: addContact, updateContact: updateContact, removeContact: removeContact }) })] })), _jsxs(Card, { id: "werklocaties", children: [_jsx(CardHeader, { children: _jsxs(CardTitle, { className: "text-base flex items-center gap-2", children: [_jsx(MapPin, { className: "w-4 h-4" }), "Werklocaties"] }) }), _jsx(CardContent, { children: _jsx(LocatiesFields, { locations: locations, addLocation: addLocation, updateLocation: updateLocation, removeLocation: removeLocation }) })] }), fields.length > 0 && (_jsxs(Card, { id: "aanvullend", children: [_jsx(CardHeader, { children: _jsx(CardTitle, { className: "text-base", children: "Aanvullende velden" }) }), _jsx(CardContent, { children: _jsx(DynamicFieldsGrid, { fields: fields, values: values, setValues: setValues }) })] })), !isNew && (_jsxs(_Fragment, { children: [_jsx(KlantGeschiedenisCard, { customerId: id }), _jsx(KlantNotitiesCard, { customerId: id, onStatusChanged: () => setRiskKey((k) => k + 1) })] })), _jsx(Separator, {}), _jsx("div", { className: "flex justify-end", children: _jsxs(Button, { onClick: save, disabled: saving, className: "gap-2", children: [_jsx(Save, { className: "w-4 h-4" }), saving ? "Opslaan…" : "Opslaan"] }) })] }));
|
|
32
|
+
return (_jsxs("main", { className: "p-6 space-y-6 max-w-5xl", children: [_jsxs("div", { className: "flex items-center justify-between gap-3", children: [_jsxs("div", { className: "flex items-center gap-3", children: [_jsx(Link, { to: "/installatietechniek-klanten-planner", children: _jsxs(Button, { variant: "ghost", size: "sm", className: "gap-2", children: [_jsx(ArrowLeft, { className: "w-4 h-4" }), "Terug"] }) }), _jsx("h1", { className: "text-2xl font-bold text-foreground", children: isNew ? "Nieuwe klant" : form.name || "Klant" }), customerNumber && (_jsx("span", { className: "font-mono text-xs text-muted-foreground bg-muted px-2 py-1 rounded", children: customerNumber })), !isNew && form.vat_reverse_charge && (_jsx("span", { className: "text-xs rounded bg-muted px-2 py-1 text-muted-foreground", children: "Btw verlegd" }))] }), _jsxs("div", { className: "flex gap-2", children: [!isNew && (_jsxs(Button, { variant: "outline", onClick: remove, className: "gap-2 text-destructive", children: [_jsx(Trash2, { className: "w-4 h-4" }), "Verwijderen"] })), _jsxs(Button, { onClick: save, disabled: saving, className: "gap-2", children: [_jsx(Save, { className: "w-4 h-4" }), saving ? "Opslaan…" : "Opslaan"] })] })] }), !isNew && (_jsxs("nav", { className: "flex flex-wrap gap-3 text-xs text-muted-foreground border-b pb-3", children: [_jsx("a", { href: "#gegevens", className: "hover:text-foreground transition-colors", children: "Gegevens" }), form.segment === "Business" && (_jsx("a", { href: "#contactpersonen", className: "hover:text-foreground transition-colors", children: "Contactpersonen" })), _jsx("a", { href: "#werklocaties", className: "hover:text-foreground transition-colors", children: "Werklocaties" }), fields.length > 0 && (_jsx("a", { href: "#aanvullend", className: "hover:text-foreground transition-colors", children: "Aanvullend" })), _jsx("a", { href: "#werkbonnen", className: "hover:text-foreground transition-colors", children: "Werkbonnen" }), _jsx("a", { href: "#notities", className: "hover:text-foreground transition-colors", children: "Notities" })] })), !isNew && _jsx(KlantRisicoBannerById, { customerId: id, refreshKey: riskKey }), !isNew && _jsx(KlantStatsCard, { customerId: id }), _jsxs(Card, { id: "gegevens", children: [_jsx(CardHeader, { children: _jsx(CardTitle, { className: "text-base", children: "Segment" }) }), _jsx(CardContent, { children: _jsx(SegmentToggle, { segment: form.segment, onChange: (s) => setForm((f) => ({ ...f, segment: s })) }) })] }), _jsxs(Card, { children: [_jsx(CardHeader, { children: _jsx(CardTitle, { className: "text-base", children: "Algemene gegevens" }) }), _jsx(CardContent, { children: _jsx(AlgemeneGegevensFields, { form: form, setForm: setForm, isNew: isNew }) })] }), form.segment === "Business" && (_jsxs(Card, { id: "contactpersonen", children: [_jsx(CardHeader, { children: _jsx(CardTitle, { className: "text-base", children: "Contactpersonen" }) }), _jsx(CardContent, { children: _jsx(ContactenFields, { contacts: contacts, addContact: addContact, updateContact: updateContact, removeContact: removeContact }) })] })), _jsxs(Card, { id: "werklocaties", children: [_jsx(CardHeader, { children: _jsxs(CardTitle, { className: "text-base flex items-center gap-2", children: [_jsx(MapPin, { className: "w-4 h-4" }), "Werklocaties"] }) }), _jsx(CardContent, { children: _jsx(LocatiesFields, { locations: locations, addLocation: addLocation, updateLocation: updateLocation, removeLocation: removeLocation }) })] }), fields.length > 0 && (_jsxs(Card, { id: "aanvullend", children: [_jsx(CardHeader, { children: _jsx(CardTitle, { className: "text-base", children: "Aanvullende velden" }) }), _jsx(CardContent, { children: _jsx(DynamicFieldsGrid, { fields: fields, values: values, setValues: setValues }) })] })), !isNew && (_jsxs(_Fragment, { children: [_jsx(KlantGeschiedenisCard, { customerId: id }), _jsx(KlantNotitiesCard, { customerId: id, onStatusChanged: () => setRiskKey((k) => k + 1) })] })), _jsx(Separator, {}), _jsx("div", { className: "flex justify-end", children: _jsxs(Button, { onClick: save, disabled: saving, className: "gap-2", children: [_jsx(Save, { className: "w-4 h-4" }), saving ? "Opslaan…" : "Opslaan"] }) })] }));
|
|
33
33
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"KlantenListPage.d.ts","sourceRoot":"","sources":["../../src/components/KlantenListPage.tsx"],"names":[],"mappings":"AAqBA,OAAO,EAAqB,KAAK,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAY3E,wBAAgB,eAAe,
|
|
1
|
+
{"version":3,"file":"KlantenListPage.d.ts","sourceRoot":"","sources":["../../src/components/KlantenListPage.tsx"],"names":[],"mappings":"AAqBA,OAAO,EAAqB,KAAK,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAY3E,wBAAgB,eAAe,gCA8O9B;AAED,wBAAgB,YAAY,CAAC,EAAE,OAAO,EAAE,EAAE;IAAE,OAAO,EAAE,eAAe,CAAA;CAAE,+BAkBrE"}
|
|
@@ -97,7 +97,7 @@ export function KlantenListPage() {
|
|
|
97
97
|
const filtered = customers;
|
|
98
98
|
const pageCount = Math.max(1, Math.ceil(total / PAGE_SIZE));
|
|
99
99
|
const statCell = (id, pick) => stats[id] ? pick(stats[id]) : statsFailed ? "n.b." : "—";
|
|
100
|
-
return (_jsxs("main", { className: "p-6 space-y-6", children: [_jsxs("div", { className: "grid grid-cols-[minmax(0,1fr)_auto] items-center gap-x-4 gap-y-3 sm:flex sm:flex-wrap sm:items-center sm:gap-6", children: [_jsxs("div", { className: "min-w-0", children: [_jsx("h1", { className: "truncate text-2xl font-bold text-foreground", children: "Klanten" }), _jsx("p", { className: "text-sm text-muted-foreground", children: "Beheer al je zakelijke en particuliere klanten" })] }), top.length > 0 && (_jsx("div", { className: "col-span-2 justify-self-start sm:col-span-1 sm:self-center sm:justify-self-auto", children: _jsx(KlantenPodium, { items: top, onSelect: openCustomer }) })), _jsxs(Button, { className: "col-start-2 row-start-1 shrink-0 justify-self-end gap-2 sm:col-start-auto sm:row-start-auto sm:ml-auto sm:justify-self-auto", onClick: () => openCustomer(null), children: [_jsx(Plus, { className: "w-4 h-4" }), "Nieuwe klant"] })] }), _jsx(Card, { children: _jsxs(CardContent, { className: "p-4 space-y-4", children: [_jsxs("div", { className: "flex gap-3 flex-wrap", children: [_jsxs("div", { className: "relative flex-1 min-w-[240px]", children: [_jsx(Search, { className: "w-4 h-4 absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground" }), _jsx(Input, { placeholder: "Zoek op klantnr, naam, e-mail of plaats\u2026", value: search, onChange: (e) => setSearch(e.target.value), className: "pl-9" })] }), _jsxs(Select, { value: segmentFilter, onValueChange: (v) => setSegmentFilter(v), children: [_jsx(SelectTrigger, { className: "w-[180px]", children: _jsx(SelectValue, {}) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "all", children: "Alle segmenten" }), _jsx(SelectItem, { value: "Business", children: "Zakelijk" }), _jsx(SelectItem, { value: "Private", children: "Particulier" })] })] })] }), _jsx("div", { className: "rounded-md border", children: _jsxs(Table, { children: [_jsx(TableHeader, { children: _jsxs(TableRow, { children: [_jsx(TableHead, { className: "w-[130px]", children: "Klantnr." }), _jsx(TableHead, { className: "w-[30%]", children: "Naam" }), _jsx(TableHead, { children: "Segment" }), _jsx(TableHead, { children: "E-mail" }), _jsx(TableHead, { children: "Telefoon" }), _jsx(TableHead, { children: "Plaats" }), _jsx(TableHead, { className: "text-right", children: "Omzet" }), _jsx(TableHead, { className: "text-right", children: "Klussen" })] }) }), _jsx(TableBody, { children: loading ? (_jsx(TableRow, { children: _jsx(TableCell, { colSpan: 8, className: "text-center text-muted-foreground py-8", children: "Laden\u2026" }) })) : filtered.length === 0 ? (_jsx(TableRow, { children: _jsx(TableCell, { colSpan: 8, className: "text-center text-muted-foreground py-8", children: loadError ? "Klanten konden niet geladen worden" : "Geen klanten gevonden" }) })) : (filtered.map((c) => (_jsxs(TableRow, { className: "hover:bg-muted/40 cursor-pointer", onClick: () => openCustomer(c.id), children: [_jsx(TableCell, { className: "font-mono text-xs text-muted-foreground", children: c.customer_number ?? "—" }), _jsxs(TableCell, { className: "font-medium text-foreground", children: [c.name, _jsx(KlantRisicoBadge, { status: c.risk_status })] }), _jsx(TableCell, { children: _jsx(SegmentBadge, { segment: c.segment }) }), _jsx(TableCell, { className: "text-sm", children: c.general_email ?? "—" }), _jsx(TableCell, { className: "text-sm", children: c.general_phone ?? "—" }), _jsx(TableCell, { className: "text-sm", children: c.billing_city ?? "—" }), _jsx(TableCell, { className: "text-sm text-right tabular-nums", children: statCell(c.id, (s) => formatEuro(s.omzetCents)) }), _jsx(TableCell, { className: "text-sm text-right tabular-nums", children: statCell(c.id, (s) => s.klussen) })] }, c.id)))) })] }) }), _jsxs("div", { className: "flex items-center justify-between gap-3 text-sm text-muted-foreground", children: [_jsxs("span", { children: [total, " ", total === 1 ? "klant" : "klanten", statsFailed && " · omzet/klussen niet beschikbaar"] }), pageCount > 1 && (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Button, { variant: "outline", size: "sm", disabled: page === 0 || loading, onClick: () => setPage((p) => p - 1), children: "Vorige" }), _jsxs("span", { className: "tabular-nums", children: [page + 1, " / ", pageCount] }), _jsx(Button, { variant: "outline", size: "sm", disabled: page + 1 >= pageCount || loading, onClick: () => setPage((p) => p + 1), children: "Volgende" })] }))] })] }) }), _jsx(KlantenTrendCard, {}), _jsx(KlantDialog, { open: dialogOpen, onOpenChange: setDialogOpen, customerId: selectedId, onChanged: () => { void load(); void loadTop(); } })] }));
|
|
100
|
+
return (_jsxs("main", { className: "p-6 space-y-6", children: [_jsxs("div", { className: "grid grid-cols-[minmax(0,1fr)_auto] items-center gap-x-4 gap-y-3 sm:flex sm:flex-wrap sm:items-center sm:gap-6", children: [_jsxs("div", { className: "min-w-0", children: [_jsx("h1", { className: "truncate text-2xl font-bold text-foreground", children: "Klanten" }), _jsx("p", { className: "text-sm text-muted-foreground", children: "Beheer al je zakelijke en particuliere klanten" })] }), top.length > 0 && (_jsx("div", { className: "col-span-2 justify-self-start sm:col-span-1 sm:self-center sm:justify-self-auto", children: _jsx(KlantenPodium, { items: top, onSelect: openCustomer }) })), _jsxs(Button, { className: "col-start-2 row-start-1 shrink-0 justify-self-end gap-2 sm:col-start-auto sm:row-start-auto sm:ml-auto sm:justify-self-auto", onClick: () => openCustomer(null), children: [_jsx(Plus, { className: "w-4 h-4" }), "Nieuwe klant"] })] }), _jsx(Card, { children: _jsxs(CardContent, { className: "p-4 space-y-4", children: [_jsxs("div", { className: "flex gap-3 flex-wrap", children: [_jsxs("div", { className: "relative flex-1 min-w-[240px]", children: [_jsx(Search, { className: "w-4 h-4 absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground" }), _jsx(Input, { placeholder: "Zoek op klantnr, naam, e-mail of plaats\u2026", value: search, onChange: (e) => setSearch(e.target.value), className: "pl-9" })] }), _jsxs(Select, { value: segmentFilter, onValueChange: (v) => setSegmentFilter(v), children: [_jsx(SelectTrigger, { className: "w-[180px]", children: _jsx(SelectValue, {}) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "all", children: "Alle segmenten" }), _jsx(SelectItem, { value: "Business", children: "Zakelijk" }), _jsx(SelectItem, { value: "Private", children: "Particulier" })] })] })] }), _jsx("div", { className: "rounded-md border", children: _jsxs(Table, { children: [_jsx(TableHeader, { children: _jsxs(TableRow, { children: [_jsx(TableHead, { className: "w-[130px]", children: "Klantnr." }), _jsx(TableHead, { className: "w-[30%]", children: "Naam" }), _jsx(TableHead, { children: "Segment" }), _jsx(TableHead, { children: "E-mail" }), _jsx(TableHead, { children: "Telefoon" }), _jsx(TableHead, { children: "Plaats" }), _jsx(TableHead, { className: "text-right", children: "Omzet" }), _jsx(TableHead, { className: "text-right", children: "Klussen" })] }) }), _jsx(TableBody, { children: loading ? (_jsx(TableRow, { children: _jsx(TableCell, { colSpan: 8, className: "text-center text-muted-foreground py-8", children: "Laden\u2026" }) })) : filtered.length === 0 ? (_jsx(TableRow, { children: _jsx(TableCell, { colSpan: 8, className: "text-center text-muted-foreground py-8", children: loadError ? "Klanten konden niet geladen worden" : "Geen klanten gevonden" }) })) : (filtered.map((c) => (_jsxs(TableRow, { className: "hover:bg-muted/40 cursor-pointer", onClick: () => openCustomer(c.id), children: [_jsx(TableCell, { className: "font-mono text-xs text-muted-foreground", children: c.customer_number ?? "—" }), _jsxs(TableCell, { className: "font-medium text-foreground", children: [c.name, _jsx(KlantRisicoBadge, { status: c.risk_status }), c.vat_reverse_charge && (_jsx("span", { className: "ml-2 text-[10px] uppercase tracking-wide rounded bg-muted px-1.5 py-0.5 text-muted-foreground", children: "Btw verlegd" }))] }), _jsx(TableCell, { children: _jsx(SegmentBadge, { segment: c.segment }) }), _jsx(TableCell, { className: "text-sm", children: c.general_email ?? "—" }), _jsx(TableCell, { className: "text-sm", children: c.general_phone ?? "—" }), _jsx(TableCell, { className: "text-sm", children: c.billing_city ?? "—" }), _jsx(TableCell, { className: "text-sm text-right tabular-nums", children: statCell(c.id, (s) => formatEuro(s.omzetCents)) }), _jsx(TableCell, { className: "text-sm text-right tabular-nums", children: statCell(c.id, (s) => s.klussen) })] }, c.id)))) })] }) }), _jsxs("div", { className: "flex items-center justify-between gap-3 text-sm text-muted-foreground", children: [_jsxs("span", { children: [total, " ", total === 1 ? "klant" : "klanten", statsFailed && " · omzet/klussen niet beschikbaar"] }), pageCount > 1 && (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Button, { variant: "outline", size: "sm", disabled: page === 0 || loading, onClick: () => setPage((p) => p - 1), children: "Vorige" }), _jsxs("span", { className: "tabular-nums", children: [page + 1, " / ", pageCount] }), _jsx(Button, { variant: "outline", size: "sm", disabled: page + 1 >= pageCount || loading, onClick: () => setPage((p) => p + 1), children: "Volgende" })] }))] })] }) }), _jsx(KlantenTrendCard, {}), _jsx(KlantDialog, { open: dialogOpen, onOpenChange: setDialogOpen, customerId: selectedId, onChanged: () => { void load(); void loadTop(); } })] }));
|
|
101
101
|
}
|
|
102
102
|
export function SegmentBadge({ segment }) {
|
|
103
103
|
if (segment === "Business") {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"KlantFormSections.d.ts","sourceRoot":"","sources":["../../../src/components/klanten/KlantFormSections.tsx"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAE9E,wBAAgB,KAAK,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;CAAE,+BAOtF;AAED,wBAAgB,aAAa,CAAC,EAC5B,MAAM,EACN,OAAO,EACP,IAAI,EACJ,KAAK,GACN,EAAE;IACD,MAAM,EAAE,OAAO,CAAC;IAChB,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,IAAI,EAAE,KAAK,CAAC,SAAS,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;CACf,+BAgBA;AAED,wBAAgB,aAAa,CAAC,EAC5B,OAAO,EACP,QAAQ,GACT,EAAE;IACD,OAAO,EAAE,cAAc,CAAC,SAAS,CAAC,CAAC;IACnC,QAAQ,EAAE,CAAC,CAAC,EAAE,cAAc,CAAC,SAAS,CAAC,KAAK,IAAI,CAAC;CAClD,+BAiBA;AAED,wBAAgB,sBAAsB,CAAC,EACrC,IAAI,EACJ,OAAO,EACP,KAAa,GACd,EAAE;IACD,IAAI,EAAE,cAAc,CAAC;IACrB,OAAO,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC,CAAC,CAAC;IAC9D,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB,+
|
|
1
|
+
{"version":3,"file":"KlantFormSections.d.ts","sourceRoot":"","sources":["../../../src/components/klanten/KlantFormSections.tsx"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAE9E,wBAAgB,KAAK,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;CAAE,+BAOtF;AAED,wBAAgB,aAAa,CAAC,EAC5B,MAAM,EACN,OAAO,EACP,IAAI,EACJ,KAAK,GACN,EAAE;IACD,MAAM,EAAE,OAAO,CAAC;IAChB,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,IAAI,EAAE,KAAK,CAAC,SAAS,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;CACf,+BAgBA;AAED,wBAAgB,aAAa,CAAC,EAC5B,OAAO,EACP,QAAQ,GACT,EAAE;IACD,OAAO,EAAE,cAAc,CAAC,SAAS,CAAC,CAAC;IACnC,QAAQ,EAAE,CAAC,CAAC,EAAE,cAAc,CAAC,SAAS,CAAC,KAAK,IAAI,CAAC;CAClD,+BAiBA;AAED,wBAAgB,sBAAsB,CAAC,EACrC,IAAI,EACJ,OAAO,EACP,KAAa,GACd,EAAE;IACD,IAAI,EAAE,cAAc,CAAC;IACrB,OAAO,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC,CAAC,CAAC;IAC9D,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB,+BA6EA;AAED,wBAAgB,eAAe,CAAC,EAC9B,QAAQ,EACR,UAAU,EACV,aAAa,EACb,aAAa,GACd,EAAE;IACD,QAAQ,EAAE,UAAU,EAAE,CAAC;IACvB,UAAU,EAAE,MAAM,IAAI,CAAC;IACvB,aAAa,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,CAAC,UAAU,CAAC,KAAK,IAAI,CAAC;IACjE,aAAa,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CACtC,+BAqDA;AAED,wBAAgB,cAAc,CAAC,EAC7B,SAAS,EACT,WAAW,EACX,cAAc,EACd,cAAc,EACd,aAAqB,GACtB,EAAE;IACD,SAAS,EAAE,WAAW,EAAE,CAAC;IACzB,WAAW,EAAE,MAAM,IAAI,CAAC;IACxB,cAAc,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,CAAC,WAAW,CAAC,KAAK,IAAI,CAAC;IACnE,cAAc,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IACtC,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB,+BA8FA;AAED,wBAAgB,iBAAiB,CAAC,EAChC,KAAK,EACL,KAAK,EACL,QAAQ,GACT,EAAE;IACD,KAAK,EAAE,YAAY,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;CAC/B,+BA4CA;AAED,wBAAgB,iBAAiB,CAAC,EAChC,MAAM,EACN,MAAM,EACN,SAAS,GACV,EAAE;IACD,MAAM,EAAE,YAAY,EAAE,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,SAAS,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC;CACzE,+BAaA"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { Button, Input, Label, Textarea, Checkbox, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@flowselections/core";
|
|
3
3
|
import { Building2, User, Plus, Trash2 } from "lucide-react";
|
|
4
4
|
export function Field({ label, children }) {
|
|
@@ -14,7 +14,7 @@ export function SegmentToggle({ segment, onChange, }) {
|
|
|
14
14
|
return (_jsxs("div", { className: "inline-flex rounded-lg border p-1 bg-muted/40", children: [_jsx(SegmentButton, { active: segment === "Business", onClick: () => onChange("Business"), icon: _jsx(Building2, { className: "w-4 h-4" }), label: "Zakelijk" }), _jsx(SegmentButton, { active: segment === "Private", onClick: () => onChange("Private"), icon: _jsx(User, { className: "w-4 h-4" }), label: "Particulier" })] }));
|
|
15
15
|
}
|
|
16
16
|
export function AlgemeneGegevensFields({ form, setForm, isNew = false, }) {
|
|
17
|
-
return (_jsxs("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4", children: [isNew && (_jsx("p", { className: "text-xs text-muted-foreground md:col-span-2", children: "Het klantnummer wordt automatisch toegekend na het opslaan." })), _jsx(Field, { label: "Naam *", children: _jsx(Input, { value: form.name, onChange: (e) => setForm((f) => ({ ...f, name: e.target.value })), placeholder: "Bedrijf of persoonsnaam" }) }), _jsx(Field, { label: "Algemeen e-mailadres *", children: _jsx(Input, { type: "email", value: form.general_email ?? "", onChange: (e) => setForm((f) => ({ ...f, general_email: e.target.value })), placeholder: "info@voorbeeld.nl" }) }), _jsx(Field, { label: "Algemeen telefoonnummer *", children: _jsx(Input, { value: form.general_phone ?? "", onChange: (e) => setForm((f) => ({ ...f, general_phone: e.target.value })), placeholder: "+31 \u2026" }) }), _jsx("div", {}), _jsx(Field, { label: "Factuuradres \u2014 straat *", children: _jsx(Input, { value: form.billing_street ?? "", onChange: (e) => setForm((f) => ({ ...f, billing_street: e.target.value })) }) }), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsx(Field, { label: "Postcode *", children: _jsx(Input, { value: form.billing_postcode ?? "", onChange: (e) => setForm((f) => ({ ...f, billing_postcode: e.target.value })) }) }), _jsx(Field, { label: "Plaats *", children: _jsx(Input, { value: form.billing_city ?? "", onChange: (e) => setForm((f) => ({ ...f, billing_city: e.target.value })) }) })] })] }));
|
|
17
|
+
return (_jsxs("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4", children: [isNew && (_jsx("p", { className: "text-xs text-muted-foreground md:col-span-2", children: "Het klantnummer wordt automatisch toegekend na het opslaan." })), _jsx(Field, { label: "Naam *", children: _jsx(Input, { value: form.name, onChange: (e) => setForm((f) => ({ ...f, name: e.target.value })), placeholder: "Bedrijf of persoonsnaam" }) }), _jsx(Field, { label: "Algemeen e-mailadres *", children: _jsx(Input, { type: "email", value: form.general_email ?? "", onChange: (e) => setForm((f) => ({ ...f, general_email: e.target.value })), placeholder: "info@voorbeeld.nl" }) }), _jsx(Field, { label: "Algemeen telefoonnummer *", children: _jsx(Input, { value: form.general_phone ?? "", onChange: (e) => setForm((f) => ({ ...f, general_phone: e.target.value })), placeholder: "+31 \u2026" }) }), _jsx("div", {}), _jsx(Field, { label: "Factuuradres \u2014 straat *", children: _jsx(Input, { value: form.billing_street ?? "", onChange: (e) => setForm((f) => ({ ...f, billing_street: e.target.value })) }) }), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsx(Field, { label: "Postcode *", children: _jsx(Input, { value: form.billing_postcode ?? "", onChange: (e) => setForm((f) => ({ ...f, billing_postcode: e.target.value })) }) }), _jsx(Field, { label: "Plaats *", children: _jsx(Input, { value: form.billing_city ?? "", onChange: (e) => setForm((f) => ({ ...f, billing_city: e.target.value })) }) })] }), form.segment === "Business" && (_jsxs(_Fragment, { children: [_jsx(Field, { label: form.vat_reverse_charge ? "Btw-identificatienummer *" : "Btw-identificatienummer", children: _jsx(Input, { value: form.vat_number ?? "", onChange: (e) => setForm((f) => ({ ...f, vat_number: e.target.value })), placeholder: "NL123456789B01" }) }), _jsxs("div", { className: "flex items-start gap-2 pt-6", children: [_jsx(Checkbox, { id: "vat-reverse-charge", checked: !!form.vat_reverse_charge, onCheckedChange: (v) => setForm((f) => ({ ...f, vat_reverse_charge: !!v })) }), _jsxs("div", { children: [_jsx(Label, { htmlFor: "vat-reverse-charge", className: "text-sm", children: "Btw automatisch verleggen" }), _jsx("p", { className: "text-xs text-muted-foreground", children: "Nieuwe facturen krijgen standaard \"btw verlegd\". Per factuur blijft dit aan te passen." })] })] })] }))] }));
|
|
18
18
|
}
|
|
19
19
|
export function ContactenFields({ contacts, addContact, updateContact, removeContact, }) {
|
|
20
20
|
return (_jsxs("div", { className: "space-y-4", children: [_jsx("div", { className: "flex justify-end", children: _jsxs(Button, { size: "sm", variant: "outline", onClick: addContact, className: "gap-2", children: [_jsx(Plus, { className: "w-4 h-4" }), "Toevoegen"] }) }), contacts.length === 0 && (_jsx("p", { className: "text-sm text-muted-foreground", children: "Nog geen contactpersonen." })), contacts.map((c, idx) => (_jsxs("div", { className: "border rounded-lg p-4 space-y-3 bg-muted/20", children: [_jsxs("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-3", children: [_jsx(Field, { label: "Naam", children: _jsx(Input, { value: c.name, onChange: (e) => updateContact(idx, { name: e.target.value }) }) }), _jsx(Field, { label: "Functie", children: _jsx(Input, { value: c.role ?? "", onChange: (e) => updateContact(idx, { role: e.target.value }) }) }), _jsx(Field, { label: "Telefoon", children: _jsx(Input, { value: c.phone ?? "", onChange: (e) => updateContact(idx, { phone: e.target.value }) }) }), _jsx(Field, { label: "E-mail", children: _jsx(Input, { type: "email", value: c.email ?? "", onChange: (e) => updateContact(idx, { email: e.target.value }) }) })] }), _jsx("div", { className: "flex justify-end", children: _jsxs(Button, { size: "sm", variant: "ghost", onClick: () => removeContact(idx), className: "text-destructive gap-2", children: [_jsx(Trash2, { className: "w-4 h-4" }), "Verwijderen"] }) })] }, idx)))] }));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useKlantForm.d.ts","sourceRoot":"","sources":["../../../src/components/klanten/useKlantForm.ts"],"names":[],"mappings":"AAGA,OAAO,EAEL,KAAK,QAAQ,EACb,KAAK,aAAa,EAClB,KAAK,YAAY,EACjB,KAAK,YAAY,EAElB,MAAM,qBAAqB,CAAC;AAE7B,MAAM,MAAM,cAAc,GAAG,IAAI,CAC/B,QAAQ,EACR,IAAI,GAAG,YAAY,GAAG,YAAY,GAAG,iBAAiB,CACvD,CAAC;AACF,MAAM,MAAM,UAAU,GAAG,IAAI,CAAC,aAAa,EAAE,IAAI,GAAG,aAAa,CAAC,GAAG;IAAE,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AACrF,MAAM,MAAM,WAAW,GAAG,IAAI,CAAC,YAAY,EAAE,IAAI,GAAG,aAAa,CAAC,GAAG;IAAE,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAErF,eAAO,MAAM,aAAa,EAAE,
|
|
1
|
+
{"version":3,"file":"useKlantForm.d.ts","sourceRoot":"","sources":["../../../src/components/klanten/useKlantForm.ts"],"names":[],"mappings":"AAGA,OAAO,EAEL,KAAK,QAAQ,EACb,KAAK,aAAa,EAClB,KAAK,YAAY,EACjB,KAAK,YAAY,EAElB,MAAM,qBAAqB,CAAC;AAE7B,MAAM,MAAM,cAAc,GAAG,IAAI,CAC/B,QAAQ,EACR,IAAI,GAAG,YAAY,GAAG,YAAY,GAAG,iBAAiB,CACvD,CAAC;AACF,MAAM,MAAM,UAAU,GAAG,IAAI,CAAC,aAAa,EAAE,IAAI,GAAG,aAAa,CAAC,GAAG;IAAE,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AACrF,MAAM,MAAM,WAAW,GAAG,IAAI,CAAC,YAAY,EAAE,IAAI,GAAG,aAAa,CAAC,GAAG;IAAE,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAErF,eAAO,MAAM,aAAa,EAAE,cAW3B,CAAC;AAEF,UAAU,OAAO;IACf,uCAAuC;IACvC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,6CAA6C;IAC7C,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,OAAO,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC;CACzB;AAED,wBAAgB,YAAY,CAAC,EAC3B,UAAU,EACV,OAAc,EACd,OAAO,EACP,SAAS,EACT,UAAU,GACX,EAAE,OAAO;;;;;;;;;yBA6EoB,MAAM,SAAS,OAAO,CAAC,UAAU,CAAC;yBAElC,MAAM;;;0BAgBL,MAAM,SAAS,OAAO,CAAC,WAAW,CAAC;0BAEnC,MAAM;;;;;;;EAiJpC"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { missingRequiredFields } from "../../lib/klantRules";
|
|
1
|
+
import { missingRequiredFields, vatReverseChargeError } from "../../lib/klantRules";
|
|
2
2
|
import { useCallback, useEffect, useState } from "react";
|
|
3
3
|
import { toast } from "@flowselections/core";
|
|
4
4
|
import { db, } from "../../lib/itkClient";
|
|
@@ -11,6 +11,8 @@ export const emptyCustomer = {
|
|
|
11
11
|
general_email: "",
|
|
12
12
|
general_phone: "",
|
|
13
13
|
legacy_customer_id: null,
|
|
14
|
+
vat_number: "",
|
|
15
|
+
vat_reverse_charge: false,
|
|
14
16
|
};
|
|
15
17
|
export function useKlantForm({ customerId, enabled = true, onSaved, onDeleted, onNotFound, }) {
|
|
16
18
|
const isNew = !customerId || customerId === "nieuw";
|
|
@@ -56,6 +58,8 @@ export function useKlantForm({ customerId, enabled = true, onSaved, onDeleted, o
|
|
|
56
58
|
general_email: c.general_email ?? "",
|
|
57
59
|
general_phone: c.general_phone ?? "",
|
|
58
60
|
legacy_customer_id: c.legacy_customer_id ?? null,
|
|
61
|
+
vat_number: c.vat_number ?? "",
|
|
62
|
+
vat_reverse_charge: !!c.vat_reverse_charge,
|
|
59
63
|
});
|
|
60
64
|
setCustomerNumber(c.customer_number ?? null);
|
|
61
65
|
setContacts(Array.isArray(cons.data) ? cons.data : []);
|
|
@@ -107,6 +111,11 @@ export function useKlantForm({ customerId, enabled = true, onSaved, onDeleted, o
|
|
|
107
111
|
toast.error(`Vul de verplichte velden in: ${missing.join(", ")}`);
|
|
108
112
|
return;
|
|
109
113
|
}
|
|
114
|
+
const vatErr = vatReverseChargeError(form);
|
|
115
|
+
if (vatErr) {
|
|
116
|
+
toast.error(vatErr);
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
110
119
|
setSaving(true);
|
|
111
120
|
try {
|
|
112
121
|
let newId = customerId;
|
|
@@ -118,6 +127,8 @@ export function useKlantForm({ customerId, enabled = true, onSaved, onDeleted, o
|
|
|
118
127
|
billing_street: form.billing_street?.trim() || null,
|
|
119
128
|
billing_city: form.billing_city?.trim() || null,
|
|
120
129
|
billing_postcode: form.billing_postcode?.trim() || null,
|
|
130
|
+
vat_number: form.vat_number?.trim() || null,
|
|
131
|
+
vat_reverse_charge: form.segment === "Business" ? !!form.vat_reverse_charge : false,
|
|
121
132
|
};
|
|
122
133
|
if (isNew) {
|
|
123
134
|
const { data, error } = await db
|