Melissa: 6 Pages aplicando blueprint + dialogs unificados + Conversa estilo WhatsApp
Sprint F (05-06). Blueprint tabular aplicado nas 6 paginas restantes;
dialogs harmonizados (FloatLabel + IconField + variant=filled + section
dividers, espelhando PatientsCadastroPage Identidade); ConversationDrawer
repaginado pra visual estilo WhatsApp.
Pages refatoradas (cada uma com subheader, sidebar __scroll + __footer
fixo "Limpar filtros", Xs inline pra zerar filtro individual, mobile
drawer com sticky footer):
- MelissaCompromissos: blueprint mantendo row design original (color
stripe + name + badges + descricao + meta inline). Filtros Status
(Ativos/Inativos) + Tipo (Nativos/Meus). Coluna Acoes frozen 140px
com toggle+pencil+trash.
- MelissaGrupos / MelissaTags: pattern completo + dialog "Pacientes
do grupo/tag" com lista vinculada via patient_group_patient /
patient_patient_tag. Avatar primary nos pacientes, header colorido
com cor da entidade, X de fechar igual .mc-close. Dialog de
criar/editar com FloatLabel + section dividers.
- MelissaMedicos: blueprint + dialog "Pacientes encaminhados" usando
cor primary do tema (medicos nao tem cor propria); dialog de
criar/editar com 4 secoes (Identificacao/Contato/Localizacao/Obs)
espelhando PatientsCadastroPage. Service ja tinha
fetchPatientsByMedicoNome (ILIKE em encaminhado_por).
- MelissaConversas: subheader, sidebar com bg-soft + border-right e
cards com sombra (mw-w--side), Limpar filtros global no footer fixo
(fix bug: filters era ref({...}) e eu lia filters.search direto, agora
usa .value), alerta de unlinked movido pro topo, kanban mobile com
min-height nas colunas pra mostrar mensagens.
- MelissaRecorrencias: subheader, button list de status (Ativas verde/
Encerradas vermelho/Todas) substitui SelectButton, busca por nome do
paciente, footer Limpar filtros, X inline no filtro Status.
ConversationDrawer redesign (WhatsApp-style):
- Header com avatar circular primary + iniciais + numero formatado
- Container de mensagens com bg "papel de parede" (color-mix com bege
esverdeado WA + radial-gradient pattern)
- Bolhas com cantos certos (top-left ou top-right zerado simulando
tail), sombra sutil, cores autenticas (#d9fdd3 light/#005c4b dark
outbound; #fff/#202c33 inbound), detecao dark via :global
- Time HH:MM + status overlay no canto inferior direito DENTRO do
balao; checks azuis quando lida (#53bdeb)
- Compose pill rounded-full + botao Send circular verde #00a884
- Removido fmtDateTime obsoleto (substituido por fmtTimeOnly)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -148,6 +148,19 @@ const carregandoInicial = computed(
|
||||
|
||||
const unlinkedCount = computed(() => filteredThreads.value.filter((t) => !t.patient_id).length);
|
||||
|
||||
// ── "Limpar filtros" global (footer fixo da sidebar) ─────────────
|
||||
// `filters` é um ref({...}) (vide useConversations.js). No script
|
||||
// preciso acessar via .value; no template o auto-unwrap cuida.
|
||||
const hasActiveFilters = computed(() =>
|
||||
!!(filters.value.search || filters.value.unreadOnly || filters.value.assigned || filters.value.channel)
|
||||
);
|
||||
function clearAllFilters() {
|
||||
filters.value.search = '';
|
||||
filters.value.unreadOnly = false;
|
||||
filters.value.assigned = null;
|
||||
filters.value.channel = null;
|
||||
}
|
||||
|
||||
// Popover de Ações (compact)
|
||||
const actionsPopRef = ref(null);
|
||||
function openActions(e) { actionsPopRef.value?.toggle(e); }
|
||||
@@ -266,119 +279,172 @@ watch(() => tenantStore.activeTenantId, async () => {
|
||||
</div>
|
||||
</Popover>
|
||||
|
||||
<!-- Subheader explicativo (blueprint §9) — diferencia de
|
||||
outras páginas Melissa que mostram listas tabulares. -->
|
||||
<div class="mw-subheader">
|
||||
<i class="pi pi-info-circle mw-subheader__icon" />
|
||||
<span class="mw-subheader__text">
|
||||
CRM de mensagens organizado em <strong>kanban por urgência</strong>:
|
||||
Urgente / Aguardando resposta / Aguardando paciente / Resolvido.
|
||||
Click num card abre a conversa no painel lateral.
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="mw-body">
|
||||
<!-- ═══ COL 1: Filtros + atribuição + canais + status ═══ -->
|
||||
<Teleport to="#mw-mobile-drawer-target" :disabled="!isMobile">
|
||||
<aside class="mw-side">
|
||||
<!-- Filtros rápidos -->
|
||||
<div class="mw-w">
|
||||
<div class="mw-w__head">
|
||||
<span class="mw-w__title"><i class="pi pi-filter" /> Filtros rápidos</span>
|
||||
</div>
|
||||
<div class="mw-side__list">
|
||||
<button
|
||||
class="mw-side__item"
|
||||
:class="{ 'is-active': !filters.unreadOnly && !filters.channel }"
|
||||
@click="filters.unreadOnly = false; filters.channel = null; filters.search = ''"
|
||||
>
|
||||
<i class="pi pi-list" />
|
||||
<span>Todas</span>
|
||||
<span class="mw-side__count">{{ summary.total }}</span>
|
||||
</button>
|
||||
<button
|
||||
class="mw-side__item"
|
||||
:class="{ 'is-active': filters.unreadOnly, 'is-warn': summary.unreadTotal > 0 }"
|
||||
@click="filters.unreadOnly = !filters.unreadOnly"
|
||||
>
|
||||
<i class="pi pi-bell" />
|
||||
<span>Não lidas</span>
|
||||
<span class="mw-side__count" :class="{ 'is-danger': summary.unreadTotal > 0 }">{{ summary.unreadTotal }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Atribuição -->
|
||||
<div class="mw-w">
|
||||
<div class="mw-w__head">
|
||||
<span class="mw-w__title"><i class="pi pi-user" /> Atribuição</span>
|
||||
</div>
|
||||
<div class="mw-side__list">
|
||||
<button
|
||||
class="mw-side__item"
|
||||
:class="{ 'is-active': !filters.assigned }"
|
||||
@click="filters.assigned = null"
|
||||
>
|
||||
<i class="pi pi-list" />
|
||||
<span>Todas</span>
|
||||
</button>
|
||||
<button
|
||||
class="mw-side__item"
|
||||
:class="{ 'is-active': filters.assigned === 'me' }"
|
||||
@click="filters.assigned = 'me'"
|
||||
>
|
||||
<i class="pi pi-user" />
|
||||
<span>Minhas</span>
|
||||
<span class="mw-side__count">{{ mineCount }}</span>
|
||||
</button>
|
||||
<button
|
||||
class="mw-side__item"
|
||||
:class="{ 'is-active': filters.assigned === 'unassigned' }"
|
||||
@click="filters.assigned = 'unassigned'"
|
||||
>
|
||||
<i class="pi pi-user-minus" />
|
||||
<span>Não atribuídas</span>
|
||||
<span class="mw-side__count" :class="{ 'is-warn': unassignedCount > 0 }">{{ unassignedCount }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Por status (kanban resumo) -->
|
||||
<div class="mw-w">
|
||||
<div class="mw-w__head">
|
||||
<span class="mw-w__title"><i class="pi pi-chart-bar" /> Por status</span>
|
||||
</div>
|
||||
<div class="mw-side__list">
|
||||
<div
|
||||
v-for="col in KANBAN_COLUMNS"
|
||||
:key="col.key"
|
||||
class="mw-side__row"
|
||||
:class="`is-${col.color}`"
|
||||
>
|
||||
<i :class="col.icon" />
|
||||
<span>{{ col.label }}</span>
|
||||
<span class="mw-side__count">{{ summary[col.key] || 0 }}</span>
|
||||
<div class="mw-side__scroll">
|
||||
<!-- Alerta unlinked — no topo pra ficar bem visível
|
||||
(números de telefone sem paciente vinculado). -->
|
||||
<div v-if="unlinkedCount > 0" class="mw-alert">
|
||||
<i class="pi pi-exclamation-circle" />
|
||||
<div>
|
||||
<div class="mw-alert__title">{{ unlinkedCount }} sem paciente vinculado</div>
|
||||
<div class="mw-alert__hint">Números de telefone que não batem com pacientes cadastrados.</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Filtros rápidos -->
|
||||
<div class="mw-w mw-w--side">
|
||||
<div class="mw-w__head">
|
||||
<span class="mw-w__title"><i class="pi pi-filter" /> Filtros rápidos</span>
|
||||
<button
|
||||
v-if="filters.unreadOnly"
|
||||
class="mw-side__clear-inline"
|
||||
v-tooltip.top="'Limpar filtro de não lidas'"
|
||||
aria-label="Limpar filtro de não lidas"
|
||||
@click="filters.unreadOnly = false"
|
||||
>
|
||||
<i class="pi pi-times" />
|
||||
</button>
|
||||
</div>
|
||||
<div class="mw-side__list">
|
||||
<button
|
||||
class="mw-side__item"
|
||||
:class="{ 'is-active': !filters.unreadOnly }"
|
||||
@click="filters.unreadOnly = false"
|
||||
>
|
||||
<i class="pi pi-list" />
|
||||
<span>Todas</span>
|
||||
<span class="mw-side__count">{{ summary.total }}</span>
|
||||
</button>
|
||||
<button
|
||||
class="mw-side__item"
|
||||
:class="{ 'is-active': filters.unreadOnly, 'is-warn': summary.unreadTotal > 0 }"
|
||||
@click="filters.unreadOnly = !filters.unreadOnly"
|
||||
>
|
||||
<i class="pi pi-bell" />
|
||||
<span>Não lidas</span>
|
||||
<span class="mw-side__count" :class="{ 'is-danger': summary.unreadTotal > 0 }">{{ summary.unreadTotal }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Atribuição -->
|
||||
<div class="mw-w mw-w--side">
|
||||
<div class="mw-w__head">
|
||||
<span class="mw-w__title"><i class="pi pi-user" /> Atribuição</span>
|
||||
<button
|
||||
v-if="filters.assigned"
|
||||
class="mw-side__clear-inline"
|
||||
v-tooltip.top="'Limpar filtro de atribuição'"
|
||||
aria-label="Limpar filtro de atribuição"
|
||||
@click="filters.assigned = null"
|
||||
>
|
||||
<i class="pi pi-times" />
|
||||
</button>
|
||||
</div>
|
||||
<div class="mw-side__list">
|
||||
<button
|
||||
class="mw-side__item"
|
||||
:class="{ 'is-active': !filters.assigned }"
|
||||
@click="filters.assigned = null"
|
||||
>
|
||||
<i class="pi pi-list" />
|
||||
<span>Todas</span>
|
||||
</button>
|
||||
<button
|
||||
class="mw-side__item"
|
||||
:class="{ 'is-active': filters.assigned === 'me' }"
|
||||
@click="filters.assigned = 'me'"
|
||||
>
|
||||
<i class="pi pi-user" />
|
||||
<span>Minhas</span>
|
||||
<span class="mw-side__count">{{ mineCount }}</span>
|
||||
</button>
|
||||
<button
|
||||
class="mw-side__item"
|
||||
:class="{ 'is-active': filters.assigned === 'unassigned' }"
|
||||
@click="filters.assigned = 'unassigned'"
|
||||
>
|
||||
<i class="pi pi-user-minus" />
|
||||
<span>Não atribuídas</span>
|
||||
<span class="mw-side__count" :class="{ 'is-warn': unassignedCount > 0 }">{{ unassignedCount }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Por status (kanban resumo — display-only, sem X) -->
|
||||
<div class="mw-w mw-w--side">
|
||||
<div class="mw-w__head">
|
||||
<span class="mw-w__title"><i class="pi pi-chart-bar" /> Por status</span>
|
||||
</div>
|
||||
<div class="mw-side__list">
|
||||
<div
|
||||
v-for="col in KANBAN_COLUMNS"
|
||||
:key="col.key"
|
||||
class="mw-side__row"
|
||||
:class="`is-${col.color}`"
|
||||
>
|
||||
<i :class="col.icon" />
|
||||
<span>{{ col.label }}</span>
|
||||
<span class="mw-side__count">{{ summary[col.key] || 0 }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Canais -->
|
||||
<div class="mw-w mw-w--side">
|
||||
<div class="mw-w__head">
|
||||
<span class="mw-w__title"><i class="pi pi-send" /> Canais</span>
|
||||
<button
|
||||
v-if="filters.channel"
|
||||
class="mw-side__clear-inline"
|
||||
v-tooltip.top="'Limpar filtro de canal'"
|
||||
aria-label="Limpar filtro de canal"
|
||||
@click="filters.channel = null"
|
||||
>
|
||||
<i class="pi pi-times" />
|
||||
</button>
|
||||
</div>
|
||||
<div class="mw-side__list">
|
||||
<button
|
||||
v-for="opt in CHANNEL_OPTIONS"
|
||||
:key="String(opt.value)"
|
||||
class="mw-side__item"
|
||||
:class="{ 'is-active': filters.channel === opt.value }"
|
||||
@click="filters.channel = opt.value"
|
||||
>
|
||||
<i v-if="opt.value" :class="['pi', channelIcon(opt.value)]" />
|
||||
<i v-else class="pi pi-list" />
|
||||
<span>{{ opt.label }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Canais -->
|
||||
<div class="mw-w">
|
||||
<div class="mw-w__head">
|
||||
<span class="mw-w__title"><i class="pi pi-send" /> Canais</span>
|
||||
</div>
|
||||
<div class="mw-side__list">
|
||||
<button
|
||||
v-for="opt in CHANNEL_OPTIONS"
|
||||
:key="String(opt.value)"
|
||||
class="mw-side__item"
|
||||
:class="{ 'is-active': filters.channel === opt.value }"
|
||||
@click="filters.channel = opt.value"
|
||||
>
|
||||
<i v-if="opt.value" :class="['pi', channelIcon(opt.value)]" />
|
||||
<i v-else class="pi pi-list" />
|
||||
<span>{{ opt.label }}</span>
|
||||
<!-- Footer fixo: "Limpar filtros" global (zera busca,
|
||||
unread, atribuição e canal de uma vez). -->
|
||||
<Transition name="mw-clear">
|
||||
<div v-if="hasActiveFilters" class="mw-side__footer">
|
||||
<button class="mw-side__clear-all" @click="clearAllFilters">
|
||||
<i class="pi pi-filter-slash" />
|
||||
<span>Limpar filtros</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Alerta unlinked -->
|
||||
<div v-if="unlinkedCount > 0" class="mw-alert">
|
||||
<i class="pi pi-exclamation-circle" />
|
||||
<div>
|
||||
<div class="mw-alert__title">{{ unlinkedCount }} sem paciente vinculado</div>
|
||||
<div class="mw-alert__hint">Números de telefone que não batem com pacientes cadastrados.</div>
|
||||
</div>
|
||||
</div>
|
||||
</Transition>
|
||||
</aside>
|
||||
</Teleport>
|
||||
|
||||
@@ -611,42 +677,166 @@ watch(() => tenantStore.activeTenantId, async () => {
|
||||
}
|
||||
.mw-menu-btn > i { font-size: 0.85rem; }
|
||||
|
||||
/* Body */
|
||||
/* Subheader explicativo (blueprint §9) */
|
||||
.mw-subheader {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 10px;
|
||||
padding: 10px 18px;
|
||||
border-bottom: 1px solid var(--m-border);
|
||||
background: var(--m-bg-soft);
|
||||
font-size: 0.78rem;
|
||||
color: var(--m-text-muted);
|
||||
line-height: 1.45;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.mw-subheader__icon {
|
||||
color: var(--p-primary-color);
|
||||
font-size: 0.92rem;
|
||||
flex-shrink: 0;
|
||||
margin-top: 1px;
|
||||
}
|
||||
.mw-subheader__text { flex: 1; min-width: 0; }
|
||||
.mw-subheader__text strong { color: var(--m-text); font-weight: 600; }
|
||||
|
||||
/* Body — sem padding/gap; a sidebar tem bg+border-right próprios e o
|
||||
main column controla seu padding interno. Espelha o pattern usado
|
||||
em MelissaGrupos / MelissaTags / MelissaMedicos. */
|
||||
.mw-body {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
min-height: 0;
|
||||
position: relative;
|
||||
gap: 12px;
|
||||
padding: 12px;
|
||||
gap: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* Aside */
|
||||
/* Aside — 2 zonas: __scroll (cards) + __footer (Limpar filtros fixo).
|
||||
bg colorido próprio (--m-bg-soft) + border-right pra separar
|
||||
visualmente da coluna principal. */
|
||||
.mw-side {
|
||||
width: 280px;
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
background: var(--m-bg-soft);
|
||||
border-right: 1px solid var(--m-border);
|
||||
overflow: hidden;
|
||||
}
|
||||
.mw-side__scroll {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
overflow-y: auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: var(--m-border-strong) transparent;
|
||||
}
|
||||
.mw-side::-webkit-scrollbar { width: 5px; }
|
||||
.mw-side::-webkit-scrollbar-thumb {
|
||||
.mw-side__scroll::-webkit-scrollbar { width: 5px; }
|
||||
.mw-side__scroll::-webkit-scrollbar-thumb {
|
||||
background: var(--m-border-strong);
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
/* Footer fixo no bottom da sidebar (fora do scroll dos filter cards).
|
||||
Aparece com fade+collapse quando algum filtro está ativo. */
|
||||
.mw-side__footer {
|
||||
flex-shrink: 0;
|
||||
padding: 12px;
|
||||
background: var(--m-bg-soft);
|
||||
border-top: 1px solid var(--m-border);
|
||||
}
|
||||
.mw-side__clear-all {
|
||||
width: 100%;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
padding: 9px 12px;
|
||||
background: var(--m-bg-medium);
|
||||
border: 1px solid var(--m-border);
|
||||
color: var(--m-text);
|
||||
border-radius: 10px;
|
||||
cursor: pointer;
|
||||
font-family: inherit;
|
||||
font-size: 0.78rem;
|
||||
font-weight: 600;
|
||||
transition: background-color 140ms ease, border-color 140ms ease, color 140ms ease;
|
||||
}
|
||||
.mw-side__clear-all:hover {
|
||||
background: var(--m-bg-soft-hover);
|
||||
border-color: var(--m-border-strong);
|
||||
color: var(--m-text);
|
||||
}
|
||||
.mw-side__clear-all > i {
|
||||
font-size: 0.78rem;
|
||||
color: var(--m-text-muted);
|
||||
transition: color 140ms ease;
|
||||
}
|
||||
.mw-side__clear-all:hover > i { color: var(--m-text); }
|
||||
|
||||
/* X inline ao lado do título de cada filter card — limpa o filtro
|
||||
individual. Espelha o pattern do MelissaPacientes/Grupos/Tags. */
|
||||
.mw-side__clear-inline {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
background: transparent;
|
||||
border: 1px solid color-mix(in srgb, rgb(220, 38, 38) 30%, var(--m-border));
|
||||
color: rgb(220, 38, 38);
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
font-family: inherit;
|
||||
transition: background-color 140ms ease, border-color 140ms ease;
|
||||
}
|
||||
.mw-side__clear-inline:hover {
|
||||
background: rgba(220, 38, 38, 0.10);
|
||||
border-color: rgba(220, 38, 38, 0.55);
|
||||
}
|
||||
.mw-side__clear-inline > i { font-size: 0.6rem; }
|
||||
|
||||
/* Transition do footer "Limpar filtros" */
|
||||
.mw-clear-enter-active,
|
||||
.mw-clear-leave-active {
|
||||
transition: opacity 220ms ease, transform 220ms ease, max-height 240ms ease;
|
||||
overflow: hidden;
|
||||
}
|
||||
.mw-clear-enter-from,
|
||||
.mw-clear-leave-to {
|
||||
opacity: 0;
|
||||
transform: translateY(6px);
|
||||
max-height: 0;
|
||||
}
|
||||
.mw-clear-enter-to,
|
||||
.mw-clear-leave-from {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
max-height: 80px;
|
||||
}
|
||||
|
||||
/* Card-base — alinhado com .ma-w / .mp-w / .mcr-w: surface --m-bg-medium
|
||||
pra destacar do bg da página/dialog (ambos --m-bg-soft). */
|
||||
pra destacar do bg da sidebar (--m-bg-soft). */
|
||||
.mw-w {
|
||||
background: var(--m-bg-medium);
|
||||
border: 1px solid var(--m-border);
|
||||
border-radius: 12px;
|
||||
padding: 12px;
|
||||
}
|
||||
.mw-w__head { margin-bottom: 10px; }
|
||||
/* Modifier pros cards dentro da .mw-side — margem lateral + sombra
|
||||
sutil pra elevar sobre o bg da sidebar. Espelha .mc-w--side, .mt-w--side. */
|
||||
.mw-w--side {
|
||||
margin: 12px 12px 0;
|
||||
flex-shrink: 0;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
.mw-w--side:last-of-type { margin-bottom: 12px; }
|
||||
.mw-w__head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.mw-w__title {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
@@ -734,12 +924,16 @@ watch(() => tenantStore.activeTenantId, async () => {
|
||||
.mw-alert {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
margin: 12px 12px 0;
|
||||
padding: 12px;
|
||||
border-radius: 10px;
|
||||
border: 1px solid rgba(251, 191, 36, 0.3);
|
||||
background: rgba(251, 191, 36, 0.05);
|
||||
color: rgb(251, 191, 36);
|
||||
flex-shrink: 0;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
.mw-alert:last-child { margin-bottom: 12px; }
|
||||
.mw-alert > i { font-size: 0.85rem; margin-top: 2px; }
|
||||
.mw-alert__title {
|
||||
font-size: 0.78rem;
|
||||
@@ -751,13 +945,15 @@ watch(() => tenantStore.activeTenantId, async () => {
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
/* Main */
|
||||
/* Main — recebe padding interno (o body não tem mais padding/gap;
|
||||
sidebar fica colada à esquerda com border-right). */
|
||||
.mw-main {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
/* Kanban */
|
||||
@@ -1009,11 +1205,40 @@ watch(() => tenantStore.activeTenantId, async () => {
|
||||
background: var(--m-border-strong);
|
||||
border-radius: 3px;
|
||||
}
|
||||
/* Sidebar teleportada pro drawer — perde bg/border-right (o drawer
|
||||
já tem chrome próprio) + cards perdem margem lateral (drawer já
|
||||
tem padding). Footer vira sticky no bottom do drawer. */
|
||||
.mw-mobile-drawer__scroll .mw-side {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
overflow: visible;
|
||||
padding: 0;
|
||||
background: transparent;
|
||||
border-right: none;
|
||||
}
|
||||
.mw-mobile-drawer__scroll .mw-side__scroll {
|
||||
flex: none;
|
||||
min-height: 0;
|
||||
overflow: visible;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
.mw-mobile-drawer__scroll .mw-w--side {
|
||||
margin: 0;
|
||||
}
|
||||
.mw-mobile-drawer__scroll .mw-w--side:last-of-type { margin-bottom: 0; }
|
||||
.mw-mobile-drawer__scroll .mw-alert { margin: 0; }
|
||||
.mw-mobile-drawer__scroll .mw-side__footer {
|
||||
position: sticky;
|
||||
bottom: 0;
|
||||
margin: 8px -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;
|
||||
}
|
||||
.mw-mobile-drawer__backdrop {
|
||||
position: fixed;
|
||||
@@ -1035,12 +1260,32 @@ watch(() => tenantStore.activeTenantId, async () => {
|
||||
.mw-kanban { grid-template-columns: repeat(2, 1fr); }
|
||||
}
|
||||
|
||||
/* ═══ Mobile (<lg) — drawer + kanban 1-col ═══ */
|
||||
/* ═══ Mobile (<lg) — drawer + kanban 1-col stacked ═══
|
||||
Em mobile o kanban vira flex column (stacked) e o scroll passa a ser
|
||||
global no .mw-main (não interno por coluna). Cada .mw-col cresce com
|
||||
o conteúdo + min-height pra empty state ter altura visível. */
|
||||
@media (max-width: 1023px) {
|
||||
.mw-body { flex-direction: column; padding: 8px; }
|
||||
.mw-main { width: 100%; }
|
||||
.mw-kanban { grid-template-columns: 1fr; }
|
||||
.mw-col { min-height: auto; }
|
||||
.mw-body { flex-direction: column; padding: 0; }
|
||||
.mw-main {
|
||||
width: 100%;
|
||||
padding: 8px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.mw-kanban {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: none;
|
||||
gap: 8px;
|
||||
}
|
||||
.mw-col {
|
||||
flex: none;
|
||||
min-height: 200px;
|
||||
}
|
||||
.mw-col__body {
|
||||
flex: none;
|
||||
overflow: visible;
|
||||
min-height: 80px;
|
||||
}
|
||||
.mw-page__title > span:first-of-type { display: none; }
|
||||
.mw-menu-btn--mobile-only { display: inline-flex; }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user