771b636cee
Completa o Grupo 3 do CRM com alerta de conversa sem resposta além do tempo configurado — reutiliza o pipeline system_alert (toast vermelho sticky + sininho + drawer). Banco (migration 20260423000005): - conversation_sla_rules: 1 linha por tenant com threshold global (1-1440 min), respect_business_hours, business_hours_start/end, business_days (ISO 1=seg..7=dom), alert_scope (assigned_only|all), notify_admin_on_breach. Default: enabled=false. - conversation_sla_breaches: incidents com UNIQUE parcial (tenant_id, thread_key) WHERE resolved_at IS NULL — idempotência. - Trigger AFTER INSERT em conversation_messages resolve o breach automaticamente quando chega nova outbound na thread. - RPCs service_role: sla_open_breach (idempotente), sla_mark_notified. - RLS: membros do tenant leem; clinic_admin/tenant_admin/saas_admin escrevem na config; service_role escreve em breaches. Edge function conversation-sla-check (cron 5min): - Varre tenants com enabled=true. - Query conversation_threads onde last_message_direction='inbound' (+ assigned_to NOT NULL se scope='assigned_only'). - Se respect_business_hours: calcula businessMinutesElapsed em TS iterando dia por dia a interseção da janela [start,end] com [last_inbound_at, now], só em dias marcados em business_days. TZ fixa em America/Sao_Paulo via Intl.DateTimeFormat. - Se elapsed >= threshold: sla_open_breach (idempotente) + notifica assigned_to sempre + admins se notify_admin_on_breach (deduplicado via Set). - Anti-spam: só notifica 1x por incident (checa notified_at). - Notification leva deeplink pra /crm/conversas e payload.thread_key pro frontend destacar a conversa (fora de escopo deste commit). UI em /configuracoes/conversas-sla: - Toggle enabled + InputNumber threshold com preview "≈ Xh Ymin". - Toggle respect_business_hours → revela start/end + seletor de dias úteis (pills toggleáveis Seg..Dom, ISO order). - Select scope. - Toggle notify_admin_on_breach. - Card abaixo com breaches dos últimos 7 dias (status aberto/resolvido, thread_key, limite configurado no momento do breach, duração). - Adicionada na ConfiguracoesPage landing + rota /configuracoes/conversas-sla. Cron template comentado no fim da migration (mesmo padrão do heartbeat). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
168 lines
6.1 KiB
JavaScript
168 lines
6.1 KiB
JavaScript
/*
|
|
|--------------------------------------------------------------------------
|
|
| Agência PSI
|
|
|--------------------------------------------------------------------------
|
|
| Criado e desenvolvido por Leonardo Nohama
|
|
|
|
|
| Tecnologia aplicada à escuta.
|
|
| Estrutura para o cuidado.
|
|
|
|
|
| Arquivo: src/router/routes.configs.js
|
|
| Data: 2026
|
|
| Local: São Carlos/SP — Brasil
|
|
|--------------------------------------------------------------------------
|
|
| © 2026 — Todos os direitos reservados
|
|
|--------------------------------------------------------------------------
|
|
*/
|
|
|
|
// ConfiguracoesPage já tem <router-view> próprio — serve de layout intermediário.
|
|
// Não precisa de RouterPassthrough.
|
|
export default {
|
|
path: 'configuracoes',
|
|
component: () => import('@/layout/ConfiguracoesPage.vue'),
|
|
redirect: { name: 'ConfiguracoesAgenda' },
|
|
|
|
meta: {
|
|
requiresAuth: true,
|
|
roles: ['admin', 'tenant_admin', 'therapist']
|
|
},
|
|
|
|
children: [
|
|
{
|
|
path: 'agenda',
|
|
name: 'ConfiguracoesAgenda',
|
|
component: () => import('@/layout/configuracoes/ConfiguracoesAgendaPage.vue')
|
|
},
|
|
{
|
|
path: 'bloqueios',
|
|
name: 'ConfiguracoesBloqueios',
|
|
component: () => import('@/layout/configuracoes/BloqueiosPage.vue')
|
|
},
|
|
{
|
|
path: 'agendador',
|
|
name: 'ConfiguracoesAgendador',
|
|
component: () => import('@/layout/configuracoes/ConfiguracoesAgendadorPage.vue')
|
|
},
|
|
{
|
|
path: 'pagamento',
|
|
name: 'ConfiguracoesPagamento',
|
|
component: () => import('@/layout/configuracoes/ConfiguracoesPagamentoPage.vue')
|
|
},
|
|
{
|
|
path: 'precificacao',
|
|
name: 'ConfiguracoesPrecificacao',
|
|
component: () => import('@/layout/configuracoes/ConfiguracoesPrecificacaoPage.vue')
|
|
},
|
|
{
|
|
path: 'descontos',
|
|
name: 'ConfiguracoesDescontos',
|
|
component: () => import('@/layout/configuracoes/ConfiguracoesDescontosPage.vue')
|
|
},
|
|
{
|
|
path: 'excecoes-financeiras',
|
|
name: 'ConfiguracoesExcecoesFinanceiras',
|
|
component: () => import('@/layout/configuracoes/ConfiguracoesExcecoesFinanceirasPage.vue')
|
|
},
|
|
{
|
|
path: 'convenios',
|
|
name: 'ConfiguracoesConvenios',
|
|
component: () => import('@/layout/configuracoes/ConfiguracoesConveniosPage.vue')
|
|
},
|
|
{
|
|
path: 'email-templates',
|
|
name: 'ConfiguracoesEmailTemplates',
|
|
component: () => import('@/layout/configuracoes/ConfiguracoesEmailTemplatesPage.vue')
|
|
},
|
|
{
|
|
path: 'empresa',
|
|
name: 'ConfiguracoesMinhaEmpresa',
|
|
component: () => import('@/layout/configuracoes/ConfiguracoesMinhaEmpresaPage.vue')
|
|
},
|
|
{
|
|
path: 'canais',
|
|
name: 'ConfiguracoesCanais',
|
|
component: () => import('@/layout/configuracoes/ConfiguracoesCanaisPage.vue')
|
|
},
|
|
{
|
|
path: 'whatsapp',
|
|
name: 'ConfiguracoesWhatsapp',
|
|
component: () => import('@/layout/configuracoes/ConfiguracoesWhatsappChooserPage.vue')
|
|
},
|
|
{
|
|
path: 'whatsapp-pessoal',
|
|
name: 'ConfiguracoesWhatsappPessoal',
|
|
component: () => import('@/layout/configuracoes/ConfiguracoesWhatsappPage.vue')
|
|
},
|
|
{
|
|
path: 'whatsapp-oficial',
|
|
name: 'ConfiguracoesWhatsappOficial',
|
|
component: () => import('@/layout/configuracoes/ConfiguracoesTwilioWhatsappPage.vue')
|
|
},
|
|
{
|
|
path: 'whatsapp-templates',
|
|
name: 'ConfiguracoesWhatsappTemplates',
|
|
component: () => import('@/layout/configuracoes/ConfiguracoesWhatsappTemplatesPage.vue')
|
|
},
|
|
// Backcompat: old /whatsapp-twilio → redirect
|
|
{
|
|
path: 'whatsapp-twilio',
|
|
redirect: { name: 'ConfiguracoesWhatsappOficial' }
|
|
},
|
|
{
|
|
path: 'sms',
|
|
name: 'ConfiguracoesSms',
|
|
component: () => import('@/layout/configuracoes/ConfiguracoesSmsPage.vue')
|
|
},
|
|
{
|
|
path: 'sms-canal',
|
|
name: 'ConfiguracoesSmsCanal',
|
|
component: () => import('@/views/pages/notifications/SmsChannelSetupPage.vue')
|
|
},
|
|
{
|
|
path: 'recursos-extras',
|
|
name: 'ConfiguracoesRecursosExtras',
|
|
component: () => import('@/layout/configuracoes/ConfiguracoesRecursosExtrasPage.vue')
|
|
},
|
|
{
|
|
path: 'recursos-extras/extrato',
|
|
name: 'ConfiguracoesRecursosExtrasExtrato',
|
|
component: () => import('@/layout/configuracoes/AddonsExtratoPage.vue')
|
|
},
|
|
{
|
|
path: 'auditoria',
|
|
name: 'ConfiguracoesAuditoria',
|
|
component: () => import('@/layout/configuracoes/AuditoriaPage.vue')
|
|
},
|
|
{
|
|
path: 'conversas-tags',
|
|
name: 'ConfiguracoesConversasTags',
|
|
component: () => import('@/layout/configuracoes/ConfiguracoesConversasTagsPage.vue')
|
|
},
|
|
{
|
|
path: 'conversas-autoreply',
|
|
name: 'ConfiguracoesConversasAutoreply',
|
|
component: () => import('@/layout/configuracoes/ConfiguracoesConversasAutoreplyPage.vue')
|
|
},
|
|
{
|
|
path: 'conversas-optouts',
|
|
name: 'ConfiguracoesConversasOptouts',
|
|
component: () => import('@/layout/configuracoes/ConfiguracoesConversasOptoutsPage.vue')
|
|
},
|
|
{
|
|
path: 'conversas-sla',
|
|
name: 'ConfiguracoesConversasSla',
|
|
component: () => import('@/layout/configuracoes/ConfiguracoesConversasSlaPage.vue')
|
|
},
|
|
{
|
|
path: 'lembretes-sessao',
|
|
name: 'ConfiguracoesLembretesSessao',
|
|
component: () => import('@/layout/configuracoes/ConfiguracoesLembretesSessaoPage.vue')
|
|
},
|
|
{
|
|
path: 'creditos-whatsapp',
|
|
name: 'ConfiguracoesCreditosWhatsapp',
|
|
component: () => import('@/layout/configuracoes/ConfiguracoesCreditosWhatsappPage.vue')
|
|
}
|
|
]
|
|
};
|