diff --git a/src/layout/melissa/MelissaAgendaConfig.vue b/src/layout/melissa/MelissaAgendaConfig.vue index 014e83f..b1be047 100644 --- a/src/layout/melissa/MelissaAgendaConfig.vue +++ b/src/layout/melissa/MelissaAgendaConfig.vue @@ -1431,7 +1431,7 @@ onBeforeUnmount(() => { /* ═══════ Page chrome ═══════ */ .mac-page { position: absolute; - inset: 6px 6px calc(var(--m-dock-h, 76px) + 6px) 6px; + inset: 6px 6px calc(var(--m-dock-h, 76px) + 6px) var(--m-config-aside-left, 6px); z-index: 40; display: flex; flex-direction: column; diff --git a/src/layout/melissa/MelissaAgendador.vue b/src/layout/melissa/MelissaAgendador.vue index 17875f7..b1a3693 100644 --- a/src/layout/melissa/MelissaAgendador.vue +++ b/src/layout/melissa/MelissaAgendador.vue @@ -1381,7 +1381,7 @@ const summaryItems = computed(() => [ /* ═══════ Page chrome ═══════ */ .mag-page { position: absolute; - inset: 6px 6px calc(var(--m-dock-h, 76px) + 6px) 6px; + inset: 6px 6px calc(var(--m-dock-h, 76px) + 6px) var(--m-config-aside-left, 6px); z-index: 40; display: flex; flex-direction: column; diff --git a/src/layout/melissa/MelissaAlterarPlano.vue b/src/layout/melissa/MelissaAlterarPlano.vue index 853d12b..f145628 100644 --- a/src/layout/melissa/MelissaAlterarPlano.vue +++ b/src/layout/melissa/MelissaAlterarPlano.vue @@ -542,7 +542,7 @@ onMounted(async () => { /* ═══════ Page chrome ═══════ */ .map-page { position: absolute; - inset: 6px 6px calc(var(--m-dock-h, 76px) + 6px) 6px; + inset: 6px 6px calc(var(--m-dock-h, 76px) + 6px) var(--m-config-aside-left, 6px); z-index: 40; display: flex; flex-direction: column; diff --git a/src/layout/melissa/MelissaBloqueios.vue b/src/layout/melissa/MelissaBloqueios.vue index 582f741..d12b39f 100644 --- a/src/layout/melissa/MelissaBloqueios.vue +++ b/src/layout/melissa/MelissaBloqueios.vue @@ -837,7 +837,7 @@ onBeforeUnmount(() => { /* ═══════ Page chrome ═══════ */ .mbq-page { position: absolute; - inset: 6px 6px calc(var(--m-dock-h, 76px) + 6px) 6px; + inset: 6px 6px calc(var(--m-dock-h, 76px) + 6px) var(--m-config-aside-left, 6px); z-index: 40; display: flex; flex-direction: column; diff --git a/src/layout/melissa/MelissaConfigSidebar.vue b/src/layout/melissa/MelissaConfigSidebar.vue new file mode 100644 index 0000000..ba0709c --- /dev/null +++ b/src/layout/melissa/MelissaConfigSidebar.vue @@ -0,0 +1,323 @@ + + + + + + + diff --git a/src/layout/melissa/MelissaConfiguracoes.vue b/src/layout/melissa/MelissaConfiguracoes.vue index ee2ac2d..11724fd 100644 --- a/src/layout/melissa/MelissaConfiguracoes.vue +++ b/src/layout/melissa/MelissaConfiguracoes.vue @@ -593,7 +593,7 @@ function resetCores() { /* ═════ Container ═════ */ .mcfg-page { position: absolute; - inset: 6px 6px calc(var(--m-dock-h, 76px) + 6px) 6px; + inset: 6px 6px calc(var(--m-dock-h, 76px) + 6px) var(--m-config-aside-left, 6px); z-index: 40; display: flex; flex-direction: column; diff --git a/src/layout/melissa/MelissaLayout.vue b/src/layout/melissa/MelissaLayout.vue index 9a1c537..6c5ebf0 100644 --- a/src/layout/melissa/MelissaLayout.vue +++ b/src/layout/melissa/MelissaLayout.vue @@ -42,6 +42,8 @@ import MelissaBloqueios from './MelissaBloqueios.vue'; import MelissaAgendador from './MelissaAgendador.vue'; import MelissaAgendaConfig from './MelissaAgendaConfig.vue'; import MelissaPagamento from './MelissaPagamento.vue'; +import MelissaConfigSidebar from './MelissaConfigSidebar.vue'; +import { isMelissaConfigSlug } from './composables/melissaConfigGrupos.js'; import MelissaEmbed from './MelissaEmbed.vue'; import MelissaCadastrosRecebidos from './MelissaCadastrosRecebidos.vue'; import MelissaAgendamentosRecebidos from './MelissaAgendamentosRecebidos.vue'; @@ -248,6 +250,17 @@ const secaoAberta = computed(() => { return null; }); +// Mostra o sidebar lateral de configuracoes (MelissaConfigSidebar) +// quando a rota atual e um slug do catalogo de configs (any item em +// MELISSA_CONFIG_GRUPOS). +const showConfigSidebar = computed(() => isMelissaConfigSlug(secaoAberta.value)); + +// CSS var pras paginas nativas saberem que precisam abrir espaco a +// esquerda pro sidebar (296px = 280 width + 16 gap). +const configAsideLeftStyle = computed(() => ({ + '--m-config-aside-left': showConfigSidebar.value ? '296px' : '6px' +})); + // Quando o usuário fecha a seção e volta pro resumo, garante que os // dados estão prontos (caso o idle callback ainda não tenha disparado // no fluxo deep-link). fetchCached é idempotente: cache hit → instant, @@ -1388,7 +1401,7 @@ function onKeydown(e) { - + @@ -2228,6 +2241,19 @@ function onKeydown(e) { @close="fecharSecao" /> + + + + + + + + + { /* ═══════ Page chrome (mesmo pattern do MelissaPerfil) ═══════ */ .mng-page { position: absolute; - inset: 6px 6px calc(var(--m-dock-h, 76px) + 6px) 6px; + inset: 6px 6px calc(var(--m-dock-h, 76px) + 6px) var(--m-config-aside-left, 6px); z-index: 40; display: flex; flex-direction: column; diff --git a/src/layout/melissa/MelissaPagamento.vue b/src/layout/melissa/MelissaPagamento.vue index bf9d50d..0912338 100644 --- a/src/layout/melissa/MelissaPagamento.vue +++ b/src/layout/melissa/MelissaPagamento.vue @@ -669,7 +669,7 @@ onBeforeUnmount(() => { /* ═══════ Page chrome ═══════ */ .mpg-page { position: absolute; - inset: 6px 6px calc(var(--m-dock-h, 76px) + 6px) 6px; + inset: 6px 6px calc(var(--m-dock-h, 76px) + 6px) var(--m-config-aside-left, 6px); z-index: 40; display: flex; flex-direction: column; diff --git a/src/layout/melissa/MelissaPerfil.vue b/src/layout/melissa/MelissaPerfil.vue index dfcf66a..e57e86a 100644 --- a/src/layout/melissa/MelissaPerfil.vue +++ b/src/layout/melissa/MelissaPerfil.vue @@ -917,7 +917,7 @@ onBeforeUnmount(() => { /* ═══════ Page chrome ═══════ */ .mpr-page { position: absolute; - inset: 6px 6px calc(var(--m-dock-h, 76px) + 6px) 6px; + inset: 6px 6px calc(var(--m-dock-h, 76px) + 6px) var(--m-config-aside-left, 6px); z-index: 40; display: flex; flex-direction: column; diff --git a/src/layout/melissa/MelissaPlano.vue b/src/layout/melissa/MelissaPlano.vue index 2d50c2a..05d55d5 100644 --- a/src/layout/melissa/MelissaPlano.vue +++ b/src/layout/melissa/MelissaPlano.vue @@ -674,7 +674,7 @@ onMounted(async () => { /* ═══════ Page chrome (mesmo pattern do MelissaPerfil) ═══════ */ .mpl-page { position: absolute; - inset: 6px 6px calc(var(--m-dock-h, 76px) + 6px) 6px; + inset: 6px 6px calc(var(--m-dock-h, 76px) + 6px) var(--m-config-aside-left, 6px); z-index: 40; display: flex; flex-direction: column; diff --git a/src/layout/melissa/MelissaSeguranca.vue b/src/layout/melissa/MelissaSeguranca.vue index 3e82698..ef7591b 100644 --- a/src/layout/melissa/MelissaSeguranca.vue +++ b/src/layout/melissa/MelissaSeguranca.vue @@ -433,7 +433,7 @@ onMounted(async () => { /* ═══════ Page chrome ═══════ */ .mse-page { position: absolute; - inset: 6px 6px calc(var(--m-dock-h, 76px) + 6px) 6px; + inset: 6px 6px calc(var(--m-dock-h, 76px) + 6px) var(--m-config-aside-left, 6px); z-index: 40; display: flex; flex-direction: column; diff --git a/src/layout/melissa/composables/melissaConfigGrupos.js b/src/layout/melissa/composables/melissaConfigGrupos.js new file mode 100644 index 0000000..e0ff5c6 --- /dev/null +++ b/src/layout/melissa/composables/melissaConfigGrupos.js @@ -0,0 +1,116 @@ +/* + * melissaConfigGrupos — fonte unica do catalogo de configs Melissa. + * + * Usado por: + * - MelissaConfigSidebar (renderiza o accordion + items) + * - MelissaLayout (decide quando mostrar o sidebar de config) + * - MelissaConfiguracoes (re-usa o mesmo catalogo via componente) + * + * Cada item tem `key` que e o slug usado em /melissa/. Se a key + * comeca com 'cfg-', a pagina e embed (renderizada pelo + * MelissaConfiguracoes via COMPONENT_MAP). Se e slug "limpo" (perfil, + * plano, agenda-config, pagamento, etc), e pagina nativa Melissa. + */ +export const MELISSA_CONFIG_GRUPOS = [ + { + key: 'layout-melissa', + label: 'Layout Melissa', + desc: 'Aparência, plano de fundo, relógio e cronômetro do resumo.', + icon: 'pi pi-palette', + items: [ + { key: 'aparencia', label: 'Layout Melissa', desc: 'Tema, cor primária, surface, plano de fundo, relógio e cronômetro — tudo numa tela só.', icon: 'pi pi-palette' } + ] + }, + { + key: 'conta', + label: 'Conta', + desc: 'Perfil, plano, negócio e segurança.', + icon: 'pi pi-user', + items: [ + { key: 'perfil', label: 'Meu Perfil', desc: 'Identidade, contato, bio, redes — gamificação no aside.', icon: 'pi pi-user' }, + { key: 'plano', label: 'Meu Plano', desc: 'Assinatura, recursos liberados e histórico de mudanças.', icon: 'pi pi-credit-card' }, + { key: 'negocio', label: 'Meu Negócio', desc: 'Identidade, fiscal, endereço, contato, redes.', icon: 'pi pi-briefcase' }, + { key: 'seguranca', label: 'Segurança', desc: 'Trocar senha + boas práticas + estado da sessão.', icon: 'pi pi-shield' } + ] + }, + { + key: 'agenda', + label: 'Agenda', + desc: 'Horários, bloqueios e agendador público.', + icon: 'pi pi-calendar', + items: [ + { key: 'agenda-config', label: 'Configurações da Agenda', desc: 'Jornada (dias e horários), ritmo das sessões e agendamento online.', icon: 'pi pi-calendar' }, + { key: 'bloqueios', label: 'Bloqueios e Feriados', desc: 'Feriados nacionais (auto), municipais e bloqueios manuais.', icon: 'pi pi-ban' }, + { key: 'online-scheduling', label: 'Agendador Online', desc: 'Link público, identidade visual, fluxo, pagamento e textos.', icon: 'pi pi-calendar-clock' } + ] + }, + { + key: 'financeiro', + label: 'Financeiro', + desc: 'Formas de pagamento, valores, descontos e convênios.', + icon: 'pi pi-wallet', + items: [ + { key: 'pagamento', label: 'Formas de Pagamento', desc: 'Pix, depósito, dinheiro, cartão e convênio.', icon: 'pi pi-wallet' }, + { key: 'cfg-precificacao', label: 'Precificação', desc: 'Valor padrão da sessão e preços por tipo de compromisso.', icon: 'pi pi-tag' }, + { key: 'cfg-descontos', label: 'Descontos por Paciente', desc: 'Descontos recorrentes aplicados automaticamente.', icon: 'pi pi-percentage' }, + { key: 'cfg-excecoes', label: 'Exceções Financeiras', desc: 'O que cobrar em faltas, cancelamentos e situações excepcionais.', icon: 'pi pi-exclamation-triangle' }, + { key: 'cfg-convenios', label: 'Convênios', desc: 'Cadastre os convênios que você atende e seus valores.', icon: 'pi pi-id-card' } + ] + }, + { + key: 'whatsapp', + label: 'WhatsApp & Conversas', + desc: 'Canal, tags, auto-reply, lembretes e créditos.', + icon: 'pi pi-whatsapp', + items: [ + { key: 'cfg-wa', label: 'Canal WhatsApp', desc: 'Escolha o canal (oficial AgenciaPSI ou pessoal) e configure a integração.', icon: 'pi pi-whatsapp' }, + { key: 'cfg-wa-templates', label: 'Templates WhatsApp', desc: 'Personalize os textos enviados ou volte ao padrão da plataforma.', icon: 'pi pi-file-edit' }, + { key: 'cfg-conversas-tags', label: 'Tags de Conversa', desc: 'Etiquetas custom pra classificar threads no CRM (urgente, remarcação…).', icon: 'pi pi-tag' }, + { key: 'cfg-conversas-autoreply', label: 'Auto-reply WhatsApp', desc: 'Resposta automática quando paciente escreve fora do horário.', icon: 'pi pi-reply' }, + { key: 'cfg-conversas-optouts', label: 'Opt-outs (LGPD)', desc: 'Números que pediram pra não receber mensagens. Direito de oposição LGPD.', icon: 'pi pi-ban' }, + { key: 'cfg-conversas-sla', label: 'SLA de resposta', desc: 'Tempo máximo pra responder. Alerta quando estourar.', icon: 'pi pi-stopwatch' }, + { key: 'cfg-conversas-bots', label: 'Bot de triagem', desc: 'Coleta nome e motivo via WhatsApp antes do fluxo humano.', icon: 'pi pi-android' }, + { key: 'cfg-lembretes', label: 'Lembretes de Sessão', desc: 'WhatsApp automático 24h e 2h antes das sessões agendadas.', icon: 'pi pi-bell' }, + { key: 'cfg-creditos-wa', label: 'Créditos WhatsApp', desc: 'Compre pacotes de mensagens, veja saldo e extrato.', icon: 'pi pi-credit-card' } + ] + }, + { + key: 'comunicacao', + label: 'Comunicação', + desc: 'SMS e templates de e-mail enviados aos pacientes.', + icon: 'pi pi-send', + items: [ + { key: 'cfg-sms', label: 'SMS', desc: 'Gerencie créditos SMS e personalize as mensagens enviadas.', icon: 'pi pi-comment' }, + { key: 'cfg-email-templates', label: 'Templates de E-mail', desc: 'Personalize os e-mails enviados aos pacientes.', icon: 'pi pi-envelope' } + ] + }, + { + key: 'plataforma', + label: 'Empresa & Plataforma', + desc: 'Dados da empresa, recursos extras e auditoria.', + icon: 'pi pi-building', + items: [ + { key: 'cfg-empresa', label: 'Minha Empresa', desc: 'CNPJ, endereço, logomarca e redes sociais.', icon: 'pi pi-building' }, + { key: 'cfg-recursos-extras', label: 'Recursos Extras', desc: 'Amplíe as funcionalidades com recursos adicionais.', icon: 'pi pi-box' }, + { key: 'cfg-auditoria', label: 'Auditoria', desc: 'Registro imutável de operações (LGPD Art. 37).', icon: 'pi pi-shield' } + ] + } +]; + +// Set de slugs que sao "config routes" — mostram o sidebar de config +export const MELISSA_CONFIG_SLUGS = new Set( + MELISSA_CONFIG_GRUPOS.flatMap((g) => g.items.map((i) => i.key)) +); + +export function isMelissaConfigSlug(slug) { + return MELISSA_CONFIG_SLUGS.has(slug); +} + +// Acha o item por slug (pra destacar ativo) +export function findMelissaConfigItem(slug) { + for (const g of MELISSA_CONFIG_GRUPOS) { + const item = g.items.find((i) => i.key === slug); + if (item) return { grupo: g, item }; + } + return null; +}