@flowselections/klanten-installatie-techniek-planner 1.0.22 → 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.
Files changed (43) hide show
  1. package/dist-lib/components/KlantenDetailPage.d.ts.map +1 -1
  2. package/dist-lib/components/KlantenDetailPage.js +4 -1
  3. package/dist-lib/components/KlantenListPage.d.ts.map +1 -1
  4. package/dist-lib/components/KlantenListPage.js +3 -1
  5. package/dist-lib/components/klanten/KlantDialog.d.ts.map +1 -1
  6. package/dist-lib/components/klanten/KlantDialog.js +6 -1
  7. package/dist-lib/components/klanten/KlantFormSections.d.ts.map +1 -1
  8. package/dist-lib/components/klanten/KlantFormSections.js +2 -2
  9. package/dist-lib/components/klanten/KlantNotitiesCard.d.ts +2 -1
  10. package/dist-lib/components/klanten/KlantNotitiesCard.d.ts.map +1 -1
  11. package/dist-lib/components/klanten/KlantNotitiesCard.js +12 -3
  12. package/dist-lib/components/klanten/KlantRisicoBanner.d.ts +18 -0
  13. package/dist-lib/components/klanten/KlantRisicoBanner.d.ts.map +1 -0
  14. package/dist-lib/components/klanten/KlantRisicoBanner.js +43 -0
  15. package/dist-lib/components/klanten/KlantRisicoStatusCard.d.ts +7 -0
  16. package/dist-lib/components/klanten/KlantRisicoStatusCard.d.ts.map +1 -0
  17. package/dist-lib/components/klanten/KlantRisicoStatusCard.js +65 -0
  18. package/dist-lib/components/klanten/KlantStatsCard.d.ts.map +1 -1
  19. package/dist-lib/components/klanten/KlantStatsCard.js +14 -4
  20. package/dist-lib/components/klanten/KlantenTrendCard.d.ts +2 -0
  21. package/dist-lib/components/klanten/KlantenTrendCard.d.ts.map +1 -0
  22. package/dist-lib/components/klanten/KlantenTrendCard.js +52 -0
  23. package/dist-lib/components/klanten/useKlantForm.d.ts.map +1 -1
  24. package/dist-lib/components/klanten/useKlantForm.js +12 -1
  25. package/dist-lib/index.d.ts +1 -0
  26. package/dist-lib/index.d.ts.map +1 -1
  27. package/dist-lib/index.js +2 -1
  28. package/dist-lib/integrations/supabase/types.d.ts +9803 -298
  29. package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
  30. package/dist-lib/integrations/supabase/types.js +1 -0
  31. package/dist-lib/lib/itkClient.d.ts +5 -1
  32. package/dist-lib/lib/itkClient.d.ts.map +1 -1
  33. package/dist-lib/lib/klantRules.d.ts +50 -0
  34. package/dist-lib/lib/klantRules.d.ts.map +1 -1
  35. package/dist-lib/lib/klantRules.js +56 -1
  36. package/dist-lib/lib/klantRules.test.js +76 -0
  37. package/dist-lib/lib/klantStatsClient.d.ts +25 -0
  38. package/dist-lib/lib/klantStatsClient.d.ts.map +1 -1
  39. package/dist-lib/lib/klantStatsClient.js +37 -1
  40. package/dist-lib/lib/statusConfig.d.ts +10 -0
  41. package/dist-lib/lib/statusConfig.d.ts.map +1 -1
  42. package/dist-lib/lib/statusConfig.js +10 -0
  43. package/package.json +45 -11
@@ -1 +1 @@
1
- {"version":3,"file":"KlantenDetailPage.d.ts","sourceRoot":"","sources":["../../src/components/KlantenDetailPage.tsx"],"names":[],"mappings":"AAsBA,wBAAgB,iBAAiB,gCAkLhC"}
1
+ {"version":3,"file":"KlantenDetailPage.d.ts","sourceRoot":"","sources":["../../src/components/KlantenDetailPage.tsx"],"names":[],"mappings":"AAwBA,wBAAgB,iBAAiB,gCAwLhC"}
@@ -1,4 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useState } from "react";
2
3
  import { useNavigate, useParams, Link } from "@tanstack/react-router";
3
4
  import { Button, Card, CardContent, CardHeader, CardTitle, Separator, } from "@flowselections/core";
4
5
  import { ArrowLeft, Trash2, Save, MapPin } from "lucide-react";
@@ -6,10 +7,12 @@ import { useKlantForm } from "./klanten/useKlantForm";
6
7
  import { SegmentToggle, AlgemeneGegevensFields, ContactenFields, LocatiesFields, DynamicFieldsGrid, } from "./klanten/KlantFormSections";
7
8
  import { KlantGeschiedenisCard } from "./klanten/KlantGeschiedenisCard";
8
9
  import { KlantNotitiesCard } from "./klanten/KlantNotitiesCard";
10
+ import { KlantRisicoBannerById } from "./klanten/KlantRisicoBanner";
9
11
  import { KlantStatsCard } from "./klanten/KlantStatsCard";
10
12
  export function KlantenDetailPage() {
11
13
  const { id } = useParams({ from: "/_authenticated/installatietechniek-klanten-planner/$id" });
12
14
  const navigate = useNavigate();
15
+ const [riskKey, setRiskKey] = useState(0);
13
16
  const { isNew, loading, saving, form, setForm, customerNumber, contacts, addContact, updateContact, removeContact, locations, addLocation, updateLocation, removeLocation, fields, values, setValues, save, remove, reload, } = useKlantForm({
14
17
  customerId: id,
15
18
  onSaved: (newId) => {
@@ -26,5 +29,5 @@ export function KlantenDetailPage() {
26
29
  if (loading) {
27
30
  return (_jsx("main", { className: "p-6", children: _jsx("p", { className: "text-muted-foreground", children: "Laden\u2026" }) }));
28
31
  }
29
- 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(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 })] })), _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"] }) })] }));
30
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;AAU3E,wBAAgB,eAAe,gCAsO9B;AAED,wBAAgB,YAAY,CAAC,EAAE,OAAO,EAAE,EAAE;IAAE,OAAO,EAAE,eAAe,CAAA;CAAE,+BAkBrE"}
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"}
@@ -5,7 +5,9 @@ import { Plus, Search, Building2, User } from "lucide-react";
5
5
  import { db } from "../lib/itkClient";
6
6
  import { fetchKlantStats, fetchTopKlanten, formatEuro } from "../lib/klantStatsClient";
7
7
  import { KlantDialog } from "./klanten/KlantDialog";
8
+ import { KlantRisicoBadge } from "./klanten/KlantRisicoBanner";
8
9
  import { KlantenPodium } from "./klanten/KlantenPodium";
10
+ import { KlantenTrendCard } from "./klanten/KlantenTrendCard";
9
11
  const PAGE_SIZE = 50;
