Skip to content

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=1 joins system.userUid β†’ {role,status} via listUsers(); 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.ts hooked in pages/api/auth/[...nextauth].ts right after acceptInvitation() (both invitation branches; first invited sign-in). Idempotent (getIndividualByUserUid guard) + 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 a safeToRetire verdict. 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 organizations data. 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 legacy computeOverdueCompanySet (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.

T-061 (RP sync fix), T-063 (Transaction Details window).