Ajuste Layout, Dashboard Terapeuta, Timeline, Suporte técnico, Documentação e FAQ

This commit is contained in:
Leonardo
2026-03-15 19:46:06 -03:00
parent ee09b30987
commit f66f6f3fde
21 changed files with 24146 additions and 721 deletions

View File

@@ -3,13 +3,13 @@ import { useLayout } from '@/layout/composables/layout'
import { computed, onMounted, onBeforeUnmount, provide, watch } from 'vue'
import { useRoute } from 'vue-router'
import AppFooter from './AppFooter.vue'
import AppSidebar from './AppSidebar.vue'
import AppTopbar from './AppTopbar.vue'
import AppRail from './AppRail.vue'
import AppRailPanel from './AppRailPanel.vue'
import AppRailTopbar from './AppRailTopbar.vue'
import AjudaDrawer from '@/components/AjudaDrawer.vue'
import AppFooter from './AppFooter.vue'
import AppSidebar from './AppSidebar.vue'
import AppTopbar from './AppTopbar.vue'
import AppRail from './AppRail.vue'
import AppRailPanel from './AppRailPanel.vue'
import AppRailSidebar from './AppRailSidebar.vue'
import AjudaDrawer from '@/components/AjudaDrawer.vue'
import { fetchDocsForPath, useAjuda } from '@/composables/useAjuda'
@@ -27,7 +27,6 @@ import { useTenantFeaturesStore } from '@/stores/tenantFeaturesStore'
const route = useRoute()
const { layoutConfig, layoutState, hideMobileMenu, isDesktop } = useLayout()
// ✅ área do layout definida por rota (shell único)
const layoutArea = computed(() => route.meta?.area || null)
provide('layoutArea', layoutArea)
@@ -60,10 +59,8 @@ async function revalidateAfterSessionRefresh () {
if (!tenantStore.loaded && !tenantStore.loading) {
await tenantStore.loadSessionAndTenant()
}
const tid = getTenantId()
if (!tid) return
await Promise.allSettled([
entitlementsStore.loadForTenant?.(tid, { force: true }),
tf.fetchForTenant?.(tid, { force: true })
@@ -74,20 +71,16 @@ async function revalidateAfterSessionRefresh () {
}
function onSessionRefreshed () {
// ✅ Só revalidar tenantStore/entitlements em áreas TENANT.
// Em /portal e /account isso causa vazamento de contexto e troca de menu.
const p = String(route.path || '')
const isTenantArea =
p.startsWith('/admin') ||
p.startsWith('/therapist') ||
p.startsWith('/supervisor') ||
p.startsWith('/saas')
if (!isTenantArea) return
revalidateAfterSessionRefresh()
}
// Dispara busca de docs de ajuda sempre que a rota muda
watch(() => route.path, (path) => fetchDocsForPath(path), { immediate: true })
onMounted(() => {
@@ -100,18 +93,19 @@ onBeforeUnmount(() => {
</script>
<template>
<!-- Fullscreen: setup wizard (sem sidebar/topbar/footer) -->
<!-- Fullscreen -->
<template v-if="route.meta?.fullscreen">
<router-view />
<Toast />
</template>
<!-- Layout 2: Rail + Painel + Main (full-width) -->
<template v-else-if="layoutConfig.variant === 'rail' && isDesktop()">
<!-- Layout Rail -->
<template v-else-if="layoutConfig.variant === 'rail'">
<div class="l2-root">
<!-- Rail de ícones: oculto em mobile ( 1200px) via CSS -->
<AppRail />
<div class="l2-body">
<AppRailTopbar />
<AppTopbar />
<div class="l2-content">
<AppRailPanel />
<div class="l2-main" :style="ajudaPushStyle">
@@ -120,11 +114,19 @@ onBeforeUnmount(() => {
</div>
</div>
</div>
<!-- Sidebar mobile exclusiva do Rail -->
<AppRailSidebar />
<!-- Overlay escuro ao abrir sidebar mobile -->
<div
v-if="layoutState.mobileMenuActive"
class="l2-mobile-overlay"
@click="hideMobileMenu"
/>
<AjudaDrawer />
<Toast />
</template>
<!-- Layout 1: Clássico -->
<!-- Layout Clássico melhorado -->
<template v-else>
<div class="layout-wrapper" :class="containerClass">
<AppTopbar />
@@ -142,8 +144,62 @@ onBeforeUnmount(() => {
</template>
</template>
<style>
/* ──────────────────────────────────────────────
LAYOUT CLÁSSICO — ajustes globais (não scoped)
para sobrescrever o tema PrimeVue/Sakai
────────────────────────────────────────────── */
/* ── Sidebar — sempre abaixo da topbar fixed (56px) ────────
z-index: 999 para flutuar sobre o conteúdo em overlay.
Topbar (z-index 1000) fica sempre acessível acima da sidebar. */
.layout-sidebar {
position: fixed !important;
top: 56px !important;
left: 0 !important;
height: calc(100vh - 56px) !important;
border-radius: 0 !important;
padding: 0 !important;
box-shadow: 2px 0 6px rgba(0,0,0,.06) !important;
border-right: 1px solid var(--surface-border) !important;
z-index: 999;
}
/* ── Topbar no layout Clássico — sempre tela toda, acima da sidebar */
.layout-wrapper .rail-topbar {
z-index: 1000 !important;
}
/* ── Conteúdo — margem esquerda por modo ───────────────────
Static ativo : afasta da sidebar
Static inativo: sem margem
Overlay : sem margem (sidebar flutua sobre o conteúdo) */
.layout-main-container {
margin-left: 20rem !important;
padding-left: 0 !important;
padding-top: 56px !important;
}
.layout-overlay .layout-main-container,
.layout-static-inactive .layout-main-container {
margin-left: 0 !important;
}
@media (max-width: 1200px) {
.layout-main-container {
margin-left: 0 !important;
}
}
/* ── Overlay: hambúrguer sempre visível ─────────────────────
Em overlay a sidebar não ocupa espaço fixo — o botão precisa
estar disponível em qualquer largura de tela. */
.layout-overlay .rail-topbar__hamburger {
display: grid !important;
}
</style>
<style scoped>
/* ─── Layout 2 ───────────────────────────────────────────── */
/* ─── Layout Rail (inalterado) ────────────────── */
.l2-root {
display: flex;
width: 100vw;
@@ -152,7 +208,6 @@ onBeforeUnmount(() => {
background: var(--surface-ground);
}
/* Coluna direita do rail: topbar + conteúdo */
.l2-body {
flex: 1;
min-width: 0;
@@ -160,9 +215,9 @@ onBeforeUnmount(() => {
flex-direction: column;
height: 100vh;
overflow: hidden;
padding-top: 56px; /* compensa a topbar fixed */
}
/* Linha: painel lateral + main */
.l2-content {
flex: 1;
min-height: 0;
@@ -170,13 +225,35 @@ onBeforeUnmount(() => {
overflow: hidden;
}
/* Área de conteúdo principal */
.l2-main {
flex: 1;
min-width: 0;
overflow-y: auto;
overflow-x: hidden;
/* Headers sticky no Rail colam no topo do scroll container (já abaixo da topbar) */
--layout-sticky-top: 0px;
}
/* Rail de ícones: oculto em mobile */
@media (max-width: 1200px) {
.l2-root :deep(.rail) {
display: none;
}
/* Painel lateral: também oculto em mobile (substituído pelo AppRailSidebar) */
.l2-content :deep(.rp) {
display: none;
}
}
/* Overlay escuro ao abrir sidebar mobile no Rail */
.l2-mobile-overlay {
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.4);
z-index: 98;
animation: fadeIn 0.2s ease;
}
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
</style>