Correcao Sidebar Classico e Rail, Correcao Layout, Ajuste de Breakpoint para Tailwind, Ajuste AppTopbar, Ajuste Menu PopOver, Recriado Paleta de Cores, Inserido algumas animações leves, Reajuste Cor items NOVOS da tabela, Drawer Ajuda Corrigido no Logout, Whatsapp, sms, email, recursos extras

This commit is contained in:
Leonardo
2026-03-24 21:26:58 -03:00
parent a89d1f5560
commit 53a4980396
453 changed files with 121427 additions and 174407 deletions
+27 -27
View File
@@ -35,16 +35,16 @@
* Obs: 'admin' é role GLOBAL (profiles.role). Aqui é guard de tenant,
* mas mantemos um fallback seguro para legado.
*/
export function roleHomePath (role) {
// ✅ clínica: aceita nomes canônicos e legado
if (role === 'clinic_admin' || role === 'tenant_admin') return '/admin'
if (role === 'therapist') return '/therapist'
if (role === 'patient') return '/portal'
export function roleHomePath(role) {
// ✅ clínica: aceita nomes canônicos e legado
if (role === 'clinic_admin' || role === 'tenant_admin') return '/admin';
if (role === 'therapist') return '/therapist';
if (role === 'patient') return '/portal';
// ✅ fallback (não deveria acontecer em tenant)
if (role === 'admin') return '/admin'
// ✅ fallback (não deveria acontecer em tenant)
if (role === 'admin') return '/admin';
return '/'
return '/';
}
/**
@@ -55,19 +55,19 @@ export function roleHomePath (role) {
* - use403 = true → sempre /pages/access (recomendado para clareza)
* - use403 = false → home do papel (útil quando você quer "auto-corrigir" navegação)
*/
export function denyByRole ({ to, currentRole, use403 = true } = {}) {
// ✅ padrão forte: 403 (não é caso de upgrade)
if (use403) return { path: '/pages/access' }
export function denyByRole({ to, currentRole, use403 = true } = {}) {
// ✅ padrão forte: 403 (não é caso de upgrade)
if (use403) return { path: '/pages/access' };
// modo "suave": manda pra home do papel
const fallback = roleHomePath(currentRole)
// modo "suave": manda pra home do papel
const fallback = roleHomePath(currentRole);
// evita loop: se já está no fallback, manda pra página de acesso negado
if (to?.path && to.path === fallback) {
return { path: '/pages/access' }
}
// evita loop: se já está no fallback, manda pra página de acesso negado
if (to?.path && to.path === fallback) {
return { path: '/pages/access' };
}
return { path: fallback }
return { path: fallback };
}
/**
@@ -75,12 +75,12 @@ export function denyByRole ({ to, currentRole, use403 = true } = {}) {
* missingFeature: feature key (ex: 'online_scheduling.manage')
* redirectTo: para onde voltar após upgrade
*/
export function denyByPlan ({ to, missingFeature, redirectTo } = {}) {
return {
path: '/upgrade',
query: {
feature: missingFeature || '',
redirectTo: redirectTo || to?.fullPath || '/'
}
}
}
export function denyByPlan({ to, missingFeature, redirectTo } = {}) {
return {
path: '/upgrade',
query: {
feature: missingFeature || '',
redirectTo: redirectTo || to?.fullPath || '/'
}
};
}
+826 -839
View File
File diff suppressed because it is too large Load Diff
+28 -25
View File
@@ -30,24 +30,24 @@ import supervisorRoutes from './routes.supervisor';
import editorRoutes from './routes.editor';
import featuresRoutes from './routes.features';
import { pinia } from '@/plugins/pinia' // ← singleton compartilhado
import { supportGuard } from '@/support/supportGuard'
import { pinia } from '@/plugins/pinia'; // ← singleton compartilhado
import { supportGuard } from '@/support/supportGuard';
import { applyGuards } from './guards';
const routes = [
...(Array.isArray(publicRoutes) ? publicRoutes : [publicRoutes]),
...(Array.isArray(authRoutes) ? authRoutes : [authRoutes]),
...(Array.isArray(miscRoutes) ? miscRoutes : [miscRoutes]),
...(Array.isArray(billingRoutes) ? billingRoutes : [billingRoutes]),
...(Array.isArray(saasRoutes) ? saasRoutes : [saasRoutes]),
...(Array.isArray(meRoutes) ? meRoutes : [meRoutes]),
...(Array.isArray(adminRoutes) ? adminRoutes : [adminRoutes]),
...(Array.isArray(therapistRoutes) ? therapistRoutes : [therapistRoutes]),
...(Array.isArray(supervisorRoutes) ? supervisorRoutes : [supervisorRoutes]),
...(Array.isArray(editorRoutes) ? editorRoutes : [editorRoutes]),
...(Array.isArray(portalRoutes) ? portalRoutes : [portalRoutes]),
...(Array.isArray(configuracoesRoutes) ? configuracoesRoutes : [configuracoesRoutes]),
...(Array.isArray(featuresRoutes) ? featuresRoutes : [featuresRoutes]),
...(Array.isArray(publicRoutes) ? publicRoutes : [publicRoutes]),
...(Array.isArray(authRoutes) ? authRoutes : [authRoutes]),
...(Array.isArray(miscRoutes) ? miscRoutes : [miscRoutes]),
...(Array.isArray(billingRoutes) ? billingRoutes : [billingRoutes]),
...(Array.isArray(saasRoutes) ? saasRoutes : [saasRoutes]),
...(Array.isArray(meRoutes) ? meRoutes : [meRoutes]),
...(Array.isArray(adminRoutes) ? adminRoutes : [adminRoutes]),
...(Array.isArray(therapistRoutes) ? therapistRoutes : [therapistRoutes]),
...(Array.isArray(supervisorRoutes) ? supervisorRoutes : [supervisorRoutes]),
...(Array.isArray(editorRoutes) ? editorRoutes : [editorRoutes]),
...(Array.isArray(portalRoutes) ? portalRoutes : [portalRoutes]),
...(Array.isArray(configuracoesRoutes) ? configuracoesRoutes : [configuracoesRoutes]),
...(Array.isArray(featuresRoutes) ? featuresRoutes : [featuresRoutes]),
// ✅ compat: rota antiga /login → /auth/login
{
@@ -56,7 +56,7 @@ const routes = [
path: '/auth/login',
query: to.query || {}
})
},
}
];
const router = createRouter({
@@ -71,7 +71,10 @@ const router = createRouter({
/* 🔎 DEBUG: listar todas as rotas registradas */
console.log(
'[ROUTES]',
router.getRoutes().map((r) => r.path).sort()
router
.getRoutes()
.map((r) => r.path)
.sort()
);
// ===== DEBUG NAV + TRACE (remover depois) =====
@@ -80,9 +83,9 @@ router.push = async (loc) => {
console.log('[router.push]', loc);
console.trace('[push caller]');
const res = await _push(loc);
if (isNavigationFailure(res, NavigationFailureType.duplicated)) console.warn('[NAV FAIL] duplicated', res);
else if (isNavigationFailure(res, NavigationFailureType.cancelled)) console.warn('[NAV FAIL] cancelled', res);
else if (isNavigationFailure(res, NavigationFailureType.aborted)) console.warn('[NAV FAIL] aborted', res);
if (isNavigationFailure(res, NavigationFailureType.duplicated)) console.warn('[NAV FAIL] duplicated', res);
else if (isNavigationFailure(res, NavigationFailureType.cancelled)) console.warn('[NAV FAIL] cancelled', res);
else if (isNavigationFailure(res, NavigationFailureType.aborted)) console.warn('[NAV FAIL] aborted', res);
else if (isNavigationFailure(res, NavigationFailureType.redirected)) console.warn('[NAV FAIL] redirected', res);
return res;
};
@@ -92,8 +95,8 @@ router.replace = async (loc) => {
console.log('[router.replace]', loc);
console.trace('[replace caller]');
const res = await _replace(loc);
if (isNavigationFailure(res, NavigationFailureType.cancelled)) console.warn('[NAV FAIL replace] cancelled', res);
else if (isNavigationFailure(res, NavigationFailureType.aborted)) console.warn('[NAV FAIL replace] aborted', res);
if (isNavigationFailure(res, NavigationFailureType.cancelled)) console.warn('[NAV FAIL replace] cancelled', res);
else if (isNavigationFailure(res, NavigationFailureType.aborted)) console.warn('[NAV FAIL replace] aborted', res);
else if (isNavigationFailure(res, NavigationFailureType.redirected)) console.warn('[NAV FAIL replace] redirected', res);
return res;
};
@@ -102,8 +105,8 @@ router.onError((e) => console.error('[router.onError]', e));
// ✅ support guard — passa pinia para garantir acesso ao store antes do app.use(pinia)
router.beforeEach(async (to) => {
await supportGuard(to, pinia)
})
await supportGuard(to, pinia);
});
router.beforeEach((to, from) => {
console.log('[beforeEach]', from.fullPath, '->', to.fullPath);
@@ -118,4 +121,4 @@ router.afterEach((to, from, failure) => {
applyGuards(router);
export default router;
export default router;
+21 -21
View File
@@ -14,26 +14,26 @@
| © 2026 — Todos os direitos reservados
|--------------------------------------------------------------------------
*/
import AppLayout from '@/layout/AppLayout.vue'
import AppLayout from '@/layout/AppLayout.vue';
export default {
path: '/account',
component: AppLayout,
meta: { requiresAuth: true, area: 'account' },
children: [
{
path: '',
redirect: { name: 'account-profile' }
},
{
path: 'profile',
name: 'account-profile',
component: () => import('@/views/pages/account/ProfilePage.vue')
},
{
path: 'security',
name: 'account-security',
component: () => import('@/views/pages/auth/SecurityPage.vue')
}
]
}
path: '/account',
component: AppLayout,
meta: { requiresAuth: true, area: 'account' },
children: [
{
path: '',
redirect: { name: 'account-profile' }
},
{
path: 'profile',
name: 'account-profile',
component: () => import('@/views/pages/account/ProfilePage.vue')
},
{
path: 'security',
name: 'account-security',
component: () => import('@/views/pages/auth/SecurityPage.vue')
}
]
};
+41 -41
View File
@@ -15,47 +15,47 @@
|--------------------------------------------------------------------------
*/
export default {
path: '/auth',
children: [
{
path: 'login',
name: 'login',
component: () => import('@/views/pages/auth/Login.vue'),
meta: { public: true }
},
path: '/auth',
children: [
{
path: 'login',
name: 'login',
component: () => import('@/views/pages/auth/Login.vue'),
meta: { public: true }
},
// ✅ Signup público, mas com URL /auth/signup
{
path: 'signup',
name: 'signup',
component: () => import('@/views/pages/public/Signup.vue'),
meta: { public: true }
},
{
path: 'welcome',
name: 'auth.welcome',
component: () => import('@/views/pages/auth/Welcome.vue'),
meta: { public: true }
},
{
path: 'reset-password',
name: 'resetPassword',
component: () => import('@/views/pages/auth/ResetPasswordPage.vue'),
meta: { public: true }
},
// ✅ Signup público, mas com URL /auth/signup
{
path: 'signup',
name: 'signup',
component: () => import('@/views/pages/public/Signup.vue'),
meta: { public: true }
},
{
path: 'welcome',
name: 'auth.welcome',
component: () => import('@/views/pages/auth/Welcome.vue'),
meta: { public: true }
},
{
path: 'reset-password',
name: 'resetPassword',
component: () => import('@/views/pages/auth/ResetPasswordPage.vue'),
meta: { public: true }
},
{
path: 'access',
name: 'accessDenied',
component: () => import('@/views/pages/auth/Access.vue'),
meta: { public: true }
},
{
path: 'access',
name: 'accessDenied',
component: () => import('@/views/pages/auth/Access.vue'),
meta: { public: true }
},
{
path: 'error',
name: 'error',
component: () => import('@/views/pages/auth/Error.vue'),
meta: { public: true }
}
]
}
{
path: 'error',
name: 'error',
component: () => import('@/views/pages/auth/Error.vue'),
meta: { public: true }
}
]
};
+12 -12
View File
@@ -14,17 +14,17 @@
| © 2026 — Todos os direitos reservados
|--------------------------------------------------------------------------
*/
import AppLayout from '@/layout/AppLayout.vue'
import AppLayout from '@/layout/AppLayout.vue';
export default {
path: '/upgrade',
component: AppLayout,
meta: { requiresAuth: true },
children: [
{
path: '',
name: 'upgrade',
component: () => import('@/views/pages/billing/UpgradePage.vue')
}
]
}
path: '/upgrade',
component: AppLayout,
meta: { requiresAuth: true },
children: [
{
path: '',
name: 'upgrade',
component: () => import('@/views/pages/billing/UpgradePage.vue')
}
]
};
+199 -199
View File
@@ -14,225 +14,225 @@
| © 2026 — Todos os direitos reservados
|--------------------------------------------------------------------------
*/
import AppLayout from '@/layout/AppLayout.vue'
import AppLayout from '@/layout/AppLayout.vue';
export default [
// ======================================================
// 🚀 SETUP WIZARD — fora do AppLayout (fullscreen)
// ======================================================
{
path: '/admin/setup',
name: 'admin.setup',
component: () => import('@/features/setup/SetupWizardPage.vue'),
meta: { area: 'admin', requiresAuth: true, roles: ['clinic_admin'], fullscreen: true },
},
{
path: '/admin',
component: AppLayout,
meta: {
// 🔐 Tudo aqui dentro exige login
area: 'admin',
requiresAuth: true,
// 👤 Perfil de acesso (tenant-level)
// tenantStore normaliza tenant_admin -> clinic_admin, mas mantemos compatibilidade
roles: ['clinic_admin']
},
children: [
// ======================================================
// 📊 DASHBOARD
// ======================================================
{ path: '', name: 'admin.dashboard', component: () => import('@/views/pages/clinic/ClinicDashboard.vue') },
// ======================================================
// 🧩 CLÍNICA — MÓDULOS (tenant_features)
// 🚀 SETUP WIZARD — fora do AppLayout (fullscreen)
// ======================================================
{
path: 'clinic/features',
name: 'admin-clinic-features',
component: () => import('@/views/pages/clinic/clinic/ClinicFeaturesPage.vue'),
meta: {
// opcional: restringir apenas para admin canônico
roles: ['clinic_admin', 'tenant_admin']
}
},
{
path: 'clinic/professionals',
name: 'admin-clinic-professionals',
component: () => import('@/views/pages/clinic/clinic/ClinicProfessionalsPage.vue'),
meta: {
requiresAuth: true,
roles: ['clinic_admin', 'tenant_admin']
}
},
// ======================================================
// 💳 MEU PLANO
// ======================================================
{
path: 'meu-plano',
name: 'admin-meu-plano',
component: () => import('@/views/pages/billing/ClinicMeuPlanoPage.vue')
path: '/admin/setup',
name: 'admin.setup',
component: () => import('@/features/setup/SetupWizardPage.vue'),
meta: { area: 'admin', requiresAuth: true, roles: ['clinic_admin'], fullscreen: true }
},
// ======================================================
// 📅 AGENDA DA CLÍNICA
// ======================================================
{
path: 'agenda/clinica',
name: 'admin-agenda-clinica',
component: () => import('@/features/agenda/pages/AgendaClinicaPage.vue'),
meta: {
feature: 'agenda.view',
roles: ['clinic_admin', 'tenant_admin']
}
},
path: '/admin',
component: AppLayout,
// Recorrências
{
path: 'agenda/recorrencias',
name: 'admin-agenda-recorrencias',
component: () => import('@/features/agenda/pages/AgendaRecorrenciasPage.vue'),
meta: { feature: 'agenda.view', roles: ['clinic_admin', 'tenant_admin'], mode: 'clinic' }
},
meta: {
// 🔐 Tudo aqui dentro exige login
area: 'admin',
requiresAuth: true,
// ✅ NOVO: Compromissos determinísticos (tipos)
{
path: 'agenda/compromissos',
name: 'admin-agenda-compromissos',
component: () => import('@/features/agenda/pages/CompromissosDeterminados.vue'),
meta: {
feature: 'agenda.view',
roles: ['clinic_admin', 'tenant_admin']
// ✅ sem tenantScope: a área /admin já está no tenant da clínica pelo fluxo normal
}
},
// 👤 Perfil de acesso (tenant-level)
// tenantStore normaliza tenant_admin -> clinic_admin, mas mantemos compatibilidade
roles: ['clinic_admin']
},
children: [
// ======================================================
// 📊 DASHBOARD
// ======================================================
{ path: '', name: 'admin.dashboard', component: () => import('@/views/pages/clinic/ClinicDashboard.vue') },
// ======================================================
// 👥 PACIENTES (módulo ativável por clínica)
// ======================================================
// ======================================================
// 🧩 CLÍNICA — MÓDULOS (tenant_features)
// ======================================================
{
path: 'clinic/features',
name: 'admin-clinic-features',
component: () => import('@/views/pages/clinic/clinic/ClinicFeaturesPage.vue'),
meta: {
// opcional: restringir apenas para admin canônico
roles: ['clinic_admin', 'tenant_admin']
}
},
{
path: 'clinic/professionals',
name: 'admin-clinic-professionals',
component: () => import('@/views/pages/clinic/clinic/ClinicProfessionalsPage.vue'),
meta: {
requiresAuth: true,
roles: ['clinic_admin', 'tenant_admin']
}
},
// 📋 Lista de pacientes
{
path: 'pacientes',
name: 'admin-pacientes',
component: () => import('@/features/patients/PatientsListPage.vue'),
meta: {
// ✅ depende do tenant_features.patients
tenantFeature: 'patients'
}
},
// ======================================================
// 💳 MEU PLANO
// ======================================================
{
path: 'meu-plano',
name: 'admin-meu-plano',
component: () => import('@/views/pages/billing/ClinicMeuPlanoPage.vue')
},
// Cadastro de paciente (novo)
{
path: 'pacientes/cadastro',
name: 'admin-pacientes-cadastro',
component: () => import('@/features/patients/cadastro/PatientsCadastroPage.vue'),
meta: {
tenantFeature: 'patients'
}
},
// ======================================================
// 📅 AGENDA DA CLÍNICA
// ======================================================
// ✏️ Editar paciente
{
path: 'pacientes/cadastro/:id',
name: 'admin-pacientes-cadastro-edit',
component: () => import('@/features/patients/cadastro/PatientsCadastroPage.vue'),
props: true,
meta: {
tenantFeature: 'patients'
}
},
{
path: 'agenda/clinica',
name: 'admin-agenda-clinica',
component: () => import('@/features/agenda/pages/AgendaClinicaPage.vue'),
meta: {
feature: 'agenda.view',
roles: ['clinic_admin', 'tenant_admin']
}
},
// 👥 Grupos de pacientes
{
path: 'pacientes/grupos',
name: 'admin-pacientes-grupos',
component: () => import('@/features/patients/grupos/GruposPacientesPage.vue'),
meta: {
tenantFeature: 'patients'
}
},
// Recorrências
{
path: 'agenda/recorrencias',
name: 'admin-agenda-recorrencias',
component: () => import('@/features/agenda/pages/AgendaRecorrenciasPage.vue'),
meta: { feature: 'agenda.view', roles: ['clinic_admin', 'tenant_admin'], mode: 'clinic' }
},
// 🏷️ Tags de pacientes
{
path: 'pacientes/tags',
name: 'admin-pacientes-tags',
component: () => import('@/features/patients/tags/TagsPage.vue'),
meta: {
tenantFeature: 'patients'
}
},
// ✅ NOVO: Compromissos determinísticos (tipos)
{
path: 'agenda/compromissos',
name: 'admin-agenda-compromissos',
component: () => import('@/features/agenda/pages/CompromissosDeterminados.vue'),
meta: {
feature: 'agenda.view',
roles: ['clinic_admin', 'tenant_admin']
// ✅ sem tenantScope: a área /admin já está no tenant da clínica pelo fluxo normal
}
},
// 🔗 Link externo para cadastro
{
path: 'pacientes/link-externo',
name: 'admin-pacientes-link-externo',
component: () => import('@/features/patients/cadastro/PatientsExternalLinkPage.vue'),
meta: {
tenantFeature: 'patients'
}
},
// ======================================================
// 👥 PACIENTES (módulo ativável por clínica)
// ======================================================
// 📥 Cadastros recebidos via link externo
{
path: 'pacientes/cadastro/recebidos',
name: 'admin-pacientes-recebidos',
component: () => import('@/features/patients/cadastro/recebidos/CadastrosRecebidosPage.vue'),
meta: {
tenantFeature: 'patients'
}
},
// 📋 Lista de pacientes
{
path: 'pacientes',
name: 'admin-pacientes',
component: () => import('@/features/patients/PatientsListPage.vue'),
meta: {
// ✅ depende do tenant_features.patients
tenantFeature: 'patients'
}
},
// ======================================================
// 🔐 SEGURANÇA
// ======================================================
{
path: 'settings/security',
name: 'admin-settings-security',
component: () => import('@/views/pages/auth/SecurityPage.vue')
},
// Cadastro de paciente (novo)
{
path: 'pacientes/cadastro',
name: 'admin-pacientes-cadastro',
component: () => import('@/features/patients/cadastro/PatientsCadastroPage.vue'),
meta: {
tenantFeature: 'patients'
}
},
// ======================================================
// 🔒 MÓDULO PRO — Online Scheduling
// ======================================================
{
path: 'online-scheduling',
name: 'admin-online-scheduling',
component: () => import('@/views/pages/clinic/OnlineSchedulingAdminPage.vue'),
meta: {
feature: 'online_scheduling.manage'
}
},
// ✏️ Editar paciente
{
path: 'pacientes/cadastro/:id',
name: 'admin-pacientes-cadastro-edit',
component: () => import('@/features/patients/cadastro/PatientsCadastroPage.vue'),
props: true,
meta: {
tenantFeature: 'patients'
}
},
// ======================================================
// 🔒 PRO — Agendamentos Recebidos
// ======================================================
{
path: 'agendamentos-recebidos',
name: 'admin-agendamentos-recebidos',
component: () => import('@/features/agenda/pages/AgendamentosRecebidosPage.vue'),
meta: {
feature: 'online_scheduling.manage'
}
},
// 👥 Grupos de pacientes
{
path: 'pacientes/grupos',
name: 'admin-pacientes-grupos',
component: () => import('@/features/patients/grupos/GruposPacientesPage.vue'),
meta: {
tenantFeature: 'patients'
}
},
// ======================================================
// 💰 FINANCEIRO
// ======================================================
{
path: 'financeiro',
name: 'admin-financeiro',
component: () => import('@/features/financeiro/pages/FinanceiroDashboardPage.vue')
},
{
path: 'financeiro/lancamentos',
name: 'admin-financeiro-lancamentos',
component: () => import('@/features/financeiro/pages/FinanceiroPage.vue')
// 🏷️ Tags de pacientes
{
path: 'pacientes/tags',
name: 'admin-pacientes-tags',
component: () => import('@/features/patients/tags/TagsPage.vue'),
meta: {
tenantFeature: 'patients'
}
},
// 🔗 Link externo para cadastro
{
path: 'pacientes/link-externo',
name: 'admin-pacientes-link-externo',
component: () => import('@/features/patients/cadastro/PatientsExternalLinkPage.vue'),
meta: {
tenantFeature: 'patients'
}
},
// 📥 Cadastros recebidos via link externo
{
path: 'pacientes/cadastro/recebidos',
name: 'admin-pacientes-recebidos',
component: () => import('@/features/patients/cadastro/recebidos/CadastrosRecebidosPage.vue'),
meta: {
tenantFeature: 'patients'
}
},
// ======================================================
// 🔐 SEGURANÇA
// ======================================================
{
path: 'settings/security',
name: 'admin-settings-security',
component: () => import('@/views/pages/auth/SecurityPage.vue')
},
// ======================================================
// 🔒 MÓDULO PRO — Online Scheduling
// ======================================================
{
path: 'online-scheduling',
name: 'admin-online-scheduling',
component: () => import('@/views/pages/clinic/OnlineSchedulingAdminPage.vue'),
meta: {
feature: 'online_scheduling.manage'
}
},
// ======================================================
// 🔒 PRO — Agendamentos Recebidos
// ======================================================
{
path: 'agendamentos-recebidos',
name: 'admin-agendamentos-recebidos',
component: () => import('@/features/agenda/pages/AgendamentosRecebidosPage.vue'),
meta: {
feature: 'online_scheduling.manage'
}
},
// ======================================================
// 💰 FINANCEIRO
// ======================================================
{
path: 'financeiro',
name: 'admin-financeiro',
component: () => import('@/features/financeiro/pages/FinanceiroDashboardPage.vue')
},
{
path: 'financeiro/lancamentos',
name: 'admin-financeiro-lancamentos',
component: () => import('@/features/financeiro/pages/FinanceiroPage.vue')
}
]
}
]
}]
];
+92 -68
View File
@@ -14,78 +14,102 @@
| © 2026 — Todos os direitos reservados
|--------------------------------------------------------------------------
*/
import AppLayout from '@/layout/AppLayout.vue'
import AppLayout from '@/layout/AppLayout.vue';
const configuracoesRoutes = {
path: '/configuracoes',
component: AppLayout,
path: '/configuracoes',
component: AppLayout,
meta: {
requiresAuth: true,
roles: ['admin', 'tenant_admin', 'therapist']
},
meta: {
requiresAuth: true,
roles: ['admin', 'tenant_admin', 'therapist']
},
children: [
{
path: '',
component: () => import('@/layout/ConfiguracoesPage.vue'),
redirect: { name: 'ConfiguracoesAgenda' },
children: [
{
path: '',
component: () => import('@/layout/ConfiguracoesPage.vue'),
redirect: { name: 'ConfiguracoesAgenda' },
children: [
{
path: 'agenda',
name: 'ConfiguracoesAgenda',
component: () => import('@/layout/configuracoes/ConfiguracoesAgendaPage.vue')
},
{
path: 'bloqueios',
name: 'ConfiguracoesBloqueios',
component: () => import('@/layout/configuracoes/BloqueiosPage.vue')
},
{
path: 'agendador',
name: 'ConfiguracoesAgendador',
component: () => import('@/layout/configuracoes/ConfiguracoesAgendadorPage.vue')
},
{
path: 'pagamento',
name: 'ConfiguracoesPagamento',
component: () => import('@/layout/configuracoes/ConfiguracoesPagamentoPage.vue')
},
{
path: 'precificacao',
name: 'ConfiguracoesPrecificacao',
component: () => import('@/layout/configuracoes/ConfiguracoesPrecificacaoPage.vue')
},
{
path: 'descontos',
name: 'ConfiguracoesDescontos',
component: () => import('@/layout/configuracoes/ConfiguracoesDescontosPage.vue')
},
{
path: 'excecoes-financeiras',
name: 'ConfiguracoesExcecoesFinanceiras',
component: () => import('@/layout/configuracoes/ConfiguracoesExcecoesFinanceirasPage.vue')
},
{
path: 'convenios',
name: 'ConfiguracoesConvenios',
component: () => import('@/layout/configuracoes/ConfiguracoesConveniosPage.vue')
},
{
path: 'email-templates',
name: 'ConfiguracoesEmailTemplates',
component: () => import('@/layout/configuracoes/ConfiguracoesEmailTemplatesPage.vue')
},
{
path: 'empresa',
name: 'ConfiguracoesMinhaEmpresa',
component: () => import('@/layout/configuracoes/ConfiguracoesMinhaEmpresaPage.vue')
children: [
{
path: 'agenda',
name: 'ConfiguracoesAgenda',
component: () => import('@/layout/configuracoes/ConfiguracoesAgendaPage.vue')
},
{
path: 'bloqueios',
name: 'ConfiguracoesBloqueios',
component: () => import('@/layout/configuracoes/BloqueiosPage.vue')
},
{
path: 'agendador',
name: 'ConfiguracoesAgendador',
component: () => import('@/layout/configuracoes/ConfiguracoesAgendadorPage.vue')
},
{
path: 'pagamento',
name: 'ConfiguracoesPagamento',
component: () => import('@/layout/configuracoes/ConfiguracoesPagamentoPage.vue')
},
{
path: 'precificacao',
name: 'ConfiguracoesPrecificacao',
component: () => import('@/layout/configuracoes/ConfiguracoesPrecificacaoPage.vue')
},
{
path: 'descontos',
name: 'ConfiguracoesDescontos',
component: () => import('@/layout/configuracoes/ConfiguracoesDescontosPage.vue')
},
{
path: 'excecoes-financeiras',
name: 'ConfiguracoesExcecoesFinanceiras',
component: () => import('@/layout/configuracoes/ConfiguracoesExcecoesFinanceirasPage.vue')
},
{
path: 'convenios',
name: 'ConfiguracoesConvenios',
component: () => import('@/layout/configuracoes/ConfiguracoesConveniosPage.vue')
},
{
path: 'email-templates',
name: 'ConfiguracoesEmailTemplates',
component: () => import('@/layout/configuracoes/ConfiguracoesEmailTemplatesPage.vue')
},
{
path: 'empresa',
name: 'ConfiguracoesMinhaEmpresa',
component: () => import('@/layout/configuracoes/ConfiguracoesMinhaEmpresaPage.vue')
},
{
path: 'canais',
name: 'ConfiguracoesCanais',
component: () => import('@/layout/configuracoes/ConfiguracoesCanaisPage.vue')
},
{
path: 'whatsapp',
name: 'ConfiguracoesWhatsapp',
component: () => import('@/layout/configuracoes/ConfiguracoesWhatsappPage.vue')
},
{
path: 'sms',
name: 'ConfiguracoesSms',
component: () => import('@/layout/configuracoes/ConfiguracoesSmsPage.vue')
},
{
path: 'sms-canal',
name: 'ConfiguracoesSmsCanal',
component: () => import('@/views/pages/notifications/SmsChannelSetupPage.vue')
},
{
path: 'recursos-extras',
name: 'ConfiguracoesRecursosExtras',
component: () => import('@/layout/configuracoes/ConfiguracoesRecursosExtrasPage.vue')
}
]
}
]
}
]
}
export default configuracoesRoutes
]
};
export default configuracoesRoutes;
+50 -50
View File
@@ -18,61 +18,61 @@
// Acesso controlado por `platform_roles` no guard (não por tenant role).
// meta.editorArea: true sinaliza ao guard que use a verificação de plataforma.
//
import AppLayout from '@/layout/AppLayout.vue'
import AppLayout from '@/layout/AppLayout.vue';
export default {
path: '/editor',
component: AppLayout,
path: '/editor',
component: AppLayout,
meta: { area: 'editor', requiresAuth: true, editorArea: true },
meta: { area: 'editor', requiresAuth: true, editorArea: true },
children: [
// ======================================================
// 📊 DASHBOARD
// ======================================================
{
path: '',
name: 'editor.dashboard',
component: () => import('@/views/pages/editor/EditorDashboard.vue')
},
children: [
// ======================================================
// 📊 DASHBOARD
// ======================================================
{
path: '',
name: 'editor.dashboard',
component: () => import('@/views/pages/editor/EditorDashboard.vue')
},
// ======================================================
// 📚 CURSOS
// ======================================================
{
path: 'cursos',
name: 'editor-cursos',
// placeholder — módulo de microlearning a implementar
component: () => import('@/views/pages/editor/EditorDashboard.vue')
},
// ======================================================
// 📚 CURSOS
// ======================================================
{
path: 'cursos',
name: 'editor-cursos',
// placeholder — módulo de microlearning a implementar
component: () => import('@/views/pages/editor/EditorDashboard.vue')
},
// ======================================================
// 📦 MÓDULOS
// ======================================================
{
path: 'modulos',
name: 'editor-modulos',
// placeholder
component: () => import('@/views/pages/editor/EditorDashboard.vue')
},
// ======================================================
// 📦 MÓDULOS
// ======================================================
{
path: 'modulos',
name: 'editor-modulos',
// placeholder
component: () => import('@/views/pages/editor/EditorDashboard.vue')
},
// ======================================================
// ✅ PUBLICADOS
// ======================================================
{
path: 'publicados',
name: 'editor-publicados',
// placeholder
component: () => import('@/views/pages/editor/EditorDashboard.vue')
},
// ======================================================
// ✅ PUBLICADOS
// ======================================================
{
path: 'publicados',
name: 'editor-publicados',
// placeholder
component: () => import('@/views/pages/editor/EditorDashboard.vue')
},
// ======================================================
// 💳 MEU PLANO (assinatura pessoal do editor)
// ======================================================
{
path: 'meu-plano',
name: 'editor-meu-plano',
component: () => import('@/views/pages/billing/TherapistMeuPlanoPage.vue')
}
]
}
// ======================================================
// 💳 MEU PLANO (assinatura pessoal do editor)
// ======================================================
{
path: 'meu-plano',
name: 'editor-meu-plano',
component: () => import('@/views/pages/billing/TherapistMeuPlanoPage.vue')
}
]
};
+23 -23
View File
@@ -14,28 +14,28 @@
| © 2026 — Todos os direitos reservados
|--------------------------------------------------------------------------
*/
import AppLayout from '@/layout/AppLayout.vue'
import AppLayout from '@/layout/AppLayout.vue';
export default {
path: '/features',
component: AppLayout,
meta: { requiresAuth: true }, // roles: se você quiser travar aqui também
children: [
// Patients
{
path: 'patients',
name: 'features.patients.list',
component: () => import('@/features/patients/PatientsListPage.vue') // ajuste se seu arquivo tiver outro nome
},
{
path: 'patients/cadastro',
name: 'features.patients.create',
component: () => import('@/features/patients/cadastro/PatientsCadastroPage.vue')
},
{
path: 'patients/cadastro/:id',
name: 'features.patients.edit',
component: () => import('@/features/patients/cadastro/PatientsCadastroPage.vue')
}
]
}
path: '/features',
component: AppLayout,
meta: { requiresAuth: true }, // roles: se você quiser travar aqui também
children: [
// Patients
{
path: 'patients',
name: 'features.patients.list',
component: () => import('@/features/patients/PatientsListPage.vue') // ajuste se seu arquivo tiver outro nome
},
{
path: 'patients/cadastro',
name: 'features.patients.create',
component: () => import('@/features/patients/cadastro/PatientsCadastroPage.vue')
},
{
path: 'patients/cadastro/:id',
name: 'features.patients.edit',
component: () => import('@/features/patients/cadastro/PatientsCadastroPage.vue')
}
]
};
+29 -29
View File
@@ -15,35 +15,35 @@
|--------------------------------------------------------------------------
*/
export default {
path: '/',
children: [
{
path: 'landing',
name: 'landing',
component: () => import('@/views/pages/Landing.vue')
},
path: '/',
children: [
{
path: 'landing',
name: 'landing',
component: () => import('@/views/pages/Landing.vue')
},
// 404
{
path: 'pages/notfound',
name: 'notfound',
component: () => import('@/views/pages/NotFound.vue')
},
// 404
{
path: 'pages/notfound',
name: 'notfound',
component: () => import('@/views/pages/NotFound.vue')
},
// 403 (Acesso negado - RBAC)
{
path: 'pages/access', // ❗ SEM barra inicial aqui
name: 'AccessDenied',
component: () => import('@/views/pages/misc/AccessDeniedPage.vue'),
meta: {
requiresAuth: true
}
},
// 403 (Acesso negado - RBAC)
{
path: 'pages/access', // ❗ SEM barra inicial aqui
name: 'AccessDenied',
component: () => import('@/views/pages/misc/AccessDeniedPage.vue'),
meta: {
requiresAuth: true
}
},
// Catch-all (SEMPRE o último)
{
path: ':pathMatch(.*)*',
redirect: { name: 'notfound' }
}
]
}
// Catch-all (SEMPRE o último)
{
path: ':pathMatch(.*)*',
redirect: { name: 'notfound' }
}
]
};
+21 -21
View File
@@ -14,27 +14,27 @@
| © 2026 — Todos os direitos reservados
|--------------------------------------------------------------------------
*/
import AppLayout from '@/layout/AppLayout.vue'
import AppLayout from '@/layout/AppLayout.vue';
export default {
path: '/portal',
component: AppLayout,
meta: { area: 'portal', requiresAuth: true, profileRole: 'portal_user' },
children: [
{ path: '', name: 'portal.dashboard', component: () => import('@/views/pages/portal/PortalDashboard.vue') },
{
path: 'sessoes',
name: 'portal-sessoes',
component: () => import('@/views/pages/portal/MinhasSessoes.vue')
},
path: '/portal',
component: AppLayout,
meta: { area: 'portal', requiresAuth: true, profileRole: 'portal_user' },
children: [
{ path: '', name: 'portal.dashboard', component: () => import('@/views/pages/portal/PortalDashboard.vue') },
{
path: 'sessoes',
name: 'portal-sessoes',
component: () => import('@/views/pages/portal/MinhasSessoes.vue')
},
// ======================================================
// 💳 MEU PLANO (assinatura pessoal do paciente)
// ======================================================
{
path: 'meu-plano',
name: 'portal-meu-plano',
component: () => import('@/views/pages/billing/TherapistMeuPlanoPage.vue')
}
]
}
// ======================================================
// 💳 MEU PLANO (assinatura pessoal do paciente)
// ======================================================
{
path: 'meu-plano',
name: 'portal-meu-plano',
component: () => import('@/views/pages/billing/TherapistMeuPlanoPage.vue')
}
]
};
+11 -11
View File
@@ -38,19 +38,19 @@ export default {
component: () => import('@/views/pages/public/CadastroPacienteExterno.vue'),
meta: { public: true }
},
// ✅ convite de clinicas
{
path: '/accept-invite',
name: 'accept-invite',
component: () => import('@/views/pages/public/AcceptInvitePage.vue'),
meta: { public: true }
},
// ✅ convite de clinicas
{
path: '/accept-invite',
name: 'accept-invite',
component: () => import('@/views/pages/public/AcceptInvitePage.vue'),
meta: { public: true }
},
// ✅ agendador online público
{
path: '/agendar/:slug',
name: 'agendador.publico',
component: () => import('@/views/pages/public/AgendadorPublicoPage.vue'),
meta: { public: true }
path: '/agendar/:slug',
name: 'agendador.publico',
component: () => import('@/views/pages/public/AgendadorPublicoPage.vue'),
meta: { public: true }
}
]
};
+119 -101
View File
@@ -14,106 +14,124 @@
| © 2026 — Todos os direitos reservados
|--------------------------------------------------------------------------
*/
import AppLayout from '@/layout/AppLayout.vue'
import AppLayout from '@/layout/AppLayout.vue';
export default {
path: '/saas',
component: AppLayout,
meta: { requiresAuth: true, saasAdmin: true },
children: [
{
path: '',
name: 'saas-dashboard',
component: () => import('@/views/pages/saas/SaasDashboard.vue')
},
{
path: 'plans',
name: 'saas-plans',
component: () => import('@/views/pages/saas/SaasPlansPage.vue')
},
{
path: 'plans-public',
name: 'saas-plans-public',
component: () => import('@/views/pages/saas/SaasPlansPublicPage.vue')
},
{
path: 'features',
name: 'saas-features',
component: () => import('@/views/pages/saas/SaasFeaturesPage.vue')
},
{
path: 'plan-features',
name: 'saas-plan-features',
component: () => import('@/views/pages/saas/SaasPlanFeaturesMatrixPage.vue')
},
{
path: 'plan-limits',
name: 'saas-plan-limits',
component: () => import('@/views/pages/saas/SaasPlanLimitsPage.vue')
},
{
path: 'subscriptions',
name: 'saas-subscriptions',
component: () => import('@/views/pages/saas/SaasSubscriptionsPage.vue')
},
{
path: 'subscription-events',
name: 'saas-subscription-events',
component: () => import('@/views/pages/saas/SaasSubscriptionEventsPage.vue')
},
{
path: 'subscription-health',
name: 'saas-subscription-health',
component: () => import('@/views/pages/saas/SaasSubscriptionHealthPage.vue')
},
{
path: 'subscription-intents',
name: 'saas.subscriptionIntents',
component: () => import('@/views/pages/saas/SubscriptionIntentsPage.vue'),
meta: { requiresAuth: true, saasAdmin: true }
},
{
path: 'tenants',
name: 'saas-tenants',
component: () => import('@/views/pages/saas/SaasPlaceholder.vue')
},
{
path: 'feriados',
name: 'saas-feriados',
component: () => import('@/views/pages/saas/SaasFeriadosPage.vue')
},
{
path: 'docs',
name: 'saas-docs',
component: () => import('@/views/pages/saas/SaasDocsPage.vue')
},
{
path: 'faq',
name: 'saas-faq',
component: () => import('@/views/pages/saas/SaasFaqPage.vue')
},
{
path: 'support',
name: 'saas-support',
component: () => import('@/views/pages/saas/SaasSupportPage.vue'),
meta: { requiresAuth: true, saasAdmin: true }
},
{
path: 'login-carousel',
name: 'saas-login-carousel',
component: () => import('@/views/pages/saas/SaasLoginCarousel.vue')
},
{
path: 'global-notices',
name: 'saas-global-notices',
component: () => import('@/views/pages/saas/SaasGlobalNoticesPage.vue'),
meta: { requiresAuth: true, saasAdmin: true }
},
{
path: 'email-templates',
name: 'saas-email-templates',
component: () => import('@/views/pages/saas/SaasEmailTemplatesPage.vue'),
meta: { requiresAuth: true, saasAdmin: true }
}
]
}
path: '/saas',
component: AppLayout,
meta: { requiresAuth: true, saasAdmin: true },
children: [
{
path: '',
name: 'saas-dashboard',
component: () => import('@/views/pages/saas/SaasDashboard.vue')
},
{
path: 'plans',
name: 'saas-plans',
component: () => import('@/views/pages/saas/SaasPlansPage.vue')
},
{
path: 'plans-public',
name: 'saas-plans-public',
component: () => import('@/views/pages/saas/SaasPlansPublicPage.vue')
},
{
path: 'features',
name: 'saas-features',
component: () => import('@/views/pages/saas/SaasFeaturesPage.vue')
},
{
path: 'plan-features',
name: 'saas-plan-features',
component: () => import('@/views/pages/saas/SaasPlanFeaturesMatrixPage.vue')
},
{
path: 'plan-limits',
name: 'saas-plan-limits',
component: () => import('@/views/pages/saas/SaasPlanLimitsPage.vue')
},
{
path: 'subscriptions',
name: 'saas-subscriptions',
component: () => import('@/views/pages/saas/SaasSubscriptionsPage.vue')
},
{
path: 'subscription-events',
name: 'saas-subscription-events',
component: () => import('@/views/pages/saas/SaasSubscriptionEventsPage.vue')
},
{
path: 'subscription-health',
name: 'saas-subscription-health',
component: () => import('@/views/pages/saas/SaasSubscriptionHealthPage.vue')
},
{
path: 'subscription-intents',
name: 'saas.subscriptionIntents',
component: () => import('@/views/pages/saas/SubscriptionIntentsPage.vue'),
meta: { requiresAuth: true, saasAdmin: true }
},
{
path: 'tenants',
name: 'saas-tenants',
component: () => import('@/views/pages/saas/SaasPlaceholder.vue')
},
{
path: 'feriados',
name: 'saas-feriados',
component: () => import('@/views/pages/saas/SaasFeriadosPage.vue')
},
{
path: 'docs',
name: 'saas-docs',
component: () => import('@/views/pages/saas/SaasDocsPage.vue')
},
{
path: 'faq',
name: 'saas-faq',
component: () => import('@/views/pages/saas/SaasFaqPage.vue')
},
{
path: 'support',
name: 'saas-support',
component: () => import('@/views/pages/saas/SaasSupportPage.vue'),
meta: { requiresAuth: true, saasAdmin: true }
},
{
path: 'login-carousel',
name: 'saas-login-carousel',
component: () => import('@/views/pages/saas/SaasLoginCarousel.vue')
},
{
path: 'global-notices',
name: 'saas-global-notices',
component: () => import('@/views/pages/saas/SaasGlobalNoticesPage.vue'),
meta: { requiresAuth: true, saasAdmin: true }
},
{
path: 'email-templates',
name: 'saas-email-templates',
component: () => import('@/views/pages/saas/SaasEmailTemplatesPage.vue'),
meta: { requiresAuth: true, saasAdmin: true }
},
{
path: 'notification-templates',
name: 'saas-notification-templates',
component: () => import('@/views/pages/saas/SaasNotificationTemplatesPage.vue'),
meta: { requiresAuth: true, saasAdmin: true }
},
{
path: 'whatsapp',
name: 'saas-whatsapp',
component: () => import('@/views/pages/saas/SaasWhatsappPage.vue'),
meta: { requiresAuth: true, saasAdmin: true }
},
{
path: 'addons',
name: 'saas-addons',
component: () => import('@/views/pages/saas/SaasAddonsPage.vue'),
meta: { requiresAuth: true, saasAdmin: true }
}
]
};
+39 -39
View File
@@ -14,48 +14,48 @@
| © 2026 — Todos os direitos reservados
|--------------------------------------------------------------------------
*/
import AppLayout from '@/layout/AppLayout.vue'
import AppLayout from '@/layout/AppLayout.vue';
export default {
path: '/supervisor',
component: AppLayout,
path: '/supervisor',
component: AppLayout,
// tenantScope: 'supervisor' → o guard troca automaticamente para o tenant
// com kind='supervisor' quando o usuário navega para esta área.
meta: {
area: 'supervisor',
requiresAuth: true,
roles: ['supervisor'],
tenantScope: 'supervisor'
},
children: [
// ======================================================
// 📊 DASHBOARD
// ======================================================
{
path: '',
name: 'supervisor.dashboard',
component: () => import('@/views/pages/supervisor/SupervisorDashboard.vue')
// tenantScope: 'supervisor' → o guard troca automaticamente para o tenant
// com kind='supervisor' quando o usuário navega para esta área.
meta: {
area: 'supervisor',
requiresAuth: true,
roles: ['supervisor'],
tenantScope: 'supervisor'
},
// ======================================================
// 🎓 SALA DE SUPERVISÃO
// ======================================================
{
path: 'sala',
name: 'supervisor.sala',
component: () => import('@/views/pages/supervisor/SupervisaoSalaPage.vue'),
meta: { feature: 'supervisor.access' }
},
children: [
// ======================================================
// 📊 DASHBOARD
// ======================================================
{
path: '',
name: 'supervisor.dashboard',
component: () => import('@/views/pages/supervisor/SupervisorDashboard.vue')
},
// ======================================================
// 💳 MEU PLANO
// ======================================================
{
path: 'meu-plano',
name: 'supervisor.meu-plano',
component: () => import('@/views/pages/billing/TherapistMeuPlanoPage.vue')
}
]
}
// ======================================================
// 🎓 SALA DE SUPERVISÃO
// ======================================================
{
path: 'sala',
name: 'supervisor.sala',
component: () => import('@/views/pages/supervisor/SupervisaoSalaPage.vue'),
meta: { feature: 'supervisor.access' }
},
// ======================================================
// 💳 MEU PLANO
// ======================================================
{
path: 'meu-plano',
name: 'supervisor.meu-plano',
component: () => import('@/views/pages/billing/TherapistMeuPlanoPage.vue')
}
]
};
+153 -153
View File
@@ -14,173 +14,173 @@
| © 2026 — Todos os direitos reservados
|--------------------------------------------------------------------------
*/
import AppLayout from '@/layout/AppLayout.vue'
import AppLayout from '@/layout/AppLayout.vue';
export default [
// ======================================================
// 🚀 SETUP WIZARD — fora do AppLayout (fullscreen)
// ======================================================
{
path: '/therapist/setup',
name: 'therapist.setup',
component: () => import('@/features/setup/SetupWizardPage.vue'),
meta: { area: 'therapist', requiresAuth: true, roles: ['therapist'], fullscreen: true },
},
{
path: '/therapist',
component: AppLayout,
meta: { area: 'therapist', requiresAuth: true, roles: ['therapist'] },
children: [
// ======================================================
// 📊 DASHBOARD
// ======================================================
{ path: '', name: 'therapist.dashboard', component: () => import('@/views/pages/therapist/TherapistDashboard.vue') },
// ======================================================
// 📅 AGENDA
// 🚀 SETUP WIZARD — fora do AppLayout (fullscreen)
// ======================================================
{
path: 'agenda',
name: 'therapist-agenda',
component: () => import('@/features/agenda/pages/AgendaTerapeutaPage.vue'),
meta: {
feature: 'agenda.view'
}
path: '/therapist/setup',
name: 'therapist.setup',
component: () => import('@/features/setup/SetupWizardPage.vue'),
meta: { area: 'therapist', requiresAuth: true, roles: ['therapist'], fullscreen: true }
},
// Recorrências
{
path: 'agenda/recorrencias',
name: 'therapist-agenda-recorrencias',
component: () => import('@/features/agenda/pages/AgendaRecorrenciasPage.vue'),
meta: { feature: 'agenda.view', mode: 'therapist' }
},
path: '/therapist',
component: AppLayout,
// ✅ Compromissos determinísticos
{
path: 'agenda/compromissos',
name: 'therapist-agenda-compromissos',
component: () => import('@/features/agenda/pages/CompromissosDeterminados.vue'),
meta: {
feature: 'agenda.view',
roles: ['therapist']
// ✅ sem tenantScope
}
},
meta: { area: 'therapist', requiresAuth: true, roles: ['therapist'] },
// ======================================================
// 💳 MEU PLANO
// ======================================================
{
path: 'meu-plano',
name: 'therapist-meu-plano',
component: () => import('@/views/pages/billing/TherapistMeuPlanoPage.vue')
},
{
path: 'upgrade',
name: 'therapist-upgrade',
component: () => import('@/views/pages/billing/TherapistUpgradePage.vue')
},
children: [
// ======================================================
// 📊 DASHBOARD
// ======================================================
{ path: '', name: 'therapist.dashboard', component: () => import('@/views/pages/therapist/TherapistDashboard.vue') },
// ======================================================
// 👥 PATIENTS
// ======================================================
{
path: 'patients',
name: 'therapist-patients',
component: () => import('@/features/patients/PatientsListPage.vue')
},
{
path: 'patients/cadastro',
name: 'therapist-patients-create',
component: () => import('@/features/patients/cadastro/PatientsCadastroPage.vue')
},
{
path: 'patients/cadastro/:id',
name: 'therapist-patients-edit',
component: () => import('@/features/patients/cadastro/PatientsCadastroPage.vue'),
props: true
},
{
path: 'patients/grupos',
name: 'therapist-patients-groups',
component: () => import('@/features/patients/grupos/GruposPacientesPage.vue')
},
{
path: 'patients/tags',
name: 'therapist-patients-tags',
component: () => import('@/features/patients/tags/TagsPage.vue')
},
{
path: 'patients/link-externo',
name: 'therapist-patients-link-externo',
component: () => import('@/features/patients/cadastro/PatientsExternalLinkPage.vue')
},
{
path: 'patients/cadastro/recebidos',
name: 'therapist-patients-recebidos',
component: () => import('@/features/patients/cadastro/recebidos/CadastrosRecebidosPage.vue')
},
// ======================================================
// 📅 AGENDA
// ======================================================
{
path: 'agenda',
name: 'therapist-agenda',
component: () => import('@/features/agenda/pages/AgendaTerapeutaPage.vue'),
meta: {
feature: 'agenda.view'
}
},
// ======================================================
// 🔒 PRO — Online Scheduling
// ======================================================
{
path: 'online-scheduling',
name: 'therapist-online-scheduling',
component: () => import('@/views/pages/therapist/OnlineSchedulingPage.vue'),
meta: {
feature: 'online_scheduling.manage'
}
},
// Recorrências
{
path: 'agenda/recorrencias',
name: 'therapist-agenda-recorrencias',
component: () => import('@/features/agenda/pages/AgendaRecorrenciasPage.vue'),
meta: { feature: 'agenda.view', mode: 'therapist' }
},
// ======================================================
// 🔒 PRO — Agendamentos Recebidos
// ======================================================
{
path: 'agendamentos-recebidos',
name: 'therapist-agendamentos-recebidos',
component: () => import('@/features/agenda/pages/AgendamentosRecebidosPage.vue'),
meta: {
feature: 'online_scheduling.manage'
}
},
// ✅ Compromissos determinísticos
{
path: 'agenda/compromissos',
name: 'therapist-agenda-compromissos',
component: () => import('@/features/agenda/pages/CompromissosDeterminados.vue'),
meta: {
feature: 'agenda.view',
roles: ['therapist']
// ✅ sem tenantScope
}
},
// ======================================================
// 💰 FINANCEIRO
// ======================================================
{
path: 'financeiro',
name: 'therapist-financeiro',
component: () => import('@/features/financeiro/pages/FinanceiroDashboardPage.vue')
},
{
path: 'financeiro/lancamentos',
name: 'therapist-financeiro-lancamentos',
component: () => import('@/features/financeiro/pages/FinanceiroPage.vue')
},
// ======================================================
// 💳 MEU PLANO
// ======================================================
{
path: 'meu-plano',
name: 'therapist-meu-plano',
component: () => import('@/views/pages/billing/TherapistMeuPlanoPage.vue')
},
{
path: 'upgrade',
name: 'therapist-upgrade',
component: () => import('@/views/pages/billing/TherapistUpgradePage.vue')
},
// ======================================================
// 📈 RELATÓRIOS
// ======================================================
{
path: 'relatorios',
name: 'therapist-relatorios',
component: () => import('@/views/pages/therapist/RelatoriosPage.vue'),
meta: { feature: 'agenda.view' }
},
// ======================================================
// 👥 PATIENTS
// ======================================================
{
path: 'patients',
name: 'therapist-patients',
component: () => import('@/features/patients/PatientsListPage.vue')
},
{
path: 'patients/cadastro',
name: 'therapist-patients-create',
component: () => import('@/features/patients/cadastro/PatientsCadastroPage.vue')
},
{
path: 'patients/cadastro/:id',
name: 'therapist-patients-edit',
component: () => import('@/features/patients/cadastro/PatientsCadastroPage.vue'),
props: true
},
{
path: 'patients/grupos',
name: 'therapist-patients-groups',
component: () => import('@/features/patients/grupos/GruposPacientesPage.vue')
},
{
path: 'patients/tags',
name: 'therapist-patients-tags',
component: () => import('@/features/patients/tags/TagsPage.vue')
},
{
path: 'patients/link-externo',
name: 'therapist-patients-link-externo',
component: () => import('@/features/patients/cadastro/PatientsExternalLinkPage.vue')
},
{
path: 'patients/cadastro/recebidos',
name: 'therapist-patients-recebidos',
component: () => import('@/features/patients/cadastro/recebidos/CadastrosRecebidosPage.vue')
},
// ======================================================
// 🔐 SECURITY
// ======================================================
{
path: 'settings/security',
name: 'therapist-settings-security',
component: () => import('@/views/pages/auth/SecurityPage.vue')
// ======================================================
// 🔒 PRO — Online Scheduling
// ======================================================
{
path: 'online-scheduling',
name: 'therapist-online-scheduling',
component: () => import('@/views/pages/therapist/OnlineSchedulingPage.vue'),
meta: {
feature: 'online_scheduling.manage'
}
},
// ======================================================
// 🔒 PRO — Agendamentos Recebidos
// ======================================================
{
path: 'agendamentos-recebidos',
name: 'therapist-agendamentos-recebidos',
component: () => import('@/features/agenda/pages/AgendamentosRecebidosPage.vue'),
meta: {
feature: 'online_scheduling.manage'
}
},
// ======================================================
// 💰 FINANCEIRO
// ======================================================
{
path: 'financeiro',
name: 'therapist-financeiro',
component: () => import('@/features/financeiro/pages/FinanceiroDashboardPage.vue')
},
{
path: 'financeiro/lancamentos',
name: 'therapist-financeiro-lancamentos',
component: () => import('@/features/financeiro/pages/FinanceiroPage.vue')
},
// ======================================================
// 📈 RELATÓRIOS
// ======================================================
{
path: 'relatorios',
name: 'therapist-relatorios',
component: () => import('@/views/pages/therapist/RelatoriosPage.vue'),
meta: { feature: 'agenda.view' }
},
// ======================================================
// 🔐 SECURITY
// ======================================================
{
path: 'settings/security',
name: 'therapist-settings-security',
component: () => import('@/views/pages/auth/SecurityPage.vue')
}
]
}
]
}]
];