roadmap #11: recently viewed (ultimos 5 pacientes acessados)
ROADMAP item #1.3 #11. localStorage por user_id pra isolar sessoes diferentes no mesmo browser. ROADMAP sugeria localStorage OU tabela user_recent_access — escolhi localStorage por simplicidade (sem migration adicional + zero round-trip por visita). composables/useRecentPatients.js: - useRecentPatients() — composable reativo Tipo A: items + hasItems + addVisit + remove + clear + refresh - registerPatientVisit(patient) — helper stateless pra usar fora de setup (ex: navigation guards, action handlers) - Sincroniza entre instancias na mesma aba via CustomEvent + 'storage' - Max 5 items. Dedup por id, novo no topo. Wire-up de visita (registra ao carregar prontuario): - MelissaPaciente.vue: registerPatientVisit no loadAll apos detail.load - PatientProntuario.vue: registerPatientVisit em loadDetail apos p resolved Wire-up de visualizacao (mostra quando query vazia): - GlobalSearch.vue: grupo "Acessados recentemente" antes dos Atalhos. goTo("recent") navega pra /therapist/patients/:id. - MelissaBusca.vue: grupo "Acessados recentemente". emit('paciente') reusando a logica do MelissaLayout que ja navega pra /melissa/paciente?id=X. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -15,9 +15,11 @@ import InputText from 'primevue/inputtext';
|
||||
import { supabase } from '@/lib/supabase/client';
|
||||
import { useTenantStore } from '@/stores/tenantStore';
|
||||
import { searchPages } from './pagesIndex';
|
||||
import { useRecentPatients } from '@/composables/useRecentPatients';
|
||||
|
||||
const router = useRouter();
|
||||
const tenantStore = useTenantStore();
|
||||
const { items: recentPatients, hasItems: hasRecentPatients } = useRecentPatients();
|
||||
|
||||
// ────────────────────────────────────────────────────────────
|
||||
// State
|
||||
@@ -67,9 +69,14 @@ const filteredPages = computed(() => {
|
||||
// ────────────────────────────────────────────────────────────
|
||||
// Flat list pra navegação por teclado
|
||||
// ────────────────────────────────────────────────────────────
|
||||
// Recently-viewed só aparece quando a query está vazia — não polui resultados de busca.
|
||||
const showRecent = computed(() => !query.value.trim() && hasRecentPatients.value);
|
||||
const recentItems = computed(() => showRecent.value ? (recentPatients.value || []).slice(0, 5) : []);
|
||||
|
||||
const flatList = computed(() => {
|
||||
const out = [];
|
||||
filteredActions.value.forEach((a, i) => out.push({ group: 'actions', item: a, idx: i }));
|
||||
recentItems.value.forEach((p, i) => out.push({ group: 'recent', item: p, idx: i }));
|
||||
results.value.patients.forEach((p, i) => out.push({ group: 'patients', item: p, idx: i }));
|
||||
results.value.intakes.forEach((r, i) => out.push({ group: 'intakes', item: r, idx: i }));
|
||||
results.value.appointments.forEach((a, i) => out.push({ group: 'appointments', item: a, idx: i }));
|
||||
@@ -195,6 +202,16 @@ function onInputKeydown(e) {
|
||||
}
|
||||
|
||||
async function goTo(entry) {
|
||||
// Recent patients: usa id pra navegar pro prontuário do paciente
|
||||
if (entry?.group === 'recent' && entry?.item?.id) {
|
||||
showPanel.value = false;
|
||||
query.value = '';
|
||||
resetResults();
|
||||
activeIndex.value = -1;
|
||||
await router.push({ path: '/therapist/patients/' + entry.item.id });
|
||||
return;
|
||||
}
|
||||
|
||||
const target = entry?.item?.to || entry?.item?.deeplink || entry?.item?.path;
|
||||
if (!target) return;
|
||||
showPanel.value = false;
|
||||
@@ -266,6 +283,27 @@ const kbdModifier = kbdIsMac ? '⌘' : 'Ctrl';
|
||||
</div>
|
||||
|
||||
<template v-else>
|
||||
<!-- Acessados recentemente (só quando query vazia) -->
|
||||
<div v-if="showRecent" class="gs-group">
|
||||
<div class="gs-group__title">Acessados recentemente</div>
|
||||
<button
|
||||
v-for="(p, i) in recentItems"
|
||||
:key="'rp-' + p.id"
|
||||
type="button"
|
||||
class="gs-item"
|
||||
:class="{ 'is-active': findFlatIndex('recent', i) === activeIndex }"
|
||||
@mouseenter="activeIndex = findFlatIndex('recent', i)"
|
||||
@click="goTo({ group: 'recent', item: p, idx: i })"
|
||||
>
|
||||
<span class="gs-item__icon"><i class="pi pi-history" /></span>
|
||||
<span class="gs-item__main">
|
||||
<span class="gs-item__label">{{ p.nome }}</span>
|
||||
<span class="gs-item__sub">{{ p.extras?.telefone || p.extras?.email || 'Abrir prontuário' }}</span>
|
||||
</span>
|
||||
<i class="gs-item__go pi pi-arrow-right" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Ações -->
|
||||
<div v-if="filteredActions.length" class="gs-group">
|
||||
<div class="gs-group__title">{{ query.trim() ? 'Ações' : 'Atalhos' }}</div>
|
||||
|
||||
Reference in New Issue
Block a user