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
+16 -3
View File
@@ -204,15 +204,28 @@ export function useLayout() {
const setVariant = (v, { fromUser = true } = {}) => {
if (v !== 'classic' && v !== 'rail' && v !== 'melissa') return;
const prev = layoutConfig.variant;
layoutConfig.variant = v;
try {
localStorage.setItem('layout_variant', v);
} catch {}
// reset rail state ao trocar
layoutState.railSectionKey = null;
layoutState.railPanelOpen = false;
// Reset do estado do rail SÓ quando o novo variant não é 'rail'.
// Antes, o reset acontecia em todo setVariant — incluindo na troca
// de volta pra rail (ex.: rail → melissa → rail). Resultado: o rail
// remontava sem seção ativa e o menu da esquerda aparecia sem itens
// até o usuário clicar em algo. Preservar o estado quando volta pra
// rail mantém a UX coerente.
if (v !== 'rail') {
layoutState.railSectionKey = null;
layoutState.railPanelOpen = false;
}
// marca que o usuário fez uma escolha explícita (não restauração do DB)
if (fromUser) layoutState._variantDirty = true;
// dev-only: facilita auditar trocas de layout sem instrumentar o profile
if (typeof window !== 'undefined' && window?.__DEV_LAYOUT_LOG) {
// eslint-disable-next-line no-console
console.debug('[layout.setVariant]', prev, '→', v);
}
};
const setRailOpenMode = (mode) => {