copacol-react-app
v2.1.0
Published
Cria um projeto novo (backend CAP + frontend web React) a partir do template Copacol
Readme
Template Copacol React — backend CAP + frontend React
Ponto de partida para um projeto novo no padrão da Copacol. É o esqueleto do
Controle de Tarefas (XCOP-CONTROLE_TAREFAS_BACK / _FRONT) sem as
regras de negócio de lá: fica a infraestrutura que todo projeto precisa e que
já está provada em produção.
Só o template React (web): backend CAP e frontend React, sem as versões
Fiori e Mobile do create-copacol-app.
| Pasta | O que é |
|---|---|
| XCOP_template_back/ | SAP CAP (Node.js). Postgres em dev, HANA Cloud em qas/prd. Segue docs/cap/. |
| XCOP_template_front/ | React 18 + Vite 5, tema Copacol, três páginas: Home, Gestão de usuários e Configurações. |
| XCOP_template_componentes/ | Os componentes padrão das páginas (hoje: a tabela de lista, a barra de filtros e o painel de indicadores) e o catálogo que mostra cada um ao vivo (npm run dev lá). O CLI copia os componentes para src/components de todo projeto. Ver Componentes padrão. |
Cada pasta vira um repositório — o CI, o deploy.sh e o .env.deploy são
por repositório.
| | |
|---|---|
| Projeto gerado | XCOP_<nome>_back + XCOP_<nome>_front |
| Onde roda | navegador (Swarm) |
| Visual | tema Copacol próprio: faixa laranja institucional, Nunito + Poppins, CSS do template |
| Temas (Configurações) | Copacol, os seis do Controle de Tarefas e o Noturno |
| Login | keycloak-js na máquina de dev; oauth2-proxy na borda no servidor |
| Ícones | lucide-react: em src/appPages.js, icon: Settings (componente) |
| Página nova do CLI (o miolo é o do tipo: o cartão, a tabela, os filtros ou o painel) | src/pages/<Nome>.jsx + src/pages/Pagina.css |
| Referência | XCOP_template_front/CLAUDE.md e XCOP_template_back/CLAUDE.md |
O que já vem pronto
Login pelo Keycloak (
copacol_ad). Na máquina de desenvolvimento o SDKkeycloak-jsfaz o login; no servidor quem autentica é o oauth2-proxy na borda, e o front só repassa o token dele. Sair encerra as duas sessões.Sessão montada no backend (
whoami()): identidade do Keycloak + papéis do portal de usuários + cadastro de RH do colaborador.As integrações corporativas, configuradas e com fachada no backend:
| Serviço | Variável no
.envdo backend | |---|---| | FileUpload |cds_requires_FileUpload_credentials_url| | Portal de usuários |cds_requires_authUsers_credentials_url| | Portal de RH |cds_requires_authEmployee_credentials_url|A Home mostra se cada uma responde (
integrationStatus()).Opcionais, com fachada e linha na Home: DocuSign (
/external/docusign), Termos de aceite (/external/termos_aceite) e E-mail (/external/email: osendEmaildo serviço no-reply, que envia sempre comoCopacol <[email protected]>;cds_requires_email_credentials_url=https://www.copacol.com.br/<amb>/api/no-reply_email/odata/v4/app). Ocopacol-react-apppergunta se cada uma fica; falta só a URL no.env(ver.env.example) — sem ela, fachada e Home dizem qual variável falta (503).O Cofre de senhas (TI_password_vault) também é opcional — desmarcado, não entra nada dele, nem no backend (
cds_requires_vault_credentials_urlecds_requires_vaultIntegration_credentials_urlno.env): a credencial de cada pessoa em outros sistemas, que o app usa sem nunca ver a senha. O backend serve a API do cofre como ela é —/external/vault(oVaultService:Systems,Applications,MyCredentialscomreplace/validate,register,MyGrants,MyAuditEvents, com o token da pessoa) e/external/vault-integration(checkAvailability/execute, com o token de conta de serviço do Keycloak noAuthorizatione o JWT da pessoa nox-user-token) —, os contratos importados emsrv/external/vault*.cdse, emsrv/lib/cofre.js, o consumo (usarCredencial: os desfechos viram 428VAULT_CREDENTIAL_REQUIREDcom a ação quando a pessoa tem de agir) e o relay OData (<cofre>/relay/<SISTEMA>:<AMBIENTE>/<caminho>na URL de um serviço importado, comcds_requires_<Nome>_vault=true; vai com o token da pessoa e o código do app emx-vault-application, sem token de serviço). Para os dois, o app cadastrado no cofre (o código:cds_requires_vaultIntegration_credentials_application, padrão o nome do projeto); só o consumo pede o token de serviço:cds_requires_vaultIntegration_credentials_tokenUrl,_clientIde_clientSecretno.env. No front, o Cofre de senhas traz a página Minhas credenciais (menu,src/appPages.jse Gestão de usuários): cadastrar (sistema, nome, usuário e senha), conferir, trocar a senha, remover e autorizar os aplicativos a usar cada credencial — a senha vai direto ao cofre e não volta.E qualquer serviço OData externo, pelo
$metadatadele: ocopacol-react-app(ounpm run importar-servicono backend) importa comcds importe já deixa a rota/external/<Nome>funcionando — ver "Serviços externos (OData)" abaixo.Deploy no Swarm:
Dockerfile,stack.yml,scripts/swarm-deploy.sh,.github/workflows/ci.yaml— push emdev/qas/mainpublica no ambiente de mesmo nome../deploy.sh dev|qas|main "mensagem"faz a promoção.Temas, escolhidos em Configurações: o tema Copacol (faixa laranja institucional, Nunito + Poppins), mais os seis do Controle de Tarefas e o Noturno (escuro de verdade).
Configurações (a página do Tarefas, sem presença): trocar e remover a foto de perfil, escolher o tema e o idioma — que valem na hora e ficam salvos no navegador.
Português, inglês e espanhol no app inteiro (
src/i18n): texto novo na tela vai nos três dicionários.Foto de perfil no FileUpload, identificada pelo NOME do arquivo:
avatar-<matrícula>-<ambiente>.<ext>. A pessoa troca clicando na foto na Home; a foto do Controle de Tarefas (avatar-<matrícula>.<ext>) e a do portal de usuários também são achadas — vale a mais recente. Mostrada no topo e na Home, com as iniciais quando não há foto.Menu lateral nos três modos do Tarefas: recolhido (gaveta), só ícones e fixo — a escolha fica salva no navegador.
Gestão de usuários (
/usuarios): todo mundo que loga entra e fica registrado (primeiro e último acesso). Lá se marcam as páginas padrão (valem para todos) e se criam permissões (um nome + as páginas que libera) para dar às pessoas. As páginas vêm de uma lista única no front (src/appPages.js): página nova aparece sozinha para incluir na permissão. O admin do Keycloak abre tudo. Tabelas novas no banco: rode onpm run deploy:<perfil>do backend (o CI não faz).
Criar um projeto a partir do template
Pelo CLI, na pasta onde os projetos vão ficar:
npx copacol-react-appAbre uma página no navegador (local, em 127.0.0.1, só nesta máquina) com
as etapas: o que criar, os campos com a regra e a sugestão na hora (o que vem
sugerido segue o nome até você mudar; "usar a sugestão" volta), a lista de
páginas com a rota, o arquivo e o ícone de cada uma — na ordem do menu, que se
muda arrastando ou pelas setas (a Home fica sempre em primeiro: é a página
inicial; Gestão de usuários e Configurações mudam de lugar, mas não saem; no
terminal, as novas entram logo depois da Home) —, as integrações, o resumo e,
à direita, a árvore do que vai ser criado. Depois de criar, um botão sobe o
backend e o front e mostra o andamento ao vivo — o npm install, "No ar" e as
mensagens. O terminal fica aberto com o endereço (se o navegador não abrir,
copie de lá); "Encerrar" na página, ou Ctrl+C no terminal, para tudo.
--nao-abrir só mostra o endereço; --terminal faz as mesmas perguntas no
terminal (o padrão em CI, por SSH e no Linux sem tela). As regras são as mesmas
nos dois: a página só mostra — quem valida e gera é o bin/create.js
(bin/web.js é o servidor da página, bin/web/ a
página).
As perguntas: primeiro o que criar (ver abaixo), depois o nome e o título
do projeto (o nome sugere as pastas XCOP_<nome>_back e XCOP_<nome>_front e
todo o resto — Enter aceita; a pasta de cada projeto também pode ter outro
nome: letras sem acento, números, _, - e ponto, sem espaço), DEPLOY_DIR, STACK_BASE e APP_NAME
do .env.deploy de cada lado (o APP_NAME é a rota e pode ter mais de um
trecho, como o senior/auth do SENIOR-Auth: o swarm-deploy.sh aceita), se
quer páginas além da Home (quantas, o nome e o tipo de cada uma — Em
branco (título e um cartão), Lista (tabela), que já nasce com a tabela de
lista do XCOP_template_componentes, dados de exemplo e os botões
funcionando, Lista com filtros, a mesma tabela com a barra de filtros em
cima — texto, uma opção, várias opções e período, filtrando na hora —, ou
Painel (analytics), o painel de indicadores com dados de exemplo —
números, colunas por dia, barras, roscas e o mapa-múndi com o ranking de
países, e o seletor de período; cada página ganha src/pages/<Nome>.jsx e uma
entrada em src/appPages.js — rota, item no menu e a página na Gestão de
usuários —, com ícone do lucide-react escolhido pelo nome (sem palavra
conhecida, o painel fica com o de gráfico). Um nome igual ao de um componente
que a página importa (Analytics, DataTable, FilterBar, KpiCard…) ou ao de um
arquivo que o template já tem ganha o sufixo Pagina no arquivo e no
componente: "Analytics" vira src/pages/AnalyticsPagina.jsx, com a rota
/analytics), e as integrações opcionais (DocuSign, Termos de aceite, E-mail,
Cofre de senhas: espaço marca, Enter confirma — a que ficar de fora sai dos
dois projetos, inteira; o Cofre de senhas marcado traz também a página Minhas
credenciais). Mostra o resumo, pede confirmação e cria os dois com o
nome trocado em toda a tabela do passo 2: rota e título no front,
API_PREFIX, namespace e CSVs, banco no .env, instância HDI no
.cdsrc-private.json.example e os package.json. No fim, oferece subir os
dois no mesmo terminal ([back]/[front] na frente de cada linha; Ctrl+C para
os dois). A validação é inline: valor inválido não passa, e o erro diz o que
corrigir. npx copacol-react-app --help resume as perguntas.
O que criar — a primeira etapa (no terminal, a primeira pergunta; ou pela flag, que já responde):
| Escolha | Flag | O que sai |
|---|---|---|
| Backend e front-end — o padrão | — | as duas pastas |
| Só o backend | --so-backend | só XCOP_<nome>_back. Sem a etapa de páginas (elas moram no front, em src/appPages.js); das integrações opcionais, a parte do backend. "Subir" liga só ele |
| Só o front-end | --so-front | só XCOP_<nome>_front. Continua pedindo o .env.deploy do backend que já existe — o APP_NAME dele dá o caminho da API (/<amb>/api/<APP_NAME>), com as mesmas sugestões e regras —, mas não cria a pasta _back; das integrações, a parte do front (a linha na Home e os textos). "Subir" liga só o front, que fala com o backend configurado (em desenvolvimento, o CAP desta máquina na 4004) |
Na página, /api/previa e /api/criar recebem isso no campo criar
(ambos | back | cliente); sem o campo, ambos.
Serviços externos (OData) — no backend (some em "só o front-end"): na
página, na etapa Integrações, "Adicionar metadata (.xml, .edmx)" escolhe um ou
mais arquivos $metadata (OData V4 ou V2, até 5 MB cada); no terminal, a
pergunta depois das integrações, ou --servico <arquivo> (repete para mais de
um). O nome do serviço vem do arquivo (ddic_service.xml → ddic; genérico
como metadata.xml, do namespace) e muda à vontade: só letras e números,
começando por letra (o _ quebraria a variável do .env), sem repetir um
serviço do template (um nome que já existe ganha um número na sugestão). Ao
criar, o CLI roda no backend o npm install (o mesmo que o primeiro npm run
watch faria) e o scripts/importar-servico.js dele — cds import
srv/external/<Nome>.xml --as cds --force — e o projeto já sai com:
srv/external/<Nome>.cds(o modelo importado) ecds.requires.<Nome>nopackage.json, com a URL vazia: ela vem do.envde cada ambiente (cds_requires_<Nome>_credentials_url; usuário e senha técnicos, se o remoto não aceitar o token de quem está logado, em_username/_password);- a fachada
srv/external-<nome>.cds/.js, em/external/<Nome>: o login das outras rotas (sem sessão, 401), só o que o metadata deixa (um serviço só de leitura recusa criar, alterar e excluir com 405) e a leitura —$filter,$orderby,$top,$skip,$select,$count— encaminhada ao remoto pelocds.connect.to. Sem a URL, na máquina, a rota responde com dados de exemplo gerados do metadata (o backend sobe e a rota funciona logo depois de criado); em qas/prd, 503 dizendo a variável que falta; - as variáveis no
.env.example(comentadas, sem valor real), a linha noCLAUDE.mddo backend e os testes (test/external-<nome>.test.js, sem URL, e-remoto.test.js, com um remoto de mentira), que entram nonpm test.
Num projeto que já existe, o mesmo, no backend: npm run importar-servico --
<arquivo.xml> [Nome] (ver "Serviços OData importados" no CLAUDE.md dele).
Fica na mão: cds_requires_db_credentials_password no .env, criar o .cdsrc-private.json a partir
do .example, e os passos 3 a 5 abaixo.
Na mão:
Copie as duas pastas do template para os repositórios novos (sem
node_modules):XCOP_template_back+XCOP_template_front.Troque o nome
templatepelo do projeto. Todos os lugares:| Arquivo | Chave | Exemplo | |---|---|---| | BACK
.env.deploy|DEPLOY_DIR,STACK_BASE,APP_NAME|compras/compras_service,compras,compras| | FRONT.env.deploy|DEPLOY_DIR,STACK_BASE,APP_NAME|compras/compras_app,compras,compras| | FRONTsrc/manifest.json|id,title,basePath,postLogoutRedirectUri,dataSources.*.uri|/compras,/api/compras/odata/v4/app/| | FRONTvite.config.js|API_PREFIX|/api/compras| | FRONTindex.html,src/brandingDefaults.js| título /APP_NAME|Compras| | FRONTindex.html(script inline) |var app = 'template': oiddosrc/manifest.json(chaves<id>.theme/<id>.language) |var app = 'compras'| | BACKdb/schema.cds,srv/app-service.cds|namespace com.copacol.template|com.copacol.compras| | BACKdb/local-data/*.csv| o prefixo do nome do arquivo é o namespace |com.copacol.compras-Examples.csv| | BACK.env(a partir de.env.example) |cds_requires_db_credentials_database,_user,_password| banco do projeto | | BACK.cdsrc-private.json(a partir do.example) |instance,keydo HDI |copacol-hdi-compras| | os doispackage.json|name,description| |O namespace decide o nome físico das tabelas: troque antes do primeiro
cds deploy.Apague o exemplo
Examples(db/schema.cds,srv/app-service.cds,db/local-data/,listExamplesemsrc/api/app.js) quando criar a primeira entidade real — e adaptetest/memory.test.js, que faz o CRUD do modo sem banco emExamples: troque pela entidade nova, senão onpm testquebra.No servidor, crie o
.envdo backend em/docker-data/<dev|qas|prd>/apps/<DEPLOY_DIR>/.env— as URLs das integrações mudam de prefixo por ambiente (/appdev,/appqas,/app).No Keycloak, confira se o client de login aceita a URL do app novo (
http://localhost:5173/*em desenvolvimento).
Componentes padrão — XCOP_template_componentes
Os elementos que as páginas usam, num lugar só, escritos como vão ficar no
projeto (src/components). O copacol-react-app copia todos para
src/components de cada projeto gerado; a página do tipo Lista (tabela)
já usa a tabela de lista, a do tipo Lista com filtros, a barra de
filtros em cima dela, e a do tipo Painel (analytics), o painel de
indicadores.
cd XCOP_template_componentes
npm run dev # o catálogo em http://localhost:5180 (a 1ª vez instala sozinho)
npm run build # o catálogo estático em dist/, para publicar se quiserO catálogo mostra cada componente ao vivo, com o CSS, os temas e os textos
de verdade do template (troca tema, idioma e largura — computador, tablet,
celular), mais o código do exemplo, as props e os arquivos que o CLI copia.
Componente novo: ver
XCOP_template_componentes/CLAUDE.md.
Hoje, três:
- Tabela de lista (
DataTable) — título com a contagem, visões, busca, atualizar/novo/copiar/excluir, marcar linhas, ordenar pelo título da coluna, escolher colunas, exportar CSV, selo de status e paginação. - Filtros (
FilterBar) — a barra por cima da lista: campos de texto, de uma opção, de várias opções e de período; quantos estão ativos, um chip por filtro (com o x para tirar), Limpar filtros e mostrar/ocultar. Filtra ao digitar, ou só no botão Filtrar (applyOnSubmit, para o filtro que vai ao backend). A regra de cada filtro fica emfiltrosLogica.js. - Analytics (
Analytics, com as peçasAnalyticsGroup,KpiCard,TrendCard,BarsCard,DonutCardeMapCard) — o painel de indicadores: a barra do período e do Atualizar, uma grade de 12 colunas (ospande cada cartão), o número grande com a variação (▲/▼), colunas por dia, barras agrupadas, roscas com o total no meio e o mapa-múndi com o ranking de países; cada gráfico tem o "Ver como tabela". Os gráficos são dorecharts, nas cores do tema; o mapa vem pronto dentro do componente (mapaMundi.js, gerado uma vez no catálogo pornpm run gerar-mapa), sem depender de internet. A lógica (larguras, formatos, percentuais, classes do mapa) fica emanalyticsLogica.js.
E os quatro tipos de página nova do CLI: Em branco, Lista (tabela) (a tabela com dados de exemplo), Lista com filtros (a barra de filtros em cima da tabela, um campo de cada tipo, o estado na página) e Painel (analytics) (o painel com os dados de exemplo e o estado do período, pronto para trocar o exemplo pela chamada ao backend).
Publicar o CLI
A raiz é o pacote copacol-react-app (bin/create.js, mais
a página do navegador em bin/web.js e bin/web/,
sem dependência nova); o que ele copia são as duas pastas do template e a dos
componentes (sem o node_modules do catálogo) (o files do package.json),
como estão aqui.
A foto de cada tipo de página, que a página do CLI mostra na etapa Páginas,
fica em bin/web/previas/: mudou um componente ou uma página de tipo, rode
npm run previas antes de publicar (ver deploy_npm.md).
npm link # testa o comando na máquina, sem publicar
npm pack --dry-run # confere o que vai no pacote
npm version minor # a cada mudança no template
npm publishO npm nunca publica .gitignore nem .npmrc. O prepack guarda cópias
_gitignore/_npmrc e o CLI renomeia de volta — por isso não publique com
--ignore-scripts: sem o .npmrc o build do Docker do backend quebra, e sem o
.gitignore o projeto levaria node_modules e o .env para o git. O
node_modules e o dist/ ficam fora do pacote pelo .gitignore.
Rodar local
# backend — copie .env.example para .env
cd XCOP_template_back
npm run watch # http://localhost:4004 — reinicia sozinho ao salvar
# frontend
cd XCOP_template_front
npm run dev # http://localhost:5173/template/Na primeira vez, backend e frontend instalam as dependências sozinhos
(npm install) antes de subir.
Sem Postgres: o .env.example já vem com DB_IN_MEMORY=true — o backend
sobe com SQLite em memória, cria as tabelas sozinho e carrega os exemplos de
db/local-data/. Os dados somem ao parar. Para usar o Postgres de dev, mude
para DB_IN_MEMORY=false, preencha cds_requires_db_credentials_password e
rode npm run deploy:dev uma vez para criar as tabelas.
Credenciais no .env: todas no padrão cds_requires_<serviço>_credentials_<campo>,
que o CAP lê sozinho — o banco (cds_requires_db_credentials_host, _port,
_database, _user, _password; em qas/prd, o HANA nos mesmos nomes, com
_schema no lugar de _database), as URLs das integrações
(cds_requires_FileUpload_credentials_url…) e, com o Cofre de senhas, o cofre
(cds_requires_vaultIntegration_credentials_*). Senha entre aspas simples.
A forma com ponto (cds.requires.db.credentials.host) e os nomes antigos
(DB_*, HANA_*, VAULT_*) ainda valem.
Use localhost, não 127.0.0.1 — é o que está nos redirect URIs do Keycloak.
Regras de desenvolvimento
- Backend: só se altera com base em
docs/cap/. Projeto novo segue o livro sem os desvios de legado do Tarefas (usaID,cuid,managed, associação gerenciada). Os únicos desvios são os da plataforma, em08-aplicacao-neste-projeto.md. - Frontend: o navegador fala com um backend só; nada de URL fixa — tudo sai
do
manifest.json. VerXCOP_template_front/CLAUDE.md. - As âncoras que o
copacol-react-apptroca (a tabela do passo 2 e o comentário// ↓ páginas novas entram aquidosrc/appPages.js) têm de continuar nas pastas do template — se uma sumir, o CLI avisa "confira à mão".
