PR: #752: feat(channels): routed My connection / Firm register shell [Story-3065] by @ayush-pramaan
View PR →
Story-3065 · Epic-3059 (Channels / WhatsApp redesign)

Routed My connection / Firm register shell

The Channels surface gains its shared shell: one WhatsApp-branded header for both views, and a segmented switch between My connection and Firm register that is built as two routes, never client-side tabs.

Why two routes, not tabs

AGENTS.md "Route-per-mode" bans a single page.tsx holding useState for an active tab and conditionally rendering two views on one URL. Precedent is Bug-2150: the document viewer shipped Original / Text as client tabs and loaded pdfjs-dist for readers who never opened the PDF. Here the register view carries the firm-wide table + members join; a reader on My connection must not pay for it. So the segments are <Link>s to sibling routes, styled to look like SegmentedFilter, with aria-current="page" on the active one.

What changed

Fat layout, thin page

New settings/firm/channels/layout.tsx renders the shared SettingsPageHeader (WhatsApp glyph + title + lede) once for both routes. Each page.tsx keeps only its own toolbar row and content.

Shared tab strip

ChannelViewTabs (src/components/settings/channels/) is the routed switch. Both pages compose it instead of duplicating it.

Admin-gated row

The entire toolbar row renders only when isFirmAdmin(me). A non-admin sees header → content with no row — the design's lone one-tab pill is pointless and never renders.

Header primitive widened

SettingsPageHeader.icon now accepts a rendered node or any glyph component (Lucide or the filled WhatsApp mark), not just LucideIcon. InlinePageHelp still renders inside the h1. Every existing caller is unchanged.

Request dedupe

loadAppMe is wrapped in React cache(), so the layout and the page dedupe the /app/me resolution within one request.

Copy is Vani

New ChannelsShell keys added in en + hi. The persona is Vani — the pre-rename design mock's "Mira" copy was not used.

Admin — My connection (active)

Settings › Channels › WhatsApp
WhatsApp

Talk to Vani on WhatsApp, right where you already work.

↳ personal binding / connect card renders here (Story-3066+).

Non-admin — My connection (row absent)

Settings › Channels › WhatsApp
WhatsApp

Talk to Vani on WhatsApp, right where you already work.

↳ content renders directly under the lede — no toolbar row, no pill.

Files

File Role
settings/firm/channels/layout.tsx New fat layout — shared WhatsApp header for both routes.
settings/channels/channel-view-tabs.tsx New shared routed tab strip (<Link>, aria-current).
settings/firm/channels/page.tsx Thin — admin-gated toolbar row + ChannelsPage.
settings/firm/channels/register/page.tsx Thin — keeps the server-side admin gate; adds the toolbar row.
settings/channels/channels-page.tsx Header removed; showRegisterLink → isFirmAdmin.
settings/channels/channel-register-page.tsx Header removed; renders register content only.
settings/settings-page-header.tsx Icon prop widened to a node / any glyph component.
messages/{en,hi}.json New ChannelsShell namespace; superseded Channels.openRegister removed.

Verification

Files changed

14 file(s), +513 / -106 lines

Path Role + -
messages/en.jsonConfig+8-2
messages/hi.jsonConfig+8-2
src/app/[locale]/(workspace)/settings/firm/channels/layout.tsxApp route+34-0
src/app/[locale]/(workspace)/settings/firm/channels/page.tsxApp route+13-3
src/app/[locale]/(workspace)/settings/firm/channels/register/page.tsxApp route+17-8
src/components/settings/channels/channel-register-page.tsxComponent+5-9
src/components/settings/channels/channel-view-tabs.stories.tsxComponent+69-0
src/components/settings/channels/channel-view-tabs.test.tsxComponent+106-0
src/components/settings/channels/channel-view-tabs.tsxComponent+79-0
src/components/settings/channels/channels-page.tsxComponent+10-24
src/components/settings/settings-page-header.tsxComponent+40-13
src/components/shared/segmented-filter.stories.tsxComponent+30-1
src/components/shared/segmented-filter.tsxComponent+89-39
tests/unit/channels-page.test.tsxTest+5-5