# PLAN_FRONTEND.md — Front-end single-tenant por tenant

## Descrição geral

Este plano descreve a camada visual do Rota Viva como **front-ends single-tenant, um por tenant**,
que consomem exclusivamente o backend unificado (REST). Cada implantação conhece um único
município (`TENANT_SLUG` + `API_BASE_URL` + `TENANT_TOKEN`) e envia `X-Tenant` +
`Authorization: Bearer` em toda requisição. Abrange as telas de visitante, cadastro de
empreendedor, painéis de análise e gestão municipal; **será implementado como Blade thin-client
dentro dos limites do Laravel 5.5** (Bootstrap 4, CSS próprio só quando necessário). O backend
permanece estritamente stack-agnostic. Inclui ainda requisitos de qualidade
não-funcionais — performance (Core Web Vitals), CSS/theming por tenant, acessibilidade WCAG 2.2 AA,
segurança de front-end (XSS/CSP, token fora de URL/localStorage) e boilerplate semântico — aplicáveis
a todas as telas. O contrato de API e o isolamento estão em `PLAN_BACKEND.md`; o roteiro de execução
está em `EPIC.md` (PLANO F).

## Objetivo

Isolar toda a camada visual — site do visitante, cadastro de empreendedor, painéis de
análise e gestão municipal — em um **front-end único por tenant**, single-tenant, que consome
exclusivamente o backend unificado (REST). Cada tenant implanta sua própria cópia do front-end
em sua estrutura/projeto/domínio, conectada à API.

## Princípio central: single-tenant

O front-end **conhece um único município**. Ele não faz descoberta de tenant: recebe, em
build/runtime, uma configuração com `tenant_slug` (ou `uuid`), a `API_BASE_URL` e o
`TENANT_TOKEN`. Toda requisição envia `X-Tenant` + `Authorization: Bearer <tenant_token>`.

Isso contrasta com o backend multi-tenant: o backend atende N tenants; cada front-end serve 1.

## O que fica no front-end

Todas as views atuais de `resources/views`, reimplementadas como consumidoras da API:

- **Visitante:** `home`, `landing` (descubra/experiências), `catalog`, `detail`, `map`,
  `route-builder` (rota `/criar-rota` — tela anônima, ver endurecimento anti-bot), `route-result`,
  `route-adaptation`, `content` (institucionais).
- **Cadastro:** `entrepreneurs` (formulário de cadastro de empreendedor).
- **Análises:** `admin/dashboard` (indicadores, demanda não atendida, heatmap) — consome
  `/api/v1/gestor/dashboard`.
- **Gestor:** `login`, `admin/module` + `admin/form` (CRUD dos módulos), `admin/appearance`.

## Arquitetura alvo

```
[Deploy por tenant]  app único single-tenant
   config: TENANT_SLUG, API_BASE_URL, TENANT_TOKEN
        │  X-Tenant: <slug>   Authorization: Bearer <tenant_token>
        ▼
   Backend API (PLAN_BACKEND)  ── multi-tenant, restrito
```

Recomendações:
- O front-end **é** um thin-client em **Laravel 5.5** (`resources/views` em Blade + Bootstrap 4,
  CSS próprio só quando estritamente necessário) que consome a API via HTTP. **Não haverá SPA**
  (React/Vue): o Blade renderiza o esqueleto e o JS mínimo faz as chamadas `X-Tenant` +
  `Authorization`. O build usa Laravel Mix (webpack) — não Vite (Vite é Laravel 9+).
- O backend permanece estritamente stack-agnostic (JSON, CORS, sem cookies de sessão), mas o
  front de referência é Blade/Bootstrap 4 dentro dos limites do Laravel 5.5.
- Mantenha um **repositório de referência** do front-end; cada tenant faz fork/deploy com sua
  própria config e tema (aplicado via `tenant/config`).

## Consumo da API (mapeamento das telas)

