@flowselections/floriday-klanten-module 1.0.45 → 1.0.48

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 (48) hide show
  1. package/dist-lib/components/klanten/BewerkKlant.d.ts.map +1 -1
  2. package/dist-lib/components/klanten/BewerkKlant.js +1 -1
  3. package/dist-lib/components/klanten/CustomerAnalyticsTab.d.ts.map +1 -1
  4. package/dist-lib/components/klanten/CustomerAnalyticsTab.js +2 -2
  5. package/dist-lib/components/klanten/FloridayNetworkStatus.d.ts +5 -0
  6. package/dist-lib/components/klanten/FloridayNetworkStatus.d.ts.map +1 -0
  7. package/dist-lib/components/klanten/FloridayNetworkStatus.js +18 -0
  8. package/dist-lib/components/klanten/KlantDetailLayout.d.ts +2 -0
  9. package/dist-lib/components/klanten/KlantDetailLayout.d.ts.map +1 -1
  10. package/dist-lib/components/klanten/KlantDetailLayout.js +6 -1
  11. package/dist-lib/components/klanten/KlantFormulier.d.ts +3 -1
  12. package/dist-lib/components/klanten/KlantFormulier.d.ts.map +1 -1
  13. package/dist-lib/components/klanten/KlantFormulier.js +16 -4
  14. package/dist-lib/components/klanten/KlantenOverzicht.d.ts.map +1 -1
  15. package/dist-lib/components/klanten/KlantenOverzicht.js +6 -5
  16. package/dist-lib/components/klanten/NetwerkPagina.d.ts.map +1 -1
  17. package/dist-lib/components/klanten/NetwerkPagina.js +9 -5
  18. package/dist-lib/components/klanten/NieuweKlant.d.ts.map +1 -1
  19. package/dist-lib/components/klanten/NieuweKlant.js +10 -7
  20. package/dist-lib/components/klanten/pricegroups/CustomerPickerDialog.d.ts.map +1 -1
  21. package/dist-lib/components/klanten/pricegroups/CustomerPickerDialog.js +10 -2
  22. package/dist-lib/components/klanten/pricegroups/PriceGroupsPage.d.ts.map +1 -1
  23. package/dist-lib/components/klanten/pricegroups/PriceGroupsPage.js +4 -7
  24. package/dist-lib/components/klanten/pricegroups/UnknownFloridayMembers.d.ts +11 -0
  25. package/dist-lib/components/klanten/pricegroups/UnknownFloridayMembers.d.ts.map +1 -0
  26. package/dist-lib/components/klanten/pricegroups/UnknownFloridayMembers.js +43 -0
  27. package/dist-lib/index.js +1 -1
  28. package/dist-lib/integrations/supabase/types.d.ts +1470 -2
  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/customer-schemas.d.ts +2 -0
  32. package/dist-lib/lib/customer-schemas.d.ts.map +1 -1
  33. package/dist-lib/lib/customer-schemas.js +2 -0
  34. package/dist-lib/lib/customers.functions.d.ts +37 -0
  35. package/dist-lib/lib/customers.functions.d.ts.map +1 -1
  36. package/dist-lib/lib/customers.functions.js +49 -4
  37. package/dist-lib/lib/floriday-network-status.functions.d.ts +18 -0
  38. package/dist-lib/lib/floriday-network-status.functions.d.ts.map +1 -0
  39. package/dist-lib/lib/floriday-network-status.functions.js +41 -0
  40. package/dist-lib/lib/price-groups-local.functions.d.ts +42 -0
  41. package/dist-lib/lib/price-groups-local.functions.d.ts.map +1 -1
  42. package/dist-lib/lib/price-groups-local.functions.js +120 -1
  43. package/dist-lib/lib/twinfield-adopt.functions.d.ts +2 -0
  44. package/dist-lib/lib/twinfield-adopt.functions.d.ts.map +1 -1
  45. package/dist-lib/lib/vat-format.d.ts +17 -0
  46. package/dist-lib/lib/vat-format.d.ts.map +1 -0
  47. package/dist-lib/lib/vat-format.js +64 -0
  48. package/package.json +5 -5
@@ -1 +1 @@
1
- {"version":3,"file":"BewerkKlant.d.ts","sourceRoot":"","sources":["../../../src/components/klanten/BewerkKlant.tsx"],"names":[],"mappings":"AAkEA,wBAAgB,WAAW,CAAC,EAC1B,EAAE,EACF,MAAM,EACN,QAAQ,EACR,YAAY,GACb,EAAE;IACD,EAAE,EAAE,MAAM,CAAC;IACX,uEAAuE;IACvE,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,gEAAgE;IAChE,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB,+BAkFA"}
1
+ {"version":3,"file":"BewerkKlant.d.ts","sourceRoot":"","sources":["../../../src/components/klanten/BewerkKlant.tsx"],"names":[],"mappings":"AAkEA,wBAAgB,WAAW,CAAC,EAC1B,EAAE,EACF,MAAM,EACN,QAAQ,EACR,YAAY,GACb,EAAE;IACD,EAAE,EAAE,MAAM,CAAC;IACX,uEAAuE;IACvE,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,gEAAgE;IAChE,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB,+BAmFA"}
@@ -98,7 +98,7 @@ export function BewerkKlant({ id, onDone, onCancel, hideBackLink, }) {
98
98
  return (_jsxs("main", { className: "p-6 space-y-3", children: [!hideBackLink && _jsx(BackLink, {}), _jsx("p", { className: "text-sm text-destructive", children: error instanceof Error ? error.message : "Klant niet gevonden." })] }));
99
99
  }
100
100
  const c = data.customer;
