@flowselections/floriday-klanten-module 1.0.46 → 1.0.49
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist-lib/components/klanten/BewerkKlant.d.ts.map +1 -1
- package/dist-lib/components/klanten/BewerkKlant.js +1 -1
- package/dist-lib/components/klanten/CustomerAnalyticsTab.d.ts.map +1 -1
- package/dist-lib/components/klanten/CustomerAnalyticsTab.js +2 -2
- package/dist-lib/components/klanten/FloridayNetworkStatus.d.ts +5 -0
- package/dist-lib/components/klanten/FloridayNetworkStatus.d.ts.map +1 -0
- package/dist-lib/components/klanten/FloridayNetworkStatus.js +18 -0
- package/dist-lib/components/klanten/KlantDetailLayout.d.ts +2 -0
- package/dist-lib/components/klanten/KlantDetailLayout.d.ts.map +1 -1
- package/dist-lib/components/klanten/KlantDetailLayout.js +6 -1
- package/dist-lib/components/klanten/KlantFormulier.d.ts +3 -1
- package/dist-lib/components/klanten/KlantFormulier.d.ts.map +1 -1
- package/dist-lib/components/klanten/KlantFormulier.js +16 -4
- package/dist-lib/components/klanten/NetwerkPagina.d.ts.map +1 -1
- package/dist-lib/components/klanten/NetwerkPagina.js +9 -5
- package/dist-lib/components/klanten/pricegroups/CustomerPickerDialog.d.ts.map +1 -1
- package/dist-lib/components/klanten/pricegroups/CustomerPickerDialog.js +10 -2
- package/dist-lib/components/klanten/pricegroups/PriceGroupsPage.d.ts.map +1 -1
- package/dist-lib/components/klanten/pricegroups/PriceGroupsPage.js +4 -7
- package/dist-lib/components/klanten/pricegroups/UnknownFloridayMembers.d.ts +11 -0
- package/dist-lib/components/klanten/pricegroups/UnknownFloridayMembers.d.ts.map +1 -0
- package/dist-lib/components/klanten/pricegroups/UnknownFloridayMembers.js +43 -0
- package/dist-lib/index.js +1 -1
- package/dist-lib/integrations/supabase/types.d.ts +464 -2
- package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/types.js +1 -0
- package/dist-lib/lib/customers.functions.d.ts +3 -0
- package/dist-lib/lib/customers.functions.d.ts.map +1 -1
- package/dist-lib/lib/customers.functions.js +6 -2
- package/dist-lib/lib/floriday-network-status.functions.d.ts +18 -0
- package/dist-lib/lib/floriday-network-status.functions.d.ts.map +1 -0
- package/dist-lib/lib/floriday-network-status.functions.js +41 -0
- package/dist-lib/lib/price-groups-local.functions.d.ts +42 -0
- package/dist-lib/lib/price-groups-local.functions.d.ts.map +1 -1
- package/dist-lib/lib/price-groups-local.functions.js +120 -1
- package/dist-lib/lib/vat-format.d.ts +17 -0
- package/dist-lib/lib/vat-format.d.ts.map +1 -0
- package/dist-lib/lib/vat-format.js +64 -0
- package/package.json +4 -4
|
@@ -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,+
|
|
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,
|
|
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;
|
|
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;IACtB,6DAA6D;IAC7D,wBAAwB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1C,CAAC;AA8BF,wBAAgB,mBAAmB,CAAC,CAAC,EAAE,GAAG,EAAE,QAAQ,GAAE,GAAG,EAAO,GAAG,eAAe,CAkCjF;AAED,wBAAgB,qBAAqB,CAAC,CAAC,EAAE,eAAe,GAAG,eAAe,CAyCzE;AAED,wBAAgB,sBAAsB,CAAC,CAAC,EAAE,gBAAgB,GAAG,eAAe,CAiC3E;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,+BAyMA;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"}
|
|
@@ -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;
|
|
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) }) }),
|
|
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: "
|
|
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":"NetwerkPagina.d.ts","sourceRoot":"","sources":["../../../src/components/klanten/NetwerkPagina.tsx"],"names":[],"mappings":"
|
|
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:
|
|
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] })] }),
|
|
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":"CustomerPickerDialog.d.ts","sourceRoot":"","sources":["../../../../src/components/klanten/pricegroups/CustomerPickerDialog.tsx"],"names":[],"mappings":"
|
|
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"}
|
|
@@ -5,14 +5,17 @@ import { useServerFn } from "@tanstack/react-start";
|
|
|
5
5
|
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter, Button, Input, Checkbox, Badge, } from "@flowselections/core";
|
|
6
6
|
import { Search } from "lucide-react";
|
|
7
7
|
import { listExternalCustomers } from "../../../lib/external-customers.functions";
|
|
8
|
-
import { setPriceGroupMembers, getCustomerPriceGroup, } from "../../../lib/price-groups-local.functions";
|
|
8
|
+
import { setPriceGroupMembers, getCustomerPriceGroup, listUnknownFloridayMembers, } from "../../../lib/price-groups-local.functions";
|
|
9
9
|
import { decodeDeep } from "../../../lib/decode-html";
|
|
10
10
|
import { countryNameToFlag } from "../../../lib/countries";
|
|
11
|
+
import { formatDateTime } from "../../../lib/format-date";
|
|
12
|
+
import { UnknownFloridayMembers } from "./UnknownFloridayMembers";
|
|
11
13
|
export function CustomerPickerDialog({ priceGroupId, open, onOpenChange, value, onChange, }) {
|
|
12
14
|
const qc = useQueryClient();
|
|
13
15
|
const listCustomers = useServerFn(listExternalCustomers);
|
|
14
16
|
const getGroup = useServerFn(getCustomerPriceGroup);
|
|
15
17
|
const setMembers = useServerFn(setPriceGroupMembers);
|
|
18
|
+
const listUnknown = useServerFn(listUnknownFloridayMembers);
|
|
16
19
|
const [search, setSearch] = useState("");
|
|
17
20
|
const [selected, setSelected] = useState(new Set());
|
|
18
21
|
const isControlled = onChange !== undefined;
|
|
@@ -26,6 +29,11 @@ export function CustomerPickerDialog({ priceGroupId, open, onOpenChange, value,
|
|
|
26
29
|
queryFn: () => getGroup({ data: { id: priceGroupId } }).then(decodeDeep),
|
|
27
30
|
enabled: open && !!priceGroupId && !isControlled,
|
|
28
31
|
});
|
|
32
|
+
const { data: unknown } = useQuery({
|
|
33
|
+
queryKey: ["unknown-floriday-members", priceGroupId],
|
|
34
|
+
queryFn: () => listUnknown({ data: { priceGroupId: priceGroupId } }).then(decodeDeep),
|
|
35
|
+
enabled: open && !!priceGroupId && !isControlled,
|
|
36
|
+
});
|
|
29
37
|
useEffect(() => {
|
|
30
38
|
if (!open)
|
|
31
39
|
return;
|
|
@@ -73,5 +81,5 @@ export function CustomerPickerDialog({ priceGroupId, open, onOpenChange, value,
|
|
|
73
81
|
return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { className: "sm:max-w-[720px]", children: [_jsx(DialogHeader, { children: _jsx(DialogTitle, { children: "Klanten toewijzen" }) }), _jsxs("div", { className: "relative", 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 naam, plaats of BTW-nummer\u2026", value: search, onChange: (e) => setSearch(e.target.value), className: "pl-9" })] }), _jsxs("div", { className: "flex items-center justify-between text-xs text-muted-foreground", children: [_jsxs("span", { children: [filtered.length, " klanten"] }), _jsxs("span", { children: [_jsx("strong", { className: "text-foreground", children: selected.size }), " geselecteerd"] })] }), _jsx("div", { className: "max-h-[380px] overflow-y-auto border rounded-md divide-y", children: isLoading ? (_jsx("div", { className: "p-6 text-center text-sm text-muted-foreground", children: "Laden\u2026" })) : filtered.length === 0 ? (_jsx("div", { className: "p-6 text-center text-sm text-muted-foreground", children: "Geen klanten gevonden." })) : filtered.map((c) => {
|
|
74
82
|
const checked = selected.has(c.group_key);
|
|
75
83
|
return (_jsxs("label", { className: "flex items-center gap-3 p-2.5 hover:bg-muted/50 cursor-pointer", children: [_jsx(Checkbox, { checked: checked, onCheckedChange: () => toggle(c.group_key) }), _jsxs("div", { className: "flex-1 min-w-0", children: [_jsx("div", { className: "text-sm font-medium truncate", children: c.display_name }), _jsx("div", { className: "text-xs text-muted-foreground truncate", children: c.city ? (_jsxs("span", { className: "inline-flex items-center gap-1", children: [_jsx("span", { children: c.city }), c.country && (_jsxs(_Fragment, { children: [_jsx("span", { children: "\u00B7" }), _jsx("span", { className: "text-base", "aria-hidden": "true", children: countryNameToFlag(c.country) ?? "🌐" }), _jsx("span", { children: c.country })] }))] })) : c.country ? (_jsxs("span", { className: "inline-flex items-center gap-1", children: [_jsx("span", { className: "text-base", "aria-hidden": "true", children: countryNameToFlag(c.country) ?? "🌐" }), _jsx("span", { children: c.country })] })) : "—" })] }), _jsx("div", { className: "flex gap-1", children: c.providers.slice(0, 3).map((p) => (_jsx(Badge, { variant: "secondary", className: "text-[10px]", children: p }, p))) })] }, c.group_key));
|
|
76
|
-
}) }), _jsxs(DialogFooter, { children: [_jsx(Button, { variant: "ghost", onClick: () => onOpenChange(false), children: "Annuleren" }), _jsx(Button, { onClick: handleSave, disabled: !isControlled && saveMut.isPending, children: !isControlled && saveMut.isPending ? "Opslaan…" : "Selectie opslaan" })] })] }) }));
|
|
84
|
+
}) }), unknown?.lastModifiedInFloriday && (_jsxs("p", { className: "text-xs text-muted-foreground", children: ["Laatst gewijzigd in Floriday: ", formatDateTime(unknown.lastModifiedInFloriday)] })), priceGroupId && unknown && unknown.members.length > 0 && (_jsx(UnknownFloridayMembers, { members: unknown.members, priceGroupIdFor: () => priceGroupId })), _jsxs(DialogFooter, { children: [_jsx(Button, { variant: "ghost", onClick: () => onOpenChange(false), children: "Annuleren" }), _jsx(Button, { onClick: handleSave, disabled: !isControlled && saveMut.isPending, children: !isControlled && saveMut.isPending ? "Opslaan…" : "Selectie opslaan" })] })] }) }));
|
|
77
85
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PriceGroupsPage.d.ts","sourceRoot":"","sources":["../../../../src/components/klanten/pricegroups/PriceGroupsPage.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"PriceGroupsPage.d.ts","sourceRoot":"","sources":["../../../../src/components/klanten/pricegroups/PriceGroupsPage.tsx"],"names":[],"mappings":"AA2HA,wBAAgB,eAAe,gCAsV9B"}
|
|
@@ -12,6 +12,7 @@ import { SortableContext, sortableKeyboardCoordinates, verticalListSortingStrate
|
|
|
12
12
|
import { CSS } from "@dnd-kit/utilities";
|
|
13
13
|
import { listCustomerPriceGroups, deleteCustomerPriceGroup, reorderCustomerPriceGroups, updateCustomerPriceGroup, createCustomerPriceGroup, importFloridayPriceGroups, } from "../../../lib/price-groups-local.functions";
|
|
14
14
|
import { CustomerPickerDialog } from "./CustomerPickerDialog";
|
|
15
|
+
import { UnknownFloridayMembers } from "./UnknownFloridayMembers";
|
|
15
16
|
import { friendlyError } from "../../../lib/errors";
|
|
16
17
|
import { toast } from "sonner";
|
|
17
18
|
function unitLabel(u) {
|
|
@@ -34,7 +35,7 @@ function calcSummary(g) {
|
|
|
34
35
|
function SortableRow({ g, onEdit, onMembers, onDelete, onDuplicate, onToggleActive }) {
|
|
35
36
|
const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({ id: g.id });
|
|
36
37
|
const style = { transform: CSS.Transform.toString(transform), transition, opacity: isDragging ? 0.6 : 1 };
|
|
37
|
-
return (_jsxs(TableRow, { ref: setNodeRef, style: style, className: isDragging ? "bg-muted/50" : undefined, children: [_jsx(TableCell, { className: "w-[36px] p-0 pl-2", children: _jsx("button", { type: "button", className: "cursor-grab touch-none text-muted-foreground hover:text-foreground p-2", "aria-label": "Verslepen", ...attributes, ...listeners, children: _jsx(GripVertical, { className: "h-4 w-4" }) }) }), _jsxs(TableCell, { children: [_jsxs("div", { className: "font-medium flex items-center gap-2", children: [g.name, g.source === "floriday" && _jsx(Badge, { variant: "outline", className: "text-[10px]", children: "Floriday" }), !g.is_active && _jsx(Badge, { variant: "secondary", className: "text-[10px]", children: "Inactief" })] }), _jsxs("div", { className: "text-xs text-muted-foreground", children: ["Laatst gewijzigd: ", formatDateTime(g.updated_at)] })] }), _jsx(TableCell, { children: _jsx(Badge, { variant: g.pricing_mode === "calculated" ? "default" : "secondary", children: g.pricing_mode === "calculated" ? "Berekende prijzen" : "Handmatige prijzen" }) }), _jsxs(TableCell, { className: "tabular-nums", children: [g.member_count, " klanten"] }), _jsxs(TableCell, { className: "text-sm", children: [g.price_scope === "incl_delivery" ? "Levering en transport" : "Exclusief levering", g.include_stickers && _jsx("div", { className: "text-xs text-muted-foreground", children: "Incl. stickers" })] }), _jsx(TableCell, { className: "text-sm", children: unitLabel(g.unit) }), _jsx(TableCell, { className: "text-sm", children: g.currency }), _jsx(TableCell, { className: "text-sm text-muted-foreground", children: calcSummary(g) }), _jsx(TableCell, { className: "w-[52px] text-right pr-2", children: _jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsx(Button, { variant: "ghost", size: "icon", "aria-label": "Acties", children: _jsx(MoreVertical, { className: "h-4 w-4" }) }) }), _jsxs(DropdownMenuContent, { align: "end", children: [_jsxs(DropdownMenuItem, { onClick: onEdit, children: [_jsx(Pencil, { className: "h-4 w-4 mr-2" }), "Bewerken"] }), _jsxs(DropdownMenuItem, { onClick: onMembers, children: [_jsx(Users, { className: "h-4 w-4 mr-2" }), "Klanten toewijzen"] }), _jsxs(DropdownMenuItem, { onClick: onDuplicate, children: [_jsx(Copy, { className: "h-4 w-4 mr-2" }), "Dupliceren"] }), _jsx(DropdownMenuItem, { onClick: onToggleActive, children: g.is_active
|
|
38
|
+
return (_jsxs(TableRow, { ref: setNodeRef, style: style, className: isDragging ? "bg-muted/50" : undefined, children: [_jsx(TableCell, { className: "w-[36px] p-0 pl-2", children: _jsx("button", { type: "button", className: "cursor-grab touch-none text-muted-foreground hover:text-foreground p-2", "aria-label": "Verslepen", ...attributes, ...listeners, children: _jsx(GripVertical, { className: "h-4 w-4" }) }) }), _jsxs(TableCell, { children: [_jsxs("div", { className: "font-medium flex items-center gap-2", children: [g.name, g.source === "floriday" && _jsx(Badge, { variant: "outline", className: "text-[10px]", children: "Floriday" }), g.source === "floriday" && g.external_synced_at && (_jsxs("span", { className: "text-[10px] text-muted-foreground", children: ["Ge\u00EFmporteerd op ", formatDateTime(g.external_synced_at)] })), !g.is_active && _jsx(Badge, { variant: "secondary", className: "text-[10px]", children: "Inactief" })] }), _jsxs("div", { className: "text-xs text-muted-foreground", children: ["Laatst gewijzigd: ", formatDateTime(g.updated_at)] })] }), _jsx(TableCell, { children: _jsx(Badge, { variant: g.pricing_mode === "calculated" ? "default" : "secondary", children: g.pricing_mode === "calculated" ? "Berekende prijzen" : "Handmatige prijzen" }) }), _jsxs(TableCell, { className: "tabular-nums", children: [g.member_count, " klanten"] }), _jsxs(TableCell, { className: "text-sm", children: [g.price_scope === "incl_delivery" ? "Levering en transport" : "Exclusief levering", g.include_stickers && _jsx("div", { className: "text-xs text-muted-foreground", children: "Incl. stickers" })] }), _jsx(TableCell, { className: "text-sm", children: unitLabel(g.unit) }), _jsx(TableCell, { className: "text-sm", children: g.currency }), _jsx(TableCell, { className: "text-sm text-muted-foreground", children: calcSummary(g) }), _jsx(TableCell, { className: "w-[52px] text-right pr-2", children: _jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsx(Button, { variant: "ghost", size: "icon", "aria-label": "Acties", children: _jsx(MoreVertical, { className: "h-4 w-4" }) }) }), _jsxs(DropdownMenuContent, { align: "end", children: [_jsxs(DropdownMenuItem, { onClick: onEdit, children: [_jsx(Pencil, { className: "h-4 w-4 mr-2" }), "Bewerken"] }), _jsxs(DropdownMenuItem, { onClick: onMembers, children: [_jsx(Users, { className: "h-4 w-4 mr-2" }), "Klanten toewijzen"] }), _jsxs(DropdownMenuItem, { onClick: onDuplicate, children: [_jsx(Copy, { className: "h-4 w-4 mr-2" }), "Dupliceren"] }), _jsx(DropdownMenuItem, { onClick: onToggleActive, children: g.is_active
|
|
38
39
|
? (_jsxs(_Fragment, { children: [_jsx(EyeOff, { className: "h-4 w-4 mr-2" }), "Op inactief zetten"] }))
|
|
39
40
|
: (_jsxs(_Fragment, { children: [_jsx(Eye, { className: "h-4 w-4 mr-2" }), "Activeren"] })) }), _jsx(DropdownMenuSeparator, {}), _jsxs(DropdownMenuItem, { onClick: onDelete, className: "text-destructive focus:text-destructive", children: [_jsx(Trash2, { className: "h-4 w-4 mr-2" }), "Verwijderen"] })] })] }) })] }));
|
|
40
41
|
}
|
|
@@ -69,13 +70,9 @@ export function PriceGroupsPage() {
|
|
|
69
70
|
mutationFn: () => importFn({ data: { preview: false } }),
|
|
70
71
|
onSuccess: (res) => {
|
|
71
72
|
const r = res;
|
|
72
|
-
|
|
73
|
-
setPreview(null);
|
|
73
|
+
setPreview(r);
|
|
74
74
|
qc.invalidateQueries({ queryKey: ["customer-price-groups"] });
|
|
75
75
|
toast.success(`Import klaar: ${r.created} nieuw, ${r.updated} bijgewerkt, ${r.deactivated} op inactief, ${r.membersLinked} klantkoppelingen.`);
|
|
76
|
-
if (r.unmatchedOrganizations.length > 0) {
|
|
77
|
-
toast.warning(`${r.unmatchedOrganizations.length} Floriday-klant(en) zijn hier nog niet bekend en overgeslagen.`);
|
|
78
|
-
}
|
|
79
76
|
},
|
|
80
77
|
onError: (e) => toast.error(friendlyError(e)),
|
|
81
78
|
});
|
|
@@ -158,5 +155,5 @@ export function PriceGroupsPage() {
|
|
|
158
155
|
setDeleteTarget(null); }, children: _jsxs(DialogContent, { children: [_jsx(DialogHeader, { children: _jsxs(DialogTitle, { children: ["Prijsgroep \"", deleteTarget?.name, "\" verwijderen?"] }) }), _jsxs(DialogFooter, { children: [_jsx(Button, { variant: "outline", onClick: () => setDeleteTarget(null), children: "Annuleren" }), _jsx(Button, { variant: "destructive", onClick: () => { if (deleteTarget)
|
|
159
156
|
deleteMut.mutate(deleteTarget.id); setDeleteTarget(null); }, children: "Verwijderen" })] })] }) }), membersFor && (_jsx(CustomerPickerDialog, { priceGroupId: membersFor, open: !!membersFor, onOpenChange: (o) => { if (!o)
|
|
160
157
|
setMembersFor(null); } })), _jsx(Dialog, { open: importOpen, onOpenChange: (o) => { setImportOpen(o); if (!o)
|
|
161
|
-
setPreview(null); }, children: _jsxs(DialogContent, { className: "max-w-lg", children: [_jsx(DialogHeader, { children: _jsx(DialogTitle, { children: "Prijsgroepen importeren uit Floriday" }) }), preview && (_jsxs("div", { className: "space-y-3 text-sm", children: [_jsxs("div", { className: "flex flex-wrap gap-2", children: [_jsxs(Badge, { variant: "default", children: [preview.created, " nieuw"] }), _jsxs(Badge, { variant: "secondary", children: [preview.updated, " bijwerken"] }), _jsxs(Badge, { variant: "outline", children: [preview.deactivated, " op inactief"] }), _jsxs(Badge, { variant: "outline", children: [preview.membersLinked, " klantkoppelingen"] })] }), preview.items.length === 0 ? (_jsx("p", { className: "text-muted-foreground", children: "Er zijn geen prijsgroepen uit Floriday om te importeren." })) : (_jsx("div", { className: "max-h-64 overflow-y-auto rounded-md border divide-y", children: preview.items.map((it) => (_jsxs("div", { className: "flex items-center justify-between gap-3 px-3 py-2", children: [_jsxs("div", { className: "min-w-0", children: [_jsx("div", { className: "font-medium truncate", children: it.name }), _jsxs("div", { className: "text-xs text-muted-foreground", children: [it.customers, " klant(en) in Floriday"] })] }), _jsx(Badge, { variant: it.action === "create" ? "default" : it.action === "update" ? "secondary" : "outline", children: it.action === "create" ? "Nieuw" : it.action === "update" ? "Bijwerken" : "Inactief" })] }, it.external_id))) })), preview.
|
|
158
|
+
setPreview(null); }, children: _jsxs(DialogContent, { className: "max-w-lg", children: [_jsx(DialogHeader, { children: _jsx(DialogTitle, { children: preview && !preview.preview ? "Uitkomst import uit Floriday" : "Prijsgroepen importeren uit Floriday" }) }), preview && (_jsxs("div", { className: "space-y-3 text-sm", children: [_jsxs("div", { className: "flex flex-wrap gap-2", children: [_jsxs(Badge, { variant: "default", children: [preview.created, " nieuw"] }), _jsxs(Badge, { variant: "secondary", children: [preview.updated, " bijwerken"] }), _jsxs(Badge, { variant: "outline", children: [preview.deactivated, " op inactief"] }), _jsxs(Badge, { variant: "outline", children: [preview.membersLinked, " klantkoppelingen"] })] }), preview.items.length === 0 ? (_jsx("p", { className: "text-muted-foreground", children: "Er zijn geen prijsgroepen uit Floriday om te importeren." })) : (_jsx("div", { className: "max-h-64 overflow-y-auto rounded-md border divide-y", children: preview.items.map((it) => (_jsxs("div", { className: "flex items-center justify-between gap-3 px-3 py-2", children: [_jsxs("div", { className: "min-w-0", children: [_jsx("div", { className: "font-medium truncate", children: it.name }), _jsxs("div", { className: "text-xs text-muted-foreground", children: [it.customers, " klant(en) in Floriday", it.last_modified_in_floriday && _jsxs(_Fragment, { children: [" \u00B7 laatst gewijzigd in Floriday ", formatDateTime(it.last_modified_in_floriday)] })] })] }), _jsx(Badge, { variant: it.action === "create" ? "default" : it.action === "update" ? "secondary" : "outline", children: it.action === "create" ? "Nieuw" : it.action === "update" ? "Bijwerken" : "Inactief" })] }, it.external_id))) })), preview.unmatchedDetails.length > 0 && (preview.preview ? (_jsxs("p", { className: "text-xs text-muted-foreground", children: [preview.unmatchedDetails.length, " Floriday-klant(en) zijn nog geen klant in het ERP. Na het importeren kun je ze per klant toevoegen."] })) : (_jsx(UnknownFloridayMembers, { members: preview.unmatchedDetails, priceGroupIdFor: (ext) => groups.find((g) => g.source === "floriday" && g.external_id === ext)?.id ?? null }))), _jsx("p", { className: "text-xs text-muted-foreground", children: "Kortingen, toeslagen en afrondingsregels blijven onaangeroerd; prijsgroepen die je zelf hebt gemaakt blijven ongewijzigd." })] })), _jsxs(DialogFooter, { children: [_jsx(Button, { variant: "outline", onClick: () => setImportOpen(false), children: preview && !preview.preview ? "Sluiten" : "Annuleren" }), (!preview || preview.preview) && (_jsx(Button, { onClick: () => importMut.mutate(), disabled: importMut.isPending || !preview || preview.items.length === 0, children: importMut.isPending ? "Bezig…" : "Importeren" }))] })] }) })] }));
|
|
162
159
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type UnknownFloridayMember } from "../../../lib/price-groups-local.functions";
|
|
2
|
+
/**
|
|
3
|
+
* Floriday-leden van een prijsgroep die nog geen ERP-klant zijn. Er wordt niets vastgelegd
|
|
4
|
+
* tot iemand op "Toevoegen" klikt; dan ontstaat een klant zonder Twinfield-debiteur.
|
|
5
|
+
*/
|
|
6
|
+
export declare function UnknownFloridayMembers({ members, priceGroupIdFor, }: {
|
|
7
|
+
members: UnknownFloridayMember[];
|
|
8
|
+
/** Geeft het ERP-prijsgroep-id voor een Floriday-prijsgroep, of null als die nog niet geïmporteerd is. */
|
|
9
|
+
priceGroupIdFor: (externalId: string) => string | null;
|
|
10
|
+
}): import("react").JSX.Element | null;
|
|
11
|
+
//# sourceMappingURL=UnknownFloridayMembers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"UnknownFloridayMembers.d.ts","sourceRoot":"","sources":["../../../../src/components/klanten/pricegroups/UnknownFloridayMembers.tsx"],"names":[],"mappings":"AAKA,OAAO,EAEL,KAAK,qBAAqB,EAC3B,MAAM,2CAA2C,CAAC;AAGnD;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,EACrC,OAAO,EACP,eAAe,GAChB,EAAE;IACD,OAAO,EAAE,qBAAqB,EAAE,CAAC;IACjC,0GAA0G;IAC1G,eAAe,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC;CACxD,sCAoEA"}
|