feat(uat-batch): Group I — Residential parity (4 ships)
I34–I37 from the 2026-05-21 plan.
Shipped:
I34 Residential client header layout parity. Email / Call /
WhatsApp action buttons mirror the main ClientDetailHeader.
WhatsApp number resolves from phoneE164 (preferred) or strips
the free-text phone to digits. Header surfaces "Linked to
main client" chip when the auto-link matcher (I37) finds a
counterpart in the main CRM.
I35 Residential interests list rebuilt for parity with the main
InterestList. New ResidentialInterestCard +
getResidentialInterestColumns + residentialInterestFilter-
Definitions; the list page drives DataTable + FilterBar +
ColumnPicker + SavedViewsDropdown + bulkActions. List
endpoint validator widened to accept pipelineStage as a
string OR string[] and added a source filter. Service post-
fetches client names via a single IN-list lookup so the
table renders fullName in column 1 without N+1.
New /api/v1/residential/interests/bulk supports
change_stage + archive (100-id cap). Kanban view deferred.
I36 Residential inquiries auto-forward to partner email(s).
New registry entry residential_partner_recipients (comma-
separated) under section residential.partner.
createResidentialInterest fires
forwardResidentialInquiryToPartner after the row lands.
Helper uses the same branded shell other transactional
emails use. Failures log + never block create. The
/admin/residential-stages page picks up a registry-driven
card so admins manage recipients alongside stages.
I37 Auto-link residential ↔ main client. Migration 0080 adds
residential_clients.linked_client_id (nullable FK, SET NULL
on cascade) + partial index. New findAndLinkMatchingMainClient
service matches by email first (case-insensitive client_contacts
lookup) then by E.164 phone. First exact match wins. Fires
fire-and-forget from createResidentialClient. Header surfaces
the link via a "Linked to main client" chip. Backfill script
+ reverse-direction link from main ClientDetailHeader stay
as follow-ups.
Verified: tsc clean, vitest 1454/1454, migration applied.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -599,6 +599,18 @@ export const REGISTRY: SettingEntry[] = [
|
||||
scope: 'port',
|
||||
placeholder: 'Cold',
|
||||
},
|
||||
|
||||
// ─── Residential — partner forwarding ──────────────────────────────────────
|
||||
{
|
||||
key: 'residential_partner_recipients',
|
||||
section: 'residential.partner',
|
||||
label: 'Partner forwarding recipients',
|
||||
description:
|
||||
'Comma-separated list of email addresses that receive a copy of every new residential inquiry the moment it lands. Leave blank to disable partner forwarding. Reps still see every inquiry in the CRM; this is an outbound courtesy notification for an external partner who handles residential leads on the port’s behalf.',
|
||||
type: 'string',
|
||||
scope: 'port',
|
||||
placeholder: 'partner@example.com, partner2@example.com',
|
||||
},
|
||||
];
|
||||
|
||||
/** Quick lookup index keyed by setting key. */
|
||||
|
||||
Reference in New Issue
Block a user