feat: redesign superadmin agencies list, implement flat design, add date filters, and fix UI bugs
This commit is contained in:
267
front-end-dash.aggios.app/app/cadastro/[slug]/page.tsx
Normal file
267
front-end-dash.aggios.app/app/cadastro/[slug]/page.tsx
Normal file
@@ -0,0 +1,267 @@
|
||||
"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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user