| Tela atual (Blade) | Chamada à API |
|--------------------|---------------|
| `catalog` / `detail` | `GET /api/v1/catalogos/{section}[/{slug}]` |
| `map` | `GET /api/v1/mapa` |
| `route-builder` (`/criar-rota`) → `route-result` | `POST /api/v1/roteiros` (+ `GET /roteiros/{id}`) — tela anônima, ver endurecimento anti-bot |
| `route-adaptation` | `POST /api/v1/roteiros/{id}/adaptar` (`contextos`: `chuva`, `banheiro`, `cadeirante`, `ar_livre`, …) — ver `FEATURES.md` F-D |
| `entrepreneurs` | `POST /api/v1/empreendedores` |
| tracking de interação | `POST /api/v1/interacoes` |
| `login` (gestor) | `POST /api/v1/auth/login` → token do usuário |
| `admin/dashboard` (análises) | `GET /api/v1/gestor/dashboard` |
| `admin/module` + `form` | CRUD `/api/v1/gestor/{module}[/{id}]` |
| `admin/appearance` | `GET /api/v1/tenant/config` + `PUT /api/v1/gestor/aparencia` |
| branding/inicial | `GET /api/v1/tenant/config` (substitui `View::composer` do `AppServiceProvider`) |

## Configuração por tenant

Arquivo de ambiente do front-end (exemplo):

```dotenv
TENANT_SLUG=lucena
API_BASE_URL=https://api.rotaviva.com
TENANT_TOKEN=tk_live_xxxxxxxx
```

O `tenant/config` devolve nome, cores, logo e textos do município para aplicar o tema no
single-tenant sem rebuild.

## Passos de implementação (resumo)

1. Criar projeto/pacote de front-end desacoplado (ou extrair `resources/views` para um app
   cliente). Definir `TENANT_SLUG` / `API_BASE_URL` / `TENANT_TOKEN`.
2. Criar um cliente HTTP que injeta `X-Tenant` e `Authorization` em toda requisição.
3. Migrar cada tela para buscar dados na API (substituir `view()` + queries Eloquent locais).
4. Mover auth de gestor para fluxo de token (login → armazena token → usa em chamadas protegidas).
5. Replicar painel de análises consumindo `gestor/dashboard` (gráficos no front-end).
6. Aplicar tema via `tenant/config`.
7. Empacotar como template de deploy por tenant (fork + env por domínio).

## Riscos / atenção

- O front-end **não deve** conter lógica de IA nem chaves — apenas consome narrativas prontas.
- Upload de mídia (gestor) precisa de endpoint assinado/proxy no backend; o front só envia o
  arquivo e recebe a URL.
- Manter os rate limits do backend visíveis ao usuário (tratar 429 no front).
- Heatmap/mapa: dados geográficos vêm de `GET /api/v1/mapa`; o front apenas renderiza.
- **`/criar-rota` (criar minha rota) — endurecimento anti-bot:** é o único ponto **anônimo** onde
  a IA é consumida por cidadãos não-identificados, portanto precisa de resiliência **rigorosa** a
  bots. Defesas combinadas: campo honeypot invisível (`_website`/`_url` descartado no back),
  `throttle` restrito (ex.: `throttle:5,1`) por IP, challenge (CAPTCHA/Turnstile) antes da geração,
  checagem de `Sec-Fetch-Site`/`Sec-Fetch-Mode`/`Sec-Fetch-Dest` no backend, bloqueio de user-agents
  e ranges suspeitos, e geração da rota em fila com custo/timeout controlado para evitar abuso da
  IA. Ver `EPIC.md` F-4.

---

## Requisitos de qualidade não-funcionais (front-end)

Seção consolidada a partir dos guias `web_performance_guide`, `css_guide`,
`web_accessibility_guide`, `web_security_guide`, `html_guide`, `php_guide/72` e
`laravel_guide/55`. Aplicam-se a todas as telas (visitante, cadastro, análises, gestor),
independentemente de ser Blade thin client (Laravel 5.5 + Bootstrap 4).

### 1. Performance — Core Web Vitals e carregamento
- **Alvos:** LCP ≤ 2.5s, INP ≤ 200ms, CLS ≤ 0.1; TTFB ≤ 800ms, FCP ≤ 1.8s
  (`web_performance_guide/06-core-web-vitals.md`).
- **LCP:** `fetchpriority="high"` + `loading="eager"` na imagem/cabeçalho do hero e no
  primeiro gráfico do dashboard; nunca `loading="lazy"` no LCP (`04-loading-strategies.md`).
- **INP (maior risco em formulário e dashboards):** debounce em handlers, quebre tarefas
  longas (`scheduler.yield()`/`setTimeout(0)`), mantenha handlers < 5ms (`06-core-web-vitals.md`).
