From c8aee1b58ea85c2807285c55132950b32f4b04de Mon Sep 17 00:00:00 2001 From: AI Assistant Date: Sun, 22 Mar 2026 04:04:17 +0200 Subject: [PATCH] fix(registratura): remove parentheses from institution-only contacts, add live contact sync MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Fix display format: institutions without a person name no longer show as "(Company)" — now shows just "Company" - Three-case formatting: name+company → "Name (Company)", only company → "Company", only name → "Name" - Registry table now resolves sender/recipient live from address book via contactMap — edits in address book reflect immediately in registry - New contacts created via quick-contact are added to contactMap on the fly Co-Authored-By: Claude Opus 4.6 (1M context) --- .../components/registratura-module.tsx | 24 ++++++++++++- .../components/registry-entry-form.tsx | 17 +++++++--- .../components/registry-table.tsx | 34 +++++++++++++++---- 3 files changed, 63 insertions(+), 12 deletions(-) diff --git a/src/modules/registratura/components/registratura-module.tsx b/src/modules/registratura/components/registratura-module.tsx index 49cb9ab..88ae19b 100644 --- a/src/modules/registratura/components/registratura-module.tsx +++ b/src/modules/registratura/components/registratura-module.tsx @@ -1,6 +1,6 @@ "use client"; -import { useState, useMemo, useCallback } from "react"; +import { useState, useEffect, useMemo, useCallback } from "react"; import { BookOpen, Plus } from "lucide-react"; import { NotificationPreferences } from "./notification-preferences"; import { @@ -76,6 +76,21 @@ export function RegistraturaModule() { const contactStorage = useStorage("address-book"); const tagStorageService = useStorageService(); + // ── Live contact map for table display (contactId → { name, company }) ── + const [contactMap, setContactMap] = useState>(new Map()); + useEffect(() => { + contactStorage.exportAll().then((all) => { + const map = new Map(); + for (const [key, value] of Object.entries(all)) { + if (key.startsWith("contact:") && value) { + const c = value as { id?: string; name?: string; company?: string }; + if (c.id) map.set(c.id, { name: c.name ?? "", company: c.company ?? "" }); + } + } + setContactMap(map); + }); + }, [contactStorage]); + const [viewMode, setViewMode] = useState("list"); const [editingEntry, setEditingEntry] = useState(null); const [viewingEntry, setViewingEntry] = useState(null); @@ -121,6 +136,12 @@ export function RegistraturaModule() { updatedAt: now, }; await contactStorage.set(`contact:${contact.id}`, contact); + // Update live contact map + setContactMap((prev) => { + const next = new Map(prev); + next.set(contact.id, { name: contact.name, company: contact.company }); + return next; + }); return contact; } catch { return undefined; @@ -539,6 +560,7 @@ export function RegistraturaModule() { onDelete={handleDelete} onClose={handleCloseViaReply} onReply={handleConex} + contactMap={contactMap} /> {!loading && ( diff --git a/src/modules/registratura/components/registry-entry-form.tsx b/src/modules/registratura/components/registry-entry-form.tsx index 1efc441..bb24214 100644 --- a/src/modules/registratura/components/registry-entry-form.tsx +++ b/src/modules/registratura/components/registry-entry-form.tsx @@ -86,6 +86,14 @@ import { } from "../services/deadline-service"; import { getDeadlineType } from "../services/deadline-catalog"; +/** Format a contact for display: "Name (Company)" / "Company" / "Name" */ +function formatContactLabel(c: { name: string; company: string }): string { + const name = c.name?.trim(); + const company = c.company?.trim(); + if (name && company) return `${name} (${company})`; + return name || company || ""; +} + interface RegistryEntryFormProps { initial?: RegistryEntry; /** Pre-fill as reply to this entry — sets threadParentId, flips direction (used by "Inchide") */ @@ -666,8 +674,7 @@ export function RegistryEntryForm({ const hasExactMatch = suggestions.some( (c) => - c.name.toLowerCase() === currentValue.toLowerCase() || - `${c.name} (${c.company})`.toLowerCase() === currentValue.toLowerCase(), + formatContactLabel(c).toLowerCase() === currentValue.toLowerCase(), ); const showCreateButton = @@ -1067,7 +1074,7 @@ export function RegistryEntryForm({ "sender", sender, (c) => { - setSender(c.company ? `${c.name} (${c.company})` : c.name); + setSender(formatContactLabel(c)); setSenderContactId(c.id); setSenderFocused(false); }, @@ -1108,7 +1115,7 @@ export function RegistryEntryForm({ "recipient", recipient, (c) => { - setRecipient(c.company ? `${c.name} (${c.company})` : c.name); + setRecipient(formatContactLabel(c)); setRecipientContactId(c.id); setRecipientFocused(false); }, @@ -1203,7 +1210,7 @@ export function RegistryEntryForm({ "assignee", assignee, (c) => { - setAssignee(c.company ? `${c.name} (${c.company})` : c.name); + setAssignee(formatContactLabel(c)); setAssigneeContactId(c.id); setAssigneeFocused(false); }, diff --git a/src/modules/registratura/components/registry-table.tsx b/src/modules/registratura/components/registry-table.tsx index 6d622cb..7f451f5 100644 --- a/src/modules/registratura/components/registry-table.tsx +++ b/src/modules/registratura/components/registry-table.tsx @@ -39,6 +39,19 @@ import { DEFAULT_DOC_TYPE_LABELS, EXTERNAL_STATUS_LABELS } from "../types"; import { getOverdueDays } from "../services/registry-service"; import { cn } from "@/shared/lib/utils"; +/** Format a contact for display: "Name (Company)" / "Company" / "Name" */ +function formatContactLabel(c: { name?: string; company?: string }): string { + const name = (c.name ?? "").trim(); + const company = (c.company ?? "").trim(); + if (name && company) return `${name} (${company})`; + return name || company || ""; +} + +interface ContactInfo { + name: string; + company: string; +} + interface RegistryTableProps { entries: RegistryEntry[]; loading: boolean; @@ -48,6 +61,8 @@ interface RegistryTableProps { onClose: (entry: RegistryEntry) => void; /** Create a new entry linked as reply (conex) to this entry */ onReply?: (entry: RegistryEntry) => void; + /** Live contact data map: contactId → { name, company } for live sync */ + contactMap?: Map; } // ── Column definitions ── @@ -185,6 +200,7 @@ export function RegistryTable({ onDelete, onClose, onReply, + contactMap, }: RegistryTableProps) { const [visibleCols, setVisibleCols] = useState>( () => DEFAULT_VISIBLE, @@ -416,16 +432,22 @@ export function RegistryTable({ )} {visibleCols.has("sender") && ( - {entry.sender || ( - - )} + {(() => { + const live = entry.senderContactId && contactMap?.get(entry.senderContactId); + return (live ? formatContactLabel(live) : entry.sender) || ( + + ); + })()} )} {visibleCols.has("recipient") && ( - {entry.recipient || ( - - )} + {(() => { + const live = entry.recipientContactId && contactMap?.get(entry.recipientContactId); + return (live ? formatContactLabel(live) : entry.recipient) || ( + + ); + })()} )} {visibleCols.has("assignee") && (