10
12
  export function KlantenListPage() {
11
13
  const [customers, setCustomers] = useState([]);
@@ -95,7 +97,7 @@ export function KlantenListPage() {
95
97
  const filtered = customers;
96
98
  const pageCount = Math.max(1, Math.ceil(total / PAGE_SIZE));
97
99
  const statCell = (id, pick) => stats[id] ? pick(stats[id]) : statsFailed ? "n.b." : "—";
98
- 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 ?? "—" }), _jsx(TableCell, { className: "font-medium text-foreground", children: c.name }), _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(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(); } })] }));
99
101
  }
100
102
  export function SegmentBadge({ segment }) {
101
103
  if (segment === "Business") {
@@ -1 +1 @@
1
- {"version":3,"file":"KlantDialog.d.ts","sourceRoot":"","sources":["../../../src/components/klanten/KlantDialog.tsx"],"names":[],"mappings":"AAqCA,UAAU,KAAK;IACb,IAAI,EAAE,OAAO,CAAC;IACd,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACtC,6BAA6B;IAC7B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;CACxB;AAED,wBAAgB,WAAW,CAAC,EAAE,IAAI,EAAE,YAAY,EAAE,UAAU,EAAE,SAAS,EAAE,EAAE,KAAK,+BA4L/E"}
1
+ {"version":3,"file":"KlantDialog.d.ts","sourceRoot":"","sources":["../../../src/components/klanten/KlantDialog.tsx"],"names":[],"mappings":"AAsCA,UAAU,KAAK;IACb,IAAI,EAAE,OAAO,CAAC;IACd,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACtC,6BAA6B;IAC7B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;CACxB;AAED,wBAAgB,WAAW,CAAC,EAAE,IAAI,EAAE,YAAY,EAAE,UAAU,EAAE,SAAS,EAAE,EAAE,KAAK,+BAsM/E"}
@@ -7,10 +7,12 @@ import { useKlantForm } from "./useKlantForm";
7
7
  import { SegmentToggle, AlgemeneGegevensFields, ContactenFields, LocatiesFields, DynamicFieldsGrid, } from "./KlantFormSections";
8
8
  import { KlantGeschiedenisCard } from "./KlantGeschiedenisCard";
9
9
  import { KlantNotitiesCard } from "./KlantNotitiesCard";
10
+ import { KlantRisicoBannerById } from "./KlantRisicoBanner";
10
11
  import { KlantStatsCard } from "./KlantStatsCard";
11
12
  import { SegmentBadge } from "../KlantenListPage";
12
13
  export function KlantDialog({ open, onOpenChange, customerId, onChanged }) {
13
14
  const [tab, setTab] = useState("gegevens");
15
+ const [riskKey, setRiskKey] = useState(0);
14
16
  const { isNew, loading, saving, form, setForm, customerNumber, contacts, addContact, updateContact, removeContact, locations, addLocation, updateLocation, removeLocation, fields, values, setValues, save, remove, } = useKlantForm({
15
17
  customerId,
16
18
  enabled: open,
@@ -33,5 +35,8 @@ export function KlantDialog({ open, onOpenChange, customerId, onChanged }) {
33
35
  setTab("gegevens");
34
36
  }, [form.segment, tab]);
35
37
  const isBusiness = form.segment === "Business";
36
- return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { className: "sm:max-w-[720px] max-h-[85vh] overflow-hidden flex flex-col", children: [_jsxs(DialogHeader, { children: [_jsxs(DialogTitle, { className: "flex items-center gap-2 pr-8", children: [_jsx(User, { className: "w-5 h-5" }), isNew ? "Nieuwe klant" : form.name || "Klant", customerNumber && (_jsx("span", { className: "font-mono text-xs text-muted-foreground bg-muted px-2 py-0.5 rounded", children: customerNumber })), !isNew && _jsx(SegmentBadge, { segment: form.segment })] }), _jsx(DialogDescription, { className: "sr-only", children: "Klantgegevens bewerken" })] }), loading ? (_jsx("p", { className: "text-sm text-muted-foreground py-8 text-center", children: "Laden\u2026" })) : (_jsxs(Tabs, { value: tab, onValueChange: setTab, className: "flex-1 overflow-hidden flex flex-col", children: [_jsxs(TabsList, { className: `grid w-full ${isBusiness ? "grid-cols-5" : "grid-cols-4"}`, children: [_jsxs(TabsTrigger, { value: "gegevens", title: "Gegevens", className: "gap-1.5 px-1.5 text-xs", children: [_jsx(User, { className: "w-3.5 h-3.5 shrink-0" }), _jsx("span", { className: "hidden lg:inline", children: "Gegevens" })] }), isBusiness && (_jsxs(TabsTrigger, { value: "contacten", title: "Contacten", className: "gap-1.5 px-1.5 text-xs", children: [_jsx(Users, { className: "w-3.5 h-3.5 shrink-0" }), _jsx("span", { className: "hidden lg:inline", children: "Contacten" })] })), _jsxs(TabsTrigger, { value: "locaties", title: "Locaties", className: "gap-1.5 px-1.5 text-xs", children: [_jsx(MapPin, { className: "w-3.5 h-3.5 shrink-0" }), _jsx("span", { className: "hidden lg:inline", children: "Locaties" })] }), _jsxs(TabsTrigger, { value: "werkbonnen", title: "Werkbonnen", disabled: isNew, className: "gap-1.5 px-1.5 text-xs", children: [_jsx(Wrench, { className: "w-3.5 h-3.5 shrink-0" }), _jsx("span", { className: "hidden lg:inline", children: "Werkbonnen" })] }), _jsxs(TabsTrigger, { value: "notities", title: "Notities", disabled: isNew, className: "gap-1.5 px-1.5 text-xs", children: [_jsx(StickyNote, { className: "w-3.5 h-3.5 shrink-0" }), _jsx("span", { className: "hidden lg:inline", children: "Notities" })] })] }), _jsxs("div", { className: "flex-1 overflow-y-auto mt-4 pr-1", children: [_jsxs(TabsContent, { value: "gegevens", className: "m-0 space-y-5", children: [!isNew && customerId && _jsx(KlantStatsCard, { customerId: customerId }), _jsx(SegmentToggle, { segment: form.segment, onChange: (s) => setForm((f) => ({ ...f, segment: s })) }), _jsx(AlgemeneGegevensFields, { form: form, setForm: setForm, isNew: isNew }), fields.length > 0 && (_jsxs("div", { className: "space-y-3 pt-2 border-t", children: [_jsx("p", { className: "text-sm font-medium text-foreground pt-3", children: "Aanvullende velden" }), _jsx(DynamicFieldsGrid, { fields: fields, values: values, setValues: setValues })] }))] }), isBusiness && (_jsx(TabsContent, { value: "contacten", className: "m-0", children: _jsx(ContactenFields, { contacts: contacts, addContact: addContact, updateContact: updateContact, removeContact: removeContact }) })), _jsx(TabsContent, { value: "locaties", className: "m-0", children: _jsx(LocatiesFields, { locations: locations, addLocation: addLocation, updateLocation: updateLocation, removeLocation: removeLocation, hideGpsFields: isNew }) }), !isNew && customerId && (_jsxs(_Fragment, { children: [_jsx(TabsContent, { value: "werkbonnen", className: "m-0", children: _jsx(KlantGeschiedenisCard, { customerId: customerId }) }), _jsx(TabsContent, { value: "notities", className: "m-0", children: _jsx(KlantNotitiesCard, { customerId: customerId }) })] }))] }), _jsxs("div", { className: "flex items-center justify-between gap-2 pt-4 border-t mt-4", children: [_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"] })), !isNew && customerId && (_jsx(Link, { to: "/installatietechniek-klanten-planner/$id", params: { id: customerId }, children: _jsxs(Button, { variant: "ghost", className: "gap-2", children: [_jsx(ExternalLink, { className: "w-4 h-4" }), _jsx("span", { className: "hidden sm:inline", children: "Volledige pagina" })] }) }))] }), _jsxs("div", { className: "flex gap-2", children: [_jsx(Button, { variant: "outline", onClick: () => onOpenChange(false), children: "Annuleren" }), _jsxs(Button, { onClick: save, disabled: saving, className: "gap-2", children: [_jsx(Save, { className: "w-4 h-4" }), saving ? "Opslaan…" : "Opslaan"] })] })] })] }))] }) }));
38
+ return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { className: "sm:max-w-[720px] max-h-[85vh] overflow-hidden flex flex-col", children: [_jsxs(DialogHeader, { children: [_jsxs(DialogTitle, { className: "flex items-center gap-2 pr-8", children: [_jsx(User, { className: "w-5 h-5" }), isNew ? "Nieuwe klant" : form.name || "Klant", customerNumber && (_jsx("span", { className: "font-mono text-xs text-muted-foreground bg-muted px-2 py-0.5 rounded", children: customerNumber })), !isNew && _jsx(SegmentBadge, { segment: form.segment })] }), _jsx(DialogDescription, { className: "sr-only", children: "Klantgegevens bewerken" })] }), !isNew && customerId && !loading && (_jsx(KlantRisicoBannerById, { customerId: customerId, refreshKey: riskKey })), loading ? (_jsx("p", { className: "text-sm text-muted-foreground py-8 text-center", children: "Laden\u2026" })) : (_jsxs(Tabs, { value: tab, onValueChange: setTab, className: "flex-1 overflow-hidden flex flex-col", children: [_jsxs(TabsList, { className: `grid w-full ${isBusiness ? "grid-cols-5" : "grid-cols-4"}`, children: [_jsxs(TabsTrigger, { value: "gegevens", title: "Gegevens", className: "gap-1.5 px-1.5 text-xs", children: [_jsx(User, { className: "w-3.5 h-3.5 shrink-0" }), _jsx("span", { className: "hidden lg:inline", children: "Gegevens" })] }), isBusiness && (_jsxs(TabsTrigger, { value: "contacten", title: "Contacten", className: "gap-1.5 px-1.5 text-xs", children: [_jsx(Users, { className: "w-3.5 h-3.5 shrink-0" }), _jsx("span", { className: "hidden lg:inline", children: "Contacten" })] })), _jsxs(TabsTrigger, { value: "locaties", title: "Locaties", className: "gap-1.5 px-1.5 text-xs", children: [_jsx(MapPin, { className: "w-3.5 h-3.5 shrink-0" }), _jsx("span", { className: "hidden lg:inline", children: "Locaties" })] }), _jsxs(TabsTrigger, { value: "werkbonnen", title: "Werkbonnen", disabled: isNew, className: "gap-1.5 px-1.5 text-xs", children: [_jsx(Wrench, { className: "w-3.5 h-3.5 shrink-0" }), _jsx("span", { className: "hidden lg:inline", children: "Werkbonnen" })] }), _jsxs(TabsTrigger, { value: "notities", title: "Notities", disabled: isNew, className: "gap-1.5 px-1.5 text-xs", children: [_jsx(StickyNote, { className: "w-3.5 h-3.5 shrink-0" }), _jsx("span", { className: "hidden lg:inline", children: "Notities" })] })] }), _jsxs("div", { className: "flex-1 overflow-y-auto mt-4 pr-1", children: [_jsxs(TabsContent, { value: "gegevens", className: "m-0 space-y-5", children: [!isNew && customerId && _jsx(KlantStatsCard, { customerId: customerId }), _jsx(SegmentToggle, { segment: form.segment, onChange: (s) => setForm((f) => ({ ...f, segment: s })) }), _jsx(AlgemeneGegevensFields, { form: form, setForm: setForm, isNew: isNew }), fields.length > 0 && (_jsxs("div", { className: "space-y-3 pt-2 border-t", children: [_jsx("p", { className: "text-sm font-medium text-foreground pt-3", children: "Aanvullende velden" }), _jsx(DynamicFieldsGrid, { fields: fields, values: values, setValues: setValues })] }))] }), isBusiness && (_jsx(TabsContent, { value: "contacten", className: "m-0", children: _jsx(ContactenFields, { contacts: contacts, addContact: addContact, updateContact: updateContact, removeContact: removeContact }) })), _jsx(TabsContent, { value: "locaties", className: "m-0", children: _jsx(LocatiesFields, { locations: locations, addLocation: addLocation, updateLocation: updateLocation, removeLocation: removeLocation, hideGpsFields: isNew }) }), !isNew && customerId && (_jsxs(_Fragment, { children: [_jsx(TabsContent, { value: "werkbonnen", className: "m-0", children: _jsx(KlantGeschiedenisCard, { customerId: customerId }) }), _jsx(TabsContent, { value: "notities", className: "m-0", children: _jsx(KlantNotitiesCard, { customerId: customerId, onStatusChanged: () => {
39
+ setRiskKey((k) => k + 1);
40
+ onChanged?.();
41
+ } }) })] }))] }), _jsxs("div", { className: "flex items-center justify-between gap-2 pt-4 border-t mt-4", children: [_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"] })), !isNew && customerId && (_jsx(Link, { to: "/installatietechniek-klanten-planner/$id", params: { id: customerId }, children: _jsxs(Button, { variant: "ghost", className: "gap-2", children: [_jsx(ExternalLink, { className: "w-4 h-4" }), _jsx("span", { className: "hidden sm:inline", children: "Volledige pagina" })] }) }))] }), _jsxs("div", { className: "flex gap-2", children: [_jsx(Button, { variant: "outline", onClick: () => onOpenChange(false), children: "Annuleren" }), _jsxs(Button, { onClick: save, disabled: saving, className: "gap-2", children: [_jsx(Save, { className: "w-4 h-4" }), saving ? "Opslaan…" : "Opslaan"] })] })] })] }))] }) }));
37
42
  }
@@ -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,+BAqDA;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
+ {"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,6 +1,7 @@
1
1
  interface Props {
2
2
  customerId: string;
3
+ onStatusChanged?: () => void;
3
4
  }
4
- export declare function KlantNotitiesCard({ customerId }: Props): import("react").JSX.Element;
5
+ export declare function KlantNotitiesCard({ customerId, onStatusChanged }: Props): import("react").JSX.Element;
5
6
  export {};
6
7
  //# sourceMappingURL=KlantNotitiesCard.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"KlantNotitiesCard.d.ts","sourceRoot":"","sources":["../../../src/components/klanten/KlantNotitiesCard.tsx"],"names":[],"mappings":"AAsDA,UAAU,KAAK;IACb,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,iBAAiB,CAAC,EAAE,UAAU,EAAE,EAAE,KAAK,+BAgLtD"}
1
+ {"version":3,"file":"KlantNotitiesCard.d.ts","sourceRoot":"","sources":["../../../src/components/klanten/KlantNotitiesCard.tsx"],"names":[],"mappings":"AA6DA,UAAU,KAAK;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,MAAM,IAAI,CAAC;CAC9B;AAED,wBAAgB,iBAAiB,CAAC,EAAE,UAAU,EAAE,eAAe,EAAE,EAAE,KAAK,+BAuLvE"}
@@ -2,6 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useEffect, useState } from "react";
3
3
  import { Button, Card, CardContent, CardHeader, CardTitle, Label, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, Textarea, supabase, toast, } from "@flowselections/core";
4
4
  import { StickyNote, Trash2, Plus } from "lucide-react";
5
+ import { KlantRisicoStatusCard } from "./KlantRisicoStatusCard";
5
6
  import { db } from "../../lib/itkClient";
6
7
  const TYPE_OPTIONS = [
7
8
  { value: "general", label: "Algemeen" },
@@ -10,15 +11,20 @@ const TYPE_OPTIONS = [
10
11
  { value: "visit", label: "Bezoek" },
11
12
  { value: "other", label: "Overig" },
12
13
  ];
14
+ const TYPE_LABELS = {
15
+ general: "Algemeen", call: "Telefonisch", complaint: "Klacht", visit: "Bezoek", other: "Overig",
16
+ risk_status: "Klantstatus",
17
+ };
13
18
  const TYPE_STYLE = {
14
19
  general: "bg-muted text-muted-foreground",
15
20
  call: "bg-blue-500/15 text-blue-400",
16
21
  complaint: "bg-red-500/15 text-red-400",
17
22
  visit: "bg-emerald-500/15 text-emerald-400",
18
23
  other: "bg-slate-500/15 text-slate-400",
24
+ risk_status: "bg-orange-500/15 text-orange-500",
19
25
  };
20
26
  function typeLabel(t) {
21
- return TYPE_OPTIONS.find((o) => o.value === t)?.label ?? t;
27
+ return TYPE_LABELS[t] ?? t;
22
28
  }
23
29
  function formatDateTime(iso) {
24
30
  try {
@@ -34,7 +40,7 @@ function formatDateTime(iso) {
34
40
  return iso;
35
41
  }
36
42
  }
37
- export function KlantNotitiesCard({ customerId }) {
43
+ export function KlantNotitiesCard({ customerId, onStatusChanged }) {
38
44
  const [notes, setNotes] = useState([]);
39
45
  const [authors, setAuthors] = useState({});
40
46
  const [loading, setLoading] = useState(true);
@@ -114,6 +120,9 @@ export function KlantNotitiesCard({ customerId }) {
114
120
  toast.success("Notitie verwijderd");
115
121
  void load();
116
122
  };
117
- return (_jsxs(Card, { id: "notities", children: [_jsx(CardHeader, { children: _jsxs(CardTitle, { className: "text-base flex items-center gap-2", children: [_jsx(StickyNote, { className: "w-4 h-4" }), "Notities"] }) }), _jsxs(CardContent, { className: "space-y-4", children: [_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-[200px_1fr] gap-3", children: [_jsxs("div", { className: "space-y-1.5", children: [_jsx(Label, { className: "text-xs font-medium text-muted-foreground", children: "Type" }), _jsxs(Select, { value: noteType, onValueChange: (v) => setNoteType(v), children: [_jsx(SelectTrigger, { children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: TYPE_OPTIONS.map((o) => (_jsx(SelectItem, { value: o.value, children: o.label }, o.value))) })] })] }), _jsxs("div", { className: "space-y-1.5", children: [_jsx(Label, { className: "text-xs font-medium text-muted-foreground", children: "Notitie" }), _jsx(Textarea, { value: body, onChange: (e) => setBody(e.target.value), placeholder: "Bijv. Klant gebeld over aflever\u00ADafspraak\u2026", rows: 3 })] })] }), _jsx("div", { className: "flex justify-end", children: _jsxs(Button, { size: "sm", onClick: addNote, disabled: saving, className: "gap-2", children: [_jsx(Plus, { className: "w-4 h-4" }), saving ? "Opslaan…" : "Toevoegen"] }) })] }), loading ? (_jsx("p", { className: "text-sm text-muted-foreground", children: "Laden\u2026" })) : notes.length === 0 ? (_jsx("p", { className: "text-sm text-muted-foreground", children: "Nog geen notities." })) : (_jsx("div", { className: "space-y-2", children: notes.map((n) => (_jsxs("div", { className: "border rounded-lg p-3", children: [_jsxs("div", { className: "flex items-start justify-between gap-3", children: [_jsxs("div", { className: "flex items-center gap-2 flex-wrap", children: [_jsx("span", { className: "text-xs px-2 py-0.5 rounded-full font-medium " +
123
+ return (_jsxs(Card, { id: "notities", children: [_jsx(CardHeader, { children: _jsxs(CardTitle, { className: "text-base flex items-center gap-2", children: [_jsx(StickyNote, { className: "w-4 h-4" }), "Notities"] }) }), _jsxs(CardContent, { className: "space-y-4", children: [_jsx(KlantRisicoStatusCard, { customerId: customerId, onChanged: () => {
124
+ void load();
125
+ onStatusChanged?.();
126
+ } }), _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-[200px_1fr] gap-3", children: [_jsxs("div", { className: "space-y-1.5", children: [_jsx(Label, { className: "text-xs font-medium text-muted-foreground", children: "Type" }), _jsxs(Select, { value: noteType, onValueChange: (v) => setNoteType(v), children: [_jsx(SelectTrigger, { children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: TYPE_OPTIONS.map((o) => (_jsx(SelectItem, { value: o.value, children: o.label }, o.value))) })] })] }), _jsxs("div", { className: "space-y-1.5", children: [_jsx(Label, { className: "text-xs font-medium text-muted-foreground", children: "Notitie" }), _jsx(Textarea, { value: body, onChange: (e) => setBody(e.target.value), placeholder: "Bijv. Klant gebeld over aflever\u00ADafspraak\u2026", rows: 3 })] })] }), _jsx("div", { className: "flex justify-end", children: _jsxs(Button, { size: "sm", onClick: addNote, disabled: saving, className: "gap-2", children: [_jsx(Plus, { className: "w-4 h-4" }), saving ? "Opslaan…" : "Toevoegen"] }) })] }), loading ? (_jsx("p", { className: "text-sm text-muted-foreground", children: "Laden\u2026" })) : notes.length === 0 ? (_jsx("p", { className: "text-sm text-muted-foreground", children: "Nog geen notities." })) : (_jsx("div", { className: "space-y-2", children: notes.map((n) => (_jsxs("div", { className: "border rounded-lg p-3", children: [_jsxs("div", { className: "flex items-start justify-between gap-3", children: [_jsxs("div", { className: "flex items-center gap-2 flex-wrap", children: [_jsx("span", { className: "text-xs px-2 py-0.5 rounded-full font-medium " +
118
127
  TYPE_STYLE[n.note_type], children: typeLabel(n.note_type) }), _jsx("span", { className: "text-xs text-muted-foreground", children: formatDateTime(n.created_at) }), n.created_by && authors[n.created_by] && (_jsxs("span", { className: "text-xs text-muted-foreground", children: ["\u00B7 ", authors[n.created_by]] }))] }), _jsx(Button, { size: "sm", variant: "ghost", onClick: () => removeNote(n.id), className: "text-destructive gap-2 h-7", children: _jsx(Trash2, { className: "w-3.5 h-3.5" }) })] }), _jsx("p", { className: "text-sm text-foreground/90 mt-2 whitespace-pre-wrap", children: n.body })] }, n.id))) }))] })] }));
119
128
  }
@@ -0,0 +1,18 @@
1
+ interface Props {
2
+ status: unknown;
3
+ reason?: string | null;
4
+ className?: string;
5
+ }
6
+ /** Waarschuwingsbalk voor Risicoklant / Geblokkeerd. Toont niets bij een normale klant. */
7
+ export declare function KlantRisicoBanner({ status, reason, className }: Props): import("react").JSX.Element | null;
8
+ export declare function KlantRisicoBadge({ status }: {
9
+ status: unknown;
10
+ }): import("react").JSX.Element | null;
11
+ /** Laadt de klantstatus zelf; handig in pop-up, klantpagina en andere modules. */
12
+ export declare function KlantRisicoBannerById({ customerId, refreshKey, className }: {
13
+ customerId: string;
14
+ refreshKey?: number;
15
+ className?: string;
16
+ }): import("react").JSX.Element | null;
17
+ export {};
18
+ //# sourceMappingURL=KlantRisicoBanner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"KlantRisicoBanner.d.ts","sourceRoot":"","sources":["../../../src/components/klanten/KlantRisicoBanner.tsx"],"names":[],"mappings":"AAKA,UAAU,KAAK;IACb,MAAM,EAAE,OAAO,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,2FAA2F;AAC3F,wBAAgB,iBAAiB,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,SAAc,EAAE,EAAE,KAAK,sCAyB1E;AAED,wBAAgB,gBAAgB,CAAC,EAAE,MAAM,EAAE,EAAE;IAAE,MAAM,EAAE,OAAO,CAAA;CAAE,sCAY/D;AAED,kFAAkF;AAClF,wBAAgB,qBAAqB,CAAC,EAAE,UAAU,EAAE,UAAc,EAAE,SAAS,EAAE,EAAE;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,sCAgB/I"}
@@ -0,0 +1,43 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useState } from "react";
3
+ import { db } from "../../lib/itkClient";
4
+ import { AlertTriangle, Ban } from "lucide-react";
5
+ import { normalizeRiskStatus } from "../../lib/klantRules";
6
+ /** Waarschuwingsbalk voor Risicoklant / Geblokkeerd. Toont niets bij een normale klant. */
7
+ export function KlantRisicoBanner({ status, reason, className = "" }) {
8
+ const s = normalizeRiskStatus(status);
9
+ if (s === "none")
10
+ return null;
11
+ const blocked = s === "blocked";
12
+ const Icon = blocked ? Ban : AlertTriangle;
13
+ return (_jsxs("div", { role: "alert", className: `flex items-start gap-3 rounded-md border p-3 text-sm ${blocked
14
+ ? "border-destructive/40 bg-destructive/10 text-destructive"
15
+ : "border-orange-500/40 bg-orange-500/10 text-orange-700 dark:text-orange-300"} ${className}`, children: [_jsx(Icon, { className: "mt-0.5 h-4 w-4 shrink-0" }), _jsxs("div", { className: "min-w-0", children: [_jsx("p", { className: "font-semibold", children: blocked
16
+ ? "Geblokkeerd — geen nieuwe werkbonnen mogelijk"
17
+ : "Risicoklant — let op bij nieuwe aanvragen" }), reason && _jsxs("p", { className: "mt-0.5 whitespace-pre-wrap text-foreground/80", children: ["Reden: ", reason] })] })] }));
18
+ }
19
+ export function KlantRisicoBadge({ status }) {
20
+ const s = normalizeRiskStatus(status);
21
+ if (s === "none")
22
+ return null;
23
+ return s === "blocked" ? (_jsx("span", { className: "ml-2 rounded-full bg-destructive/15 px-2 py-0.5 text-[10px] font-semibold text-destructive", children: "Geblokkeerd" })) : (_jsx("span", { className: "ml-2 rounded-full bg-orange-500/15 px-2 py-0.5 text-[10px] font-semibold text-orange-700 dark:text-orange-300", children: "Risicoklant" }));
24
+ }
25
+ /** Laadt de klantstatus zelf; handig in pop-up, klantpagina en andere modules. */
26
+ export function KlantRisicoBannerById({ customerId, refreshKey = 0, className }) {
27
+ const [row, setRow] = useState(null);
28
+ useEffect(() => {
29
+ let alive = true;
30
+ void db
31
+ .from("itk_customers")
32
+ .select("risk_status, risk_reason")
33
+ .eq("id", customerId)
34
+ .maybeSingle()
35
+ .then(({ data }) => alive && setRow(data ?? null));
36
+ return () => {
37
+ alive = false;
38
+ };
39
+ }, [customerId, refreshKey]);
40
+ if (!row)
41
+ return null;
42
+ return _jsx(KlantRisicoBanner, { status: row.risk_status, reason: row.risk_reason, className: className });
43
+ }
@@ -0,0 +1,7 @@
1
+ interface Props {
2
+ customerId: string;
3
+ onChanged?: () => void;
4
+ }
5
+ export declare function KlantRisicoStatusCard({ customerId, onChanged }: Props): import("react").JSX.Element;
6
+ export {};
7
+ //# sourceMappingURL=KlantRisicoStatusCard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"KlantRisicoStatusCard.d.ts","sourceRoot":"","sources":["../../../src/components/klanten/KlantRisicoStatusCard.tsx"],"names":[],"mappings":"AAmBA,UAAU,KAAK;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;CACxB;AAED,wBAAgB,qBAAqB,CAAC,EAAE,UAAU,EAAE,SAAS,EAAE,EAAE,KAAK,+BA2GrE"}
@@ -0,0 +1,65 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useEffect, useState } from "react";
3
+ import { Button, Label, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, Textarea, supabase, toast, } from "@flowselections/core";
4
+ import { ShieldAlert } from "lucide-react";
5
+ import { db } from "../../lib/itkClient";
6
+ import { CUSTOMER_RISK_LABELS } from "../../lib/statusConfig";
7
+ import { normalizeRiskStatus, riskReasonMissing } from "../../lib/klantRules";
8
+ import { KlantRisicoBanner } from "./KlantRisicoBanner";
9
+ export function KlantRisicoStatusCard({ customerId, onChanged }) {
10
+ const [current, setCurrent] = useState({
11
+ status: "none",
12
+ reason: null,
13
+ });
14
+ const [status, setStatus] = useState("none");
15
+ const [reason, setReason] = useState("");
16
+ const [saving, setSaving] = useState(false);
17
+ const [failed, setFailed] = useState(false);
18
+ const load = async () => {
19
+ const { data, error } = await db
20
+ .from("itk_customers")
21
+ .select("risk_status, risk_reason")
22
+ .eq("id", customerId)
23
+ .maybeSingle();
24
+ if (error) {
25
+ setFailed(true);
26
+ return;
27
+ }
28
+ setFailed(false);
29
+ const s = normalizeRiskStatus(data?.risk_status);
30
+ setCurrent({ status: s, reason: data?.risk_reason ?? null });
31
+ setStatus(s);
32
+ setReason(data?.risk_reason ?? "");
33
+ };
34
+ useEffect(() => {
35
+ void load();
36
+ // eslint-disable-next-line react-hooks/exhaustive-deps
37
+ }, [customerId]);
38
+ const dirty = status !== current.status || (status !== "none" && reason.trim() !== (current.reason ?? ""));
39
+ const save = async () => {
40
+ if (riskReasonMissing(status, reason)) {
41
+ toast.error("Vul een interne reden in");
42
+ return;
43
+ }
44
+ setSaving(true);
45
+ try {
46
+ const { error } = await supabase.rpc("itk_set_customer_risk", {
47
+ _customer_id: customerId,
48
+ _status: status,
49
+ _reason: status === "none" ? null : reason.trim(),
50
+ });
51
+ if (error)
52
+ throw error;
53
+ toast.success("Klantstatus opgeslagen");
54
+ await load();
55
+ onChanged?.();
56
+ }
57
+ catch (e) {
58
+ toast.error(e?.message ?? "Opslaan mislukt");
59
+ }
60
+ finally {
61
+ setSaving(false);
62
+ }
63
+ };
64
+ return (_jsxs("div", { className: "space-y-3 rounded-lg border p-4", children: [_jsxs("div", { className: "flex items-center gap-2 text-sm font-medium text-foreground", children: [_jsx(ShieldAlert, { className: "h-4 w-4" }), "Blacklist"] }), failed ? (_jsx("p", { className: "text-sm text-muted-foreground", children: "Blacklist-status kon niet geladen worden." })) : (_jsxs(_Fragment, { children: [_jsx(KlantRisicoBanner, { status: current.status, reason: current.reason }), _jsxs("div", { className: "grid grid-cols-1 gap-3 md:grid-cols-[200px_1fr]", children: [_jsxs("div", { className: "space-y-1.5", children: [_jsx(Label, { className: "text-xs font-medium text-muted-foreground", children: "Status" }), _jsxs(Select, { value: status, onValueChange: (v) => setStatus(v), children: [_jsx(SelectTrigger, { children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: Object.keys(CUSTOMER_RISK_LABELS).map((k) => (_jsx(SelectItem, { value: k, children: CUSTOMER_RISK_LABELS[k] }, k))) })] })] }), status !== "none" && (_jsxs("div", { className: "space-y-1.5", children: [_jsxs(Label, { className: "text-xs font-medium text-muted-foreground", children: ["Interne reden ", _jsx("span", { className: "text-destructive", children: "*" })] }), _jsx(Textarea, { value: reason, onChange: (e) => setReason(e.target.value), placeholder: "Bijv. Factuur na 3 herinneringen niet betaald", rows: 2 })] }))] }), _jsx("div", { className: "flex justify-end", children: _jsx(Button, { size: "sm", onClick: save, disabled: saving || !dirty, children: saving ? "Opslaan…" : "Status opslaan" }) })] }))] }));
65
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"KlantStatsCard.d.ts","sourceRoot":"","sources":["../../../src/components/klanten/KlantStatsCard.tsx"],"names":[],"mappings":"AAIA,wBAAgB,cAAc,CAAC,EAAE,UAAU,EAAE,EAAE;IAAE,UAAU,EAAE,MAAM,CAAA;CAAE,+BA2CpE"}
1
+ {"version":3,"file":"KlantStatsCard.d.ts","sourceRoot":"","sources":["../../../src/components/klanten/KlantStatsCard.tsx"],"names":[],"mappings":"AAwBA,wBAAgB,cAAc,CAAC,EAAE,UAAU,EAAE,EAAE;IAAE,UAAU,EAAE,MAAM,CAAA;CAAE,+BA0FpE"}
@@ -1,7 +1,13 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { jsxs as _jsxs, jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { useEffect, useState } from "react";
3
- import { Euro, Wrench } from "lucide-react";
4
- import { fetchKlantStats, formatEuro, emptyStats } from "../../lib/klantStatsClient";
3
+ import { CalendarClock, Clock, Euro, TrendingUp, Wrench } from "lucide-react";
4
+ import { emptyStats, fetchKlantStats, formatEuro, } from "../../lib/klantStatsClient";
5
+ import { berekenWinst } from "../../lib/klantRules";
6
+ function Tile({ icon, label, children }) {
7
+ return (_jsxs("div", { className: "rounded-md border p-3 min-w-0", children: [_jsxs("p", { className: "text-xs text-muted-foreground flex items-center gap-1", children: [icon, " ", label] }), children] }));
8
+ }
9
+ const big = "text-xl font-semibold text-foreground tabular-nums";
10
+ const muted = "text-sm text-muted-foreground pt-1";
5
11
  export function KlantStatsCard({ customerId }) {
6
12
  const [stats, setStats] = useState(null);
7
13
  const [failed, setFailed] = useState(false);
@@ -17,5 +23,9 @@ export function KlantStatsCard({ customerId }) {
17
23
  };
18
24
  }, [customerId]);
19
25
  const placeholder = failed ? "Niet beschikbaar" : "…";
20
- return (_jsxs("div", { className: "grid grid-cols-2 gap-3", children: [_jsxs("div", { className: "rounded-md border p-3", children: [_jsxs("p", { className: "text-xs text-muted-foreground flex items-center gap-1", children: [_jsx(Euro, { className: "w-3.5 h-3.5" }), " Totale omzet"] }), _jsx("p", { className: stats ? "text-xl font-semibold text-foreground" : "text-sm text-muted-foreground pt-1", children: stats ? formatEuro(stats.omzetCents) : placeholder })] }), _jsxs("div", { className: "rounded-md border p-3", children: [_jsxs("p", { className: "text-xs text-muted-foreground flex items-center gap-1", children: [_jsx(Wrench, { className: "w-3.5 h-3.5" }), " Aantal klussen"] }), _jsx("p", { className: stats ? "text-xl font-semibold text-foreground" : "text-sm text-muted-foreground pt-1", children: stats ? stats.klussen : placeholder }), stats && (_jsxs("p", { className: "text-xs text-muted-foreground", children: [stats.open, " open \u00B7 ", stats.bezig, " bezig \u00B7 ", stats.afgerond, " afgerond"] }))] })] }));
26
+ const winst = stats
27
+ ? berekenWinst({ omzetCents: stats.omzetCents, materiaalCents: stats.materiaalKostenCents, arbeidCents: stats.arbeidKostenCents })
28
+ : null;
29
+ const zonderLoon = stats?.monteursZonderLoon ?? [];
30
+ return (_jsx("div", { className: "space-y-3", children: _jsxs("div", { className: "grid grid-cols-2 lg:grid-cols-3 gap-3", children: [_jsx(Tile, { icon: _jsx(Euro, { className: "w-3.5 h-3.5" }), label: "Totale omzet", children: _jsx("p", { className: stats ? big : muted, children: stats ? formatEuro(stats.omzetCents) : placeholder }) }), _jsxs(Tile, { icon: _jsx(Wrench, { className: "w-3.5 h-3.5" }), label: "Aantal klussen", children: [_jsx("p", { className: stats ? big : muted, children: stats ? stats.klussen : placeholder }), stats && (_jsxs("p", { className: "text-xs text-muted-foreground", children: [stats.open, " open \u00B7 ", stats.bezig, " bezig \u00B7 ", stats.afgerond, " afgerond"] }))] }), _jsx(Tile, { icon: _jsx(Clock, { className: "w-3.5 h-3.5" }), label: "Te laat betaald", children: stats && stats.teLaat != null ? (_jsxs(_Fragment, { children: [_jsx("p", { className: stats.teLaat > 0 ? `${big} text-destructive` : big, children: stats.teLaat }), (stats.teLaatOpen ?? 0) > 0 && (_jsxs("p", { className: "text-xs text-muted-foreground", children: ["waarvan ", stats.teLaatOpen, " nog open"] }))] })) : (_jsx("p", { className: muted, children: stats ? "n.b." : placeholder })) }), _jsx(Tile, { icon: _jsx(CalendarClock, { className: "w-3.5 h-3.5" }), label: "Gem. betaaltermijn", children: stats && stats.gemBetaaltermijnDagen != null ? (_jsxs("p", { className: big, children: [Math.round(stats.gemBetaaltermijnDagen), " ", Math.round(stats.gemBetaaltermijnDagen) === 1 ? "dag" : "dagen"] })) : (_jsx("p", { className: muted, children: stats ? "n.b." : placeholder })) }), _jsxs("div", { className: "col-span-2 rounded-md border p-3 min-w-0", children: [_jsx("div", { className: "flex items-center justify-between gap-2", children: _jsxs("p", { className: "text-xs text-muted-foreground flex items-center gap-1", children: [_jsx(TrendingUp, { className: "w-3.5 h-3.5" }), " Winstgevendheid"] }) }), winst ? (_jsxs(_Fragment, { children: [_jsxs("p", { className: winst.winstCents < 0 ? `${big} text-destructive` : big, children: [formatEuro(winst.winstCents), winst.marge != null && (_jsxs("span", { className: "ml-2 text-sm font-normal text-muted-foreground", children: [Math.round(winst.marge * 100), "%"] }))] }), _jsxs("p", { className: "text-xs text-muted-foreground", children: ["Kosten ", formatEuro(winst.kostenCents), ": materialen ", formatEuro(winst.materiaalCents), " \u00B7 uren en reistijd", " ", formatEuro(winst.arbeidCents), " (uurloon per monteur)"] }), zonderLoon.length > 0 && (_jsxs("p", { className: "text-xs text-destructive", children: ["Uurloon ontbreekt voor: ", zonderLoon.join(", ")] }))] })) : (_jsx("p", { className: muted, children: !stats ? placeholder : "Stel uurlonen in bij de urenregistratie" }))] })] }) }));
21
31
  }
@@ -0,0 +1,2 @@
1
+ export declare function KlantenTrendCard(): import("react").JSX.Element;
2
+ //# sourceMappingURL=KlantenTrendCard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"KlantenTrendCard.d.ts","sourceRoot":"","sources":["../../../src/components/klanten/KlantenTrendCard.tsx"],"names":[],"mappings":"AA4DA,wBAAgB,gBAAgB,gCAqD/B"}
@@ -0,0 +1,52 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useEffect, useState } from "react";
3
+ import { Card, CardContent } from "@flowselections/core";
4
+ import { TrendingDown, TrendingUp } from "lucide-react";
5
+ import { fetchMonthTrend, formatEuro } from "../../lib/klantStatsClient";
6
+ import { berekenWinst, projectMonth, trend } from "../../lib/klantRules";
7
+ const pad = (n) => String(n).padStart(2, "0");
8
+ const isoLocal = (d) => `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`;
9
+ const monthLabel = (iso) => new Intl.DateTimeFormat("nl-NL", { month: "short", year: "numeric" }).format(new Date(`${iso}T12:00:00`));
10
+ function winstVan(p) {
11
+ return berekenWinst({ omzetCents: p.omzetCents, materiaalCents: p.materiaalKostenCents, arbeidCents: p.arbeidKostenCents })?.winstCents ?? null;
12
+ }
13
+ function TrendTile({ titel, vorig, verwachting, vorigLabel, nuLabel, melding, }) {
14
+ const ok = melding == null && vorig != null && verwachting != null;
15
+ const t = ok ? trend(verwachting, vorig) : null;
16
+ const up = (t?.verschilCents ?? 0) >= 0;
17
+ const Icon = up ? TrendingUp : TrendingDown;
18
+ return (_jsx(Card, { children: _jsxs(CardContent, { className: "p-4 space-y-1", children: [_jsxs("div", { className: "flex items-center justify-between gap-2", children: [_jsx("h3", { className: "text-sm font-medium text-muted-foreground", children: titel }), t && t.procent != null && (_jsxs("span", { className: `inline-flex items-center gap-1 rounded-full px-2 py-0.5 text-xs font-semibold tabular-nums ${up ? "bg-[var(--success-soft)] text-[var(--success-soft-foreground)]" : "bg-destructive/10 text-destructive"}`, children: [_jsx(Icon, { className: "w-3.5 h-3.5" }), up ? "+" : "−", Math.abs(Math.round(t.procent * 100)), "%"] })), t && t.procent == null && _jsx("span", { className: "text-xs text-muted-foreground", children: "geen vergelijking" })] }), ok ? (_jsxs(_Fragment, { children: [_jsx("p", { className: "text-2xl font-semibold text-foreground tabular-nums", children: formatEuro(verwachting) }), _jsxs("p", { className: "text-xs text-muted-foreground", children: ["Verwachting ", nuLabel] }), _jsxs("p", { className: "pt-2 text-sm text-muted-foreground tabular-nums", children: [vorigLabel, ": ", _jsx("span", { className: "text-foreground", children: formatEuro(vorig) })] }), _jsxs("p", { className: `text-sm tabular-nums ${up ? "text-[var(--success-text)]" : "text-destructive"}`, children: [up ? "+" : "−", " ", formatEuro(Math.abs(t.verschilCents)), " t.o.v. vorig jaar"] })] })) : (_jsx("p", { className: "pt-2 text-sm text-muted-foreground", children: melding }))] }) }));
19
+ }
20
+ export function KlantenTrendCard() {
21
+ const [data, setData] = useState(null);
22
+ const [failed, setFailed] = useState(false);
23
+ const today = new Date();
24
+ useEffect(() => {
25
+ let active = true;
26
+ fetchMonthTrend(isoLocal(new Date()))
27
+ .then((d) => active && setData(d))
28
+ .catch(() => active && setFailed(true));
29
+ return () => {
30
+ active = false;
31
+ };
32
+ }, []);
33
+ const dag = today.getDate();
34
+ const dagen = new Date(today.getFullYear(), today.getMonth() + 1, 0).getDate();
35
+ if (failed)
36
+ return _jsx("p", { className: "text-sm text-muted-foreground", children: "Maandvergelijking niet beschikbaar" });
37
+ if (!data)
38
+ return _jsx("p", { className: "text-sm text-muted-foreground", children: "Maandvergelijking laden\u2026" });
39
+ const omzetNu = projectMonth(data.nu.omzetCents, dag, dagen);
40
+ const omzetVorig = data.vorigJaar.omzetCents;
41
+ const wNuRaw = winstVan(data.nu);
42
+ const wNuKosten = wNuRaw == null ? null : data.nu.omzetCents - wNuRaw;
43
+ const winstNu = wNuKosten == null ? null : omzetNu - projectMonth(wNuKosten, dag, dagen);
44
+ const winstVorig = winstVan(data.vorigJaar);
45
+ const zonderLoon = Array.from(new Set([...data.nu.monteursZonderLoon, ...data.vorigJaar.monteursZonderLoon]));
46
+ const vorigLabel = monthLabel(data.vorigJaar.van);
47
+ const nuLabel = monthLabel(data.nu.van);
48
+ const winstMelding = winstNu != null && winstVorig != null
49
+ ? undefined
50
+ : "Stel uurlonen in bij de urenregistratie";
51
+ return (_jsxs("div", { className: "space-y-2", children: [_jsxs("div", { className: "grid grid-cols-1 sm:grid-cols-2 gap-4", children: [_jsx(TrendTile, { titel: "Omzet", vorig: omzetVorig, verwachting: omzetNu, vorigLabel: vorigLabel, nuLabel: nuLabel }), _jsx(TrendTile, { titel: "Winst", vorig: winstVorig ?? undefined, verwachting: winstNu ?? undefined, vorigLabel: vorigLabel, nuLabel: nuLabel, melding: winstMelding })] }), _jsxs("p", { className: "text-xs text-muted-foreground", children: [zonderLoon.length > 0 && _jsxs("span", { className: "text-destructive", children: ["Uurloon ontbreekt voor: ", zonderLoon.join(", "), ". "] }), "Verwachting = stand t/m vandaag (", dag, " van ", dagen, " dagen) doorgetrokken naar de hele maand, vergeleken met dezelfde maand vorig jaar. Alle klanten samen."] })] }));
52
+ }
@@ -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,cAS3B,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;;;;;;;;;yBA2EoB,MAAM,SAAS,OAAO,CAAC,UAAU,CAAC;yBAElC,MAAM;;;0BAgBL,MAAM,SAAS,OAAO,CAAC,WAAW,CAAC;0BAEnC,MAAM;;;;;;;EA0IpC"}
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
@@ -2,6 +2,7 @@ import type { FlowModule } from "@flowselections/core";
2
2
  export * from "./_core-safelist";
3
3
  export { KlantenListPage as KlantenListPagePlanner } from "./components/KlantenListPage";
4
4
  export { KlantenDetailPage as KlantenDetailPagePlanner } from "./components/KlantenDetailPage";
5
+ export { KlantRisicoBanner, KlantRisicoBannerById, KlantRisicoBadge } from "./components/klanten/KlantRisicoBanner";
5
6
  export { WerkbonDetailPage as WerkbonDetailPagePlanner } from "./components/WerkbonDetailPage";
6
7
  export declare const klantenInstallatieTechniekPlannerModule: FlowModule;
7
8
  export declare const KlantenInstallatieTechniekPlanner: FlowModule;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAEvD,cAAc,kBAAkB,CAAC;AACjC,OAAO,EAAE,eAAe,IAAI,sBAAsB,EAAE,MAAM,8BAA8B,CAAC;AACzF,OAAO,EAAE,iBAAiB,IAAI,wBAAwB,EAAE,MAAM,gCAAgC,CAAC;AAC/F,OAAO,EAAE,iBAAiB,IAAI,wBAAwB,EAAE,MAAM,gCAAgC,CAAC;AAE/F,eAAO,MAAM,uCAAuC,EAAE,UAUrD,CAAC;AAGF,eAAO,MAAM,iCAAiC,YAA0C,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAEvD,cAAc,kBAAkB,CAAC;AACjC,OAAO,EAAE,eAAe,IAAI,sBAAsB,EAAE,MAAM,8BAA8B,CAAC;AACzF,OAAO,EAAE,iBAAiB,IAAI,wBAAwB,EAAE,MAAM,gCAAgC,CAAC;AAC/F,OAAO,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,MAAM,wCAAwC,CAAC;AACpH,OAAO,EAAE,iBAAiB,IAAI,wBAAwB,EAAE,MAAM,gCAAgC,CAAC;AAE/F,eAAO,MAAM,uCAAuC,EAAE,UAUrD,CAAC;AAGF,eAAO,MAAM,iCAAiC,YAA0C,CAAC"}