Skip to main content
A estrutura do app do Switchbord é construída em torno de uma barra lateral recolhível, um breadcrumb auto-derivado na barra superior e um pequeno conjunto de primitivos de página composáveis (PageHeader, FilterTabs, Tabs, Card, seções simples). Esta página documenta o que está onde e — para colaboradores — quando usar cada primitivo, para que novas telas tenham a mesma aparência e comportamento do restante do produto.

Estrutura da barra lateral

A barra lateral esquerda usa grupos recolhíveis. Cada grupo é uma rota pai; seus filhos são uma lista simples de destinos de primeira classe abaixo dela.

Inbox

Simples — sem subgrupo. Leva direto ao inbox do operador.

Contacts

Simples. Filtros e segmentos vivem dentro da página, não na barra lateral.

Agents

Grupo recolhível. Filhos: Overview, Templates e rotas de detalhe por agente.

Campaigns

Grupo recolhível. Filhos: All campaigns, Audiences, Schedules.

Journeys

Grupo recolhível. Filhos: All journeys, Templates, Runs.

Settings

Simples; abas internas cuidam das várias sub-páginas.
Os grupos persistem seu estado aberto/fechado por usuário em localStorage, para que a estrutura não se recolha novamente a cada navegação.
Se você está adicionando um novo destino de nível superior, prefira primeiro uma entrada simples. Só promova para um grupo recolhível quando houver três ou mais rotas irmãs que se beneficiem de ficar visíveis ao mesmo tempo.

Sub-rotas sob um registro pai

Páginas de detalhe para Agents, Campaigns e Journeys usam um formato de URL consistente:
Essas sub-rotas são renderizadas como FilterTabs no topo da página do registro, imediatamente abaixo do PageHeader. Cada aba é uma rota real — pode ser acessada por link direto, compartilhada e é preservada entre recarregamentos. As trocas de aba usam navegação shallow do Next.js, então a posição de scroll e o estado transitório não se perdem.
As abas de sub-rota não são o mesmo primitivo que os Tabs de página. Veja Abas de filtro vs. abas de conteúdo abaixo.
A barra superior renderiza uma trilha de breadcrumb que é, por padrão, auto-derivada da rota atual. O sistema percorre os segmentos do pathname, consulta cada um em um pequeno registro (/apps/app/lib/breadcrumbs.ts) para obter um rótulo legível e renderiza migalhas clicáveis para cada segmento, exceto o último.

Modo automático (o padrão)

Toda página recebe breadcrumbs auto-derivados sem fazer nada:
Para /agents/abc123/runs, isso renderiza: Agents › Acme support bot › Runs. O rótulo do meio é resolvido pelo loader [id] do registro (ele busca o nome de exibição do agente no cache). Use o modo automático quando:
  • Os segmentos de rota mapeiam de forma clara para rótulos legíveis (o caso comum).
  • Você já está dentro de uma hierarquia de registro pai/filho já cadastrada no registro.

Modo explícito

Passe breadcrumbs como um array quando a trilha auto-derivada estaria errada ou sem contexto suficiente:
Use o modo explícito quando:
  • A URL não reflete o local mental do usuário (por exemplo, rotas modais, wizards).
  • Você precisa exibir um rótulo que ainda não está no registro e não quer adicioná-lo.
  • Você está renderizando uma página pontual (migrações, ferramentas administrativas) em que manter o registro atualizado não vale o esforço.
Não misture os dois modos. Se você passar breadcrumbs, o resolvedor automático é ignorado completamente — overrides parciais não são suportados de propósito, para manter a trilha previsível.

Abas de filtro vs. abas de conteúdo

O Switchbord fornece dois primitivos de abas que parecem semelhantes, mas cumprem papéis diferentes.

FilterTabs — controle segmentado

