Skip to main content
La shell dell’app di Switchbord è costruita attorno a una barra laterale comprimibile, un breadcrumb derivato automaticamente nella barra superiore e un piccolo insieme di primitive componibili per le pagine (PageHeader, FilterTabs, Tabs, Card, sezioni semplici). Questa pagina documenta cosa si trova dove e — per i contributor — quando usare ciascuna primitiva, così le nuove schermate hanno lo stesso aspetto e la stessa sensazione del resto del prodotto.

Struttura della barra laterale

La barra laterale a sinistra usa gruppi comprimibili. Ogni gruppo è una route principale; i suoi figli sono un elenco semplice di destinazioni di primo livello sottostanti.

Posta in arrivo

Piatta — nessun sotto-gruppo. Porta direttamente alla posta in arrivo dell’operatore.

Contatti

Piatta. I filtri e i segmenti vivono all’interno della pagina, non nella barra laterale.

Agenti

Gruppo comprimibile. Figli: Overview, Templates e le route di dettaglio per singolo agente.

Campagne

Gruppo comprimibile. Figli: All campaigns, Audiences, Schedules.

Journeys

Gruppo comprimibile. Figli: All journeys, Templates, Runs.

Impostazioni

Piatta; le scheda interne gestiscono le numerose sotto-pagine.
I gruppi mantengono il proprio stato aperto/chiuso per utente in localStorage, così la shell non si richiude a ogni navigazione.
Se stai aggiungendo una nuova destinazione di primo livello, preferisci prima una voce piatta. Passa a un gruppo comprimibile solo quando hai tre o più route sorelle che beneficiano di essere visibili contemporaneamente.

Sotto-route sotto un record principale

Le pagine di dettaglio per Agenti, Campagne e Journeys usano una struttura di URL coerente:
Queste sotto-route sono rese come FilterTabs nella parte superiore della pagina del record, immediatamente sotto il PageHeader. Ogni scheda è una route reale — collegabile in profondità, condivisibile e conservata attraverso i ricaricamenti. I cambi di scheda usano la navigazione shallow di Next.js, così la posizione di scorrimento e lo stato transitorio non vengono persi.
Le scheda delle sotto-route non sono la stessa primitiva delle Tabs all’interno della pagina. Consulta Filter tabs vs content tabs di seguito.
La barra superiore mostra un percorso di breadcrumb che è derivato automaticamente dalla route corrente per default. Il sistema scorre i segmenti del pathname, cerca ciascuno in un piccolo registro (/apps/app/lib/breadcrumbs.ts) per un’etichetta leggibile e mostra breadcrumb cliccabili per ogni segmento tranne l’ultimo.

Modalità automatica (il default)

Ogni pagina ottiene breadcrumb derivati automaticamente senza fare nulla:
Per /agents/abc123/runs questo mostra: Agents › Acme support bot › Runs. L’etichetta centrale è risolta dal loader [id] del registro (recupera il nome visualizzato dell’agente dalla cache). Usa la modalità automatica quando:
  • I segmenti della route corrispondono in modo chiaro a etichette leggibili (il caso comune).
  • Ti trovi già all’interno di una gerarchia record padre/figlio collegata al registro.

Modalità esplicita

Passa breadcrumbs come array quando il percorso derivato automaticamente sarebbe errato o privo di contesto:
Usa la modalità esplicita quando:
  • L’URL non riflette la posizione mentale percepita dall’utente (es. route modali, wizard).
  • Devi mostrare un’etichetta non ancora presente nel registro e non vuoi aggiungerla.
  • Stai rendendo una pagina occasionale (migrazioni, strumenti admin) dove mantenere il registro non ne vale la pena.
Non mescolare le due modalità. Se passi breadcrumbs, il resolver automatico viene completamente saltato — gli override parziali non sono supportati di proposito, per mantenere il percorso predicibile.

Filter tabs vs content tabs

Switchbord fornisce due primitive di scheda che si somigliano ma hanno ruoli diversi.

FilterTabs — controllo segmentato

