From 48bf2726a5d782e3c43cb98575d39977ed7b418b Mon Sep 17 00:00:00 2001 From: Leonardo Date: Wed, 6 May 2026 10:40:07 -0300 Subject: [PATCH] Drawer mobile + footer colado + Menu nomeado + tenant ensureLoaded MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Tres ajustes globais nas Melissa Pages com sidebar: 1) FOOTER "Limpar filtros" colado no bottom do drawer mobile Problema: o sticky bottom precisa que algum container parent tenha altura definida e overflow. No drawer, o `.xx-side` tinha `height: auto` — entao o footer ficava no fluxo natural (logo apos os cards) mesmo com pouco conteudo, em vez de empurrado pro bottom do drawer. Fix: `.xx-mobile-drawer__scroll .xx-side` ganha `flex: 1; min-height: 0; display: flex; flex-direction: column` pra ocupar altura disponivel; o `.xx-side__footer` ganha `margin: auto -12px -24px` (margin-top: auto empurra pro fim). Sticky bottom continua pro caso de scroll com muito conteudo. Aplicado em: Compromissos, Grupos, Tags, Medicos, Conversas, Recorrencias, Pacientes (caso especial — separa .mp-side de .mp-quick), Cadastros Recebidos, FinanceiroLancamentos. 2) DRAWER MOBILE adicionado em Notificacoes, Documentos e Relatorios (estavam com sidebar virando topo via max-height 50vh — faltava o pattern oficial das demais Melissa Pages). Pattern aplicado: - Aside host com id="-mobile-drawer-target" + Transition backdrop com fade - Botao "Menu " no header (esquerda do titulo) - envolvendo a sidebar - Script: drawerOpen + isMobile + matchMedia listener registrado no onMounted, removido no onBeforeUnmount - CSS completo: .xx-mobile-drawer (fixed, transform translateX), __scroll (overflow + padding), __backdrop (rgba 0.45 + blur), overrides quando teleportada (sidebar perde bg/border-right, footer vira sticky bottom com margin-top auto) 3) Botao "Menu" passa a ter sufixo da pagina: - "Menu Lancamentos" (FinanceiroLancamentos) - "Menu Notificacoes" (Notificacoes) - "Menu Documentos" (Documentos) - "Menu Relatorios" (Relatorios) - "Menu Agendamentos" (AgendamentosRecebidos — corrigido tambem) 4) Bug de "lista vazia ao carregar via URL direto": FinanceiroLancamentos e Relatorios usam composables que dependem de tenantStore.activeTenantId. Quando aberta direto via URL (sem navegar pelo menu), o tenantStore pode nao estar inicializado ainda — entao fetchRecords() / loadSessions() retornam vazio. Fix: adicionar `await tenantStore.ensureLoaded()` no onMounted antes do fetch. Ja era pattern usado em outras Melissa Pages (Compromissos, etc). Co-Authored-By: Claude Opus 4.7 (1M context) --- .../melissa/MelissaAgendamentosRecebidos.vue | 2 +- .../melissa/MelissaCadastrosRecebidos.vue | 5 +- src/layout/melissa/MelissaCompromissos.vue | 7 +- src/layout/melissa/MelissaConversas.vue | 7 +- src/layout/melissa/MelissaDocumentos.vue | 164 +++++++++++++++- .../melissa/MelissaFinanceiroLancamentos.vue | 175 +++++++++++++++++- src/layout/melissa/MelissaGrupos.vue | 7 +- src/layout/melissa/MelissaMedicos.vue | 7 +- src/layout/melissa/MelissaNotificacoes.vue | 168 ++++++++++++++++- src/layout/melissa/MelissaPacientes.vue | 18 +- src/layout/melissa/MelissaRecorrencias.vue | 7 +- src/layout/melissa/MelissaRelatorios.vue | 173 ++++++++++++++++- src/layout/melissa/MelissaTags.vue | 7 +- 13 files changed, 698 insertions(+), 49 deletions(-) diff --git a/src/layout/melissa/MelissaAgendamentosRecebidos.vue b/src/layout/melissa/MelissaAgendamentosRecebidos.vue index a612b6b..0c7b000 100644 --- a/src/layout/melissa/MelissaAgendamentosRecebidos.vue +++ b/src/layout/melissa/MelissaAgendamentosRecebidos.vue @@ -509,7 +509,7 @@ onBeforeUnmount(() => { @click="toggleDrawer" > - Menu + Menu Agendamentos
diff --git a/src/layout/melissa/MelissaCadastrosRecebidos.vue b/src/layout/melissa/MelissaCadastrosRecebidos.vue index d0d5548..366f769 100644 --- a/src/layout/melissa/MelissaCadastrosRecebidos.vue +++ b/src/layout/melissa/MelissaCadastrosRecebidos.vue @@ -1846,11 +1846,14 @@ onBeforeUnmount(() => { precisa do bg/border-right que tem em desktop. */ .mcr-mobile-drawer__scroll .mcr-side { width: 100%; - height: auto; + flex: 1; + min-height: 0; overflow: visible; padding: 0; background: transparent; border-right: none; + display: flex; + flex-direction: column; } .mcr-mobile-drawer__scroll .mcr-w--side { margin: 0 0 12px; diff --git a/src/layout/melissa/MelissaCompromissos.vue b/src/layout/melissa/MelissaCompromissos.vue index 387cd53..d24a79b 100644 --- a/src/layout/melissa/MelissaCompromissos.vue +++ b/src/layout/melissa/MelissaCompromissos.vue @@ -1862,11 +1862,14 @@ async function onDelete(c) { do drawer via position: sticky. */ .mc-mobile-drawer__scroll .mc-side { width: 100%; - height: auto; + flex: 1; + min-height: 0; overflow: visible; padding: 0; background: transparent; border-right: none; + display: flex; + flex-direction: column; } .mc-mobile-drawer__scroll .mc-side__scroll { flex: none; @@ -1878,7 +1881,7 @@ async function onDelete(c) { .mc-mobile-drawer__scroll .mc-side__footer { position: sticky; bottom: 0; - margin: 8px -12px -24px; /* compensa o padding do drawer pra ficar de borda a borda */ + margin: auto -12px -24px; /* compensa o padding do drawer pra ficar de borda a borda */ background: var(--m-bg-medium); border-top: 1px solid var(--m-border); backdrop-filter: blur(24px) saturate(160%); diff --git a/src/layout/melissa/MelissaConversas.vue b/src/layout/melissa/MelissaConversas.vue index 68af74a..7c005a7 100644 --- a/src/layout/melissa/MelissaConversas.vue +++ b/src/layout/melissa/MelissaConversas.vue @@ -1210,11 +1210,14 @@ watch(() => tenantStore.activeTenantId, async () => { tem padding). Footer vira sticky no bottom do drawer. */ .mw-mobile-drawer__scroll .mw-side { width: 100%; - height: auto; + flex: 1; + min-height: 0; overflow: visible; padding: 0; background: transparent; border-right: none; + display: flex; + flex-direction: column; } .mw-mobile-drawer__scroll .mw-side__scroll { flex: none; @@ -1232,7 +1235,7 @@ watch(() => tenantStore.activeTenantId, async () => { .mw-mobile-drawer__scroll .mw-side__footer { position: sticky; bottom: 0; - margin: 8px -12px -24px; + margin: auto -12px -24px; padding: 12px; background: var(--m-bg-medium); border-top: 1px solid var(--m-border); diff --git a/src/layout/melissa/MelissaDocumentos.vue b/src/layout/melissa/MelissaDocumentos.vue index 266f915..ec3fe0a 100644 --- a/src/layout/melissa/MelissaDocumentos.vue +++ b/src/layout/melissa/MelissaDocumentos.vue @@ -12,7 +12,7 @@ * abrir um paciente específico via /melissa/pacientes (atualmente * desabilitados aqui). Lista é read-only nesse contexto. */ -import { ref, computed, onMounted, watch } from 'vue'; +import { ref, computed, onMounted, onBeforeUnmount, watch } from 'vue'; import { useToast } from 'primevue/usetoast'; import { useConfirm } from 'primevue/useconfirm'; @@ -28,6 +28,17 @@ const emit = defineEmits(['close']); const toast = useToast(); const confirm = useConfirm(); +// ── Breakpoints + drawer mobile ──────────────────────── +const drawerOpen = ref(false); +const isMobile = ref(false); +let _mqMobile = null; +function _onMqMobileChange(e) { + isMobile.value = e.matches; + if (!e.matches) drawerOpen.value = false; +} +function toggleDrawer() { drawerOpen.value = !drawerOpen.value; } +function fecharDrawer() { drawerOpen.value = false; } + // patientId = null → modo "todos os pacientes" (read-only). const patientId = computed(() => null); @@ -110,15 +121,48 @@ function onSign(doc) { watch(filters, () => fetchDocuments(), { deep: true }); onMounted(async () => { + if (typeof window !== 'undefined' && window.matchMedia) { + _mqMobile = window.matchMedia('(max-width: 1023px)'); + isMobile.value = _mqMobile.matches; + _mqMobile.addEventListener('change', _onMqMobileChange); + } await Promise.all([fetchDocuments(), fetchUsedTags()]); }); +onBeforeUnmount(() => { + if (_mqMobile) _mqMobile.removeEventListener('change', _onMqMobileChange); +});