Components

Contact Channels Field

A backend-agnostic, fully controlled editor for a record's contact channels, their consent cache and validation status.

Client Only

ContactChannelsField is a compact, read-at-a-glance summary of a record's contact channels — the primary email / phone / messaging / social entry per kind (with a +N badge for the rest) plus a per-brand consent grid. A Details link opens a dialog with the full ContactChannelsManager for adding, editing and removing channels and editing consent & validation. It mirrors the snake_case shape of the Contact Channels & Consent API, so the same ContactChannel[] round-trips with the Docyrus-backed Contact Channels Panel — but the field itself needs no backend: give it value + onChange and it works in any form.

Installation

pnpm dlx @docyrus/cli add @docyrus/ui-contact-channels-field

Usage

import { useState } from 'react';
import {
  ContactChannelsField,
  type ContactChannel
} from '@docyrus/ui/components/contact-channels-field';

function Example() {
  const [channels, setChannels] = useState<ContactChannel[]>([]);

  return <ContactChannelsField value={channels} onChange={setChannels} />;
}

Opening the Docyrus panel from Details

Pass onDetailsClick to take over the Details link — typically to open the backend-backed ContactChannelsPanel as a dialog instead of the built-in editor:

const [open, setOpen] = useState(false);

<ContactChannelsField value={channels} brands={brands} readOnly onDetailsClick={() => setOpen(true)} />
<ContactChannelsPanel open={open} onOpenChange={setOpen} client={client} appSlug="base" dataSourceSlug="contact" recordId={recordId} />

Comprehensive editor

ContactChannelsManager is the full backend-agnostic editor rendered inside the Details dialog — use it directly (inline) when you don't want the summary chrome:

import { ContactChannelsManager } from '@docyrus/ui/components/contact-channels-field';

<ContactChannelsManager value={channels} onChange={setChannels} brands={brands} />

Inside a TanStack form

Use the ContactChannelsFormField wrapper to bind the channel array to a form field. It edits inline with ContactChannelsManager and surfaces validation errors.

import { ContactChannelsFormField } from '@docyrus/ui/components/form-fields';

<ContactChannelsFormField
  field={{ id: 'channels', slug: 'channels', name: 'Contact channels', type: 'field-json' }}
  form={form}
  brands={brands}
/>

Each channel carries a channel_kind (email · phone · messaging · social · web) and a channel_type (email, mobile, whatsapp, linkedin, …). At most one channel per kind is is_primary. The consent cache is a three-level map keyed brand → medium → purpose; the field's inline consent editor only offers mediums that are valid for the channel type (e.g. a mobile channel exposes call and sms, an email channel exposes email), matching the medium ↔ channel-type rules the API enforces server-side. A new channel defaults to transactional consent opted_in at the organization-wide / medium-agnostic level.

API Reference

ContactChannelsField (summary)

PropTypeDefaultDescription
valueContactChannel[]—Controlled list of channels.
onChange(next: ContactChannel[]) => void—Emitted when channels are edited in the Details dialog.
brandsContactBrand[]—Brands for the consent summary (one row each); omit for a single organization-wide row.
titlestring'Contact Points'Summary heading.
hideDetailsbooleanfalseHide the Details link.
detailsLabelstring'Details'Label for the Details link.
onDetailsClick() => void—When set, Details calls this instead of opening the built-in dialog.
showConsentbooleantrueShow the consent summary section.
disabledbooleanfalseDisable editing in the built-in dialog.
readOnlybooleanfalseRender the built-in dialog read-only.
emptyTextstring'No contact points yet.'Placeholder when there are no channels.
classNamestring—Additional class on the root.

ContactChannelsManager (full editor)

PropTypeDefaultDescription
valueContactChannel[]—Controlled list of channels.
onChange(next: ContactChannel[]) => void—Emitted on any add / edit / remove / make-primary.
brandsContactBrand[]—Brands for labelling / scoping consent.
disabledbooleanfalseDisable every interaction.
readOnlybooleanfalseRender read-only (hides editors and action menus).
enableConsentbooleantrueShow the inline consent editor per channel.
enableValidationbooleantrueShow validation status chips.
groupByKindbooleantrueGroup channels under kind headers.
emptyTextstring'No contact channels yet.'Placeholder when there are no channels.

ContactChannelsFormField

Extends the standard Docyrus form-field props (field, form, disabled, required, className) with brands, enableConsent, enableValidation and groupByKind. Edits inline with ContactChannelsManager.

Exports

ExportDescription
ContactChannelsFieldThe compact summary + Details dialog.
ContactChannelsManagerThe full backend-agnostic editor (rendered in the Details dialog).
ChannelEditorBackend-agnostic single-channel attribute editor.
ConsentMatrixConsent-cache editor (medium × purpose) for one channel.
ConsentStatusBadge / ValidationStatusBadge / VerifiedBadgeStatus chips.
normalizeChannelValue / validateChannelValueValue helpers (email lower-casing, format checks).
flattenConsent / readConsent / summarizeBrandConsentConsent-cache readers.
CHANNEL_TYPES_BY_KIND / MEDIUMS_BY_CHANNEL_TYPE / allowedMediumsEnum + compatibility maps.

Type Exports

TypeDescription
ContactChannelA channel row (snake_case, mirrors the API response).
ContactBrandBrand lookup (id, name, logo_url, color_primary).
ConsentCachebrand → medium → purpose consent map.
ConsentRecord / ValidationRecordLedger rows (used by the panel).
ChannelKind / ChannelType / ConsentPurpose / ConsentMedium / ConsentStatus / ValidationStatusEnumerations.

On this page