Aligned board/member info

X-Lovable-Edit-ID: edt-54c2f070-7fce-48d4-a053-2ecd5da65ab9
Co-authored-by: renee-png <262607627+renee-png@users.noreply.github.com>
This commit is contained in:
gpt-engineer-app[bot]
2026-04-23 21:55:55 +00:00
co-authored by renee-png
2 changed files with 57 additions and 5 deletions
@@ -134,6 +134,50 @@ export function ClientFormDialog({
toast.error("Could not save client", { description: error.message });
return;
}
// Auto-create contacts for board members that aren't already in contacts
try {
const members = (values.board_members || []).filter((m) => m.name?.trim());
if (members.length > 0) {
const emails = members.map((m) => m.email?.trim().toLowerCase()).filter(Boolean) as string[];
const names = members.map((m) => m.name.trim());
const { data: existing } = await supabase
.from("contacts")
.select("name,email")
.or(
[
emails.length ? `email.in.(${emails.join(",")})` : "",
`name.in.(${names.map((n) => `"${n.replace(/"/g, '\\"')}"`).join(",")})`,
]
.filter(Boolean)
.join(","),
);
const existingEmails = new Set((existing ?? []).map((c) => c.email?.toLowerCase()).filter(Boolean));
const existingNames = new Set((existing ?? []).map((c) => c.name?.toLowerCase()));
const toInsert = members
.filter((m) => {
const e = m.email?.trim().toLowerCase();
if (e && existingEmails.has(e)) return false;
if (existingNames.has(m.name.trim().toLowerCase())) return false;
return true;
})
.map((m) => ({
name: m.name.trim(),
title: m.role?.trim() || null,
email: m.email?.trim() || null,
phone: m.phone?.trim() || null,
contact_type: "board_member",
created_by: user?.id ?? null,
}));
if (toInsert.length > 0) {
await supabase.from("contacts").insert(toInsert);
}
}
} catch (e) {
// Non-fatal
console.warn("Board member contact sync failed", e);
}
toast.success(client ? "Client updated" : "Client created");
onOpenChange(false);
onSaved?.();
+13 -5
View File
@@ -227,11 +227,19 @@ function ClientDetail() {
</div>
<div className="divide-y">
{client.board_members.map((m: any, i: number) => (
<div key={i} className="py-2.5 flex flex-wrap items-baseline gap-x-4 gap-y-1 text-sm">
<span className="font-medium">{m.name}</span>
{m.role && <span className="text-xs uppercase tracking-wider text-muted-foreground">{m.role}</span>}
{m.email && <span className="text-muted-foreground">{m.email}</span>}
{m.phone && <span className="text-muted-foreground">{m.phone}</span>}
<div key={i} className="py-2.5 flex flex-wrap items-baseline justify-between gap-x-4 gap-y-1 text-sm">
<div className="flex items-baseline gap-x-3 min-w-0">
<span className="font-medium">{m.name}</span>
{m.role && <span className="text-xs uppercase tracking-wider text-muted-foreground">{m.role}</span>}
</div>
<div className="flex items-baseline gap-x-4 text-muted-foreground">
{m.email && (
<a href={`mailto:${m.email}`} className="hover:text-foreground hover:underline">
{m.email}
</a>
)}
{m.phone && <span>{m.phone}</span>}
</div>
</div>
))}
</div>