Translate to pt, fix some ui details, add proper icon and logo
This commit is contained in:
@@ -0,0 +1,181 @@
|
||||
import { useState } from 'react';
|
||||
import {
|
||||
Eye,
|
||||
EyeOff,
|
||||
LockKeyhole,
|
||||
Shield,
|
||||
User,
|
||||
} from 'lucide-react';
|
||||
|
||||
import { Button } from '@/components/ui/Button';
|
||||
import { loginApi } from '@/services/loginApi';
|
||||
|
||||
type LoginScreenProps = {
|
||||
onLogin: (keepLoggedIn: boolean) => void;
|
||||
};
|
||||
|
||||
export function LoginScreen({
|
||||
onLogin,
|
||||
}: LoginScreenProps) {
|
||||
const [username, setUsername] = useState('');
|
||||
const [password, setPassword] = useState('');
|
||||
const [error, setError] = useState('');
|
||||
const [keepLoggedIn, setKeepLoggedIn] = useState(false);
|
||||
const [showPassword, setShowPassword] = useState(false);
|
||||
const [submitting, setSubmitting] = useState(false);
|
||||
|
||||
async function handleSubmit(event: React.FormEvent) {
|
||||
event.preventDefault();
|
||||
|
||||
if (submitting) {
|
||||
return;
|
||||
}
|
||||
|
||||
setSubmitting(true);
|
||||
setError('');
|
||||
|
||||
try {
|
||||
const response = await loginApi.login(
|
||||
username.trim(),
|
||||
password,
|
||||
);
|
||||
|
||||
if (response.authenticated) {
|
||||
onLogin(keepLoggedIn);
|
||||
return;
|
||||
}
|
||||
|
||||
setError('Credenciais inválidas.');
|
||||
} catch {
|
||||
setError('Credenciais inválidas.');
|
||||
} finally {
|
||||
setSubmitting(false);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="relative flex min-h-screen items-center justify-center overflow-hidden bg-ink-950 px-6 text-white">
|
||||
<div className="pointer-events-none absolute -left-32 top-10 h-96 w-96 rounded-full bg-blue-500/20 blur-3xl" />
|
||||
<div className="pointer-events-none absolute -right-32 bottom-10 h-96 w-96 rounded-full bg-cyan-400/10 blur-3xl" />
|
||||
<div className="pointer-events-none absolute inset-0 bg-[radial-gradient(circle_at_top,rgba(59,130,246,0.16),transparent_42%)]" />
|
||||
|
||||
<div className="relative w-full max-w-md">
|
||||
<div className="absolute -inset-px rounded-[2rem] bg-gradient-to-br from-blue-500/40 via-cyan-400/10 to-white/5 opacity-80 blur-sm" />
|
||||
|
||||
<div className="relative rounded-[2rem] border border-white/10 bg-slate-950/80 p-8 shadow-2xl shadow-blue-500/10 backdrop-blur-xl">
|
||||
<div className="mb-8 flex items-center gap-4">
|
||||
<div className="rounded-2xl border border-blue-400/20 bg-blue-500/15 p-4 text-blue-300 shadow-lg shadow-blue-500/10">
|
||||
<Shield size={32} />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h1 className="text-3xl font-bold tracking-tight text-white">
|
||||
Litoral Regas
|
||||
</h1>
|
||||
|
||||
<p className="mt-1 text-sm text-blue-200/80">
|
||||
VPN Orchestrator
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form onSubmit={handleSubmit}>
|
||||
<div className="space-y-5">
|
||||
<div>
|
||||
<label className="mb-2 block text-xs font-semibold uppercase tracking-[0.18em] text-slate-500">
|
||||
Utilizador
|
||||
</label>
|
||||
|
||||
<div className="group relative">
|
||||
<User
|
||||
size={17}
|
||||
className="absolute left-4 top-1/2 -translate-y-1/2 text-blue-300/60 transition group-focus-within:text-blue-300"
|
||||
/>
|
||||
|
||||
<input
|
||||
value={username}
|
||||
onChange={(event) =>
|
||||
setUsername(event.target.value)
|
||||
}
|
||||
autoComplete="username"
|
||||
className="w-full rounded-2xl border border-white/10 bg-black/25 py-4 pl-11 pr-4 text-sm text-white outline-none transition placeholder:text-slate-600 focus:border-blue-400/60 focus:bg-black/35 focus:shadow-[0_0_0_4px_rgba(59,130,246,0.12)]"
|
||||
placeholder="Introduza o utilizador"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="mb-2 block text-xs font-semibold uppercase tracking-[0.18em] text-slate-500">
|
||||
Palavra-passe
|
||||
</label>
|
||||
|
||||
<div className="group relative">
|
||||
<LockKeyhole
|
||||
size={17}
|
||||
className="absolute left-4 top-1/2 -translate-y-1/2 text-blue-300/60 transition group-focus-within:text-blue-300"
|
||||
/>
|
||||
|
||||
<input
|
||||
type={showPassword ? 'text' : 'password'}
|
||||
value={password}
|
||||
onChange={(event) =>
|
||||
setPassword(event.target.value)
|
||||
}
|
||||
autoComplete="current-password"
|
||||
className="w-full rounded-2xl border border-white/10 bg-black/25 py-4 pl-11 pr-12 text-sm text-white outline-none transition placeholder:text-slate-600 focus:border-blue-400/60 focus:bg-black/35 focus:shadow-[0_0_0_4px_rgba(59,130,246,0.12)]"
|
||||
placeholder="Introduza a palavra-passe"
|
||||
/>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
onClick={() =>
|
||||
setShowPassword((current) => !current)
|
||||
}
|
||||
className="absolute right-4 top-1/2 -translate-y-1/2 text-slate-500 transition hover:text-blue-200"
|
||||
>
|
||||
{showPassword ? (
|
||||
<EyeOff size={17} />
|
||||
) : (
|
||||
<Eye size={17} />
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{error && (
|
||||
<div className="mt-5 rounded-2xl border border-red-500/20 bg-red-500/10 p-3 text-sm text-red-200">
|
||||
{error}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<label className="mt-5 flex cursor-pointer items-center gap-3 text-sm text-slate-400 transition hover:text-slate-200">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={keepLoggedIn}
|
||||
onChange={(event) =>
|
||||
setKeepLoggedIn(event.target.checked)
|
||||
}
|
||||
className="h-4 w-4 accent-blue-500"
|
||||
/>
|
||||
|
||||
Manter sessão iniciada
|
||||
</label>
|
||||
|
||||
<Button
|
||||
type="submit"
|
||||
disabled={submitting}
|
||||
className="mt-7 w-full justify-center rounded-2xl py-4 text-sm font-bold shadow-lg shadow-blue-500/20 transition hover:-translate-y-[1px]"
|
||||
>
|
||||
{submitting ? 'A entrar...' : 'Entrar'}
|
||||
</Button>
|
||||
</form>
|
||||
|
||||
<div className="mt-8 border-t border-white/10 pt-5 text-center text-xs text-slate-600">
|
||||
Acesso restrito a técnicos autorizados
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user