# Auditoria Técnica — AgenciaPsi MVP
**Data:** 2026-03-11
**Stack:** Vue 3 · PrimeVue · Supabase · PostgreSQL · FullCalendar
**Modelo:** claude-sonnet-4-6

---

## 1. Visão Geral Arquitetural

**Pontos fortes:**
- Estrutura feature-based bem definida (`features/agenda`, `features/patients`)
- Separação correta: repository → composable → page
- Multi-tenancy é first-class: `tenant_id` em todos os queries críticos
- Sistema de guards robusto: RBAC + entitlements + tenantFeatures + session race-condition handling
- `useRecurrence` é bem arquitetado: virtual occurrences no frontend + exceções no banco (sem N linhas futuras)

---

## 2. Bugs Críticos

### ✅ [RESOLVIDO] `useRecurrence.js` — variável `occurrenceCount` não declarada

**Bug original:** branches `custom_weekdays`, `monthly` e `yearly` usavam `occurrenceCount` sem declará-la → `ReferenceError` em runtime. Nenhum dos três contava ocorrências anteriores ao range, então `max_occurrences` nunca funcionava corretamente.

**Correção (Sessão 2):** Cada branch ganhou `let occurrenceCount = 0` + fase de pré-contagem de `ruleStart` até `effStart`.

**Resolvido em:** Sessão 2 — 2026-03-11

---

### ✅ [RESOLVIDO] Exceção de remarcação fora do range não aparece

**Bug original:** `loadExceptions` só buscava `original_date` no range. Se `original_date` estivesse fora mas `new_date` caísse dentro, a sessão remarcada não aparecia.

**Correção (Sessão 3):**
- `loadExceptions`: duas queries paralelas — `q1` (original_date no range) + `q2` (reschedule com new_date no range). Mescladas e deduplicadas por `id`.
- `expandRules` post-pass: itera exceções não consumidas (`handledExIds`), injeta inbound reschedules com `buildOccurrence(rule, newDate, ex.original_date, ex)`.

**Resolvido em:** Sessão 3 — 2026-03-11

---

## 3. Segurança

### ✅ [RESOLVIDO] SQL dumps no repositório

**Arquivos:** `schema.sql`, `backup.sql`, `data_dump.sql`, `full_dump.sql`

Verificado via `git log --all --full-history` — arquivos **nunca foram commitados**. Movidos para pasta externa ao repositório. Nenhuma purga de histórico necessária.

**Resolvido em:** Sessão 3 — 2026-03-11

---

### ✅ [RESOLVIDO] `useAgendaEvents` — sem `tenant_id` em nenhuma operação

**Correção (Sessão 2):** `tenant_id` injetado em `create()`, `loadMyRange()`, `update()`, `remove()`, `removeSeriesFrom()`, `removeAllSeries()`. Helpers `assertTenantId()` e `getUid()` adicionados.

**Resolvido em:** Sessão 2 — 2026-03-11

---

### ✅ [RESOLVIDO] `loadRules` em `useRecurrence` sem filtro `tenant_id`

**Correção (Sessão 2):** `loadRules` e `loadAndExpand` aceitam `tenantId` opcional e aplicam `.eq('tenant_id', tenantId)`. Call site em `AgendaTerapeutaPage._reloadRange` passa `tenantStore.activeTenantId`.

**Resolvido em:** Sessão 2 — 2026-03-11

---

### ✅ [RESOLVIDO] `console.log` expõe dados de pacientes no browser

**Correção (Sessão 2):** Todos os `console.*` substituídos pelo `supportLogger`. Logs só aparecem quando modo suporte está ativo (token válido no banco).

**Resolvido em:** Sessão 2 — 2026-03-11
**Arquivos criados:** `src/support/supportLogger.js`, `src/support/supportDebugStore.js`

---

### 🟡 [ABERTO] `window.__guardsBound` / `window.__supabaseAuthListenerBound`

Usar `window.*` para controle de listeners é frágil em hot-reload.
**Solução:** Gerenciar via módulo singleton ou `app.config.globalProperties`.

---

### ✅ [RESOLVIDO] `globalRole` do `profiles` sem cache no guard

Adicionados `globalRoleCacheUid` + `globalRoleCache` no `guards.js`.
Cache invalida em: uid change, SIGNED_OUT, SIGNED_IN com user diferente.
Query ao banco ocorre apenas na primeira navegação por sessão.

**Resolvido em:** Sessão 4 — 2026-03-11

---

## 4. Duplicações e Inconsistências

### ✅ [RESOLVIDO] Dois composables para a mesma entidade

`src/composables/useAgendaEvents.js` era código morto (sem imports). Deletado.
O autoritativo `src/features/agenda/composables/useAgendaEvents.js` permanece.

