From 89b4ecaba1d8fb3bc017ee33869f7aaa621973d8 Mon Sep 17 00:00:00 2001 From: Leonardo Date: Sun, 22 Feb 2026 17:56:01 -0300 Subject: [PATCH] Ajuste em Massa - Paciente, Terapeuta, Clinica e Admin - Inicio agenda --- O-que-foi-feito.txt | 101 - checklist-novo-chat.txt | 47 - package-lock.json | 178 + package.json | 7 + src/App.vue | 33 +- src/app/session.js | 2 +- src/components/agenda/PausasChipsEditor.vue | 246 ++ src/constants/roles.js | 48 +- .../components/AdicionarCompromissoPage.vue | 1004 +++++ .../agenda/components/AgendaCalendar.vue | 10 + .../components/AgendaClinicCalendar.vue | 100 + .../agenda/components/AgendaClinicMosaic.vue | 192 + .../agenda/components/AgendaEventDialog.vue | 243 ++ .../agenda/components/AgendaRightPanel.vue | 13 + .../agenda/components/AgendaToolbar.vue | 115 + .../agenda/components/ConflictBanner.vue | 0 .../agenda/components/PreviewTimeline.vue | 0 .../cards/AgendaNextSessionsCardList.vue | 12 + .../components/cards/AgendaPulseCardGrid.vue | 12 + .../composables/useAgendaClinicStaff.js | 24 + .../agenda/composables/useAgendaEvents.js | 106 + .../agenda/composables/useAgendaLimits.js | 0 .../composables/useAgendaPermissions.js | 0 .../agenda/composables/useAgendaQuery.js | 0 .../agenda/composables/useAgendaSettings.js | 24 + src/features/agenda/domain/agenda.mappers.js | 0 src/features/agenda/domain/agenda.types.js | 0 .../agenda/pages/AgendaClinicaPage.vue | 288 ++ .../agenda/pages/AgendaTerapeutaPage.vue | 449 +++ .../agenda/services/agenda.service.js | 0 src/features/agenda/services/agendaMappers.js | 146 + .../agenda/services/agendaRepository.js | 98 + .../patients/PatientsListPage.vue} | 38 +- .../cadastro/PatientsCadastroPage.vue | 262 +- .../cadastro/PatientsExternalLinkPage.vue | 30 +- .../recebidos/CadastrosRecebidosPage.vue | 372 +- .../patients}/grupos/GruposPacientesPage.vue | 69 +- .../prontuario/PatientProntuario.vue | 150 +- .../patients}/tags/TagsPage.vue | 7 +- src/layout/AppMenu.vue | 21 +- src/layout/AppMenuFooterPanel.vue | 10 +- src/layout/AppMenuItem.vue | 2 +- src/layout/AppTopbar.vue | 9 +- .../configuracoes/ConfiguracoesAgendaPage.vue | 3468 +++++++++++------ src/navigation/index.js | 94 +- src/navigation/menus/admin.menu.js | 157 +- .../menus/{patient.menu.js => portal.menu.js} | 24 +- src/navigation/menus/saas.menu.js | 32 +- src/navigation/menus/therapist.menu.js | 37 +- src/router/guards.js | 147 +- src/router/index.js | 169 +- src/router/router.me.js | 32 - src/router/routes.account.js | 23 + src/router/routes.admin.js | 137 +- ...ter.configuracoes.js => routes.configs.js} | 2 +- src/router/routes.features.js | 26 + src/router/routes.patient.js | 19 - src/router/routes.portal.js | 27 + src/router/routes.public.js | 9 +- src/router/routes.therapist.js | 124 +- .../supabase_patient_index_page.sql | 2 +- src/stores/tenantFeaturesStore.js | 69 + src/stores/tenantStore.js | 185 +- src/views/pages/HomeCards.vue | 206 +- .../ProfilePage.vue} | 0 .../pages/admin/agenda/MyAppointmentsPage.vue | 9 + .../pages/admin/agenda/NewAppointmentPage.vue | 9 + .../pages/admin/clinic/ClinicFeaturesPage.vue | 330 ++ .../admin/clinic/ClinicProfessionalsPage.vue | 1113 ++++++ src/views/pages/auth/Login.vue | 93 +- src/views/pages/billing/UpgradePage.vue | 111 +- .../PortalDashboard.vue} | 0 .../portal/agenda/MyAppointmentsPage.vue | 9 + .../portal/agenda/NewAppointmentPage.vue | 9 + src/views/pages/public/AcceptInvitePage.vue | 270 ++ .../therapist/agenda/MyAppointmentsPage.vue | 9 + .../therapist/agenda/NewAppointmentPage.vue | 9 + 77 files changed, 9433 insertions(+), 1995 deletions(-) delete mode 100644 O-que-foi-feito.txt delete mode 100644 checklist-novo-chat.txt create mode 100644 src/components/agenda/PausasChipsEditor.vue create mode 100644 src/features/agenda/components/AdicionarCompromissoPage.vue create mode 100644 src/features/agenda/components/AgendaCalendar.vue create mode 100644 src/features/agenda/components/AgendaClinicCalendar.vue create mode 100644 src/features/agenda/components/AgendaClinicMosaic.vue create mode 100644 src/features/agenda/components/AgendaEventDialog.vue create mode 100644 src/features/agenda/components/AgendaRightPanel.vue create mode 100644 src/features/agenda/components/AgendaToolbar.vue create mode 100644 src/features/agenda/components/ConflictBanner.vue create mode 100644 src/features/agenda/components/PreviewTimeline.vue create mode 100644 src/features/agenda/components/cards/AgendaNextSessionsCardList.vue create mode 100644 src/features/agenda/components/cards/AgendaPulseCardGrid.vue create mode 100644 src/features/agenda/composables/useAgendaClinicStaff.js create mode 100644 src/features/agenda/composables/useAgendaEvents.js create mode 100644 src/features/agenda/composables/useAgendaLimits.js create mode 100644 src/features/agenda/composables/useAgendaPermissions.js create mode 100644 src/features/agenda/composables/useAgendaQuery.js create mode 100644 src/features/agenda/composables/useAgendaSettings.js create mode 100644 src/features/agenda/domain/agenda.mappers.js create mode 100644 src/features/agenda/domain/agenda.types.js create mode 100644 src/features/agenda/pages/AgendaClinicaPage.vue create mode 100644 src/features/agenda/pages/AgendaTerapeutaPage.vue create mode 100644 src/features/agenda/services/agenda.service.js create mode 100644 src/features/agenda/services/agendaMappers.js create mode 100644 src/features/agenda/services/agendaRepository.js rename src/{views/pages/admin/pacientes/PatientsIndexPage.vue => features/patients/PatientsListPage.vue} (96%) rename src/{views/pages/admin/pacientes => features/patients}/cadastro/PatientsCadastroPage.vue (90%) rename src/{views/pages/admin/pacientes => features/patients}/cadastro/PatientsExternalLinkPage.vue (93%) rename src/{views/pages/admin/pacientes => features/patients}/cadastro/recebidos/CadastrosRecebidosPage.vue (71%) rename src/{views/pages/admin/pacientes => features/patients}/grupos/GruposPacientesPage.vue (91%) rename src/{views/pages/admin/pacientes => features/patients}/prontuario/PatientProntuario.vue (89%) rename src/{views/pages/admin/pacientes => features/patients}/tags/TagsPage.vue (99%) rename src/navigation/menus/{patient.menu.js => portal.menu.js} (69%) delete mode 100644 src/router/router.me.js create mode 100644 src/router/routes.account.js rename src/router/{router.configuracoes.js => routes.configs.js} (96%) create mode 100644 src/router/routes.features.js delete mode 100644 src/router/routes.patient.js create mode 100644 src/router/routes.portal.js create mode 100644 src/stores/tenantFeaturesStore.js rename src/views/pages/{me/MeuPerfilPage.vue => account/ProfilePage.vue} (100%) create mode 100644 src/views/pages/admin/agenda/MyAppointmentsPage.vue create mode 100644 src/views/pages/admin/agenda/NewAppointmentPage.vue create mode 100644 src/views/pages/admin/clinic/ClinicFeaturesPage.vue create mode 100644 src/views/pages/admin/clinic/ClinicProfessionalsPage.vue rename src/views/pages/{patient/PatientDashboard.vue => portal/PortalDashboard.vue} (100%) create mode 100644 src/views/pages/portal/agenda/MyAppointmentsPage.vue create mode 100644 src/views/pages/portal/agenda/NewAppointmentPage.vue create mode 100644 src/views/pages/public/AcceptInvitePage.vue create mode 100644 src/views/pages/therapist/agenda/MyAppointmentsPage.vue create mode 100644 src/views/pages/therapist/agenda/NewAppointmentPage.vue diff --git a/O-que-foi-feito.txt b/O-que-foi-feito.txt deleted file mode 100644 index 3fd539b..0000000 --- a/O-que-foi-feito.txt +++ /dev/null @@ -1,101 +0,0 @@ -O que foi feito (até agora) -Usuários de teste criados - -admin@agenciapsi.com.br - — senha: 123Mudar@ - -patient@agenciapsi.com.br - — senha: 123Mudar@ - -therapist@agenciapsi.com.br - — senha: 123Mudar@ - -Base funcionando - -✅ Auth (Supabase) está funcionando -✅ Tabela profiles criada e ok -✅ Trigger automático cria profile após signup -✅ Campo role definido (admin | therapist | patient) -✅ RLS básico ativo -✅ Login funcionando -✅ Logout funcionando -✅ Guard de rota implementado e ativo -✅ RBAC básico operando via meta.role + redirect para painel correto -✅ Home pública / com 3 cards (Admin | Therapist | Patient) levando ao login -✅ Pós-login: busca profiles.role e redireciona para: - -/admin - -/therapist - -/patient - -Estrutura implementada agora (menus e sessão para o Sakai) -Sessão central (evita menu errado e if(role) espalhado) - -✅ Criado src/app/session.js com: - -sessionUser, sessionRole, sessionReady (refs globais) - -initSession() (carrega user + role antes de renderizar o layout) - -listenAuthChanges() (atualiza sessão ao logar/deslogar) - -✅ Ajustado src/main.js para usar bootstrap async: - -chama await initSession() antes de app.mount() - -liga listenAuthChanges() - -mantém PrimeVue, tema Aura, ToastService e ConfirmationService - -mantém imports de CSS existentes - -Menu dinâmico por role no Sakai - -✅ Menus foram estruturados no formato do Sakai (sections com label + items) e separados por role: - -src/navigation/menus/admin.menu.js - -src/navigation/menus/therapist.menu.js - -src/navigation/menus/patient.menu.js - -✅ Criado src/navigation/index.js com getMenuByRole(role) para centralizar a escolha do menu (sem if(role) em componentes). - -✅ Ajustado o AppMenu.vue (menu do Sakai) para: - -usar computed() com sessionRole/sessionReady - -carregar dinamicamente getMenuByRole(sessionRole.value) - -evitar “piscar” menu errado antes de carregar (sessionReady) - -Menu demo do Sakai mantido sem quebrar o produto - -✅ Mantivemos o menu demo (UIKit/Blocks/Start etc.) em arquivo separado para não perder as páginas do template: - -src/navigation/menus/sakai.demo.menu.js (conteúdo original do template) - -✅ Estratégia adotada: - -Admin pode ver o menu demo (idealmente só em DEV) - -Therapist/Patient ficam com menu limpo (clínico) - -Rotas demo do Sakai corrigidas (arquivos com sufixo Doc) - -✅ Problema resolvido: itens do menu demo davam 404 porque as rotas/imports não existiam com os nomes esperados (Input.vue etc.). -✅ Ajuste aplicado: rotas demo apontam para arquivos *Doc.vue (ex.: ButtonDoc.vue, InputDoc.vue). - -📌 Criado/ajustado src/router/routes.demo.js para mapear: - -/uikit/* → @/views/uikit/*Doc.vue - -e demais demos conforme existirem - -✅ Incluído demoRoutes no router principal para o menu demo funcionar. - -Testes - -✅ Confirmado que localStorage.clear() limpa sessão para testar outros usuários/roles rapidamente. \ No newline at end of file diff --git a/checklist-novo-chat.txt b/checklist-novo-chat.txt deleted file mode 100644 index 6f95736..0000000 --- a/checklist-novo-chat.txt +++ /dev/null @@ -1,47 +0,0 @@ -🔁 CONTEXTO DO PROJETO (SaaS multi-tenant) - -Stack: -- Supabase -- Multi-tenant por clinic/tenant -- Assinaturas por tenant (subscriptions.tenant_id) -- Controle de features: features, plan_features, subscription_intents, entitlementsStore, view v_tenant_entitlements -- Ativação manual: activate_subscription_from_intent() -- Merge concluído: agenda_online → online_scheduling.manage -- Entitlements e bloqueio PRO no menu funcionando -- Signup + intent funcionando; ativação cria subscription ativa; view retorna feature correta - -Modelo de “Contas” decidido: -- Auth user (login) ≠ Clínica (tenant) -- Clínica = tenant; Usuário pode ser dono/admin de clínica e também profissional -- Clínica convida usuários (tenant_members). Usuário pode aceitar/recusar. -- Profissional pode trabalhar anos e depois sair: clínica mantém registros; profissional mantém histórico (audit trail), sem acesso após saída. - -Regras de offboarding: -- Profissional só pode sair se NÃO houver agenda futura atribuída a ele. -- Se houver, cria “pedido de saída” e admin precisa realocar/cancelar; depois finaliza saída. - -Tabelas existentes: -- tenant_members: (id uuid pk, tenant_id uuid, user_id uuid, role text, status text, created_at timestamptz) - - UNIQUE (tenant_id, user_id) atualmente -- Agenda: agenda_eventos, agenda_excecoes, agenda_configuracoes, agenda_regras_semanais -- Outros: subscriptions, subscription_intents, plan_features, features, subscription_events - -O que estamos fazendo agora: -- Ajustar modelo de membership lifecycle e offboarding (exit_requests) -- Garantir integridade: histórico de vínculos + auditoria + bloqueio de saída com agenda futura -- Implementar SQL + RPC + RLS + UI (passo a passo) - -✔ subscriptions -Representa o plano da clínica (tenant) -✔ tenant_members -Define quais usuários pertencem à clínica -✔ entitlements - -Define o que aquela clínica pode usar - -Dados que faltam confirmar: -1) Estrutura de agenda_eventos (colunas e como relaciona com profissional) -2) Valores usados em tenant_members.status (active/invited/etc) -3) Estratégia de reentrada: remover UNIQUE (tenant_id,user_id) e usar unique parcial por status ativo/convite -4) Se existe tabela public.users como espelho do auth.users - diff --git a/package-lock.json b/package-lock.json index 58bb217..390382c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,13 @@ "name": "sakai-vue", "version": "5.0.0", "dependencies": { + "@fullcalendar/core": "^6.1.20", + "@fullcalendar/daygrid": "^6.1.20", + "@fullcalendar/interaction": "^6.1.20", + "@fullcalendar/resource": "^6.1.20", + "@fullcalendar/resource-timegrid": "^6.1.20", + "@fullcalendar/timegrid": "^6.1.20", + "@fullcalendar/vue3": "^6.1.20", "@primeuix/themes": "^2.0.0", "@supabase/supabase-js": "^2.95.3", "chart.js": "3.3.2", @@ -513,6 +520,96 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/@fullcalendar/core": { + "version": "6.1.20", + "resolved": "https://registry.npmjs.org/@fullcalendar/core/-/core-6.1.20.tgz", + "integrity": "sha512-1cukXLlePFiJ8YKXn/4tMKsy0etxYLCkXk8nUCFi11nRONF2Ba2CD5b21/ovtOO2tL6afTJfwmc1ed3HG7eB1g==", + "dependencies": { + "preact": "~10.12.1" + } + }, + "node_modules/@fullcalendar/daygrid": { + "version": "6.1.20", + "resolved": "https://registry.npmjs.org/@fullcalendar/daygrid/-/daygrid-6.1.20.tgz", + "integrity": "sha512-AO9vqhkLP77EesmJzuU+IGXgxNulsA8mgQHynclJ8U70vSwAVnbcLG9qftiTAFSlZjiY/NvhE7sflve6cJelyQ==", + "peerDependencies": { + "@fullcalendar/core": "~6.1.20" + } + }, + "node_modules/@fullcalendar/interaction": { + "version": "6.1.20", + "resolved": "https://registry.npmjs.org/@fullcalendar/interaction/-/interaction-6.1.20.tgz", + "integrity": "sha512-p6txmc5txL0bMiPaJxe2ip6o0T384TyoD2KGdsU6UjZ5yoBlaY+dg7kxfnYKpYMzEJLG58n+URrHr2PgNL2fyA==", + "peerDependencies": { + "@fullcalendar/core": "~6.1.20" + } + }, + "node_modules/@fullcalendar/premium-common": { + "version": "6.1.20", + "resolved": "https://registry.npmjs.org/@fullcalendar/premium-common/-/premium-common-6.1.20.tgz", + "integrity": "sha512-rT+AitNnRyZuFEtYvsB1OJ2g1Bq2jmTR6qdn/dEU6LwkIj/4L499goLtMOena/JyJ31VBztdHrccX//36QrY3w==", + "peerDependencies": { + "@fullcalendar/core": "~6.1.20" + } + }, + "node_modules/@fullcalendar/resource": { + "version": "6.1.20", + "resolved": "https://registry.npmjs.org/@fullcalendar/resource/-/resource-6.1.20.tgz", + "integrity": "sha512-vpQs1eYJbc1zGOzF3obVVr+XsHTMTG7STKVQBEGy3AeFgfosRkUz+3DUawmy98vSjJUYOAQHO+pWW0ek0n5g0w==", + "dependencies": { + "@fullcalendar/premium-common": "~6.1.20" + }, + "peerDependencies": { + "@fullcalendar/core": "~6.1.20" + } + }, + "node_modules/@fullcalendar/resource-daygrid": { + "version": "6.1.20", + "resolved": "https://registry.npmjs.org/@fullcalendar/resource-daygrid/-/resource-daygrid-6.1.20.tgz", + "integrity": "sha512-g1rhNsTiGyx6U/01MCjRjQfpmkHpJABoTLS9TR2jcMa7X0SJd2xNd88phoMhIkYdfp+cZ29VOjhwN+3Xg6aohg==", + "dependencies": { + "@fullcalendar/daygrid": "~6.1.20", + "@fullcalendar/premium-common": "~6.1.20" + }, + "peerDependencies": { + "@fullcalendar/core": "~6.1.20", + "@fullcalendar/resource": "~6.1.20" + } + }, + "node_modules/@fullcalendar/resource-timegrid": { + "version": "6.1.20", + "resolved": "https://registry.npmjs.org/@fullcalendar/resource-timegrid/-/resource-timegrid-6.1.20.tgz", + "integrity": "sha512-uMf9ERh1c/WeYHg5CPNGxYorkamDzfwUh2o9XS+9fR+KypIIovH1ArflOZF42XFsdrvQx61vDF0alt6/cOqT8Q==", + "dependencies": { + "@fullcalendar/premium-common": "~6.1.20", + "@fullcalendar/resource-daygrid": "~6.1.20", + "@fullcalendar/timegrid": "~6.1.20" + }, + "peerDependencies": { + "@fullcalendar/core": "~6.1.20", + "@fullcalendar/resource": "~6.1.20" + } + }, + "node_modules/@fullcalendar/timegrid": { + "version": "6.1.20", + "resolved": "https://registry.npmjs.org/@fullcalendar/timegrid/-/timegrid-6.1.20.tgz", + "integrity": "sha512-4H+/MWbz3ntA50lrPif+7TsvMeX3R1GSYjiLULz0+zEJ7/Yfd9pupZmAwUs/PBpA6aAcFmeRr0laWfcz1a9V1A==", + "dependencies": { + "@fullcalendar/daygrid": "~6.1.20" + }, + "peerDependencies": { + "@fullcalendar/core": "~6.1.20" + } + }, + "node_modules/@fullcalendar/vue3": { + "version": "6.1.20", + "resolved": "https://registry.npmjs.org/@fullcalendar/vue3/-/vue3-6.1.20.tgz", + "integrity": "sha512-8qg6pS27II9QBwFkkJC+7SfflMpWqOe7i3ii5ODq9KpLAjwQAd/zjfq8RvKR1Yryoh5UmMCmvRbMB7i4RGtqog==", + "peerDependencies": { + "@fullcalendar/core": "~6.1.20", + "vue": "^3.0.11" + } + }, "node_modules/@humanwhocodes/config-array": { "version": "0.13.0", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", @@ -3645,6 +3742,15 @@ "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", "dev": true }, + "node_modules/preact": { + "version": "10.12.1", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.12.1.tgz", + "integrity": "sha512-l8386ixSsBdbreOAkqtrwqHwdvR35ID8c3rKPa8lCWuO86dBi32QWHV4vfsZK1utLLFMvw+Z5Ad4XLkZzchscg==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/preact" + } + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -4672,6 +4778,73 @@ "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", "dev": true }, + "@fullcalendar/core": { + "version": "6.1.20", + "resolved": "https://registry.npmjs.org/@fullcalendar/core/-/core-6.1.20.tgz", + "integrity": "sha512-1cukXLlePFiJ8YKXn/4tMKsy0etxYLCkXk8nUCFi11nRONF2Ba2CD5b21/ovtOO2tL6afTJfwmc1ed3HG7eB1g==", + "requires": { + "preact": "~10.12.1" + } + }, + "@fullcalendar/daygrid": { + "version": "6.1.20", + "resolved": "https://registry.npmjs.org/@fullcalendar/daygrid/-/daygrid-6.1.20.tgz", + "integrity": "sha512-AO9vqhkLP77EesmJzuU+IGXgxNulsA8mgQHynclJ8U70vSwAVnbcLG9qftiTAFSlZjiY/NvhE7sflve6cJelyQ==", + "requires": {} + }, + "@fullcalendar/interaction": { + "version": "6.1.20", + "resolved": "https://registry.npmjs.org/@fullcalendar/interaction/-/interaction-6.1.20.tgz", + "integrity": "sha512-p6txmc5txL0bMiPaJxe2ip6o0T384TyoD2KGdsU6UjZ5yoBlaY+dg7kxfnYKpYMzEJLG58n+URrHr2PgNL2fyA==", + "requires": {} + }, + "@fullcalendar/premium-common": { + "version": "6.1.20", + "resolved": "https://registry.npmjs.org/@fullcalendar/premium-common/-/premium-common-6.1.20.tgz", + "integrity": "sha512-rT+AitNnRyZuFEtYvsB1OJ2g1Bq2jmTR6qdn/dEU6LwkIj/4L499goLtMOena/JyJ31VBztdHrccX//36QrY3w==", + "requires": {} + }, + "@fullcalendar/resource": { + "version": "6.1.20", + "resolved": "https://registry.npmjs.org/@fullcalendar/resource/-/resource-6.1.20.tgz", + "integrity": "sha512-vpQs1eYJbc1zGOzF3obVVr+XsHTMTG7STKVQBEGy3AeFgfosRkUz+3DUawmy98vSjJUYOAQHO+pWW0ek0n5g0w==", + "requires": { + "@fullcalendar/premium-common": "~6.1.20" + } + }, + "@fullcalendar/resource-daygrid": { + "version": "6.1.20", + "resolved": "https://registry.npmjs.org/@fullcalendar/resource-daygrid/-/resource-daygrid-6.1.20.tgz", + "integrity": "sha512-g1rhNsTiGyx6U/01MCjRjQfpmkHpJABoTLS9TR2jcMa7X0SJd2xNd88phoMhIkYdfp+cZ29VOjhwN+3Xg6aohg==", + "requires": { + "@fullcalendar/daygrid": "~6.1.20", + "@fullcalendar/premium-common": "~6.1.20" + } + }, + "@fullcalendar/resource-timegrid": { + "version": "6.1.20", + "resolved": "https://registry.npmjs.org/@fullcalendar/resource-timegrid/-/resource-timegrid-6.1.20.tgz", + "integrity": "sha512-uMf9ERh1c/WeYHg5CPNGxYorkamDzfwUh2o9XS+9fR+KypIIovH1ArflOZF42XFsdrvQx61vDF0alt6/cOqT8Q==", + "requires": { + "@fullcalendar/premium-common": "~6.1.20", + "@fullcalendar/resource-daygrid": "~6.1.20", + "@fullcalendar/timegrid": "~6.1.20" + } + }, + "@fullcalendar/timegrid": { + "version": "6.1.20", + "resolved": "https://registry.npmjs.org/@fullcalendar/timegrid/-/timegrid-6.1.20.tgz", + "integrity": "sha512-4H+/MWbz3ntA50lrPif+7TsvMeX3R1GSYjiLULz0+zEJ7/Yfd9pupZmAwUs/PBpA6aAcFmeRr0laWfcz1a9V1A==", + "requires": { + "@fullcalendar/daygrid": "~6.1.20" + } + }, + "@fullcalendar/vue3": { + "version": "6.1.20", + "resolved": "https://registry.npmjs.org/@fullcalendar/vue3/-/vue3-6.1.20.tgz", + "integrity": "sha512-8qg6pS27II9QBwFkkJC+7SfflMpWqOe7i3ii5ODq9KpLAjwQAd/zjfq8RvKR1Yryoh5UmMCmvRbMB7i4RGtqog==", + "requires": {} + }, "@humanwhocodes/config-array": { "version": "0.13.0", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", @@ -6664,6 +6837,11 @@ "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", "dev": true }, + "preact": { + "version": "10.12.1", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.12.1.tgz", + "integrity": "sha512-l8386ixSsBdbreOAkqtrwqHwdvR35ID8c3rKPa8lCWuO86dBi32QWHV4vfsZK1utLLFMvw+Z5Ad4XLkZzchscg==" + }, "prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", diff --git a/package.json b/package.json index 4e88faf..a947a66 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,13 @@ "lint": "eslint --fix . --ext .vue,.js,.jsx,.cjs,.mjs --fix --ignore-path .gitignore" }, "dependencies": { + "@fullcalendar/core": "^6.1.20", + "@fullcalendar/daygrid": "^6.1.20", + "@fullcalendar/interaction": "^6.1.20", + "@fullcalendar/resource": "^6.1.20", + "@fullcalendar/resource-timegrid": "^6.1.20", + "@fullcalendar/timegrid": "^6.1.20", + "@fullcalendar/vue3": "^6.1.20", "@primeuix/themes": "^2.0.0", "@supabase/supabase-js": "^2.95.3", "chart.js": "3.3.2", diff --git a/src/App.vue b/src/App.vue index ca93f6d..43cb4d2 100644 --- a/src/App.vue +++ b/src/App.vue @@ -6,20 +6,35 @@ import { useEntitlementsStore } from '@/stores/entitlementsStore' const route = useRoute() -const tenant = useTenantStore() -const ent = useEntitlementsStore() +const tenantStore = useTenantStore() +const entStore = useEntitlementsStore() onMounted(async () => { - await tenant.loadSessionAndTenant() - await ent.loadForTenant(tenant.activeTenantId) + // 1) carrega sessão + tenant ativo (do seu fluxo atual) + await tenantStore.loadSessionAndTenant() - // pode remover esses logs depois - console.log('tenant.activeTenantId', tenant.activeTenantId) - console.log('role', tenant.activeRole) - console.log('can online_scheduling.manage?', ent.can('online_scheduling.manage')) + // 2) carrega permissões do tenant ativo (se existir) + if (tenantStore.activeTenantId) { + await entStore.loadForTenant(tenantStore.activeTenantId) + } + + // 3) debug: localStorage com rótulos + console.groupCollapsed('[Debug] Tenant localStorage') + console.log('tenant_id:', localStorage.getItem('tenant_id')) + console.log('currentTenantId:', localStorage.getItem('currentTenantId')) + console.log('tenant:', localStorage.getItem('tenant')) + console.groupEnd() + + // 4) debug: stores + console.groupCollapsed('[Debug] Tenant stores') + console.log('route:', route.fullPath) + console.log('activeTenantId:', tenantStore.activeTenantId) + console.log('activeRole:', tenantStore.activeRole) + console.log("can('online_scheduling.manage'):", entStore.can('online_scheduling.manage')) + console.groupEnd() }) + \ No newline at end of file diff --git a/src/app/session.js b/src/app/session.js index 2776226..65dc672 100644 --- a/src/app/session.js +++ b/src/app/session.js @@ -219,4 +219,4 @@ export function stopAuthChanges () { authSubscription.unsubscribe() authSubscription = null } -} +} \ No newline at end of file diff --git a/src/components/agenda/PausasChipsEditor.vue b/src/components/agenda/PausasChipsEditor.vue new file mode 100644 index 0000000..b978def --- /dev/null +++ b/src/components/agenda/PausasChipsEditor.vue @@ -0,0 +1,246 @@ + + + + \ No newline at end of file diff --git a/src/constants/roles.js b/src/constants/roles.js index b783efa..3bac3c5 100644 --- a/src/constants/roles.js +++ b/src/constants/roles.js @@ -1,11 +1,49 @@ +// src/constants/roles.js + +/** + * Roles canônicas do sistema (tenant-level) + * Esses valores devem refletir exatamente o que existe no banco. + */ export const ROLES = { - ADMIN: 'admin', + CLINIC_ADMIN: 'clinic_admin', + TENANT_ADMIN: 'tenant_admin', // legado (compatibilidade) THERAPIST: 'therapist', PATIENT: 'patient' } -export const ROLE_HOME = { - admin: '/admin', - therapist: '/therapist', - patient: '/patient' + +/** + * Retorna a rota base (home) de cada role. + * Usado após login, guards e redirecionamentos. + */ +export function roleToHome(role) { + switch (role) { + case ROLES.CLINIC_ADMIN: + case ROLES.TENANT_ADMIN: // compatibilidade + return '/admin' + + case ROLES.THERAPIST: + return '/therapist' + + case ROLES.PATIENT: + return '/portal' + + default: + return '/' + } } + + +/** + * Lista todas as roles válidas + * Útil para validações e guards + */ +export const ALL_ROLES = Object.values(ROLES) + + +/** + * Verifica se uma role é válida + */ +export function isValidRole(role) { + return ALL_ROLES.includes(role) +} \ No newline at end of file diff --git a/src/features/agenda/components/AdicionarCompromissoPage.vue b/src/features/agenda/components/AdicionarCompromissoPage.vue new file mode 100644 index 0000000..7915af7 --- /dev/null +++ b/src/features/agenda/components/AdicionarCompromissoPage.vue @@ -0,0 +1,1004 @@ + + + + + \ No newline at end of file diff --git a/src/features/agenda/components/AgendaCalendar.vue b/src/features/agenda/components/AgendaCalendar.vue new file mode 100644 index 0000000..abce031 --- /dev/null +++ b/src/features/agenda/components/AgendaCalendar.vue @@ -0,0 +1,10 @@ + + + \ No newline at end of file diff --git a/src/features/agenda/components/AgendaClinicCalendar.vue b/src/features/agenda/components/AgendaClinicCalendar.vue new file mode 100644 index 0000000..a18fd72 --- /dev/null +++ b/src/features/agenda/components/AgendaClinicCalendar.vue @@ -0,0 +1,100 @@ + + + + \ No newline at end of file diff --git a/src/features/agenda/components/AgendaClinicMosaic.vue b/src/features/agenda/components/AgendaClinicMosaic.vue new file mode 100644 index 0000000..3a17a8c --- /dev/null +++ b/src/features/agenda/components/AgendaClinicMosaic.vue @@ -0,0 +1,192 @@ + + + \ No newline at end of file diff --git a/src/features/agenda/components/AgendaEventDialog.vue b/src/features/agenda/components/AgendaEventDialog.vue new file mode 100644 index 0000000..d9b373a --- /dev/null +++ b/src/features/agenda/components/AgendaEventDialog.vue @@ -0,0 +1,243 @@ + + +