- **CLS:** `width`/`height` (ou `aspect-ratio`) em toda `<img>`/`<iframe>` e em containers de
  gráfico/mapa (`06-core-web-vitals.md`, `09-anti-patterns.md`).
- **Scripts:** `defer` (ou `async` p/ analytics) em todo `<script>`; nunca síncrono no `<head>`;
  JS mínimo por tela, carregado via `defer` (`03-critical-rendering-path.md`).
- **Hints:** `preconnect`/`dns-prefetch` à origem da API e CDN de fontes (≤ 3); `preload` do
  hero/font crítica com `crossorigin` (`04-loading-strategies.md`).
- **Imagens:** `loading="lazy"` + `decoding="async"` abaixo da dobra; `srcset`/`sizes` +
  `<picture>` AVIF→WebP→JPEG (`04-loading-strategies.md`, `14-adaptive-content.md`).
- **Fontes:** `woff2`, `font-display: swap`, `preload` da face crítica (`06-core-web-vitals.md`).
- **CSS crítico inline** no `<head>` (acima da dobra); `content-visibility: auto` em seções
  abaixo da dobra; `contain: layout style` em componentes caros (gráficos)
  (`css_guide/03-performance.md`).
- **Caching/entrega:** Brotli + gzip no edge; assets versionados `immutable`
  (`max-age=31536000`); HTML `no-cache`; JSON autenticado `private`/`no-store`; CDN +
  ETag (`web_performance_guide/15-delivery-and-caching.md`).
- **Adaptivo:** Network Information API e `deviceMemory` para reduzir pacotes/qualidade de
  imagem em `slow-2g`/`2g`/`3g`; `prefers-reduced-data` (`14-adaptive-content.md`).
- **Anti-patterns:** `@import` em CSS, animar `top/left/width/height`, layout thrashing,
  CSR sem SSR no site do visitante, bundle monolítico (`09-anti-patterns.md`).

### 2. CSS e theming por tenant (Bootstrap 4 + CSS mínimo)
- **Bootstrap 4 como base:** usar o grid e utilitários do Bootstrap 4 (`container`, `row`,
  `col-*`, `btn`, `card`, `form-group`, `alert`, `sr-only`, `collapse`) para todo o layout e
  componentes; **CSS próprio só quando estritamente necessário** (sobrescrever variáveis do
  Bootstrap ou regras que ele não cobre). Evitar modernidades sem suporte no alvo (ver abaixo).
- **Tema via variáveis do Bootstrap/Sass + custom properties mínimas:** o `tenant/config` emite
  um payload mínimo de tokens (`--color-primary`, `--color-bg`, `--color-text`, `--color-surface`,
  `--color-border`, `--font-sans`, …) aplicados em `:root`; sobrescrever `$primary`, `$body-bg`,
  `$body-color` etc. no Sass do Bootstrap antes do build, ou via `:root` no runtime (sem rebuild).
- **Variantes de cor:** derivar tons a partir do primário no Sass (`theme-color-level` / `mix()`)
  ou em CSS simples; **não** depender de `color-mix()`/`oklch()` (sem suporte no alvo) — pedir só
  a cor primária à API e derivar o resto localmente.
- **Layout responsivo:** grid do Bootstrap 4 (`col-12 col-md-6 col-lg-4` …) com os breakpoints
  padrão; não usar container queries/`subgrid`/`auto-fill minmax` (sem suporte). `min-height:100vh`
  (ou `100vh` com fallback) em CTAs; `env(safe-area-inset-*)` onde útil.
- **Acessibilidade visual:** anel de foco visível (`.focus`/`:focus`) na cor do tenant;
  `accent-color` onde suportado; skip link; `.sr-only`; texto `line-height: 1.5`, `max-width: 70ch`;
  respeitar `prefers-contrast`/`forced-colors` como piso de contraste.
- **Imagens:** `aspect-ratio` (ou `width`/`height` fixos) + `object-fit: cover`; logo do tenant
  com `object-fit: contain`; ícones inline SVG `aria-hidden` + `stroke: currentColor`.
- **Motion:** animar só `transform`/`opacity`; `@media (prefers-reduced-motion: reduce)` desliga
  animações.
