import React, { useState, useEffect, useCallback } from 'react'; import { UserSearch, Stethoscope, Wrench, Sparkles, MapPin, Search, Mail, Phone, RefreshCw, Save, BadgeCheck, Inbox, Send, CheckCircle2, XCircle, CircleDollarSign, UserCircle2, X, ChevronUp, Eraser, FileText, Plus, Pencil, Trash2 } from 'lucide-react'; import { PageHeader } from '../../components/PageHeader.tsx'; import { useToast } from '../../contexts/ToastContext.tsx'; import { useConfirm } from '../../contexts/ConfirmContext.tsx'; import { HybridBackend } from '../../services/backend.ts'; import { buscarCep } from '../../services/viacep.ts'; const API_URL = (window as any).__API_URL__ || ''; const UF_LIST = ['', '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 COLOR = '#0d9488'; // teal — identidade do plugin (paleta do login) // Tipo (role) → área (slug) para popular o dropdown de especialidade pela seleção anterior. const ROLE_TO_AREA: Record = { dentista: 'odontologia', biomedico: 'biomedicina', protetico: 'protese' }; const apiFetch = (path: string, opts: RequestInit = {}) => fetch(`${API_URL}${path}`, { ...opts, headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${localStorage.getItem('SCOREODONTO_AUTH_TOKEN')}`, ...(opts.headers || {}), }, }); // ─── Types ─────────────────────────────────────────────────────────────────── interface Profissional { id: string; nome: string; email: string; celular?: string; role: 'dentista' | 'biomedico' | 'protetico'; estado: string; cidade: string; bairro: string; logradouro?: string; numero?: string; cep?: string; especialidade: string; raio_atuacao_km?: number | null; bio_profissional?: string | null; dist_km?: number | string | null; } interface Proposta { id: string; clinica_id: string; clinica_nome?: string; profissional_id: string; profissional_nome?: string; profissional_email?: string; profissional_role?: string; enviada_por_nome?: string; mensagem?: string | null; status: 'pendente' | 'aceita' | 'recusada' | 'cancelada'; created_at: string; } const ROLE_META: Record = { dentista: { label: 'Dentista', icon: Stethoscope, cls: 'bg-teal-100 text-teal-700' }, biomedico: { label: 'Biomédico(a)', icon: Sparkles, cls: 'bg-pink-100 text-pink-700' }, protetico: { label: 'Protético', icon: Wrench, cls: 'bg-violet-100 text-violet-700' }, }; const STATUS_STYLE: Record = { pendente: 'bg-amber-100 text-amber-700', aceita: 'bg-emerald-100 text-emerald-700', recusada: 'bg-red-100 text-red-600', cancelada: 'bg-gray-100 text-gray-500', }; // Modelos de proposta editáveis/persistidos por clínica (backend). // Placeholders no texto: {prof} = nome do profissional, {clinica} = unidade ativa. type CatModelo = 'dentista' | 'biomedico'; interface ModeloProposta { id: string; categoria: CatModelo; titulo: string; texto: string; ordem?: number } const CAT_META: Record = { dentista: { label: 'Dentista', icon: Stethoscope }, biomedico: { label: 'Biomédico', icon: Sparkles }, }; const inputCls = 'w-full px-3 py-2.5 bg-gray-50 border border-gray-200 rounded-xl text-xs font-bold text-gray-700 outline-none focus:border-teal-400'; const labelCls = 'text-[10px] font-black text-gray-400 uppercase tracking-widest block mb-1'; // Estilo da busca (versão nova) — rótulos title-case e campos maiores/arredondados. const inputNew = 'w-full px-4 py-3 bg-gray-50 border border-gray-200 rounded-xl text-sm font-semibold text-gray-700 outline-none focus:border-teal-400 focus:bg-white transition-colors'; const labelNew = 'text-[11px] font-bold text-gray-500 mb-1.5 block'; // ─── Modal: enviar proposta ────────────────────────────────────────────────── const PropostaModal: React.FC<{ prof: Profissional; onClose: () => void; onSent: () => void }> = ({ prof, onClose, onSent }) => { const toast = useToast(); const ws = HybridBackend.getActiveWorkspace(); const [mensagem, setMensagem] = useState(''); const [saving, setSaving] = useState(false); const [showModelos, setShowModelos] = useState(false); const [catModelo, setCatModelo] = useState(prof.role === 'biomedico' ? 'biomedico' : 'dentista'); const [modelos, setModelos] = useState([]); const [modelosLoading, setModelosLoading] = useState(false); const preencherModelo = (texto: string) => texto.replace(/\{prof\}/g, (prof.nome || '').split(' ')[0] || prof.nome) .replace(/\{clinica\}/g, ws?.nome || 'nossa clínica'); const aplicarModelo = (texto: string) => { setMensagem(preencherModelo(texto)); setShowModelos(false); }; const fetchModelos = useCallback(async () => { if (!ws?.id) return; setModelosLoading(true); try { const res = await apiFetch(`/api/propostas/modelos?clinicaId=${encodeURIComponent(ws.id)}`); setModelos(res.ok ? await res.json() : []); } catch { setModelos([]); } finally { setModelosLoading(false); } }, [ws?.id]); useEffect(() => { if (showModelos) fetchModelos(); }, [showModelos, fetchModelos]); const handleSend = async () => { if (!ws?.id) { toast.error('SELECIONE UMA CLÍNICA/CONSULTÓRIO ATIVO PARA ENVIAR PROPOSTAS.'); return; } setSaving(true); try { const res = await apiFetch('/api/propostas', { method: 'POST', body: JSON.stringify({ clinicaId: ws.id, profissionalId: prof.id, mensagem: mensagem || null }), }); const data = await res.json(); if (!res.ok) throw new Error(data.error || 'Erro ao enviar proposta.'); toast.success('PROPOSTA ENVIADA! O PROFISSIONAL SERÁ NOTIFICADO.'); onSent(); onClose(); } catch (e: any) { toast.error(e.message.toUpperCase()); } finally { setSaving(false); } }; return (

PROPOSTA PARA {prof.nome}

EM NOME DE {ws?.nome || '—'}

{showModelos && (
{(Object.keys(CAT_META) as CatModelo[]).map(c => { const M = CAT_META[c]; const Icon = M.icon; const on = catModelo === c; return ( ); })}
{modelosLoading ? (
) : modelos.filter(m => m.categoria === catModelo).length === 0 ? (

Nenhum modelo · cadastre na aba Modelos

) : modelos.filter(m => m.categoria === catModelo).map(m => ( ))}
)}