import React, { useState, useEffect, useRef } from 'react'; import { Building2, Briefcase, Stethoscope, Sparkles, DoorOpen, FlaskConical, ArrowRight, ArrowLeft, ChevronLeft, Loader2, Send, Check, CheckCircle2, User as UserIcon } from 'lucide-react'; const API_URL = (window as any).__API_URL__ || ''; interface Props { onDone: (result?: { noWorkspace?: boolean; userRole?: string }) => void; onBackToLogin: () => void; } // ── Carrossel de apresentação (clínicas, consultórios, dentistas, biomédicos, salas) ── const SLIDES: { icon: any; accent: string; title: string; desc: string; cta: string; pre?: Partial }[] = [ { icon: Building2, accent: '#2563eb', title: 'Clínicas', desc: 'Gestão completa: agenda, pacientes, financeiro e equipe — tudo num lugar.', cta: 'Cadastrar minha clínica', pre: { perfil: 'clinica' } }, { icon: Briefcase, accent: '#0d9488', title: 'Consultórios', desc: 'Para quem voa solo: agenda inteligente e cobrança sem complicação.', cta: 'Cadastrar meu consultório', pre: { perfil: 'consultorio' } }, { icon: Stethoscope, accent: '#7c3aed', title: 'Dentistas', desc: 'Sua agenda única — sem overbooking, mesmo atendendo em vários lugares.', cta: 'Sou dentista', pre: { perfil: 'profissional', profissao: 'dentista' } }, { icon: Sparkles, accent: '#db2777', title: 'Biomédicos', desc: 'Harmonização orofacial e estética com prontuário e fotos por procedimento.', cta: 'Sou biomédico(a)', pre: { perfil: 'profissional', profissao: 'biomedico' } }, { icon: FlaskConical, accent: '#2d6a4f', title: 'Protéticos', desc: 'Laboratório completo: bancada, ordens, etapas, componentes, financeiro e score.', cta: 'Sou protético(a)', pre: { perfil: 'profissional', profissao: 'protetico' } }, { icon: DoorOpen, accent: '#0d9488', title: 'Salas', desc: 'Alugue ou anuncie salas por hora, dia ou mês. Valor fixo ou negociado.', cta: 'Tenho salas para alugar', pre: { perfil: 'sala' } }, ]; // ── Esquema do chat ─────────────────────────────────────────────────────────── type Perfil = 'profissional' | 'clinica' | 'consultorio' | 'paciente' | 'sala'; type Profissao = 'dentista' | 'biomedico' | 'protetico'; interface Answers { perfil?: Perfil; profissao?: Profissao; especialidades?: string[]; nome?: string; nomeEstab?: string; email?: string; senha?: string; } const ESPECIALIDADES: Record = { dentista: ['Clínico Geral', 'Ortodontia', 'Implantodontia', 'Endodontia', 'Periodontia', 'Odontopediatria', 'Prótese', 'Harmonização Orofacial', 'Cirurgia', 'Estética'], biomedico: ['Harmonização Orofacial', 'Estética', 'Biomedicina Estética', 'Análises Clínicas'], protetico: ['Prótese Fixa', 'Prótese Removível', 'Prótese sobre Implante', 'CAD/CAM'], }; const STEP_ORDER = ['perfil', 'profissao', 'especialidades', 'nome', 'nomeEstab', 'email', 'senha', 'revisao'] as const; type StepKey = typeof STEP_ORDER[number]; function isRelevant(key: StepKey, a: Answers): boolean { if (key === 'profissao' || key === 'especialidades') return a.perfil === 'profissional'; if (key === 'nomeEstab') return a.perfil === 'clinica' || a.perfil === 'consultorio'; return true; } function isAnswered(key: StepKey, a: Answers): boolean { if (key === 'especialidades') return Array.isArray(a.especialidades); // pode ser [] if (key === 'revisao') return false; return (a as any)[key] !== undefined && (a as any)[key] !== ''; } const PERFIL_LABEL: Record = { profissional: 'Sou profissional', clinica: 'Tenho uma clínica', consultorio: 'Tenho um consultório', paciente: 'Sou paciente', sala: 'Tenho salas para alugar' }; const PROF_LABEL: Record = { dentista: 'Dentista', biomedico: 'Biomédico(a)', protetico: 'Protético(a)' }; const emailOk = (e: string) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(e); const OnboardingChat: React.FC = ({ onDone, onBackToLogin }) => { const [phase, setPhase] = useState<'intro' | 'chat'>('intro'); const [slide, setSlide] = useState(0); const [answers, setAnswers] = useState({}); const [text, setText] = useState(''); const [multi, setMulti] = useState([]); const [erro, setErro] = useState(''); const [loading, setLoading] = useState(false); const [done, setDone] = useState(false); const scrollRef = useRef(null); // passo atual = primeiro passo relevante ainda não respondido const current: StepKey = STEP_ORDER.find(k => isRelevant(k, answers) && !isAnswered(k, answers)) || 'revisao'; useEffect(() => { scrollRef.current?.scrollTo({ top: scrollRef.current.scrollHeight, behavior: 'smooth' }); }, [answers, current, phase, erro]); useEffect(() => { setErro(''); setText(''); setMulti(answers.especialidades || []); }, [current]); const setAns = (patch: Partial) => setAnswers(a => ({ ...a, ...patch })); // inicia o chat já com o perfil do slide preenchido (ou sem nada, para perguntar) const iniciar = (pre?: Partial) => { setAnswers(pre || {}); setPhase('chat'); }; const voltar = () => { // remove o último passo relevante já respondido const answered = STEP_ORDER.filter(k => isRelevant(k, answers) && isAnswered(k, answers)); const last = answered[answered.length - 1]; if (!last) { setPhase('intro'); return; } setAnswers(a => { const n = { ...a }; delete (n as any)[last]; return n; }); }; const finalizar = async () => { setLoading(true); setErro(''); const role = answers.perfil === 'clinica' ? 'donoclinica' : answers.perfil === 'consultorio' ? 'donoconsultorio' : answers.perfil === 'paciente' ? 'paciente' : answers.perfil === 'sala' ? 'donosala' : (answers.profissao || 'dentista'); try { const res = await fetch(`${API_URL}/api/register`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ nome: answers.nome, email: answers.email, senha: answers.senha, role, workspaceNome: answers.nomeEstab || undefined, especialidade: answers.especialidades && answers.especialidades.length ? answers.especialidades : undefined, }), }); const data = await res.json().catch(() => ({} as any)); if (!res.ok || !data.success) { setErro(data.message || 'Erro ao criar conta.'); setLoading(false); return; } localStorage.setItem('SCOREODONTO_AUTH_TOKEN', data.token); localStorage.setItem('SCOREODONTO_USER_DATA', JSON.stringify(data.user)); localStorage.setItem('SCOREODONTO_WORKSPACES', JSON.stringify(data.workspaces || [])); if (data.workspaces?.length) localStorage.setItem('SCOREODONTO_ACTIVE_WORKSPACE', JSON.stringify(data.workspaces[0])); setDone(true); setTimeout(() => onDone(data.noWorkspace ? { noWorkspace: true, userRole: data.userRole } : undefined), 1300); } catch { setErro('Erro de conexão. Tente novamente.'); setLoading(false); } }; // ── INTRO (carrossel) ───────────────────────────────────────────────────── if (phase === 'intro') { const S = SLIDES[slide]; const Icon = S.icon; return (

