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:
@@ -16,6 +16,7 @@
|
||||
*/
|
||||
import { ref, computed, onMounted, onBeforeUnmount, watch } from 'vue';
|
||||
import { supabase } from '@/lib/supabase/client';
|
||||
import { useRecentPatients } from '@/composables/useRecentPatients';
|
||||
|
||||
const props = defineProps({
|
||||
pacientes: { type: Array, default: () => [] },
|
||||
@@ -48,6 +49,11 @@ const searching = ref(false);
|
||||
let debounceT = null;
|
||||
let searchSeq = 0;
|
||||
|
||||
// Recently viewed (localStorage) — só aparece quando o input está vazio
|
||||
const { items: recentPatients, hasItems: hasRecentPatients } = useRecentPatients();
|
||||
const showRecent = computed(() => !query.value.trim() && hasRecentPatients.value);
|
||||
const recentItems = computed(() => showRecent.value ? (recentPatients.value || []).slice(0, 5) : []);
|
||||
|
||||
function normalize(s) {
|
||||
return String(s || '')
|
||||
.normalize('NFD')
|
||||
@@ -112,6 +118,7 @@ const rpcIntakes = computed(() => rpcResults.value.intakes || []);
|
||||
|
||||
const flatList = computed(() => {
|
||||
const out = [];
|
||||
recentItems.value.forEach((p, i) => out.push({ group: 'recent', item: p, idx: i }));
|
||||
filteredAtalhos.value.forEach((a, i) => out.push({ group: 'atalhos', item: a, idx: i }));
|
||||
filteredPacientes.value.forEach((p, i) => out.push({ group: 'pacientes', item: p, idx: i }));
|
||||
filteredEventos.value.forEach((e, i) => out.push({ group: 'eventos', item: e, idx: i }));
|
||||
@@ -130,6 +137,7 @@ function findFlatIndex(group, idx) {
|
||||
function selectEntry(entry) {
|
||||
if (entry.group === 'atalhos') emit('acao', entry.item.id);
|
||||
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);
|
||||
@@ -261,6 +269,26 @@ onBeforeUnmount(() => {
|
||||
Nada encontrado pra "<span class="text-white/80">{{ query.trim() }}</span>"
|
||||
</div>
|
||||
|
||||
<!-- Acessados recentemente (só quando query vazia) -->
|
||||
<div v-if="showRecent" class="mb-group">
|
||||
<div class="mb-group__title">Acessados recentemente</div>
|
||||
<button
|
||||
v-for="(p, i) in recentItems"
|
||||
:key="'mr-' + p.id"
|
||||
class="mb-item"
|
||||
:class="{ 'is-active': findFlatIndex('recent', i) === activeIndex }"
|
||||
@click="selectEntry({ group: 'recent', item: p })"
|
||||
@mouseenter="activeIndex = findFlatIndex('recent', i)"
|
||||
>
|
||||
<span class="mb-item__icon"><i class="pi pi-history" /></span>
|
||||
<span class="mb-item__main">
|
||||
<span class="mb-item__label">{{ p.nome }}</span>
|
||||
<span class="mb-item__sub">{{ p.extras?.telefone || p.extras?.email || 'Abrir prontuário' }}</span>
|
||||
</span>
|
||||
<i class="mb-item__go pi pi-arrow-right" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Atalhos -->
|
||||
<div v-if="filteredAtalhos.length" class="mb-group">
|
||||
<div class="mb-group__title">{{ query.trim() ? 'Ações' : 'Atalhos' }}</div>
|
||||
|
||||
Reference in New Issue
Block a user