**Resolvido em:** Sessão 3 — 2026-03-11

---

### ✅ [RESOLVIDO] Dois mappers para agenda

`src/features/agenda/domain/agenda.mappers.js` estava vazio e sem imports. Deletado.
`src/features/agenda/domain/agenda.types.js` também sem imports. Deletado. Diretório `domain/` removido.
O autoritativo `src/features/agenda/services/agendaMappers.js` permanece.

**Resolvido em:** Sessão 4 — 2026-03-11

---

### ✅ [RESOLVIDO] N+1 Query — migração `paciente_id` → `patient_id`

**Resolvido em:** Sessão 3 — 2026-03-11

**Migration executada:** `migrations/unify_patient_id.sql`
- UPDATE copiou `paciente_id` → `patient_id` onde null (resultado: 0 órfãos — todos já tinham `patient_id`)
- `ALTER TABLE agenda_eventos DROP COLUMN paciente_id` executado com sucesso

**Código atualizado:**
- `useAgendaEvents.js`: `paciente_id` removido do `BASE_SELECT`; `create()`/`update()` stripam `paciente_id` do payload
- `agendaRepository.js`: workaround N+1 de orphan ids removido
- `agendaMappers.js`: `paciente_id` agora é alias de `patient_id` (UI only)
- `AgendaTerapeutaPage.vue` + `AgendaClinicaPage.vue`: `pickDbFields` usa `patient_id`
- `AgendamentosRecebidosPage.vue`: `dbFields` removeu `paciente_id`
- `PatientsListPage.vue` + `AgendaEventDialog.vue`: `.or()` → `.eq('patient_id', id)`

---

## 5. Limpeza Necessária

### ✅ [RESOLVIDO] Template Sakai removido — bundle de produção

**Resolvido em:** Sessão 3 — 2026-03-11

**Removidos:** `src/views/uikit/` (15 arquivos), `src/views/utilities/Blocks.vue`, `src/components/BlockViewer.vue`, `src/components/FloatingConfigurator.vue`, `src/views/pages/Documentation.vue`, `src/assets/demo/`, `src/navigation/menus/sakai.demo.menu.js`, `src/router/routes.demo.js`, `src/assets/styles.scss` (@use demo removido)

**Referências limpas:** `package.json` renomeado para `agenciapsi`, demoRoutes e sakaiDemoMenu removidos dos index files, `FloatingConfigurator` removido de Login, NotFound, Access, Error, ResetPasswordPage.

---

### 🟡 [PARCIAL] Arquivos obsoletos no projeto

**Deletados (Sessão 4):**
- `src/layout/ConfiguracoesPage-old.vue`
- `src/features/agenda/domain/` (diretório inteiro — 2 arquivos não usados)

**Ainda presentes:**
- `src/layout/ConfiguracoesPage - Copia.vue` — verificar se está no git (staged como D)
- `src/views/pages/public/Landingpage-v1 - bkp.vue`
- `comandos.txt` (na raiz)

---

### ✅ [RESOLVIDO] Logs excessivos em produção

`console.time/timeLog/timeEnd/warn/error` em `guards.js` substituídos por `logGuard()`, `logError()`, `logPerf()`.

**Resolvido em:** Sessão 2 — 2026-03-11

---

## 6. Status das Features do MVP

| Feature | Status | Observação |
|---|---|---|
| Agenda de sessões | ✅ Implementado | FullCalendar + composables |
| Cadastro de pacientes | ✅ Implementado | CRUD completo |
| Recorrência de sessões | ✅ Corrigido | Bugs de occurrenceCount e cross-range resolvidos |
| Sessões presenciais/online | ✅ Implementado | campo `modalidade` |
| Controle de faltas | ✅ Implementado | `exception_type = 'patient_missed'` |
| Remarcação | ✅ Corrigido | Bug cross-range resolvido (Sessão 3) |
| Bloqueio de agenda | ✅ Implementado | `BloqueioDialog.vue` |
| Agendamento online | ✅ Implementado | `AgendadorPublicoPage.vue` |
| Prontuário | ✅ Integrado | Seção "Sessões" adicionada ao `PatientProntuario.vue` |
| Notificações/lembretes | ❌ Não implementado | Sem trigger/edge function |
| Financeiro/faturamento | ⚠️ Parcial | Páginas de plano mas sem sessão→pagamento |
| Relatórios | ✅ Implementado | `RelatoriosPage.vue` — terapeuta — sessões, faltas, taxa, gráfico |

---

## 7. Backlog Técnico

