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
+164 -168
View File
@@ -19,64 +19,68 @@
// 📦 Importação dos menus base por área
// ======================================================
import adminMenu from './menus/clinic.menu'
import therapistMenu from './menus/therapist.menu'
import supervisorMenu from './menus/supervisor.menu'
import editorMenu from './menus/editor.menu'
import portalMenu from './menus/portal.menu'
import saasMenu from './menus/saas.menu'
import adminMenu from './menus/clinic.menu';
import therapistMenu from './menus/therapist.menu';
import supervisorMenu from './menus/supervisor.menu';
import editorMenu from './menus/editor.menu';
import portalMenu from './menus/portal.menu';
import saasMenu from './menus/saas.menu';
import { useSaasHealthStore } from '@/stores/saasHealthStore'
import { useTenantFeaturesStore } from '@/stores/tenantFeaturesStore'
import { useEntitlementsStore } from '@/stores/entitlementsStore'
import { countAtencao } from '@/composables/useDocsHealth'
import { useSaasHealthStore } from '@/stores/saasHealthStore';
import { useTenantFeaturesStore } from '@/stores/tenantFeaturesStore';
import { useEntitlementsStore } from '@/stores/entitlementsStore';
import { countAtencao } from '@/composables/useDocsHealth';
// ======================================================
// 🎭 Mapeamento de role → menu base
// ======================================================
const MENUS = {
clinic_admin: adminMenu,
tenant_admin: adminMenu, // alias
therapist: therapistMenu,
supervisor: supervisorMenu,
editor: editorMenu,
patient: portalMenu,
portal_user: portalMenu, // alias (globalRole do paciente)
saas_admin: saasMenu
}
clinic_admin: adminMenu,
tenant_admin: adminMenu, // alias
therapist: therapistMenu,
supervisor: supervisorMenu,
editor: editorMenu,
patient: portalMenu,
portal_user: portalMenu, // alias (globalRole do paciente)
saas_admin: saasMenu
};
// ======================================================
// 🧠 Helpers
// ======================================================
function resolveMenu (builder, ctx) {
if (!builder) return []
try {
return typeof builder === 'function' ? builder(ctx) : builder
} catch (e) {
// se um builder estourar, não derruba o app: cai no fallback
console.warn('[NAV] menu builder error:', e)
return []
}
function resolveMenu(builder, ctx) {
if (!builder) return [];
try {
return typeof builder === 'function' ? builder(ctx) : builder;
} catch (e) {
// se um builder estourar, não derruba o app: cai no fallback
console.warn('[NAV] menu builder error:', e);
return [];
}
}
// core menu anti-"sumir"
function coreMenu () {
return [
{
label: 'Geral',
items: [
{ label: 'Início', icon: 'pi pi-home', to: '/' },
{ label: 'Assinatura', icon: 'pi pi-credit-card', to: '/billing' },
{ label: 'Perfil', icon: 'pi pi-user', to: '/profile' }
]
}
]
function coreMenu() {
return [
{
label: 'Geral',
items: [
{ label: 'Início', icon: 'pi pi-home', to: '/' },
{ label: 'Assinatura', icon: 'pi pi-credit-card', to: '/billing' },
{ label: 'Perfil', icon: 'pi pi-user', to: '/profile' }
]
}
];
}
function safeHasFeature (fn, key) {
try { return !!fn?.(key) } catch { return false }
function safeHasFeature(fn, key) {
try {
return !!fn?.(key);
} catch {
return false;
}
}
// ======================================================
@@ -85,43 +89,43 @@ function safeHasFeature (fn, key) {
// - Apenas calcula badge PRO dinâmico (com base em entitlements)
// ======================================================
function decorateMenu (menu, hasFeature) {
const arr = Array.isArray(menu) ? menu : []
return arr.map((group) => {
if (group?.items && Array.isArray(group.items)) {
return { ...group, items: decorateItems(group.items, hasFeature) }
}
return group
})
function decorateMenu(menu, hasFeature) {
const arr = Array.isArray(menu) ? menu : [];
return arr.map((group) => {
if (group?.items && Array.isArray(group.items)) {
return { ...group, items: decorateItems(group.items, hasFeature) };
}
return group;
});
}
function decorateItems (items, hasFeature) {
return (items || []).map((it) => {
if (it?.items && Array.isArray(it.items)) {
return { ...it, items: decorateItems(it.items, hasFeature) }
}
function decorateItems(items, hasFeature) {
return (items || []).map((it) => {
if (it?.items && Array.isArray(it.items)) {
return { ...it, items: decorateItems(it.items, hasFeature) };
}
const featureKey = it?.feature ? String(it.feature) : null
const showPro = !!it?.proBadge && !!featureKey && !safeHasFeature(hasFeature, featureKey)
const featureKey = it?.feature ? String(it.feature) : null;
const showPro = !!it?.proBadge && !!featureKey && !safeHasFeature(hasFeature, featureKey);
return { ...it, __showProBadge: showPro }
})
return { ...it, __showProBadge: showPro };
});
}
// ======================================================
// ✅ Normalização de role (evita menu vazio)
// ======================================================
function normalizeRole (role) {
const r = String(role || '').trim()
if (!r) return null
function normalizeRole(role) {
const r = String(role || '').trim();
if (!r) return null;
// aliases comuns (blindagem)
if (r === 'tenant_admin') return 'clinic_admin'
if (r === 'admin') return 'clinic_admin'
if (r === 'clinic') return 'clinic_admin'
// aliases comuns (blindagem)
if (r === 'tenant_admin') return 'clinic_admin';
if (r === 'admin') return 'clinic_admin';
if (r === 'clinic') return 'clinic_admin';
return r
return r;
}
// ======================================================
@@ -130,129 +134,121 @@ function normalizeRole (role) {
// - Senão tenta deduzir do store com vários formatos comuns
// ======================================================
function buildHasFeature (sessionCtx, entitlementsStore) {
// 1) se já veio pronto, usa
if (typeof sessionCtx?.hasFeature === 'function') return sessionCtx.hasFeature
function buildHasFeature(sessionCtx, entitlementsStore) {
// 1) se já veio pronto, usa
if (typeof sessionCtx?.hasFeature === 'function') return sessionCtx.hasFeature;
// 2) se veio entitlements como objeto { key: true }
if (sessionCtx?.entitlements && typeof sessionCtx.entitlements === 'object') {
const bag = sessionCtx.entitlements
return (k) => !!bag[String(k || '').trim()]
}
// 3) se veio allowedFeatures como array ou Set
if (sessionCtx?.allowedFeatures) {
const af = sessionCtx.allowedFeatures
if (Array.isArray(af)) {
const s = new Set(af.map((x) => String(x).trim()).filter(Boolean))
return (k) => s.has(String(k || '').trim())
// 2) se veio entitlements como objeto { key: true }
if (sessionCtx?.entitlements && typeof sessionCtx.entitlements === 'object') {
const bag = sessionCtx.entitlements;
return (k) => !!bag[String(k || '').trim()];
}
if (af instanceof Set) {
return (k) => af.has(String(k || '').trim())
}
}
// 4) fallback no store
return (featureKey) => {
const k = String(featureKey || '').trim()
if (!k) return false
try {
if (typeof entitlementsStore?.hasFeature === 'function') return !!entitlementsStore.hasFeature(k)
if (typeof entitlementsStore?.has === 'function') return !!entitlementsStore.has(k)
// formatos comuns:
// - entitlements: { key: true }
// - entitlements: Set([...])
// - entitlements: Array([...])
// - byOwner: { [ownerId]: { key: true } }
const bag =
entitlementsStore?.entitlements ||
entitlementsStore?.features ||
entitlementsStore?.data ||
entitlementsStore?.state
if (bag instanceof Set) return bag.has(k)
if (Array.isArray(bag)) return bag.includes(k)
if (bag && typeof bag === 'object') {
// tenta direto
if (Object.prototype.hasOwnProperty.call(bag, k)) return !!bag[k]
// tenta byOwner (caso exista)
const ownerId = sessionCtx?.ownerId || sessionCtx?.activeTenantId || sessionCtx?.tenantId
if (ownerId && bag[ownerId] && typeof bag[ownerId] === 'object') {
return !!bag[ownerId][k]
// 3) se veio allowedFeatures como array ou Set
if (sessionCtx?.allowedFeatures) {
const af = sessionCtx.allowedFeatures;
if (Array.isArray(af)) {
const s = new Set(af.map((x) => String(x).trim()).filter(Boolean));
return (k) => s.has(String(k || '').trim());
}
}
} catch {}
if (af instanceof Set) {
return (k) => af.has(String(k || '').trim());
}
}
return false
}
// 4) fallback no store
return (featureKey) => {
const k = String(featureKey || '').trim();
if (!k) return false;
try {
if (typeof entitlementsStore?.hasFeature === 'function') return !!entitlementsStore.hasFeature(k);
if (typeof entitlementsStore?.has === 'function') return !!entitlementsStore.has(k);
// formatos comuns:
// - entitlements: { key: true }
// - entitlements: Set([...])
// - entitlements: Array([...])
// - byOwner: { [ownerId]: { key: true } }
const bag = entitlementsStore?.entitlements || entitlementsStore?.features || entitlementsStore?.data || entitlementsStore?.state;
if (bag instanceof Set) return bag.has(k);
if (Array.isArray(bag)) return bag.includes(k);
if (bag && typeof bag === 'object') {
// tenta direto
if (Object.prototype.hasOwnProperty.call(bag, k)) return !!bag[k];
// tenta byOwner (caso exista)
const ownerId = sessionCtx?.ownerId || sessionCtx?.activeTenantId || sessionCtx?.tenantId;
if (ownerId && bag[ownerId] && typeof bag[ownerId] === 'object') {
return !!bag[ownerId][k];
}
}
} catch {}
return false;
};
}
// ======================================================
// 🎯 getMenuByRole
// ======================================================
export function getMenuByRole (role, sessionCtx = {}) {
const saasHealthStore = useSaasHealthStore()
const mismatchCount = saasHealthStore?.mismatchCount || 0
export function getMenuByRole(role, sessionCtx = {}) {
const saasHealthStore = useSaasHealthStore();
const mismatchCount = saasHealthStore?.mismatchCount || 0;
const tenantFeaturesStore = useTenantFeaturesStore()
const entitlementsStore = useEntitlementsStore()
const tenantFeaturesStore = useTenantFeaturesStore();
const entitlementsStore = useEntitlementsStore();
const isSaas = sessionCtx?.isSaasAdmin === true
const isSaas = sessionCtx?.isSaasAdmin === true;
const tenantFeatureEnabled =
typeof sessionCtx?.tenantFeatureEnabled === 'function'
? sessionCtx.tenantFeatureEnabled
: (key) => {
try { return !!tenantFeaturesStore?.isEnabled?.(key) } catch { return false }
}
const tenantFeatureEnabled =
typeof sessionCtx?.tenantFeatureEnabled === 'function'
? sessionCtx.tenantFeatureEnabled
: (key) => {
try {
return !!tenantFeaturesStore?.isEnabled?.(key);
} catch {
return false;
}
};
const tenantLoading =
typeof sessionCtx?.tenantLoading === 'function'
? sessionCtx.tenantLoading
: () => false
const tenantLoading = typeof sessionCtx?.tenantLoading === 'function' ? sessionCtx.tenantLoading : () => false;
const tenantFeaturesLoading =
typeof sessionCtx?.tenantFeaturesLoading === 'function'
? sessionCtx.tenantFeaturesLoading
: () => false
const tenantFeaturesLoading = typeof sessionCtx?.tenantFeaturesLoading === 'function' ? sessionCtx.tenantFeaturesLoading : () => false;
const hasFeature = buildHasFeature(sessionCtx, entitlementsStore)
const hasFeature = buildHasFeature(sessionCtx, entitlementsStore);
const ctx = {
...sessionCtx,
mismatchCount,
tenantFeaturesStore,
tenantFeatureEnabled,
tenantLoading,
tenantFeaturesLoading,
entitlementsStore,
hasFeature
}
const ctx = {
...sessionCtx,
mismatchCount,
tenantFeaturesStore,
tenantFeatureEnabled,
tenantLoading,
tenantFeaturesLoading,
entitlementsStore,
hasFeature
};
// ✅ role normalizado
const r = normalizeRole(role)
// ✅ role normalizado
const r = normalizeRole(role);
const baseRaw = resolveMenu(MENUS[r], ctx)
const base = decorateMenu(baseRaw, hasFeature)
const baseRaw = resolveMenu(MENUS[r], ctx);
const base = decorateMenu(baseRaw, hasFeature);
const saasRaw = typeof saasMenu === 'function'
? saasMenu(ctx, { mismatchCount, docsAtencaoCount: countAtencao.value })
: saasMenu
const saas = decorateMenu(saasRaw, hasFeature)
const saasRaw = typeof saasMenu === 'function' ? saasMenu(ctx, { mismatchCount, docsAtencaoCount: countAtencao.value }) : saasMenu;
const saas = decorateMenu(saasRaw, hasFeature);
// 🔒 SaaS master: somente área SaaS
if (isSaas) {
return saas.length ? saas : coreMenu()
}
// 🔒 SaaS master: somente área SaaS
if (isSaas) {
return saas.length ? saas : coreMenu();
}
// ✅ fallback: nunca retorna vazio
if (!base || !base.length) {
return coreMenu()
}
// ✅ fallback: nunca retorna vazio
if (!base || !base.length) {
return coreMenu();
}
return [...base]
}
return [...base];
}
+93 -95
View File
@@ -14,107 +14,105 @@
| © 2026 — Todos os direitos reservados
|--------------------------------------------------------------------------
*/
export default function adminMenu (ctx = {}) {
const menu = [
{
label: 'Clínica',
items: [
// ✅ usar name real da rota (evita /admin cair em redirect estranho)
{ label: 'Dashboard', icon: 'pi pi-fw pi-home', to: { name: 'admin.dashboard' } },
export default function adminMenu(ctx = {}) {
const menu = [
{
label: 'Agenda da Clínica',
icon: 'pi pi-fw pi-calendar',
to: { name: 'admin-agenda-clinica' },
feature: 'agenda.view',
badgeKey: 'agendaHoje'
label: 'Clínica',
items: [
// ✅ usar name real da rota (evita /admin cair em redirect estranho)
{ label: 'Dashboard', icon: 'pi pi-fw pi-home', to: { name: 'admin.dashboard' } },
{
label: 'Agenda da Clínica',
icon: 'pi pi-fw pi-calendar',
to: { name: 'admin-agenda-clinica' },
feature: 'agenda.view',
badgeKey: 'agendaHoje'
},
{
label: 'Recorrências',
icon: 'pi pi-fw pi-refresh',
to: { name: 'admin-agenda-recorrencias' },
feature: 'agenda.view'
},
// ✅ Compromissos determinísticos (tipos)
{
label: 'Compromissos',
icon: 'pi pi-fw pi-clock',
to: { name: 'admin-agenda-compromissos' },
feature: 'agenda.view'
}
]
},
// ✅ SEM IF: sempre existe, só fica visível quando a feature estiver ligada
{
label: 'Pacientes',
visible: () => {
// 1) enquanto tenant/features estão carregando, NÃO some (evita piscar ao trocar de aba)
if (ctx?.tenantLoading?.()) return true;
if (ctx?.tenantFeaturesLoading?.()) return true;
// 2) quando estabilizou, aí sim decide pela feature
return !!ctx?.tenantFeatureEnabled?.('patients');
},
items: [
// ✅ usar name real das rotas (você já tem todas no routes.clinic.js)
{ label: 'Lista de Pacientes', icon: 'pi pi-fw pi-users', to: { name: 'admin-pacientes' }, quickCreate: true, quickCreateRoute: 'admin-pacientes-cadastro', quickCreateLinkTo: { name: 'admin-pacientes-link-externo' } },
{ label: 'Grupos', icon: 'pi pi-fw pi-sitemap', to: { name: 'admin-pacientes-grupos' } },
{ label: 'Tags', icon: 'pi pi-fw pi-tags', to: { name: 'admin-pacientes-tags' } },
{ label: 'Link Externo', icon: 'pi pi-fw pi-link', to: { name: 'admin-pacientes-link-externo' } },
{ label: 'Cadastros recebidos', icon: 'pi pi-inbox', to: { name: 'admin-pacientes-recebidos' }, badgeKey: 'cadastrosRecebidos' }
]
},
{
label: 'Recorrências',
icon: 'pi pi-fw pi-refresh',
to: { name: 'admin-agenda-recorrencias' },
feature: 'agenda.view'
label: 'Gestão',
items: [
{ label: 'Profissionais', icon: 'pi pi-fw pi-id-card', to: { name: 'admin-clinic-professionals' } },
{
label: 'Tipos de Clínicas',
icon: 'pi pi-fw pi-sliders-h',
to: { name: 'admin-clinic-features' },
visible: () => {
if (ctx?.tenantLoading?.()) return true; // ← true durante loading (evita piscar)
const role = ctx?.role?.();
return role === 'clinic_admin'; // tenant_admin normaliza para clinic_admin
}
},
{ label: 'Meu plano', icon: 'pi pi-fw pi-credit-card', to: { name: 'admin-meu-plano' } }
]
},
// ✅ Compromissos determinísticos (tipos)
{
label: 'Compromissos',
icon: 'pi pi-fw pi-clock',
to: { name: 'admin-agenda-compromissos' },
feature: 'agenda.view'
label: 'Financeiro',
items: [{ label: 'Cobranças', icon: 'pi pi-fw pi-wallet', to: { name: 'admin-financeiro' } }]
},
{
label: 'Sistema',
items: [
{ label: 'Segurança', icon: 'pi pi-fw pi-shield', to: { name: 'admin-settings-security' } },
{
label: 'Agendamento Online (PRO)',
icon: 'pi pi-fw pi-calendar-plus',
to: { name: 'admin-online-scheduling' },
feature: 'online_scheduling.manage',
proBadge: true
},
{
label: 'Agendamentos Recebidos',
icon: 'pi pi-fw pi-inbox',
to: { name: 'admin-agendamentos-recebidos' },
feature: 'online_scheduling.manage',
proBadge: true,
badgeKey: 'agendamentosRecebidos'
}
]
}
]
},
];
// ✅ SEM IF: sempre existe, só fica visível quando a feature estiver ligada
{
label: 'Pacientes',
visible: () => {
// 1) enquanto tenant/features estão carregando, NÃO some (evita piscar ao trocar de aba)
if (ctx?.tenantLoading?.()) return true
if (ctx?.tenantFeaturesLoading?.()) return true
// 2) quando estabilizou, aí sim decide pela feature
return !!ctx?.tenantFeatureEnabled?.('patients')
},
items: [
// ✅ usar name real das rotas (você já tem todas no routes.clinic.js)
{ label: 'Lista de Pacientes', icon: 'pi pi-fw pi-users', to: { name: 'admin-pacientes' }, quickCreate: true, quickCreateRoute: 'admin-pacientes-cadastro', quickCreateLinkTo: { name: 'admin-pacientes-link-externo' } },
{ label: 'Grupos', icon: 'pi pi-fw pi-sitemap', to: { name: 'admin-pacientes-grupos' } },
{ label: 'Tags', icon: 'pi pi-fw pi-tags', to: { name: 'admin-pacientes-tags' } },
{ label: 'Link Externo', icon: 'pi pi-fw pi-link', to: { name: 'admin-pacientes-link-externo' } },
{ label: 'Cadastros recebidos', icon: 'pi pi-inbox', to: { name: 'admin-pacientes-recebidos' }, badgeKey: 'cadastrosRecebidos' }
]
},
{
label: 'Gestão',
items: [
{ label: 'Profissionais', icon: 'pi pi-fw pi-id-card', to: { name: 'admin-clinic-professionals' } },
{
label: 'Tipos de Clínicas',
icon: 'pi pi-fw pi-sliders-h',
to: { name: 'admin-clinic-features' },
visible: () => {
if (ctx?.tenantLoading?.()) return true // ← true durante loading (evita piscar)
const role = ctx?.role?.()
return role === 'clinic_admin' // tenant_admin normaliza para clinic_admin
}
},
{ label: 'Meu plano', icon: 'pi pi-fw pi-credit-card', to: { name: 'admin-meu-plano' } }
]
},
{
label: 'Financeiro',
items: [
{ label: 'Cobranças', icon: 'pi pi-fw pi-wallet', to: { name: 'admin-financeiro' } }
]
},
{
label: 'Sistema',
items: [
{ label: 'Segurança', icon: 'pi pi-fw pi-shield', to: { name: 'admin-settings-security' } },
{
label: 'Agendamento Online (PRO)',
icon: 'pi pi-fw pi-calendar-plus',
to: { name: 'admin-online-scheduling' },
feature: 'online_scheduling.manage',
proBadge: true
},
{
label: 'Agendamentos Recebidos',
icon: 'pi pi-fw pi-inbox',
to: { name: 'admin-agendamentos-recebidos' },
feature: 'online_scheduling.manage',
proBadge: true,
badgeKey: 'agendamentosRecebidos'
}
]
}
]
return menu
}
return menu;
}
+21 -23
View File
@@ -21,28 +21,26 @@
//
export default [
{
label: 'Início',
items: [
{ label: 'Dashboard', icon: 'pi pi-fw pi-home', to: '/editor' }
]
},
{
label: 'Início',
items: [{ label: 'Dashboard', icon: 'pi pi-fw pi-home', to: '/editor' }]
},
{
label: 'Conteúdo',
items: [
{ label: 'Cursos', icon: 'pi pi-fw pi-book', to: '/editor/cursos' },
{ label: 'Módulos', icon: 'pi pi-fw pi-th-large', to: '/editor/modulos' },
{ label: 'Publicados', icon: 'pi pi-fw pi-check-circle', to: '/editor/publicados' }
]
},
{
label: 'Conteúdo',
items: [
{ label: 'Cursos', icon: 'pi pi-fw pi-book', to: '/editor/cursos' },
{ label: 'Módulos', icon: 'pi pi-fw pi-th-large', to: '/editor/modulos' },
{ label: 'Publicados', icon: 'pi pi-fw pi-check-circle', to: '/editor/publicados' }
]
},
{
label: 'Conta',
items: [
{ label: 'Meu plano', icon: 'pi pi-fw pi-credit-card', to: '/editor/meu-plano' },
{ label: 'Meu Perfil', icon: 'pi pi-fw pi-user', to: '/account/profile' },
{ label: 'Segurança', icon: 'pi pi-fw pi-shield', to: '/account/security' }
]
}
]
{
label: 'Conta',
items: [
{ label: 'Meu plano', icon: 'pi pi-fw pi-credit-card', to: '/editor/meu-plano' },
{ label: 'Meu Perfil', icon: 'pi pi-fw pi-user', to: '/account/profile' },
{ label: 'Segurança', icon: 'pi pi-fw pi-shield', to: '/account/security' }
]
}
];
+17 -21
View File
@@ -16,26 +16,22 @@
*/
export default [
{
label: 'Início',
items: [
{ label: 'Dashboard', icon: 'pi pi-fw pi-home', to: '/portal' }
]
},
{
label: 'Início',
items: [{ label: 'Dashboard', icon: 'pi pi-fw pi-home', to: '/portal' }]
},
{
label: 'Minhas sessões',
items: [
{ label: 'Sessões', icon: 'pi pi-fw pi-calendar', to: '/portal/sessoes' }
]
},
{
label: 'Minhas sessões',
items: [{ label: 'Sessões', icon: 'pi pi-fw pi-calendar', to: '/portal/sessoes' }]
},
{
label: 'Conta',
items: [
{ label: 'Meu plano', icon: 'pi pi-fw pi-credit-card', to: '/portal/meu-plano' },
{ label: 'Minha Conta', icon: 'pi pi-fw pi-user', to: '/account/profile' },
{ label: 'Segurança', icon: 'pi pi-fw pi-shield', to: '/account/security' }
]
}
]
{
label: 'Conta',
items: [
{ label: 'Meu plano', icon: 'pi pi-fw pi-credit-card', to: '/portal/meu-plano' },
{ label: 'Minha Conta', icon: 'pi pi-fw pi-user', to: '/account/profile' },
{ label: 'Segurança', icon: 'pi pi-fw pi-shield', to: '/account/security' }
]
}
];
+70 -67
View File
@@ -15,77 +15,80 @@
|--------------------------------------------------------------------------
*/
export default function saasMenu (sessionCtx, opts = {}) {
if (!sessionCtx?.isSaasAdmin) return []
export default function saasMenu(sessionCtx, opts = {}) {
if (!sessionCtx?.isSaasAdmin) return [];
const mismatchCount = Number(opts?.mismatchCount || 0)
const docsAtencaoCount = Number(opts?.docsAtencaoCount || 0)
const mismatchCount = Number(opts?.mismatchCount || 0);
const docsAtencaoCount = Number(opts?.docsAtencaoCount || 0);
const mismatchBadge = mismatchCount > 0
? { badge: String(mismatchCount), badgeClass: 'p-badge p-badge-danger' }
: {}
const mismatchBadge = mismatchCount > 0 ? { badge: String(mismatchCount), badgeClass: 'p-badge p-badge-danger' } : {};
const docsBadge = docsAtencaoCount > 0
? { badge: String(docsAtencaoCount), badgeClass: 'p-badge p-badge-danger' }
: {}
const docsBadge = docsAtencaoCount > 0 ? { badge: String(docsAtencaoCount), badgeClass: 'p-badge p-badge-danger' } : {};
return [
{
label: 'Início',
items: [
{ label: 'Dashboard', icon: 'pi pi-fw pi-chart-bar', to: '/saas' }
]
},
{
label: 'Planos',
items: [
{ label: 'Planos e Preços', icon: 'pi pi-fw pi-list', to: '/saas/plans' },
{ label: 'Vitrine Pública', icon: 'pi pi-fw pi-megaphone', to: '/saas/plans-public' },
{ label: 'Recursos', icon: 'pi pi-fw pi-bolt', to: '/saas/features' },
{ label: 'Controle de Recursos',icon: 'pi pi-fw pi-th-large', to: '/saas/plan-features' },
{ label: 'Limites por Plano', icon: 'pi pi-fw pi-sliders-h', to: '/saas/plan-limits' }
]
},
{
label: 'Assinaturas',
items: [
{ label: 'Listagem', icon: 'pi pi-fw pi-list', to: '/saas/subscriptions' },
{ label: 'Intenções', icon: 'pi pi-fw pi-inbox', to: '/saas/subscription-intents' },
{ label: 'Histórico', icon: 'pi pi-fw pi-history', to: '/saas/subscription-events' },
return [
{
label: 'Saúde das Assinaturas',
icon: 'pi pi-fw pi-shield',
to: '/saas/subscription-health',
...mismatchBadge
}
]
},
{
label: 'Operações',
items: [
{ label: 'Clínicas (Tenants)', icon: 'pi pi-fw pi-users', to: '/saas/tenants' },
{ label: 'Feriados', icon: 'pi pi-fw pi-star', to: '/saas/feriados' },
{ label: 'Suporte Técnico', icon: 'pi pi-fw pi-headphones', to: '/saas/support' }
]
},
{
label: 'Conteúdo',
items: [
{
label: 'Documentação',
icon: 'pi pi-fw pi-question-circle',
to: '/saas/docs',
...docsBadge
label: 'Início',
items: [{ label: 'Dashboard', icon: 'pi pi-fw pi-chart-bar', to: '/saas' }]
},
{ label: 'FAQ', icon: 'pi pi-fw pi-comments', to: '/saas/faq' },
{ label: 'Carrossel Login', icon: 'pi pi-fw pi-images', to: '/saas/login-carousel' },
{ label: 'Avisos Globais', icon: 'pi pi-fw pi-megaphone', to: '/saas/global-notices' },
{ label: 'Templates de E-mail', icon: 'pi pi-fw pi-envelope', to: '/saas/email-templates' }
]
}
]
{
label: 'Planos',
items: [
{ label: 'Planos e Preços', icon: 'pi pi-fw pi-list', to: '/saas/plans' },
{ label: 'Vitrine Pública', icon: 'pi pi-fw pi-megaphone', to: '/saas/plans-public' },
{ label: 'Recursos', icon: 'pi pi-fw pi-bolt', to: '/saas/features' },
{ label: 'Controle de Recursos', icon: 'pi pi-fw pi-th-large', to: '/saas/plan-features' },
{ label: 'Limites por Plano', icon: 'pi pi-fw pi-sliders-h', to: '/saas/plan-limits' }
]
},
{
label: 'Assinaturas',
items: [
{ label: 'Listagem', icon: 'pi pi-fw pi-list', to: '/saas/subscriptions' },
{ label: 'Intenções', icon: 'pi pi-fw pi-inbox', to: '/saas/subscription-intents' },
{ label: 'Histórico', icon: 'pi pi-fw pi-history', to: '/saas/subscription-events' },
{
label: 'Saúde das Assinaturas',
icon: 'pi pi-fw pi-shield',
to: '/saas/subscription-health',
...mismatchBadge
}
]
},
{
label: 'Operações',
items: [
{ label: 'Clínicas (Tenants)', icon: 'pi pi-fw pi-users', to: '/saas/tenants' },
{ label: 'Feriados', icon: 'pi pi-fw pi-star', to: '/saas/feriados' },
{ label: 'Suporte Técnico', icon: 'pi pi-fw pi-headphones', to: '/saas/support' }
]
},
{
label: 'Canais',
items: [
{ label: 'WhatsApp', icon: 'pi pi-fw pi-whatsapp', to: '/saas/whatsapp' },
{ label: 'Templates WhatsApp/SMS', icon: 'pi pi-fw pi-comment', to: '/saas/notification-templates' },
{ label: 'Add-ons / Créditos SMS', icon: 'pi pi-fw pi-box', to: '/saas/addons' }
]
},
{
label: 'Conteúdo',
items: [
{
label: 'Documentação',
icon: 'pi pi-fw pi-question-circle',
to: '/saas/docs',
...docsBadge
},
{ label: 'FAQ', icon: 'pi pi-fw pi-comments', to: '/saas/faq' },
{ label: 'Carrossel Login', icon: 'pi pi-fw pi-images', to: '/saas/login-carousel' },
{ label: 'Avisos Globais', icon: 'pi pi-fw pi-megaphone', to: '/saas/global-notices' },
{ label: 'Templates de E-mail', icon: 'pi pi-fw pi-envelope', to: '/saas/email-templates' }
]
}
];
}
+24 -26
View File
@@ -16,31 +16,29 @@
*/
export default [
{
label: 'Início',
items: [
{ label: 'Dashboard', icon: 'pi pi-fw pi-home', to: '/supervisor' }
]
},
{
label: 'Início',
items: [{ label: 'Dashboard', icon: 'pi pi-fw pi-home', to: '/supervisor' }]
},
{
label: 'Supervisão',
items: [
{
label: 'Sala de Supervisão',
icon: 'pi pi-fw pi-users',
to: '/supervisor/sala',
feature: 'supervisor.access'
}
]
},
{
label: 'Supervisão',
items: [
{
label: 'Sala de Supervisão',
icon: 'pi pi-fw pi-users',
to: '/supervisor/sala',
feature: 'supervisor.access'
}
]
},
{
label: 'Conta',
items: [
{ label: 'Meu plano', icon: 'pi pi-fw pi-credit-card', to: '/supervisor/meu-plano' },
{ label: 'Meu Perfil', icon: 'pi pi-fw pi-user', to: '/account/profile' },
{ label: 'Segurança', icon: 'pi pi-fw pi-shield', to: '/account/security' }
]
}
]
{
label: 'Conta',
items: [
{ label: 'Meu plano', icon: 'pi pi-fw pi-credit-card', to: '/supervisor/meu-plano' },
{ label: 'Meu Perfil', icon: 'pi pi-fw pi-user', to: '/account/profile' },
{ label: 'Segurança', icon: 'pi pi-fw pi-shield', to: '/account/security' }
]
}
];
+62 -66
View File
@@ -16,75 +16,71 @@
*/
export default [
{
label: 'Início',
items: [
{ label: 'Dashboard', icon: 'pi pi-fw pi-home', to: '/therapist' }
]
},
{
label: 'Início',
items: [{ label: 'Dashboard', icon: 'pi pi-fw pi-home', to: '/therapist' }]
},
{
label: 'Agenda',
items: [
{ label: 'Agenda', icon: 'pi pi-fw pi-calendar', to: '/therapist/agenda', feature: 'agenda.view', proBadge: true, badgeKey: 'agendaHoje' },
{ label: 'Recorrências', icon: 'pi pi-fw pi-refresh', to: '/therapist/agenda/recorrencias', feature: 'agenda.view', proBadge: true },
{ label: 'Compromissos', icon: 'pi pi-fw pi-clock', to: '/therapist/agenda/compromissos', feature: 'agenda.view', proBadge: true }
]
},
{
label: 'Agenda',
items: [
{ label: 'Agenda', icon: 'pi pi-fw pi-calendar', to: '/therapist/agenda', feature: 'agenda.view', proBadge: true, badgeKey: 'agendaHoje' },
{ label: 'Recorrências', icon: 'pi pi-fw pi-refresh', to: '/therapist/agenda/recorrencias', feature: 'agenda.view', proBadge: true },
{ label: 'Compromissos', icon: 'pi pi-fw pi-clock', to: '/therapist/agenda/compromissos', feature: 'agenda.view', proBadge: true }
]
},
{
label: 'Pacientes',
items: [
{ label: 'Meus pacientes', icon: 'pi pi-list', to: '/therapist/patients', quickCreate: true, quickCreateRoute: 'therapist-patients-cadastro', quickCreateLinkTo: '/therapist/patients/link-externo' },
{ label: 'Grupo de pacientes', icon: 'pi pi-fw pi-users', to: '/therapist/patients/grupos' },
{ label: 'Tags', icon: 'pi pi-tags', to: '/therapist/patients/tags' },
{ label: 'Meu link de cadastro', icon: 'pi pi-link', to: '/therapist/patients/link-externo' },
{ label: 'Cadastros recebidos', icon: 'pi pi-inbox', to: '/therapist/patients/cadastro/recebidos', badgeKey: 'cadastrosRecebidos' }
]
},
{
label: 'Pacientes',
items: [
{ label: 'Meus pacientes', icon: 'pi pi-list', to: '/therapist/patients', quickCreate: true, quickCreateRoute: 'therapist-patients-cadastro', quickCreateLinkTo: '/therapist/patients/link-externo' },
{ label: 'Grupo de pacientes', icon: 'pi pi-fw pi-users', to: '/therapist/patients/grupos' },
{ label: 'Tags', icon: 'pi pi-tags', to: '/therapist/patients/tags' },
{ label: 'Meu link de cadastro', icon: 'pi pi-link', to: '/therapist/patients/link-externo' },
{ label: 'Cadastros recebidos', icon: 'pi pi-inbox', to: '/therapist/patients/cadastro/recebidos', badgeKey: 'cadastrosRecebidos' }
]
},
{
label: 'Agendamento Online',
items: [
{
label: 'Configurar página',
icon: 'pi pi-fw pi-globe',
to: '/therapist/online-scheduling',
feature: 'online_scheduling.manage',
proBadge: true
},
{
label: 'Agendamentos Recebidos',
icon: 'pi pi-fw pi-inbox',
to: '/therapist/agendamentos-recebidos',
feature: 'online_scheduling.manage',
proBadge: true,
badgeKey: 'agendamentosRecebidos'
}
]
},
{
label: 'Agendamento Online',
items: [
{
label: 'Configurar página',
icon: 'pi pi-fw pi-globe',
to: '/therapist/online-scheduling',
feature: 'online_scheduling.manage',
proBadge: true
},
{
label: 'Agendamentos Recebidos',
icon: 'pi pi-fw pi-inbox',
to: '/therapist/agendamentos-recebidos',
feature: 'online_scheduling.manage',
proBadge: true,
badgeKey: 'agendamentosRecebidos'
}
]
},
{
label: 'Financeiro',
items: [
{ label: 'Cobranças', icon: 'pi pi-fw pi-wallet', to: '/therapist/financeiro' },
{ label: 'Lançamentos', icon: 'pi pi-fw pi-list', to: '/therapist/financeiro/lancamentos' }
]
},
{
label: 'Financeiro',
items: [
{ label: 'Cobranças', icon: 'pi pi-fw pi-wallet', to: '/therapist/financeiro' },
{ label: 'Lançamentos', icon: 'pi pi-fw pi-list', to: '/therapist/financeiro/lancamentos' }
]
},
{
label: 'Relatórios',
items: [
{ label: 'Relatórios', icon: 'pi pi-fw pi-chart-bar', to: '/therapist/relatorios', feature: 'agenda.view' }
]
},
{
label: 'Relatórios',
items: [{ label: 'Relatórios', icon: 'pi pi-fw pi-chart-bar', to: '/therapist/relatorios', feature: 'agenda.view' }]
},
{
label: 'Conta',
items: [
{ label: 'Meu plano', icon: 'pi pi-fw pi-credit-card', to: '/therapist/meu-plano' },
{ label: 'Meu Perfil', icon: 'pi pi-fw pi-user', to: '/account/profile' },
{ label: 'Segurança', icon: 'pi pi-fw pi-shield', to: '/account/security' }
]
}
]
{
label: 'Conta',
items: [
{ label: 'Meu plano', icon: 'pi pi-fw pi-credit-card', to: '/therapist/meu-plano' },
{ label: 'Meu Perfil', icon: 'pi pi-fw pi-user', to: '/account/profile' },
{ label: 'Segurança', icon: 'pi pi-fw pi-shield', to: '/account/security' }
]
}
];