@@ -1294,17 +1340,123 @@ onMounted(() => {
}
.mn-row__btn > i { font-size: 0.78rem; }
+/* ─── Botão Menu mobile (abre drawer com sidebar) ─── */
+.mn-menu-btn {
+ display: none;
+ height: 32px;
+ align-items: center;
+ gap: 6px;
+ flex-shrink: 0;
+ background: var(--m-accent);
+ border: 1px solid var(--m-accent);
+ color: white;
+ padding: 0 11px;
+ border-radius: 9px;
+ cursor: pointer;
+ font-family: inherit;
+ font-size: 0.78rem;
+ font-weight: 600;
+ transition: background-color 140ms ease, transform 140ms ease;
+}
+.mn-menu-btn:hover {
+ background: color-mix(in srgb, var(--m-accent) 88%, white);
+ transform: translateY(-1px);
+}
+.mn-menu-btn > i { font-size: 0.85rem; }
+
+/* ─── Drawer mobile (Teleport target) ─── */
+.mn-mobile-drawer {
+ position: fixed;
+ top: 0;
+ left: 0;
+ height: 100vh;
+ height: 100dvh;
+ width: min(360px, 88vw);
+ z-index: 80;
+ background: var(--m-bg-medium);
+ backdrop-filter: blur(28px) saturate(160%);
+ -webkit-backdrop-filter: blur(28px) saturate(160%);
+ border-right: 1px solid var(--m-border);
+ transform: translateX(-100%);
+ transition: transform 250ms cubic-bezier(0.4, 0, 0.2, 1);
+ color: var(--m-text);
+ font-family: 'Segoe UI', system-ui, -apple-system, sans-serif;
+}
+.mn-mobile-drawer.is-open { transform: translateX(0); }
+.mn-mobile-drawer__scroll {
+ height: 100%;
+ overflow-y: auto;
+ overflow-x: hidden;
+ padding: 12px 12px 24px;
+ display: flex;
+ flex-direction: column;
+ gap: 12px;
+ scrollbar-width: thin;
+ scrollbar-color: var(--m-border-strong) transparent;
+}
+.mn-mobile-drawer__scroll::-webkit-scrollbar { width: 5px; }
+.mn-mobile-drawer__scroll::-webkit-scrollbar-thumb {
+ background: var(--m-border-strong);
+ border-radius: 3px;
+}
+
+/* Sidebar teleportada — ocupa altura disponivel pra empurrar o footer
+ pro bottom (margin: auto faz o trabalho). */
+.mn-mobile-drawer__scroll .mn-side {
+ width: 100%;
+ flex: 1;
+ min-height: 0;
+ overflow: visible;
+ padding: 0;
+ background: transparent;
+ border-right: none;
+ display: flex;
+ flex-direction: column;
+}
+.mn-mobile-drawer__scroll .mn-side__scroll {
+ flex: none;
+ min-height: 0;
+ overflow: visible;
+ display: flex;
+ flex-direction: column;
+ gap: 12px;
+}
+.mn-mobile-drawer__scroll .mn-w--side {
+ margin: 0;
+}
+.mn-mobile-drawer__scroll .mn-w--side:last-of-type { margin-bottom: 0; }
+.mn-mobile-drawer__scroll .mn-side__footer {
+ position: sticky;
+ bottom: 0;
+ margin: auto -12px -24px;
+ padding: 12px;
+ background: var(--m-bg-medium);
+ border-top: 1px solid var(--m-border);
+ backdrop-filter: blur(24px) saturate(160%);
+ -webkit-backdrop-filter: blur(24px) saturate(160%);
+ z-index: 5;
+}
+
+.mn-mobile-drawer__backdrop {
+ position: fixed;
+ inset: 0;
+ background: rgba(0, 0, 0, 0.45);
+ backdrop-filter: blur(4px);
+ -webkit-backdrop-filter: blur(4px);
+ z-index: 79;
+}
+.mn-drawer-fade-enter-active,
+.mn-drawer-fade-leave-active { transition: opacity 200ms ease; }
+.mn-drawer-fade-enter-from,
+.mn-drawer-fade-leave-to { opacity: 0; }
+
/* ─── Mobile (<1024px) ─── */
@media (max-width: 1023px) {
+ /* Sidebar saiu pro drawer via Teleport — body fica só com .mn-main. */
.mn-body { flex-direction: column; padding: 0; }
- .mn-side {
- width: 100%;
- max-height: 50vh;
- border-right: none;
- border-bottom: 1px solid var(--m-border);
- }
- .mn-main { padding: 8px; }
+ .mn-main { width: 100%; padding: 8px; }
.mn-page__title > span:first-of-type { display: none; }
+ .mn-menu-btn--mobile-only { display: inline-flex; }
.mn-act-btn span { display: none; }
.mn-act-btn { width: 32px; padding: 0; justify-content: center; }
.mn-row__actions { opacity: 1; }
diff --git a/src/layout/melissa/MelissaPacientes.vue b/src/layout/melissa/MelissaPacientes.vue
index b3e23cd..7a8e4c3 100644
--- a/src/layout/melissa/MelissaPacientes.vue
+++ b/src/layout/melissa/MelissaPacientes.vue
@@ -2935,7 +2935,6 @@ function sessaoStatusColor(s) {
.mp-side__scroll também perde scroll/padding (drawer já tem) e o
.mp-side__footer vira sticky no bottom do drawer pra manter o
"Limpar filtros" sempre acessível. */
-.mp-mobile-drawer__scroll .mp-side,
.mp-mobile-drawer__scroll .mp-quick {
width: 100%;
flex-shrink: 0;
@@ -2946,6 +2945,21 @@ function sessaoStatusColor(s) {
background: transparent;
padding: 0;
}
+/* .mp-side ganha flex: 1 pra ocupar altura disponível, com flex column
+ interno — assim o footer sticky é empurrado pro fim do drawer pelo
+ margin: auto, mesmo quando há pouco conteúdo. */
+.mp-mobile-drawer__scroll .mp-side {
+ width: 100%;
+ flex: 1;
+ min-height: 0;
+ overflow: visible;
+ border-right: none;
+ border-left: none;
+ background: transparent;
+ padding: 0;
+ display: flex;
+ flex-direction: column;
+}
.mp-mobile-drawer__scroll .mp-side__scroll {
flex: none;
min-height: 0;
@@ -2955,7 +2969,7 @@ function sessaoStatusColor(s) {
.mp-mobile-drawer__scroll .mp-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; /* margin-top: auto empurra o footer pro bottom + margin lateral compensa o padding do drawer */
background: var(--m-bg-medium);
border-top: 1px solid var(--m-border);
backdrop-filter: blur(24px) saturate(160%);
diff --git a/src/layout/melissa/MelissaRecorrencias.vue b/src/layout/melissa/MelissaRecorrencias.vue
index 0290297..e6f977f 100644
--- a/src/layout/melissa/MelissaRecorrencias.vue
+++ b/src/layout/melissa/MelissaRecorrencias.vue
@@ -1389,11 +1389,14 @@ onBeforeUnmount(() => {
}
.mr-mobile-drawer__scroll .mr-side {
width: 100%;
- height: auto;
+ flex: 1;
+ min-height: 0;
overflow: visible;
padding: 0;
background: transparent;
border-right: none;
+ display: flex;
+ flex-direction: column;
}
.mr-mobile-drawer__scroll .mr-side__scroll {
flex: none;
@@ -1410,7 +1413,7 @@ onBeforeUnmount(() => {
.mr-mobile-drawer__scroll .mr-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/MelissaRelatorios.vue b/src/layout/melissa/MelissaRelatorios.vue
index f2a7aa3..271f5df 100644
--- a/src/layout/melissa/MelissaRelatorios.vue
+++ b/src/layout/melissa/MelissaRelatorios.vue
@@ -11,7 +11,7 @@
* Lógica idêntica à RelatoriosPage (query agenda_eventos + grouping
* isoWeek/isoMonth + Chart.js).
*/
-import { ref, computed, watch, onMounted } from 'vue';
+import { ref, computed, watch, onMounted, onBeforeUnmount } from 'vue';
import { supabase } from '@/lib/supabase/client';
import { useTenantStore } from '@/stores/tenantStore';
// Chart/DataTable/Column/Tag/Skeleton: auto via PrimeVueResolver
@@ -19,6 +19,17 @@ import { useTenantStore } from '@/stores/tenantStore';
const emit = defineEmits(['close']);
const tenantStore = useTenantStore();
+// ── 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; }
+
// ── Período ────────────────────────────────────────────
const PERIOD_OPTIONS = [
{ key: 'week', label: 'Esta semana', icon: 'pi pi-calendar' },
@@ -245,12 +256,52 @@ watch(selectedPeriod, () => {
loadSessions();
});
-onMounted(loadSessions);
+onMounted(async () => {
+ if (typeof window !== 'undefined' && window.matchMedia) {
+ _mqMobile = window.matchMedia('(max-width: 1023px)');
+ isMobile.value = _mqMobile.matches;
+ _mqMobile.addEventListener('change', _onMqMobileChange);
+ }
+ // Garante tenant carregado antes de carregar sessoes — sem isso, a
+ // primeira render via URL direta pode pegar tenantStore vazio.
+ if (typeof tenantStore.ensureLoaded === 'function') {
+ await tenantStore.ensureLoaded();
+ }
+ await loadSessions();
+});
+onBeforeUnmount(() => {
+ if (_mqMobile) _mqMobile.removeEventListener('change', _onMqMobileChange);
+});
+
+
+
+
+
+
+
Relatórios
@@ -283,6 +334,7 @@ onMounted(loadSessions);
+
+
@@ -998,17 +1051,119 @@ onMounted(loadSessions);
gap: 6px;
}
+/* ─── Botão Menu mobile ─── */
+.mr-menu-btn {
+ display: none;
+ height: 32px;
+ align-items: center;
+ gap: 6px;
+ flex-shrink: 0;
+ background: var(--m-accent);
+ border: 1px solid var(--m-accent);
+ color: white;
+ padding: 0 11px;
+ border-radius: 9px;
+ cursor: pointer;
+ font-family: inherit;
+ font-size: 0.78rem;
+ font-weight: 600;
+ transition: background-color 140ms ease, transform 140ms ease;
+}
+.mr-menu-btn:hover {
+ background: color-mix(in srgb, var(--m-accent) 88%, white);
+ transform: translateY(-1px);
+}
+.mr-menu-btn > i { font-size: 0.85rem; }
+
+/* ─── Drawer mobile ─── */
+.mr-mobile-drawer {
+ position: fixed;
+ top: 0; left: 0;
+ height: 100vh;
+ height: 100dvh;
+ width: min(360px, 88vw);
+ z-index: 80;
+ background: var(--m-bg-medium);
+ backdrop-filter: blur(28px) saturate(160%);
+ -webkit-backdrop-filter: blur(28px) saturate(160%);
+ border-right: 1px solid var(--m-border);
+ transform: translateX(-100%);
+ transition: transform 250ms cubic-bezier(0.4, 0, 0.2, 1);
+ color: var(--m-text);
+ font-family: 'Segoe UI', system-ui, -apple-system, sans-serif;
+}
+.mr-mobile-drawer.is-open { transform: translateX(0); }
+.mr-mobile-drawer__scroll {
+ height: 100%;
+ overflow-y: auto;
+ overflow-x: hidden;
+ padding: 12px 12px 24px;
+ display: flex;
+ flex-direction: column;
+ gap: 12px;
+ scrollbar-width: thin;
+ scrollbar-color: var(--m-border-strong) transparent;
+}
+.mr-mobile-drawer__scroll::-webkit-scrollbar { width: 5px; }
+.mr-mobile-drawer__scroll::-webkit-scrollbar-thumb {
+ background: var(--m-border-strong);
+ border-radius: 3px;
+}
+
+.mr-mobile-drawer__scroll .mr-side {
+ width: 100%;
+ flex: 1;
+ min-height: 0;
+ overflow: visible;
+ padding: 0;
+ background: transparent;
+ border-right: none;
+ display: flex;
+ flex-direction: column;
+}
+.mr-mobile-drawer__scroll .mr-side__scroll {
+ flex: none;
+ min-height: 0;
+ overflow: visible;
+ display: flex;
+ flex-direction: column;
+ gap: 12px;
+}
+.mr-mobile-drawer__scroll .mr-w--side {
+ margin: 0;
+}
+.mr-mobile-drawer__scroll .mr-w--side:last-of-type { margin-bottom: 0; }
+.mr-mobile-drawer__scroll .mr-side__footer {
+ position: sticky;
+ bottom: 0;
+ margin: auto -12px -24px;
+ padding: 12px;
+ background: var(--m-bg-medium);
+ border-top: 1px solid var(--m-border);
+ backdrop-filter: blur(24px) saturate(160%);
+ -webkit-backdrop-filter: blur(24px) saturate(160%);
+ z-index: 5;
+}
+
+.mr-mobile-drawer__backdrop {
+ position: fixed;
+ inset: 0;
+ background: rgba(0, 0, 0, 0.45);
+ backdrop-filter: blur(4px);
+ -webkit-backdrop-filter: blur(4px);
+ z-index: 79;
+}
+.mr-drawer-fade-enter-active,
+.mr-drawer-fade-leave-active { transition: opacity 200ms ease; }
+.mr-drawer-fade-enter-from,
+.mr-drawer-fade-leave-to { opacity: 0; }
+
/* Mobile */
@media (max-width: 1023px) {
.mr-body { flex-direction: column; padding: 0; }
- .mr-side {
- width: 100%;
- max-height: 50vh;
- border-right: none;
- border-bottom: 1px solid var(--m-border);
- }
- .mr-main { padding: 8px; }
+ .mr-main { width: 100%; padding: 8px; }
.mr-page__title > span:first-of-type { display: none; }
+ .mr-menu-btn--mobile-only { display: inline-flex; }
.mr-stats { grid-template-columns: repeat(3, 1fr); }
}
diff --git a/src/layout/melissa/MelissaTags.vue b/src/layout/melissa/MelissaTags.vue
index 87f5d8c..15d4497 100644
--- a/src/layout/melissa/MelissaTags.vue
+++ b/src/layout/melissa/MelissaTags.vue
@@ -2273,11 +2273,14 @@ watch(editPatientDialog, (isOpen) => {
}
.mt-mobile-drawer__scroll .mt-side {
width: 100%;
- height: auto;
+ flex: 1;
+ min-height: 0;
overflow: visible;
padding: 0;
background: transparent;
border-right: none;
+ display: flex;
+ flex-direction: column;
}
.mt-mobile-drawer__scroll .mt-side__scroll {
flex: none;
@@ -2289,7 +2292,7 @@ watch(editPatientDialog, (isOpen) => {
.mt-mobile-drawer__scroll .mt-side__footer {
position: sticky;
bottom: 0;
- margin: 8px -12px -24px;
+ margin: auto -12px -24px;
background: var(--m-bg-medium);
border-top: 1px solid var(--m-border);
backdrop-filter: blur(24px) saturate(160%);