Fix share name toggle revert and stale table data for umbral contacts
Bug 1: _to_settings_response() was missing share_first_name and share_last_name — the response always returned false (Pydantic default), causing the frontend to sync toggles back to off after save. Bug 2: Table column renderers read from stale Person record fields. Added sf() helper that overlays shared_fields for umbral contacts, applied to name, phone, email, role, and birthday columns. The table now shows live shared profile data matching the detail panel. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
33aac72639
commit
4513227338
@ -48,6 +48,8 @@ def _to_settings_response(s: Settings) -> SettingsResponse:
|
||||
# Social settings
|
||||
accept_connections=s.accept_connections,
|
||||
# Sharing defaults
|
||||
share_first_name=s.share_first_name,
|
||||
share_last_name=s.share_last_name,
|
||||
share_preferred_name=s.share_preferred_name,
|
||||
share_email=s.share_email,
|
||||
share_phone=s.share_phone,
|
||||
|
||||
@ -85,6 +85,14 @@ function sortPeople(people: Person[], key: string, dir: 'asc' | 'desc'): Person[
|
||||
// ---------------------------------------------------------------------------
|
||||
// Column definitions
|
||||
// ---------------------------------------------------------------------------
|
||||
/** Get a field value, preferring shared_fields for umbral contacts. */
|
||||
function sf(p: Person, key: string): string | null | undefined {
|
||||
if (p.is_umbral_contact && p.shared_fields && key in p.shared_fields) {
|
||||
return p.shared_fields[key] as string | null;
|
||||
}
|
||||
return p[key as keyof Person] as string | null | undefined;
|
||||
}
|
||||
|
||||
const columns: ColumnDef<Person>[] = [
|
||||
{
|
||||
key: 'name',
|
||||
@ -92,7 +100,10 @@ const columns: ColumnDef<Person>[] = [
|
||||
sortable: true,
|
||||
visibilityLevel: 'essential',
|
||||
render: (p) => {
|
||||
const initialsName = getPersonInitialsName(p);
|
||||
const firstName = sf(p, 'first_name') ?? p.first_name;
|
||||
const lastName = sf(p, 'last_name') ?? p.last_name;
|
||||
const liveName = [firstName, lastName].filter(Boolean).join(' ') || p.nickname || p.name;
|
||||
const initialsName = liveName || getPersonInitialsName(p);
|
||||
return (
|
||||
<div className="flex items-center gap-2.5">
|
||||
<div
|
||||
@ -100,7 +111,7 @@ const columns: ColumnDef<Person>[] = [
|
||||
>
|
||||
{getInitials(initialsName)}
|
||||
</div>
|
||||
<span className="font-medium truncate">{p.nickname || p.name}</span>
|
||||
<span className="font-medium truncate">{liveName}</span>
|
||||
{p.is_umbral_contact && (
|
||||
<Ghost className="h-3.5 w-3.5 text-violet-400 shrink-0" aria-label="Umbral contact" />
|
||||
)}
|
||||
@ -113,18 +124,21 @@ const columns: ColumnDef<Person>[] = [
|
||||
label: 'Number',
|
||||
sortable: false,
|
||||
visibilityLevel: 'essential',
|
||||
render: (p) => (
|
||||
<span className="text-muted-foreground truncate">{p.mobile || p.phone || '—'}</span>
|
||||
),
|
||||
render: (p) => {
|
||||
const mobile = sf(p, 'mobile') ?? p.mobile;
|
||||
const phone = sf(p, 'phone') ?? p.phone;
|
||||
return <span className="text-muted-foreground truncate">{mobile || phone || '—'}</span>;
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'email',
|
||||
label: 'Email',
|
||||
sortable: true,
|
||||
visibilityLevel: 'essential',
|
||||
render: (p) => (
|
||||
<span className="text-muted-foreground truncate">{p.email || '—'}</span>
|
||||
),
|
||||
render: (p) => {
|
||||
const email = sf(p, 'email') ?? p.email;
|
||||
return <span className="text-muted-foreground truncate">{email || '—'}</span>;
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'job_title',
|
||||
@ -132,10 +146,10 @@ const columns: ColumnDef<Person>[] = [
|
||||
sortable: true,
|
||||
visibilityLevel: 'filtered',
|
||||
render: (p) => {
|
||||
const parts = [p.job_title, p.company].filter(Boolean);
|
||||
return (
|
||||
<span className="text-muted-foreground truncate">{parts.join(', ') || '—'}</span>
|
||||
);
|
||||
const jobTitle = sf(p, 'job_title') ?? p.job_title;
|
||||
const company = sf(p, 'company') ?? p.company;
|
||||
const parts = [jobTitle, company].filter(Boolean);
|
||||
return <span className="text-muted-foreground truncate">{parts.join(', ') || '—'}</span>;
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -143,12 +157,14 @@ const columns: ColumnDef<Person>[] = [
|
||||
label: 'Birthday',
|
||||
sortable: true,
|
||||
visibilityLevel: 'filtered',
|
||||
render: (p) =>
|
||||
p.birthday ? (
|
||||
<span className="text-muted-foreground">{format(parseISO(p.birthday), 'MMM d')}</span>
|
||||
render: (p) => {
|
||||
const birthday = sf(p, 'birthday') ?? p.birthday;
|
||||
return birthday ? (
|
||||
<span className="text-muted-foreground">{format(parseISO(birthday), 'MMM d')}</span>
|
||||
) : (
|
||||
<span className="text-muted-foreground">—</span>
|
||||
),
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'category',
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user