Appearance
Mapa de Rotas — vue-spa (frontend)
Referência canônica e code-verified de TODAS as rotas do app web (contrasync-vue-spa). É a fonte de verdade que a IA (Zelor) consulta para montar URLs da plataforma: ela NUNCA inventa caminho nem id, só combina uma rota REAL desta lista com um id REAL vindo de uma tool/consulta. O egress do ai-api (sanitizeAgentLinks) faz o enforcement disso; este doc é o mapa que a IA usa para acertar a rota certa.
Convenção de URL
Toda tela autenticada vive sob o parent /company/:companyId (CompanyLayout, meta.requiresAuth: true). O prefixo /company/{companyId} é injetado automaticamente por patchRouterWithCompanyPrefix (src/router/companyPrefix.ts) quando se navega por path sem prefixo (exceto paths context-free: /auth/, /access, /company/new, /onboarding/, /signing/, /unauthorized). {companyId} é uma string opaca do tenant (UUID em produção; nos mocks aparece como c1, c101): trate como opaco, nunca deduza nem troque.
Tipos de proteção (guard em modules/auth/guards/authGuard.ts):
- protected (
requiresAuth !== false): exige login +companyId+ role/permission. Falha redireciona parapermission-denied. - guest-only (
guestOnly: true): redireciona quem já está logado. - requiresLogin (
requiresLogin: true, semrequiresAuth): logado, sem empresa selecionada. - public (
requiresAuth: false): portais de token, sem login.
Rotas autenticadas — /company/{companyId}/...
Todas abaixo são protected. {id}/{providerId}/{contractId}/{periodId} são ids de item que a IA só usa com o valor REAL vindo de uma tool/consulta.
Painel, perfil, config
| Path | Nome | Params | Permissão / nota |
|---|---|---|---|
/company/{companyId}/dashboard | dashboard | companyId | dashboard:view (borrower) |
/company/{companyId}/profile | profile | companyId | provider, borrower |
/company/{companyId}/support | support | companyId | borrower; bloqueado em modo apresentação |
/company/{companyId}/monitoring | (redirect) | companyId | → team/list (não é página) |
/company/{companyId}/company-config | company-config | companyId | → company-config/general |
/company/{companyId}/company-config/general | company-config-general | companyId | borrower |
/company/{companyId}/company-config/identity | company-config-identity | companyId | borrower |
/company/{companyId}/company-config/address | company-config-address | companyId | borrower |
/company/{companyId}/company-config/subscription | company-config-subscription | companyId | borrower |
/company/{companyId}/company-config/permissions | company-config-permissions | companyId | borrower |
/company/{companyId}/company-config/sso | company-config-sso | companyId | borrower |
/company/{companyId}/company-config/security | company-config-security | companyId | borrower |
Contratos (borrower — segmento plural contracts)
| Path | Nome | Params | Permissão / nota |
|---|---|---|---|
/company/{companyId}/contracts | contracts | companyId | contracts:view; → contracts/list |
/company/{companyId}/contracts/list | list-contract | companyId | lista |
/company/{companyId}/contracts/new | new-contract | companyId | contracts:create |
/company/{companyId}/contracts/detail | contract-timeline | companyId | timeline de vigência (sem id). Query ?ids=a,b,c (UUIDs) |
/company/{companyId}/contracts/{id}/detail | contract-detail | companyId, id | detalhe de contrato terminal. Query ?sidebar=info|providers|documents|history|compliance |
/company/{companyId}/contracts/{id} | edit-contract | companyId, id | editor de contrato não-terminal |
/company/{companyId}/contracts/{id}/renewal | contract-renewal | companyId, id | contracts:edit |
/company/{companyId}/contracts/{id}/amendments | contract-amendments | companyId, id | aditivos/distrato/substituição |
/company/{companyId}/contracts/{id}/negotiation | negotiation-detail | companyId, id | negociação/redlining |
/company/{companyId}/contracts/at-risk | contracts-at-risk | companyId | dashboard de renovação |
/company/{companyId}/contracts/legacy | contracts-legacy | companyId | → legacy/list |
/company/{companyId}/contracts/legacy/list | legacy-documents | companyId | documentos legados |
/company/{companyId}/contracts/legacy/incorporate | legacy-incorporate | companyId | incorporar documento |
/company/{companyId}/contracts/legacy/{id} | legacy-document-detail | companyId, id | detalhe legado |
Portal do prestador (provider — segmento singular contract)
Distinto do borrower: role provider, segmento contract (singular).
| Path | Nome | Params |
|---|---|---|
/company/{companyId}/contract/{contractId} | provider-layout | companyId, contractId (→ hours) |
/company/{companyId}/contract/{contractId}/hours | provider-hours | companyId, contractId |
/company/{companyId}/contract/{contractId}/hours/{periodId} | provider-hours-detail | companyId, contractId, periodId |
/company/{companyId}/contract/{contractId}/compliance | provider-compliance | companyId, contractId |
/company/{companyId}/contract/{contractId}/invoices | provider-invoices | companyId, contractId |
/company/{companyId}/contract/{contractId}/history | provider-history | companyId, contractId |
Templates, workflows, compliance, equipe, financeiro, usuários, notas
| Path | Nome | Params | Nota |
|---|---|---|---|
/company/{companyId}/templates/list | list-template | companyId | templates:view (parent templates → list) |
/company/{companyId}/templates/new | new-template | companyId | |
/company/{companyId}/templates/{id} | edit-template | companyId, id | |
/company/{companyId}/templates/{id}/structure | template-structure-builder | companyId, id | |
/company/{companyId}/workflows/list | list-workflow | companyId | workflows:view (parent workflows → list) |
/company/{companyId}/workflows/new | new-workflow | companyId | |
/company/{companyId}/workflows/{id} | edit-workflow | companyId, id | |
/company/{companyId}/workflows/revision/list | revision-workflows | companyId | (+ /new, /{id} = revision-workflow-new/edit) |
/company/{companyId}/workflows/signature/list | signature-workflows | companyId | (+ /new, /{id} = signature-workflow-new/edit) |
/company/{companyId}/compliance/list | list-compliance | companyId | compliance:view (parent compliance → list) |
/company/{companyId}/compliance/new | new-compliance | companyId | |
/company/{companyId}/compliance/{id} | edit-compliance | companyId, id | {id} = cf-<uuid> |
/company/{companyId}/team/list | list-team | companyId | providers:view (Equipe = lista de prestadores) |
/company/{companyId}/team/{id} | team-detail | companyId, id | → entries |
/company/{companyId}/team/{id}/entries | team-detail-entries | companyId, id | horas |
/company/{companyId}/team/{id}/consolidated | team-detail-consolidated | companyId, id | |
/company/{companyId}/team/{id}/compliance | team-detail-compliance | companyId, id | |
/company/{companyId}/team/{id}/history | team-detail-history | companyId, id | |
/company/{companyId}/team/{id}/invoices | team-detail-invoices | companyId, id | |
/company/{companyId}/financial | financial-overview | companyId | reports:view |
/company/{companyId}/financial/providers | financial-providers | companyId | |
/company/{companyId}/financial/providers/{providerId} | financial-provider-detail | companyId, providerId | |
/company/{companyId}/user/list | list-user | companyId | users:view (parent user → list) |
/company/{companyId}/user/{id} | edit-user | companyId, id | |
/company/{companyId}/report | reports-list | companyId | reports:view (segmento singular report) |
/company/{companyId}/invoices/list | list-invoices | companyId | (parent invoices → list) |
/company/{companyId}/invoices/emit | emit-invoice | companyId | |
/company/{companyId}/invoices/{id}/detail | detail-invoice | companyId, id | {id} = cert-### (não-UUID) |
/company/{companyId}/subscriptions/plans | subscription-plans | companyId | memberRoles owner/admin |
/company/{companyId}/subscriptions/success | subscription-success | companyId | memberRoles owner/admin |
Rotas fora do /company/{companyId} (auth / público)
| Path | Nome | Tipo |
|---|---|---|
/auth/login, /auth/register, /auth/recovery, /auth/first-access, /auth/unlock | login, register, ... | guest-only |
/auth/callback/{provider}, /auth/sso/callback | auth-callback, sso-callback | guest-only |
/access | access | requiresLogin (seleção de empresa). Só existe na aplicação principal: num subdomínio de tenant o guard sempre redireciona (ver RN-006) |
/company/new, /company/new/basic|address|privacy | company-new* | requiresLogin (onboarding de empresa) |
/user/onboarding/personal-data|address|terms | user-onboarding-* | requiresLogin |
/public/review/{token}, /public/negotiation/{token}, /public/signing/{token}, /public/onboarding/{token}, /public/validate | external-* | público (portais de token) |
/unauthorized, /{pathMatch} | unauthorized, not-found | público / 404 |
Regras semânticas que a IA deve aplicar
RN-001 — Status do contrato define a rota de detalhe
Ao linkar UM contrato, a rota depende do status (a IA já tem o status no dado que consultou):
- Status terminal →
contract-detail→/company/{companyId}/contracts/{id}/detail. - Status não-terminal →
edit-contract→/company/{companyId}/contracts/{id}(editor).
TERMINAL_STATUSES (src/domain/contract/step-engine.ts): completed, active, finished, overdue, cancelled, archived. Não-terminais: draft, parts, model, documents, in_review, provider, provider_filling, signature, signing. A SPA se autocorrige (redireciona) se a rota não bate com o status, mas a IA deve montar a certa de primeira.
RN-002 — Enum completo de ContractStatus
draft, parts, model, documents, in_review, provider, provider_filling, signature, signing, completed, active, finished, overdue, cancelled, archived (src/domain/contract/types.ts).
RN-003 — Query params que mudam a tela
contract-detail:?sidebar=info|providers|documents|history|compliance.contract-timeline(/contracts/detail, sem id):?ids=a,b,c(UUIDs de contrato separados por vírgula).
RN-004 — Formato de id por entidade
| Entidade | Formato | Exemplo |
|---|---|---|
| Contrato, empresa, membro de equipe, provider | UUID | c1a2b3c4-d5e6-4f7a-8b9c-0d1e2f3a4b5c |
Compliance flow (edit-compliance) | cf-<uuid> | cf-1a2b3c4d-... |
| Provider-compliance | pc-<uuid> / pcf-<uuid> | pc-1a2b3c4d-... |
Nota fiscal (detail-invoice) | cert-### | cert-001 |
Nem todo id é UUID: nunca valide id de compliance/nota fiscal como UUID. O egress do ai-api valida por PROVENIÊNCIA (o id apareceu numa tool/consulta desta sessão), não por formato.
RN-005 — Portal do prestador usa contract (singular)
Borrower = contracts (plural); prestador = contract (singular). São componentes e gates de role diferentes (provider vs borrower). Não confundir.
RN-006 — /access nunca renderiza dentro de um tenant
Num subdomínio de tenant (useTenant().isTenant), o authGuard intercepta a rota access (handleTenantAccessRoute) e redireciona sempre: carrega o /me/access (já escopado ao tenant pelo backend), e então (a) se o usuário é membro da empresa do tenant → dashboard; (b) se é prestador com contrato naquela empresa → provider-hours do contrato; (c) caso contrário → unauthorized. A tela /access (seleção de empresa/contrato) só aparece na aplicação principal (host sem subdomínio). O bloqueio real é do backend (guard de tenant no core); o guard do front é só UX. Ver business/sso-acesso-tenant.md.
Exemplos canônicos
- Feliz: contrato
active→https://app.contrasync.com/company/{companyId}/contracts/{uuid}/detail. - Borda: contrato
draft→ NÃO usar/detail; usarhttps://app.contrasync.com/company/{companyId}/contracts/{uuid}(editor). - Falha: id inventado (ex.:
ctr_001, não veio de tool) → a IA não pode montar o link; manda a lista/company/{companyId}/contractsou mostra os dados na conversa. O egress remove o link caso ela tente.
Regras operacionais de página
- Sem
<h1>na página: o título vem doTopHeaderviameta.titleKey. - Botão voltar automático: rotas de lista marcam
meta.isListPage: true(sem botão); detalhe/edição mostram o botão automaticamente. meta.menuKey: mantém o item do menu lateral ativo em sub-rotas (deve casar com ogroupemListMenu.vue).- Carregamento: navegação assíncrona não-bloqueante com barra fina (naive-ui) via
src/router/routeProgress.ts; recuperação pós-deploy escutandovite:preloadErrorcom um único reload protegido por timestamp.
Observações
- Registros sem
name(montar por path):/company,monitoring(redirect), wrappers definancial/report, parent decompany-config. negotiation-detailé montado direto sob/company/{companyId}e não herdacontracts:view.- Fonte: os 21
router/index.tsdo vue-spa (verificado 2026-07-03).