Skip to content

corabank/platform-module-quickstart

Repository files navigation

Backoffice Module Quickstart

Template para criar modulos para o Backoffice Shell. Clone, desenvolva, deploy — o modulo se registra automaticamente no shell.

Comecando

# 1. "Use this template" no GitHub, ou clone:
git clone https://github.com/corabank/platform-module-quickstart my-module
cd my-module

# 2. Instalar dependencias (frontend + backend)
npm install
(cd backend && npm install)

# 3. Dev server — frontend + backend em paralelo
npm run dev:all    # frontend :4100, backend :8080

# 4. Testes
npm test
(cd backend && npm test)

# 5. Build
npm run build              # → dist/module.js
(cd backend && npm run build)  # → backend/dist/server.js

Stack default: React + Vite no src/ + Hono no backend/. Se quiser trocar a stack do backend por Go/Python/Kotlin, rode /switch-backend-stack <stack>. Se seu modulo e pure-view e nao precisa de backend, rode /remove-backend.

Como funciona

Seu modulo e carregado pelo shell em tempo de execucao. Voce desenvolve de forma independente no seu proprio repo.

Seu repo                          Shell
┌──────────────┐     deploy    ┌──────────────────┐
│  src/        │  ──────────→  │  Carrega via      │
│  module.json │    CDN/K8s    │  import(bundleUrl) │
│  dist/       │               │                    │
└──────────────┘               └──────────────────┘
       │                               │
       └─── auto-registro ───────────→ │
            (POST /api/modules)        ↓
                                Aparece na sidebar

O que o shell fornece

Feature Como usar
Usuario context.user
Permissoes context.hasPermission("orders", "write")
Auditoria context.auditLog("order.created", { id: "123" })
Notificacoes context.notify("Salvo!", "success")
Cross-module context.eventBus.emit("order.created", data)
Logger context.logger.info("processando", { orderId })
Tema CSS vars automaticos + useShellTheme(context)
Cache compartilhado context.sharedCache.get/set/invalidate/subscribe
Token OAuth context.getProviderToken() ou useApi(context)

Estrutura

src/                      # Frontend (React + Vite)
├── index.ts              # Entry point (bootstrap, mount, unmount)
├── register.ts           # Auto-registro com o shell
├── components/
│   ├── App.tsx           # Componente principal
│   └── ExampleBackendCall.tsx  # Referencia do padrao front→back→API
├── hooks/
│   ├── useShellTheme.ts  # Reagir a mudanca de tema
│   ├── useApi.ts         # Chamadas autenticadas genericas
│   └── useBackendApi.ts  # Chamadas ao backend deste modulo
├── styles/
│   └── module.css        # Estilos com design tokens
└── test-utils.ts         # Mock do ShellContext

backend/                  # Backend (Hono + Node)
├── src/
│   ├── server.ts         # Hono app, rotas sob /api
│   ├── config.ts         # Env vars
│   └── routes/
│       └── example.ts    # Referencia de proxy para API externa
├── package.json
├── tsconfig.json
└── Dockerfile

charts/my-module/         # Helm chart (frontend + backend, 1 release)
platform/configs/         # Values por ambiente (stage, prod)
module.json               # Metadados para auto-registro

Identidade visual — accent e icon

Cada módulo aparece no grid da Home do shell como um card com um chip colorido (44×44) e um ícone. Dois campos opcionais no module.json controlam isso:

"accent": "coral",
"icon": "shield",

accent — cor do chip. Um destes 7 valores:

accent hue (aprox.) uso típico
coral vermelho coral ações principais / regulatório
amber âmbar configuração / toggles
violet roxo orquestração / fluxo
teal verde-azul aprendizagem / coleta
blue azul ferramentas / API
rose rosa suporte / pessoas
lime verde-lima dados / analytics

icon — nome do ícone lucide em kebab-case (ex.: shield, git-branch, database, book-open, key).

Os dois são opcionais

Se você não declarar accent, o shell pega 1 dos 7 valores via hash determinístico do id do módulo — mesmo id sempre cai na mesma cor, em qualquer sessão e dispositivo.

Se você não declarar icon, o shell usa um default por group:

group icon default
operations layers
finance wallet
customer-support life-buoy
analytics chart-line
settings settings

Sem custo cognitivo no dia 1 — declare quando quiser dar identidade visual ao módulo, deixe vazio quando estiver tudo bem com o auto.

A skill /check-module valida se o accent declarado está no enum e avisa quando os dois estão ausentes (informativo — não bloqueia deploy).

Sidebar — Navegação e Perspectivas

Por padrão o module.json declara um menu único no campo navigation (caso da maioria dos módulos):

"navigation": [
  { "label": "Overview", "path": "/my-module", "requiredPermission": "my-module:read" },
  { "label": "Items",    "path": "/my-module/items", "requiredPermission": "my-module:read" }
]

Perspectivas (múltiplos menus para o mesmo módulo)

