@flowselections/floriday-klanten-module 1.0.45 → 1.0.48

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/dist-lib/components/klanten/BewerkKlant.d.ts.map +1 -1
  2. package/dist-lib/components/klanten/BewerkKlant.js +1 -1
  3. package/dist-lib/components/klanten/CustomerAnalyticsTab.d.ts.map +1 -1
  4. package/dist-lib/components/klanten/CustomerAnalyticsTab.js +2 -2
  5. package/dist-lib/components/klanten/FloridayNetworkStatus.d.ts +5 -0
  6. package/dist-lib/components/klanten/FloridayNetworkStatus.d.ts.map +1 -0
  7. package/dist-lib/components/klanten/FloridayNetworkStatus.js +18 -0
  8. package/dist-lib/components/klanten/KlantDetailLayout.d.ts +2 -0
  9. package/dist-lib/components/klanten/KlantDetailLayout.d.ts.map +1 -1
  10. package/dist-lib/components/klanten/KlantDetailLayout.js +6 -1
  11. package/dist-lib/components/klanten/KlantFormulier.d.ts +3 -1
  12. package/dist-lib/components/klanten/KlantFormulier.d.ts.map +1 -1
  13. package/dist-lib/components/klanten/KlantFormulier.js +16 -4
  14. package/dist-lib/components/klanten/KlantenOverzicht.d.ts.map +1 -1
  15. package/dist-lib/components/klanten/KlantenOverzicht.js +6 -5
  16. package/dist-lib/components/klanten/NetwerkPagina.d.ts.map +1 -1
  17. package/dist-lib/components/klanten/NetwerkPagina.js +9 -5
  18. package/dist-lib/components/klanten/NieuweKlant.d.ts.map +1 -1
  19. package/dist-lib/components/klanten/NieuweKlant.js +10 -7
  20. package/dist-lib/components/klanten/pricegroups/CustomerPickerDialog.d.ts.map +1 -1
  21. package/dist-lib/components/klanten/pricegroups/CustomerPickerDialog.js +10 -2
  22. package/dist-lib/components/klanten/pricegroups/PriceGroupsPage.d.ts.map +1 -1
  23. package/dist-lib/components/klanten/pricegroups/PriceGroupsPage.js +4 -7
  24. package/dist-lib/components/klanten/pricegroups/UnknownFloridayMembers.d.ts +11 -0
  25. package/dist-lib/components/klanten/pricegroups/UnknownFloridayMembers.d.ts.map +1 -0
  26. package/dist-lib/components/klanten/pricegroups/UnknownFloridayMembers.js +43 -0
  27. package/dist-lib/index.js +1 -1
  28. package/dist-lib/integrations/supabase/types.d.ts +1470 -2
  29. package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
  30. package/dist-lib/integrations/supabase/types.js +1 -0
  31. package/dist-lib/lib/customer-schemas.d.ts +2 -0
  32. package/dist-lib/lib/customer-schemas.d.ts.map +1 -1
  33. package/dist-lib/lib/customer-schemas.js +2 -0
  34. package/dist-lib/lib/customers.functions.d.ts +37 -0
  35. package/dist-lib/lib/customers.functions.d.ts.map +1 -1
  36. package/dist-lib/lib/customers.functions.js +49 -4
  37. package/dist-lib/lib/floriday-network-status.functions.d.ts +18 -0
  38. package/dist-lib/lib/floriday-network-status.functions.d.ts.map +1 -0
  39. package/dist-lib/lib/floriday-network-status.functions.js +41 -0
  40. package/dist-lib/lib/price-groups-local.functions.d.ts +42 -0
  41. package/dist-lib/lib/price-groups-local.functions.d.ts.map +1 -1
  42. package/dist-lib/lib/price-groups-local.functions.js +120 -1
  43. package/dist-lib/lib/twinfield-adopt.functions.d.ts +2 -0
  44. package/dist-lib/lib/twinfield-adopt.functions.d.ts.map +1 -1
  45. package/dist-lib/lib/vat-format.d.ts +17 -0
  46. package/dist-lib/lib/vat-format.d.ts.map +1 -0
  47. package/dist-lib/lib/vat-format.js +64 -0
  48. package/package.json +5 -5
