Files
lux_fiscal/frontend/src/features/auth/LoginPage.tsx
T
lauadminandClaude Opus 5.5 b8f2fe6b6e Translate UI and user-facing messages to Ukrainian (#5)
- All frontend pages, labels, notices and errors; html lang=uk, uk-UA money format
- Brand "Assistant System" in the top bar and page title
- Backend error details returned to the UI (auth, orders, receipts,
  cash registers, Checkbox/CRM/NP errors) and CLI output
- Tests updated for the new messages

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-25 15:27:39 +03:00

126 lines
4.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { useId, useState } from 'react'
import type { FormEvent } from 'react'
import { Navigate, useLocation } from 'react-router-dom'
import { ApiError } from '@/api/client'
import '@/features/auth/LoginPage.css'
import { useAuth } from '@/features/auth/useAuth'
interface LocationState {
from?: { pathname: string }
}
export function LoginPage() {
const { status, login } = useAuth()
const location = useLocation()
const emailId = useId()
const passwordId = useId()
const [email, setEmail] = useState('')
const [password, setPassword] = useState('')
const [showPassword, setShowPassword] = useState(false)
const [submitting, setSubmitting] = useState(false)
const [formError, setFormError] = useState<string | null>(null)
// Уже вошли — на странице входа делать нечего.
if (status === 'authenticated') {
const state = location.state as LocationState | null
const redirectTo = state?.from?.pathname ?? '/'
return <Navigate to={redirectTo} replace />
}
async function handleSubmit(event: FormEvent<HTMLFormElement>) {
event.preventDefault()
setFormError(null)
setSubmitting(true)
try {
await login(email, password)
} catch (err) {
const message = err instanceof ApiError ? err.message : 'Не вдалося підключитися до сервера'
setFormError(message)
} finally {
setSubmitting(false)
}
}
return (
<div className="login-page">
<div className="login-backdrop" aria-hidden="true">
<span className="login-blob login-blob--a" />
<span className="login-blob login-blob--b" />
<span className="login-blob login-blob--c" />
</div>
<div className="login-card">
<div className="login-header">
<div className="login-logo" aria-hidden="true">
<svg viewBox="0 0 24 24" width="26" height="26" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<path d="M12 3l7 4v5c0 4.5-3 8-7 9-4-1-7-4.5-7-9V7l7-4z" />
<path d="M9 12l2 2 4-4" />
</svg>
</div>
<h1>Assistant System</h1>
<p>Вхід у систему</p>
</div>
<form onSubmit={handleSubmit} noValidate>
{formError && (
<div className="login-error" role="alert">
{formError}
</div>
)}
<div className="login-field">
<label htmlFor={emailId}>Email</label>
<input
id={emailId}
type="email"
autoComplete="username"
required
value={email}
disabled={submitting}
onChange={(e) => setEmail(e.target.value)}
placeholder="you@example.com"
/>
</div>
<div className="login-field">
<label htmlFor={passwordId}>Пароль</label>
<div className="login-password">
<input
id={passwordId}
type={showPassword ? 'text' : 'password'}
autoComplete="current-password"
required
value={password}
disabled={submitting}
onChange={(e) => setPassword(e.target.value)}
placeholder="••••••••"
/>
<button
type="button"
className="login-toggle"
onClick={() => setShowPassword((v) => !v)}
aria-label={showPassword ? 'Приховати пароль' : 'Показати пароль'}
aria-pressed={showPassword}
>
<svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
<path d="M2 12s3.5-7 10-7 10 7 10 7-3.5 7-10 7S2 12 2 12z" />
<circle cx="12" cy="12" r="3" />
{showPassword && <path d="M3 3l18 18" />}
</svg>
</button>
</div>
</div>
<button type="submit" className="login-submit" disabled={submitting}>
{submitting && <span className="spinner" aria-hidden="true" />}
{submitting ? 'Входимо…' : 'Увійти'}
</button>
</form>
</div>
</div>
)
}