Some checks failed
Build & Release / build (push) Failing after 32s
Newer TanStack Router enforces strict types on search params — 'search: {} as Record<string, unknown>' no longer satisfies routes with validateSearch. Replace all occurrences with the correct search shape for each destination route (pagination defaults for list routes, tab/field defaults for detail routes).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
94 lines
3.5 KiB
TypeScript
94 lines
3.5 KiB
TypeScript
import { useState } from 'react'
|
|
import { createFileRoute, useRouter, redirect } from '@tanstack/react-router'
|
|
import { useAuthStore } from '@/stores/auth.store'
|
|
import { login } from '@/api/auth'
|
|
|
|
export const Route = createFileRoute('/login')({
|
|
beforeLoad: () => {
|
|
const { token } = useAuthStore.getState()
|
|
if (token) {
|
|
throw redirect({ to: '/accounts', search: { page: 1, limit: 25, q: undefined, sort: undefined, order: 'asc' as const } })
|
|
}
|
|
},
|
|
component: LoginPage,
|
|
})
|
|
|
|
function LoginPage() {
|
|
const router = useRouter()
|
|
const setAuth = useAuthStore((s) => s.setAuth)
|
|
const [email, setEmail] = useState('')
|
|
const [password, setPassword] = useState('')
|
|
const [error, setError] = useState('')
|
|
const [loading, setLoading] = useState(false)
|
|
|
|
async function handleSubmit(e: React.FormEvent) {
|
|
e.preventDefault()
|
|
setError('')
|
|
setLoading(true)
|
|
|
|
try {
|
|
const res = await login(email, password)
|
|
setAuth(res.token, res.user)
|
|
await router.invalidate()
|
|
await router.navigate({ to: '/accounts', search: { page: 1, limit: 25, q: undefined, sort: undefined, order: 'asc' as const }, replace: true })
|
|
} catch (err) {
|
|
setError(err instanceof Error ? err.message : 'Login failed')
|
|
} finally {
|
|
setLoading(false)
|
|
}
|
|
}
|
|
|
|
return (
|
|
<div
|
|
className="flex min-h-screen items-center justify-center"
|
|
style={{ background: 'linear-gradient(135deg, #0f1724 0%, #142038 100%)' }}
|
|
>
|
|
<div
|
|
className="w-full max-w-sm rounded-xl border p-8 shadow-2xl"
|
|
style={{ backgroundColor: '#131c2e', borderColor: '#1e2d45' }}
|
|
>
|
|
<div className="text-center mb-8">
|
|
<h1 className="text-3xl font-bold" style={{ color: '#d8dfe9' }}>LunarFront</h1>
|
|
<p className="text-sm mt-1" style={{ color: '#6b7a8d' }}>Small Business Management</p>
|
|
</div>
|
|
<form onSubmit={handleSubmit} className="space-y-4">
|
|
<div className="space-y-2">
|
|
<label className="text-sm font-medium" style={{ color: '#b0bec5' }}>Email</label>
|
|
<input
|
|
type="email"
|
|
placeholder="you@example.com"
|
|
value={email}
|
|
onChange={(e) => setEmail(e.target.value)}
|
|
required
|
|
className="h-9 w-full rounded-md border px-3 py-1 text-sm outline-none login-input"
|
|
/>
|
|
</div>
|
|
<div className="space-y-2">
|
|
<label className="text-sm font-medium" style={{ color: '#b0bec5' }}>Password</label>
|
|
<input
|
|
type="password"
|
|
value={password}
|
|
onChange={(e) => setPassword(e.target.value)}
|
|
required
|
|
className="h-9 w-full rounded-md border px-3 py-1 text-sm outline-none login-input"
|
|
/>
|
|
</div>
|
|
{error && (
|
|
<p className="text-sm" style={{ color: '#e57373' }}>{error}</p>
|
|
)}
|
|
<button
|
|
type="submit"
|
|
disabled={loading}
|
|
className="h-9 w-full rounded-md border text-sm font-medium transition-colors disabled:opacity-50"
|
|
style={{ backgroundColor: 'transparent', color: '#d0d8e0', borderColor: '#3a4a62' }}
|
|
onMouseEnter={(e) => { (e.target as HTMLElement).style.backgroundColor = '#1e2d45' }}
|
|
onMouseLeave={(e) => { (e.target as HTMLElement).style.backgroundColor = 'transparent' }}
|
|
>
|
|
{loading ? 'Signing in...' : 'Sign in'}
|
|
</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|