268 lines
10 KiB
TypeScript
268 lines
10 KiB
TypeScript
"use client";
|
||
|
||
import { useState, useEffect } from 'react';
|
||
import { useRouter } from 'next/navigation';
|
||
import Input from '@/components/ui/Input';
|
||
import Button from '@/components/ui/Button';
|
||
import { CheckCircleIcon } from '@heroicons/react/24/solid';
|
||
|
||
interface FormField {
|
||
name: string;
|
||
label: string;
|
||
type: string;
|
||
required: boolean;
|
||
order: number;
|
||
}
|
||
|
||
interface SignupTemplate {
|
||
id: string;
|
||
name: string;
|
||
description: string;
|
||
slug: string;
|
||
form_fields: FormField[];
|
||
enabled_modules: string[];
|
||
redirect_url?: string;
|
||
success_message?: string;
|
||
custom_logo_url?: string;
|
||
custom_primary_color?: string;
|
||
}
|
||
|
||
export default function CustomSignupPage({ params }: { params: Promise<{ slug: string }> }) {
|
||
const router = useRouter();
|
||
const [template, setTemplate] = useState<SignupTemplate | null>(null);
|
||
const [loading, setLoading] = useState(true);
|
||
const [submitting, setSubmitting] = useState(false);
|
||
const [success, setSuccess] = useState(false);
|
||
const [error, setError] = useState('');
|
||
const [formData, setFormData] = useState<Record<string, string>>({});
|
||
const [slug, setSlug] = useState<string>('');
|
||
|
||
useEffect(() => {
|
||
params.then(p => {
|
||
setSlug(p.slug);
|
||
});
|
||
}, [params]);
|
||
|
||
useEffect(() => {
|
||
if (slug) {
|
||
loadTemplate();
|
||
}
|
||
}, [slug]);
|
||
|
||
const loadTemplate = async () => {
|
||
try {
|
||
const response = await fetch(`/api/signup-templates/slug/${slug}`);
|
||
|
||
if (response.ok) {
|
||
const data = await response.json();
|
||
setTemplate(data);
|
||
|
||
// Inicializar formData com campos vazios
|
||
const initialData: Record<string, string> = {};
|
||
data.form_fields.forEach((field: FormField) => {
|
||
initialData[field.name] = '';
|
||
});
|
||
setFormData(initialData);
|
||
} else {
|
||
setError('Template de cadastro não encontrado');
|
||
}
|
||
} catch (err) {
|
||
setError('Erro ao carregar formulário de cadastro');
|
||
} finally {
|
||
setLoading(false);
|
||
}
|
||
};
|
||
|
||
const handleSubmit = async (e: React.FormEvent) => {
|
||
e.preventDefault();
|
||
setSubmitting(true);
|
||
setError('');
|
||
|
||
try {
|
||
// Registro público via template
|
||
const payload = {
|
||
template_slug: slug,
|
||
email: formData.email,
|
||
password: formData.password,
|
||
name: formData.company_name || formData.subdomain || 'Cliente',
|
||
subdomain: formData.subdomain,
|
||
company_name: formData.company_name,
|
||
...formData, // Incluir todos os campos adicionais
|
||
};
|
||
|
||
const response = await fetch('/api/signup/register', {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json',
|
||
},
|
||
body: JSON.stringify(payload),
|
||
});
|
||
|
||
if (response.ok) {
|
||
setSuccess(true);
|
||
|
||
// Redirecionar após 2 segundos
|
||
setTimeout(() => {
|
||
if (template?.redirect_url) {
|
||
window.location.href = template.redirect_url;
|
||
} else {
|
||
router.push('/login');
|
||
}
|
||
}, 2000);
|
||
} else {
|
||
const data = await response.json();
|
||
setError(data.error || 'Erro ao realizar cadastro');
|
||
}
|
||
} catch (err) {
|
||
setError('Erro ao processar cadastro');
|
||
} finally {
|
||
setSubmitting(false);
|
||
}
|
||
};
|
||
|
||
const handleInputChange = (fieldName: string, value: string) => {
|
||
setFormData(prev => ({
|
||
...prev,
|
||
[fieldName]: value,
|
||
}));
|
||
};
|
||
|
||
if (loading) {
|
||
return (
|
||
<div className="min-h-screen flex items-center justify-center bg-gray-50 dark:bg-gray-950">
|
||
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-gray-900 dark:border-white"></div>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
if (error && !template) {
|
||
return (
|
||
<div className="min-h-screen flex items-center justify-center bg-gray-50 dark:bg-gray-950 p-4">
|
||
<div className="bg-white dark:bg-gray-900 rounded-lg p-8 max-w-md w-full text-center border border-gray-200 dark:border-gray-800">
|
||
<div className="w-16 h-16 bg-red-100 dark:bg-red-900 rounded-full flex items-center justify-center mx-auto mb-4">
|
||
<span className="text-3xl">⚠️</span>
|
||
</div>
|
||
<h2 className="text-2xl font-bold text-gray-900 dark:text-white mb-2">
|
||
Link Inválido
|
||
</h2>
|
||
<p className="text-gray-600 dark:text-gray-400 mb-6">
|
||
{error}
|
||
</p>
|
||
<Button onClick={() => router.push('/')}>
|
||
Voltar para Início
|
||
</Button>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
if (success) {
|
||
return (
|
||
<div className="min-h-screen flex items-center justify-center bg-gray-50 dark:bg-gray-950 p-4">
|
||
<div className="bg-white dark:bg-gray-900 rounded-lg p-8 max-w-md w-full text-center border border-gray-200 dark:border-gray-800">
|
||
<div className="w-16 h-16 bg-green-100 dark:bg-green-900 rounded-full flex items-center justify-center mx-auto mb-4">
|
||
<CheckCircleIcon className="w-10 h-10 text-green-600 dark:text-green-400" />
|
||
</div>
|
||
<h2 className="text-2xl font-bold text-gray-900 dark:text-white mb-2">
|
||
Cadastro Realizado!
|
||
</h2>
|
||
<p className="text-gray-600 dark:text-gray-400 mb-6">
|
||
{template?.success_message || 'Seu cadastro foi realizado com sucesso. Redirecionando...'}
|
||
</p>
|
||
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-gray-900 dark:border-white mx-auto"></div>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
const sortedFields = [...(template?.form_fields || [])].sort((a, b) => a.order - b.order);
|
||
|
||
return (
|
||
<div className="min-h-screen flex items-center justify-center bg-gray-50 dark:bg-gray-950 p-4">
|
||
<div className="bg-white dark:bg-gray-900 rounded-lg p-8 max-w-md w-full border border-gray-200 dark:border-gray-800">
|
||
{/* Logo personalizado */}
|
||
{template?.custom_logo_url && (
|
||
<div className="flex justify-center mb-6">
|
||
<img
|
||
src={template.custom_logo_url}
|
||
alt="Logo"
|
||
className="h-12 object-contain"
|
||
/>
|
||
</div>
|
||
)}
|
||
|
||
{/* Cabeçalho */}
|
||
<div className="text-center mb-6">
|
||
<h1 className="text-2xl font-bold text-gray-900 dark:text-white mb-2">
|
||
{template?.name}
|
||
</h1>
|
||
{template?.description && (
|
||
<p className="text-sm text-gray-600 dark:text-gray-400">
|
||
{template.description}
|
||
</p>
|
||
)}
|
||
</div>
|
||
|
||
{/* Módulos incluídos */}
|
||
{template && template.enabled_modules.length > 0 && (
|
||
<div className="mb-6 p-4 bg-blue-50 dark:bg-blue-900/20 rounded-lg">
|
||
<p className="text-sm font-medium text-blue-900 dark:text-blue-100 mb-2">
|
||
Módulos incluídos:
|
||
</p>
|
||
<div className="flex flex-wrap gap-2">
|
||
{template.enabled_modules.map((module) => (
|
||
<span
|
||
key={module}
|
||
className="px-2 py-1 bg-blue-100 dark:bg-blue-900 text-blue-800 dark:text-blue-200 rounded text-xs font-medium"
|
||
>
|
||
{module}
|
||
</span>
|
||
))}
|
||
</div>
|
||
</div>
|
||
)}
|
||
|
||
{/* Formulário */}
|
||
<form onSubmit={handleSubmit} className="space-y-4">
|
||
{sortedFields.map((field) => (
|
||
<Input
|
||
key={field.name}
|
||
label={field.label}
|
||
type={field.type}
|
||
value={formData[field.name] || ''}
|
||
onChange={(e) => handleInputChange(field.name, e.target.value)}
|
||
required={field.required}
|
||
placeholder={`Digite ${field.label.toLowerCase()}`}
|
||
/>
|
||
))}
|
||
|
||
{error && (
|
||
<div className="p-3 bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 rounded-lg">
|
||
<p className="text-sm text-red-600 dark:text-red-400">{error}</p>
|
||
</div>
|
||
)}
|
||
|
||
<Button
|
||
type="submit"
|
||
className="w-full"
|
||
disabled={submitting}
|
||
style={template?.custom_primary_color ? {
|
||
background: template.custom_primary_color
|
||
} : undefined}
|
||
>
|
||
{submitting ? 'Cadastrando...' : 'Criar Conta'}
|
||
</Button>
|
||
</form>
|
||
|
||
{/* Link para login */}
|
||
<p className="mt-6 text-center text-sm text-gray-600 dark:text-gray-400">
|
||
Já tem uma conta?{' '}
|
||
<a href="/login" className="font-medium text-blue-600 hover:text-blue-500 dark:text-blue-400">
|
||
Fazer login
|
||
</a>
|
||
</p>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|