Files
aggios.app/front-end-dash.aggios.app/app/cadastro/[slug]/page.tsx

268 lines
10 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"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">
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>
);
}