Contact timeline

Every message, call, and voicemail with one customer, merged into a single newest-first feed. This is the component CRMs actually want.

Maria's full history — texts and a call (expand it for the transcript). The composer replies in place.

Installation

npx shadcn@latest add @handset/contact-timeline

Installs voicemail-player, call-transcript, and composer as dependencies.

Usage

import { ContactTimeline } from "@/components/handset/contact-timeline";

// In your CRM's contact panel:
<ContactTimeline
  externalNumber={contact.phone}   // E.164
  withComposer                     // reply without leaving the panel
/>

The timeline keys on the customer's phone number, so it works from any record that has one — a candidate, a tenant, a patient, a homeowner. Calls that ended in voicemail appear once, as the voicemail (with player and transcript), not as a duplicate call row. Completed calls expand into their transcript in place.

Under the hood this is useContactTimeline: conversations, calls, and voicemails fetched in parallel, filtered to the contact, merged and sorted. Use the hook directly if you want a custom feed rendering.

Props

Props
PropTypeDefaultDescription
externalNumberstring | nullThe customer's number, E.164.
withComposerbooleanfalseShow a composer wired to the contact's conversation.
tenantIdstringScope to one tenant (usually handled server-side).
pollMsnumber10000Refresh interval. 0 disables polling.
limitnumber50Max items fetched per source.
classNamestringMerged onto the wrapper.