Sprint F (05-06). Blueprint tabular aplicado nas 6 paginas restantes;
dialogs harmonizados (FloatLabel + IconField + variant=filled + section
dividers, espelhando PatientsCadastroPage Identidade); ConversationDrawer
repaginado pra visual estilo WhatsApp.
Pages refatoradas (cada uma com subheader, sidebar __scroll + __footer
fixo "Limpar filtros", Xs inline pra zerar filtro individual, mobile
drawer com sticky footer):
- MelissaCompromissos: blueprint mantendo row design original (color
stripe + name + badges + descricao + meta inline). Filtros Status
(Ativos/Inativos) + Tipo (Nativos/Meus). Coluna Acoes frozen 140px
com toggle+pencil+trash.
- MelissaGrupos / MelissaTags: pattern completo + dialog "Pacientes
do grupo/tag" com lista vinculada via patient_group_patient /
patient_patient_tag. Avatar primary nos pacientes, header colorido
com cor da entidade, X de fechar igual .mc-close. Dialog de
criar/editar com FloatLabel + section dividers.
- MelissaMedicos: blueprint + dialog "Pacientes encaminhados" usando
cor primary do tema (medicos nao tem cor propria); dialog de
criar/editar com 4 secoes (Identificacao/Contato/Localizacao/Obs)
espelhando PatientsCadastroPage. Service ja tinha
fetchPatientsByMedicoNome (ILIKE em encaminhado_por).
- MelissaConversas: subheader, sidebar com bg-soft + border-right e
cards com sombra (mw-w--side), Limpar filtros global no footer fixo
(fix bug: filters era ref({...}) e eu lia filters.search direto, agora
usa .value), alerta de unlinked movido pro topo, kanban mobile com
min-height nas colunas pra mostrar mensagens.
- MelissaRecorrencias: subheader, button list de status (Ativas verde/
Encerradas vermelho/Todas) substitui SelectButton, busca por nome do
paciente, footer Limpar filtros, X inline no filtro Status.
ConversationDrawer redesign (WhatsApp-style):
- Header com avatar circular primary + iniciais + numero formatado
- Container de mensagens com bg "papel de parede" (color-mix com bege
esverdeado WA + radial-gradient pattern)
- Bolhas com cantos certos (top-left ou top-right zerado simulando
tail), sombra sutil, cores autenticas (#d9fdd3 light/#005c4b dark
outbound; #fff/#202c33 inbound), detecao dark via :global
- Time HH:MM + status overlay no canto inferior direito DENTRO do
balao; checks azuis quando lida (#53bdeb)
- Compose pill rounded-full + botao Send circular verde #00a884
- Removido fmtDateTime obsoleto (substituido por fmtTimeOnly)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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>
Fecha o gap de analytics que faltava: MRR/ARR de assinatura já existia,
mas não havia visão de receita dos créditos WhatsApp comprados via Asaas.
Banco (migration 20260423000011) — 4 RPCs saas_admin only:
- saas_wa_credits_revenue_stats(from, to): total arrecadado, count de
compras, tenants únicos, créditos vendidos, ticket médio.
- saas_wa_credits_top_packages(from, to): ranking top 10 pacotes por
revenue, consolida nome atual se pacote foi renomeado.
- saas_wa_credits_usage_summary(): snapshot atual de lifetime_purchased
vs lifetime_used vs current_balance + taxa de consumo.
- saas_wa_credits_revenue_evolution(from, to, bucket_days): série
temporal pra sparkline.
Todas com check is_saas_admin() no início + SECURITY DEFINER.
Frontend:
- useSaasCreditsAnalytics composable orquestra as 4 RPCs em paralelo
com seleção de período (30d/90d/6m/12m) que ajusta bucket_days
automaticamente.
- SaasCreditsRevenueCard.vue: 4 KPIs (receita + ticket médio, compras +
tenants, créditos vendidos, % consumo global), sparkline SVG com
indicador de tendência, ranking top 5 pacotes.
- Integrado no SaasDashboard logo antes da tabela "Distribuição por plano".
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Card novo pra clínica e terapeuta com 3 métricas + sparkline:
- Tempo médio (e mediana) de 1ª resposta no período
- Taxa de SLA cumprido — % de respostas dentro do threshold configurado
- Contagem total de respostas no período
- Sparkline da evolução com indicador de tendência (melhorando/piorando)
- Ranking top 5 terapeutas (só no ClinicDashboard)
Filtro de período: 7/30/90 dias (muda granularidade do bucket:
1/7/15 dias pra sparkline com ~5-6 pontos).
Banco (migration 20260423000006):
- Helper interno _first_response_runs: identifica "runs" de inbound
(sequências do paciente sem outbound entre) e calcula delta até a
próxima outbound. Evita contar múltiplas mensagens repetidas do
paciente. responder_id vem de conversation_assignments.
- first_response_stats: agregados (count, avg, median, min, max,
sla_compliance_rate baseado em conversation_sla_rules).
- first_response_by_therapist: ranking com avg e count por assigned_to.
- first_response_evolution: série temporal com bucket alinhado a
p_from (p_from + bucket_index * N days). Parâmetro p_bucket_days
deixa o frontend escolher granularidade por período.
Todas SECURITY DEFINER + GRANT authenticated/service_role. Filtro
opcional por therapist_id nas funções que aplicam.
Frontend:
- useFirstResponseAnalytics composable wraps as 3 RPCs com cache
via Promise.all paralelo. Helper formatSeconds (Ns/Xmin/Xh).
- FirstResponseCard.vue renderiza sparkline SVG nativo
(sem lib extra), cor da taxa SLA por threshold (verde ≥80%,
âmbar ≥50%, vermelho).
- Integrado em ClinicDashboard (visão global) e TherapistDashboard
(filtrado por ownerId, sem ranking).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
3 melhorias no item de notificação do sininho:
1. handleRowClick: agora resolve alias (/conversas → /therapist|admin/
conversas) baseado em tenantStore.activeRole. Antes caía em NotFound
quando o deeplink era /conversas ou /crm/conversas.
2. Se payload tem thread_key (alertas do SLA), o clique abre o drawer
global diretamente na thread em vez de navegar — experiência similar
à do botão do toast. Fallback pra deeplink se a thread sumiu.
3. typeMap ganha entrada 'system_alert' (ícone pi-exclamation-circle,
borda vermelha).
4. Botões inline "Conversa" e "Abrir" aparecem embaixo do detail quando
o payload tem thread_key ou deeplink — atalhos pras ações mais
comuns sem precisar clicar na área do item.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>