145 lines
5.6 KiB
TypeScript
145 lines
5.6 KiB
TypeScript
import { useState } from 'react'
|
|
import { useNavigate } from 'react-router-dom'
|
|
import { useMutation } from '@tanstack/react-query'
|
|
import { authApi } from '@/api/auth'
|
|
import { useAuthStore } from '@/store/auth'
|
|
|
|
export default function LoginPage() {
|
|
const navigate = useNavigate()
|
|
const setAuth = useAuthStore((state) => state.setAuth)
|
|
const [username, setUsername] = useState('')
|
|
const [password, setPassword] = useState('')
|
|
const [error, setError] = useState('')
|
|
|
|
const loginMutation = useMutation({
|
|
mutationFn: authApi.login,
|
|
onSuccess: (data) => {
|
|
setAuth(data.token, data.user)
|
|
navigate('/')
|
|
},
|
|
onError: (err: any) => {
|
|
setError(err.response?.data?.error || 'Login failed')
|
|
},
|
|
})
|
|
|
|
const handleSubmit = (e: React.FormEvent) => {
|
|
e.preventDefault()
|
|
setError('')
|
|
loginMutation.mutate({ username, password })
|
|
}
|
|
|
|
return (
|
|
<>
|
|
<style>{`
|
|
input:-webkit-autofill,
|
|
input:-webkit-autofill:hover,
|
|
input:-webkit-autofill:focus,
|
|
input:-webkit-autofill:active {
|
|
-webkit-box-shadow: 0 0 0 30px #111a22 inset !important;
|
|
-webkit-text-fill-color: #ffffff !important;
|
|
box-shadow: 0 0 0 30px #111a22 inset !important;
|
|
caret-color: #ffffff !important;
|
|
}
|
|
input:-webkit-autofill::first-line {
|
|
color: #ffffff !important;
|
|
}
|
|
`}</style>
|
|
<div className="min-h-screen flex items-center justify-center bg-background-dark">
|
|
<div className="max-w-md w-full space-y-8 p-8 bg-card-dark border border-border-dark rounded-lg shadow-md">
|
|
<div className="flex flex-col items-center">
|
|
{/* Logo */}
|
|
<div className="mb-4">
|
|
<img
|
|
src="/logo.png"
|
|
alt="Calypso Logo"
|
|
className="w-16 h-16 object-contain"
|
|
/>
|
|
</div>
|
|
{/* Title */}
|
|
<h2 className="text-center text-3xl font-extrabold text-white">
|
|
Calypso
|
|
</h2>
|
|
{/* Version */}
|
|
<p className="mt-1 text-center text-xs text-text-secondary">
|
|
Dev Release V.1
|
|
</p>
|
|
{/* Subtitle */}
|
|
<p className="mt-2 text-center text-sm text-text-secondary">
|
|
Adastra Backup Storage Appliance
|
|
</p>
|
|
{/* Sign in instruction */}
|
|
<p className="mt-4 text-center text-sm text-text-secondary">
|
|
Sign in to your account
|
|
</p>
|
|
</div>
|
|
<form className="mt-8 space-y-6" onSubmit={handleSubmit}>
|
|
{error && (
|
|
<div className="rounded-md bg-red-500/10 border border-red-500/30 p-4">
|
|
<p className="text-sm text-red-400 font-medium">{error}</p>
|
|
</div>
|
|
)}
|
|
<div className="rounded-md shadow-sm -space-y-px">
|
|
<div>
|
|
<label htmlFor="username" className="sr-only">
|
|
Username
|
|
</label>
|
|
<input
|
|
id="username"
|
|
name="username"
|
|
type="text"
|
|
required
|
|
className="appearance-none rounded-none relative block w-full px-3 py-2 border border-border-dark bg-[#111a22] placeholder-text-secondary text-white rounded-t-md focus:outline-none focus:ring-primary focus:border-primary focus:z-10 sm:text-sm autofill:bg-[#111a22] autofill:text-white"
|
|
placeholder="Username"
|
|
value={username}
|
|
onChange={(e) => setUsername(e.target.value)}
|
|
autoComplete="username"
|
|
/>
|
|
</div>
|
|
<div>
|
|
<label htmlFor="password" className="sr-only">
|
|
Password
|
|
</label>
|
|
<input
|
|
id="password"
|
|
name="password"
|
|
type="password"
|
|
required
|
|
className="appearance-none rounded-none relative block w-full px-3 py-2 border border-border-dark bg-[#111a22] placeholder-text-secondary text-white rounded-b-md focus:outline-none focus:ring-primary focus:border-primary focus:z-10 sm:text-sm autofill:bg-[#111a22] autofill:text-white"
|
|
placeholder="Password"
|
|
value={password}
|
|
onChange={(e) => setPassword(e.target.value)}
|
|
autoComplete="current-password"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div>
|
|
<button
|
|
type="submit"
|
|
disabled={loginMutation.isPending}
|
|
className="relative group relative w-full flex justify-center py-2.5 px-4 border border-primary/30 bg-card-dark text-white text-sm font-bold rounded-lg hover:bg-[#233648] transition-all overflow-hidden electric-glow electric-glow-border disabled:opacity-50 disabled:cursor-not-allowed"
|
|
>
|
|
{/* Electric glow gradient overlay */}
|
|
<span className="absolute inset-0 bg-gradient-to-r from-primary/0 via-primary/15 to-primary/0 opacity-60"></span>
|
|
{/* Shimmer effect */}
|
|
<span className="absolute inset-0 bg-gradient-to-r from-transparent via-white/5 to-transparent animate-[shimmer_3s_infinite]"></span>
|
|
<span className="relative z-10 flex items-center gap-2">
|
|
{loginMutation.isPending ? (
|
|
<>
|
|
<span className="material-symbols-outlined text-[18px] animate-spin">refresh</span>
|
|
Signing in...
|
|
</>
|
|
) : (
|
|
'Sign in'
|
|
)}
|
|
</span>
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</>
|
|
)
|
|
}
|
|
|