{S.title}

{S.desc}

{SLIDES.map((_, i) => (
{/* navegação entre os slides */}
{/* CTA do slide atual → pré-seleciona o perfil e inicia o chat */}
); } // ── CHAT ────────────────────────────────────────────────────────────────── // monta as bolhas a partir do histórico respondido const bubbles: { from: 'bot' | 'user'; text: string }[] = []; bubbles.push({ from: 'bot', text: 'Oi! Vou te ajudar a criar sua conta em 1 minuto. 😊' }); const pushQA = (botText: string, userText?: string) => { bubbles.push({ from: 'bot', text: botText }); if (userText) bubbles.push({ from: 'user', text: userText }); }; if (answers.perfil) pushQA('Para começar, o que você é?', PERFIL_LABEL[answers.perfil]); if (answers.profissao) pushQA('Qual a sua profissão?', PROF_LABEL[answers.profissao]); if (Array.isArray(answers.especialidades)) pushQA('Quais áreas você atua? (opcional)', answers.especialidades.length ? answers.especialidades.join(', ') : 'Pular'); if (answers.nome) pushQA('Como é o seu nome completo?', answers.nome); if (answers.nomeEstab) pushQA('Qual o nome do estabelecimento?', answers.nomeEstab); if (answers.email) pushQA('Seu melhor e-mail?', answers.email); if (answers.senha) pushQA('Crie uma senha (mín. 6 caracteres).', '••••••'); const stepPrompt: Record = { perfil: 'Para começar, o que você é?', profissao: 'Qual a sua profissão?', especialidades: 'Quais áreas você atua? (opcional)', nome: 'Como é o seu nome completo?', nomeEstab: answers.perfil === 'clinica' ? 'Qual o nome da clínica?' : 'Qual o nome do consultório?', email: 'Seu melhor e-mail?', senha: 'Crie uma senha (mín. 6 caracteres).', revisao: 'Tudo certo? É só confirmar para criar sua conta.', }; const Chip: React.FC<{ active?: boolean; onClick: () => void; children: React.ReactNode }> = ({ active, onClick, children }) => ( ); return (
{/* header */}
S

Criar conta

ScoreOdonto

{/* mensagens */}
{bubbles.map((b, i) => (
{b.text}
))} {!done && (
{stepPrompt[current]}
)} {done && (

Conta criada!

)} {erro &&
{erro}
}
{/* entrada por passo */} {!done && (
{current === 'perfil' && (
{(['profissional', 'clinica', 'consultorio', 'sala', 'paciente'] as Perfil[]).map(p => ( setAns({ perfil: p })}>{PERFIL_LABEL[p]} ))}
)} {current === 'profissao' && (
{(['dentista', 'biomedico', 'protetico'] as Profissao[]).map(p => ( setAns({ profissao: p })}>{PROF_LABEL[p]} ))}
)} {current === 'especialidades' && (
{ESPECIALIDADES[answers.profissao || 'dentista'].map(e => ( setMulti(m => m.includes(e) ? m.filter(x => x !== e) : [...m, e])}>{e} ))}
)} {(current === 'nome' || current === 'nomeEstab' || current === 'email' || current === 'senha') && (
{ e.preventDefault(); const v = text.trim(); if (current === 'nome') { if (v.length < 2) { setErro('Digite seu nome.'); return; } setAns({ nome: v }); } else if (current === 'nomeEstab') { if (v.length < 2) { setErro('Digite o nome.'); return; } setAns({ nomeEstab: v }); } else if (current === 'email') { if (!emailOk(v)) { setErro('E-mail inválido.'); return; } setAns({ email: v.toLowerCase() }); } else if (current === 'senha') { if (v.length < 6) { setErro('Mínimo de 6 caracteres.'); return; } setAns({ senha: v }); } }} className="flex items-center gap-2"> setText(current === 'nome' || current === 'nomeEstab' ? e.target.value.toUpperCase() : e.target.value)} placeholder={current === 'nome' ? 'Seu nome completo' : current === 'nomeEstab' ? 'Nome do estabelecimento' : current === 'email' ? 'seu@email.com' : '••••••'} className="flex-1 px-4 py-3.5 bg-gray-50 border border-gray-200 rounded-2xl text-sm font-medium outline-none focus:border-teal-500 focus:bg-white focus:ring-2 focus:ring-teal-100" />
)} {current === 'revisao' && ( )}
)}
); }; export default OnboardingChat; export { OnboardingChat };