Files
telemtPanel/apps/web/src/routes/login.tsx
T
Denozordec b5f31c1083
Build and Push Telemt Panel Docker Image / build-and-push (push) Failing after 38s
Build and Push Telemt Panel Docker Image / create-release (push) Skipped
Init
2026-08-04 18:33:48 +07:00

112 lines
3.7 KiB
TypeScript

import { createFileRoute, redirect, useNavigate } from '@tanstack/react-router'
import { useState, type FormEvent } from 'react'
import { api } from '@/lib/api-client'
import { isLoggedIn, setToken } from '@/lib/auth'
import { Button } from '@telemt/ui/components/button'
import { Field, FieldGroup, FieldLabel } from '@telemt/ui/components/field'
import { Input } from '@telemt/ui/components/input'
import {
Frame,
FrameDescription,
FramePanel,
FrameTitle,
} from '@/components/reui/frame'
/**
* Local login — ReUI PRO auth-4 DNA, wired to panel JWT.
* @see https://reui.io/preview/base/auth-4
* @see https://reui.io/docs/blocks
*/
export const Route = createFileRoute('/login')({
beforeLoad: () => {
if (isLoggedIn()) throw redirect({ to: '/dashboard' })
},
component: LoginPage,
})
function LoginPage() {
const navigate = useNavigate()
const [username, setUsername] = useState('admin')
const [password, setPassword] = useState('')
const [error, setError] = useState<string | null>(null)
const [pending, setPending] = useState(false)
async function handleSubmit(e: FormEvent) {
e.preventDefault()
setPending(true)
setError(null)
try {
const res = await api<{ accessToken: string }>('/api/auth/login', {
method: 'POST',
body: JSON.stringify({ username, password }),
})
setToken(res.accessToken)
await navigate({ to: '/dashboard' })
} catch (err) {
setError(err instanceof Error ? err.message : 'Ошибка входа')
} finally {
setPending(false)
}
}
return (
<div className="bg-background relative flex min-h-svh flex-col items-center justify-center p-6">
<div className="w-full max-w-md">
<Frame className="w-full">
<FramePanel className="flex flex-col gap-6 p-6">
<div className="flex flex-col gap-1 text-center">
<FrameTitle className="text-xl">Telemt Panel</FrameTitle>
<FrameDescription>Локальный вход оператора</FrameDescription>
</div>
<form onSubmit={handleSubmit} className="flex flex-col gap-4">
<FieldGroup>
<Field>
<FieldLabel htmlFor="username">Логин</FieldLabel>
<Input
id="username"
autoComplete="username"
value={username}
onChange={(e) => setUsername(e.target.value)}
required
/>
</Field>
<Field>
<FieldLabel htmlFor="password">Пароль</FieldLabel>
<Input
id="password"
type="password"
autoComplete="current-password"
value={password}
onChange={(e) => setPassword(e.target.value)}
required
/>
</Field>
</FieldGroup>
{error ? (
<p className="text-destructive text-sm" role="alert">
{error}
</p>
) : null}
<Button type="submit" disabled={pending} className="w-full">
{pending ? 'Вход…' : 'Войти'}
</Button>
</form>
<p className="text-muted-foreground text-center text-xs">
UI: ReUI auth-4 ·{' '}
<a
className="underline"
href="https://reui.io/preview/base/auth-4"
target="_blank"
rel="noreferrer"
>
preview
</a>
</p>
</FramePanel>
</Frame>
</div>
</div>
)
}