# rn-contact-channels-panel URL: /docs/native/docyrus/contact-channels-panel Docyrus-backed contact points and consent for a record — preferred channels, per-kind channel cards, brand consent and a consent / validation history, with every endpoint wired through useDocyrusContactChannels. ## Installation ```bash pnpm dlx @docyrus/cli add @docyrus/rn-contact-channels-panel ``` **Dependencies:** - [@tanstack/react-query](https://www.npmjs.com/package/@tanstack/react-query) - [@docyrus/api-client](https://www.npmjs.com/package/@docyrus/api-client) Give the panel an authenticated `client` and the owning `(appSlug, dataSourceSlug, recordId)`. It loads the channels, the record-wide consent and validation ledgers and the tenant brands through [`useDocyrusContactChannels`](/docs/native/hooks/use-docyrus-contact-channels), and shows: - a **Summary** of the preferred email and phone, - channel sections (**Emails** and **Phones** always, other kinds when populated) with a card per channel (status, validated by, last validated, source, notes). The ⋯ button or a long-press opens the actions: Edit, Make primary, Record consent, Validate now, Archive / Restore and Delete permanently (with a confirm dialog), - **Brand consent** cards (organization-wide, each tenant brand, and any other brand in a consent cache), - a collapsible **History** of consent and validation events. The editors (channel, consent, validation) are bottom sheets. Pull down to refresh. A **Show archived** switch adds archived channels, shown muted. Renders inline by default (`flex-1`, or bounded with `maxHeight`). Pass `onOpenChange` to show it in an `AwesomeDialog` (a bottom sheet by default). Read endpoints need `DS.Read.All`, write endpoints `DS.ReadWrite.All`. ## Usage ```tsx import { ContactChannelsPanel } from '@/components/docyrus-native/contact-channels-panel'; ``` Pair it with [rn-contact-channels-field](/docs/native/docyrus/contact-channels-field) by opening the panel from the field's `onDetailsClick`. ## API Reference | Prop | Type | Default | Description | |------|------|---------|-------------| | `client` | `ContactChannelsClient` | - | Authenticated REST client (RestApiClient fits) (required) | | `appSlug` | `string` | - | App slug of the owning record (required) | | `dataSourceSlug` | `string` | - | Data source slug of the owning record (required) | | `recordId` | `string` | - | Owning record id (required) | | `title` | `string` | `'Contact Points & Consent'` | Heading (dialog mode) | | `subtitle` | `string` | - | Sub-heading (dialog mode) | | `includeArchived` | `boolean` | `false` | Start with archived channels shown | | `enabled` | `boolean` | - | Defer all queries | | `loadBrands` | `boolean` | `true` | Fetch /v1/tenant/brands | | `readOnly` | `boolean` | - | Hide every write affordance | | `maxHeight` | `number` | - | Max inline height in px (unbounded flex-1 when omitted) | | `onChange` | `(channels: ContactChannel[]) => void` | - | Fired with the refreshed channel list | | `className` | `string` | - | Container classes | | `open` | `boolean` | - | Dialog mode open state | | `onOpenChange` | `(open: boolean) => void` | - | Enables dialog mode | | `container` | `'modal' \| 'sheet' \| 'drawer'` | `'sheet'` | Dialog container | | `side` | `'left' \| 'right' \| 'top' \| 'bottom'` | `'bottom'` | Sheet side | | `size` | `'sm' \| 'default' \| 'lg' \| 'xl' \| 'full'` | `'xl'` | Dialog size | ## Components | Component | Description | |-----------|-------------| | `ContactChannelsPanel` | The panel | | `ChannelCard` | Channel read card with the actions sheet | | `ChannelFormDialog` | Add / edit channel sheet (`draftToInput` maps a draft to `ChannelCreateInput`) | | `ConsentDialog` | Record-consent sheet | | `ValidationDialog` | Record-validation sheet | | `BrandConsentCard` | Per-brand consent card | | `ConsentHistory` | Merged history | | `PanelSummary` | Preferred email / phone | | `BrandMark`, `ChannelStatusIcon`, `ProviderBadge`, `StatusText`, `DialogField` | Display helpers | `lib/panel-utils` (synced with web): `CHANNEL_SECTIONS`, `channelsOfKind`, `primaryOfKind`, `channelValidationDisplay`, `channelMetaString`, `brandMediumStatus`, `brandKeysInUse`, `resolveBrand`, `latestConsentForBrand`, `evidenceFromProof`, `latestValidationFor`, `fmtDay`, `fmtDateTime`, `ORG_BRAND_KEY`. ## Type Exports | Type | Description | |------|-------------| | `ContactChannelsPanelProps` | Props | | `ChannelCardProps`, `ChannelCardActions`, `ChannelFormDialogProps`, `ConsentDialogProps`, `ValidationDialogProps`, `BrandConsentCardProps`, `ConsentHistoryProps` | Sub-component props | | `ChannelSectionMeta`, `ConsentDetail` | Panel utils types | | `ContactChannelsClient`, `UseDocyrusContactChannelsOptions`, `UseDocyrusContactChannelsResult`, `ContactChannelsPanelData`, `ConsentEntryWithChannel` | Re-exported from the hook | ## Differences from web - `maxHeight` is a number (px). - `side` defaults to `'bottom'` (web: `'right'`). - The refresh button is replaced by pull-to-refresh.