Retire Contacts legacy tabs (parity-first) + new-tab UI convergence
Why¶
The T-048 contacts unification added new Individuals + Client Company tabs ALONGSIDE the legacy Clients / People / Staff tabs. Owner wants the legacy tabs gone β but the T-048 P7 audit flagged teardown as a REGRESSION (the new views lacked parity: the Individuals view held only the migrated contacts, no role/status; Staff showed ALL RBAC users; People = the full payee directory). Owner chose the parity-first path: close the gaps, make the new tabs look/function like the legacy ones, then remove legacy.
Done¶
- P1 (origin/nightly fe22b934) β role/status view in Individuals. API
/api/aote-system/individuals?withUsers=1joinssystem.userUidβ {role,status} vialistUsers(); Role(+filter)/Status columns + All/Staff(n) toggle. Closes the Staff parity gap (the audit's core blocker). - P2 (f3e5dd44) β provision an Individual at invitation onboarding. New
lib/individuals/ensureForUser.server.tshooked inpages/api/auth/[...nextauth].tsright afteracceptInvitation()(both invitation branches; first invited sign-in). Idempotent (getIndividualByUserUidguard) + best-effort (never blocks sign-in). Owner chose to integrate into onboarding rather than a separate staff guard. - P3 (aca4a5ab) β read-only admin coverage check. GET
/api/aote-system/contacts-coverage(payees with no bridged Individual via canonical abbreviation; clients with no Organization via name) + a "Directory coverage" modal in Contacts with asafeToRetireverdict. Sizes the People/Clients gap that can't be known statically. - Item 2 (3e54daa9) β Individuals tab restyled to model the People tab: seed-coloured avatar + name + inline abbr tag, two-line email, elevated card, large search, 12/24/48/96 paging. ProfileDrawer + the P1 Role/Status/All-Staff additions kept.
Done (cont.)¶
- Item 1 (origin/nightly 3e3d9b20) β Client Company tab rebuilt to model the legacy
Clients tab, keeping the
organizationsdata. Legacy-style table (company avatar, Payment status, Representative names, Email+phone, Subsidiaries; elevated card; 12/24/48/96) + add/edit modal (companyName / brNumber / Representatives / email / phone / address / subsidiaries). Representative = multi-select picker over Individuals β representativeIds[] (updateOrganizationServer reconciles the two-way repOfCompanyIds link). Payment status = reuses the legacycomputeOverdueCompanySet(now exported: outstanding project invoices β normalized company keys), matched to orgs by name, background-loaded. - Item 3 (origin/nightly a5300e1d) β removed the Clients/People/Staff tabs from
ContactsApp.tsx(now Individuals + Client Company only; the admin Directory-coverage check stays); cut the subsidiary selector, renderClientsContent, the 4 dynamic imports + unused state/imports. Repointed user-facing "People" tab references β "Contacts" (1120/2200 director-payee errors in transactions [id]/index + BankTransactionsTab, RP empty state, template-editor placeholder). Legacy component files + client/payee data providers kept (still wired into contactsDataProvider) β reversible.
T-062 COMPLETE. Item 4 (Transaction Details external window) is the separate T-063.
Verify¶
Each shipped phase tsc + lint clean; full suite 325/325. Everything is auth-gated β eyeball on nightly. Owner to verify P2 with a test invite.