Prepara versao dev 1.0
This commit is contained in:
@@ -1,12 +1,7 @@
|
||||
'use client';
|
||||
|
||||
import { ThemeProvider } from '@/contexts/ThemeContext';
|
||||
import { ReactNode } from 'react';
|
||||
|
||||
export default function AuthLayoutWrapper({ children }: { children: ReactNode }) {
|
||||
return (
|
||||
<ThemeProvider>
|
||||
{children}
|
||||
</ThemeProvider>
|
||||
);
|
||||
return <>{children}</>;
|
||||
}
|
||||
|
||||
@@ -38,19 +38,6 @@ export default function CadastroPage() {
|
||||
|
||||
// Carregar dados do localStorage ao montar
|
||||
useEffect(() => {
|
||||
// Mostrar dica de atalho
|
||||
setTimeout(() => {
|
||||
toast('💡 Dica: Pressione a tecla T para preencher dados de teste automaticamente!', {
|
||||
duration: 5000,
|
||||
icon: '⚡',
|
||||
style: {
|
||||
background: '#FFA500',
|
||||
color: '#fff',
|
||||
fontWeight: 'bold',
|
||||
},
|
||||
});
|
||||
}, 1000);
|
||||
|
||||
const saved = localStorage.getItem('cadastroFormData');
|
||||
if (saved) {
|
||||
try {
|
||||
@@ -94,20 +81,6 @@ export default function CadastroPage() {
|
||||
localStorage.setItem('cadastroFormData', JSON.stringify(dataToSave));
|
||||
}, [currentStep, completedSteps, formData, contacts, password, passwordStrength, cnpjData, cepData, subdomain, domainAvailable, primaryColor, secondaryColor, logoUrl]);
|
||||
|
||||
// ATALHO DE TECLADO - Pressione T para preencher dados de teste
|
||||
useEffect(() => {
|
||||
const handleKeyPress = (e: KeyboardEvent) => {
|
||||
if (e.key === 't' || e.key === 'T') {
|
||||
if (confirm('🚀 PREENCHER DADOS DE TESTE?\n\nIsso vai preencher todos os campos automaticamente e ir pro Step 5.\n\nClique OK para continuar.')) {
|
||||
fillTestData();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener('keydown', handleKeyPress);
|
||||
return () => window.removeEventListener('keydown', handleKeyPress);
|
||||
}, []);
|
||||
|
||||
// Função para atualizar formData
|
||||
const updateFormData = (name: string, value: any) => {
|
||||
setFormData(prev => ({ ...prev, [name]: value }));
|
||||
@@ -323,48 +296,48 @@ export default function CadastroPage() {
|
||||
const handleSubmitRegistration = async () => {
|
||||
try {
|
||||
const payload = {
|
||||
// Step 1 - Dados Pessoais
|
||||
email: formData.email,
|
||||
password: password,
|
||||
fullName: formData.fullName,
|
||||
newsletter: formData.newsletter || false,
|
||||
|
||||
// Step 2 - Empresa
|
||||
companyName: formData.companyName,
|
||||
// Dados da agência
|
||||
agencyName: formData.companyName,
|
||||
subdomain: subdomain,
|
||||
cnpj: formData.cnpj,
|
||||
razaoSocial: cnpjData.razaoSocial,
|
||||
razaoSocial: formData.razaoSocial,
|
||||
description: formData.description,
|
||||
website: formData.website,
|
||||
industry: formData.industry,
|
||||
teamSize: formData.teamSize,
|
||||
|
||||
// Step 3 - Localização e Contato
|
||||
// Endereço
|
||||
cep: formData.cep,
|
||||
state: cepData.state,
|
||||
city: cepData.city,
|
||||
neighborhood: cepData.neighborhood,
|
||||
street: cepData.street,
|
||||
state: formData.state,
|
||||
city: formData.city,
|
||||
neighborhood: formData.neighborhood,
|
||||
street: formData.street,
|
||||
number: formData.number,
|
||||
complement: formData.complement,
|
||||
contacts: contacts,
|
||||
|
||||
// Step 4 - Domínio
|
||||
subdomain: subdomain,
|
||||
|
||||
// Step 5 - Personalização
|
||||
primaryColor: primaryColor,
|
||||
secondaryColor: secondaryColor,
|
||||
logoUrl: logoUrl,
|
||||
// Admin
|
||||
adminEmail: formData.email,
|
||||
adminPassword: password,
|
||||
adminName: formData.fullName,
|
||||
};
|
||||
|
||||
console.log('📤 Enviando cadastro completo:', payload);
|
||||
toast.loading('Criando sua conta...', { id: 'register' });
|
||||
|
||||
const data = await apiRequest(API_ENDPOINTS.register, {
|
||||
const response = await fetch('/api/admin/agencies', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(payload),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const error = await response.json();
|
||||
throw new Error(error.message || 'Erro ao criar conta');
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
console.log('📥 Resposta data:', data);
|
||||
|
||||
// Salvar autenticação
|
||||
@@ -373,6 +346,7 @@ export default function CadastroPage() {
|
||||
id: data.id,
|
||||
email: data.email,
|
||||
name: data.name,
|
||||
role: data.role || 'ADMIN_AGENCIA',
|
||||
tenantId: data.tenantId,
|
||||
company: data.company,
|
||||
subdomain: data.subdomain
|
||||
@@ -382,7 +356,7 @@ export default function CadastroPage() {
|
||||
// Sucesso - limpar localStorage do form
|
||||
localStorage.removeItem('cadastroFormData');
|
||||
|
||||
toast.success('Conta criada com sucesso! Redirecionando para o painel...', {
|
||||
toast.success('Conta criada com sucesso! Redirecionando para seu painel...', {
|
||||
id: 'register',
|
||||
duration: 2000,
|
||||
style: {
|
||||
@@ -391,9 +365,10 @@ export default function CadastroPage() {
|
||||
},
|
||||
});
|
||||
|
||||
// Aguardar 2 segundos e redirecionar para o painel
|
||||
// Redirecionar para o painel da agência no subdomínio
|
||||
setTimeout(() => {
|
||||
window.location.href = '/painel';
|
||||
const agencyUrl = `http://${data.subdomain}.localhost/login`;
|
||||
window.location.href = agencyUrl;
|
||||
}, 2000);
|
||||
|
||||
} catch (error: any) {
|
||||
@@ -702,35 +677,12 @@ export default function CadastroPage() {
|
||||
{currentStepData?.description}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* BOTÃO TESTE RÁPIDO - GRANDE E VISÍVEL */}
|
||||
<button
|
||||
type="button"
|
||||
onClick={fillTestData}
|
||||
className="px-8 py-4 text-xl font-bold text-white bg-yellow-500 hover:bg-yellow-600 rounded-lg shadow-2xl border-4 border-yellow-700 animate-pulse"
|
||||
style={{ minWidth: '250px' }}
|
||||
>
|
||||
⚡ TESTE RÁPIDO ⚡
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Formulário */}
|
||||
<div className="flex-1 overflow-y-auto bg-[#FDFDFC] px-6 sm:px-12 py-6">
|
||||
<div className="max-w-2xl mx-auto">
|
||||
{/* Botão Teste Rápido GRANDE */}
|
||||
<div className="mb-6 p-4 bg-yellow-50 border-2 border-yellow-400 rounded-lg">
|
||||
<button
|
||||
type="button"
|
||||
onClick={fillTestData}
|
||||
className="w-full px-6 py-4 text-lg font-bold text-white bg-gradient-to-r from-[#FF3A05] to-[#FF0080] rounded-lg hover:opacity-90 transition-opacity shadow-lg"
|
||||
>
|
||||
⚡ CLIQUE AQUI - PREENCHER DADOS DE TESTE AUTOMATICAMENTE
|
||||
</button>
|
||||
<p className="text-sm text-yellow-800 mt-2 text-center">
|
||||
Preenche todos os campos e vai direto pro Step 5 para você só clicar em Finalizar
|
||||
</p>
|
||||
</div>
|
||||
<form onSubmit={(e) => { e.preventDefault(); handleNext(e); }} className="space-y-6">
|
||||
{currentStep === 1 && (
|
||||
<div className="space-y-5">
|
||||
|
||||
@@ -1,17 +1,13 @@
|
||||
"use client";
|
||||
|
||||
import { ThemeProvider } from '@/contexts/ThemeContext';
|
||||
|
||||
export default function LoginLayout({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<ThemeProvider>
|
||||
<div className="min-h-screen bg-[#FDFDFC] dark:bg-gray-900">
|
||||
{children}
|
||||
</div>
|
||||
</ThemeProvider>
|
||||
<div className="min-h-screen bg-[#FDFDFC] dark:bg-gray-900">
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,275 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import Link from "next/link";
|
||||
import { Button, Input, Checkbox } from "@/components/ui";
|
||||
import toast, { Toaster } from 'react-hot-toast';
|
||||
import { saveAuth } from '@/lib/auth';
|
||||
import { API_ENDPOINTS, apiRequest } from '@/lib/api';
|
||||
import dynamic from 'next/dynamic';
|
||||
|
||||
const ThemeToggle = dynamic(() => import('@/components/ThemeToggle'), { ssr: false });
|
||||
|
||||
export default function LoginPage() {
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [formData, setFormData] = useState({
|
||||
email: "",
|
||||
password: "",
|
||||
rememberMe: false,
|
||||
});
|
||||
|
||||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
|
||||
// Validações básicas
|
||||
if (!formData.email) {
|
||||
toast.error('Por favor, insira seu email');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(formData.email)) {
|
||||
toast.error('Por favor, insira um email válido');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!formData.password) {
|
||||
toast.error('Por favor, insira sua senha');
|
||||
return;
|
||||
}
|
||||
|
||||
setIsLoading(true);
|
||||
|
||||
try {
|
||||
const response = await fetch('http://localhost:3000/api/auth/login', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
email: formData.email,
|
||||
password: formData.password,
|
||||
}),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const error = await response.json();
|
||||
throw new Error(error.message || 'Credenciais inválidas');
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
// Salvar token e dados do usuário
|
||||
localStorage.setItem('token', data.token);
|
||||
localStorage.setItem('user', JSON.stringify(data.user));
|
||||
|
||||
toast.success('Login realizado com sucesso! Redirecionando...');
|
||||
|
||||
setTimeout(() => {
|
||||
window.location.href = '/painel';
|
||||
}, 1000);
|
||||
} catch (error: any) {
|
||||
toast.error(error.message || 'Erro ao fazer login. Verifique suas credenciais.');
|
||||
setIsLoading(false);
|
||||
}
|
||||
}; return (
|
||||
<>
|
||||
<Toaster
|
||||
position="top-center"
|
||||
toastOptions={{
|
||||
duration: 5000,
|
||||
style: {
|
||||
background: '#FFFFFF',
|
||||
color: '#000000',
|
||||
padding: '16px',
|
||||
borderRadius: '8px',
|
||||
border: '1px solid #E5E5E5',
|
||||
boxShadow: '0 4px 12px rgba(0, 0, 0, 0.15)',
|
||||
},
|
||||
error: {
|
||||
icon: '⚠️',
|
||||
style: {
|
||||
background: '#ff3a05',
|
||||
color: '#FFFFFF',
|
||||
border: 'none',
|
||||
},
|
||||
},
|
||||
success: {
|
||||
icon: '✓',
|
||||
style: {
|
||||
background: '#10B981',
|
||||
color: '#FFFFFF',
|
||||
border: 'none',
|
||||
},
|
||||
},
|
||||
}}
|
||||
/>
|
||||
<div className="flex min-h-screen">
|
||||
{/* Lado Esquerdo - Formulário */}
|
||||
<div className="w-full lg:w-1/2 flex items-center justify-center px-6 sm:px-12 py-12">
|
||||
<div className="w-full max-w-md">
|
||||
{/* Logo mobile */}
|
||||
<div className="lg:hidden text-center mb-8">
|
||||
<div className="inline-block px-6 py-3 rounded-2xl bg-linear-to-r from-[#FF3A05] to-[#FF0080]">
|
||||
<h1 className="text-3xl font-bold text-white">aggios</h1>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Theme Toggle */}
|
||||
<div className="flex justify-end mb-4">
|
||||
<ThemeToggle />
|
||||
</div>
|
||||
|
||||
{/* Header */}
|
||||
<div className="mb-8">
|
||||
<h2 className="text-[28px] font-bold text-[#000000] dark:text-white">
|
||||
Bem-vindo de volta
|
||||
</h2>
|
||||
<p className="text-[14px] text-[#7D7D7D] dark:text-gray-400 mt-2">
|
||||
Entre com suas credenciais para acessar o painel
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Form */}
|
||||
<form onSubmit={handleSubmit} className="space-y-5">
|
||||
<Input
|
||||
label="Email"
|
||||
type="email"
|
||||
placeholder="seu@email.com"
|
||||
leftIcon="ri-mail-line"
|
||||
value={formData.email}
|
||||
onChange={(e) => setFormData({ ...formData, email: e.target.value })}
|
||||
required
|
||||
/>
|
||||
|
||||
<Input
|
||||
label="Senha"
|
||||
type="password"
|
||||
placeholder="Digite sua senha"
|
||||
leftIcon="ri-lock-line"
|
||||
value={formData.password}
|
||||
onChange={(e) =>
|
||||
setFormData({ ...formData, password: e.target.value })
|
||||
}
|
||||
required
|
||||
/>
|
||||
|
||||
<div className="flex items-center justify-between">
|
||||
<Checkbox
|
||||
label="Lembrar de mim"
|
||||
checked={formData.rememberMe}
|
||||
onChange={(e) =>
|
||||
setFormData({ ...formData, rememberMe: e.target.checked })
|
||||
}
|
||||
/>
|
||||
<Link
|
||||
href="/recuperar-senha"
|
||||
className="text-[14px] gradient-text hover:underline font-medium cursor-pointer"
|
||||
>
|
||||
Esqueceu a senha?
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
<Button
|
||||
type="submit"
|
||||
variant="primary"
|
||||
className="w-full"
|
||||
size="lg"
|
||||
isLoading={isLoading}
|
||||
>
|
||||
Entrar
|
||||
</Button>
|
||||
</form>
|
||||
|
||||
{/* Divider */}
|
||||
<div className="relative my-8">
|
||||
<div className="absolute inset-0 flex items-center">
|
||||
<div className="w-full border-t border-[#E5E5E5]" />
|
||||
</div>
|
||||
<div className="relative flex justify-center text-[13px]">
|
||||
<span className="px-4 bg-white text-[#7D7D7D]">
|
||||
ou continue com
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Social Login */}
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<Button variant="outline" leftIcon="ri-google-fill">
|
||||
Google
|
||||
</Button>
|
||||
<Button variant="outline" leftIcon="ri-microsoft-fill">
|
||||
Microsoft
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* Sign up link */}
|
||||
<p className="text-center mt-8 text-[14px] text-[#7D7D7D]">
|
||||
Não tem uma conta?{" "}
|
||||
<Link href="/cadastro" className="gradient-text font-medium hover:underline cursor-pointer">
|
||||
Criar conta
|
||||
</Link>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Lado Direito - Branding */}
|
||||
<div className="hidden lg:flex lg:w-1/2 relative overflow-hidden" style={{ background: 'linear-gradient(90deg, #FF3A05, #FF0080)' }}>
|
||||
<div className="relative z-10 flex flex-col justify-center items-center w-full p-12 text-white">
|
||||
{/* Logo */}
|
||||
<div className="mb-8">
|
||||
<div className="inline-block px-6 py-3 rounded-2xl bg-white/10 backdrop-blur-sm border border-white/20">
|
||||
<h1 className="text-5xl font-bold tracking-tight bg-linear-to-r from-white to-white/80 bg-clip-text text-transparent">
|
||||
aggios
|
||||
</h1>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Conteúdo */}
|
||||
<div className="max-w-lg text-center">
|
||||
<div className="w-20 h-20 rounded-2xl bg-white/20 flex items-center justify-center mb-6 mx-auto">
|
||||
<i className="ri-dashboard-line text-4xl" />
|
||||
</div>
|
||||
<h2 className="text-4xl font-bold mb-4">Gerencie seus projetos com facilidade</h2>
|
||||
<p className="text-white/80 text-lg mb-8">
|
||||
Tenha controle total sobre seus projetos, equipe e clientes em um só lugar.
|
||||
</p>
|
||||
|
||||
{/* Features */}
|
||||
<div className="space-y-4 text-left">
|
||||
<div className="flex items-start gap-3">
|
||||
<div className="w-6 h-6 rounded-full bg-white/20 flex items-center justify-center shrink-0 mt-0.5">
|
||||
<i className="ri-shield-check-line text-sm" />
|
||||
</div>
|
||||
<div>
|
||||
<h4 className="font-semibold mb-1">Gestão Completa</h4>
|
||||
<p className="text-white/70 text-sm">Controle projetos, tarefas e prazos em tempo real</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-start gap-3">
|
||||
<div className="w-6 h-6 rounded-full bg-white/20 flex items-center justify-center shrink-0 mt-0.5">
|
||||
<i className="ri-check-line text-sm" />
|
||||
</div>
|
||||
<div>
|
||||
<h4 className="font-semibold mb-1">Relatórios Detalhados</h4>
|
||||
<p className="text-white/70 text-sm">Análises e métricas para tomada de decisão</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-start gap-3">
|
||||
<div className="w-6 h-6 rounded-full bg-white/20 flex items-center justify-center shrink-0 mt-0.5">
|
||||
<i className="ri-check-line text-sm" />
|
||||
</div>
|
||||
<div>
|
||||
<h4 className="font-semibold mb-1">Colaboração em Equipe</h4>
|
||||
<p className="text-white/70 text-sm">Trabalhe junto com sua equipe de forma eficiente</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Círculos decorativos */}
|
||||
<div className="absolute top-0 right-0 w-96 h-96 bg-white/5 rounded-full blur-3xl" />
|
||||
<div className="absolute bottom-0 left-0 w-96 h-96 bg-white/5 rounded-full blur-3xl" />
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -77,7 +77,7 @@ export default function RecuperarSenhaPage() {
|
||||
<div className="w-full max-w-md">
|
||||
{/* Logo mobile */}
|
||||
<div className="lg:hidden text-center mb-8">
|
||||
<div className="inline-block px-6 py-3 rounded-2xl bg-linear-to-r from-[#FF3A05] to-[#FF0080]">
|
||||
<div className="inline-block px-6 py-3 rounded-2xl" style={{ background: 'var(--gradient-primary)' }}>
|
||||
<h1 className="text-3xl font-bold text-white">aggios</h1>
|
||||
</div>
|
||||
</div>
|
||||
@@ -86,10 +86,10 @@ export default function RecuperarSenhaPage() {
|
||||
<>
|
||||
{/* Header */}
|
||||
<div className="mb-8">
|
||||
<h2 className="text-[28px] font-bold text-[#000000]">
|
||||
Recuperar senha
|
||||
<h2 className="text-[28px] font-bold text-zinc-900 dark:text-white">
|
||||
Recuperar Senha
|
||||
</h2>
|
||||
<p className="text-[14px] text-[#7D7D7D] mt-2">
|
||||
<p className="text-[14px] text-zinc-600 dark:text-zinc-400 mt-2">
|
||||
Digite seu email e enviaremos um link para redefinir sua senha
|
||||
</p>
|
||||
</div>
|
||||
@@ -136,15 +136,15 @@ export default function RecuperarSenhaPage() {
|
||||
<i className="ri-mail-check-line text-4xl text-[#10B981]" />
|
||||
</div>
|
||||
|
||||
<h2 className="text-[28px] font-bold text-[#000000] mb-4">
|
||||
<h2 className="text-[28px] font-bold text-zinc-900 dark:text-white mb-4">
|
||||
Email enviado!
|
||||
</h2>
|
||||
|
||||
<p className="text-[14px] text-[#7D7D7D] mb-2">
|
||||
<p className="text-[14px] text-zinc-600 dark:text-zinc-400 mb-2">
|
||||
Enviamos um link de recuperação para:
|
||||
</p>
|
||||
|
||||
<p className="text-[16px] font-semibold text-[#000000] mb-6">
|
||||
<p className="text-[16px] font-semibold text-zinc-900 dark:text-white mb-6">
|
||||
{email}
|
||||
</p>
|
||||
|
||||
@@ -152,10 +152,10 @@ export default function RecuperarSenhaPage() {
|
||||
<div className="flex gap-4">
|
||||
<i className="ri-information-line text-[#0EA5E9] text-xl mt-0.5" />
|
||||
<div>
|
||||
<h4 className="text-sm font-semibold text-[#000000] mb-1">
|
||||
<h4 className="text-sm font-semibold text-zinc-900 dark:text-white mb-1">
|
||||
Verifique sua caixa de entrada
|
||||
</h4>
|
||||
<p className="text-xs text-[#7D7D7D]">
|
||||
<p className="text-xs text-zinc-600 dark:text-zinc-400">
|
||||
Clique no link que enviamos para redefinir sua senha.
|
||||
Se não receber em alguns minutos, verifique sua pasta de spam.
|
||||
</p>
|
||||
@@ -185,12 +185,12 @@ export default function RecuperarSenhaPage() {
|
||||
</div>
|
||||
|
||||
{/* Lado Direito - Branding */}
|
||||
<div className="hidden lg:flex lg:w-1/2 relative overflow-hidden" style={{ background: 'linear-gradient(90deg, #FF3A05, #FF0080)' }}>
|
||||
<div className="hidden lg:flex lg:w-1/2 relative overflow-hidden" style={{ background: 'var(--gradient-primary)' }}>
|
||||
<div className="relative z-10 flex flex-col justify-center items-center w-full p-12 text-white">
|
||||
{/* Logo */}
|
||||
<div className="mb-8">
|
||||
<div className="inline-block px-6 py-3 rounded-2xl bg-white/10 backdrop-blur-sm border border-white/20">
|
||||
<h1 className="text-5xl font-bold tracking-tight bg-linear-to-r from-white to-white/80 bg-clip-text text-transparent">
|
||||
<h1 className="text-5xl font-bold tracking-tight text-white">
|
||||
aggios
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user