417 lines
15 KiB
TypeScript
417 lines
15 KiB
TypeScript
import React, { useState, useEffect } from "react";
|
|
import { useNavigate } from "react-router-dom";
|
|
import { Button } from "../components/Button";
|
|
import { Input } from "../components/Input";
|
|
import { useAuth } from "../contexts/AuthContext";
|
|
import { getCompanies } from "../services/apiService";
|
|
import { formatPhone, formatCPFCNPJ, formatCEP } from "../utils/masks";
|
|
|
|
interface RegisterProps {
|
|
onNavigate: (page: string) => void;
|
|
}
|
|
|
|
export const Register: React.FC<RegisterProps> = ({ onNavigate }) => {
|
|
const navigate = useNavigate();
|
|
const { register } = useAuth();
|
|
const [companies, setCompanies] = useState<
|
|
Array<{ id: string; nome: string }>
|
|
>([]);
|
|
const [isLoadingCompanies, setIsLoadingCompanies] = useState(true);
|
|
const [formData, setFormData] = useState({
|
|
name: "",
|
|
email: "",
|
|
phone: "",
|
|
password: "",
|
|
confirmPassword: "",
|
|
empresaId: "",
|
|
cpfCnpj: "",
|
|
cep: "",
|
|
endereco: "",
|
|
numero: "",
|
|
complemento: "",
|
|
bairro: "",
|
|
cidade: "",
|
|
estado: "",
|
|
});
|
|
const [agreedToTerms, setAgreedToTerms] = useState(false);
|
|
const [isLoading, setIsLoading] = useState(false);
|
|
const [error, setError] = useState("");
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
const loadCompanies = async () => {
|
|
setIsLoadingCompanies(true);
|
|
const result = await getCompanies();
|
|
if (result.data) {
|
|
setCompanies(result.data);
|
|
}
|
|
setIsLoadingCompanies(false);
|
|
};
|
|
loadCompanies();
|
|
}, []);
|
|
|
|
// Verifica se tem empresa pré-selecionada via URL (código de acesso)
|
|
useEffect(() => {
|
|
const urlParams = new URLSearchParams(window.location.search);
|
|
const empresaId = urlParams.get('empresa_id');
|
|
const empresaNome = urlParams.get('empresa_nome');
|
|
|
|
if (empresaId) {
|
|
setFormData(prev => ({ ...prev, empresaId }));
|
|
// Limpa os parâmetros da URL após usar
|
|
window.history.replaceState({}, document.title, window.location.pathname);
|
|
}
|
|
}, [companies]);
|
|
|
|
const handleChange = (field: string, value: string | boolean) => {
|
|
setFormData((prev) => ({ ...prev, [field]: value }));
|
|
setError("");
|
|
};
|
|
|
|
const handleSubmit = async (e: React.FormEvent) => {
|
|
e.preventDefault();
|
|
setIsLoading(true);
|
|
setError("");
|
|
|
|
// Validação do checkbox de termos
|
|
if (!agreedToTerms) {
|
|
setError("Você precisa concordar com os termos de uso para continuar");
|
|
setIsLoading(false);
|
|
return;
|
|
}
|
|
|
|
// Validações
|
|
if (formData.password !== formData.confirmPassword) {
|
|
setError("As senhas não coincidem");
|
|
setIsLoading(false);
|
|
return;
|
|
}
|
|
|
|
if (formData.password.length < 6) {
|
|
setError("A senha deve ter no mínimo 6 caracteres");
|
|
setIsLoading(false);
|
|
return;
|
|
}
|
|
|
|
try {
|
|
await register({
|
|
name: formData.name,
|
|
email: formData.email,
|
|
password: formData.password,
|
|
phone: formData.phone,
|
|
role: "EVENT_OWNER", // Client Role
|
|
company_id: formData.empresaId,
|
|
cpf_cnpj: formData.cpfCnpj,
|
|
cep: formData.cep,
|
|
endereco: formData.endereco,
|
|
numero: formData.numero,
|
|
complemento: formData.complemento,
|
|
bairro: formData.bairro,
|
|
cidade: formData.cidade,
|
|
|
|
estado: formData.estado,
|
|
}, false, true); // autoLogin=false, skipLoading=true
|
|
|
|
// Keep loading true while redirecting
|
|
navigate("/cadastro-sucesso", { replace: true });
|
|
} catch (err: any) {
|
|
setIsLoading(false);
|
|
setError(err.message || "Erro ao realizar cadastro");
|
|
}
|
|
};
|
|
|
|
|
|
return (
|
|
<div className="min-h-screen flex items-center justify-center bg-gradient-to-br from-gray-50 to-white p-4 py-8 pt-24">
|
|
<div className="w-full max-w-md fade-in relative z-10">
|
|
<div className="bg-white rounded-2xl shadow-xl border border-gray-100 p-6 sm:p-8">
|
|
{/* Logo dentro do card */}
|
|
<div className="flex justify-center mb-4">
|
|
<img
|
|
src="/logo.png"
|
|
alt="Photum Formaturas"
|
|
className="h-18 sm:h-30 w-auto max-w-[200px] object-contain"
|
|
/>
|
|
</div>
|
|
|
|
<div className="text-center">
|
|
<span
|
|
className="font-bold tracking-widest uppercase text-[10px] sm:text-xs"
|
|
style={{ color: "#B9CF33" }}
|
|
>
|
|
Comece agora
|
|
</span>
|
|
<h2 className="mt-1.5 sm:mt-2 text-xl sm:text-2xl md:text-3xl font-serif font-bold text-gray-900">
|
|
Crie sua conta
|
|
</h2>
|
|
<p className="mt-1.5 sm:mt-2 text-xs sm:text-sm text-gray-600">
|
|
Já tem uma conta?{" "}
|
|
<button
|
|
onClick={() => onNavigate("entrar")}
|
|
className="font-medium hover:opacity-80 transition-opacity"
|
|
style={{ color: "#B9CF33" }}
|
|
>
|
|
Faça login
|
|
</button>
|
|
</p>
|
|
</div>
|
|
|
|
<form
|
|
className="mt-5 sm:mt-6 space-y-3 sm:space-y-4"
|
|
onSubmit={handleSubmit}
|
|
>
|
|
<div className="flex items-start bg-blue-50 border border-blue-200 rounded-lg p-2.5 sm:p-3 md:p-4 mb-3 sm:mb-4">
|
|
<div className="flex-1">
|
|
<p className="text-xs sm:text-sm text-gray-700">
|
|
<span className="font-medium text-xs sm:text-sm">
|
|
Você é um profissional?
|
|
</span>
|
|
</p>
|
|
<button
|
|
type="button"
|
|
onClick={() => onNavigate("cadastro-profissional")}
|
|
className="text-xs sm:text-sm mt-1 hover:opacity-80 transition-opacity underline font-medium"
|
|
style={{ color: "#B9CF33" }}
|
|
>
|
|
Clique aqui para se cadastrar como profissional
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="space-y-3">
|
|
<Input
|
|
label="Nome Completo"
|
|
type="text"
|
|
required
|
|
placeholder="João Silva"
|
|
value={formData.name}
|
|
onChange={(e) => handleChange("name", e.target.value)}
|
|
/>
|
|
|
|
<Input
|
|
label="E-mail"
|
|
type="email"
|
|
required
|
|
placeholder="nome@exemplo.com"
|
|
value={formData.email}
|
|
onChange={(e) => handleChange("email", e.target.value)}
|
|
/>
|
|
|
|
<Input
|
|
label="Telefone"
|
|
type="tel"
|
|
required
|
|
placeholder="(00) 00000-0000"
|
|
value={formData.phone}
|
|
onChange={(e) => handleChange("phone", formatPhone(e.target.value))}
|
|
mask="phone"
|
|
/>
|
|
|
|
<Input
|
|
label="CPF/CNPJ"
|
|
value={formData.cpfCnpj}
|
|
onChange={(e) => handleChange("cpfCnpj", formatCPFCNPJ(e.target.value))}
|
|
placeholder="000.000.000-00"
|
|
maxLength={18}
|
|
/>
|
|
|
|
<div className="grid grid-cols-2 gap-3">
|
|
<Input
|
|
label="CEP"
|
|
value={formData.cep}
|
|
onChange={(e) => handleChange("cep", formatCEP(e.target.value))}
|
|
placeholder="00000-000"
|
|
onBlur={(e) => {
|
|
const cep = e.target.value.replace(/\D/g, '');
|
|
if (cep.length === 8) {
|
|
fetch(`https://viacep.com.br/ws/${cep}/json/`)
|
|
.then(res => res.json())
|
|
.then(data => {
|
|
if (!data.erro) {
|
|
setFormData(prev => ({
|
|
...prev,
|
|
endereco: data.logradouro,
|
|
bairro: data.bairro,
|
|
cidade: data.localidade,
|
|
estado: data.uf
|
|
}));
|
|
}
|
|
});
|
|
}
|
|
}}
|
|
/>
|
|
<Input
|
|
label="Número"
|
|
value={formData.numero}
|
|
onChange={(e) => handleChange("numero", e.target.value)}
|
|
/>
|
|
</div>
|
|
|
|
<Input
|
|
label="Endereço"
|
|
value={formData.endereco}
|
|
onChange={(e) => handleChange("endereco", e.target.value)}
|
|
/>
|
|
|
|
<div className="grid grid-cols-2 gap-3">
|
|
<Input
|
|
label="Complemento"
|
|
value={formData.complemento}
|
|
onChange={(e) => handleChange("complemento", e.target.value)}
|
|
/>
|
|
<Input
|
|
label="Bairro"
|
|
value={formData.bairro}
|
|
onChange={(e) => handleChange("bairro", e.target.value)}
|
|
/>
|
|
</div>
|
|
|
|
<div className="grid grid-cols-[2fr_1fr] gap-3">
|
|
<Input
|
|
label="Cidade"
|
|
value={formData.cidade}
|
|
onChange={(e) => handleChange("cidade", e.target.value)}
|
|
/>
|
|
<Input
|
|
label="UF"
|
|
value={formData.estado}
|
|
onChange={(e) => handleChange("estado", e.target.value)}
|
|
maxLength={2}
|
|
/>
|
|
</div>
|
|
|
|
<div>
|
|
<label className="block text-[10px] sm:text-xs md:text-sm font-medium text-gray-700 mb-1">
|
|
Empresa *
|
|
</label>
|
|
<p className="text-[10px] text-gray-500 mb-2 leading-tight">
|
|
Se a sua empresa não estiver listada, entre em contato com a
|
|
administração e selecione <strong>"Não Cadastrado"</strong>{" "}
|
|
abaixo.
|
|
</p>
|
|
{formData.empresaId && companies.find(c => c.id === formData.empresaId) && (
|
|
<p className="text-[10px] text-green-600 mb-2 leading-tight">
|
|
✓ Empresa pré-selecionada baseada no seu código de acesso
|
|
</p>
|
|
)}
|
|
{isLoadingCompanies ? (
|
|
<p className="text-xs sm:text-sm text-gray-500">
|
|
Carregando empresas...
|
|
</p>
|
|
) : (
|
|
<select
|
|
required
|
|
value={formData.empresaId}
|
|
onChange={(e) => handleChange("empresaId", e.target.value)}
|
|
className="w-full px-2.5 sm:px-3 md:px-4 py-1.5 sm:py-2 text-xs sm:text-sm border border-gray-300 rounded-lg focus:ring-2 focus:border-transparent"
|
|
style={{ focusRing: "2px solid #B9CF33" }}
|
|
disabled={!!formData.empresaId && !!companies.find(c => c.id === formData.empresaId)}
|
|
>
|
|
<option value="">
|
|
{formData.empresaId ? "Empresa selecionada" : "Selecione uma empresa"}
|
|
</option>
|
|
{companies
|
|
.sort((a, b) => {
|
|
const nameA = a.nome.toLowerCase();
|
|
const nameB = b.nome.toLowerCase();
|
|
if (nameA.includes("não cadastrado") || nameA.includes("nao cadastrado")) return -1;
|
|
if (nameB.includes("não cadastrado") || nameB.includes("nao cadastrado")) return 1;
|
|
return nameA.localeCompare(nameB);
|
|
})
|
|
.map((company) => (
|
|
<option key={company.id} value={company.id}>
|
|
{company.nome}
|
|
</option>
|
|
))}
|
|
</select>
|
|
)}
|
|
</div>
|
|
|
|
<Input
|
|
label="Senha"
|
|
type="password"
|
|
required
|
|
placeholder="••••••••"
|
|
value={formData.password}
|
|
onChange={(e) => handleChange("password", e.target.value)}
|
|
/>
|
|
|
|
<Input
|
|
label="Confirmar Senha"
|
|
type="password"
|
|
required
|
|
placeholder="••••••••"
|
|
value={formData.confirmPassword}
|
|
onChange={(e) =>
|
|
handleChange("confirmPassword", e.target.value)
|
|
}
|
|
error={
|
|
error &&
|
|
(error.includes("senha") || error.includes("coincidem"))
|
|
? error
|
|
: undefined
|
|
}
|
|
/>
|
|
</div>
|
|
|
|
<div>
|
|
<div className="flex items-start">
|
|
<input
|
|
type="checkbox"
|
|
checked={agreedToTerms}
|
|
onChange={(e) => setAgreedToTerms(e.target.checked)}
|
|
className="mt-0.5 sm:mt-1 h-4 w-4 flex-shrink-0 border-gray-300 rounded focus:ring-2"
|
|
style={{ accentColor: "#B9CF33" }}
|
|
/>
|
|
<label className="ml-2 text-xs sm:text-sm text-gray-600">
|
|
Concordo com os{" "}
|
|
<button
|
|
type="button"
|
|
onClick={() => onNavigate("termos")}
|
|
className="hover:opacity-80 transition-opacity underline"
|
|
style={{ color: "#B9CF33" }}
|
|
>
|
|
termos de uso
|
|
</button>{" "}
|
|
e{" "}
|
|
<button
|
|
type="button"
|
|
onClick={() => onNavigate("privacidade")}
|
|
className="hover:opacity-80 transition-opacity underline"
|
|
style={{ color: "#B9CF33" }}
|
|
>
|
|
política de privacidade
|
|
</button>
|
|
</label>
|
|
</div>
|
|
{error && error.includes("termos") && (
|
|
<span className="text-xs text-red-500 mt-1 block ml-6">
|
|
{error}
|
|
</span>
|
|
)}
|
|
</div>
|
|
|
|
{error &&
|
|
!error.includes("termos") &&
|
|
!error.includes("senha") &&
|
|
!error.includes("coincidem") && (
|
|
<div className="bg-red-50 border border-red-200 text-red-600 px-4 py-3 rounded-lg text-sm mb-4">
|
|
{error}
|
|
</div>
|
|
)}
|
|
|
|
<Button
|
|
type="submit"
|
|
className="w-full"
|
|
size="lg"
|
|
isLoading={isLoading}
|
|
>
|
|
Criar Conta
|
|
</Button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|