feat(interests): Email / Call / WhatsApp deep-links on interest header

The interest detail is the rep's workbench — but until now, calling or
emailing the lead meant navigating away to the client page first. Surface
the same Email / Call / WhatsApp affordances that already live on the
client header right where the work is happening.

  - getInterestById: extended to also resolve the linked client's primary
    phone (display value + canonical E.164 form for wa.me).
    `clientPrimaryEmail` is the same column we surfaced earlier for the
    EOI prereq checklist; this commit just adds the phone columns
    alongside it.

  - InterestDetailHeader: new contact-actions row tucked under the meta
    line. Each button is asChild over a real <a href> so middle-click,
    Cmd-click, and screen-readers behave correctly. Renders only the
    buttons whose underlying contact channel is present (Email-only when
    no phone is on file, etc.). The whole row is hidden when the client
    has no contacts at all.

  - WhatsApp number prefers the E.164 form; falls back to digits-stripped
    display value when the canonical form is missing.

tsc clean. vitest 835/835 pass. ESLint clean on every file touched.

Closes audit recommendation #1 (top-of-list — biggest sales-workflow
win per click saved).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Matt Ciaccio
2026-05-02 03:33:13 +02:00
parent d197f8b321
commit c824b2df12
3 changed files with 119 additions and 4 deletions

View File

@@ -16,6 +16,18 @@ interface InterestData {
portId: string;
clientId: string;
clientName: string | null;
/** Linked client's primary email (display value). Powers the header
* "Email" button and the EOI prereq checklist. */
clientPrimaryEmail: string | null;
/** Linked client's primary phone (display value). Powers the header
* "Call" button. */
clientPrimaryPhone: string | null;
/** Linked client's primary phone in E.164 form ("+1XXXXXXXXXX"). Used
* by wa.me to assemble the WhatsApp deep-link. */
clientPrimaryPhoneE164: string | null;
/** True when the linked client has any primary address row. Used by
* the EOI prereq checklist on the Documents tab. */
clientHasAddress: boolean;
berthId: string | null;
berthMooringNumber: string | null;
pipelineStage: string;
@@ -39,6 +51,8 @@ interface InterestData {
archivedAt: string | null;
createdAt: string;
updatedAt: string;
outcome?: string | null;
outcomeReason?: string | null;
tags: Array<{ id: string; name: string; color: string }>;
}