Melissa polish + Prontuario Visao Geral + agenda historico

Sprints B (05-03) e C (05-04) acumulados:

- NotificationDrawer/Item redesign (visual mais limpo, ações inline)
- Dock pins compose (useMelissaDockPins) + cache store global (melissaCacheStore)
- MelissaAgenda: timeline FullCalendar parity + cards resumo, histórico
  card com useMelissaAgendaHistorico, MelissaEventoPanel ajustado
- useFeriados: cache opt-in pra evitar fetch redundante de feriados
- PatientProntuario: aba Visão Geral nova; PatientConversationsTab polish
- AgendaClinicMosaic / AgendaTerapeutaPage / useAgendaSettings: ajustes
  de paridade com Melissa
- DocumentsListPage: pequenos ajustes
- DB migration 20260504000001: fix do trigger pra status 'excluido' nas
  cancel_notifications

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Leonardo
2026-05-06 09:11:55 -03:00
parent 86311ef305
commit 957e912a7f
19 changed files with 5203 additions and 285 deletions
+72
View File
@@ -0,0 +1,72 @@
/*
|--------------------------------------------------------------------------
| Agência PSI
|--------------------------------------------------------------------------
| Arquivo: src/stores/melissaCacheStore.js
| Data: 2026-05-04
|
| Cache in-memory (Pinia) com stale-while-revalidate pra dados que o
| Melissa Layout consome em todas as visitas e raramente mudam:
| - pacientesTimeline: lista de pacientes do tenant (1000)
| - eventosHoje: eventos do dia (resumo)
| - feriados: municipais + globais por (tenant_id, ano)
| - agendaSettings: configurações + workRules do owner
|
| LGPD: tudo só em RAM. Some ao recarregar a aba ou trocar de sessão. Nunca
| persistido em localStorage/IndexedDB porque contém dados clínicos.
|
| Pattern de uso (composable):
| const cached = cache.get('pacientesTimeline', key, TTL.pacientes);
| if (cached) { ref.value = cached; refetchInBackground(); return; }
| const fresh = await fetch();
| cache.set('pacientesTimeline', fresh, key);
|
| Invalidação manual: chamar `cache.invalidate('slot')` em mutations
| (ex: criar paciente → invalidate('pacientesTimeline')).
|--------------------------------------------------------------------------
*/
import { defineStore } from 'pinia';
// Time-to-live por slot (ms). Slots de dados que mudam pouco ganham TTL
// mais longo; eventos do dia ganham TTL curto pra não mostrar lista
// desatualizada se uma sessão foi marcada/cancelada em outra aba.
export const MELISSA_CACHE_TTL = {
pacientesTimeline: 5 * 60 * 1000, // 5 min
eventosHoje: 90 * 1000, // 90 s
feriados: 60 * 60 * 1000, // 1 h
agendaSettings: 5 * 60 * 1000 // 5 min
};
function emptySlot() {
return { data: null, ts: 0, key: null };
}
export const useMelissaCacheStore = defineStore('melissaCache', {
state: () => ({
pacientesTimeline: emptySlot(),
eventosHoje: emptySlot(),
feriados: emptySlot(),
agendaSettings: emptySlot()
}),
actions: {
// Retorna data se houver cache válido pro `slot` E se a `key` bater
// (key encapsula contexto: uid, tenant, ano, dia — o que mudar
// invalida o slot automaticamente). Retorna null se inválido/expirado.
get(slot, key, ttl) {
const s = this[slot];
if (!s?.ts) return null;
if (key !== undefined && s.key !== key) return null;
if (Date.now() - s.ts > ttl) return null;
return s.data;
},
set(slot, data, key) {
this[slot] = { data, ts: Date.now(), key: key ?? null };
},
invalidate(slot) {
this[slot] = emptySlot();
},
invalidateAll() {
for (const k of Object.keys(this.$state)) this.invalidate(k);
}
}
});