- [ ] Cache de `globalRole` no guard (reduzir queries por navegação)
- [ ] Implementar notificações: WhatsApp/Email via Supabase Edge Functions
- [ ] Integração prontuário ↔ sessões
- [ ] Integração sessão ↔ pagamento (financeiro)
- [ ] Relatórios básicos: sessões realizadas, faltas, receita
- [ ] Migrar domínio de recorrência para TypeScript
- [ ] Consolidar dois mappers de agenda (`agendaMappers.js` vs `domain/agenda.mappers.js`)
- [ ] Remover arquivos obsoletos (ConfiguracoesPage-old, Landingpage-v1 bkp, etc.)

---

## 8. Prioridades de Ação

### ✅ Fazer AGORA — todos concluídos
1. `[x]` ~~Remover dumps SQL~~ → nunca commitados, movidos para fora do repo
2. `[x]` ~~Corrigir bug `occurrenceCount`~~ → pré-contagem em todos os branches
3. `[x]` ~~Adicionar `tenant_id` ao `useAgendaEvents` e `loadRules`~~ → injetado em todas as operações
4. `[x]` ~~Remover `console.log` com dados de pacientes~~ → `supportLogger`

### ✅ Fazer em seguida — todos concluídos
5. `[x]` ~~Corrigir bug remarcação cross-range~~ → 2 queries + post-pass em `expandRules`
6. `[x]` ~~Consolidar dois `useAgendaEvents`~~ → legado deletado
7. `[x]` ~~Unificar `paciente_id` + `patient_id`~~ → migration executada + código limpo
8. `[x]` ~~Remover Sakai de demo~~ → removido + menu SaaS limpo

### Backlog
9. `[x]` Cache de `globalRole` no guard — `globalRoleCacheUid/globalRoleCache` em guards.js
10. `[ ]` Notificações (WhatsApp/Email via Edge Functions) ← próximo
11. `[x]` Integração prontuário ↔ sessões — seção "Sessões" em `PatientProntuario.vue`
12. `[x]` Relatórios básicos — `RelatoriosPage.vue` em /therapist/relatorios
13. `[x]` Consolidar mappers de agenda — `domain/` deletado, `agendaMappers.js` é único

---

## 9. Sistema de Suporte Técnico SaaS

Sistema seguro para admins SaaS acessarem a agenda de terapeutas em modo debug.

| Arquivo | Responsabilidade |
|---|---|
| `migrations/support_sessions.sql` | Tabela, índices, RLS, RPCs (token gerado via `gen_random_uuid()` duplo — sem pgcrypto) |
| `src/support/supportLogger.js` | Logger centralizado — silencioso fora do modo suporte |
| `src/support/supportDebugStore.js` | Store Pinia — valida token via RPC `validate_support_session` |
| `src/support/supportSessionService.js` | CRUD de sessões de suporte (criar/listar/revogar) |
| `src/support/components/SupportDebugBanner.vue` | Banner fixo na agenda com painel de logs filtráveis |
| `src/views/pages/saas/SaasSupportPage.vue` | Painel SaaS para gerenciar sessões de suporte |

**RPCs no banco:**
- `create_support_session(p_tenant_id, p_ttl_minutes)` → `{ token, expires_at, session_id }`
- `validate_support_session(p_token)` → `{ valid, tenant_id }`
- `revoke_support_session(p_token)` → `boolean`

---

## 10. Histórico de Sessões

### Sessão 1 — 2026-03-11
- Auditoria técnica completa gerada
- Nenhum item resolvido

### Sessão 2 — 2026-03-11
- Sistema de suporte técnico SaaS implementado (migration + 5 arquivos criados)
- Bug `occurrenceCount` corrigido (itens 2 e 4)
- `tenant_id` adicionado ao `useAgendaEvents` e `loadRules` (item 3)
- `console.*` substituídos por `supportLogger`

### Sessão 4 — 2026-03-11
- Cache `globalRole` adicionado ao guard (item 9) — sem query ao banco por navegação
- Integração prontuário ↔ sessões (item 11) — painel "Sessões" em `PatientProntuario.vue`
- `RelatoriosPage.vue` criada em `/therapist/relatorios` (item 12) — cards, gráfico Chart.js, tabela DataTable
- Consolidação mappers (item 13) — `domain/agenda.mappers.js` vazio deletado + `agenda.types.js` + dir `domain/`
- `ConfiguracoesPage-old.vue` deletado (limpeza)

### Sessão 3 — 2026-03-11
- Bug remarcação cross-range resolvido (item 5)
- `logPerf is not defined` em guards.js corrigido
- `pgcrypto` → substituído por `gen_random_uuid()` duplo no support_sessions
- Sakai demo removido completamente (item 8) + `styles.scss` corrigido
- `useAgendaEvents` legado deletado (item 6)
- `paciente_id` unificado em `patient_id` — migration executada (item 7)
- SQL dumps confirmados como nunca commitados (item 1 encerrado)

---

*Para retomar: devolva este arquivo ao início da conversa e indique qual item quer atacar.*