101
- return (_jsx(KlantFormulier, { title: `${c.company_name || "Klant"} bewerken`, description: "Pas de gegevens aan. Wijzigingen worden direct naar Twinfield doorgestuurd.", submitLabel: "Wijzigingen opslaan", submitting: mut.isPending, defaultValues: toFormValues(c), numberValue: c.twinfield_code ? String(c.twinfield_code) : undefined, numberHint: c.twinfield_code ? "Debiteurnummer in Twinfield" : undefined, creditInfo: {
101
+ return (_jsx(KlantFormulier, { title: `${c.company_name || "Klant"} bewerken`, description: "Pas de gegevens aan. Wijzigingen worden direct naar Twinfield doorgestuurd.", submitLabel: "Wijzigingen opslaan", submitting: mut.isPending, defaultValues: toFormValues(c), numberValue: c.twinfield_code ? String(c.twinfield_code) : undefined, numberHint: c.twinfield_code ? "Debiteurnummer in Twinfield" : undefined, syncError: c.twinfield_sync_error ?? null, creditInfo: {
102
102
  credit_manager: c.credit_manager,
103
103
  credit_manager_name: c.credit_manager_name,
104
104
  reminder_email: c.reminder_email,
@@ -1 +1 @@
1
- {"version":3,"file":"CustomerAnalyticsTab.d.ts","sourceRoot":"","sources":["../../../src/components/klanten/CustomerAnalyticsTab.tsx"],"names":[],"mappings":"AA0GA,wBAAgB,oBAAoB,CAAC,EAAE,UAAU,EAAE,EAAE;IAAE,UAAU,EAAE,MAAM,CAAA;CAAE,sCA6N1E"}
1
+ {"version":3,"file":"CustomerAnalyticsTab.d.ts","sourceRoot":"","sources":["../../../src/components/klanten/CustomerAnalyticsTab.tsx"],"names":[],"mappings":"AA0GA,wBAAgB,oBAAoB,CAAC,EAAE,UAAU,EAAE,EAAE;IAAE,UAAU,EAAE,MAAM,CAAA;CAAE,sCAqO1E"}
@@ -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 { useMemo, useState } from "react";
3
3
  import { formatDate as formatDateFull } from "../../lib/format-date.js";
4
4
  import { formatEur } from "../../lib/format-currency.js";
@@ -100,7 +100,7 @@ export function CustomerAnalyticsTab({ customerId }) {
100
100
  return null;
101
101
  const a = data;
102
102
  const k = a.kpis;
103
- return (_jsxs("div", { className: "space-y-4", children: [_jsx(Card, { children: _jsx(CardContent, { className: "pt-4 pb-4", children: _jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [PRESETS.map((p) => (_jsx(Button, { size: "sm", variant: preset === p.key ? "default" : "outline", onClick: () => setPreset(p.key), children: p.label }, p.key))), preset === "custom" && (_jsxs("div", { className: "flex items-center gap-2 ml-2", children: [_jsx(Input, { type: "date", value: customFrom, onChange: (e) => setCustomFrom(e.target.value), className: "h-8 w-40" }), _jsx("span", { className: "text-xs text-muted-foreground", children: "tot" }), _jsx(Input, { type: "date", value: customTo, onChange: (e) => setCustomTo(e.target.value), className: "h-8 w-40" })] }))] }) }) }), _jsxs("div", { className: "grid gap-3 grid-cols-2 md:grid-cols-4", children: [_jsx(Kpi, { label: "Omzet in periode", value: formatEUR(k.revenue_period), delta: k.revenue_growth_pct, hint: `vs vorige periode: ${formatEUR(k.revenue_compare)}` }), _jsx(Kpi, { label: "Aantal bestellingen", value: formatNum(k.order_count_period), hint: `totaal: ${formatNum(k.order_count)}` }), _jsx(Kpi, { label: "Gem. orderwaarde", value: formatEUR(k.avg_order_value) }), _jsx(Kpi, { label: "Totale omzet (alle tijd)", value: formatEUR(k.total_revenue) }), _jsx(Kpi, { label: "Dit jaar", value: formatEUR(k.revenue_this_year), hint: `vorig jaar: ${formatEUR(k.revenue_last_year)}` }), _jsx(Kpi, { label: "Deze maand", value: formatEUR(k.revenue_this_month), hint: `vorige maand: ${formatEUR(k.revenue_last_month)}` }), _jsx(Kpi, { label: "Grootste bestelling", value: formatEUR(k.largest_order), hint: `kleinste: ${formatEUR(k.smallest_order)}` }), _jsx(Kpi, { label: "Gem. tijd tussen bestellingen", value: k.avg_days_between_orders != null ? `${formatNum(k.avg_days_between_orders, 1)} dgn` : "—", hint: k.avg_orders_per_month != null ? `${formatNum(k.avg_orders_per_month, 1)} / maand` : undefined })] }), _jsxs("div", { className: "grid gap-3 grid-cols-1 md:grid-cols-3 text-sm", children: [_jsx(Card, { children: _jsxs(CardContent, { className: "pt-4 pb-4", children: [_jsx("div", { className: "text-xs text-muted-foreground", children: "Eerste bestelling" }), _jsx("div", { className: "font-medium", children: formatDate(k.first_order_date) })] }) }), _jsx(Card, { children: _jsxs(CardContent, { className: "pt-4 pb-4", children: [_jsx("div", { className: "text-xs text-muted-foreground", children: "Laatste bestelling" }), _jsx("div", { className: "font-medium", children: formatDate(k.last_order_date) })] }) }), _jsx(Card, { children: _jsxs(CardContent, { className: "pt-4 pb-4", children: [_jsx("div", { className: "text-xs text-muted-foreground", children: "Periode" }), _jsxs("div", { className: "font-medium", children: [formatDate(a.period.from), " \u2013 ", formatDate(new Date(new Date(a.period.to).getTime() - 86400000).toISOString())] })] }) })] }), _jsxs("div", { className: "grid gap-4 lg:grid-cols-2", children: [_jsxs(Card, { children: [_jsx(CardHeader, { children: _jsx(CardTitle, { className: "text-base", children: "Omzet per maand" }) }), _jsx(CardContent, { className: "h-72", children: a.monthly.length === 0 ? _jsx(EmptyChart, {}) : (_jsx(ResponsiveContainer, { width: "100%", height: "100%", children: _jsxs(LineChart, { data: a.monthly, margin: { left: 30, right: 10 }, children: [_jsx(CartesianGrid, { strokeDasharray: "3 3", stroke: "hsl(var(--border))" }), _jsx(XAxis, { dataKey: "label", fontSize: 11 }), _jsx(YAxis, { fontSize: 11, width: 100, tickFormatter: (v) => formatEUR(Number(v)) }), _jsx(Tooltip, { formatter: (v) => formatEUR(Number(v)) }), _jsx(Line, { type: "monotone", dataKey: "revenue", stroke: "#2563eb", strokeWidth: 2, dot: false, name: "Omzet" })] }) })) })] }), _jsxs(Card, { children: [_jsx(CardHeader, { children: _jsx(CardTitle, { className: "text-base", children: "Aantal bestellingen per maand" }) }), _jsx(CardContent, { className: "h-72", children: a.monthly.length === 0 ? _jsx(EmptyChart, {}) : (_jsx(ResponsiveContainer, { width: "100%", height: "100%", children: _jsxs(BarChart, { data: a.monthly, margin: { left: 30, right: 10 }, children: [_jsx(CartesianGrid, { strokeDasharray: "3 3", stroke: "hsl(var(--border))" }), _jsx(XAxis, { dataKey: "label", fontSize: 11 }), _jsx(YAxis, { fontSize: 11, width: 40, allowDecimals: false }), _jsx(Tooltip, {}), _jsx(Bar, { dataKey: "orders", fill: "#16a34a", name: "Bestellingen" })] }) })) })] }), _jsxs(Card, { children: [_jsx(CardHeader, { children: _jsx(CardTitle, { className: "text-base", children: "Gem. orderwaarde per maand" }) }), _jsx(CardContent, { className: "h-72", children: a.monthly.length === 0 ? _jsx(EmptyChart, {}) : (_jsx(ResponsiveContainer, { width: "100%", height: "100%", children: _jsxs(LineChart, { data: a.monthly, margin: { left: 30, right: 10 }, children: [_jsx(CartesianGrid, { strokeDasharray: "3 3", stroke: "hsl(var(--border))" }), _jsx(XAxis, { dataKey: "label", fontSize: 11 }), _jsx(YAxis, { fontSize: 11, width: 100, tickFormatter: (v) => formatEUR(Number(v)) }), _jsx(Tooltip, { formatter: (v) => formatEUR(Number(v)) }), _jsx(Line, { type: "monotone", dataKey: "aov", stroke: "#f59e0b", strokeWidth: 2, dot: false, name: "Gem. waarde" })] }) })) })] }), _jsxs(Card, { children: [_jsx(CardHeader, { children: _jsx(CardTitle, { className: "text-base", children: "Omzet per jaar" }) }), _jsx(CardContent, { className: "h-72", children: a.yearly.length === 0 ? _jsx(EmptyChart, {}) : (_jsx(ResponsiveContainer, { width: "100%", height: "100%", children: _jsxs(BarChart, { data: a.yearly, margin: { left: 30, right: 10 }, children: [_jsx(CartesianGrid, { strokeDasharray: "3 3", stroke: "hsl(var(--border))" }), _jsx(XAxis, { dataKey: "label", fontSize: 11 }), _jsx(YAxis, { fontSize: 11, width: 100, tickFormatter: (v) => formatEUR(Number(v)) }), _jsx(Tooltip, { formatter: (v) => formatEUR(Number(v)) }), _jsx(Bar, { dataKey: "revenue", fill: "#7c3aed", name: "Omzet" })] }) })) })] })] }), _jsxs("div", { className: "grid gap-4 lg:grid-cols-[2fr_1fr]", children: [_jsxs(Card, { children: [_jsx(CardHeader, { children: _jsx(CardTitle, { className: "text-base", children: "Top artikelen in periode" }) }), _jsx(CardContent, { className: "p-0", children: a.topProducts.length === 0 ? (_jsx("div", { className: "p-6 text-sm text-muted-foreground", children: "Geen orderregels in deze periode." })) : (_jsxs(Table, { children: [_jsx(TableHeader, { children: _jsxs(TableRow, { children: [_jsx(TableHead, { children: "Product" }), _jsx(TableHead, { className: "text-right", children: "Aantal" }), _jsx(TableHead, { children: "Eenheid" }), _jsx(TableHead, { className: "text-right", children: "Omzet" })] }) }), _jsx(TableBody, { children: a.topProducts.map((p) => (_jsxs(TableRow, { children: [_jsx(TableCell, { className: "font-medium", children: displayProductName(p.product_name) }), _jsx(TableCell, { className: "text-right tabular-nums", children: formatNum(p.quantity) }), _jsx(TableCell, { className: "text-muted-foreground", children: p.unit ?? "—" }), _jsx(TableCell, { className: "text-right tabular-nums", children: formatEUR(p.revenue) })] }, p.product_name))) })] })) })] }), _jsxs("div", { className: "space-y-4", children: [_jsxs(Card, { children: [_jsx(CardHeader, { children: _jsx(CardTitle, { className: "text-base", children: "Verkocht per eenheid" }) }), _jsx(CardContent, { className: "space-y-2", children: a.unitTotals.length === 0 ? (_jsx("div", { className: "text-sm text-muted-foreground", children: "Geen data." })) : a.unitTotals.map((u) => (_jsxs("div", { className: "flex items-center justify-between text-sm", children: [_jsx("span", { className: "capitalize", children: u.unit }), _jsx("span", { className: "tabular-nums font-medium", children: formatNum(u.quantity) })] }, u.unit))) })] }), _jsxs(Card, { children: [_jsx(CardHeader, { children: _jsx(CardTitle, { className: "text-base", children: "Status van bestellingen" }) }), _jsx(CardContent, { className: "h-56", children: a.statusBreakdown.length === 0 ? _jsx(EmptyChart, {}) : (_jsx(ResponsiveContainer, { width: "100%", height: "100%", children: _jsxs(PieChart, { children: [_jsx(Pie, { data: a.statusBreakdown, dataKey: "count", nameKey: "status", outerRadius: 70, label: true, children: a.statusBreakdown.map((_, i) => (_jsx(Cell, { fill: PIE_COLORS[i % PIE_COLORS.length] }, i))) }), _jsx(Tooltip, {}), _jsx(Legend, {})] }) })) })] })] })] }), _jsx(Card, { children: _jsx(CardContent, { className: "pt-4 pb-4 text-xs text-muted-foreground space-y-1", children: _jsxs("div", { children: [_jsx(Badge, { variant: "secondary", children: "Let op" }), " Sommige analyses zijn nog niet beschikbaar omdat de onderliggende data ontbreekt: btw-bedragen, marge en vervoerder/afhaallocatie per bestelling. Voeg deze velden toe aan orders/orderregels om die overzichten te activeren."] }) }) })] }));
103
+ return (_jsxs("div", { className: "space-y-4", children: [_jsx(Card, { children: _jsx(CardContent, { className: "pt-4 pb-4", children: _jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [PRESETS.map((p) => (_jsx(Button, { size: "sm", variant: preset === p.key ? "default" : "outline", onClick: () => setPreset(p.key), children: p.label }, p.key))), preset === "custom" && (_jsxs("div", { className: "flex items-center gap-2 ml-2", children: [_jsx(Input, { type: "date", value: customFrom, onChange: (e) => setCustomFrom(e.target.value), className: "h-8 w-40" }), _jsx("span", { className: "text-xs text-muted-foreground", children: "tot" }), _jsx(Input, { type: "date", value: customTo, onChange: (e) => setCustomTo(e.target.value), className: "h-8 w-40" })] }))] }) }) }), _jsxs("div", { className: "grid gap-3 grid-cols-2 md:grid-cols-4", children: [_jsx(Kpi, { label: "Omzet in periode", value: formatEUR(k.revenue_period), delta: k.revenue_growth_pct, hint: `vs vorige periode: ${formatEUR(k.revenue_compare)}` }), _jsx(Kpi, { label: "Aantal bestellingen", value: formatNum(k.order_count_period), hint: `totaal: ${formatNum(k.order_count)}` }), _jsx(Kpi, { label: "Gem. orderwaarde", value: formatEUR(k.avg_order_value) }), _jsx(Kpi, { label: "Totale omzet (alle tijd)", value: formatEUR(k.total_revenue) }), _jsx(Kpi, { label: "Dit jaar", value: formatEUR(k.revenue_this_year), hint: `vorig jaar: ${formatEUR(k.revenue_last_year)}` }), _jsx(Kpi, { label: "Deze maand", value: formatEUR(k.revenue_this_month), hint: `vorige maand: ${formatEUR(k.revenue_last_month)}` }), _jsx(Kpi, { label: "Grootste bestelling", value: formatEUR(k.largest_order), hint: `kleinste: ${formatEUR(k.smallest_order)}` }), _jsx(Kpi, { label: "Gem. tijd tussen bestellingen", value: k.avg_days_between_orders != null ? `${formatNum(k.avg_days_between_orders, 1)} dgn` : "—", hint: k.avg_orders_per_month != null ? `${formatNum(k.avg_orders_per_month, 1)} / maand` : undefined })] }), _jsxs("p", { className: "text-xs text-muted-foreground", children: ["Bron: ", formatNum(k.order_count), " eigen ERP-orders (ordertotaal)", k.first_order_date && k.last_order_date && (_jsxs(_Fragment, { children: [", periode ", new Date(k.first_order_date).toLocaleDateString("nl-NL"), " \u2013 ", new Date(k.last_order_date).toLocaleDateString("nl-NL")] })), k.order_count > 0 && _jsxs(_Fragment, { children: [", inclusief de grootste order van ", formatEUR(k.largest_order)] }), ". Facturen uit Floriday of Twinfield tellen hier niet mee."] }), _jsxs("div", { className: "grid gap-3 grid-cols-1 md:grid-cols-3 text-sm", children: [_jsx(Card, { children: _jsxs(CardContent, { className: "pt-4 pb-4", children: [_jsx("div", { className: "text-xs text-muted-foreground", children: "Eerste bestelling" }), _jsx("div", { className: "font-medium", children: formatDate(k.first_order_date) })] }) }), _jsx(Card, { children: _jsxs(CardContent, { className: "pt-4 pb-4", children: [_jsx("div", { className: "text-xs text-muted-foreground", children: "Laatste bestelling" }), _jsx("div", { className: "font-medium", children: formatDate(k.last_order_date) })] }) }), _jsx(Card, { children: _jsxs(CardContent, { className: "pt-4 pb-4", children: [_jsx("div", { className: "text-xs text-muted-foreground", children: "Periode" }), _jsxs("div", { className: "font-medium", children: [formatDate(a.period.from), " \u2013 ", formatDate(new Date(new Date(a.period.to).getTime() - 86400000).toISOString())] })] }) })] }), _jsxs("div", { className: "grid gap-4 lg:grid-cols-2", children: [_jsxs(Card, { children: [_jsx(CardHeader, { children: _jsx(CardTitle, { className: "text-base", children: "Omzet per maand" }) }), _jsx(CardContent, { className: "h-72", children: a.monthly.length === 0 ? _jsx(EmptyChart, {}) : (_jsx(ResponsiveContainer, { width: "100%", height: "100%", children: _jsxs(LineChart, { data: a.monthly, margin: { left: 30, right: 10 }, children: [_jsx(CartesianGrid, { strokeDasharray: "3 3", stroke: "hsl(var(--border))" }), _jsx(XAxis, { dataKey: "label", fontSize: 11 }), _jsx(YAxis, { fontSize: 11, width: 100, tickFormatter: (v) => formatEUR(Number(v)) }), _jsx(Tooltip, { formatter: (v) => formatEUR(Number(v)) }), _jsx(Line, { type: "monotone", dataKey: "revenue", stroke: "#2563eb", strokeWidth: 2, dot: false, name: "Omzet" })] }) })) })] }), _jsxs(Card, { children: [_jsx(CardHeader, { children: _jsx(CardTitle, { className: "text-base", children: "Aantal bestellingen per maand" }) }), _jsx(CardContent, { className: "h-72", children: a.monthly.length === 0 ? _jsx(EmptyChart, {}) : (_jsx(ResponsiveContainer, { width: "100%", height: "100%", children: _jsxs(BarChart, { data: a.monthly, margin: { left: 30, right: 10 }, children: [_jsx(CartesianGrid, { strokeDasharray: "3 3", stroke: "hsl(var(--border))" }), _jsx(XAxis, { dataKey: "label", fontSize: 11 }), _jsx(YAxis, { fontSize: 11, width: 40, allowDecimals: false }), _jsx(Tooltip, {}), _jsx(Bar, { dataKey: "orders", fill: "#16a34a", name: "Bestellingen" })] }) })) })] }), _jsxs(Card, { children: [_jsx(CardHeader, { children: _jsx(CardTitle, { className: "text-base", children: "Gem. orderwaarde per maand" }) }), _jsx(CardContent, { className: "h-72", children: a.monthly.length === 0 ? _jsx(EmptyChart, {}) : (_jsx(ResponsiveContainer, { width: "100%", height: "100%", children: _jsxs(LineChart, { data: a.monthly, margin: { left: 30, right: 10 }, children: [_jsx(CartesianGrid, { strokeDasharray: "3 3", stroke: "hsl(var(--border))" }), _jsx(XAxis, { dataKey: "label", fontSize: 11 }), _jsx(YAxis, { fontSize: 11, width: 100, tickFormatter: (v) => formatEUR(Number(v)) }), _jsx(Tooltip, { formatter: (v) => formatEUR(Number(v)) }), _jsx(Line, { type: "monotone", dataKey: "aov", stroke: "#f59e0b", strokeWidth: 2, dot: false, name: "Gem. waarde" })] }) })) })] }), _jsxs(Card, { children: [_jsx(CardHeader, { children: _jsx(CardTitle, { className: "text-base", children: "Omzet per jaar" }) }), _jsx(CardContent, { className: "h-72", children: a.yearly.length === 0 ? _jsx(EmptyChart, {}) : (_jsx(ResponsiveContainer, { width: "100%", height: "100%", children: _jsxs(BarChart, { data: a.yearly, margin: { left: 30, right: 10 }, children: [_jsx(CartesianGrid, { strokeDasharray: "3 3", stroke: "hsl(var(--border))" }), _jsx(XAxis, { dataKey: "label", fontSize: 11 }), _jsx(YAxis, { fontSize: 11, width: 100, tickFormatter: (v) => formatEUR(Number(v)) }), _jsx(Tooltip, { formatter: (v) => formatEUR(Number(v)) }), _jsx(Bar, { dataKey: "revenue", fill: "#7c3aed", name: "Omzet" })] }) })) })] })] }), _jsxs("div", { className: "grid gap-4 lg:grid-cols-[2fr_1fr]", children: [_jsxs(Card, { children: [_jsx(CardHeader, { children: _jsx(CardTitle, { className: "text-base", children: "Top artikelen in periode" }) }), _jsx(CardContent, { className: "p-0", children: a.topProducts.length === 0 ? (_jsx("div", { className: "p-6 text-sm text-muted-foreground", children: "Geen orderregels in deze periode." })) : (_jsxs(Table, { children: [_jsx(TableHeader, { children: _jsxs(TableRow, { children: [_jsx(TableHead, { children: "Product" }), _jsx(TableHead, { className: "text-right", children: "Aantal" }), _jsx(TableHead, { children: "Eenheid" }), _jsx(TableHead, { className: "text-right", children: "Omzet" })] }) }), _jsx(TableBody, { children: a.topProducts.map((p) => (_jsxs(TableRow, { children: [_jsx(TableCell, { className: "font-medium", children: displayProductName(p.product_name) }), _jsx(TableCell, { className: "text-right tabular-nums", children: formatNum(p.quantity) }), _jsx(TableCell, { className: "text-muted-foreground", children: p.unit ?? "—" }), _jsx(TableCell, { className: "text-right tabular-nums", children: formatEUR(p.revenue) })] }, p.product_name))) })] })) })] }), _jsxs("div", { className: "space-y-4", children: [_jsxs(Card, { children: [_jsx(CardHeader, { children: _jsx(CardTitle, { className: "text-base", children: "Verkocht per eenheid" }) }), _jsx(CardContent, { className: "space-y-2", children: a.unitTotals.length === 0 ? (_jsx("div", { className: "text-sm text-muted-foreground", children: "Geen data." })) : a.unitTotals.map((u) => (_jsxs("div", { className: "flex items-center justify-between text-sm", children: [_jsx("span", { className: "capitalize", children: u.unit }), _jsx("span", { className: "tabular-nums font-medium", children: formatNum(u.quantity) })] }, u.unit))) })] }), _jsxs(Card, { children: [_jsx(CardHeader, { children: _jsx(CardTitle, { className: "text-base", children: "Status van bestellingen" }) }), _jsx(CardContent, { className: "h-56", children: a.statusBreakdown.length === 0 ? _jsx(EmptyChart, {}) : (_jsx(ResponsiveContainer, { width: "100%", height: "100%", children: _jsxs(PieChart, { children: [_jsx(Pie, { data: a.statusBreakdown, dataKey: "count", nameKey: "status", outerRadius: 70, label: true, children: a.statusBreakdown.map((_, i) => (_jsx(Cell, { fill: PIE_COLORS[i % PIE_COLORS.length] }, i))) }), _jsx(Tooltip, {}), _jsx(Legend, {})] }) })) })] })] })] }), _jsx(Card, { children: _jsx(CardContent, { className: "pt-4 pb-4 text-xs text-muted-foreground space-y-1", children: _jsxs("div", { children: [_jsx(Badge, { variant: "secondary", children: "Let op" }), " Sommige analyses zijn nog niet beschikbaar omdat de onderliggende data ontbreekt: btw-bedragen, marge en vervoerder/afhaallocatie per bestelling. Voeg deze velden toe aan orders/orderregels om die overzichten te activeren."] }) }) })] }));
104
104
  }
105
105
  function EmptyChart() {
106
106
  return _jsx("div", { className: "h-full flex items-center justify-center text-sm text-muted-foreground", children: "Geen data in deze periode." });
@@ -0,0 +1,5 @@
1
+ /** Alleen weergave: staat deze Floriday-organisatie in je Floriday-netwerk? Geen klantstatus. */
2
+ export declare function FloridayNetworkStatus({ organizationId }: {
3
+ organizationId: string;
4
+ }): import("react").JSX.Element | null;
5
+ //# sourceMappingURL=FloridayNetworkStatus.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FloridayNetworkStatus.d.ts","sourceRoot":"","sources":["../../../src/components/klanten/FloridayNetworkStatus.tsx"],"names":[],"mappings":"AAKA,iGAAiG;AACjG,wBAAgB,qBAAqB,CAAC,EAAE,cAAc,EAAE,EAAE;IAAE,cAAc,EAAE,MAAM,CAAA;CAAE,sCAenF"}
@@ -0,0 +1,18 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useQuery } from "@tanstack/react-query";
3
+ import { useServerFn } from "@tanstack/react-start";
4
+ import { getFloridayNetworkStatus } from "../../lib/floriday-network-status.functions";
5
+ import { formatDate } from "../../lib/format-date";
6
+ /** Alleen weergave: staat deze Floriday-organisatie in je Floriday-netwerk? Geen klantstatus. */
7
+ export function FloridayNetworkStatus({ organizationId }) {
8
+ const fn = useServerFn(getFloridayNetworkStatus);
9
+ const { data } = useQuery({
10
+ queryKey: ["floriday-network-status", organizationId],
11
+ queryFn: () => fn({ data: { organizationId } }),
12
+ staleTime: 5 * 60 * 1000,
13
+ });
14
+ if (!data)
15
+ return null;
16
+ const label = data.inNetwork === null ? "onbekend" : data.inNetwork ? "ja" : "nee";
17
+ return (_jsxs("div", { className: "mt-2 text-xs text-muted-foreground space-y-0.5", children: [_jsxs("p", { children: ["In je Floriday-netwerk: ", _jsx("span", { className: "font-medium text-foreground", children: label })] }), data.endDate && _jsxs("p", { children: ["Einddatum in Floriday: ", formatDate(data.endDate)] })] }));
18
+ }
@@ -26,6 +26,8 @@ export type CustomerProfile = {
26
26
  email_from_contact: boolean;
27
27
  phone_from_contact: boolean;
28
28
  has_contacts: boolean;
29
+ /** Floriday-organisatie-id (alleen op id, nooit op naam). */
30
+ floriday_organization_id?: string | null;
29
31
  };
30
32
  export declare function adaptLocalToProfile(c: any, contacts?: any[]): CustomerProfile;
31
33
  export declare function adaptGroupedToProfile(g: GroupedCustomer): CustomerProfile;
@@ -1 +1 @@
1
- {"version":3,"file":"KlantDetailLayout.d.ts","sourceRoot":"","sources":["../../../src/components/klanten/KlantDetailLayout.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAuB,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAY5D,OAAO,KAAK,EACV,gBAAgB,EAChB,eAAe,EAChB,MAAM,wCAAwC,CAAC;AAShD,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,SAAS,EAAE,OAAO,CAAC;IACnB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,sBAAsB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvC,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC7B,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,kBAAkB,EAAE,OAAO,CAAC;IAC5B,kBAAkB,EAAE,OAAO,CAAC;IAC5B,YAAY,EAAE,OAAO,CAAC;CACvB,CAAC;AA6BF,wBAAgB,mBAAmB,CAAC,CAAC,EAAE,GAAG,EAAE,QAAQ,GAAE,GAAG,EAAO,GAAG,eAAe,CAiCjF;AAED,wBAAgB,qBAAqB,CAAC,CAAC,EAAE,eAAe,GAAG,eAAe,CAsCzE;AAED,wBAAgB,sBAAsB,CAAC,CAAC,EAAE,gBAAgB,GAAG,eAAe,CAgC3E;AAED,wBAAgB,WAAW,CAAC,EAC1B,OAAO,EAAE,CAAC,EACV,KAAK,EACL,WAAW,EACX,QAAgB,GACjB,EAAE;IACD,OAAO,EAAE,eAAe,CAAC;IACzB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACtD,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,+BAwMA;AAGD,wBAAgB,YAAY,CAAC,EAAE,QAAQ,EAAE,EAAE;IAAE,QAAQ,EAAE,SAAS,CAAA;CAAE,+BAEjE;AAED,wBAAgB,WAAW,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,+BAWjI;AAED,wBAAgB,OAAO,CAAC,EACtB,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,GAC/C,EAAE;IACD,IAAI,EAAE,SAAS,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAAC,IAAI,CAAC,EAAE,OAAO,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;CACrI,+BAyBA;AAED,wBAAgB,QAAQ,CAAC,EACvB,MAAM,EAAE,QAAQ,GACjB,EAAE;IACD,MAAM,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAC3F,QAAQ,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CACjC,+BA6BA;AAED,wBAAgB,YAAY,CAAC,EAAE,KAAK,EAAE,EAAE;IAAE,KAAK,EAAE;QAAE,EAAE,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,+BAiBrG;AAED,wBAAgB,SAAS,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,GAAG,SAAS,CAAA;CAAE,+BAgBhG;AAED,wBAAgB,UAAU,CAAC,EAAE,OAAO,EAAE,EAAE;IAAE,OAAO,EAAE,MAAM,CAAA;CAAE,+BAM1D"}
1
+ {"version":3,"file":"KlantDetailLayout.d.ts","sourceRoot":"","sources":["../../../src/components/klanten/KlantDetailLayout.tsx"],"names":[],"mappings":"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"}
@@ -20,6 +20,7 @@ function firstNonEmpty(values) {
20
20
  return null;
21
21
  }
22
22
  import { formatPaymentMethodLabel } from "../../lib/payment-method";
23
+ import { FloridayNetworkStatus } from "./FloridayNetworkStatus";
23
24
  function fmtAddr(a) {
24
25
  if (!a)
25
26
  return null;
@@ -71,6 +72,7 @@ export function adaptLocalToProfile(c, contacts = []) {
71
72
  email_from_contact: !c.email && !!primary?.email,
72
73
  phone_from_contact: !c.phone && !!primary?.phone,
73
74
  has_contacts: contacts.length > 0,
75
+ floriday_organization_id: c.floriday_organization_id ?? null,
74
76
  };
75
77
  }
76
78
  export function adaptGroupedToProfile(g) {
@@ -104,6 +106,8 @@ export function adaptGroupedToProfile(g) {
104
106
  email_from_contact: false,
105
107
  phone_from_contact: false,
106
108
  has_contacts: !!(g.contact_person || pick("contact_person")),
109
+ floriday_organization_id: firstNonEmpty(sources.map((s) => (s.provider === "floriday" ? String(s.external_id ?? "") : null))) ??
110
+ (String(g.group_key ?? "").startsWith("fd:") ? String(g.group_key).slice(3) : null),
107
111
  };
108
112
  }
109
113
  export function adaptExternalToProfile(c) {
@@ -136,6 +140,7 @@ export function adaptExternalToProfile(c) {
136
140
  email_from_contact: false,
137
141
  phone_from_contact: false,
138
142
  has_contacts: !!c.contact_person,
143
+ floriday_organization_id: c.provider === "floriday" ? c.external_id ?? null : null,
139
144
  };
140
145
  }
141
146
  export function ProfileCard({ profile: p, notes, onSaveNotes, isSaving = false, }) {
@@ -157,7 +162,7 @@ export function ProfileCard({ profile: p, notes, onSaveNotes, isSaving = false,
157
162
  await onSaveNotes?.(draft);
158
163
  setIsEditing(false);
159
164
  };
160
- return (_jsx(Card, { className: "lg:sticky lg:top-6 self-start", children: _jsxs(CardContent, { className: "pt-6 space-y-5", children: [_jsxs("div", { className: "flex flex-col items-center text-center", children: [_jsx(CustomerAvatar, { logoUrl: p.logo_url, name: p.company_name, size: "lg", className: "h-24 w-24 text-2xl" }), _jsx("h1", { className: "mt-3 text-xl font-bold leading-tight", children: p.company_name }), p.commercial_name && p.commercial_name !== p.company_name && (_jsx("p", { className: "text-sm text-muted-foreground", children: p.commercial_name })), _jsxs("div", { className: "mt-3 flex flex-wrap items-center justify-center gap-1.5", children: [p.is_active ? _jsx(Badge, { children: "Actief" }) : _jsx(Badge, { variant: "destructive", children: "Inactief" }), p.class_badge && _jsx(Badge, { variant: "secondary", children: p.class_badge })] })] }), (p.email || p.phone || p.website) && (_jsxs(_Fragment, { children: [_jsx(Separator, {}), _jsxs("div", { children: [_jsx(SectionLabel, { children: "Snelle acties" }), _jsxs("div", { className: "grid grid-cols-3 gap-2", children: [_jsx(QuickAction, { icon: _jsx(Mail, { className: "h-4 w-4" }), label: "E-mail", href: p.email ? `mailto:${p.email}` : undefined }), _jsx(QuickAction, { icon: _jsx(Phone, { className: "h-4 w-4" }), label: "Bellen", href: p.phone ? `tel:${p.phone}` : undefined }), _jsx(QuickAction, { icon: _jsx(Globe, { className: "h-4 w-4" }), label: "Website", href: p.website ?? undefined, external: true })] })] })] })), _jsx(Separator, {}), _jsxs("div", { className: "space-y-3", children: [_jsx(SectionLabel, { children: "Contact" }), _jsx(InfoRow, { icon: _jsx(MapPin, { className: "h-4 w-4" }), value: fullAddress || null }), _jsx(InfoRow, { icon: _jsx(Mail, { className: "h-4 w-4" }), value: p.email, href: p.email ? `mailto:${p.email}` : undefined, hint: p.email_from_contact ? `via ${p.primary_contact_name ?? "contactpersoon"}` : undefined }), _jsx(InfoRow, { icon: _jsx(Phone, { className: "h-4 w-4" }), value: p.phone, href: p.phone ? `tel:${p.phone}` : undefined, hint: p.phone_from_contact ? `via ${p.primary_contact_name ?? "contactpersoon"}` : undefined }), _jsx(InfoRow, { icon: _jsx(Globe, { className: "h-4 w-4" }), value: p.website, href: p.website ?? undefined, external: true }), !p.email && !p.phone && !p.has_contacts && (_jsxs("p", { className: "text-xs text-muted-foreground leading-relaxed", children: ["Voeg e-mail of telefoon toe via ", _jsx("strong", { children: "Bewerken" }), "."] }))] }), (p.payment_providers?.length || p.mailing_address) ? (_jsxs(_Fragment, { children: [_jsx(Separator, {}), _jsxs("div", { className: "space-y-3", children: [p.payment_providers && p.payment_providers.length > 0 && (_jsx(InfoRow, { icon: _jsx(Hash, { className: "h-4 w-4" }), label: "Financi\u00EBle afhandeling", value: p.payment_providers.join(", ") })), p.mailing_address && (_jsx(InfoRow, { icon: _jsx(MapPin, { className: "h-4 w-4" }), label: "Postadres", value: p.mailing_address }))] })] })) : null, p.vat_number && (_jsxs(_Fragment, { children: [_jsx(Separator, {}), _jsxs("div", { className: "space-y-3", children: [_jsx(SectionLabel, { children: "Identificatie" }), _jsx(InfoRow, { icon: _jsx(Hash, { className: "h-4 w-4" }), label: "BTW", value: p.vat_number, mono: true })] })] })), hasTech && (_jsxs(_Fragment, { children: [_jsx(Separator, {}), _jsxs("button", { type: "button", onClick: () => setShowTech((v) => !v), className: "flex items-center gap-1 text-xs text-muted-foreground hover:text-foreground", children: [showTech ? _jsx(ChevronDown, { className: "h-3 w-3" }) : _jsx(ChevronRight, { className: "h-3 w-3" }), "Technische gegevens"] }), showTech && (_jsxs("div", { className: "space-y-3", children: [p.rfh_id && _jsx(InfoRow, { icon: _jsx(Hash, { className: "h-4 w-4" }), label: "RFH-relatienr.", value: p.rfh_id, mono: true }), p.rfh_company_account_id && _jsx(InfoRow, { icon: _jsx(Hash, { className: "h-4 w-4" }), label: "RFH-bedrijfsaccount", value: p.rfh_company_account_id, mono: true }), p.plantion_number && _jsx(InfoRow, { icon: _jsx(Hash, { className: "h-4 w-4" }), label: "Plantion-registratienr.", value: p.plantion_number, mono: true }), _jsx(InfoRow, { icon: _jsx(Hash, { className: "h-4 w-4" }), label: "GLN", value: p.gln, mono: true }), _jsx(InfoRow, { icon: _jsx(Building2, { className: "h-4 w-4" }), label: "Type", value: formatOrgType(p.organization_type) })] }))] })), _jsx(Separator, {}), _jsxs("div", { className: "space-y-3", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsx(SectionLabel, { children: "Notitie" }), editable && !isEditing && (_jsxs("button", { type: "button", onClick: () => setIsEditing(true), className: "flex items-center gap-1 text-xs text-muted-foreground hover:text-foreground", "aria-label": "Notitie bewerken", children: [_jsx(Pencil, { className: "h-3 w-3" }), "Bewerken"] }))] }), isEditing ? (_jsxs("div", { className: "space-y-2", children: [_jsx(Textarea, { value: draft, onChange: (e) => setDraft(e.target.value), rows: 4, placeholder: "Voeg een korte notitie over deze klant toe...", disabled: isSaving, className: "text-sm resize-none" }), _jsxs("div", { className: "flex items-center justify-end gap-2", children: [_jsxs(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => {
165
+ return (_jsx(Card, { className: "lg:sticky lg:top-6 self-start", children: _jsxs(CardContent, { className: "pt-6 space-y-5", children: [_jsxs("div", { className: "flex flex-col items-center text-center", children: [_jsx(CustomerAvatar, { logoUrl: p.logo_url, name: p.company_name, size: "lg", className: "h-24 w-24 text-2xl" }), _jsx("h1", { className: "mt-3 text-xl font-bold leading-tight", children: p.company_name }), p.commercial_name && p.commercial_name !== p.company_name && (_jsx("p", { className: "text-sm text-muted-foreground", children: p.commercial_name })), _jsxs("div", { className: "mt-3 flex flex-wrap items-center justify-center gap-1.5", children: [p.is_active ? _jsx(Badge, { children: "Actief" }) : _jsx(Badge, { variant: "destructive", children: "Inactief" }), p.class_badge && _jsx(Badge, { variant: "secondary", children: p.class_badge })] }), p.floriday_organization_id && _jsx(FloridayNetworkStatus, { organizationId: p.floriday_organization_id })] }), (p.email || p.phone || p.website) && (_jsxs(_Fragment, { children: [_jsx(Separator, {}), _jsxs("div", { children: [_jsx(SectionLabel, { children: "Snelle acties" }), _jsxs("div", { className: "grid grid-cols-3 gap-2", children: [_jsx(QuickAction, { icon: _jsx(Mail, { className: "h-4 w-4" }), label: "E-mail", href: p.email ? `mailto:${p.email}` : undefined }), _jsx(QuickAction, { icon: _jsx(Phone, { className: "h-4 w-4" }), label: "Bellen", href: p.phone ? `tel:${p.phone}` : undefined }), _jsx(QuickAction, { icon: _jsx(Globe, { className: "h-4 w-4" }), label: "Website", href: p.website ?? undefined, external: true })] })] })] })), _jsx(Separator, {}), _jsxs("div", { className: "space-y-3", children: [_jsx(SectionLabel, { children: "Contact" }), _jsx(InfoRow, { icon: _jsx(MapPin, { className: "h-4 w-4" }), value: fullAddress || null }), _jsx(InfoRow, { icon: _jsx(Mail, { className: "h-4 w-4" }), value: p.email, href: p.email ? `mailto:${p.email}` : undefined, hint: p.email_from_contact ? `via ${p.primary_contact_name ?? "contactpersoon"}` : undefined }), _jsx(InfoRow, { icon: _jsx(Phone, { className: "h-4 w-4" }), value: p.phone, href: p.phone ? `tel:${p.phone}` : undefined, hint: p.phone_from_contact ? `via ${p.primary_contact_name ?? "contactpersoon"}` : undefined }), _jsx(InfoRow, { icon: _jsx(Globe, { className: "h-4 w-4" }), value: p.website, href: p.website ?? undefined, external: true }), !p.email && !p.phone && !p.has_contacts && (_jsxs("p", { className: "text-xs text-muted-foreground leading-relaxed", children: ["Voeg e-mail of telefoon toe via ", _jsx("strong", { children: "Bewerken" }), "."] }))] }), (p.payment_providers?.length || p.mailing_address) ? (_jsxs(_Fragment, { children: [_jsx(Separator, {}), _jsxs("div", { className: "space-y-3", children: [p.payment_providers && p.payment_providers.length > 0 && (_jsx(InfoRow, { icon: _jsx(Hash, { className: "h-4 w-4" }), label: "Financi\u00EBle afhandeling", value: p.payment_providers.join(", ") })), p.mailing_address && (_jsx(InfoRow, { icon: _jsx(MapPin, { className: "h-4 w-4" }), label: "Postadres", value: p.mailing_address }))] })] })) : null, p.vat_number && (_jsxs(_Fragment, { children: [_jsx(Separator, {}), _jsxs("div", { className: "space-y-3", children: [_jsx(SectionLabel, { children: "Identificatie" }), _jsx(InfoRow, { icon: _jsx(Hash, { className: "h-4 w-4" }), label: "BTW", value: p.vat_number, mono: true })] })] })), hasTech && (_jsxs(_Fragment, { children: [_jsx(Separator, {}), _jsxs("button", { type: "button", onClick: () => setShowTech((v) => !v), className: "flex items-center gap-1 text-xs text-muted-foreground hover:text-foreground", children: [showTech ? _jsx(ChevronDown, { className: "h-3 w-3" }) : _jsx(ChevronRight, { className: "h-3 w-3" }), "Technische gegevens"] }), showTech && (_jsxs("div", { className: "space-y-3", children: [p.rfh_id && _jsx(InfoRow, { icon: _jsx(Hash, { className: "h-4 w-4" }), label: "RFH-relatienr.", value: p.rfh_id, mono: true }), p.rfh_company_account_id && _jsx(InfoRow, { icon: _jsx(Hash, { className: "h-4 w-4" }), label: "RFH-bedrijfsaccount", value: p.rfh_company_account_id, mono: true }), p.plantion_number && _jsx(InfoRow, { icon: _jsx(Hash, { className: "h-4 w-4" }), label: "Plantion-registratienr.", value: p.plantion_number, mono: true }), _jsx(InfoRow, { icon: _jsx(Hash, { className: "h-4 w-4" }), label: "GLN", value: p.gln, mono: true }), _jsx(InfoRow, { icon: _jsx(Building2, { className: "h-4 w-4" }), label: "Type", value: formatOrgType(p.organization_type) })] }))] })), _jsx(Separator, {}), _jsxs("div", { className: "space-y-3", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsx(SectionLabel, { children: "Notitie" }), editable && !isEditing && (_jsxs("button", { type: "button", onClick: () => setIsEditing(true), className: "flex items-center gap-1 text-xs text-muted-foreground hover:text-foreground", "aria-label": "Notitie bewerken", children: [_jsx(Pencil, { className: "h-3 w-3" }), "Bewerken"] }))] }), isEditing ? (_jsxs("div", { className: "space-y-2", children: [_jsx(Textarea, { value: draft, onChange: (e) => setDraft(e.target.value), rows: 4, placeholder: "Voeg een korte notitie over deze klant toe...", disabled: isSaving, className: "text-sm resize-none" }), _jsxs("div", { className: "flex items-center justify-end gap-2", children: [_jsxs(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => {
161
166
  setDraft(notes ?? "");
162
167
  setIsEditing(false);
163
168
  }, disabled: isSaving, children: [_jsx(X, { className: "h-4 w-4 mr-1" }), " Annuleren"] }), _jsx(Button, { type: "button", size: "sm", onClick: handleSave, disabled: isSaving || draft === (notes ?? ""), children: isSaving ? (_jsxs("span", { className: "flex items-center gap-1", children: [_jsx("span", { className: "h-4 w-4 animate-spin rounded-full border-2 border-current border-t-transparent" }), "Opslaan..."] })) : (_jsxs(_Fragment, { children: [_jsx(Check, { className: "h-4 w-4 mr-1" }), " Opslaan"] })) })] })] })) : notes ? (_jsx("p", { className: "text-sm whitespace-pre-wrap text-foreground", children: notes })) : editable ? (_jsxs("button", { type: "button", onClick: () => setIsEditing(true), className: "flex w-full items-center gap-2 rounded-md border border-dashed border-border bg-muted/40 px-3 py-2 text-sm text-muted-foreground transition-colors hover:bg-muted", children: [_jsx(StickyNote, { className: "h-4 w-4" }), "Notitie toevoegen"] })) : (_jsxs("div", { className: "flex items-center gap-2 text-sm text-muted-foreground", children: [_jsx(StickyNote, { className: "h-4 w-4" }), "Geen notitie"] }))] })] }) }));
@@ -10,7 +10,7 @@ export declare const emptyKlantFormValues: KlantFormValues;
10
10
  * Herbruikbaar klantformulier voor zowel aanmaken als bewerken. De opbouw,
11
11
  * validatie en verplichte Twinfield-velden zijn in beide gevallen identiek.
12
12
  */
13
- export declare function KlantFormulier({ title, description, submitLabel, submitting, defaultValues, onSubmit, onCancel, extra, extraSections, notice, numberReadOnly, numberHint, numberValue, creditInfo, base, external, }: {
13
+ export declare function KlantFormulier({ title, description, submitLabel, submitting, defaultValues, onSubmit, onCancel, extra, extraSections, notice, numberReadOnly, numberHint, numberValue, syncError, creditInfo, base, external, }: {
14
14
  title: string;
15
15
  description?: string;
16
16
  /** Rustige melding boven het formulier (bv. ontbrekende standaardwaarde, alleen voor beheerders). */
@@ -28,6 +28,8 @@ export declare function KlantFormulier({ title, description, submitLabel, submit
28
28
  numberHint?: string;
29
29
  /** Vaste weergave in het nummerveld (Twinfield-debiteurnummer bij een gekoppelde klant). */
30
30
  numberValue?: string;
31
+ /** Laatste Twinfield-foutmelding van deze klant; een btw-fout (field4) wordt bij het btw-veld getoond. */
32
+ syncError?: string | null;
31
33
  /** Alleen-lezen kredietgegevens uit Twinfield. */
32
34
  creditInfo?: {
33
35
  credit_manager?: string | null;
@@ -1 +1 @@
1
- {"version":3,"file":"KlantFormulier.d.ts","sourceRoot":"","sources":["../../../src/components/klanten/KlantFormulier.tsx"],"names":[],"mappings":"AAgBA,OAAO,EAAwB,KAAK,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAsBxF,MAAM,MAAM,eAAe,GAAG,mBAAmB,CAAC;AAElD;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,EAAE,eAuBf,CAAC;AAErB;;;GAGG;AACH,wBAAgB,cAAc,CAAC,EAC7B,KAAK,EACL,WAAW,EACX,WAAW,EACX,UAAU,EACV,aAAa,EACb,QAAQ,EACR,QAAQ,EACR,KAAK,EACL,aAAa,EACb,MAAM,EACN,cAAc,EACd,UAAU,EACV,WAAW,EACX,UAAU,EACV,IAAI,EACJ,QAAQ,GACT,EAAE;IACD,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,qGAAqG;IACrG,MAAM,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,OAAO,CAAC;IACpB,aAAa,EAAE,eAAe,CAAC;IAC/B,QAAQ,EAAE,CAAC,MAAM,EAAE,eAAe,KAAK,IAAI,CAAC;IAC5C,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,KAAK,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACxB,iEAAiE;IACjE,aAAa,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAChC,6EAA6E;IAC7E,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,4FAA4F;IAC5F,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kDAAkD;IAClD,UAAU,CAAC,EAAE;QAAE,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,mBAAmB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC;IACrH,4FAA4F;IAC5F,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,mFAAmF;IACnF,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC,+BAgrBA"}
1
+ {"version":3,"file":"KlantFormulier.d.ts","sourceRoot":"","sources":["../../../src/components/klanten/KlantFormulier.tsx"],"names":[],"mappings":"AAgBA,OAAO,EAAwB,KAAK,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAuBxF,MAAM,MAAM,eAAe,GAAG,mBAAmB,CAAC;AAElD;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,EAAE,eAuBf,CAAC;AAErB;;;GAGG;AACH,wBAAgB,cAAc,CAAC,EAC7B,KAAK,EACL,WAAW,EACX,WAAW,EACX,UAAU,EACV,aAAa,EACb,QAAQ,EACR,QAAQ,EACR,KAAK,EACL,aAAa,EACb,MAAM,EACN,cAAc,EACd,UAAU,EACV,WAAW,EACX,SAAS,EACT,UAAU,EACV,IAAI,EACJ,QAAQ,GACT,EAAE;IACD,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,qGAAqG;IACrG,MAAM,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,OAAO,CAAC;IACpB,aAAa,EAAE,eAAe,CAAC;IAC/B,QAAQ,EAAE,CAAC,MAAM,EAAE,eAAe,KAAK,IAAI,CAAC;IAC5C,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,KAAK,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACxB,iEAAiE;IACjE,aAAa,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAChC,6EAA6E;IAC7E,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,4FAA4F;IAC5F,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,0GAA0G;IAC1G,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,kDAAkD;IAClD,UAAU,CAAC,EAAE;QAAE,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,mBAAmB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC;IACrH,4FAA4F;IAC5F,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,mFAAmF;IACnF,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC,+BA4sBA"}
@@ -12,7 +12,8 @@ import { listTwinfieldLedgerAccounts } from "../../lib/twinfield-ledger.function
12
12
  import { customerCreateSchema } from "../../lib/customer-schemas.js";
13
13
  import { requiresVatNumber } from "../../lib/twinfield-required-fields.js";
14
14
  import { TWINFIELD_FIELD_MAX, TWINFIELD_READONLY_FIELDS, TWINFIELD_READONLY_HINT } from "../../lib/twinfield-limits.js";
15
- import { countryNameToFlag, COMMON_COUNTRY_NAMES } from "../../lib/countries.js";
15
+ import { countryNameToFlag, COMMON_COUNTRY_NAMES, toIsoCountry } from "../../lib/countries.js";
16
+ import { checkVatFormat, isTwinfieldVatError } from "../../lib/vat-format.js";
16
17
  import { creditManagerDisplay } from "../../lib/credit-management.js";
17
18
  import { TWINFIELD_DEFAULTS, VAT_DISPLAY_OPTIONS, PAYMENT_SPEC_METHOD_OPTIONS, INVOICE_SEND_AS_OPTIONS, SEND_REMINDER_OPTIONS, DISCOUNT_SURCHARGE_OPTIONS, } from "../../lib/twinfield-defaults.js";
18
19
  /** Rustige aanwijzing per veld wanneer de waarde intussen elders is gewijzigd. */
@@ -53,7 +54,7 @@ export const emptyKlantFormValues = {
53
54
  * Herbruikbaar klantformulier voor zowel aanmaken als bewerken. De opbouw,
54
55
  * validatie en verplichte Twinfield-velden zijn in beide gevallen identiek.
55
56
  */
56
- export function KlantFormulier({ title, description, submitLabel, submitting, defaultValues, onSubmit, onCancel, extra, extraSections, notice, numberReadOnly, numberHint, numberValue, creditInfo, base, external, }) {
57
+ export function KlantFormulier({ title, description, submitLabel, submitting, defaultValues, onSubmit, onCancel, extra, extraSections, notice, numberReadOnly, numberHint, numberValue, syncError, creditInfo, base, external, }) {
57
58
  const classesFn = useServerFn(listCustomerClasses);
58
59
  const ledgerFn = useServerFn(listTwinfieldLedgerAccounts);
59
60
  const [open, setOpen] = useState({
@@ -116,7 +117,12 @@ export function KlantFormulier({ title, description, submitLabel, submitting, de
116
117
  }
117
118
  }
118
119
  /** Bij opslaan: niet-aangeraakte velden volgen de laatst bekende serverwaarde. */
120
+ const [vatOverride, setVatOverride] = useState(false);
119
121
  const handleSubmit = form.handleSubmit((values) => {
122
+ if (checkVatFormat(values.vat_number, toIsoCountry(values.country)).status === "invalid" && !vatOverride) {
123
+ form.setFocus("vat_number");
124
+ return;
125
+ }
120
126
  const merged = { ...values };
121
127
  for (const k of Object.keys(customerCreateSchema.shape)) {
122
128
  if (!form.getFieldState(k).isDirty && k in baseRef.current) {
@@ -213,7 +219,13 @@ export function KlantFormulier({ title, description, submitLabel, submitting, de
213
219
  "customer_number", "company_name", "language", "website", "vat_number",
214
220
  "coc_number", "contact_person", "attention_to", "email", "billing_email",
215
221
  "phone", "fax",
216
- ]), children: _jsxs("div", { className: "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4", children: [_jsx(Field, { label: "Nummer", error: fieldErr("customer_number"), success: !numberReadOnly && isValid("customer_number"), hint: numberHint, children: numberValue ? (_jsx(Input, { readOnly: true, className: "bg-muted", value: numberValue })) : (_jsx(Input, { readOnly: numberReadOnly, ...form.register("customer_number") })) }), _jsx(Field, { label: "Bedrijfsnaam", required: true, error: fieldErr("company_name"), success: isValid("company_name"), hint: lengthWarning(values.company_name, 40, "de bedrijfsnaam"), children: _jsx(Input, { "aria-invalid": !!errors.company_name, ...form.register("company_name") }) }), _jsx(Field, { label: "Taal", error: fieldErr("language"), children: _jsxs(Select, { value: form.watch("language") || undefined, onValueChange: (v) => form.setValue("language", v, { shouldValidate: true }), children: [_jsx(SelectTrigger, { children: _jsx(SelectValue, { placeholder: "Geen standaard" }) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "nl", children: "nl" }), _jsx(SelectItem, { value: "en", children: "en" }), _jsx(SelectItem, { value: "de", children: "de" }), _jsx(SelectItem, { value: "fr", children: "fr" })] })] }) }), _jsx(Field, { label: "Website", error: fieldErr("website"), success: isValid("website"), children: _jsx(Input, { placeholder: "https://", ...normalizeOnBlur("website", normUrl) }) }), _jsx(Field, { label: "BTW-nummer", required: vatRequired, error: fieldErr("vat_number"), success: isValid("vat_number"), children: _jsx(Input, { "aria-invalid": !!errors.vat_number, ...normalizeOnBlur("vat_number", normVat) }) }), _jsx(Field, { label: "KvK-nummer", error: fieldErr("coc_number"), success: isValid("coc_number"), children: _jsx(Input, { ...form.register("coc_number") }) }), _jsx(Field, { label: "Contactpersoon", error: fieldErr("contact_person"), success: isValid("contact_person"), children: _jsx(Input, { ...form.register("contact_person") }) }), _jsx(Field, { label: "T.a.v.", error: fieldErr("attention_to"), children: _jsx(Input, { ...form.register("attention_to") }) }), _jsx(Field, { label: "E-mail", error: fieldErr("email"), success: isValid("email"), children: _jsx(Input, { type: "email", "aria-invalid": !!errors.email, placeholder: "naam@bedrijf.nl", ...form.register("email") }) }), _jsx(Field, { label: "E-mail voor facturering", error: fieldErr("billing_email"), success: isValid("billing_email"), children: _jsx(Input, { type: "email", placeholder: "facturen@bedrijf.nl", ...form.register("billing_email") }) }), _jsx(Field, { label: "Telefoon", error: fieldErr("phone"), success: isValid("phone"), children: _jsx(Input, { ...form.register("phone") }) }), _jsx(Field, { label: "Faxnummer", error: fieldErr("fax"), children: _jsx(Input, { ...form.register("fax") }) }), _jsx(Field, { label: "Klantklasse", children: _jsxs(Select, { value: form.watch("customer_class") || "__none__", onValueChange: (v) => form.setValue("customer_class", v === "__none__" ? "" : v, { shouldDirty: true }), children: [_jsx(SelectTrigger, { children: _jsx(SelectValue, { placeholder: "Kies klasse..." }) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "__none__", children: "Geen klasse" }), (classData?.classes ?? []).map((c) => _jsx(SelectItem, { value: c, children: c }, c))] })] }) }), _jsx(Field, { label: "Status", hint: "Een inactieve klant wordt in Twinfield verborgen.", children: _jsxs(Select, { value: form.watch("is_active") === false ? "inactief" : "actief", onValueChange: (v) => form.setValue("is_active", v === "actief", { shouldDirty: true }), children: [_jsx(SelectTrigger, { children: _jsx(SelectValue, {}) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "actief", children: "Actief" }), _jsx(SelectItem, { value: "inactief", children: "Inactief" })] })] }) })] }) }), _jsx(Section, { k: "adres", title: "Adres", icon: _jsx(MapPin, { className: "h-5 w-5" }), open: open.adres, onToggle: toggle, hasError: sectionHasError([
222
+ ]), children: _jsxs("div", { className: "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4", children: [_jsx(Field, { label: "Nummer", error: fieldErr("customer_number"), success: !numberReadOnly && isValid("customer_number"), hint: numberHint, children: numberValue ? (_jsx(Input, { readOnly: true, className: "bg-muted", value: numberValue })) : (_jsx(Input, { readOnly: numberReadOnly, ...form.register("customer_number") })) }), _jsx(Field, { label: "Bedrijfsnaam", required: true, error: fieldErr("company_name"), success: isValid("company_name"), hint: lengthWarning(values.company_name, 40, "de bedrijfsnaam"), children: _jsx(Input, { "aria-invalid": !!errors.company_name, ...form.register("company_name") }) }), _jsx(Field, { label: "Taal", error: fieldErr("language"), children: _jsxs(Select, { value: form.watch("language") || undefined, onValueChange: (v) => form.setValue("language", v, { shouldValidate: true }), children: [_jsx(SelectTrigger, { children: _jsx(SelectValue, { placeholder: "Geen standaard" }) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "nl", children: "nl" }), _jsx(SelectItem, { value: "en", children: "en" }), _jsx(SelectItem, { value: "de", children: "de" }), _jsx(SelectItem, { value: "fr", children: "fr" })] })] }) }), _jsx(Field, { label: "Website", error: fieldErr("website"), success: isValid("website"), children: _jsx(Input, { placeholder: "https://", ...normalizeOnBlur("website", normUrl) }) }), _jsxs(Field, { label: "BTW-nummer", required: vatRequired, error: fieldErr("vat_number"), success: isValid("vat_number"), children: [_jsx(Input, { "aria-invalid": !!errors.vat_number, ...normalizeOnBlur("vat_number", normVat) }), (() => {
223
+ const chk = checkVatFormat(values.vat_number, toIsoCountry(values.country));
224
+ const tfErr = isTwinfieldVatError(syncError) ? syncError : null;
225
+ if (chk.status !== "invalid" && !tfErr)
226
+ return null;
227
+ return (_jsxs("div", { className: "mt-1.5 rounded-md border border-warning/40 bg-warning/10 px-2.5 py-2 text-xs space-y-1.5", children: [_jsx("p", { className: "font-medium", children: chk.status === "invalid" ? chk.message : "Twinfield weigerde het btw-nummer" }), tfErr && _jsxs("p", { className: "text-muted-foreground", children: ["Melding van Twinfield: ", tfErr] }), _jsxs("div", { className: "flex flex-wrap gap-2", children: [_jsx(Button, { type: "button", size: "sm", variant: "outline", onClick: () => { setVatOverride(false); form.setFocus("vat_number"); }, children: "Corrigeren" }), chk.status === "invalid" && (_jsx(Button, { type: "button", size: "sm", variant: vatOverride ? "secondary" : "ghost", onClick: () => setVatOverride(true), children: vatOverride ? "Wordt toch verstuurd" : "Toch versturen" }))] })] }));
228
+ })()] }), _jsx(Field, { label: "KvK-nummer", error: fieldErr("coc_number"), success: isValid("coc_number"), children: _jsx(Input, { ...form.register("coc_number") }) }), _jsx(Field, { label: "Contactpersoon", error: fieldErr("contact_person"), success: isValid("contact_person"), children: _jsx(Input, { ...form.register("contact_person") }) }), _jsx(Field, { label: "T.a.v.", error: fieldErr("attention_to"), children: _jsx(Input, { ...form.register("attention_to") }) }), _jsx(Field, { label: "E-mail", error: fieldErr("email"), success: isValid("email"), children: _jsx(Input, { type: "email", "aria-invalid": !!errors.email, placeholder: "naam@bedrijf.nl", ...form.register("email") }) }), _jsx(Field, { label: "E-mail voor facturering", error: fieldErr("billing_email"), success: isValid("billing_email"), children: _jsx(Input, { type: "email", placeholder: "facturen@bedrijf.nl", ...form.register("billing_email") }) }), _jsx(Field, { label: "Telefoon", error: fieldErr("phone"), success: isValid("phone"), children: _jsx(Input, { ...form.register("phone") }) }), _jsx(Field, { label: "Faxnummer", error: fieldErr("fax"), children: _jsx(Input, { ...form.register("fax") }) }), _jsx(Field, { label: "Klantklasse", children: _jsxs(Select, { value: form.watch("customer_class") || "__none__", onValueChange: (v) => form.setValue("customer_class", v === "__none__" ? "" : v, { shouldDirty: true }), children: [_jsx(SelectTrigger, { children: _jsx(SelectValue, { placeholder: "Kies klasse..." }) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "__none__", children: "Geen klasse" }), (classData?.classes ?? []).map((c) => _jsx(SelectItem, { value: c, children: c }, c))] })] }) }), _jsx(Field, { label: "Status", hint: "Een inactieve klant wordt in Twinfield verborgen.", children: _jsxs(Select, { value: form.watch("is_active") === false ? "inactief" : "actief", onValueChange: (v) => form.setValue("is_active", v === "actief", { shouldDirty: true }), children: [_jsx(SelectTrigger, { children: _jsx(SelectValue, {}) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "actief", children: "Actief" }), _jsx(SelectItem, { value: "inactief", children: "Inactief" })] })] }) })] }) }), _jsx(Section, { k: "adres", title: "Adres", icon: _jsx(MapPin, { className: "h-5 w-5" }), open: open.adres, onToggle: toggle, hasError: sectionHasError([
217
229
  "address_type", "icp_country_code", "address_line1", "address_line2",
218
230
  "street", "house_number", "postal_code", "city", "country",
219
231
  ]), children: _jsxs("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4", children: [_jsx(Field, { label: "Straat", error: fieldErr("street"), success: isValid("street"), children: _jsx(Input, { "aria-invalid": !!errors.street, ...form.register("street") }) }), _jsx(Field, { label: "Huisnummer", error: fieldErr("house_number"), success: isValid("house_number"), children: _jsx(Input, { "aria-invalid": !!errors.house_number, ...form.register("house_number") }) }), _jsx(Field, { label: "Postcode", error: fieldErr("postal_code"), success: isValid("postal_code"), children: _jsx(Input, { "aria-invalid": !!errors.postal_code, ...normalizeOnBlur("postal_code", normPostal) }) }), _jsx(Field, { label: "Plaats", error: fieldErr("city"), success: isValid("city"), children: _jsx(Input, { "aria-invalid": !!errors.city, ...form.register("city") }) }), _jsx(Field, { label: "Land", error: fieldErr("country"), success: isValid("country"), children: _jsxs(Select, { value: form.watch("country") || undefined, onValueChange: (v) => form.setValue("country", v, { shouldValidate: true }), children: [_jsx(SelectTrigger, { children: _jsx(SelectValue, { children: (() => {
@@ -232,7 +244,7 @@ export function KlantFormulier({ title, description, submitLabel, submitting, de
232
244
  : undefined, children: _jsx(Input, { ...form.register("bank_bic") }) }), _jsx(Field, { label: "Bank - Nationale bankcode", children: _jsx(Input, { ...form.register("bank_national_code") }) }), _jsx(Field, { label: "Bank - Adresregel 1", children: _jsx(Input, { ...form.register("bank_address_line1") }) }), _jsx(Field, { label: "Bank - Adresregel 2", children: _jsx(Input, { ...form.register("bank_address_line2") }) }), _jsx(Field, { label: "Bank - Huisnummer", children: _jsx(Input, { ...form.register("bank_house_number") }) }), _jsx(Field, { label: "Bank - Postcode", error: fieldErr("bank_postal_code"), children: _jsx(Input, { ...normalizeOnBlur("bank_postal_code", normPostal) }) }), _jsx(Field, { label: "Bank - Plaats", children: _jsx(Input, { ...form.register("bank_city") }) }), _jsx(Field, { label: "Bank - Staat", children: _jsx(Input, { ...form.register("bank_state") }) }), _jsx("div", { className: "md:col-span-2", children: _jsx(Field, { label: "Bank - Land", children: _jsx(Input, { ...form.register("bank_country") }) }) })] }) }), _jsx(Section, { k: "facturering", title: "Facturering & betaling", icon: _jsx(ReceiptText, { className: "h-5 w-5" }), open: open.facturering, onToggle: toggle, summary: `${values.payment_term_days ?? "—"} vervaldagen · ${values.vat_display || TWINFIELD_DEFAULTS.vat_display}`, hasError: sectionHasError([
233
245
  "discount_percentage", "discount_days", "payment_term_days", "vat_display",
234
246
  "discount_surcharge", "payment_spec_method", "invoice_send_as", "notes",
235
- ]), children: _jsxs("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4", children: [_jsx(Field, { label: "Kortingspercentage (%)", error: fieldErr("discount_percentage"), hint: "Alleen voor dit systeem; Twinfield accepteert dit niet op een debiteur.", children: _jsx(Input, { type: "number", step: "0.01", min: 0, max: 100, ...numberField("discount_percentage") }) }), _jsx(Field, { label: "Aantal kortingsdagen", error: fieldErr("discount_days"), hint: "Alleen voor dit systeem; gaat niet mee naar Twinfield.", children: _jsx(Input, { type: "number", min: 0, max: 365, ...numberField("discount_days") }) }), _jsx(Field, { label: "Aantal vervaldagen", error: fieldErr("payment_term_days"), children: _jsx(Input, { type: "number", min: 0, max: 365, ...numberField("payment_term_days") }) }), _jsx(Field, { label: "Btw-weergave op factuur", error: fieldErr("vat_display"), children: _jsxs(Select, { value: form.watch("vat_display") || TWINFIELD_DEFAULTS.vat_display, onValueChange: (v) => form.setValue("vat_display", v), children: [_jsx(SelectTrigger, { children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: VAT_DISPLAY_OPTIONS.map((o) => (_jsx(SelectItem, { value: o.value, children: o.label }, o.value))) })] }) }), _jsx(Field, { label: "Korting / opslag", error: fieldErr("discount_surcharge"), children: _jsxs(Select, { value: form.watch("discount_surcharge") || TWINFIELD_DEFAULTS.discount_surcharge, onValueChange: (v) => form.setValue("discount_surcharge", v), children: [_jsx(SelectTrigger, { children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: DISCOUNT_SURCHARGE_OPTIONS.map((o) => (_jsx(SelectItem, { value: o.value, children: o.label }, o.value))) })] }) }), _jsx(Field, { label: "Verstuurmethode betaalspecificatie", error: fieldErr("payment_spec_method"), children: _jsxs(Select, { value: form.watch("payment_spec_method") || TWINFIELD_DEFAULTS.payment_spec_method, onValueChange: (v) => form.setValue("payment_spec_method", v), children: [_jsx(SelectTrigger, { children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: PAYMENT_SPEC_METHOD_OPTIONS.map((o) => (_jsx(SelectItem, { value: o.value, children: o.label }, o.value))) })] }) }), _jsx(Field, { label: "Elektronisch factureren", error: fieldErr("e_invoicing"), children: _jsxs(Select, { value: form.watch("e_invoicing") ? "ja" : "nee", onValueChange: (v) => form.setValue("e_invoicing", v === "ja"), children: [_jsx(SelectTrigger, { children: _jsx(SelectValue, {}) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "nee", children: "Nee" }), _jsx(SelectItem, { value: "ja", children: "Ja" })] })] }) }), _jsx(Field, { label: "Factuur versturen als", error: fieldErr("invoice_send_as"), children: _jsxs(Select, { value: form.watch("invoice_send_as") || TWINFIELD_DEFAULTS.invoice_send_as, onValueChange: (v) => form.setValue("invoice_send_as", v), children: [_jsx(SelectTrigger, { children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: INVOICE_SEND_AS_OPTIONS.map((o) => (_jsx(SelectItem, { value: o.value, children: o.label }, o.value))) })] }) }), _jsxs("div", { className: "md:col-span-2 space-y-1.5", children: [_jsx(Label, { children: "Notities" }), _jsx(Textarea, { rows: 4, ...form.register("notes") }), _jsx("p", { className: "text-xs text-muted-foreground", children: "Interne notitie; wordt niet naar Twinfield gestuurd." })] })] }) }), _jsx(Section, { k: "boekhouding", title: "Kredietbeheer & rekeningschema", icon: _jsx(BookOpen, { className: "h-5 w-5" }), open: open.boekhouding, onToggle: toggle, summary: filled(values.ledger_account)
247
+ ]), children: _jsxs("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4", children: [_jsx(Field, { label: "Kortingspercentage (%)", error: fieldErr("discount_percentage"), hint: "Alleen voor dit systeem; Twinfield accepteert dit niet op een debiteur.", children: _jsx(Input, { type: "number", step: "0.01", min: 0, max: 100, ...numberField("discount_percentage") }) }), _jsx(Field, { label: "Aantal kortingsdagen", error: fieldErr("discount_days"), hint: "Alleen voor dit systeem; gaat niet mee naar Twinfield.", children: _jsx(Input, { type: "number", min: 0, max: 365, ...numberField("discount_days") }) }), _jsx(Field, { label: "Aantal vervaldagen", error: fieldErr("payment_term_days"), children: _jsx(Input, { type: "number", min: 0, max: 365, ...numberField("payment_term_days") }) }), _jsx(Field, { label: "Btw-weergave op factuur", error: fieldErr("vat_display"), children: _jsxs(Select, { value: form.watch("vat_display") || TWINFIELD_DEFAULTS.vat_display, onValueChange: (v) => form.setValue("vat_display", v), children: [_jsx(SelectTrigger, { children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: VAT_DISPLAY_OPTIONS.map((o) => (_jsx(SelectItem, { value: o.value, children: o.label }, o.value))) })] }) }), _jsx(Field, { label: "Korting / opslag", error: fieldErr("discount_surcharge"), children: _jsxs(Select, { value: form.watch("discount_surcharge") || TWINFIELD_DEFAULTS.discount_surcharge, onValueChange: (v) => form.setValue("discount_surcharge", v), children: [_jsx(SelectTrigger, { children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: DISCOUNT_SURCHARGE_OPTIONS.map((o) => (_jsx(SelectItem, { value: o.value, children: o.label }, o.value))) })] }) }), _jsx(Field, { label: "Verstuurmethode betaalspecificatie", error: fieldErr("payment_spec_method"), children: _jsxs(Select, { value: form.watch("payment_spec_method") || TWINFIELD_DEFAULTS.payment_spec_method, onValueChange: (v) => form.setValue("payment_spec_method", v), children: [_jsx(SelectTrigger, { children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: PAYMENT_SPEC_METHOD_OPTIONS.map((o) => (_jsx(SelectItem, { value: o.value, children: o.label }, o.value))) })] }) }), _jsx(Field, { label: "Elektronisch factureren", error: fieldErr("e_invoicing"), children: _jsxs(Select, { value: form.watch("e_invoicing") ? "ja" : "nee", onValueChange: (v) => form.setValue("e_invoicing", v === "ja"), children: [_jsx(SelectTrigger, { children: _jsx(SelectValue, {}) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "nee", children: "Nee" }), _jsx(SelectItem, { value: "ja", children: "Ja" })] })] }) }), _jsx(Field, { label: "Factuur versturen als", error: fieldErr("invoice_send_as"), children: _jsxs(Select, { value: form.watch("invoice_send_as") || TWINFIELD_DEFAULTS.invoice_send_as, onValueChange: (v) => form.setValue("invoice_send_as", v), children: [_jsx(SelectTrigger, { children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: INVOICE_SEND_AS_OPTIONS.map((o) => (_jsx(SelectItem, { value: o.value, children: o.label }, o.value))) })] }) }), _jsxs("div", { className: "md:col-span-2 space-y-1.5", children: [_jsx(Label, { children: "Notities" }), _jsx(Textarea, { rows: 4, ...form.register("notes") }), _jsx("p", { className: "text-xs text-muted-foreground", children: "Interne notitie; wordt niet naar Twinfield gestuurd." })] })] }) }), _jsx(Section, { k: "boekhouding", title: "Boekhouding (krediet en grootboek)", icon: _jsx(BookOpen, { className: "h-5 w-5" }), open: open.boekhouding, onToggle: toggle, summary: filled(values.ledger_account)
236
248
  ? `Grootboek ${values.ledger_account}`
237
249
  : "Grootboekrekening nog niet gekozen", hasError: sectionHasError(["credit_limit", "send_reminder", "ledger_account"]), children: _jsxs("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4", children: [_jsx(Field, { label: "Kredietlimiet (\u20AC)", error: fieldErr("credit_limit"), hint: TWINFIELD_READONLY_HINT, children: _jsx(Input, { type: "number", step: "0.01", min: 0, readOnly: true, className: "bg-muted", ...numberField("credit_limit") }) }), _jsx(Field, { label: "Aanmanen", error: fieldErr("send_reminder"), hint: TWINFIELD_READONLY_HINT, children: _jsxs(Select, { disabled: true, value: form.watch("send_reminder") || TWINFIELD_DEFAULTS.send_reminder, onValueChange: (v) => form.setValue("send_reminder", v), children: [_jsx(SelectTrigger, { children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: SEND_REMINDER_OPTIONS.map((o) => (_jsx(SelectItem, { value: o.value, children: o.label }, o.value))) })] }) }), _jsx(Field, { label: "Kredietbeheerder", hint: TWINFIELD_READONLY_HINT, children: _jsx(Input, { readOnly: true, className: "bg-muted", value: creditManagerDisplay(creditInfo ?? {}), placeholder: "\u2014" }) }), _jsx(Field, { label: "E-mail voor aanmaningen", hint: TWINFIELD_READONLY_HINT, children: _jsx(Input, { readOnly: true, className: "bg-muted", value: creditInfo?.reminder_email ?? "", placeholder: "\u2014" }) }), _jsx(Field, { label: "Geblokkeerd", error: fieldErr("blocked"), hint: TWINFIELD_READONLY_HINT, children: _jsxs(Select, { disabled: true, value: form.watch("blocked") ? "ja" : "nee", onValueChange: (v) => form.setValue("blocked", v === "ja"), children: [_jsx(SelectTrigger, { children: _jsx(SelectValue, {}) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "nee", children: "Nee" }), _jsx(SelectItem, { value: "ja", children: "Ja" })] })] }) }), _jsx(Field, { label: "Grootboekrekening", error: fieldErr("ledger_account"), success: isValid("ledger_account"), hint: ledgerLoading
238
250
  ? "Rekeningschema wordt geladen uit Twinfield..."
@@ -1 +1 @@
1
- {"version":3,"file":"KlantenOverzicht.d.ts","sourceRoot":"","sources":["../../../src/components/klanten/KlantenOverzicht.tsx"],"names":[],"mappings":"AAgEA,wBAAgB,gBAAgB,gCAu2B/B"}
1
+ {"version":3,"file":"KlantenOverzicht.d.ts","sourceRoot":"","sources":["../../../src/components/klanten/KlantenOverzicht.tsx"],"names":[],"mappings":"AAgEA,wBAAgB,gBAAgB,gCAw2B/B"}
@@ -472,7 +472,7 @@ export function KlantenOverzicht() {
472
472
  });
473
473
  const toggleSort = (col) => setSort((s) => (s.col === col ? { col, dir: s.dir === "asc" ? "desc" : "asc" } : { col, dir: "asc" }));
474
474
  const lastUpdated = externalData?.last_fetched_at ?? null;
475
- return (_jsx(TooltipPrimitive.Provider, { delayDuration: 200, children: _jsxs("main", { className: "p-6 space-y-5", children: [_jsxs("div", { className: "flex items-start justify-between gap-4 flex-wrap", children: [_jsxs("div", { children: [_jsx("h1", { className: "text-2xl font-bold tracking-tight", children: "Klanten" }), _jsx("p", { className: "text-sm text-muted-foreground mt-1", children: "Beheer en doorzoek al je klanten." })] }), _jsxs("div", { className: "flex gap-2", children: [_jsxs(Button, { variant: "outline", onClick: () => goTo({ to: "/klanten/import" }), children: [_jsx(Upload, { className: "h-4 w-4 mr-2" }), " Importeren"] }), _jsxs(Button, { onClick: () => goTo({ to: "/klanten/nieuw" }), children: [_jsx(Plus, { className: "h-4 w-4 mr-2" }), " Nieuwe klant"] })] })] }), loadError && (_jsxs("div", { className: "flex flex-wrap items-center gap-3 rounded-lg border border-destructive/30 bg-destructive/5 px-4 py-3", children: [_jsx(AlertCircle, { className: "h-5 w-5 text-destructive shrink-0" }), _jsxs("div", { className: "text-sm", children: [_jsx("div", { className: "font-medium", children: isBackendUnavailable(loadError)
475
+ return (_jsx(TooltipPrimitive.Provider, { delayDuration: 200, children: _jsxs("main", { className: "p-4 sm:p-6 space-y-5 min-w-0 max-w-full overflow-x-hidden", children: [_jsxs("div", { className: "flex items-start justify-between gap-4 flex-wrap", children: [_jsxs("div", { className: "min-w-0", children: [_jsx("h1", { className: "text-2xl font-bold tracking-tight", children: "Klanten" }), _jsx("p", { className: "text-sm text-muted-foreground mt-1", children: "Beheer en doorzoek al je klanten." })] }), _jsxs("div", { className: "flex flex-wrap gap-2", children: [_jsxs(Button, { variant: "outline", onClick: () => goTo({ to: "/klanten/import" }), children: [_jsx(Upload, { className: "h-4 w-4 mr-2" }), " Importeren"] }), _jsxs(Button, { onClick: () => goTo({ to: "/klanten/nieuw" }), children: [_jsx(Plus, { className: "h-4 w-4 mr-2" }), " Nieuwe klant"] })] })] }), loadError && (_jsxs("div", { className: "flex flex-wrap items-center gap-3 rounded-lg border border-destructive/30 bg-destructive/5 px-4 py-3", children: [_jsx(AlertCircle, { className: "h-5 w-5 text-destructive shrink-0" }), _jsxs("div", { className: "text-sm", children: [_jsx("div", { className: "font-medium", children: isBackendUnavailable(loadError)
476
476
  ? "De database is tijdelijk niet bereikbaar"
477
477
  : "De klanten konden niet geladen worden" }), _jsx("div", { className: "text-muted-foreground", children: isBackendUnavailable(loadError)
478
478
  ? "Je bent niet uitgelogd. Probeer het over een paar minuten opnieuw."
@@ -504,15 +504,16 @@ export function KlantenOverzicht() {
504
504
  setView("all");
505
505
  setPage(1);
506
506
  } }) }) })) : pageRows.map((c) => {
507
- const secondary = c.commercial_name && c.commercial_name !== c.company_name
508
- ? c.commercial_name
509
- : c.city ?? c.email ?? null;
507
+ // Handelsnaam voorop (zoals Floriday en de verzendbrief); officiële naam klein eronder.
508
+ const hasTrade = !!c.commercial_name && c.commercial_name !== c.company_name;
509
+ const primaryName = hasTrade ? c.commercial_name : c.company_name;
510
+ const secondary = hasTrade ? c.company_name : c.city ?? c.email ?? null;
510
511
  return (_jsxs(TableRow, { className: "cursor-pointer hover:bg-muted/50", onClick: () => c.id
511
512
  ? goTo({ to: "/klanten/$id", params: { id: c.id } })
512
513
  : goTo({
513
514
  to: "/klanten/groep/$groupKey",
514
515
  params: { groupKey: c.group_key },
515
- }), children: [_jsx(TableCell, { className: "py-3", children: _jsxs("div", { className: "flex items-center gap-3", children: [_jsx(CustomerAvatar, { logoUrl: c.logo_url, name: c.company_name, size: "sm" }), _jsxs("div", { className: "min-w-0", children: [_jsxs("div", { className: "font-medium truncate flex items-center gap-2", children: [_jsx("span", { className: "truncate", children: c.company_name }), c.overdue && c.overdue.count > 0 && (_jsxs("span", { title: `${c.overdue.count} openstaande factu${c.overdue.count === 1 ? "ur" : "ren"} · oudste vervallen op ${c.overdue.oldest_due}`, className: "inline-flex items-center gap-1 rounded-full bg-destructive/10 text-destructive px-2 py-0.5 text-[11px] font-medium shrink-0", children: [_jsx(AlertCircle, { className: "h-3 w-3" }), " ", formatEuro(c.overdue.total), " achterstallig"] }))] }), secondary && _jsx("div", { className: "text-xs text-muted-foreground truncate", children: secondary })] })] }) }), _jsx(TableCell, { className: "py-3", children: c.contact_person || c.email ? (_jsxs("div", { className: "min-w-0", children: [c.contact_person && _jsx("div", { className: "text-sm truncate", children: c.contact_person }), c.email && (_jsxs("a", { href: `mailto:${c.email}`, onClick: (e) => e.stopPropagation(), className: "text-xs text-muted-foreground inline-flex items-center gap-1 hover:underline truncate", children: [_jsx(Mail, { className: "h-3 w-3" }), " ", c.email] }))] })) : _jsx("span", { className: "text-muted-foreground", children: "\u2014" }) }), _jsx(TableCell, { className: "py-3", children: _jsx(PriceGroupBadges, { groups: c.price_groups }) }), _jsx(TableCell, { className: "py-3", children: _jsx(ChannelBadges, { providers: c.providers }) }), _jsx(TableCell, { className: "py-3", children: _jsx("span", { className: `inline-flex items-center rounded-full border px-2 py-0.5 text-xs font-medium ${paymentToneClasses(c.payment_method.tone)}`, children: c.payment_method.label }) }), _jsx(TableCell, { className: "py-3 text-sm whitespace-nowrap", children: c.revenue && c.revenue.count > 0 ? (_jsxs("span", { title: `${c.revenue.count} transactie${c.revenue.count === 1 ? "" : "s"} · laatste op ${formatDateNl(c.revenue.last_date)}`, children: [_jsx("span", { className: "font-medium", children: formatEuro(c.revenue.amount) }), _jsxs("span", { className: "block text-xs text-muted-foreground", children: ["laatst ", formatDateNl(c.revenue.last_date)] })] })) : (_jsx("span", { className: "text-muted-foreground", children: "Geen omzet" })) }), _jsx(TableCell, { className: "py-3 text-sm", children: c.country ? (_jsxs("span", { className: "inline-flex items-center gap-1.5", children: [_jsx("span", { className: "text-base", "aria-hidden": "true", children: countryNameToFlag(c.country) ?? "🌐" }), _jsx("span", { children: formatCountry(c.country) })] })) : _jsx("span", { className: "text-muted-foreground", children: "\u2014" }) }), _jsx(TableCell, { className: "py-3", children: _jsxs("div", { className: "flex items-center justify-between gap-2", children: [_jsx(StatusPill, { active: c.is_active, missing: c.missing }), c.id ? (_jsxs(Button, { variant: "ghost", size: "sm", className: "h-8 w-8 p-0", title: "Klant bewerken", onClick: (e) => {
516
+ }), children: [_jsx(TableCell, { className: "py-3", children: _jsxs("div", { className: "flex items-center gap-3", children: [_jsx(CustomerAvatar, { logoUrl: c.logo_url, name: c.company_name, size: "sm" }), _jsxs("div", { className: "min-w-0", children: [_jsxs("div", { className: "font-medium truncate flex items-center gap-2", children: [_jsx("span", { className: "truncate", children: primaryName }), c.overdue && c.overdue.count > 0 && (_jsxs("span", { title: `${c.overdue.count} openstaande factu${c.overdue.count === 1 ? "ur" : "ren"} · oudste vervallen op ${c.overdue.oldest_due}`, className: "inline-flex items-center gap-1 rounded-full bg-destructive/10 text-destructive px-2 py-0.5 text-[11px] font-medium shrink-0", children: [_jsx(AlertCircle, { className: "h-3 w-3" }), " ", formatEuro(c.overdue.total), " achterstallig"] }))] }), secondary && _jsx("div", { className: "text-xs text-muted-foreground truncate", children: secondary })] })] }) }), _jsx(TableCell, { className: "py-3", children: c.contact_person || c.email ? (_jsxs("div", { className: "min-w-0", children: [c.contact_person && _jsx("div", { className: "text-sm truncate", children: c.contact_person }), c.email && (_jsxs("a", { href: `mailto:${c.email}`, onClick: (e) => e.stopPropagation(), className: "text-xs text-muted-foreground inline-flex items-center gap-1 hover:underline truncate", children: [_jsx(Mail, { className: "h-3 w-3" }), " ", c.email] }))] })) : _jsx("span", { className: "text-muted-foreground", children: "\u2014" }) }), _jsx(TableCell, { className: "py-3", children: _jsx(PriceGroupBadges, { groups: c.price_groups }) }), _jsx(TableCell, { className: "py-3", children: _jsx(ChannelBadges, { providers: c.providers }) }), _jsx(TableCell, { className: "py-3", children: _jsx("span", { className: `inline-flex items-center rounded-full border px-2 py-0.5 text-xs font-medium ${paymentToneClasses(c.payment_method.tone)}`, children: c.payment_method.label }) }), _jsx(TableCell, { className: "py-3 text-sm whitespace-nowrap", children: c.revenue && c.revenue.count > 0 ? (_jsxs("span", { title: `${c.revenue.count} transactie${c.revenue.count === 1 ? "" : "s"} · laatste op ${formatDateNl(c.revenue.last_date)}`, children: [_jsx("span", { className: "font-medium", children: formatEuro(c.revenue.amount) }), _jsxs("span", { className: "block text-xs text-muted-foreground", children: ["laatst ", formatDateNl(c.revenue.last_date)] })] })) : (_jsx("span", { className: "text-muted-foreground", children: "Geen omzet" })) }), _jsx(TableCell, { className: "py-3 text-sm", children: c.country ? (_jsxs("span", { className: "inline-flex items-center gap-1.5", children: [_jsx("span", { className: "text-base", "aria-hidden": "true", children: countryNameToFlag(c.country) ?? "🌐" }), _jsx("span", { children: formatCountry(c.country) })] })) : _jsx("span", { className: "text-muted-foreground", children: "\u2014" }) }), _jsx(TableCell, { className: "py-3", children: _jsxs("div", { className: "flex items-center justify-between gap-2", children: [_jsx(StatusPill, { active: c.is_active, missing: c.missing }), c.id ? (_jsxs(Button, { variant: "ghost", size: "sm", className: "h-8 w-8 p-0", title: "Klant bewerken", onClick: (e) => {
516
517
  e.stopPropagation();
517
518
  setEditTarget({ customerId: c.id });
518
519
  }, children: [_jsx(Pencil, { className: "h-4 w-4" }), _jsx("span", { className: "sr-only", children: "Bewerken" })] })) : c.twinfield_code ? (_jsxs(Button, { variant: "ghost", size: "sm", className: "h-8 w-8 p-0", title: "Twinfield-klant overnemen en bewerken", onClick: (e) => {
@@ -1 +1 @@
1
- {"version":3,"file":"NetwerkPagina.d.ts","sourceRoot":"","sources":["../../../src/components/klanten/NetwerkPagina.tsx"],"names":[],"mappings":"AAqCA,wBAAgB,aAAa,gCAmP5B"}
1
+ {"version":3,"file":"NetwerkPagina.d.ts","sourceRoot":"","sources":["../../../src/components/klanten/NetwerkPagina.tsx"],"names":[],"mappings":"AAsCA,wBAAgB,aAAa,gCAmQ5B"}
@@ -3,7 +3,7 @@ import { formatDate } from "../../lib/format-date.js";
3
3
  import { useQuery } from "@tanstack/react-query";
4
4
  import { useServerFn } from "@tanstack/react-start";
5
5
  import { useState } from "react";
6
- import { Input, Card, CardContent, Badge, Table, TableBody, TableCell, TableHead, TableHeader, TableRow, Switch, Label, } from "@flowselections/core";
6
+ import { Input, Button, Card, CardContent, Badge, Table, TableBody, TableCell, TableHead, TableHeader, TableRow, Switch, Label, } from "@flowselections/core";
7
7
  import { Search, MapPin, Globe } from "lucide-react";
8
8
  import { BackLink } from "./BackLink.js";
9
9
  import { countryNameToFlag } from "../../lib/countries.js";
@@ -25,10 +25,13 @@ export function NetwerkPagina() {
25
25
  const [hideUnknown, setHideUnknown] = useState(false);
26
26
  const [types, setTypes] = useState(["CUSTOMER", "AGENT"]);
27
27
  const [includeExpired, setIncludeExpired] = useState(false);
28
+ const [page, setPage] = useState(0);
29
+ const PAGE_SIZE = 100;
28
30
  const list = useServerFn(listFloridayNetwork);
29
31
  const { data, isLoading } = useQuery({
30
- queryKey: ["floriday-network", search, types, includeExpired],
31
- queryFn: () => list({ data: { search, types, includeExpired, limit: 1000 } }).then(decodeDeep),
32
+ queryKey: ["floriday-network", search, types, includeExpired, page],
33
+ queryFn: () => list({ data: { search, types, includeExpired, limit: PAGE_SIZE, offset: page * PAGE_SIZE } }).then(decodeDeep),
34
+ placeholderData: (prev) => prev,
32
35
  });
33
36
  const all = data?.organizations ?? [];
34
37
  const named = all.filter((o) => o.organization_name && o.organization_name.trim() !== "");
@@ -38,14 +41,15 @@ export function NetwerkPagina() {
38
41
  const truncated = !!data?.truncated;
39
42
  const toggleType = (value) => {
40
43
  setTypes((prev) => prev.includes(value) ? prev.filter((t) => t !== value) : [...prev, value]);
44
+ setPage(0);
41
45
  };
42
- return (_jsx(TooltipPrimitive.Provider, { delayDuration: 200, children: _jsxs("main", { className: "p-6 space-y-4", children: [_jsx("div", { className: "flex items-center justify-between gap-4", children: _jsxs("div", { children: [_jsx(BackLink, {}), _jsx("h1", { className: "text-2xl font-bold tracking-tight", children: "Klanten toevoegen uit netwerk" }), _jsx("p", { className: "text-sm text-muted-foreground", children: "Doorzoek alle organisaties die via het netwerk beschikbaar zijn." })] }) }), _jsxs("div", { className: "relative max-w-md", children: [_jsx(Search, { className: "absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" }), _jsx(Input, { placeholder: "Zoek op organisatienaam of GLN...", value: search, onChange: (e) => setSearch(e.target.value), className: "pl-9" })] }), _jsx("div", { className: "flex items-center gap-2 flex-wrap", children: TYPE_OPTIONS.map((opt) => {
46
+ return (_jsx(TooltipPrimitive.Provider, { delayDuration: 200, children: _jsxs("main", { className: "p-4 sm:p-6 space-y-4 min-w-0 max-w-full overflow-x-hidden", children: [_jsx("div", { className: "flex items-center justify-between gap-4", children: _jsxs("div", { children: [_jsx(BackLink, {}), _jsx("h1", { className: "text-2xl font-bold tracking-tight", children: "Klanten toevoegen uit netwerk" }), _jsx("p", { className: "text-sm text-muted-foreground", children: "Doorzoek alle organisaties die via het netwerk beschikbaar zijn." })] }) }), _jsxs("div", { className: "relative max-w-md", children: [_jsx(Search, { className: "absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" }), _jsx(Input, { placeholder: "Zoek op organisatienaam of GLN...", value: search, onChange: (e) => { setSearch(e.target.value); setPage(0); }, className: "pl-9" })] }), _jsx("div", { className: "flex items-center gap-2 flex-wrap", children: TYPE_OPTIONS.map((opt) => {
43
47
  const active = types.includes(opt.value);
44
48
  return (_jsx("button", { type: "button", onClick: () => toggleType(opt.value), className: "inline-flex items-center gap-1.5 rounded-full border px-3 py-1 text-xs transition-colors " +
45
49
  (active
46
50
  ? "bg-primary text-primary-foreground border-primary"
47
51
  : "bg-card text-muted-foreground border-border hover:bg-muted"), children: opt.label }, opt.value));
48
- }) }), _jsxs("div", { className: "flex items-center gap-6 flex-wrap text-sm", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Switch, { id: "include-expired", checked: includeExpired, onCheckedChange: setIncludeExpired }), _jsx(Label, { htmlFor: "include-expired", className: "cursor-pointer", children: "Toon be\u00EBindigde relaties" })] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Switch, { id: "hide-unknown", checked: hideUnknown, onCheckedChange: setHideUnknown }), _jsx(Label, { htmlFor: "hide-unknown", className: "cursor-pointer", children: "Verberg organisaties zonder naam" }), hideUnknown && hiddenCount > 0 ? (_jsxs("span", { className: "text-muted-foreground", children: ["(", hiddenCount, " verborgen)"] })) : null] })] }), _jsx("div", { className: "text-xs text-muted-foreground", children: isLoading ? ("Laden…") : (_jsxs(_Fragment, { children: [_jsx("span", { className: "font-medium text-foreground", children: orgs.length.toLocaleString("nl-NL") }), " ", "weergegeven van", " ", _jsx("span", { className: "font-medium text-foreground", children: totalMatching.toLocaleString("nl-NL") }), " ", "organisaties \u00B7 bron: Floriday", truncated && (_jsx("span", { className: "ml-1 text-warning", children: "(verfijn de zoekopdracht om alles te zien)" }))] })) }), _jsx(Card, { children: _jsx(CardContent, { className: "p-0", children: _jsxs(Table, { children: [_jsx(TableHeader, { children: _jsxs(TableRow, { children: [_jsx(TableHead, { children: "Organisatie" }), _jsx(TableHead, { children: "GLN" }), _jsx(TableHead, { children: "Adres" }), _jsx(TableHead, { children: "Land" }), _jsx(TableHead, { children: "Type" }), _jsx(TableHead, { children: "Prijscategorie" }), _jsx(TableHead, { children: "Bijgewerkt" })] }) }), _jsx(TableBody, { children: isLoading ? (_jsx(TableRow, { children: _jsx(TableCell, { colSpan: 7, className: "text-center py-8 text-muted-foreground", children: "Laden\u2026" }) })) : orgs.length === 0 ? (_jsx(TableRow, { children: _jsx(TableCell, { colSpan: 7, className: "text-center py-8 text-muted-foreground", children: "Geen organisaties gevonden." }) })) : (orgs.map((o) => {
52
+ }) }), _jsxs("div", { className: "flex items-center gap-6 flex-wrap text-sm", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Switch, { id: "include-expired", checked: includeExpired, onCheckedChange: (v) => { setIncludeExpired(v); setPage(0); } }), _jsx(Label, { htmlFor: "include-expired", className: "cursor-pointer", children: "Toon be\u00EBindigde relaties" })] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Switch, { id: "hide-unknown", checked: hideUnknown, onCheckedChange: setHideUnknown }), _jsx(Label, { htmlFor: "hide-unknown", className: "cursor-pointer", children: "Verberg organisaties zonder naam" }), hideUnknown && hiddenCount > 0 ? (_jsxs("span", { className: "text-muted-foreground", children: ["(", hiddenCount, " verborgen)"] })) : null] })] }), _jsxs("div", { className: "flex flex-wrap items-center justify-between gap-2 text-xs text-muted-foreground", children: [_jsx("div", { children: isLoading ? ("Laden…") : totalMatching === 0 ? ("0 organisaties gevonden · bron: Floriday") : (_jsxs(_Fragment, { children: [_jsxs("span", { className: "font-medium text-foreground", children: [(page * PAGE_SIZE + 1).toLocaleString("nl-NL"), "\u2013", (page * PAGE_SIZE + all.length).toLocaleString("nl-NL")] }), " ", "van", " ", _jsx("span", { className: "font-medium text-foreground", children: totalMatching.toLocaleString("nl-NL") }), " ", "getoond \u00B7 bron: Floriday", totalMatching > PAGE_SIZE && _jsx("span", { className: "ml-1", children: "\u00B7 verfijn je zoekopdracht" }), hiddenCount > 0 && _jsxs("span", { className: "ml-1", children: ["(", hiddenCount, " zonder naam verborgen)"] })] })) }), totalMatching > PAGE_SIZE && (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Button, { size: "sm", variant: "outline", disabled: page === 0, onClick: () => setPage((p) => Math.max(0, p - 1)), children: "Vorige" }), _jsxs("span", { children: ["Pagina ", page + 1, " van ", Math.ceil(totalMatching / PAGE_SIZE)] }), _jsx(Button, { size: "sm", variant: "outline", disabled: !truncated, onClick: () => setPage((p) => p + 1), children: "Volgende" })] }))] }), _jsx(Card, { children: _jsx(CardContent, { className: "p-0", children: _jsxs(Table, { children: [_jsx(TableHeader, { children: _jsxs(TableRow, { children: [_jsx(TableHead, { children: "Organisatie" }), _jsx(TableHead, { children: "GLN" }), _jsx(TableHead, { children: "Adres" }), _jsx(TableHead, { children: "Land" }), _jsx(TableHead, { children: "Type" }), _jsx(TableHead, { children: "Prijscategorie" }), _jsx(TableHead, { children: "Bijgewerkt" })] }) }), _jsx(TableBody, { children: isLoading ? (_jsx(TableRow, { children: _jsx(TableCell, { colSpan: 7, className: "text-center py-8 text-muted-foreground", children: "Laden\u2026" }) })) : orgs.length === 0 ? (_jsx(TableRow, { children: _jsx(TableCell, { colSpan: 7, className: "text-center py-8 text-muted-foreground", children: "Geen organisaties gevonden." }) })) : (orgs.map((o) => {
49
53
  const displayName = o.organization_name && o.organization_name.trim() !== ""
50
54
  ? o.organization_name
51
55
  : null;
@@ -1 +1 @@
1
- {"version":3,"file":"NieuweKlant.d.ts","sourceRoot":"","sources":["../../../src/components/klanten/NieuweKlant.tsx"],"names":[],"mappings":"AAoBA,wBAAgB,WAAW,gCAmH1B"}
1
+ {"version":3,"file":"NieuweKlant.d.ts","sourceRoot":"","sources":["../../../src/components/klanten/NieuweKlant.tsx"],"names":[],"mappings":"AAoBA,wBAAgB,WAAW,gCAgJ1B"}
@@ -1,11 +1,11 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { friendlyError } from "../../lib/errors";
3
3
  import { useNavigate } from "@tanstack/react-router";
4
4
  import { useState } from "react";
5
- import { useMutation, useQueryClient } from "@tanstack/react-query";
5
+ import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
6
6
  import { useServerFn } from "@tanstack/react-start";
7
7
  import { toast, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, } from "@flowselections/core";
8
- import { createCustomer } from "../../lib/customers.functions.js";
8
+ import { createCustomer, getActiveTwinfieldAdministration } from "../../lib/customers.functions.js";
9
9
  import { upsertDeliveryLocation } from "../../lib/delivery-locations.functions.js";
10
10
  import { KlantFormulier, emptyKlantFormValues, } from "./KlantFormulier.js";
11
11
  import { useCustomerDefaults, MISSING_DEFAULT_MESSAGE } from "./useCustomerDefaults.js";
@@ -19,6 +19,9 @@ export function NieuweKlant() {
19
19
  const [pendingData, setPendingData] = useState(null);
20
20
  const [locations, setLocations] = useState([]);
21
21
  const cd = useCustomerDefaults();
22
+ const [twCreate, setTwCreate] = useState(true);
23
+ const getAdmin = useServerFn(getActiveTwinfieldAdministration);
24
+ const admin = useQuery({ queryKey: ["twinfield-active-admin"], queryFn: () => getAdmin() });
22
25
  const mut = useMutation({
23
26
  mutationFn: async (data) => {
24
27
  const r = await create({ data });
@@ -62,9 +65,9 @@ export function NieuweKlant() {
62
65
  onError: (e) => toast.error(friendlyError(e, "Klant aanmaken mislukt")),
63
66
  });
64
67
  const onSubmit = (values) => {
65
- setPendingData(values);
66
- // Elke lokale klant gaat automatisch naar Twinfield — geen uitzondering.
67
- mut.mutate(values);
68
+ const v = { ...values, twinfield_create: twCreate };
69
+ setPendingData(v);
70
+ mut.mutate(v);
68
71
  };
69
72
  const forceCreate = () => {
70
73
  if (!pendingData)
@@ -86,5 +89,5 @@ export function NieuweKlant() {
86
89
  const notice = cd.isAdmin && d && d.missing.length > 0
87
90
  ? `${MISSING_DEFAULT_MESSAGE}: ${d.missing.map((m) => LABELS[m]).join(", ")}. Leeg gelaten velden gaan niet naar Twinfield; Twinfield gebruikt dan zijn eigen standaard.`
88
91
  : undefined;
89
- return (_jsx(KlantFormulier, { notice: notice, title: "Nieuwe klant", description: "Vul de gegevens in om een nieuwe klant aan te maken. Het debiteurnummer wordt automatisch gegenereerd.", submitLabel: "Klant aanmaken", submitting: mut.isPending, defaultValues: defaultValues, onSubmit: onSubmit, onCancel: () => navigate({ to: "/klanten" }), extraSections: _jsx(NieuweKlantLocaties, { locations: locations, onChange: setLocations, defaultCountry: d?.country ?? "" }), extra: _jsx(AlertDialog, { open: dupes.length > 0, onOpenChange: (o) => !o && setDupes([]), children: _jsxs(AlertDialogContent, { children: [_jsxs(AlertDialogHeader, { children: [_jsx(AlertDialogTitle, { children: "Mogelijke duplicaten gevonden" }), _jsx(AlertDialogDescription, { children: _jsx("ul", { className: "list-disc pl-5 space-y-1 mt-2", children: dupes.map((d) => (_jsxs("li", { children: [_jsx("strong", { children: d.company_name }), " \u2014 ", d.reason] }, d.id + d.reason))) }) })] }), _jsxs(AlertDialogFooter, { children: [_jsx(AlertDialogCancel, { children: "Annuleren" }), _jsx(AlertDialogAction, { onClick: forceCreate, children: "Toch aanmaken" })] })] }) }) }));
92
+ return (_jsx(KlantFormulier, { notice: notice, title: "Nieuwe klant", description: "Vul de gegevens in om een nieuwe klant aan te maken. Het debiteurnummer wordt automatisch gegenereerd.", submitLabel: "Klant aanmaken", submitting: mut.isPending, defaultValues: defaultValues, onSubmit: onSubmit, onCancel: () => navigate({ to: "/klanten" }), extraSections: _jsxs(_Fragment, { children: [_jsx(NieuweKlantLocaties, { locations: locations, onChange: setLocations, defaultCountry: d?.country ?? "" }), _jsx("div", { className: "rounded-md border px-4 py-3 text-sm", children: admin.data?.active ? (_jsxs("label", { className: "flex items-start gap-2 cursor-pointer", children: [_jsx("input", { type: "checkbox", className: "mt-1", checked: twCreate, onChange: (e) => setTwCreate(e.target.checked) }), _jsxs("span", { children: ["Ook als debiteur aanmaken in Twinfield", _jsxs("span", { className: "block text-xs text-muted-foreground", children: ["Administratie: ", admin.data.name ? `${admin.data.name} (${admin.data.code})` : admin.data.code] })] })] })) : admin.isLoading ? (_jsx("span", { className: "text-muted-foreground", children: "Twinfield-koppeling controleren\u2026" })) : (_jsx("span", { className: "text-muted-foreground", children: "Er is geen actieve Twinfield-koppeling. De klant wordt alleen in het ERP aangemaakt; er gaat niets naar Twinfield." })) })] }), extra: _jsx(AlertDialog, { open: dupes.length > 0, onOpenChange: (o) => !o && setDupes([]), children: _jsxs(AlertDialogContent, { children: [_jsxs(AlertDialogHeader, { children: [_jsx(AlertDialogTitle, { children: "Mogelijke duplicaten gevonden" }), _jsx(AlertDialogDescription, { children: _jsx("ul", { className: "list-disc pl-5 space-y-1 mt-2", children: dupes.map((d) => (_jsxs("li", { children: [_jsx("strong", { children: d.company_name }), " \u2014 ", d.reason] }, d.id + d.reason))) }) })] }), _jsxs(AlertDialogFooter, { children: [_jsx(AlertDialogCancel, { children: "Annuleren" }), _jsx(AlertDialogAction, { onClick: forceCreate, children: "Toch aanmaken" })] })] }) }) }));
90
93
  }
@@ -1 +1 @@
1
- {"version":3,"file":"CustomerPickerDialog.d.ts","sourceRoot":"","sources":["../../../../src/components/klanten/pricegroups/CustomerPickerDialog.tsx"],"names":[],"mappings":"AAcA,wBAAgB,oBAAoB,CAAC,EACnC,YAAY,EAAE,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,QAAQ,GAClD,EAAE;IACD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,IAAI,EAAE,OAAO,CAAC;IACd,YAAY,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IACnC,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;CACrC,+BA+IA"}
1
+ {"version":3,"file":"CustomerPickerDialog.d.ts","sourceRoot":"","sources":["../../../../src/components/klanten/pricegroups/CustomerPickerDialog.tsx"],"names":[],"mappings":"AAgBA,wBAAgB,oBAAoB,CAAC,EACnC,YAAY,EAAE,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,QAAQ,GAClD,EAAE;IACD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,IAAI,EAAE,OAAO,CAAC;IACd,YAAY,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IACnC,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;CACrC,+BA6JA"}