Melissa: hub Configuracoes + Embed + 9 Pages novas + dialog blueprint dark

Sprints 04-29 + 04-30 acumuladas.

- MelissaConfiguracoes: hub 2-col com 6 grupos (Layout/Conta/Agenda/
  Financeiro/WhatsApp/Sistema), tudo embedado via MelissaEmbed.
- MelissaEmbed: wrapper generico que injeta layout-variant=melissa
  e remove cromos pra reaproveitar Pages tradicionais.
- 9 Melissa Pages novas: CadastrosRecebidos, Compromissos, Configuracoes,
  Conversas, Embed, Grupos, Medicos, Recorrencias, Tags.
- Dialog blueprint atualizado: bg-gray-100 (hardcoded light) ->
  bg-[var(--surface-ground)] (tema-aware). 22 dialogs migrados em
  9 arquivos. Anti-pattern documentado.
- PatientsCadastroPage: bug fix dropdown Grupo (optionLabel nome->name),
  toggle vertical/abas com persist localStorage, sticky margin-top.
- Surface picker no popover do MelissaLayout (8 swatches).
- useTopbarPlanMenu, useMelissaWhatsapp, useMelissaPacientesAside novos.
- Migration: status agenda remarcado/confirmado.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Leonardo
2026-05-04 11:41:19 -03:00
parent 269c380d9c
commit 86311ef305
52 changed files with 16214 additions and 1027 deletions
@@ -24,7 +24,7 @@
* Os handlers exibem toasts (success/warn) — o composable assume que os
* componentes consumidores já registraram `<Toast />` e `<ConfirmDialog />`.
*/
import { ref, computed, watch, onMounted } from 'vue';
import { ref, computed, watch, onMounted, onBeforeUnmount } from 'vue';
import { useToast } from 'primevue/usetoast';
import { useConfirm } from 'primevue/useconfirm';
import { supabase } from '@/lib/supabase/client';
@@ -322,11 +322,38 @@ export function useMelissaAgenda() {
// ── Inicialização ───────────────────────────────────────────
onMounted(async () => {
await loadSettings();
await loadDeterminedCommitments();
const tid = clinicTenantId.value;
if (tid) await loadFeriadosBase(tid);
});
// Refetch settings + workRules quando o user salva jornada/ritmo/online
// em /configuracoes/agenda (embedado no Melissa). Sem isso, a timeline
// do resumo continuaria mostrando o range antigo até reload da página.
function _onSettingsSaved() {
loadSettings();
}
onMounted(() => {
window.addEventListener('agenda:settings-saved', _onSettingsSaved);
});
onBeforeUnmount(() => {
window.removeEventListener('agenda:settings-saved', _onSettingsSaved);
});
// Commitments + feriados dependem do tenant. Em refresh "frio", o
// tenantStore ainda não terminou de hidratar quando o composable
// monta, e clinicTenantId fica null. loadDeterminedCommitments faz
// bail-out silencioso quando tenantId é vazio (rows = [], sem retry)
// — daí o "às vezes" do bug onde commitmentOptions chegava vazio no
// AgendaEventDialog. Watch com immediate: true dispara já se o tenant
// estiver pronto, ou no momento exato em que ele aparecer.
watch(
clinicTenantId,
async (tid) => {
if (!tid) return;
await loadDeterminedCommitments();
await loadFeriadosBase(tid);
},
{ immediate: true }
);
// Reload quando view muda OU quando settings/ownerId aparece
watch([viewStart, viewEnd], _reloadRange);
watch(ownerId, (v) => {