fayzfayz sdk

Rotas e navegação

Num app Fayz você não escreve um roteador. A navegação — os itens de menu e as telas por trás deles — é derivada do que o manifesto declara: os plugins ligados na surface e as páginas extras que você definir. Esta página mostra de onde cada rota vem.

A surface é a raiz

Toda rota nasce dentro de uma surface. Uma surface é uma face do app (admin, storefront, member); ela declara o scaffold (o layout do shell), a lista de plugins e uma lista opcional de pages:

{
  "surfaces": {
    "admin": {
      "scaffold": "admin",
      "plugins": [{ "id": "dashboard" }, { "id": "crm" }],
      "pages": []
    }
  }
}

Do bloco acima o runtime monta a navegação inteira. Você liga um plugin e o menu e as telas dele aparecem — sem tocar em nenhum arquivo de rotas.

De onde vem a rota de um plugin

Cada plugin descreve a própria navegação e as próprias rotas no seu manifesto (o PluginManifest). São dois campos:

  • navigation — os itens de menu. Cada entrada tem section (main, secondary ou settings), position (ordem), label, route e um icon opcional. É isso que pinta a barra lateral.
  • routes — o mapeamento path → componente. Cada rota aponta para um componente (direto ou por componentId do registry) e pode declarar um guard (authenticated, public, role, share-token).

Quando você adiciona { "id": "crm" } à surface, o runtime lê o navigation e o routes do CRM e injeta ambos: o item do menu e a tela em /crm. Ligar dois plugins junta as duas navegações na mesma barra lateral, ordenadas por section + position. É por isso que a ordem dos ids no manifesto não define a ordem visual — quem define é o position de cada plugin.

Páginas próprias, sem plugin

Quando você precisa de uma tela avulsa que não pertence a nenhum plugin, declare-a em pages. Cada página é dado puro e aponta para exatamente uma fonte de conteúdo:

{
  "pages": [
    { "path": "/sobre", "label": "Sobre", "section": "secondary",
      "blocks": [{ "type": "hero", "props": { "title": "Nossa história" } }] },
    { "path": "/relatorio", "label": "Relatório", "component": "custom:harvest-board" }
  ]
}

O schema exige uma de três chaves na página:

  • blocks — uma árvore de blocos resolvida pelo registry de blocos (recompor conteúdo sem código).
  • entity — o id de uma entidade, para gerar um CRUD sobre ela.
  • component — o id de um componente próprio registrado no src/registry.tsx (namespace custom:).

Os campos label, icon, section e permission controlam como a página aparece no menu e quem a enxerga.

O registry conecta ids a código

Rotas e páginas referenciam código por id, nunca por import inline. Um component: "custom:harvest-board" no manifesto só resolve porque você registrou esse id no src/registry.tsx:

// src/registry.tsx
import { registerPage } from '@fayz-ai/core'
import { HarvestBoard } from './pages/HarvestBoard'

registerPage('custom:harvest-board', HarvestBoard)

registerPage (e as suas irmãs registerComponent / registerBlock) vêm de @fayz-ai/core — esse import compila hoje. O src/lib/fayz-runtime.ts gerado pelo scaffold ainda é uma tela-âncora e não reexporta essas funções; ele vai reexportá-las em breve. Até lá, importe direto de @fayz-ai/core.

Essa separação — dado no manifesto, código no registry — é o que mantém o app atualizável: a plataforma sabe exatamente quais ids você sobrescreveu e o fayz doctor consegue reportar divergências.

Regra prática: capacidade = ligar um plugin (o menu cresce sozinho); tela avulsa = uma entrada em pages; código próprio = um id custom: no registry.tsx, referenciado como dado. Você nunca edita um roteador.


Próximo: entenda o arquivo inteiro em Configuração.