@@ -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":"AAuHA,wBAAgB,eAAe,gCA8U9B"}
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
- setImportOpen(false);
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.unmatchedOrganizations.length > 0 && (_jsxs("p", { className: "text-xs text-muted-foreground", children: [preview.unmatchedOrganizations.length, " Floriday-klant(en) zijn hier nog niet bekend en worden overgeslagen."] })), _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: "Annuleren" }), _jsx(Button, { onClick: () => importMut.mutate(), disabled: importMut.isPending || !preview || preview.items.length === 0, children: importMut.isPending ? "Bezig…" : "Importeren" })] })] }) })] }));
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"}
@@ -0,0 +1,43 @@
1
+ import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
2
+ import { useState } from "react";
3
+ import { useMutation, useQueryClient } from "@tanstack/react-query";
4
+ import { useServerFn } from "@tanstack/react-start";
5
+ import { Button, Badge } from "@flowselections/core";
6
+ import { toast } from "sonner";
7
+ import { addFloridayMemberToPriceGroup, } from "../../../lib/price-groups-local.functions";
8
+ import { friendlyError } from "../../../lib/errors";
9
+ /**
10
+ * Floriday-leden van een prijsgroep die nog geen ERP-klant zijn. Er wordt niets vastgelegd
11
+ * tot iemand op "Toevoegen" klikt; dan ontstaat een klant zonder Twinfield-debiteur.
12
+ */
13
+ export function UnknownFloridayMembers({ members, priceGroupIdFor, }) {
14
+ const qc = useQueryClient();
15
+ const addFn = useServerFn(addFloridayMemberToPriceGroup);
16
+ const [done, setDone] = useState(new Set());
17
+ const addMut = useMutation({
18
+ mutationFn: async (m) => {
19
+ for (const g of m.groups) {
20
+ const id = priceGroupIdFor(g.external_id);
21
+ if (id)
22
+ await addFn({ data: { organizationId: m.organization_id, priceGroupId: id } });
23
+ }
24
+ return m;
25
+ },
26
+ onSuccess: (m) => {
27
+ setDone((prev) => new Set(prev).add(m.organization_id));
28
+ qc.invalidateQueries({ queryKey: ["customer-price-groups"] });
29
+ qc.invalidateQueries({ queryKey: ["unknown-floriday-members"] });
30
+ qc.invalidateQueries({ queryKey: ["customer-price-group"] });
31
+ qc.invalidateQueries({ queryKey: ["external-customers-picker"] });
32
+ toast.success(`${m.commercial_name ?? m.name} is toegevoegd als klant (zonder debiteur in Twinfield).`);
33
+ },
34
+ onError: (e) => toast.error(friendlyError(e)),
35
+ });
36
+ if (members.length === 0)
37
+ return null;
38
+ return (_jsxs("div", { className: "space-y-1.5", children: [_jsxs("p", { className: "text-xs font-medium", children: ["Onbekend in ERP (", members.length, ")"] }), _jsx("div", { className: "max-h-56 overflow-y-auto rounded-md border divide-y", children: members.map((m) => {
39
+ const added = done.has(m.organization_id);
40
+ const canAdd = m.groups.some((g) => priceGroupIdFor(g.external_id));
41
+ return (_jsxs("div", { className: "flex flex-wrap items-center justify-between gap-2 px-3 py-2", children: [_jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("div", { className: "text-sm font-medium truncate", children: m.commercial_name ?? m.name }), _jsxs("div", { className: "text-xs text-muted-foreground truncate", children: [m.commercial_name ? `${m.name} · ` : "", m.city ?? "—", " \u00B7 ", m.reason] }), m.groups.length > 0 && (_jsxs("div", { className: "text-xs text-muted-foreground truncate", children: ["In Floriday: ", m.groups.map((g) => g.name).join(", ")] }))] }), added ? (_jsx(Badge, { variant: "secondary", children: "Toegevoegd" })) : (_jsx(Button, { size: "sm", variant: "outline", disabled: !canAdd || addMut.isPending, title: canAdd ? undefined : "Importeer eerst de prijsgroep", onClick: () => addMut.mutate(m), children: "Toevoegen" }))] }, m.organization_id));
42
+ }) }), _jsx("p", { className: "text-xs text-muted-foreground", children: "Toevoegen maakt een klant aan op organisatie-id, zonder debiteur in Twinfield, en deelt die in bij de prijsgroep." })] }));
43
+ }
package/dist-lib/index.js CHANGED
@@ -52,7 +52,7 @@ export { emitLogisticsEvent } from "./lib/logistics-events.functions";
52
52
  export const floridayKlantenModule = {
53
53
  id: "customers",
54
54
  name: "Klanten",
55
- version: "1.0.0",
55
+ version: "1.0.47",
56
56
  nav: {
57
57
  label: "Klanten",
58
58
  href: "/klanten",