# Registro de entregas

Histórico por data de referência (migrations, entrega ou correção). Para cada lote: visão do produto e inventário de ficheiros.

**Snapshots:**

- [estado-entregas-e-pendencias-2026-05-19.md](./estado-entregas-e-pendencias-2026-05-19.md) — histórico 2026-05-19
- [pendente-commit-perfil-auth-provider-2026-05-20.md](./pendente-commit-perfil-auth-provider-2026-05-20.md) — **pendente agora** (perfil PT + `auth_provider`)

**Fonte da verdade:** `git log`, `git diff`, `git status`.

**Índice por data (mais recente primeiro):**

| Data | Lote | Estado em `main` |
|------|------|------------------|
| [2026-05-26](#2026-05-26--áreas-baterias-exclusão-em-lote-e-performance-bi) | Áreas (métricas + bulk delete), perguntas na bateria, índices BI | **Parcial** — commits `02f112a`/`4c24a53` + migration/queries **staged** — [lote-treinamento-areas-performance-2026-05-26.md](./lote-treinamento-areas-performance-2026-05-26.md) |
| [2026-05-22 (b)](#2026-05-22-b--baterias-import-progresso-e-título) | Baterias + escopo N-N + usuários multi-área/grupo | **Pendente commit** — [lote-baterias-import-progresso-2026-05-22.md](./lote-baterias-import-progresso-2026-05-22.md) |
| [2026-05-22](#2026-05-22--avatar-portal-login-slogan-dashboard-progresso) | Avatar portal + crop, login slogan/wires, dashboard progresso Excel | **Pendente commit** — [lote-portal-avatar-login-dashboard-2026-05-22.md](./lote-portal-avatar-login-dashboard-2026-05-22.md) |
| [2026-05-20 (d)](#2026-05-20-d--import-fila-histórico-listagem-usuários-bi-paginado) | Import fila, histórico, listagem usuários, exclusão lote, BI paginado | **Pendente commit** — [lote-usuarios-import-fila-dashboard-2026-05-20.md](./lote-usuarios-import-fila-dashboard-2026-05-20.md) |
| [2026-05-20](#2026-05-20--perfil-pt--auth_provider-microsoft) | Perfil PT, ocultar excluir conta/senha MS, `auth_provider` | **Pendente commit** (doc separado) |
| [2026-05-19 (c)](#2026-05-19-c--zerar-respostas-da-bateria-admin-global) | Zerar respostas (testes), Admin global | Commitado (`9b1196c`) |
| [2026-05-19](#2026-05-19--bi-dashboard--ui-portal-questionário-game) | BI dashboard + UI portal/questionário | Commitado (`af118d8` + anteriores) |
| [2026-05-19 (b)](#2026-05-19-b--login-jogar-modais-resultado-ranking) | Login, jogar, modal erro, resultado ranking | Commitado (`308e9fe`) |
| [2026-05-18](#2026-05-18--upload-imagens-gd-iccp) | Upload favicon/logo — GD libpng iCCP (500 produção) |
| [2026-05-18](#2026-05-18--correção-scan-segurança) | Correção SCAN (Axios + clickjacking) |
| [2026-05-21](#2026-05-21--tagline-da-experiência-game) | Tagline splash (questionário game) |
| [2026-05-20](#2026-05-20--configuração-da-experiência-game--portal-questionário) | Configuração game + fluxo portal questionário |
| [2026-05-16](#2026-05-16--setor-obrigatório-em-grupos-de-treinamento) | `sector_id` em `training_groups` |
| [2026-05-15](#2026-05-15--grupos-de-treinamento-e-vínculo-com-baterias) | Grupos de treinamento |
| [2026-05-14](#2026-05-14--baterias-de-perguntas-áreas-grupos-e-usuários) | Baterias, áreas, grupos, perfil participante |

---

## 2026-05-26 — Áreas, baterias, exclusão em lote e performance BI

**Registro:** **2026-05-26** — commits `02f112a` (exclusão/confirm/bulk) e `4c24a53` (métricas áreas + total perguntas); **staged:** migration de índices + queries agregadas no BI.

Documentação completa: [lote-treinamento-areas-performance-2026-05-26.md](./lote-treinamento-areas-performance-2026-05-26.md).

### Visão do produto

| Funcionalidade | Descrição |
|----------------|-----------|
| Áreas — listagem | Utilizadores, perguntas e progresso por área |
| Áreas — exclusão | Confirmação unitária e em lote (checkboxes) |
| Baterias | Total de perguntas na listagem e destaque no detalhe |
| BI — baterias | Bloco de progresso por bateria no `/dashboard` + export Excel (`dashboard.batteries.export`) |
| BI / sessões | Subqueries correlacionadas substituídas por agregações + `JOIN` |
| Base de dados | Índices em respostas, sessões, `trainee_profiles` e `users` (migration idempotente; não remove índice usado por FK) |

### Comandos pós-deploy

```bash
php artisan migrate
php artisan test --filter='SectorDeleteTest|QuestionBatteryImportTest|UserBulkDeleteTest|TrainingDashboardTest'
```

---

## 2026-05-20 (d) — Import fila, histórico, listagem usuários, BI paginado

**Registro:** **2026-05-20** — **pendente** na working tree.

Documentação completa: [lote-usuarios-import-fila-dashboard-2026-05-20.md](./lote-usuarios-import-fila-dashboard-2026-05-20.md).

### Visão do produto

| Funcionalidade | Descrição |
|----------------|-----------|
| Import em fila | Job `ProcessUserBulkImportJob`, progresso na UI, cancelar, relatório XLSX |
| Histórico de importações | Modal com data/hora, responsável, status, link relatório |
| Listagem usuários | Filtros, busca, ordenação, paginação (`IndexUsersRequest`) |
| Exclusão em lote | Por área ou grupo, com pré-visualização |
| Dashboard progresso | Busca, ordenação e paginação na tabela de participantes |
| Dashboard ranking/pódio | `/dashboard/ranking` — nomes, abas geral/área/grupo; SQL MySQL 5.7+ |

### Comandos pós-deploy

```bash
php artisan migrate
npm run build            # CSS do ranking no BI (dashboard-ranking.css)
php artisan queue:work   # obrigatório para processar importações
php artisan test --filter='UserBulkImportTest|UserIndexListingTest|UserBulkDeleteTest|TrainingDashboardTest'
```

---

## 2026-05-22 (b) — Baterias, escopo N-N e usuários multi-área/grupo

**Registro:** **2026-05-22** — **pendente** na working tree.

Documentação completa: [lote-baterias-import-progresso-2026-05-22.md](./lote-baterias-import-progresso-2026-05-22.md).

### Visão do produto

| Funcionalidade | Descrição |
|----------------|-----------|
| Progresso na listagem | % iniciaram, barra, iniciaram / pendentes / total na bateria |
| Escopo N-N | «VMA E VMR» = duas áreas; pivôs bateria/usuário |
| Import multi-bateria | Um XLSX → várias baterias; auto-criação área/grupo |
| Import planilha | Prefixo alternativas, correta A–D, tags «e»/«-» |
| Editar título bateria | PATCH no detalhe |
| Criar/editar usuário | Multiselect várias áreas e grupos |
| Import usuários | Mesma regra N-N na planilha |

### Comandos pós-deploy

```bash
php artisan migrate
php artisan test --filter='QuestionBatteryImportTest|QuestionBatteryRepositoryProgressStatsTest|BatteryQuestionAlternativeTextTest|BatteryQuestionTagsTest|BatteryScopeLabelsTest|UserTraineeGroupUpdateTest'
```

---

## 2026-05-22 — Avatar portal + login slogan + dashboard progresso

**Registro:** **2026-05-22** — **pendente** na working tree.

Documentação completa: [lote-portal-avatar-login-dashboard-2026-05-22.md](./lote-portal-avatar-login-dashboard-2026-05-22.md).

### Visão do produto

| Funcionalidade | Descrição |
|----------------|-----------|
| Foto de perfil | Modal no 1.º acesso, crop 1:1, Meu perfil, avatar no topbar |
| Login guest | Slogan do game, wires, logo animada mobile |
| Dashboard | Tabela progresso por participante + export Excel |
| Modal UI | Centrado na viewport |

### Comandos pós-deploy

`php artisan migrate` · `php artisan storage:link` · `npm run build`

---

## 2026-05-20 — Perfil PT + auth_provider Microsoft

**Registro:** **2026-05-20** — **pendente** na working tree (não commitado).

Documentação: [pendente-commit-perfil-auth-provider-2026-05-20.md](./pendente-commit-perfil-auth-provider-2026-05-20.md).

### Visão do produto

| Funcionalidade | Descrição |
|----------------|-----------|
| Meu perfil PT | Dados do perfil, alterar senha, Salvar — sem inglês Breeze |
| Sem excluir conta | Bloco removido (portal + gestão) |
| `users.auth_provider` | `password` \| `microsoft` — por utilizador |
| Login Microsoft | Marca `microsoft` no onboarding e no callback; perfil sem senha |
| Segurança | `PUT /password` → 403 para `microsoft` |
| Locale | Default `pt`; `lang/pt.json` para `__()` em auth/nav |

### Migration

`2026_05_20_120000_add_auth_provider_to_users_table.php`

### Verificação

```bash
php artisan migrate
php artisan test --filter=ProfileLayoutContextTest
php artisan test --filter=ProfilePasswordChangeTest
php artisan test --filter=MicrosoftOAuthPortalLoginTest
```

### Mensagem de commit sugerida

```
feat(auth): auth_provider Microsoft e perfil em português
```

---

## 2026-05-19 (c) — Zerar respostas da bateria (Admin global)

**Registro:** **2026-05-19** — ferramenta de **QA/testes** na tela de detalhe da bateria (gestão). **Commitado** (`9b1196c`).

Documentação: [estado-entregas-e-pendencias-2026-05-19.md](./estado-entregas-e-pendencias-2026-05-19.md).

### Visão do produto

| Funcionalidade | Descrição |
|----------------|-----------|
| Onde | `GET training/question-batteries/{id}` — bloco “Testes — zerar respostas” |
| Autorização | `QuestionBatteryPolicy::resetParticipantResponses` — Admin global + mesma empresa da bateria |
| Zerar todos | `POST` com `scope=all` — apaga todas as sessões da bateria |
| Zerar um | `POST` com `scope=user` + `user_id` — apaga sessão do participante |
| Efeito | Participante pode voltar a jogar do zero (`ensureInProgressSession` cria sessão nova) |

### Rotas

| Método | Caminho | Nome |
|--------|---------|------|
| POST | `/training/question-batteries/{question_battery}/reset-participant-responses` | `training.question-batteries.reset-participant-responses` |

### Inventário — criados

- `app/Http/Requests/Training/ResetQuestionBatteryParticipantResponsesRequest.php`
- `app/Services/QuestionnaireBatteryParticipantResetService.php`
- `resources/views/training/question-batteries/_participant-response-reset.blade.php`
- `tests/Feature/Training/QuestionBatteryParticipantResponseResetTest.php`

### Inventário — alterados

- `app/Http/Controllers/Training/QuestionBatteryController.php`
- `app/Policies/QuestionBatteryPolicy.php`
- `app/Repositories/Contracts/QuestionnaireParticipantSessionRepositoryInterface.php`
- `app/Repositories/Eloquent/QuestionnaireParticipantSessionRepository.php`
- `resources/views/training/question-batteries/show.blade.php`
- `routes/web.php`

### Verificação

```bash
php artisan test --filter=QuestionBatteryParticipantResponseResetTest
```

### Mensagem de commit sugerida

```
feat(training): zerar respostas da bateria para Admin global (testes)
```

---

## 2026-05-19 — BI dashboard + UI portal questionário game

**Registro:** **2026-05-19** — **commitado** em `main` (`af118d8` e commits relacionados). Sem migrations novas neste lote.

Documentação detalhada:

- [bi-dashboard.md](./bi-dashboard.md) — painel, KPIs, rotas, testes.
- [portal-questionario-ui-game.md](./portal-questionario-ui-game.md) — hub, botão 3D, triângulos splash, portal shell, Vite.
- [lote-ui-login-jogar-resultado-2026-05-19.md](./lote-ui-login-jogar-resultado-2026-05-19.md) — login, jogar, modais, resultado com ranking (lote complementar).

### Visão do produto

#### A) BI — painel de gestão (`/dashboard`)

| Funcionalidade | Descrição |
|----------------|-----------|
| KPIs | Total participantes, sem login, começou e não finalizou (com links para listagens quando total &gt; 0) |
| Filtros | Período, status do participante, setor, grupo, bateria |
| Rankings e gráficos | Por time, perguntas com mais acerto/erro, alternativas erradas frequentes, sessões em progresso |
| Relatório | `/dashboard/relatorio` — visão consolidada para impressão |
| Listagens | `/dashboard/participantes`, `/sem-login`, `/nao-finalizaram` |
| Último login | Listener `RecordUserLastLoginAt` no evento `Login` → `users.last_login_at` (KPI “sem login”) |

**Arquitetura:** `DashboardController` → `TrainingDashboardService` → `TrainingDashboardRepository` (+ `DashboardRequest`, enums de filtro).

#### B) UI — portal e experiência game

| Funcionalidade | Descrição |
|----------------|-----------|
| Menu portal | Sidebar/topbar com tema primário e ícones estilo game |
| Hub questionário | Card escuro, layout coluna, CSS `portal-questionnaire-hub.css` |
| Botão INICIAR 3D | CSS partilhado `game-start-button.css`; fino no hub, gordinho na splash/pausa/modal |
| Splash | Triângulos wireframe em SVG (`splash-wire-corner`), grandes e espaçados; botão responsivo no mobile |
| Fonte | Cinzel no layout `questionnaire-game` |
| Ranking home | Pódio/lista refinados; badge **“Você”** |
| Admin mobile | `min-w-0` no layout e componentes de página para evitar scroll horizontal no dashboard |

### Rotas novas (gestão)

| Método | Caminho | Nome |
|--------|---------|------|
| GET | `/dashboard` | `dashboard` |
| GET | `/dashboard/relatorio` | `dashboard.report` |
| GET | `/dashboard/participantes` | `dashboard.participants` |
| GET | `/dashboard/sem-login` | `dashboard.participants-without-login` |
| GET | `/dashboard/nao-finalizaram` | `dashboard.in-progress` |

Middleware: `verified`, `dashboard.access`.

### Dependências front

- **Chart.js** (+ adapter) em `package.json` — gráficos do dashboard via `resources/js/dashboard-charts.js`.

### Inventário — arquivos **criados**

**Backend BI**

- `app/Enums/TrainingDashboardParticipantStatusFilter.php`
- `app/Enums/TrainingDashboardPeriodFilter.php`
- `app/Http/Controllers/DashboardController.php`
- `app/Http/Requests/Dashboard/DashboardRequest.php`
- `app/Listeners/RecordUserLastLoginAt.php`
- `app/Repositories/Contracts/TrainingDashboardRepositoryInterface.php`
- `app/Repositories/Eloquent/TrainingDashboardRepository.php`
- `app/Services/TrainingDashboardService.php`
- `tests/Feature/Dashboard/TrainingDashboardTest.php`

**Views dashboard**

- `resources/views/dashboard/_filters.blade.php`
- `resources/views/dashboard/_kpi-link.blade.php`
- `resources/views/dashboard/_team-ranking.blade.php`
- `resources/views/dashboard/_question-rankings.blade.php`
- `resources/views/dashboard/_question-charts.blade.php`
- `resources/views/dashboard/_wrong-alternatives.blade.php`
- `resources/views/dashboard/_in-progress-sessions.blade.php`
- `resources/views/dashboard/_report-nav.blade.php`
- `resources/views/dashboard/report.blade.php`
- `resources/views/dashboard/reports/participants.blade.php`
- `resources/views/dashboard/reports/in-progress.blade.php`
- `resources/js/dashboard-charts.js`

**UI portal / questionário**

- `resources/css/game-start-button.css`
- `resources/css/portal-questionnaire-hub.css`
- `resources/css/battery-ranking.css` (pódio/lista; pode estar só no lote b)
- `resources/views/components/portal/game-button.blade.php`
- `resources/views/components/questionnaire/splash-wire-corner.blade.php`
- `resources/views/portal/questionnaire/_experience-error-modal.blade.php` (lote b)

**Documentação**

- `docs/bi-dashboard.md`
- `docs/portal-questionario-ui-game.md`
- `docs/lote-ui-login-jogar-resultado-2026-05-19.md` (lote b)

### Inventário — arquivos **alterados**

- `app/Providers/AppServiceProvider.php` — binding `TrainingDashboardRepository`; `Event::listen(Login::class, RecordUserLastLoginAt::class)`
- `routes/web.php` — rotas do dashboard
- `resources/views/dashboard.blade.php` — composição do painel
- `resources/css/questionnaire-game.css` — wires SVG, import botão, splash `btn-wrap`
- `resources/views/layouts/admin.blade.php`, `portal.blade.php`, `portal-sidebar.blade.php`, `portal-topbar.blade.php`, `questionnaire-game.blade.php`
- `resources/views/portal/questionnaire/welcome.blade.php`, `experience-splash.blade.php`, `experience-play.blade.php`
- `resources/views/components/portal/battery-ranking.blade.php`
- `resources/views/components/page/card.blade.php`, `header.blade.php`
- `package.json`, `package-lock.json`, `vite.config.js`
- `tests/Feature/Portal/PortalHomeRankingTest.php`
- `docs/README.md` — links para BI e UI game

### Nota — lote iCCP

`GdImageFactory` e normalizers — **commitado** em `52f3e55`. Ver [correcao-upload-imagens-gd-iccp.md](./correcao-upload-imagens-gd-iccp.md).

### Verificação (referência histórica)

```bash
php artisan test
php artisan test --filter=TrainingDashboard
php artisan test --filter=PortalHomeRankingTest
npm ci && npm run build
```

**QA manual:** [portal-questionario-ui-game.md §6](./portal-questionario-ui-game.md#6-verificação-manual) e KPIs/links em [bi-dashboard.md](./bi-dashboard.md).

### Mensagem de commit sugerida

```
feat: BI dashboard de treinamento e UI game no portal/questionário

- Painel /dashboard com KPIs, filtros, gráficos, relatório e listagens
- Registo de last_login_at no login para KPI sem login
- Hub e splash com botão 3D partilhado, triângulos SVG no splash
- Shell do portal e ranking com refinamentos visuais; correções mobile
```

---

## 2026-05-19 (b) — Login, jogar, modais, resultado, ranking

**Registro:** **2026-05-19** — **commitado** em `main` (`308e9fe`).

Documentação: [lote-ui-login-jogar-resultado-2026-05-19.md](./lote-ui-login-jogar-resultado-2026-05-19.md).

### Visão do produto

| Funcionalidade | Descrição |
|----------------|-----------|
| Login `/login` | Logo e favicon do cadastro; fundo **gradiente game** (single-tenant); botão com cor primária; logo maior |
| Jogar | Barra de progresso; ícone de relógio no cronómetro; pergunta/alternativas em blocos; botão **Confirmar** 3D |
| Modal de erro | Igual fluxo do acerto: overlay, “Não foi desta vez”, botão Continuar/Ver resultado |
| Resultado | Resumo (acertos, pontos, tempo) + **pódio e top 10**; sem lista pergunta a pergunta |
| Ranking home | Visual game refinado (`battery-ranking.css`), espaçamento do pódio |

### Inventário — arquivos **criados**

- `resources/views/portal/questionnaire/_experience-error-modal.blade.php`
- `resources/css/battery-ranking.css`
- `docs/lote-ui-login-jogar-resultado-2026-05-19.md`

### Inventário — arquivos **alterados**

**Backend**

- `app/Services/PortalAppearanceSettingService.php` — `themeForGuestLogin`, `themeFromModel`
- `app/Services/PortalBatteryRankingService.php` — `topTenForBattery`
- `app/Http/Controllers/Portal/PortalQuestionnaireController.php` — ranking no resultado
- `app/Http/Controllers/Auth/AuthenticatedSessionController.php` — tema no login
- `app/Http/Middleware/SharePortalBranding.php` — `questionnaireGameTheme`, `guestLoginHasCompanyBranding`
- `app/Repositories/Contracts/PortalAppearanceSettingRepositoryInterface.php` — `findUniquePortalAppearanceOrNull`
- `app/Repositories/Eloquent/PortalAppearanceSettingRepository.php` — implementação

**Views / CSS**

- `resources/views/layouts/guest.blade.php`
- `resources/views/layouts/questionnaire-game.blade.php` — Font Awesome
- `resources/views/auth/login.blade.php`
- `resources/views/portal/questionnaire/experience-play.blade.php`
- `resources/views/portal/questionnaire/experience-result.blade.php`
- `resources/views/components/portal/battery-ranking.blade.php`
- `resources/views/portal/home.blade.php`
- `resources/css/questionnaire-game.css`
- `resources/css/portal-questionnaire-hub.css` — import `battery-ranking.css`

**Testes**

- `tests/Feature/Auth/AuthenticationTest.php`
- `tests/Feature/Portal/QuestionnaireParticipantSessionTest.php`

**Docs**

- `docs/portal-questionario-ui-game.md`
- `docs/deploy-producao-questionario-experiencia.md` (se actualizado)
- `docs/README.md`

### Verificação

```bash
php artisan test --filter=AuthenticationTest
php artisan test --filter=QuestionnaireParticipantSessionTest
php artisan test --filter=PortalHomeRankingTest
npm run build
```

### Mensagem de commit sugerida (lote b, ou unificar com 2026-05-19)

```
feat(portal): login com branding, modal de erro e resultado com ranking

- Login: logo/favicon e fundo gradiente game (single-tenant)
- Jogar: barra de progresso, relógio no HUD, confirmar 3D
- Modal de erro; resultado com resumo + pódio em vez de lista por pergunta
- battery-ranking.css partilhado (home + resultado)
```

---

## Upload imagens GD iCCP

**Registro:** **2026-05-18** — correção de erro 500 em produção ao enviar favicon ou logo em Configurações → Aparência do portal (`PUT /settings/portal-appearance`).

### Problema

- Mensagem: `imagecreatefrompng(): gd-png: libpng warning: iCCP: known incorrect sRGB profile`
- PNGs comuns de design tools; warning do libpng convertido em `ErrorException` no ambiente Hypera.

### Solução

- Nova classe `App\Support\GdImageFactory` — carrega PNG/JPEG/GIF/WebP ignorando avisos benignos do GD.
- `PortalFaviconNormalizer` e `RasterLogoBackgroundNormalizer` passam a usar a factory.

### Inventário — arquivos **criados**

- **`app/Support/GdImageFactory.php`** — carregamento GD com handler temporário para warnings iCCP/gd-*.
- **`tests/Unit/Support/GdImageFactoryTest.php`** — teste unitário (extensão GD).
- **`docs/correcao-upload-imagens-gd-iccp.md`** — documentação da correção (sintoma, causa, deploy, QA).

### Inventário — arquivos **alterados**

- **`app/Support/PortalFaviconNormalizer.php`** — `createImageResource()` → `GdImageFactory::fromPath()`.
- **`app/Support/RasterLogoBackgroundNormalizer.php`** — idem.
- **`docs/deploy-producao-questionario-experiencia.md`** — secção 9 e QA.
- **`docs/README.md`** — link para doc da correção.

### Verificação

1. Deploy do código (sem migration).
2. Reenviar o PNG que falhava em produção.
3. `php artisan test --filter=GdImageFactoryTest`

---

## Correção SCAN

**Registro:** **2026-05-18** — correções após relatório de vulnerabilidades (Qualys ou equivalente) nas rotas públicas `/login` e `/forgot-password` do ambiente Hypera em produção.

### Achados tratados

| ID / título (scan) | Severidade | CVE / CWE | URLs afetadas | Correção aplicada |
|--------------------|------------|-----------|---------------|-------------------|
| **151079** Axios Denial of Service | **HIGH** | CVE-2026-25639, CWE-754 | `/login` (bundle JS) | Axios **≥ 1.13.5** (`package.json` `^1.13.5`; lock resolve **1.16.1**) |
| **150124** Clickjacking — Framable Page | **MEDIUM** | CWE-1021, CWE-693, OWASP A6 | `/login`, `/forgot-password` | Middleware `PreventClickjacking` em todo o grupo `web` |
| **150112** Sensitive form field autocomplete | **LOW** | CWE-200, OWASP A5 | `/login` (campo `password`) | `autocomplete="off"` no `<form>` e nos inputs sensíveis (login, reset, confirm) |
| **150123** Cookie without HttpOnly | **LOW** | CWE-1004, OWASP A5 | `/login` (`XSRF-TOKEN`) | `App\Http\Middleware\ValidateCsrfToken` — cookie CSRF com `http_only` da sessão |

### Detalhe técnico — Axios (CVE-2026-25639)

- **Problema:** `mergeConfig` do Axios falha com `TypeError` se a config receber `__proto__` como propriedade própria (ex.: via `JSON.parse` malicioso), causando DoS no processo que usa a lib.
- **Versões afetadas:** &lt; **1.13.5**.
- **Uso no projeto:** `resources/js/bootstrap.js` (defaults + header `X-Requested-With`); risco operacional baixo sem passar config controlada pelo utilizador, mas o scan deteta a versão no bundle.
- **Deploy:** após `npm ci`, executar `npm run build` — `public/build/` está no `.gitignore`; produção precisa do build atualizado.

### Detalhe técnico — Clickjacking

- **Problema:** páginas sem header anti-iframe podem ser embutidas em site malicioso; cliques do utilizador podem ir para formulários reais (login / reset de senha).
- **Solução:** em cada resposta web:
  - `X-Frame-Options: SAMEORIGIN`
  - `Content-Security-Policy: frame-ancestors 'self'`

### Detalhe técnico — Autocomplete (150112)

- **Problema:** o scanner exige `autocomplete="off"` em campos sensíveis; `current-password` / `username` continuam a ser sinalizados.
- **Solução:** `autocomplete="off"` no formulário e nos inputs de e-mail/senha em `resources/views/auth/login.blade.php`, `forgot-password`, `reset-password`, `confirm-password`.
- **Nota:** navegadores modernos podem ignorar `off` em passwords; o objetivo principal aqui é fechar o achado do scan.

### Detalhe técnico — XSRF-TOKEN HttpOnly (150123)

- **Problema:** o Laravel envia por defeito `XSRF-TOKEN` **sem** `HttpOnly` (legível via `document.cookie`).
- **Solução:** middleware `App\Http\Middleware\ValidateCsrfToken` substitui o do framework e define `http_only` igual ao da sessão (`config/session.php`, default `true`).
- **CSRF em AJAX:** continua via `<meta name="csrf-token">` e header `X-CSRF-TOKEN` (já usado no projeto); não depende do cookie legível por JS.

### Inventário — arquivos **criados** (correção SCAN)

- **`app/Http/Middleware/PreventClickjacking.php`** — Aplica os headers anti-iframe na resposta, após `$next($request)`.

- **`app/Http/Middleware/ValidateCsrfToken.php`** — Cookie `XSRF-TOKEN` com flag `HttpOnly`.

- **`tests/Feature/Security/ClickjackingProtectionTest.php`** — Headers anti-frame, autocomplete no login e `HttpOnly` no `XSRF-TOKEN`.

### Inventário — arquivos **alterados** (correção SCAN)

- **`bootstrap/app.php`** — `appendToGroup('web', PreventClickjacking::class)`; `replaceInGroup` do `ValidateCsrfToken`.

- **`package.json`** — `axios`: `^1.11.0` → `^1.13.5`.

- **`package-lock.json`** — Resolução para `axios@1.16.1` (e dependências transitivas atualizadas pelo `npm install`).

- **`resources/views/auth/login.blade.php`**, **`forgot-password.blade.php`**, **`reset-password.blade.php`**, **`confirm-password.blade.php`** — `autocomplete="off"`.

### Verificação pós-deploy (scan)

1. `curl -sI https://<domínio>/login | grep -iE 'x-frame-options|content-security-policy'`
2. Confirmar que o JS servido em `/build/assets/*` não reporta Axios &lt; 1.13.5 (re-scan ou inspeção do manifest após build).

---

## 2026-05-21 — Tagline da experiência game

**Registro:** **2026-05-21**.

### Visão

- Campo **`splash_tagline`** em `questionnaire_game_settings` (texto da splash em ecrã cheio; suporta negrito com `**texto**`).
- Valor por omissão em `config/questionnaire-game.php` quando a empresa não define.

### Migração

- **`database/migrations/2026_05_21_100000_add_splash_tagline_to_questionnaire_game_settings.php`**

### Inventário — alterados (relacionados)

- **`app/Models/QuestionnaireGameSetting.php`**, **`app/Services/QuestionnaireGameSettingService.php`**, **`app/Support/QuestionnaireGameTheme.php`**, **`app/Http/Requests/Settings/UpdateQuestionnaireGameSettingRequest.php`**, **`resources/views/settings/portal-appearance/_questionnaire-game-tab.blade.php`**, **`config/questionnaire-game.php`**.

---

## 2026-05-20 — Configuração da experiência game + portal questionário

**Registro:** **2026-05-20** (base settings); fluxo portal e telas game na mesma entrega funcional.

### Visão do produto

- **Configurações → Aparência do portal** passa a ter abas: portal clássico + **Questionário (game)** (cores, imagens, textos da experiência imersiva).
- **Portal participante** (`/portal/questionario/*`): boas-vindas, splash/play da experiência, responder perguntas da bateria publicada para o par **setor (área) + grupo** do participante, correção imediata na sessão (sem persistência de tentativas/BI neste lote).
- Layout dedicado **`questionnaire-game`**, CSS em `resources/css/questionnaire-game.css`, entrada no **`vite.config.js`**.

### Modelo de dados

- **`questionnaire_game_settings`** — `company_id`, cores, paths de imagens (storage), `splash_tagline` (migration 2026-05-21).

### Rotas (portal, autenticado + `portal.context` + `verified`)

| Método | Caminho | Nome |
|--------|---------|------|
| GET | `/portal/questionario` | `portal.questionnaire.index` |
| GET | `/portal/questionario/experiencia` | `portal.questionnaire.experience` |
| GET | `/portal/questionario/experiencia/jogar` | `portal.questionnaire.experience.play` |
| GET | `/portal/questionario/responder` | `portal.questionnaire.take` |
| POST | `/portal/questionario/responder` | `portal.questionnaire.submit` |

### Rotas (gestão)

- `PUT /settings/questionnaire-game` → `settings.questionnaire-game.update`

### Inventário — arquivos **criados**

**Persistência e domínio**

- **`database/migrations/2026_05_20_140000_create_questionnaire_game_settings_table.php`**
- **`app/Models/QuestionnaireGameSetting.php`**
- **`app/Repositories/Contracts/QuestionnaireGameSettingRepositoryInterface.php`**
- **`app/Repositories/Eloquent/QuestionnaireGameSettingRepository.php`**
- **`app/Services/QuestionnaireGameSettingService.php`** — CRUD de tema, upload de imagens, `themeForExperience()`.
- **`app/Support/QuestionnaireGameTheme.php`** — Value object / defaults a partir do model ou config.
- **`config/questionnaire-game.php`** — Textos default (tagline, linhas do brasão).

**HTTP (gestão e portal)**

- **`app/Http/Controllers/Settings/QuestionnaireGameSettingController.php`**
- **`app/Http/Requests/Settings/UpdateQuestionnaireGameSettingRequest.php`**
- **`app/Policies/QuestionnaireGameSettingPolicy.php`**
- **`app/Http/Controllers/Portal/PortalQuestionnaireController.php`**
- **`app/Http/Requests/Portal/StorePortalQuestionnaireSubmissionRequest.php`**
- **`app/Services/PortalQuestionnaireService.php`** — Resolve bateria por `company_id` + `sector_id` + `training_group_id`; `grade()` das respostas.

**Views e assets**

- **`resources/views/layouts/questionnaire-game.blade.php`**
- **`resources/css/questionnaire-game.css`**
- **`resources/views/portal/questionnaire/welcome.blade.php`**
- **`resources/views/portal/questionnaire/experience-splash.blade.php`**
- **`resources/views/portal/questionnaire/experience-play.blade.php`**
- **`resources/views/portal/questionnaire/experience-result.blade.php`**
- **`resources/views/portal/questionnaire/take.blade.php`**
- **`resources/views/portal/questionnaire/result.blade.php`**
- **`resources/views/settings/portal-appearance/_portal-tab.blade.php`**
- **`resources/views/settings/portal-appearance/_questionnaire-game-tab.blade.php`**

**Testes**

- **`tests/Feature/Settings/QuestionnaireGameSettingTest.php`**
- **`tests/Feature/Portal/PortalQuestionnaireTest.php`**
- **`tests/Unit/Support/QuestionnaireGameThemeTest.php`**

### Inventário — arquivos **alterados** (portal questionário / game)

- **`app/Http/Controllers/Settings/PortalAppearanceController.php`** — Abas portal + questionário game na edição.
- **`resources/views/settings/portal-appearance/edit.blade.php`** — Composição por partials das abas.
- **`resources/views/layouts/portal-sidebar.blade.php`** — Link para questionário no menu portal.
- **`routes/web.php`** — Rotas portal questionário e `settings/questionnaire-game`.
- **`app/Providers/AppServiceProvider.php`** — Bindings dos repositórios novos.
- **`app/Providers/AuthServiceProvider.php`** — Policies `QuestionnaireGameSetting`, `QuestionBattery`, `TrainingGroup`.
- **`vite.config.js`** — Input CSS `questionnaire-game.css`.

---

## 2026-05-16 — Setor obrigatório em grupos de treinamento

**Registro:** **2026-05-16**.

### Visão

- **`training_groups.sector_id`** (FK `sectors`, mesma noção de **Área**): grupo de treinamento fica ligado a um setor; alinha import de baterias, onboarding Microsoft e participante no portal.

### Migração

- **`database/migrations/2026_05_16_120000_add_sector_id_to_training_groups.php`**

### Inventário — alterados (principais)

- **`app/Models/TrainingGroup.php`**, **`app/Services/TrainingGroupService.php`**, **`app/Repositories/Eloquent/TrainingGroupRepository.php`**
- **`app/Http/Requests/Training/StoreTrainingGroupRequest.php`**, **`UpdateTrainingGroupRequest.php`**
- **`resources/views/training/groups/_form.blade.php`**
- **`app/Services/MicrosoftPortalOnboardingService.php`** — Validação área + grupo contra cadastros existentes.
- **`resources/views/auth/microsoft-portal-complete.blade.php`**

---

## 2026-05-15 — Grupos de treinamento e vínculo com baterias

**Registro:** **2026-05-15**.

### Visão

- Tabela **`training_groups`**; FK opcional em **`question_batteries`** e **`trainee_profiles.training_group_id`**.
- CRUD gestão: `training/groups` (permissão `menu.training.groups`).

### Migração

- **`database/migrations/2026_05_15_100000_create_training_groups_and_link_question_batteries.php`**

### Inventário — arquivos **criados**

- **`app/Models/TrainingGroup.php`**
- **`database/factories/TrainingGroupFactory.php`**
- **`app/Repositories/Contracts/TrainingGroupRepositoryInterface.php`**
- **`app/Repositories/Eloquent/TrainingGroupRepository.php`**
- **`app/Services/TrainingGroupService.php`**
- **`app/Http/Controllers/Training/TrainingGroupController.php`**
- **`app/Http/Requests/Training/StoreTrainingGroupRequest.php`**, **`UpdateTrainingGroupRequest.php`**, **`DestroyTrainingGroupRequest.php`**
- **`app/Policies/TrainingGroupPolicy.php`**
- **`resources/views/training/groups/`** — `index`, `create`, `edit`, `_form`
- **`tests/Feature/Users/UserTraineeGroupUpdateTest.php`**

---

## 2026-05-14 — Baterias de perguntas, áreas, grupos e usuários

**Registro:** **2026-05-14** (migrations e módulo treinamento); integrações de utilizador e menu na mesma entrega.

### Visão do produto

- Import **XLSX/XLS** de baterias (`QuestionBattery` → `BatteryQuestion` → `BatteryQuestionAlternative`).
- **Áreas** = setores (`training/areas` → model `Sector`).
- **Grupos** e **baterias** com escopo `company_id`; participante usa `sector_id` + `training_group_id` para ver bateria publicada.
- Formulário de **usuários** e **import em lote** com grupo de treinamento; `TraineeProfile.training_group_id`.

Documentação detalhada de regras de import: [plataforma-treinamento.md](./plataforma-treinamento.md#baterias-de-perguntas-import-xlsx).

### Migrações

- **`database/migrations/2026_05_14_100000_create_question_batteries_table.php`**
- **`database/migrations/2026_05_14_100100_create_battery_questions_table.php`**
- **`database/migrations/2026_05_14_100200_create_battery_question_alternatives_table.php`**

### Inventário — arquivos **criados** (2026-05-14)

**Enums, models, repositórios, services**

- **`app/Enums/QuestionBatteryStatus.php`**
- **`app/Models/QuestionBattery.php`**, **`BatteryQuestion.php`**, **`BatteryQuestionAlternative.php`**
- **`app/Repositories/Contracts/QuestionBatteryRepositoryInterface.php`**, **`BatteryQuestionRepositoryInterface.php`**, **`BatteryQuestionAlternativeRepositoryInterface.php`**
- **`app/Repositories/Eloquent/QuestionBatteryRepository.php`**, **`BatteryQuestionRepository.php`**, **`BatteryQuestionAlternativeRepository.php`**
- **`app/Services/QuestionBatteryImportService.php`**
- **`app/Policies/QuestionBatteryPolicy.php`**

**HTTP treinamento**

- **`app/Http/Controllers/Training/QuestionBatteryController.php`**
- **`app/Http/Controllers/Training/TrainingAreaController.php`** — CRUD setores como “áreas”.
- **`app/Http/Requests/Training/StoreQuestionBatteryImportRequest.php`**
- **`resources/views/training/question-batteries/`** — `index`, `show`, `import`
- **`resources/views/training/areas/`** — `index`, `create`, `edit`

**Testes e fixture**

- **`tests/Feature/Training/QuestionBatteryImportTest.php`**
- **`tests/Fixtures/question_battery_minimal.xlsx`**

### Inventário — arquivos **alterados** (2026-05-14 e integrações)

- **`app/Enums/MenuPermission.php`** — `menu.training`, `menu.training.question_batteries`, `menu.training.areas`, `menu.training.groups`.
- **`app/Roles/Catalog/GestorOperacoesRoleDefinition.php`** — Permissões de treinamento.
- **`resources/views/layouts/sidebar.blade.php`** — Menu Treinamento.
- **`routes/web.php`** — Grupo `training/*`.
- **`app/Models/TraineeProfile.php`** — `training_group_id`.
- **`app/Http/Controllers/UserController.php`**, **`app/Http/Requests/Users/StoreUserRequest.php`**, **`UpdateUserRequest.php`**
- **`app/Services/UserService.php`**, **`UserBulkImportService.php`**
- **`resources/views/users/_form.blade.php`**, **`create.blade.php`**, **`edit.blade.php`**
- **`tests/Feature/Users/UserBulkImportTest.php`**
- **`app/Repositories/Contracts/SectorRepositoryInterface.php`**, **`SectorRepository.php`**, **`SectorService.php`**, **`SectorPolicy.php`**
- **`resources/views/inventory/sectors/_form.blade.php`**
- **`app/Support/AccessRules.php`**
- **`tests/Feature/Auth/MicrosoftOAuthPortalLoginTest.php`**
- **`docs/plataforma-treinamento.md`** — Secção baterias e resumo atualizado.
- **`composer.lock`** — Dependências PHP (ex.: PhpSpreadsheet já no projeto).

---

## Anexo — lista completa pendente de commit (`git status`)

Legenda: `??` criado · ` M` alterado.

### Criados (`??`)

```
app/Enums/QuestionBatteryStatus.php
app/Http/Controllers/Portal/PortalQuestionnaireController.php
app/Http/Controllers/Settings/QuestionnaireGameSettingController.php
app/Http/Controllers/Training/QuestionBatteryController.php
app/Http/Controllers/Training/TrainingAreaController.php
app/Http/Controllers/Training/TrainingGroupController.php
app/Http/Middleware/PreventClickjacking.php
app/Http/Requests/Portal/StorePortalQuestionnaireSubmissionRequest.php
app/Http/Requests/Settings/UpdateQuestionnaireGameSettingRequest.php
app/Http/Requests/Training/DestroyTrainingGroupRequest.php
app/Http/Requests/Training/StoreQuestionBatteryImportRequest.php
app/Http/Requests/Training/StoreTrainingGroupRequest.php
app/Http/Requests/Training/UpdateTrainingGroupRequest.php
app/Models/BatteryQuestion.php
app/Models/BatteryQuestionAlternative.php
app/Models/QuestionBattery.php
app/Models/QuestionnaireGameSetting.php
app/Models/TrainingGroup.php
app/Policies/QuestionBatteryPolicy.php
app/Policies/QuestionnaireGameSettingPolicy.php
app/Policies/TrainingGroupPolicy.php
app/Repositories/Contracts/BatteryQuestionAlternativeRepositoryInterface.php
app/Repositories/Contracts/BatteryQuestionRepositoryInterface.php
app/Repositories/Contracts/QuestionBatteryRepositoryInterface.php
app/Repositories/Contracts/QuestionnaireGameSettingRepositoryInterface.php
app/Repositories/Contracts/TrainingGroupRepositoryInterface.php
app/Repositories/Eloquent/BatteryQuestionAlternativeRepository.php
app/Repositories/Eloquent/BatteryQuestionRepository.php
app/Repositories/Eloquent/QuestionBatteryRepository.php
app/Repositories/Eloquent/QuestionnaireGameSettingRepository.php
app/Repositories/Eloquent/TrainingGroupRepository.php
app/Services/PortalQuestionnaireService.php
app/Services/QuestionBatteryImportService.php
app/Services/QuestionnaireGameSettingService.php
app/Services/TrainingGroupService.php
app/Support/QuestionnaireGameTheme.php
config/questionnaire-game.php
database/factories/TrainingGroupFactory.php
database/migrations/2026_05_14_100000_create_question_batteries_table.php
database/migrations/2026_05_14_100100_create_battery_questions_table.php
database/migrations/2026_05_14_100200_create_battery_question_alternatives_table.php
database/migrations/2026_05_15_100000_create_training_groups_and_link_question_batteries.php
database/migrations/2026_05_16_120000_add_sector_id_to_training_groups.php
database/migrations/2026_05_20_140000_create_questionnaire_game_settings_table.php
database/migrations/2026_05_21_100000_add_splash_tagline_to_questionnaire_game_settings.php
resources/css/questionnaire-game.css
resources/views/layouts/questionnaire-game.blade.php
resources/views/portal/questionnaire/ (6 blades)
resources/views/settings/portal-appearance/_portal-tab.blade.php
resources/views/settings/portal-appearance/_questionnaire-game-tab.blade.php
resources/views/training/ (areas, groups, question-batteries)
tests/Feature/Portal/PortalQuestionnaireTest.php
tests/Feature/Security/ClickjackingProtectionTest.php
tests/Feature/Settings/QuestionnaireGameSettingTest.php
tests/Feature/Training/QuestionBatteryImportTest.php
tests/Feature/Users/UserTraineeGroupUpdateTest.php
tests/Fixtures/question_battery_minimal.xlsx
tests/Unit/Support/QuestionnaireGameThemeTest.php
```

### Alterados (` M`)

```
app/Enums/MenuPermission.php
app/Http/Controllers/Settings/PortalAppearanceController.php
app/Http/Controllers/UserController.php
app/Http/Requests/Users/StoreUserRequest.php
app/Http/Requests/Users/UpdateUserRequest.php
app/Models/TraineeProfile.php
app/Policies/SectorPolicy.php
app/Providers/AppServiceProvider.php
app/Providers/AuthServiceProvider.php
app/Repositories/Contracts/SectorRepositoryInterface.php
app/Repositories/Eloquent/SectorRepository.php
app/Roles/Catalog/GestorOperacoesRoleDefinition.php
app/Services/MicrosoftPortalOnboardingService.php
app/Services/SectorService.php
app/Services/UserBulkImportService.php
app/Services/UserService.php
app/Support/AccessRules.php
bootstrap/app.php
composer.lock
docs/plataforma-treinamento.md
package.json
package-lock.json
resources/views/auth/microsoft-portal-complete.blade.php
resources/views/inventory/sectors/_form.blade.php
resources/views/layouts/portal-sidebar.blade.php
resources/views/layouts/sidebar.blade.php
resources/views/settings/portal-appearance/edit.blade.php
resources/views/users/_form.blade.php
resources/views/users/create.blade.php
resources/views/users/edit.blade.php
routes/web.php
tests/Feature/Auth/MicrosoftOAuthPortalLoginTest.php
tests/Feature/Users/UserBulkImportTest.php
vite.config.js
```

### Fora do commit (gerados localmente)

- **`public/build/`** — produzido por `npm run build` (`.gitignore`); obrigatório no deploy para refletir Axios atualizado e CSS do questionário game.

---

## Comandos sugeridos antes do commit

```bash
php artisan migrate
php artisan test
npm ci && npm run build
```

---

## Manutenção

Após cada commit relevante: atualizar a data no topo da secção correspondente, mover o lote do “pendente” para histórico ou arquivar entrada; manter **Correção SCAN** atualizada se surgirem novos achados.
