feat: site institucional completo com design system - Implementa\u00e7\u00e3o do site institucional da Aggios com design system completo incluindo gradientes, tipografia, componentes e se\u00e7\u00f5es de recursos, pre\u00e7os e CTA

This commit is contained in:
Erik Silva
2025-12-07 02:19:08 -03:00
parent 53f240a4da
commit bf6707e746
90 changed files with 25927 additions and 1 deletions

View File

@@ -0,0 +1,12 @@
'use client';
import { ThemeProvider } from '@/contexts/ThemeContext';
import { ReactNode } from 'react';
export default function AuthLayoutWrapper({ children }: { children: ReactNode }) {
return (
<ThemeProvider>
{children}
</ThemeProvider>
);
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,17 @@
"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>
);
}

View File

@@ -0,0 +1,275 @@
"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 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>
</>
);
}

View File

@@ -0,0 +1,250 @@
"use client";
import { useState } from "react";
import Link from "next/link";
import { Button, Input } from "@/components/ui";
import toast, { Toaster } from 'react-hot-toast';
export default function RecuperarSenhaPage() {
const [isLoading, setIsLoading] = useState(false);
const [email, setEmail] = useState("");
const [emailSent, setEmailSent] = useState(false);
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
// Validações básicas
if (!email) {
toast.error('Por favor, insira seu email');
return;
}
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
toast.error('Por favor, insira um email válido');
return;
}
setIsLoading(true);
try {
// Simular envio de email
await new Promise((resolve) => setTimeout(resolve, 2000));
setEmailSent(true);
toast.success('Email de recuperação enviado com sucesso!');
} catch (error) {
toast.error('Erro ao enviar email. Tente novamente.');
} finally {
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>
{!emailSent ? (
<>
{/* Header */}
<div className="mb-8">
<h2 className="text-[28px] font-bold text-[#000000]">
Recuperar senha
</h2>
<p className="text-[14px] text-[#7D7D7D] mt-2">
Digite seu email e enviaremos um link para redefinir sua senha
</p>
</div>
{/* Form */}
<form onSubmit={handleSubmit} className="space-y-5">
<Input
label="Email"
type="email"
placeholder="seu@email.com"
leftIcon="ri-mail-line"
value={email}
onChange={(e) => setEmail(e.target.value)}
required
/>
<Button
type="submit"
variant="primary"
className="w-full"
size="lg"
isLoading={isLoading}
>
Enviar link de recuperação
</Button>
</form>
{/* Back to login */}
<div className="mt-6 text-center">
<Link
href="/login"
className="text-[14px] gradient-text hover:underline inline-flex items-center gap-2 font-medium cursor-pointer"
>
<i className="ri-arrow-left-line" />
Voltar para o login
</Link>
</div>
</>
) : (
<>
{/* Success Message */}
<div className="text-center">
<div className="w-20 h-20 rounded-full bg-[#10B981]/10 flex items-center justify-center mx-auto mb-6">
<i className="ri-mail-check-line text-4xl text-[#10B981]" />
</div>
<h2 className="text-[28px] font-bold text-[#000000] mb-4">
Email enviado!
</h2>
<p className="text-[14px] text-[#7D7D7D] mb-2">
Enviamos um link de recuperação para:
</p>
<p className="text-[16px] font-semibold text-[#000000] mb-6">
{email}
</p>
<div className="p-6 bg-[#F0F9FF] border border-[#BAE6FD] rounded-md text-left mb-6">
<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">
Verifique sua caixa de entrada
</h4>
<p className="text-xs text-[#7D7D7D]">
Clique no link que enviamos para redefinir sua senha.
Se não receber em alguns minutos, verifique sua pasta de spam.
</p>
</div>
</div>
</div>
<Button
variant="outline"
className="w-full mb-4"
onClick={() => setEmailSent(false)}
>
Enviar novamente
</Button>
<Link
href="/login"
className="text-[14px] gradient-text hover:underline inline-flex items-center gap-2 font-medium cursor-pointer"
>
<i className="ri-arrow-left-line" />
Voltar para o login
</Link>
</div>
</>
)}
</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-lock-password-line text-4xl" />
</div>
<h2 className="text-4xl font-bold mb-4">Recuperação segura</h2>
<p className="text-white/80 text-lg mb-8">
Protegemos seus dados com os mais altos padrões de segurança.
Seu link de recuperação é único e expira em 24 horas.
</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">Criptografia de ponta</h4>
<p className="text-white/70 text-sm">Seus dados são protegidos com tecnologia de última geraçã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-time-line text-sm" />
</div>
<div>
<h4 className="font-semibold mb-1">Link temporário</h4>
<p className="text-white/70 text-sm">O link expira em 24h para sua segurança</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-customer-service-2-line text-sm" />
</div>
<div>
<h4 className="font-semibold mb-1">Suporte disponível</h4>
<p className="text-white/70 text-sm">Nossa equipe está pronta para ajudar caso precise</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>
</>
);
}