From 7d2307dcf06014a47b295f6bb00785df8f18c5bc Mon Sep 17 00:00:00 2001 From: Leonardo Date: Wed, 6 May 2026 17:31:28 -0300 Subject: [PATCH] MelissaPagamento: pagina nativa 2-col com 6 cards de metodos MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Tira "Pagamento" do MelissaConfiguracoes (era embed cfg-pagamento -> ConfiguracoesPagamentoPage.vue, 580 linhas). Cria a /melissa/pagamento nativa Melissa. Sidebar (mpg-side): - Card "Resumo" — 5 mini stats coloridos por metodo (Pix verde, Deposito azul, Dinheiro amarelo, Cartao roxo, Convenio teal), com label "Ativo" ou "Inativo" + cor da borda quando ativo - Card "Como funciona" — FAQ (Agendador / Cobranca WhatsApp / Obs) Main (1-col, sem grid pq cards expandem dinamicamente quando ativos): - Pix: tipo de chave (Select 5 opcoes) + chave + nome titular - Deposito/TED: banco (Select 16 bancos BR) + tipo conta + agencia + conta + titular + CPF/CNPJ - Dinheiro: toggle simples - Cartao: toggle + instrucao opcional - Convenio: toggle + lista de convenios (Textarea) - Observacoes: Textarea livre Cada card com toggle no head + body que expande quando ativo + botao "Salvar" proprio (saveCard build payload do subset). Quando inativo, mostra so "Salvar como inativo" pra persistir o desligar. flex-shrink: 0 nos cards (mesmo padrao do AgendaConfig — conteudo varia muito). Logica espelhada do ConfiguracoesPagamentoPage (tabela payment_settings). Compativel com /configuracoes/pagamento. Wire-up: - MelissaLayout: import + render `` quando secaoAberta === 'pagamento' - 'pagamento' adicionado em SECOES + MELISSA_NON_CONFIG_SLUGS - MelissaConfiguracoes: cfg-pagamento removido de COMPONENT_MAP + item do grupo Financeiro re-rotulado pra slug 'pagamento' (atalho pra pagina nativa) Co-Authored-By: Claude Opus 4.7 (1M context) --- src/layout/melissa/MelissaConfiguracoes.vue | 4 +- src/layout/melissa/MelissaLayout.vue | 10 +- src/layout/melissa/MelissaPagamento.vue | 1137 +++++++++++++++++++ 3 files changed, 1148 insertions(+), 3 deletions(-) create mode 100644 src/layout/melissa/MelissaPagamento.vue diff --git a/src/layout/melissa/MelissaConfiguracoes.vue b/src/layout/melissa/MelissaConfiguracoes.vue index d3ecb52..ee2ac2d 100644 --- a/src/layout/melissa/MelissaConfiguracoes.vue +++ b/src/layout/melissa/MelissaConfiguracoes.vue @@ -74,7 +74,7 @@ const COMPONENT_MAP = { // 'cfg-agenda' removido — virou pagina nativa MelissaAgendaConfig em /melissa/agenda-config // 'cfg-bloqueios' removido — virou pagina nativa MelissaBloqueios em /melissa/bloqueios // 'cfg-agendador' removido — virou pagina nativa MelissaAgendador em /melissa/online-scheduling - 'cfg-pagamento': defineAsyncComponent(() => import('@/layout/configuracoes/ConfiguracoesPagamentoPage.vue')), + // 'cfg-pagamento' removido — virou pagina nativa MelissaPagamento em /melissa/pagamento 'cfg-precificacao': defineAsyncComponent(() => import('@/layout/configuracoes/ConfiguracoesPrecificacaoPage.vue')), 'cfg-descontos': defineAsyncComponent(() => import('@/layout/configuracoes/ConfiguracoesDescontosPage.vue')), 'cfg-excecoes': defineAsyncComponent(() => import('@/layout/configuracoes/ConfiguracoesExcecoesFinanceirasPage.vue')), @@ -178,7 +178,7 @@ const grupos = [ desc: 'Formas de pagamento, valores, descontos e convênios.', icon: 'pi pi-wallet', items: [ - { key: 'cfg-pagamento', label: 'Pagamento', desc: 'Formas de pagamento: Pix, depósito, dinheiro, cartão, convênio.', icon: 'pi pi-wallet' }, + { 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' }, diff --git a/src/layout/melissa/MelissaLayout.vue b/src/layout/melissa/MelissaLayout.vue index 5eca059..9a1c537 100644 --- a/src/layout/melissa/MelissaLayout.vue +++ b/src/layout/melissa/MelissaLayout.vue @@ -41,6 +41,7 @@ import MelissaSeguranca from './MelissaSeguranca.vue'; import MelissaBloqueios from './MelissaBloqueios.vue'; import MelissaAgendador from './MelissaAgendador.vue'; import MelissaAgendaConfig from './MelissaAgendaConfig.vue'; +import MelissaPagamento from './MelissaPagamento.vue'; import MelissaEmbed from './MelissaEmbed.vue'; import MelissaCadastrosRecebidos from './MelissaCadastrosRecebidos.vue'; import MelissaAgendamentosRecebidos from './MelissaAgendamentosRecebidos.vue'; @@ -183,6 +184,8 @@ const SECOES = { bloqueios: { label: 'Bloqueios e Feriados', icon: 'pi pi-ban', descricao: 'Feriados nacionais (auto), municipais e bloqueios manuais.' }, // Pagina nativa de configs da agenda (MelissaAgendaConfig) — saiu do MelissaConfiguracoes 'agenda-config': { label: 'Configurações da Agenda', icon: 'pi pi-calendar', descricao: 'Jornada (dias e horários), ritmo das sessões e agendamento online.' }, + // Pagina nativa de formas de pagamento (MelissaPagamento) — saiu do MelissaConfiguracoes + pagamento: { label: 'Formas de Pagamento', icon: 'pi pi-wallet', descricao: 'Pix, depósito, dinheiro, cartão e convênio.' }, // Pagina nativa de alterar plano (MelissaAlterarPlano) — substitui /therapist/upgrade 'alterar-plano': { label: 'Alterar Plano', icon: 'pi pi-arrow-up-right', descricao: 'Escolha um plano pessoal pra ativar todos os recursos.' }, // Onda 1 — pages embedadas via MelissaEmbed (1-coluna, hero glass) @@ -210,7 +213,7 @@ const MELISSA_NON_CONFIG_SLUGS = new Set([ 'link-externo', 'notificacoes', 'financeiro', 'financeiro-lancamentos', 'documentos', 'documentos-templates', 'relatorios', 'perfil', 'plano', 'negocio', 'seguranca', 'bloqueios', 'alterar-plano', - 'online-scheduling', 'agenda-config', + 'online-scheduling', 'agenda-config', 'pagamento', ...MELISSA_EMBED_KEYS ]); // Aliases "bonitos" + INLINE_KEYS reconhecidos pelo MelissaConfiguracoes. @@ -2266,6 +2269,11 @@ function onKeydown(e) { @close="fecharSecao" /> + + +/* + * MelissaPagamento — Pagina nativa Melissa pra "Formas de Pagamento". + * + * Substitui o embed cfg-pagamento que vivia dentro do MelissaConfiguracoes. + * Layout 2-col: + * - COL 1 (sidebar) — Card "Resumo" (5 mini stats coloridos por + * metodo: Pix/Deposito/Dinheiro/Cartao/Convenio) + Card "Como + * funciona" (FAQ) + * - COL 2 (main) — 6 cards: Pix + Deposito/TED + Dinheiro + Cartao + * + Convenio + Observacoes. Cada um com toggle ativo + fields + * + Salvar proprio (saveCard). + * + * Logica espelhada do ConfiguracoesPagamentoPage (tabela payment_settings). + */ +import { ref, computed, onMounted, onBeforeUnmount } from 'vue'; +import { useToast } from 'primevue/usetoast'; +import { supabase } from '@/lib/supabase/client'; +import { useTenantStore } from '@/stores/tenantStore'; +// InputText/Select/Textarea/ToggleSwitch/Tag/Skeleton: auto via PrimeVueResolver + +const emit = defineEmits(['close']); + +const toast = useToast(); +const tenantStore = useTenantStore(); + +// ── Breakpoints + drawer ─────────────────────────────────── +const drawerOpen = ref(false); +const isMobile = ref(false); +let _mqMobile = null; +function _onMqMobileChange(e) { + isMobile.value = e.matches; + if (!e.matches) drawerOpen.value = false; +} +function toggleDrawer() { drawerOpen.value = !drawerOpen.value; } +function fecharDrawer() { drawerOpen.value = false; } + +// ── Estado ───────────────────────────────────────────────── +const loading = ref(true); +const ownerId = ref(null); +const savingCard = ref(null); + +const DEFAULT = { + pix_ativo: false, + pix_tipo: 'cpf', + pix_chave: '', + pix_nome_titular: '', + deposito_ativo: false, + deposito_banco: '', + deposito_agencia: '', + deposito_conta: '', + deposito_tipo_conta: 'corrente', + deposito_titular: '', + deposito_cpf_cnpj: '', + dinheiro_ativo: false, + cartao_ativo: false, + cartao_instrucao: '', + convenio_ativo: false, + convenio_lista: '', + observacoes_pagamento: '' +}; +const cfg = ref({ ...DEFAULT }); + +// ── Opções ───────────────────────────────────────────────── +const pixTipoOptions = [ + { label: 'CPF', value: 'cpf' }, + { label: 'CNPJ', value: 'cnpj' }, + { label: 'E-mail', value: 'email' }, + { label: 'Celular', value: 'celular' }, + { label: 'Chave aleatória', value: 'aleatoria' } +]; +const pixTipoLabel = { + cpf: 'CPF (somente números)', + cnpj: 'CNPJ (somente números)', + email: 'E-mail', + celular: 'Celular (+55DDD9XXXXXXXX)', + aleatoria: 'Chave aleatória (UUID)' +}; +const tipoConta = [ + { label: 'Conta-corrente', value: 'corrente' }, + { label: 'Poupança', value: 'poupanca' } +]; +const bancos = [ + { label: 'Banco do Brasil' }, + { label: 'Bradesco' }, + { label: 'Caixa Econômica' }, + { label: 'Itaú' }, + { label: 'Nubank' }, + { label: 'Santander' }, + { label: 'Sicoob' }, + { label: 'Sicredi' }, + { label: 'Inter' }, + { label: 'C6 Bank' }, + { label: 'PicPay' }, + { label: 'Mercado Pago' }, + { label: 'PagBank' }, + { label: 'Neon' }, + { label: 'Next' }, + { label: 'Outro' } +].map((b) => ({ ...b, value: b.label })); + +// ── Resumo (mini stats sidebar) ──────────────────────────── +const summary = computed(() => [ + { key: 'pix', label: 'Pix', icon: 'pi pi-qrcode', color: '#10b981', ativo: !!cfg.value.pix_ativo }, + { key: 'deposito', label: 'Depósito', icon: 'pi pi-building-columns', color: '#3b82f6', ativo: !!cfg.value.deposito_ativo }, + { key: 'dinheiro', label: 'Dinheiro', icon: 'pi pi-wallet', color: '#eab308', ativo: !!cfg.value.dinheiro_ativo }, + { key: 'cartao', label: 'Cartão', icon: 'pi pi-credit-card', color: '#a855f7', ativo: !!cfg.value.cartao_ativo }, + { key: 'convenio', label: 'Convênio', icon: 'pi pi-heart', color: '#14b8a6', ativo: !!cfg.value.convenio_ativo } +]); + +const totalAtivos = computed(() => summary.value.filter((s) => s.ativo).length); + +// ── Auth / load ──────────────────────────────────────────── +async function load() { + loading.value = true; + try { + const { data: u } = await supabase.auth.getUser(); + const uid = u?.user?.id || null; + if (!uid) return; + ownerId.value = uid; + const { data } = await supabase + .from('payment_settings') + .select('*') + .eq('owner_id', uid) + .maybeSingle(); + if (data) cfg.value = { ...DEFAULT, ...data }; + } finally { + loading.value = false; + } +} + +// ── Save ─────────────────────────────────────────────────── +async function saveCard(cardKey) { + if (!ownerId.value) return; + savingCard.value = cardKey; + const payload = { + owner_id: ownerId.value, + tenant_id: tenantStore.activeTenantId || null + }; + if (cardKey === 'pix') { + Object.assign(payload, { + pix_ativo: cfg.value.pix_ativo, + pix_tipo: cfg.value.pix_tipo, + pix_chave: cfg.value.pix_chave?.trim() ?? '', + pix_nome_titular: cfg.value.pix_nome_titular?.trim() ?? '' + }); + } else if (cardKey === 'deposito') { + Object.assign(payload, { + deposito_ativo: cfg.value.deposito_ativo, + deposito_banco: cfg.value.deposito_banco, + deposito_agencia: cfg.value.deposito_agencia?.trim() ?? '', + deposito_conta: cfg.value.deposito_conta?.trim() ?? '', + deposito_tipo_conta: cfg.value.deposito_tipo_conta, + deposito_titular: cfg.value.deposito_titular?.trim() ?? '', + deposito_cpf_cnpj: cfg.value.deposito_cpf_cnpj?.trim() ?? '' + }); + } else if (cardKey === 'dinheiro') { + payload.dinheiro_ativo = cfg.value.dinheiro_ativo; + } else if (cardKey === 'cartao') { + Object.assign(payload, { + cartao_ativo: cfg.value.cartao_ativo, + cartao_instrucao: cfg.value.cartao_instrucao?.trim() ?? '' + }); + } else if (cardKey === 'convenio') { + Object.assign(payload, { + convenio_ativo: cfg.value.convenio_ativo, + convenio_lista: cfg.value.convenio_lista?.trim() ?? '' + }); + } else if (cardKey === 'observacoes') { + payload.observacoes_pagamento = cfg.value.observacoes_pagamento?.trim() ?? ''; + } + try { + const { error } = await supabase + .from('payment_settings') + .upsert(payload, { onConflict: 'owner_id' }); + if (error) throw error; + toast.add({ severity: 'success', summary: 'Salvo', detail: 'Atualizado.', life: 2200 }); + } catch (e) { + toast.add({ severity: 'error', summary: 'Erro ao salvar', detail: e?.message, life: 4000 }); + } finally { + savingCard.value = null; + } +} + +// ── Lifecycle ────────────────────────────────────────────── +onMounted(async () => { + if (typeof window !== 'undefined' && window.matchMedia) { + _mqMobile = window.matchMedia('(max-width: 1023px)'); + isMobile.value = _mqMobile.matches; + try { _mqMobile.addEventListener('change', _onMqMobileChange); } + catch { _mqMobile.addListener(_onMqMobileChange); } + } + await tenantStore.ensureLoaded(); + await load(); +}); + +onBeforeUnmount(() => { + if (_mqMobile) { + try { _mqMobile.removeEventListener('change', _onMqMobileChange); } + catch { _mqMobile.removeListener(_onMqMobileChange); } + } +}); + + +