Files
Art-gallery/client/src/components/CuratorLoginModal.tsx
T
Danila KhodjaefandCursor ca58c43648 Add Russian i18n with DB translations, locale API, and curator review UI.
UI chrome via react-i18next, catalog text in entity_translations with ru.wikipedia seeding, locale-aware search, and Translations page for publish workflow.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-15 11:51:46 +03:00

94 lines
2.8 KiB
TypeScript

import { useEffect, useState, type FormEvent } from 'react';
import { useTranslation } from 'react-i18next';
import './CuratorLoginModal.css';
interface Props {
open: boolean;
onClose: () => void;
onLogin: (username: string, password: string) => Promise<void>;
}
export default function CuratorLoginModal({ open, onClose, onLogin }: Props) {
const { t } = useTranslation('debug');
const { t: tc } = useTranslation('common');
const [username, setUsername] = useState('');
const [password, setPassword] = useState('');
const [error, setError] = useState<string | null>(null);
const [submitting, setSubmitting] = useState(false);
useEffect(() => {
if (!open) {
setUsername('');
setPassword('');
setError(null);
setSubmitting(false);
}
}, [open]);
if (!open) return null;
const handleSubmit = async (e: FormEvent) => {
e.preventDefault();
setError(null);
setSubmitting(true);
try {
await onLogin(username.trim(), password);
onClose();
} catch (err) {
setError(err instanceof Error ? err.message : t('loginFailed'));
} finally {
setSubmitting(false);
}
};
return (
<div className="curator-login-backdrop" onMouseDown={onClose}>
<div
className="curator-login-modal"
role="dialog"
aria-labelledby="curator-login-title"
aria-modal="true"
onMouseDown={(e) => e.stopPropagation()}
>
<h2 id="curator-login-title">{t('loginTitle')}</h2>
<p className="curator-login-hint">
Debug tools and catalog edits require a curator account.
</p>
<form onSubmit={handleSubmit}>
<label className="curator-login-field">
<span>{t('username')}</span>
<input
type="text"
autoComplete="username"
value={username}
onChange={(e) => setUsername(e.target.value)}
disabled={submitting}
required
/>
</label>
<label className="curator-login-field">
<span>{t('password')}</span>
<input
type="password"
autoComplete="current-password"
value={password}
onChange={(e) => setPassword(e.target.value)}
disabled={submitting}
required
/>
</label>
{error && <p className="curator-login-error">{error}</p>}
<div className="curator-login-actions">
<button type="button" className="curator-login-cancel" onClick={onClose} disabled={submitting}>
{tc('cancel')}
</button>
<button type="submit" className="curator-login-submit" disabled={submitting}>
{submitting ? t('saving') : t('signIn')}
</button>
</div>
</form>
</div>
</div>
);
}