Layout Melissa (Direção B): preview, /profile, Agenda, dock, cadastro
Sandbox completo do novo layout Win11 lockscreen-style. Não troca o
AppLayout atual — Fase 5 (router wire-up) fica pra sessão dedicada.
Estrutura
- src/layout/melissa/ — MelissaLayout (bg+ψ+overlays), MelissaCronometro,
MelissaAgenda (fullscreen), MelissaCard, MelissaMenu, MelissaBusca
- composables/useMelissaEventos.js — semana real do FC + range mensal
pros dots do mini-cal
- composables/useMelissaPacientes.js — agora retorna created_at p/ "novo"
- melissaToques.js — toques Web Audio do término
Rota e persistência
- /preview/melissa (sem auth, sem AppLayout)
- /account/profile ganha 3º card "Melissa" com badge "Em construção"
- bootstrapUserSettings + layout composable aceitam variant='melissa'
- Migration: CHECK constraint user_settings.layout_variant aceita 'melissa'
Light mode
- Gradiente Bloom flipa via CSS vars (--bloom-c1/c2/base-1/base-2)
Dark: 400/300/950 · Light: 200/100/0
- Cronômetro/Personalização: color: white → var(--m-text)
- Pílula psi-kbd ganha tokens --m-kbd-bg/--m-kbd-text
- Override mapeia text-X-200/300/400 → text-X-600 (17 cores Tailwind)
Agenda fullscreen
- Mini-cal funcional: click pula FC, range visível destacado, dots reais
- Feriados nacional/municipal/personalizado (rose/amber/violet)
- Dias fechados (workRules) cinza apagado, mutex feriado vence
- Card "Hoje" (stats+sessões) mesclado e movido pra sidebar esquerda
- ProximosFeriadosCard reaproveitado entre mini-cal e Hoje
- Avatar paciente: bg --m-accent-strong → --m-accent (saturado em light)
- Cores light: 12 substituições color:white → var(--m-text)
Dock taskbar Win11-style
- .melissa-dock 76px fixed bottom (CSS global, não scoped — Vue static
hoisting perderia data-v-{hash})
- ψ centralizado vertical na faixa (bottom:10px)
- Chip cronômetro teleportado pro dock + animação minimize macOS
(dialog encolhe + voa pro canto bottom-left, 340ms cubic-bezier)
- transform-origin: 96px calc(100% - 38px) (posição do chip no dock)
Pacientes na sidebar
- Botão fake "+" no topo abre PatientCreatePopover (rápido/completo/link)
- Reaproveita PatientCadastroDialog + ComponentCadastroRapido
- Pacientes criados nos últimos 7d sobem pro topo + badge "novo"
Dock contextual (ações do paciente selecionado)
- Avatar + nome + count + 5 ações (sessões/whatsapp/prontuário/editar/fechar)
- Teleportado pro .melissa-dock quando há paciente selecionado
- Em mobile, ações vivem em <Menu> kebab por linha
- Pattern <Transition><Teleport v-if> obrigatório (NUNCA o contrário)
pra evitar comment placeholder + emitsOptions:null no reconciler
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,175 @@
|
||||
<script setup>
|
||||
/*
|
||||
* MelissaCard
|
||||
* --------------------------------------------------
|
||||
* Card glass do dashboard Melissa.
|
||||
*
|
||||
* Dois variants:
|
||||
* - default: header (ícone+título+badge opcional) + slot de conteúdo
|
||||
* + botão redondo "+" centralizado na borda inferior que
|
||||
* emite `open` (parent decide o que fazer)
|
||||
* - add: placeholder tracejado, meia largura, com "+" centralizado;
|
||||
* emite `add` ao clicar
|
||||
*
|
||||
* Largura é fixa (não estica) — o layout flex no parent define o número
|
||||
* de cards visíveis conforme o tamanho da tela.
|
||||
*/
|
||||
defineProps({
|
||||
variant: {
|
||||
type: String,
|
||||
default: 'default',
|
||||
validator: (v) => ['default', 'add'].includes(v)
|
||||
},
|
||||
icon: { type: String, default: '' }, // ex.: 'pi pi-user'
|
||||
iconColor: { type: String, default: '' }, // classe Tailwind ex.: 'text-emerald-300'
|
||||
title: { type: String, default: '' },
|
||||
badge: { type: [String, Number, null], default: null },
|
||||
badgeColor: { type: String, default: 'bg-red-500/80' }, // classe Tailwind
|
||||
actionTitle: { type: String, default: 'Abrir' }
|
||||
});
|
||||
|
||||
defineEmits(['open', 'add']);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<article v-if="variant === 'default'" class="mc-card">
|
||||
<div class="mc-card__head">
|
||||
<span v-if="icon" class="mc-card__icon">
|
||||
<i :class="[icon, iconColor]" />
|
||||
</span>
|
||||
<span class="mc-card__title">{{ title }}</span>
|
||||
<span v-if="badge !== null && badge !== ''" class="mc-card__badge" :class="badgeColor">
|
||||
{{ badge }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="mc-card__body">
|
||||
<slot />
|
||||
</div>
|
||||
<button class="mc-card__go" :title="actionTitle" @click="$emit('open')">
|
||||
<i class="pi pi-plus text-xs" />
|
||||
</button>
|
||||
</article>
|
||||
|
||||
<button v-else class="mc-card-add" :title="actionTitle" @click="$emit('add')">
|
||||
<i class="pi pi-plus" />
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
/* ─── Card padrão ──────────────────────────────────────────── */
|
||||
.mc-card {
|
||||
position: relative; /* ancora o botão "+" */
|
||||
flex-shrink: 0;
|
||||
width: 210px;
|
||||
background: var(--m-bg-soft);
|
||||
backdrop-filter: blur(20px) saturate(140%);
|
||||
-webkit-backdrop-filter: blur(20px) saturate(140%);
|
||||
border: 1px solid var(--m-border);
|
||||
border-radius: 14px;
|
||||
padding: 14px 16px;
|
||||
transition: background-color 160ms ease, transform 160ms ease;
|
||||
}
|
||||
.mc-card:hover {
|
||||
background: var(--m-bg-soft-hover);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.mc-card__head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
color: var(--m-text);
|
||||
font-size: 0.8rem;
|
||||
font-weight: 500;
|
||||
letter-spacing: 0.01em;
|
||||
}
|
||||
.mc-card__icon {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
border-radius: 12px;
|
||||
background: var(--m-bg-soft-hover);
|
||||
backdrop-filter: blur(16px) saturate(160%);
|
||||
-webkit-backdrop-filter: blur(16px) saturate(160%);
|
||||
border: 1px solid var(--m-border-strong);
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.18);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.mc-card__title {
|
||||
/* deixa o título absorver o espaço entre ícone e badge */
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
.mc-card__badge {
|
||||
/* margin-left: auto não precisa mais (title flex:1 empurra o badge) */
|
||||
font-size: 0.7rem;
|
||||
padding: 2px 8px;
|
||||
border-radius: 9999px;
|
||||
color: white;
|
||||
font-weight: 600;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.mc-card__body {
|
||||
margin-top: 12px;
|
||||
color: white;
|
||||
}
|
||||
|
||||
/* Botão "+" centralizado na borda inferior do card */
|
||||
.mc-card__go {
|
||||
position: absolute;
|
||||
bottom: -16px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 12px;
|
||||
background: rgba(30, 30, 45, 0.85);
|
||||
backdrop-filter: blur(20px) saturate(160%);
|
||||
-webkit-backdrop-filter: blur(20px) saturate(160%);
|
||||
border: 1px solid var(--m-border-strong);
|
||||
color: white;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
cursor: pointer;
|
||||
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.35);
|
||||
transition: background-color 180ms ease, transform 180ms ease, border-color 180ms ease;
|
||||
z-index: 5;
|
||||
}
|
||||
.mc-card__go:hover {
|
||||
background: var(--m-bg-soft-hover);
|
||||
border-color: var(--m-border-strong);
|
||||
transform: translateX(-50%) scale(1.08);
|
||||
}
|
||||
.mc-card__go:active {
|
||||
transform: translateX(-50%) scale(0.96);
|
||||
}
|
||||
|
||||
/* ─── Card "Adicionar" (tracejado, meia largura) ──────────── */
|
||||
/* Sem align-self aqui — o parent (cards-shell) decide se estica
|
||||
(linha única) ou mantém intrínseco (wrap). */
|
||||
.mc-card-add {
|
||||
flex-shrink: 0;
|
||||
width: 130px; /* metade do card padrão */
|
||||
min-height: 100px;
|
||||
border: 1.5px dashed var(--m-border-strong);
|
||||
border-radius: 14px;
|
||||
background: var(--m-bg-soft);
|
||||
color: var(--m-text-muted);
|
||||
display: grid;
|
||||
place-items: center;
|
||||
cursor: pointer;
|
||||
font-size: 1.5rem;
|
||||
transition: all 200ms ease;
|
||||
}
|
||||
.mc-card-add:hover {
|
||||
background: var(--m-bg-soft);
|
||||
border-color: var(--m-text-muted);
|
||||
color: white;
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
.mc-card-add:active {
|
||||
transform: translateY(0);
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user