227 lines
8.7 KiB
TypeScript
227 lines
8.7 KiB
TypeScript
'use client';
|
|
|
|
import { useState } from 'react';
|
|
import Link from 'next/link';
|
|
import { PawPrint, Eye, EyeOff, Mail, Lock } from 'lucide-react';
|
|
import type { Metadata } from 'next';
|
|
|
|
export default function LoginPage() {
|
|
const [showPass, setShowPass] = useState(false);
|
|
const [email, setEmail] = useState('');
|
|
const [password, setPassword] = useState('');
|
|
const [loading, setLoading] = useState(false);
|
|
|
|
const handleSubmit = (e: React.FormEvent) => {
|
|
e.preventDefault();
|
|
setLoading(true);
|
|
// TODO: substituir por NextAuth signIn quando NEXTAUTH_SECRET estiver configurado
|
|
setTimeout(() => setLoading(false), 1200);
|
|
};
|
|
|
|
return (
|
|
<div
|
|
style={{
|
|
width: '100%',
|
|
maxWidth: '420px',
|
|
background: 'var(--color-surface)',
|
|
border: '1px solid var(--color-border)',
|
|
borderRadius: '20px',
|
|
padding: 'clamp(28px, 5vw, 44px)',
|
|
boxShadow: '0 8px 40px rgba(45,27,14,0.08)',
|
|
}}
|
|
>
|
|
{/* Brand mark */}
|
|
<div style={{ display: 'flex', alignItems: 'center', gap: '8px', marginBottom: '28px' }}>
|
|
<PawPrint size={20} style={{ color: 'var(--color-terra)' }} />
|
|
<span
|
|
style={{
|
|
fontFamily: 'var(--font-playfair, Georgia, serif)',
|
|
fontStyle: 'italic',
|
|
fontWeight: 700,
|
|
fontSize: '18px',
|
|
color: 'var(--color-terra)',
|
|
}}
|
|
>
|
|
PetLink
|
|
</span>
|
|
</div>
|
|
|
|
<h1
|
|
style={{
|
|
fontFamily: 'var(--font-playfair, Georgia, serif)',
|
|
fontWeight: 900,
|
|
fontSize: '28px',
|
|
color: 'var(--color-text)',
|
|
marginBottom: '6px',
|
|
lineHeight: 1.15,
|
|
}}
|
|
>
|
|
Bem-vindo de volta.
|
|
</h1>
|
|
<p style={{ fontFamily: 'var(--font-body)', fontSize: '14px', color: 'var(--color-muted)', marginBottom: '28px' }}>
|
|
Não tens conta?{' '}
|
|
<Link
|
|
href="/auth/register"
|
|
style={{ color: 'var(--color-terra)', fontWeight: 500, textDecoration: 'underline', textDecorationColor: 'transparent', transition: 'text-decoration-color 180ms ease' }}
|
|
onMouseEnter={e => ((e.target as HTMLElement).style.textDecorationColor = 'var(--color-terra)')}
|
|
onMouseLeave={e => ((e.target as HTMLElement).style.textDecorationColor = 'transparent')}
|
|
>
|
|
Criar conta grátis
|
|
</Link>
|
|
</p>
|
|
|
|
<form onSubmit={handleSubmit} noValidate style={{ display: 'flex', flexDirection: 'column', gap: '16px' }}>
|
|
{/* Email field */}
|
|
<div style={{ display: 'flex', flexDirection: 'column', gap: '6px' }}>
|
|
<label
|
|
htmlFor="login-email"
|
|
style={{ fontFamily: 'var(--font-mono, monospace)', fontSize: '11px', fontWeight: 500, letterSpacing: '0.08em', textTransform: 'uppercase', color: 'var(--color-muted)' }}
|
|
>
|
|
Email
|
|
</label>
|
|
<div style={{ position: 'relative' }}>
|
|
<Mail size={15} style={{ position: 'absolute', left: '14px', top: '50%', transform: 'translateY(-50%)', color: 'var(--color-muted)', pointerEvents: 'none' }} />
|
|
<input
|
|
id="login-email"
|
|
type="email"
|
|
value={email}
|
|
onChange={e => setEmail(e.target.value)}
|
|
placeholder="o.teu@email.pt"
|
|
required
|
|
autoComplete="email"
|
|
style={{
|
|
width: '100%',
|
|
padding: '12px 16px 12px 40px',
|
|
background: 'var(--color-bg)',
|
|
border: '1.5px solid var(--color-border)',
|
|
borderRadius: '10px',
|
|
fontFamily: 'var(--font-body)',
|
|
fontSize: '15px',
|
|
color: 'var(--color-text)',
|
|
outline: 'none',
|
|
transition: 'border-color 180ms ease',
|
|
minHeight: '48px',
|
|
}}
|
|
onFocus={e => (e.target.style.borderColor = 'var(--color-terra)')}
|
|
onBlur={e => (e.target.style.borderColor = 'var(--color-border)')}
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Password field */}
|
|
<div style={{ display: 'flex', flexDirection: 'column', gap: '6px' }}>
|
|
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
|
<label
|
|
htmlFor="login-password"
|
|
style={{ fontFamily: 'var(--font-mono, monospace)', fontSize: '11px', fontWeight: 500, letterSpacing: '0.08em', textTransform: 'uppercase', color: 'var(--color-muted)' }}
|
|
>
|
|
Palavra-passe
|
|
</label>
|
|
<Link
|
|
href="/auth/forgot-password"
|
|
style={{ fontFamily: 'var(--font-body)', fontSize: '12px', color: 'var(--color-terra)', textDecoration: 'none' }}
|
|
>
|
|
Esqueceste-a?
|
|
</Link>
|
|
</div>
|
|
<div style={{ position: 'relative' }}>
|
|
<Lock size={15} style={{ position: 'absolute', left: '14px', top: '50%', transform: 'translateY(-50%)', color: 'var(--color-muted)', pointerEvents: 'none' }} />
|
|
<input
|
|
id="login-password"
|
|
type={showPass ? 'text' : 'password'}
|
|
value={password}
|
|
onChange={e => setPassword(e.target.value)}
|
|
placeholder="••••••••"
|
|
required
|
|
autoComplete="current-password"
|
|
style={{
|
|
width: '100%',
|
|
padding: '12px 44px 12px 40px',
|
|
background: 'var(--color-bg)',
|
|
border: '1.5px solid var(--color-border)',
|
|
borderRadius: '10px',
|
|
fontFamily: 'var(--font-body)',
|
|
fontSize: '15px',
|
|
color: 'var(--color-text)',
|
|
outline: 'none',
|
|
transition: 'border-color 180ms ease',
|
|
minHeight: '48px',
|
|
}}
|
|
onFocus={e => (e.target.style.borderColor = 'var(--color-terra)')}
|
|
onBlur={e => (e.target.style.borderColor = 'var(--color-border)')}
|
|
/>
|
|
<button
|
|
type="button"
|
|
onClick={() => setShowPass(s => !s)}
|
|
style={{
|
|
position: 'absolute',
|
|
right: '12px',
|
|
top: '50%',
|
|
transform: 'translateY(-50%)',
|
|
background: 'none',
|
|
border: 'none',
|
|
cursor: 'pointer',
|
|
color: 'var(--color-muted)',
|
|
padding: '4px',
|
|
display: 'flex',
|
|
alignItems: 'center',
|
|
}}
|
|
aria-label={showPass ? 'Ocultar palavra-passe' : 'Mostrar palavra-passe'}
|
|
>
|
|
{showPass ? <EyeOff size={16} /> : <Eye size={16} />}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Submit */}
|
|
<button
|
|
id="login-submit"
|
|
type="submit"
|
|
className="btn-primary"
|
|
disabled={loading}
|
|
style={{
|
|
width: '100%',
|
|
justifyContent: 'center',
|
|
marginTop: '8px',
|
|
opacity: loading ? 0.75 : 1,
|
|
cursor: loading ? 'not-allowed' : 'pointer',
|
|
}}
|
|
aria-label="Entrar na conta PetLink"
|
|
>
|
|
{loading ? (
|
|
<span style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
|
|
<span style={{
|
|
width: '14px', height: '14px',
|
|
border: '2px solid rgba(255,255,255,0.3)',
|
|
borderTopColor: 'white',
|
|
borderRadius: '50%',
|
|
display: 'inline-block',
|
|
animation: 'spin 0.7s linear infinite',
|
|
}} />
|
|
A entrar…
|
|
</span>
|
|
) : (
|
|
'Entrar'
|
|
)}
|
|
</button>
|
|
</form>
|
|
|
|
{/* Divider */}
|
|
<div style={{ display: 'flex', alignItems: 'center', gap: '12px', margin: '20px 0' }}>
|
|
<div style={{ flex: 1, height: '1px', background: 'var(--color-border)' }} />
|
|
<span style={{ fontFamily: 'var(--font-mono, monospace)', fontSize: '10px', color: 'var(--color-muted)', letterSpacing: '0.06em', textTransform: 'uppercase' }}>ou</span>
|
|
<div style={{ flex: 1, height: '1px', background: 'var(--color-border)' }} />
|
|
</div>
|
|
|
|
<p style={{ textAlign: 'center', fontFamily: 'var(--font-body)', fontSize: '12px', color: 'var(--color-muted)', lineHeight: 1.5 }}>
|
|
Ao entrar, aceitas os nossos{' '}
|
|
<Link href="/terms" style={{ color: 'var(--color-terra)', textDecoration: 'none' }}>Termos de Utilização</Link>
|
|
{' '}e a nossa{' '}
|
|
<Link href="/privacy" style={{ color: 'var(--color-terra)', textDecoration: 'none' }}>Política de Privacidade</Link>.
|
|
</p>
|
|
|
|
<style>{`@keyframes spin { to { transform: rotate(360deg); } }`}</style>
|
|
</div>
|
|
);
|
|
}
|