Una riga compatta di pulsanti segmentati. Sceglie quale porzione dello stesso elenco stai visualizzando. Non ha pannelli di contenuto propri — controlla solo lo stato di query/filtro sottostante. Usa FilterTabs quando:
  • Passi tra viste dello stesso dataset sottostante (es. All / Draft / Pending / Approved sui modelli).
  • Vuoi un aspetto di controllo segmentato che non implichi “pagine” separate.
  • Navigazione tra sotto-route per un record di dettaglio (config | tools | mcp | runs).

Tabs — regioni di contenuto

La primitiva Tabs completa basata su Radix, con TabsList, TabsTrigger e TabsContent. Ogni scheda possiede una regione di contenuto distinta con la propria intestazione, i propri dati e potenzialmente le proprie mutazioni. Usa Tabs quando:
  • Le scheda mostrano interfacce strutturalmente diverse (es. Overview vs JSON vs Logs).
  • Ogni scheda ha contenuti sostanziali — tabelle, form, grafici.
  • Dovresti altrimenti dividere in route sorelle, ma le schermate sono troppo strettamente collegate per giustificare una navigazione a livello di URL.
Regola pratica: se passare da una scheda all’altra dovrebbe cambiare l’URL, usa FilterTabs con sotto-route. Se passare da una scheda all’altra cambia solo il contenuto di questa card/sezione, usa Tabs.

Card vs sezioni — composizione della pagina

L’errore più comune nelle nuove schermate è racchiudere tutto in una Card. La nostra grammatica di pagina è:
  1. PageHeader in alto — titolo, descrizione, breadcrumb, azione principale.
  2. Sezioni semplici sotto — un <h2> (o un piccolo componente di intestazione di sezione) seguito dal contenuto direttamente sullo sfondo della pagina.
  3. Card solo per pannelli laterali, selettori modulari o cose che richiedono davvero un contenitore con bordo.

Preferisci sezioni semplici

Questo si legge come una singola pagina con due argomenti, che di solito è ciò che si intende. Impilare due Card complete produce pagine pesanti e squadrate che non scalano oltre tre sezioni.

Ricorri a Card quando

  • Pannelli laterali — un riepilogo a colonna destra accanto al contenuto principale.
  • Selettori modulari — selettore del modello, selettore del template, box del filtro pubblico. Widget autonomi che possono vivere ovunque nella pagina.
  • Stati vuoti e prompt di onboarding — un’unica affordance centrata su una pagina altrimenti vuota.
  • Riquadri della dashboard — card di metriche nelle pagine panoramica.
Se ti ritrovi a nidificare una Card dentro un’altra Card, fermati. È il segnale che il contenitore esterno dovrebbe essere una sezione semplice, oppure che quello interno dovrebbe essere un semplice <div>.

Checklist rapida per le nuove pagine

Prima di aprire una PR per una nuova schermata, verifica che:
  • PageHeader sia il primo elemento; breadcrumbs sia omesso (auto) oppure un array esplicito — mai entrambi.
  • Le sotto-route sotto un record usino FilterTabs collegate a URL reali, non Tabs all’interno della pagina.
  • La voce nella barra laterale sia piatta a meno che non ci siano 3 o più route sorelle, nel qual caso è un gruppo comprimibile con un’icona sensata.
  • La struttura di primo livello sia PageHeader + sezioni. Card appare solo per pannelli laterali, selettori, stati vuoti o riquadri della dashboard.
  • Ogni nuovo segmento [id] abbia un loader di etichette registrato in /apps/app/lib/breadcrumbs.ts (oppure vengano passati breadcrumb espliciti a ogni punto di chiamata).

Consegnato in questo progetto

Il progetto di navigazione + rifinitura dell’interfaccia (BORD-329 → BORD-333) ha fornito:
  • BORD-329 — gruppi comprimibili della barra laterale per Agenti / Campagne / Journeys ✅
  • BORD-330 — scheda di sotto-route per singolo record (config | tools | mcp | runs e simili) ✅
  • BORD-331 — breadcrumb derivati automaticamente con un piccolo registro + via di uscita per l’override esplicito ✅
  • BORD-332 — refactoring della grammatica di pagina: PageHeader + sezioni, Card retrocessa a compito di pannello laterale ✅
  • BORD-333 — questo documento + una pulizia dei design token sulla CTA della landing /web