- **Padrões prontos:** reutilizar componentes do Bootstrap 4 (card, table, modal, form, nav, tabs,
  collapse/accordion, toast) em vez de reinventar; adicionar CSS custom só para o que o Bootstrap
  não provê (ex.: skeleton de carregamento, anel de foco do tenant).
- **Print:** `@page A4`, esconder sidebar/botões, expor URLs em relatórios de análise.

### 3. Acessibilidade — WCAG 2.2 AA
- **Alvo: WCAG 2.2 Level AA** (`web_accessibility_guide/12-wcag-quick-reference.md`).
- **HTML semântico:** um `<main>`, landmarks `<header>/<nav>/<footer>/<aside>/<search>`,
  `<section aria-labelledby>`, hierarquia `h1→h2→h3` sem pular níveis, skip link como
  primeiro foco (`01-semantic-html.md`, `03-keyboard-accessibility.md`).
- **Teclado:** `:focus-visible` sempre; `tabindex="0"/"-1"` (nunca positivo); modais via
  `<dialog showModal()>` (trava foco + `inert`); roving tabindex em tabs/menus
  (`03-keyboard-accessibility.md`, `21-keyboard-patterns.md`).
- **Contraste:** texto ≥ 4.5:1 (grande ≥ 3:1), componentes ≥ 3:1; não depender só de cor
  (links com underline, erros com ícone+texto); testar claro/escuro (`04-color-and-contrast.md`).
- **Formulários (cadastro/gestor):** `<label for>` explícita, `fieldset/legend` em grupos,
  `required`+`*` , `aria-invalid` + `aria-describedby`, erro como `role="alert"`, resumo de
  erro no topo com foco; `autocomplete`/`inputmode`; passo de confirmação p/ ação financeira
  (`06-forms-and-labels.md`).
- **Live regions:** `aria-live="polite"` (sucesso/info), `role="alert"` (erro/429); região
  `.sr-only` (não `display:none`); anunciar atualizações de dashboard
  (`05-live-regions.md`).
- **Gráficos/mapa/heatmap:** SVG com `role="img"`+`<title>`+`<desc>`; **tabela de dados
  equivalente** (`<caption>`, `<th scope>`) como alternativa acessível primária; `role="grid"`
  com `aria-rowcount/colcount` se virtualizado (`18-svg-canvas.md`, `16-table-grid-attributes.md`).
- **Motion/epilepsia:** nada com > 3 flashes/s; respeitar `prefers-reduced-motion`; SVGs
  animados precisam de `endElement()` no modo reduzido (`09-seizures-motion.md`).
- **Touch:** alvo ≥ 44×44; `font-size` ≥ 16px em inputs móveis (`08-mobile-accessibility.md`).

### 4. Segurança do front-end
- **Token Bearer em `Authorization`**, nunca em query/hash (vaza em Referer/logs/histórico)
  (`web_security_guide/01-core-defenses.md`). **Não usar `localStorage` por padrão** — preferir
  em memória + refresh token de curta duração; limpar no logout
  (`02-authentication-modern.md`, `08-modern-web-security-model.md`).
- **XSS é a defesa primária do token** (não há HttpOnly): escapar todo dado da API
  (`textContent`, nunca `innerHTML` cru), `DOMPurify` para HTML rico, **CSP estrito (nonce)**
  + **Trusted Types** (`web_security_guide/05-attack-defense-matrix.md`,
  `08-modern-web-security-model.md`).
- **Tema do tenant = dados não confiáveis:** validar/allowlist e aplicar via propriedades de
  estilo, nunca `eval`/raw CSS/HTML (`13-input-validation.md`).
- **Transporte:** HTTPS obrigatório, `Referrer-Policy: strict-origin-when-cross-origin`,
  SRI em scripts de terceiro (`01-core-defenses.md`, `07-practical-implementation.md`).
- **CSRF:** irrelevante com Bearer (não há cookie de sessão); manter checagem `Sec-Fetch-Site`
  no backend (`05-attack-defense-matrix.md`).
- **Formulários:** `autocomplete="new-password"` em criação/senha; `off` em OTP/CVV; uploads
  autenticados, allowlist de extensão, fora do webroot (`12-form-autocompletion.md`).
