MelissaPaciente Fase 5: Tab Agenda completa (KPIs + filtros + grupos por mes + acoes)

EXTENSAO src/features/patients/utils/patientFormatters.js: +2 helpers
- fmtHourShort (HH:MM 24h pt-br) — usado na coluna data dos cards
- fmtDayShort (DOW abreviado pt-br sem ponto) — usado na coluna data

EXTENSAO src/features/patients/composables/usePatientSessions.js
- Novo ref `busy` pra disable de buttons durante mutation
- _lastPatientId guardado internamente pra auto-reload
- Nova funcao `updateStatus(sessionId, novoStatus)` que faz
  supabase.from('agenda_eventos').update({status}) + auto-reload da
  lista de sessoes. Retorna {ok, error?}.

MELISSAPACIENTE.VUE — script
- agendaFilter ref ('all' default) + AGENDA_FILTERS array com 6 opcoes
  (Todas, Proximas, Passadas, Realizadas, Faltas, Canceladas)
- agendaSessoesFiltradas computed: filtra por future/past/status (regex)
- agendaAgrupadas computed: agrupa por "Mes de YYYY" DESC
- updateSessionStatus(ev, status, msg): chama sessionsHook.updateStatus +
  toast de sucesso/erro
- Removido `void toast` (toast usado de verdade agora)

MELISSAPACIENTE.VUE — Tab Agenda reescrita (substitui placeholder Fase 1)
- 4 KPI cards no padrao Visao Geral (numerados 01-04):
  Total / Realizadas (% do total) / Faltas (cor adaptativa) / Proxima
- 6 filter chips redondas (cor primary quando active)
- Empty state contextual (sem sessoes vs filtro vazio)
- Grupos por mes com header (label + badge count)
- Cards 3-col: data column (DOW + dia + hora) | main (status tag + chips
  modalidade/duracao + relative + titulo + note 2-line clamp) | actions
  (3 buttons: ok/warn/danger com tooltip + cor adaptativa no hover)
- Mobile: stack date+main em 2 cols; actions full-width abaixo

CSS: ~150L novos. Padrao visual Melissa: data column estilo calendario,
actions hover muda cor por intent (verde realiz / amarelo falta / vermelho
cancel), border-left por status.

ESLint: 0 errors da minha mudanca.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Leonardo
2026-05-08 09:53:59 -03:00
parent 1278e93b01
commit 8a8d2e05bd
4 changed files with 499 additions and 16 deletions
@@ -16,8 +16,11 @@ export function usePatientSessions() {
const sessions = ref([]);
const loading = ref(false);
const error = ref('');
const busy = ref(false); // mutations em curso (updateStatus etc)
let _lastPatientId = null;
async function load(patientId) {
_lastPatientId = patientId || null;
if (!patientId) {
sessions.value = [];
return;
@@ -81,11 +84,36 @@ export function usePatientSessions() {
.slice(0, 6)
);
/**
* Atualiza o status de uma sessao (mutation). Recarrega a lista do paciente
* ao final pra refletir o novo estado nos computeds derivados.
* Retorna {ok: true} ou {ok: false, error: msg}.
*/
async function updateStatus(sessionId, novoStatus) {
if (!sessionId || busy.value) return { ok: false, error: 'busy' };
busy.value = true;
try {
const { error: err } = await supabase
.from('agenda_eventos')
.update({ status: novoStatus })
.eq('id', sessionId);
if (err) throw err;
if (_lastPatientId) await load(_lastPatientId);
return { ok: true };
} catch (e) {
return { ok: false, error: e?.message || 'Erro ao atualizar status' };
} finally {
busy.value = false;
}
}
return {
sessions,
loading,
error,
busy,
load,
updateStatus,
proximaSessao,
ultimaSessao,
totalSessoes,
@@ -122,6 +122,26 @@ export function fmtDateBR(v) {
return `${String(d.getDate()).padStart(2, '0')}/${String(d.getMonth() + 1).padStart(2, '0')}/${d.getFullYear()}`;
}
/**
* Hora curta HH:MM (24h pt-br).
*/
export function fmtHourShort(iso) {
if (!iso) return '';
const d = new Date(iso);
if (Number.isNaN(d.getTime())) return '';
return `${String(d.getHours()).padStart(2, '0')}:${String(d.getMinutes()).padStart(2, '0')}`;
}
/**
* Dia da semana abreviado pt-br (seg/ter/qua...).
*/
export function fmtDayShort(iso) {
if (!iso) return '';
const d = new Date(iso);
if (Number.isNaN(d.getTime())) return '';
return d.toLocaleDateString('pt-BR', { weekday: 'short' }).replace('.', '');
}
export function fmtDateTimeBR(iso) {
if (!iso) return '—';
const d = new Date(iso);