Documentos Pacientes, Template Documentos Pacientes Saas, Documentos prontuários, Documentos Externos, Visualização Externa, Permissão de Visualização, Render Otimização

This commit is contained in:
Leonardo
2026-03-30 14:08:19 -03:00
parent 0658e2e9bf
commit d088a89fb7
112 changed files with 115867 additions and 5266 deletions
+20 -20
View File
@@ -60,7 +60,7 @@ function readPendingInviteToken() {
function clearPendingInviteToken() {
try {
sessionStorage.removeItem(PENDING_INVITE_TOKEN_KEY);
} catch (_) {}
} catch (_) { }
}
function isUuid(v) {
@@ -382,7 +382,7 @@ export function applyGuards(router) {
localStorage.removeItem('tenant_id');
localStorage.removeItem('tenant');
localStorage.removeItem('currentTenantId');
} catch (_) {}
} catch (_) { }
_perfEnd();
return { path: '/portal' };
@@ -438,11 +438,11 @@ export function applyGuards(router) {
if (['therapist', 'supervisor'].includes(_roleNorm) && _ent.loadedForUser !== uid) {
try {
await _ent.loadForUser(uid);
} catch {}
} catch { }
}
await ensureMenuBuilt({ uid, tenantId: _tid, tenantRole: _role, globalRole });
}
} catch {}
} catch { }
}
_perfEnd();
return true;
@@ -455,7 +455,7 @@ export function applyGuards(router) {
localStorage.removeItem('tenant_id');
localStorage.removeItem('tenant');
localStorage.removeItem('currentTenantId');
} catch (_) {}
} catch (_) { }
}
// ==========================================
@@ -489,7 +489,7 @@ export function applyGuards(router) {
try {
const menuStore = useMenuStore();
if (typeof menuStore.reset === 'function') menuStore.reset();
} catch {}
} catch { }
}
// ================================
@@ -548,7 +548,7 @@ export function applyGuards(router) {
// ================================
// 🚫 SaaS master: bloqueia tenant-app por padrão
// ✅ Mas libera rotas de DEMO em DEV (Sakai)
// ✅ Mas libera rotas de DEMO em DEV
// ================================
logGuard('saas.lockdown?');
@@ -558,7 +558,7 @@ export function applyGuards(router) {
if (isSaas) {
const isSaasArea = to.path === '/saas' || to.path.startsWith('/saas/');
// Rotas do Sakai Demo (no seu caso ficam em /demo/*)
// Rotas do Tema Demo (no seu caso ficam em /demo/*)
const isDemoArea = import.meta.env.DEV && (to.path === '/demo' || to.path.startsWith('/demo/'));
// Se for demo em DEV, libera
@@ -693,19 +693,19 @@ export function applyGuards(router) {
try {
const entX = useEntitlementsStore();
if (typeof entX.invalidate === 'function') entX.invalidate();
} catch {}
} catch { }
// ✅ NÃO invalidar o cache inteiro — invalida somente o tenant anterior
try {
const tfX = useTenantFeaturesStore();
if (typeof tfX.invalidate === 'function' && oldTenantId) tfX.invalidate(oldTenantId);
} catch {}
} catch { }
// ✅ troca tenant => menu precisa recompôr (contexto mudou)
try {
const menuStore = useMenuStore();
if (typeof menuStore.reset === 'function') menuStore.reset();
} catch {}
} catch { }
} else if (!desiredTenantId) {
logGuard('[guards] tenantScope sem match', {
scope,
@@ -858,7 +858,7 @@ export function applyGuards(router) {
globalRoleCacheUid = null;
globalRoleCache = null;
try { resetAjuda(); } catch (_) {}
try { resetAjuda(); } catch (_) { }
// ✅ FIX: limpa o localStorage de tenant na saída
// Sem isso, o próximo login restaura o tenant do usuário anterior.
@@ -866,27 +866,27 @@ export function applyGuards(router) {
localStorage.removeItem('tenant_id');
localStorage.removeItem('tenant');
localStorage.removeItem('currentTenantId');
} catch (_) {}
} catch (_) { }
try {
const tf = useTenantFeaturesStore();
if (typeof tf.invalidate === 'function') tf.invalidate();
} catch {}
} catch { }
try {
const ent = useEntitlementsStore();
if (typeof ent.invalidate === 'function') ent.invalidate();
} catch {}
} catch { }
try {
const tenant = useTenantStore();
if (typeof tenant.reset === 'function') tenant.reset();
} catch {}
} catch { }
try {
const menuStore = useMenuStore();
if (typeof menuStore.reset === 'function') menuStore.reset();
} catch {}
} catch { }
return;
}
@@ -912,17 +912,17 @@ export function applyGuards(router) {
try {
const tf = useTenantFeaturesStore();
if (typeof tf.invalidate === 'function') tf.invalidate();
} catch {}
} catch { }
try {
const ent = useEntitlementsStore();
if (typeof ent.invalidate === 'function') ent.invalidate();
} catch {}
} catch { }
try {
const menuStore = useMenuStore();
if (typeof menuStore.reset === 'function') menuStore.reset();
} catch {}
} catch { }
// tenantStore carrega de novo no fluxo do guard quando precisar
return;
+7 -5
View File
@@ -16,20 +16,22 @@
*/
import RouterPassthrough from '@/layout/RouterPassthrough.vue';
// Rotas compartilhadas — acessíveis por qualquer role autenticada
export default {
path: 'account',
component: RouterPassthrough,
meta: { requiresAuth: true, area: 'account' },
meta: { requiresAuth: true },
children: [
{
path: '',
redirect: { name: 'account-profile' }
},
{
path: 'profile',
name: 'account-profile',
component: () => import('@/views/pages/account/ProfilePage.vue')
},
{
path: 'negocio',
name: 'account-negocio',
component: () => import('@/views/pages/account/NegocioPage.vue')
},
{
path: 'security',
name: 'account-security',
+16
View File
@@ -126,6 +126,12 @@ export default {
component: () => import('@/features/patients/tags/TagsPage.vue'),
meta: { tenantFeature: 'patients' }
},
{
path: 'pacientes/medicos',
name: 'admin-pacientes-medicos',
component: () => import('@/features/patients/medicos/MedicosPage.vue'),
meta: { tenantFeature: 'patients' }
},
{
path: 'pacientes/link-externo',
name: 'admin-pacientes-link-externo',
@@ -139,6 +145,16 @@ export default {
meta: { tenantFeature: 'patients' }
},
// ======================================================
// 📄 DOCUMENTOS
// ======================================================
{
path: 'documents/templates',
name: 'admin-documents-templates',
component: () => import('@/features/documents/DocumentTemplatesPage.vue'),
meta: { feature: 'documents.templates' }
},
// ======================================================
// 🔐 SEGURANÇA
// ======================================================
+7
View File
@@ -51,6 +51,13 @@ export default {
name: 'agendador.publico',
component: () => import('@/views/pages/public/AgendadorPublicoPage.vue'),
meta: { public: true }
},
// ✅ documento compartilhado via link temporário
{
path: '/shared/document/:token',
name: 'shared.document',
component: () => import('@/views/pages/public/SharedDocumentPage.vue'),
meta: { public: true }
}
]
};
+6
View File
@@ -138,6 +138,12 @@ export default {
name: 'saas-addons',
component: () => import('@/views/pages/saas/SaasAddonsPage.vue'),
meta: { requiresAuth: true, saasAdmin: true }
},
{
path: 'document-templates',
name: 'saas-document-templates',
component: () => import('@/views/pages/saas/SaasDocumentTemplatesPage.vue'),
meta: { requiresAuth: true, saasAdmin: true }
}
]
};
+28
View File
@@ -110,6 +110,11 @@ export default {
name: 'therapist-patients-tags',
component: () => import('@/features/patients/tags/TagsPage.vue')
},
{
path: 'patients/medicos',
name: 'therapist-patients-medicos',
component: () => import('@/features/patients/medicos/MedicosPage.vue')
},
{
path: 'patients/link-externo',
name: 'therapist-patients-link-externo',
@@ -121,6 +126,29 @@ export default {
component: () => import('@/features/patients/cadastro/recebidos/CadastrosRecebidosPage.vue')
},
// ======================================================
// 📄 DOCUMENTOS
// ======================================================
{
path: 'documents',
name: 'therapist-documents',
component: () => import('@/features/documents/DocumentsListPage.vue'),
meta: { feature: 'documents.upload' }
},
{
path: 'documents/templates',
name: 'therapist-documents-templates',
component: () => import('@/features/documents/DocumentTemplatesPage.vue'),
meta: { feature: 'documents.templates' }
},
{
path: 'patients/:id/documents',
name: 'therapist-patient-documents',
component: () => import('@/features/documents/DocumentsListPage.vue'),
props: true,
meta: { feature: 'documents.upload' }
},
// ======================================================
// 🔒 PRO — Online Scheduling
// ======================================================