busca melissa: shape RPC + cores legíveis + cor de sessão
3 fixes pedidos no teste manual:
1. Shape errado da RPC: search_global retorna { id, label, sublabel,
deeplink } pra TODOS os tipos, mas o codigo lia campos diretos
(nome_completo, paciente_nome, inicio_em, nome_original etc) que
nao existem -> resultados saiam "(sem nome)", sem datas.
Fix: filteredPacientes + rpcAppointments + rpcDocuments + rpcIntakes
agora usam label/sublabel direto. selectEntry extrai patient_id da
deeplink quando precisa.
2. Cores ilegiveis: fundo do panel transparente demais (var(--m-bg-medium)
nao tinha contraste em alguns temas). Fix: fundo solido rgba(20,22,32,
0.92), border 14% white, text 96% white pra label, 65% pra sub
(sobe pra 78% no hover/active). Group title 50% + bold pra hierarquia
clara.
3. Cor das sessoes: grupo "Sessoes" tinha icone cinza generico. Fix:
classes .mb-item__icon--{patient,sessao,doc,intake} com paleta
espelhando a agenda — sessao = indigo-500 (#a5b4fc texto +
rgba(99,102,241,0.20) bg, mesma cor do pickColor() padrao);
patient = pink-400; doc = sky-500; intake = orange-400.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -79,6 +79,9 @@ const filteredAtalhos = computed(() => {
|
||||
|
||||
// Pacientes — combina RPC (autoritativo, todos os pacientes) com props (preview de hoje).
|
||||
// RPC tem prioridade; props complementa quando RPC ainda não trouxe nada.
|
||||
//
|
||||
// Shape do RPC search_global (patients): { id, label, sublabel, avatar_url, deeplink, score }
|
||||
// label = nome_completo; sublabel = email_principal ou telefone.
|
||||
const filteredPacientes = computed(() => {
|
||||
const q = normalize(query.value);
|
||||
if (q.length < 2) return [];
|
||||
@@ -87,15 +90,16 @@ const filteredPacientes = computed(() => {
|
||||
if (rpc.length) {
|
||||
return rpc.slice(0, 5).map(p => ({
|
||||
id: p.id,
|
||||
nome: p.nome_completo || p.nome_social || p.nome || '(sem nome)',
|
||||
email: p.email,
|
||||
telefone: p.telefone
|
||||
nome: p.label || '(sem nome)',
|
||||
sub: p.sublabel || '',
|
||||
avatar_url: p.avatar_url || null
|
||||
}));
|
||||
}
|
||||
// Fallback client-side
|
||||
// Fallback client-side (props.pacientes vem do MelissaLayout — shape diferente)
|
||||
return props.pacientes
|
||||
.filter((p) => normalize(p.nome).includes(q))
|
||||
.slice(0, 5);
|
||||
.slice(0, 5)
|
||||
.map(p => ({ id: p.id, nome: p.nome, sub: '', avatar_url: null }));
|
||||
});
|
||||
|
||||
const filteredEventos = computed(() => {
|
||||
@@ -139,9 +143,17 @@ function selectEntry(entry) {
|
||||
else if (entry.group === 'pacientes') emit('paciente', entry.item);
|
||||
else if (entry.group === 'recent') emit('paciente', { id: entry.item.id, nome: entry.item.nome, ...entry.item.extras });
|
||||
else if (entry.group === 'eventos') emit('evento', entry.item);
|
||||
else if (entry.group === 'rpc-appointments') emit('evento', entry.item);
|
||||
else if (entry.group === 'rpc-documents') emit('documento', entry.item);
|
||||
else if (entry.group === 'rpc-intakes') emit('intake', entry.item);
|
||||
else if (entry.group === 'rpc-appointments') {
|
||||
// Sessão da RPC: deeplink pra agenda com evento focado
|
||||
emit('evento', { id: entry.item.id, deeplink: entry.item.deeplink });
|
||||
} else if (entry.group === 'rpc-documents') {
|
||||
// Documento da RPC: extrai patient_id da deeplink se possível
|
||||
const dl = entry.item.deeplink || '';
|
||||
const m = dl.match(/patients\/([0-9a-f-]+)/i);
|
||||
emit('documento', { id: entry.item.id, patient_id: m?.[1] || null, label: entry.item.label });
|
||||
} else if (entry.group === 'rpc-intakes') {
|
||||
emit('intake', entry.item);
|
||||
}
|
||||
closePanel();
|
||||
}
|
||||
|
||||
@@ -320,10 +332,10 @@ onBeforeUnmount(() => {
|
||||
@click="selectEntry({ group: 'pacientes', item: p })"
|
||||
@mouseenter="activeIndex = findFlatIndex('pacientes', i)"
|
||||
>
|
||||
<span class="mb-item__icon"><i class="pi pi-user" /></span>
|
||||
<span class="mb-item__icon mb-item__icon--patient"><i class="pi pi-user" /></span>
|
||||
<span class="mb-item__main">
|
||||
<span class="mb-item__label">{{ p.nome }}</span>
|
||||
<span class="mb-item__sub">Abrir prontuário</span>
|
||||
<span class="mb-item__sub">{{ p.sub || 'Abrir prontuário' }}</span>
|
||||
</span>
|
||||
<i class="mb-item__go pi pi-arrow-right" />
|
||||
</button>
|
||||
@@ -354,7 +366,10 @@ onBeforeUnmount(() => {
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- RPC: Sessões/agendamentos (qualquer data) -->
|
||||
<!-- RPC: Sessões/agendamentos (qualquer data)
|
||||
RPC retorna { id, label, sublabel, deeplink }. Sublabel ja vem
|
||||
com "Paciente · dd/mm/yyyy HH:MM". Cor do icone = cor de sessao
|
||||
(indigo-500, igual ao pickColor() padrao). -->
|
||||
<div v-if="rpcAppointments.length" class="mb-group">
|
||||
<div class="mb-group__title">Sessões</div>
|
||||
<button
|
||||
@@ -365,10 +380,10 @@ onBeforeUnmount(() => {
|
||||
@click="selectEntry({ group: 'rpc-appointments', item: e })"
|
||||
@mouseenter="activeIndex = findFlatIndex('rpc-appointments', i)"
|
||||
>
|
||||
<span class="mb-item__icon"><i class="pi pi-calendar" /></span>
|
||||
<span class="mb-item__icon mb-item__icon--sessao"><i class="pi pi-calendar" /></span>
|
||||
<span class="mb-item__main">
|
||||
<span class="mb-item__label">{{ e.paciente_nome || e.title || 'Sessão' }}</span>
|
||||
<span class="mb-item__sub">{{ e.inicio_em ? new Date(e.inicio_em).toLocaleDateString('pt-BR') + ' ' + new Date(e.inicio_em).toLocaleTimeString('pt-BR', { hour: '2-digit', minute: '2-digit' }) : 'Sem data' }}</span>
|
||||
<span class="mb-item__label">{{ e.label || 'Sessão' }}</span>
|
||||
<span class="mb-item__sub">{{ e.sublabel || 'Sem detalhes' }}</span>
|
||||
</span>
|
||||
<i class="mb-item__go pi pi-arrow-right" />
|
||||
</button>
|
||||
@@ -385,10 +400,10 @@ onBeforeUnmount(() => {
|
||||
@click="selectEntry({ group: 'rpc-documents', item: d })"
|
||||
@mouseenter="activeIndex = findFlatIndex('rpc-documents', i)"
|
||||
>
|
||||
<span class="mb-item__icon"><i class="pi pi-file" /></span>
|
||||
<span class="mb-item__icon mb-item__icon--doc"><i class="pi pi-file" /></span>
|
||||
<span class="mb-item__main">
|
||||
<span class="mb-item__label">{{ d.nome_original || 'Documento' }}</span>
|
||||
<span class="mb-item__sub">{{ d.paciente_nome ? `${d.paciente_nome} · ` : '' }}{{ d.tipo_documento || 'outro' }}</span>
|
||||
<span class="mb-item__label">{{ d.label || 'Documento' }}</span>
|
||||
<span class="mb-item__sub">{{ d.sublabel || '' }}</span>
|
||||
</span>
|
||||
<i class="mb-item__go pi pi-arrow-right" />
|
||||
</button>
|
||||
@@ -405,10 +420,10 @@ onBeforeUnmount(() => {
|
||||
@click="selectEntry({ group: 'rpc-intakes', item: r })"
|
||||
@mouseenter="activeIndex = findFlatIndex('rpc-intakes', i)"
|
||||
>
|
||||
<span class="mb-item__icon"><i class="pi pi-inbox" /></span>
|
||||
<span class="mb-item__icon mb-item__icon--intake"><i class="pi pi-inbox" /></span>
|
||||
<span class="mb-item__main">
|
||||
<span class="mb-item__label">{{ r.nome_completo || 'Cadastro' }}</span>
|
||||
<span class="mb-item__sub">{{ r.created_at ? new Date(r.created_at).toLocaleDateString('pt-BR') : '' }}</span>
|
||||
<span class="mb-item__label">{{ r.label || 'Cadastro' }}</span>
|
||||
<span class="mb-item__sub">{{ r.sublabel || '' }}</span>
|
||||
</span>
|
||||
<i class="mb-item__go pi pi-arrow-right" />
|
||||
</button>
|
||||
@@ -486,40 +501,41 @@ onBeforeUnmount(() => {
|
||||
z-index: 30;
|
||||
max-height: 60vh;
|
||||
overflow-y: auto;
|
||||
background: var(--m-bg-medium);
|
||||
/* Fundo mais sólido pra melhor contraste com o lockscreen */
|
||||
background: rgba(20, 22, 32, 0.92);
|
||||
backdrop-filter: blur(28px) saturate(160%);
|
||||
-webkit-backdrop-filter: blur(28px) saturate(160%);
|
||||
border: 1px solid var(--m-border-strong);
|
||||
border: 1px solid rgba(255, 255, 255, 0.14);
|
||||
border-radius: 12px;
|
||||
padding: 6px;
|
||||
box-shadow: 0 16px 40px rgba(0, 0, 0, 0.45);
|
||||
box-shadow: 0 16px 40px rgba(0, 0, 0, 0.55);
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: var(--m-border-strong) transparent;
|
||||
scrollbar-color: rgba(255, 255, 255, 0.18) transparent;
|
||||
}
|
||||
.mb-panel::-webkit-scrollbar { width: 6px; }
|
||||
.mb-panel::-webkit-scrollbar-thumb {
|
||||
background: var(--m-border-strong);
|
||||
background: rgba(255, 255, 255, 0.18);
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.mb-empty {
|
||||
padding: 18px 14px;
|
||||
text-align: center;
|
||||
color: var(--m-text-muted);
|
||||
color: rgba(255, 255, 255, 0.65);
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.mb-group + .mb-group {
|
||||
margin-top: 4px;
|
||||
padding-top: 4px;
|
||||
border-top: 1px solid var(--m-border);
|
||||
border-top: 1px solid rgba(255, 255, 255, 0.08);
|
||||
}
|
||||
.mb-group__title {
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.18em;
|
||||
color: var(--m-text-faint);
|
||||
font-size: 0.6rem;
|
||||
font-weight: 600;
|
||||
color: rgba(255, 255, 255, 0.5);
|
||||
font-size: 0.62rem;
|
||||
font-weight: 700;
|
||||
padding: 8px 10px 4px;
|
||||
}
|
||||
|
||||
@@ -528,11 +544,11 @@ onBeforeUnmount(() => {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
padding: 8px 10px;
|
||||
padding: 9px 10px;
|
||||
background: transparent;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
color: white;
|
||||
color: rgba(255, 255, 255, 0.95);
|
||||
text-align: left;
|
||||
cursor: pointer;
|
||||
font-family: inherit;
|
||||
@@ -540,43 +556,65 @@ onBeforeUnmount(() => {
|
||||
}
|
||||
.mb-item:hover,
|
||||
.mb-item.is-active {
|
||||
background: var(--m-bg-soft);
|
||||
background: rgba(255, 255, 255, 0.08);
|
||||
}
|
||||
.mb-item__icon {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
background: var(--m-bg-soft);
|
||||
background: rgba(255, 255, 255, 0.08);
|
||||
border-radius: 7px;
|
||||
color: var(--m-text-muted);
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
flex-shrink: 0;
|
||||
font-size: 0.85rem;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
/* Cores por tipo — espelha a paleta da agenda */
|
||||
.mb-item__icon--patient {
|
||||
background: rgba(244, 114, 182, 0.18); /* pink-400 — paciente */
|
||||
color: #f9a8d4;
|
||||
}
|
||||
.mb-item__icon--sessao {
|
||||
background: rgba(99, 102, 241, 0.20); /* indigo-500 — sessão (compromisso determinístico) */
|
||||
color: #a5b4fc;
|
||||
}
|
||||
.mb-item__icon--doc {
|
||||
background: rgba(14, 165, 233, 0.18); /* sky-500 — documentos */
|
||||
color: #7dd3fc;
|
||||
}
|
||||
.mb-item__icon--intake {
|
||||
background: rgba(251, 146, 60, 0.18); /* orange-400 — cadastros recebidos */
|
||||
color: #fdba74;
|
||||
}
|
||||
.mb-item__main {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1px;
|
||||
gap: 2px;
|
||||
}
|
||||
.mb-item__label {
|
||||
font-size: 0.85rem;
|
||||
color: white;
|
||||
font-size: 0.88rem;
|
||||
font-weight: 500;
|
||||
color: rgba(255, 255, 255, 0.96);
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.mb-item__sub {
|
||||
font-size: 0.7rem;
|
||||
color: var(--m-text-muted);
|
||||
font-size: 0.74rem;
|
||||
color: rgba(255, 255, 255, 0.65);
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.mb-item:hover .mb-item__sub,
|
||||
.mb-item.is-active .mb-item__sub {
|
||||
color: rgba(255, 255, 255, 0.78);
|
||||
}
|
||||
.mb-item__go {
|
||||
color: var(--m-text-faint);
|
||||
font-size: 0.7rem;
|
||||
color: rgba(255, 255, 255, 0.4);
|
||||
font-size: 0.75rem;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user