Uma linha compacta de botões segmentados. Escolhe qual recorte da mesma lista você está visualizando. Não tem painéis de conteúdo próprios — apenas controla o estado de query/filtro abaixo. Use FilterTabs quando:
  • Estiver alternando entre visualizações do mesmo conjunto de dados subjacente (por exemplo, All / Draft / Pending / Approved em templates).
  • Você quiser um visual de controle segmentado que não sugira “páginas” separadas.
  • For navegação de sub-rota para um registro de detalhe (config | tools | mcp | runs).

Tabs — regiões de conteúdo

O primitivo Tabs completo, baseado em Radix, com TabsList, TabsTrigger e TabsContent. Cada aba possui uma região de conteúdo distinta, com seu próprio cabeçalho, seus próprios dados e, potencialmente, suas próprias mutações. Use Tabs quando:
  • As abas renderizam interfaces estruturalmente diferentes (por exemplo, Overview vs. JSON vs. Logs).
  • Cada aba tem conteúdo substancial — tabelas, formulários, gráficos.
  • De outra forma, seria necessário dividir em rotas irmãs, mas as telas estão fortemente acopladas demais para justificar navegação em nível de URL.
Regra prática: se alternar a aba deveria mudar a URL, use FilterTabs com sub-rotas. Se alternar a aba só muda o que está dentro deste card/seção, use Tabs.

Cards vs. seções — composição de página

O erro mais comum em telas novas é envolver tudo em um Card. Nossa gramática de página é:
  1. PageHeader no topo — título, descrição, breadcrumbs, ação principal.
  2. Seções simples abaixo — um <h2> (ou um pequeno componente de cabeçalho de seção) seguido de conteúdo diretamente sobre o fundo da página.
  3. Card apenas para painéis laterais, seletores modulares, ou coisas que genuinamente precisam de um contêiner com borda.

Prefira seções simples

Isso se lê como uma única página com duas preocupações, que geralmente é o que você quer dizer. Empilhar dois Cards completos produz páginas pesadas e “encaixotadas” que não escalam bem além de três seções.

Use Card quando

  • Painéis laterais — um resumo na coluna direita ao lado do conteúdo principal.
  • Seletores modulares — seletor de modelo, seletor de template, caixa de filtro de audiência. Widgets autocontidos que podem viver em qualquer lugar da página.
  • Estados vazios e prompts de onboarding — um único elemento centralizado em uma página, do contrário, vazia.
  • Blocos de dashboard — cards de métrica em páginas de visão geral.
Se você se pegar aninhando um Card dentro de outro Card, pare. Esse é o sinal de que o contêiner externo deveria ser uma seção simples, ou o interno deveria ser um <div> simples.

Checklist rápido para novas páginas

Antes de abrir um PR para uma nova tela, confirme:
  • PageHeader é o primeiro elemento; breadcrumbs está ou omitido (automático) ou é um array explícito — nunca os dois.
  • As sub-rotas sob um registro usam FilterTabs conectadas a URLs reais, e não Tabs de página.
  • A entrada na barra lateral é simples, a menos que existam 3 ou mais rotas irmãs, caso em que se torna um grupo recolhível com um ícone apropriado.
  • A estrutura de nível superior é PageHeader + seções. Card aparece apenas para painéis laterais, seletores, estados vazios ou blocos de dashboard.
  • Todo novo segmento [id] tem um loader de rótulo registrado em /apps/app/lib/breadcrumbs.ts (ou breadcrumbs explícitos são passados em cada ponto de uso).

Entregue neste projeto

O projeto de navegação e polimento de interface (BORD-329 → BORD-333) entregou:
  • BORD-329 — grupos recolhíveis na barra lateral para Agents / Campaigns / Journeys ✅
  • BORD-330 — abas de sub-rota por registro (config | tools | mcp | runs e afins) ✅
  • BORD-331 — breadcrumbs auto-derivados com um pequeno registro + escape hatch de override explícito ✅
  • BORD-332 — refatoração da gramática de página: PageHeader + seções, Card rebaixado para função de painel lateral ✅
  • BORD-333 — este documento + uma limpeza de design tokens no CTA da landing page /web