import React, { useState, useEffect, useCallback } from 'react'; import { X, Plus, Trash2, MapPin, Phone, Clock, CalendarClock, Save, Loader2, MessageSquare, AlertTriangle, CheckCircle2 } from 'lucide-react'; import { useToast } from '../contexts/ToastContext.tsx'; import { buscarCep } from '../services/viacep.ts'; import { HorariosConfig } from './HorariosConfig.tsx'; // Comunicação interna: perfil de contato de não-pacientes (dentistas/funcionários). // Números de WhatsApp múltiplos (cada um com descrição, situações e janelas de horário // permitido p/ a Secretária enviar msg — proteção contra mensagem fora do expediente), // endereço e disponibilidade de agenda (abrir/fechar + jornada). const API_URL = (window as any).__API_URL__ || ''; const COLOR = '#0d9488'; const UF = ['', 'AC', 'AL', 'AM', 'AP', 'BA', 'CE', 'DF', 'ES', 'GO', 'MA', 'MG', 'MS', 'MT', 'PA', 'PB', 'PE', 'PI', 'PR', 'RJ', 'RN', 'RO', 'RR', 'RS', 'SC', 'SE', 'SP', 'TO']; const DIAS = [{ v: 0, l: 'Dom' }, { v: 1, l: 'Seg' }, { v: 2, l: 'Ter' }, { v: 3, l: 'Qua' }, { v: 4, l: 'Qui' }, { v: 5, l: 'Sex' }, { v: 6, l: 'Sáb' }]; const api = (path: string, opts: RequestInit = {}) => fetch(`${API_URL}${path}`, { ...opts, headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${localStorage.getItem('SCOREODONTO_AUTH_TOKEN')}` }, }); interface Janela { dias: number[]; inicio: string; fim: string } interface Numero { numero: string; descricao: string; situacoes: string; janelas: Janela[] } interface Endereco { cep?: string; logradouro?: string; numero?: string; complemento?: string; bairro?: string; cidade?: string; estado?: string } interface Agenda { aberta?: boolean; horarios?: Janela[] } const input = 'w-full px-3 py-2.5 bg-gray-50 border border-gray-200 rounded-xl text-sm font-medium text-gray-700 outline-none focus:border-teal-400'; const label = 'text-[10px] font-black text-gray-400 uppercase tracking-widest block mb-1'; // ── Editor de janelas de horário (dias + faixa) — reutilizado p/ contato e agenda ── const JanelaEditor: React.FC<{ janelas: Janela[]; onChange: (j: Janela[]) => void; addLabel: string }> = ({ janelas, onChange, addLabel }) => { const upd = (i: number, patch: Partial) => onChange(janelas.map((j, k) => (k === i ? { ...j, ...patch } : j))); const toggleDia = (i: number, d: number) => { const j = janelas[i]; const has = j.dias.includes(d); upd(i, { dias: has ? j.dias.filter(x => x !== d) : [...j.dias, d].sort((a, b) => a - b) }); }; return (
{janelas.map((j, i) => (
{DIAS.map(d => { const on = j.dias.includes(d.v); return ( ); })}
Das upd(i, { inicio: e.target.value })} className="px-2 py-1.5 bg-gray-50 border border-gray-200 rounded-lg text-sm font-bold text-gray-700 outline-none focus:border-teal-400" /> até upd(i, { fim: e.target.value })} className="px-2 py-1.5 bg-gray-50 border border-gray-200 rounded-lg text-sm font-bold text-gray-700 outline-none focus:border-teal-400" />
))}
); }; export const ComunicacaoInternaModal: React.FC<{ usuarioId?: string; clinicaId?: string; nome?: string; forced?: boolean; onClose: () => void; onSaved?: (completo: boolean) => void; onSnooze?: () => void; }> = ({ usuarioId = 'me', clinicaId, nome, forced, onClose, onSaved, onSnooze }) => { const toast = useToast(); const [loading, setLoading] = useState(true); const [saving, setSaving] = useState(false); const [endereco, setEndereco] = useState({}); const [numeros, setNumeros] = useState([]); const [enderecoDaClinica, setEnderecoDaClinica] = useState(false); const [isDentista, setIsDentista] = useState(false); // só dentista precisa de jornada const [jornadaOk, setJornadaOk] = useState(false); // jornada real (dentistas_horarios) já configurada const [showHorarios, setShowHorarios] = useState(false); // abre "Horários de Funcionamento" por cima const load = useCallback(async () => { setLoading(true); try { const qs = clinicaId ? `?clinicaId=${encodeURIComponent(clinicaId)}` : ''; const r = await api(`/api/comunicacao/${usuarioId}${qs}`); const d = await r.json(); setEndereco(d.endereco || {}); setNumeros(Array.isArray(d.numeros) ? d.numeros : []); setEnderecoDaClinica(!!d.endereco_da_clinica); setIsDentista((d.role || '') === 'dentista'); setJornadaOk(!!d.jornada_configurada); } catch { /* mantém vazio */ } finally { setLoading(false); } }, [usuarioId, clinicaId]); useEffect(() => { load(); }, [load]); const setEnd = (k: keyof Endereco, v: string) => setEndereco(p => ({ ...p, [k]: v })); const onCep = async () => { const cep = (endereco.cep || '').replace(/\D/g, ''); if (cep.length !== 8) return; const e = await buscarCep(cep); if (e) setEndereco(p => ({ ...p, logradouro: e.logradouro || p.logradouro, bairro: e.bairro || p.bairro, cidade: e.cidade || p.cidade, estado: e.estado || p.estado })); }; const updNum = (i: number, patch: Partial) => setNumeros(ns => ns.map((n, k) => (k === i ? { ...n, ...patch } : n))); const addNum = () => setNumeros(ns => [...ns, { numero: '', descricao: '', situacoes: '', janelas: [{ dias: [1, 2, 3, 4, 5], inicio: '08:00', fim: '18:00' }] }]); // Notificação de número compartilhado: ao sair do campo, verifica se o número já é de // outra conta/papel da clínica (a mesma pessoa pode ser dono E dentista, por ex.). const [avisos, setAvisos] = useState>({}); const roleLabel = (r: string) => (({ dentista: 'dentista', donoclinica: 'dono', donoconsultorio: 'dono', funcionario: 'funcionário', admin: 'admin' } as Record)[r] || r || 'conta'); const checkNumero = async (i: number, numero: string) => { const tel = (numero || '').replace(/\D/g, ''); if (tel.length < 10 || !clinicaId) { setAvisos(a => ({ ...a, [i]: [] })); return; } try { const r = await api(`/api/comunicacao/numero-lookup?clinicaId=${encodeURIComponent(clinicaId)}&numero=${encodeURIComponent(tel)}`); const d = await r.json(); setAvisos(a => ({ ...a, [i]: Array.isArray(d.encontrados) ? d.encontrados : [] })); } catch { /* silencioso */ } }; // Completude (espelha o backend) — orienta o preenchimento e destrava o gate. const enderecoOk = !!(endereco.cidade && endereco.estado && (endereco.cep || endereco.logradouro)); const numerosOk = numeros.length >= 1 && numeros.every(n => (n.numero || '').replace(/\D/g, '').length >= 10 && (n.descricao || '').trim() && (n.janelas?.length ?? 0) >= 1); const jornadaFeita = !isDentista || jornadaOk; // dentista precisa da jornada real configurada const completo = enderecoOk && numerosOk && jornadaFeita; const salvar = async () => { setSaving(true); try { const r = await api(`/api/comunicacao/${usuarioId}`, { method: 'PUT', body: JSON.stringify({ clinicaId, endereco, numeros }) }); const d = await r.json(); if (!r.ok) throw new Error(d.error || 'Erro ao salvar.'); toast.success('DADOS SALVOS!'); onSaved?.(!!d.completo); if (!forced || d.completo) onClose(); else toast.error('AINDA FALTAM CAMPOS OBRIGATÓRIOS PARA LIBERAR O SISTEMA.'); } catch (e: any) { toast.error(String(e.message).toUpperCase()); } finally { setSaving(false); } }; return ( <>
{/* Header */}

Comunicação interna{nome ? ` · ${nome}` : ''}

{completo ? 'Perfil completo' : 'Preencha para liberar o sistema'}

{!forced && }
{loading ? (
) : ( <> {/* Conteúdo */}
{forced && (

Precisamos destes dados para o sistema funcionar corretamente e para a Secretária IA só contatar você nos horários certos. Mensagens fora do expediente podem gerar problemas — por isso as janelas de horário são obrigatórias.

)} {/* ── Endereço ── */}

Endereço

{enderecoDaClinica &&

Puxamos o endereço da clínica — é só conferir (ou ajustar, se o seu for diferente).

}
setEnd('cep', e.target.value)} onBlur={onCep} placeholder="00000-000" />
setEnd('logradouro', e.target.value)} placeholder="Rua / Avenida" />
setEnd('numero', e.target.value)} />
setEnd('complemento', e.target.value)} />
setEnd('bairro', e.target.value)} />
setEnd('cidade', e.target.value)} />
{/* ── Telefones / WhatsApp ── */}

Números de WhatsApp

Cadastre todos os seus números. Para cada um, descreva quando ele pode ser usado e em quais horários a Secretária pode enviar mensagem.

{numeros.map((n, i) => (
updNum(i, { numero: e.target.value })} onBlur={e => checkNumero(i, e.target.value)} placeholder="(67) 99999-9999" />
updNum(i, { descricao: e.target.value })} placeholder="Ex.: WhatsApp de trabalho" />
{(avisos[i]?.length ?? 0) > 0 && (
Este número já está cadastrado por {avisos[i].map(x => `${x.nome} (${roleLabel(x.role)})`).join(', ')}. Pode compartilhar o mesmo número — descreva abaixo as situações deste papel; a Secretária identifica pelo assunto quem deve responder.
)}