# 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.*