Files
clube67_newwhats.local/base-scoreodonto/scoreodonto/views/DentistRegisterView.tsx
T
VPS 4 Deploy Agent 5ec6bd6354
continuous-integration/webhook Falha no deploy de clube67_newwhats.local (VPS 4)
chore(ops): migrate clube67 to newwhats.clube67.com directory
2026-05-18 03:26:41 +02:00

251 lines
16 KiB
TypeScript

import React, { useState, useEffect } from 'react';
import { Database, Lock, User, Stethoscope, Phone, ShieldCheck, Mail, ArrowRight, Loader2 } from 'lucide-react';
import { useToast } from '../contexts/ToastContext';
export const DentistRegisterView: React.FC = () => {
const [step, setStep] = useState(1);
const [loading, setLoading] = useState(false);
const toast = useToast();
// Get data from URL
const query = new URLSearchParams(window.location.hash.split('?')[1]);
const clinicaId = query.get('clinica');
const tempName = query.get('nome');
const token = query.get('token');
const [formData, setFormData] = useState({
nome: tempName || '',
email: '',
senha: '',
cro: '',
cro_uf: 'MS',
celular: '',
especialidade: ''
});
if (!token || !clinicaId) {
return (
<div className="min-h-screen flex items-center justify-center bg-gray-50 p-4">
<div className="text-center space-y-4">
<div className="w-20 h-20 bg-red-50 text-red-500 rounded-full flex items-center justify-center mx-auto shadow-sm">
<Lock size={40} />
</div>
<h2 className="text-2xl font-black text-gray-900 uppercase">LINK INVÁLIDO OU EXPIRADO</h2>
<p className="text-gray-500 max-w-sm mx-auto font-medium">Por favor, solicite um novo convite ao administrador da clínica.</p>
</div>
</div>
);
}
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setLoading(true);
try {
const res = await fetch('http://localhost:3005/api/dentistas/register', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
...formData,
id: 'd_' + Math.random().toString(36).substring(2, 9),
clinicaId
})
});
if (res.ok) {
toast.success("CADASTRO REALIZADO COM SUCESSO!");
setStep(3);
} else {
toast.error("ERRO AO REALIZAR CADASTRO.");
}
} catch (err) {
toast.error("FALHA DE CONEXÃO.");
} finally {
setLoading(false);
}
};
return (
<div className="min-h-screen bg-[#F8FAFC] flex items-center justify-center p-4">
<div className="max-w-xl w-full">
{/* Progress Header */}
<div className="mb-10 text-center space-y-2">
<div className="inline-flex items-center gap-2 px-4 py-1.5 bg-blue-50 text-blue-600 rounded-full text-[10px] font-black uppercase tracking-widest border border-blue-100 mb-4">
<ShieldCheck size={14} /> Convite de Profissional
</div>
<h1 className="text-4xl font-black text-gray-900 tracking-tighter uppercase leading-none">Complete seu Perfil</h1>
<p className="text-gray-500 font-medium">Bem-vindo ao SCOREODONTO. Você está sendo convidado para a unidade: <span className="text-blue-600 font-bold uppercase">{clinicaId}</span></p>
</div>
<div className="bg-white rounded-[2.5rem] shadow-[0_50px_100px_-20px_rgba(0,0,0,0.08)] border border-gray-100 overflow-hidden relative">
<div className="h-2 bg-gray-100 w-full">
<div
className="h-full bg-blue-600 transition-all duration-700"
style={{ width: `${(step / 3) * 100}%` }}
></div>
</div>
{step === 1 && (
<div className="p-10 animate-in fade-in slide-in-from-right-10 duration-500">
<form className="space-y-6" onSubmit={(e) => { e.preventDefault(); setStep(2); }}>
<div className="space-y-4">
<div>
<label className="text-[10px] font-black text-gray-400 uppercase tracking-widest ml-1 mb-2 block">Seu Nome Profissional</label>
<div className="relative group">
<User className="absolute left-4 top-1/2 -translate-y-1/2 text-gray-400 group-focus-within:text-blue-500 transition-colors" size={20} />
<input
required
value={formData.nome}
onChange={e => setFormData({ ...formData, nome: e.target.value.toUpperCase() })}
className="w-full bg-gray-50 border border-transparent focus:border-blue-500 focus:bg-white px-12 py-4 rounded-2xl outline-none transition-all font-bold text-gray-800 uppercase"
placeholder="NOME COMPLETO"
/>
</div>
</div>
<div className="grid grid-cols-2 gap-4">
<div>
<label className="text-[10px] font-black text-gray-400 uppercase tracking-widest ml-1 mb-2 block">Email de Acesso</label>
<div className="relative group">
<Mail className="absolute left-4 top-1/2 -translate-y-1/2 text-gray-400" size={20} />
<input
type="email"
required
value={formData.email}
onChange={e => setFormData({ ...formData, email: e.target.value })}
className="w-full bg-gray-50 border border-transparent focus:border-blue-500 focus:bg-white px-12 py-4 rounded-2xl outline-none transition-all font-bold text-gray-800"
placeholder="seu@email.com"
/>
</div>
</div>
<div>
<label className="text-[10px] font-black text-gray-400 uppercase tracking-widest ml-1 mb-2 block">Senha de Acesso</label>
<div className="relative group">
<Lock className="absolute left-4 top-1/2 -translate-y-1/2 text-gray-400" size={20} />
<input
type="password"
required
value={formData.senha}
onChange={e => setFormData({ ...formData, senha: e.target.value })}
className="w-full bg-gray-50 border border-transparent focus:border-blue-500 focus:bg-white px-12 py-4 rounded-2xl outline-none transition-all font-bold text-gray-800"
placeholder="••••••••"
/>
</div>
</div>
</div>
</div>
<button
type="submit"
className="w-full bg-gray-900 text-white py-5 rounded-2xl font-black uppercase tracking-widest hover:bg-blue-600 hover:shadow-2xl hover:shadow-blue-200 transition-all flex items-center justify-center gap-3 active:scale-95"
>
Próximo Passo <ArrowRight size={20} />
</button>
</form>
</div>
)}
{step === 2 && (
<div className="p-10 animate-in fade-in slide-in-from-right-10 duration-500">
<form className="space-y-6" onSubmit={handleSubmit}>
<div className="space-y-4">
<div className="grid grid-cols-2 gap-4">
<div>
<label className="text-[10px] font-black text-gray-400 uppercase tracking-widest ml-1 mb-2 block">Inscrição CRO</label>
<div className="relative group">
<Database className="absolute left-4 top-1/2 -translate-y-1/2 text-gray-400" size={20} />
<input
required
value={formData.cro}
onChange={e => setFormData({ ...formData, cro: e.target.value })}
className="w-full bg-gray-50 border border-transparent focus:border-blue-500 focus:bg-white px-12 py-4 rounded-2xl outline-none transition-all font-bold text-gray-800 uppercase"
placeholder="00000"
/>
</div>
</div>
<div>
<label className="text-[10px] font-black text-gray-400 uppercase tracking-widest ml-1 mb-2 block">Estado CRO</label>
<select
value={formData.cro_uf}
onChange={e => setFormData({ ...formData, cro_uf: e.target.value })}
className="w-full bg-gray-50 border border-transparent focus:border-blue-500 focus:bg-white px-6 py-4 rounded-2xl outline-none transition-all font-bold text-gray-800"
>
<option>MS</option><option>SP</option><option>RJ</option><option>MG</option><option>PR</option><option>SC</option>
</select>
</div>
</div>
<div>
<label className="text-[10px] font-black text-gray-400 uppercase tracking-widest ml-1 mb-2 block">Especialidade Principal</label>
<div className="relative group">
<Stethoscope className="absolute left-4 top-1/2 -translate-y-1/2 text-gray-400" size={20} />
<input
required
value={formData.especialidade}
onChange={e => setFormData({ ...formData, especialidade: e.target.value.toUpperCase() })}
className="w-full bg-gray-50 border border-transparent focus:border-blue-500 focus:bg-white px-12 py-4 rounded-2xl outline-none transition-all font-bold text-gray-800 uppercase"
placeholder="EX: ORTODONTIA"
/>
</div>
</div>
<div>
<label className="text-[10px] font-black text-gray-400 uppercase tracking-widest ml-1 mb-2 block">WhatsApp de Contato</label>
<div className="relative group">
<Phone className="absolute left-4 top-1/2 -translate-y-1/2 text-gray-400" size={20} />
<input
required
value={formData.celular}
onChange={e => setFormData({ ...formData, celular: e.target.value })}
className="w-full bg-gray-50 border border-transparent focus:border-blue-500 focus:bg-white px-12 py-4 rounded-2xl outline-none transition-all font-bold text-gray-800 uppercase"
placeholder="(00) 00000-0000"
/>
</div>
</div>
</div>
<div className="flex gap-4 pt-4">
<button
type="button"
onClick={() => setStep(1)}
className="flex-1 bg-gray-100 text-gray-600 py-5 rounded-2xl font-black uppercase tracking-widest hover:bg-gray-200 transition-all font-bold"
>
Voltar
</button>
<button
type="submit"
disabled={loading}
className="flex-[2] bg-blue-600 text-white py-5 rounded-2xl font-black uppercase tracking-widest hover:bg-blue-700 shadow-xl shadow-blue-200 transition-all flex items-center justify-center gap-3 disabled:opacity-50"
>
{loading ? <Loader2 className="animate-spin" /> : <>Finalizar e Entrar <ArrowRight size={20} /></>}
</button>
</div>
</form>
</div>
)}
{step === 3 && (
<div className="p-16 text-center animate-in zoom-in duration-500">
<div className="w-24 h-24 bg-green-50 text-green-500 rounded-[2rem] flex items-center justify-center mx-auto mb-8 shadow-inner ring-1 ring-green-100">
<ShieldCheck size={48} />
</div>
<h2 className="text-3xl font-black text-gray-900 uppercase tracking-tight mb-4">Tudo Pronto!</h2>
<p className="text-gray-500 font-medium mb-10">Seu perfil foi criado e você está vinculado à unidade <br /> <span className="text-blue-600 font-bold">{clinicaId}</span>.</p>
<button
onClick={() => window.location.hash = '/login'}
className="w-full bg-gray-900 text-white py-5 rounded-2xl font-black uppercase tracking-widest hover:bg-blue-600 shadow-2xl transition-all"
>
Ir para o Login
</button>
</div>
)}
</div>
<div className="mt-8 flex items-center justify-center gap-2 text-gray-400 text-[10px] font-black uppercase tracking-widest">
<ShieldCheck size={14} /> Ambiente Seguro e Auditorado por Administradores
</div>
</div>
</div>
);
};