- **Admin:** proteger por auth/autorização real, **não** listar paths em `robots.txt`;
  usar `X-Robots-Tag: noindex` (`11-robots-txt.md`). Backend deve enviar
  `frame-ancestors 'none'` + `X-Frame-Options: DENY` (clickjacking)
  (`07-practical-implementation.md`).

### 5. HTML semântico / boilerplate
- `<!DOCTYPE html>`, `<html lang="pt-BR">`, `<meta charset="UTF-8">`,
  `<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">`,
  `<title>`/`description` por página (`html_guide/09_BOILERPLATE.md`).
- Landmarks + skip link; `<search>` para busca/filtros; `<article>` para cards; `aria-current`
  no item ativo (`02_SEMANTIC_STRUCTURE.md`, `04_ACCESSIBILITY.md`).
- **Forms:** `required`/`minlength`/`pattern`/`type`, `aria-describedby` + `role="alert"`,
  `datalist`/`select` com opção vazia, `enterkeyhint`/`inputmode`
  (`11_CONSTRAINT_VALIDATION.md`, `18_GLOBAL_ATTRIBUTES.md`).
- **Performance HTML:** `defer`/`async`; `loading`/`fetchpriority`; `srcset`/`<picture>`;
  resource hints no `<head>` (`06_PERFORMANCE.md`).
- **Segurança HTML:** sem segredos no markup; CSP via header (fallback `<meta>`); SRI em CDN;
  `rel="noopener noreferrer"` em `target="_blank"`; `sandbox` em iframes de terceiro
  (`05_SECURITY.md`).
- OG/Twitter cards para páginas de experiências; `<time datetime>` em eventos (`09_BOILERPLATE.md`).

### 6. Camada PHP (thin client Laravel) — se o front permanecer em Blade
- **Sem segredos/lógica:** Blade só renderiza; `TENANT_TOKEN`/API keys só em `.env`/`config`,
  nunca impressos; token via `Authorization` (não cookie); não desabilitar CSRF das rotas `web`
  (`laravel_guide/55/09-blade.md`, `03-seguranca.md`).
- **Escapar saída:** `{{ }}` auto-escapa (XSS); nunca `{!! !!}` para dados da API; usar `@json`
  para passar dados ao JS (`09-blade.md`).
- **Config por tenant:** `config/tenant.php` lendo `env('TENANT_SLUG'|'API_BASE_URL'|'TENANT_TOKEN')`;
  acessar via `config('tenant.*')`; `.env` no `.gitignore`; `APP_KEY` gerado
  (`07-configurabilidade-estrutural.md`).
- **Entrada:** Form Requests com allowlist; `filter_var`/`filter_input` se usar superglobais;
  `hash_equals` para comparações sensíveis; nunca `call_user_func` com nome vindo do usuário
  (`php_guide/72/22-input-superglobals.md`, `05-seguranca-baseline.md`).
- **Erros/logs:** `APP_DEBUG=false`; `display_errors=0`; handler global loga+500 genérico;
  **nunca logar token/PII** (só status + correlation id)
  (`php_guide/72/24-errors-exceptions.md`, `laravel_guide/55/04-logs-erros.md`).
- **Performance:** `php artisan view:cache` + `config:cache` + `route:cache` no deploy;
  `composer install --optimize-autoloader`; **OPcache** (`opcache.validate_timestamps=0`);
  `Cache::remember` para branding/nav (`laravel_guide/55/05-performance.md`).
- **Estrutura:** controllers magros (validam → chamam `TenantApiClient` → view); cliente HTTP
  centralizado em `app/Services`; `Route::view` para estáticas; `old()` em formulários
  (`laravel_guide/55/06-boas-praticas.md`).
- **Cabeçalhos de segurança** via middleware global (`X-Frame-Options`, `X-Content-Type-Options`,
  `Referrer-Policy`, HSTS, CSP) (`laravel_guide/55/03-seguranca.md`).
- *Nota:* o front segue estritamente os limites do **Laravel 5.5** (ver
  `GUIDES/laravel_guide/55/SKILL.md`): usar `{{ csrf_field() }}` (não `@csrf`),
  `config/app.php` (não `config/logging.php`), `Hash::make` (bcrypt, não Argon),
  `Route::resource` (não `apiResource`), e evitar arrow functions / typed properties / union
  types (PHP 7.2). Manter os princípios de segurança/performance.