Se o módulo precisa expor menus diferentes para personas distintas do mesmo usuário (ex.: "Colaborador" vs "Líder" num módulo de despesas), use perspectives no lugar de navigation:

"perspectives": [
  {
    "id": "colaborador",
    "label": "Colaborador",
    "navigation": [
      { "label": "Meu Painel",       "path": "/my-module" },
      { "label": "Minhas Solicitações", "path": "/my-module/solicitacoes" }
    ]
  },
  {
    "id": "lider",
    "label": "Líder",
    "navigation": [
      { "label": "Painel do Líder", "path": "/my-module/painel-lider", "requiredPermission": "my-module:approve" },
      { "label": "Histórico do Time", "path": "/my-module/time", "requiredPermission": "my-module:approve" }
    ]
  }
]

O shell renderiza um combobox no topo do sidebar quando há ≥2 perspectivas visíveis ao usuário e troca o menu na seleção. A escolha persiste no backend (segue o user entre dispositivos).

Regras (validadas no POST /api/modules — manifest fora do schema é rejeitado):

  • navigation e perspectives são mutuamente exclusivos no module.json. Declare um ou outro, nunca os dois.
  • Cada perspectiva precisa de id único, label não-vazio e ao menos um item em navigation.
  • Visibilidade é derivada do nav: a perspectiva aparece para o user quando pelo menos um item da nav dela é visível (não há requiredPermission na própria perspectiva).
  • Paths seguem o mesmo padrão do navigation flat: o convencional no module.json é absoluto começando com /${id}/... (como nos exemplos acima), e o Shell API normaliza para a forma relativa interna no registro. Forma relativa direta ("", "items") também é aceita. O que não vale é drift — /foo num módulo bar é rejeitado.

Validação local: rode /check-module antes de subir mudanças no module.json para pegar erros que o Shell API rejeitaria no deploy.

Chamando APIs

Regra de ouro: o frontend nunca chama API externa direto. Toda chamada pra banco/backoffices/APIs de terceiros passa pelo backend deste modulo, que atua como BFF/proxy. Ver CLAUDE.md pra contexto completo.

Frontend → backend do modulo

import { useBackendApi } from "@/hooks/useBackendApi";

function OrderList({ context }) {
  const api = useBackendApi(context);
  const orders = await api.get("/orders");
  // Em dev: http://localhost:8080/api/orders
  // Em prod: /modules/{module-id}/api/orders (via shell proxy)
}

Backend → API externa

Editar backend/src/routes/ e adicionar a chamada com fetch. Credenciais vem de env vars (Vault via ExternalSecret em prod). Ver backend/src/routes/example.ts como referencia.

Chamadas autenticadas diretas (excecao)

Se o servico externo aceita o token OIDC do proprio user (mesmo realm Keycloak), da pra usar useApi sem backend — mas audit, cache e transformacao ficam espalhados. Prefira backend sempre que possivel.

Tema (Light/Dark)

Use CSS Custom Properties — adaptam automaticamente:

color: var(--foreground);
background: var(--primary);         /* azul Cora */
border: 1px solid var(--border);

Nunca hardcode cores. Tokens disponiveis: --primary, --foreground, --muted-foreground, --border, --cora-positive, --cora-negative, --cora-alert, --cora-info (cada um com -foreground).

Deploy

Auto-registro (recomendado)

O modulo se registra sozinho quando sobe:

App inicia → healthy → le module.json → POST /api/modules → shell ativa
Heartbeat 30s → mantem ativo | SIGTERM → desativa

Env vars no deploy:

SHELL_API_URL=https://backoffice.stage.cora.team
SHELL_TOKEN=<token-de-servico>
BUNDLE_URL=https://cdn.internal/modules/meu-modulo/1.0.0/module.js

Sem estas vars, auto-registro e ignorado (seguro para dev local).

Manual

  1. npm run builddist/module.js
  2. Deploy no CDN
  3. Shell sidebar → Import Module → informar URL

Comandos

Comando Descricao
npm run dev Frontend dev server (4100)
npm run dev:backend Backend dev server (8080)
npm run dev:all Frontend + backend em paralelo
npm run build Build frontend (dist/module.js)
npm test Testes frontend (watch)
npm run test:run Testes frontend 1x
npm run test:coverage Cobertura frontend
npm run lint ESLint
npm run typecheck TypeScript (frontend)
cd backend && npm test Testes backend
cd backend && npm run typecheck TypeScript (backend)

Regras

  • Todo mutation → context.auditLog() + context.sharedCache.invalidate() — sem exceção
  • Feedback → context.notify() — nunca alert()
  • Logs → context.logger — nunca console.log
  • Cores → CSS vars — nunca hardcodar
  • TDD — 80% cobertura minima
  • Max 200 linhas/arquivo, 50 linhas/funcao

About

Quickstart template for building modules for the Backoffice Shell platform

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors