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>
This commit is contained in:
@@ -64,7 +64,7 @@ export function CashRegistersPage() {
|
||||
setMessage({ tone: 'ok', text: okText })
|
||||
await queryClient.invalidateQueries({ queryKey: ['cash-registers'] })
|
||||
} catch (err) {
|
||||
setMessage({ tone: 'error', text: err instanceof Error ? err.message : 'Ошибка запроса' })
|
||||
setMessage({ tone: 'error', text: err instanceof Error ? err.message : 'Помилка запиту' })
|
||||
} finally {
|
||||
setBusy(false)
|
||||
}
|
||||
@@ -101,7 +101,7 @@ export function CashRegistersPage() {
|
||||
if (form.license_key) payload.license_key = form.license_key
|
||||
if (form.pin_code) payload.pin_code = form.pin_code
|
||||
if (form.np_api_key) payload.np_api_key = form.np_api_key
|
||||
await run(() => updateCashRegister(editingId, payload), 'Касса обновлена')
|
||||
await run(() => updateCashRegister(editingId, payload), 'Касу оновлено')
|
||||
} else {
|
||||
await run(
|
||||
() =>
|
||||
@@ -114,7 +114,7 @@ export function CashRegistersPage() {
|
||||
tax_codes: parseTaxCodes(form.tax_codes),
|
||||
is_default: form.is_default,
|
||||
}),
|
||||
'Касса добавлена',
|
||||
'Касу додано',
|
||||
)
|
||||
}
|
||||
resetForm()
|
||||
@@ -123,21 +123,21 @@ export function CashRegistersPage() {
|
||||
return (
|
||||
<div className="dashboard-shell">
|
||||
<header className="dashboard-topbar">
|
||||
<span className="dashboard-brand">lux_fiscal</span>
|
||||
<span className="dashboard-brand">Assistant System</span>
|
||||
<Link to="/" className="orders-link-btn">
|
||||
← К заказам
|
||||
← До замовлень
|
||||
</Link>
|
||||
</header>
|
||||
|
||||
<main className="orders-body">
|
||||
<div className="orders-toolbar">
|
||||
<h2>Кассы Checkbox</h2>
|
||||
<h2>Каси Checkbox</h2>
|
||||
</div>
|
||||
|
||||
{message && (
|
||||
<div className={`orders-notice orders-notice--${message.tone}`}>
|
||||
<span>{message.text}</span>
|
||||
<button type="button" className="orders-notice-close" onClick={() => setMessage(null)} aria-label="Скрыть">
|
||||
<button type="button" className="orders-notice-close" onClick={() => setMessage(null)} aria-label="Сховати">
|
||||
×
|
||||
</button>
|
||||
</div>
|
||||
@@ -147,34 +147,34 @@ export function CashRegistersPage() {
|
||||
<table className="orders-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Название</th>
|
||||
<th>Фиск. номер</th>
|
||||
<th>Ключ лицензии</th>
|
||||
<th>Ключ Новой Почты</th>
|
||||
<th>Налоги</th>
|
||||
<th>Назва</th>
|
||||
<th>Фіск. номер</th>
|
||||
<th>Ключ ліцензії</th>
|
||||
<th>Ключ Нової Пошти</th>
|
||||
<th>Податки</th>
|
||||
<th>Статус</th>
|
||||
<th>Действия</th>
|
||||
<th>Дії</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{isLoading && (
|
||||
<tr>
|
||||
<td colSpan={7} className="orders-empty">
|
||||
Загрузка…
|
||||
Завантаження…
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
{isError && (
|
||||
<tr>
|
||||
<td colSpan={7} className="orders-empty">
|
||||
Не удалось загрузить кассы
|
||||
Не вдалося завантажити каси
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
{registers?.length === 0 && (
|
||||
<tr>
|
||||
<td colSpan={7} className="orders-empty">
|
||||
Кассы не настроены — чеки создавать нельзя
|
||||
Каси не налаштовані — створювати чеки неможливо
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
@@ -182,15 +182,15 @@ export function CashRegistersPage() {
|
||||
<tr key={register.id}>
|
||||
<td>
|
||||
{register.name}
|
||||
{register.is_default && <span className="orders-status orders-status--delivered cr-default">основная</span>}
|
||||
{register.is_default && <span className="orders-status orders-status--delivered cr-default">основна</span>}
|
||||
</td>
|
||||
<td>{register.fiscal_number || '—'}</td>
|
||||
<td>{register.license_key_masked}</td>
|
||||
<td>{register.np_api_key_masked ?? <span className="cr-missing">не задан</span>}</td>
|
||||
<td>{register.tax_codes.length ? register.tax_codes.join(', ') : 'без налога'}</td>
|
||||
<td>{register.np_api_key_masked ?? <span className="cr-missing">не задано</span>}</td>
|
||||
<td>{register.tax_codes.length ? register.tax_codes.join(', ') : 'без податку'}</td>
|
||||
<td>
|
||||
<span className={`orders-status orders-status--${register.is_active ? 'delivered' : 'new'}`}>
|
||||
{register.is_active ? 'Активна' : 'Отключена'}
|
||||
{register.is_active ? 'Активна' : 'Вимкнена'}
|
||||
</span>
|
||||
</td>
|
||||
<td className="orders-actions-cell">
|
||||
@@ -198,12 +198,12 @@ export function CashRegistersPage() {
|
||||
type="button"
|
||||
className="orders-view-btn"
|
||||
disabled={busy}
|
||||
onClick={() => void run(() => checkCashRegister(register.id), `Вход в «${register.name}» успешен`)}
|
||||
onClick={() => void run(() => checkCashRegister(register.id), `Вхід у «${register.name}» успішний`)}
|
||||
>
|
||||
Проверить
|
||||
Перевірити
|
||||
</button>
|
||||
<button type="button" className="orders-view-btn" disabled={busy} onClick={() => startEdit(register)}>
|
||||
Изменить
|
||||
Змінити
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
@@ -212,11 +212,11 @@ export function CashRegistersPage() {
|
||||
onClick={() =>
|
||||
void run(
|
||||
() => updateCashRegister(register.id, { is_active: !register.is_active }),
|
||||
register.is_active ? 'Касса отключена' : 'Касса включена',
|
||||
register.is_active ? 'Касу вимкнено' : 'Касу увімкнено',
|
||||
)
|
||||
}
|
||||
>
|
||||
{register.is_active ? 'Отключить' : 'Включить'}
|
||||
{register.is_active ? 'Вимкнути' : 'Увімкнути'}
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -226,48 +226,48 @@ export function CashRegistersPage() {
|
||||
</div>
|
||||
|
||||
<form className="cr-form" onSubmit={(e) => void handleSubmit(e)}>
|
||||
<h3>{editingId ? 'Изменить кассу' : 'Добавить кассу'}</h3>
|
||||
<h3>{editingId ? 'Змінити касу' : 'Додати касу'}</h3>
|
||||
<label>
|
||||
Название
|
||||
Назва
|
||||
<input required value={form.name} onChange={(e) => setForm({ ...form, name: e.target.value })} />
|
||||
</label>
|
||||
<label>
|
||||
Фискальный номер
|
||||
Фіскальний номер
|
||||
<input value={form.fiscal_number} onChange={(e) => setForm({ ...form, fiscal_number: e.target.value })} />
|
||||
</label>
|
||||
<label>
|
||||
Ключ лицензии кассы
|
||||
Ключ ліцензії каси
|
||||
<input
|
||||
required={!editingId}
|
||||
autoComplete="off"
|
||||
placeholder={editingId ? 'не менять' : ''}
|
||||
placeholder={editingId ? 'не змінювати' : ''}
|
||||
value={form.license_key}
|
||||
onChange={(e) => setForm({ ...form, license_key: e.target.value })}
|
||||
/>
|
||||
</label>
|
||||
<label>
|
||||
PIN-код кассира
|
||||
PIN-код касира
|
||||
<input
|
||||
required={!editingId}
|
||||
type="password"
|
||||
autoComplete="new-password"
|
||||
placeholder={editingId ? 'не менять' : ''}
|
||||
placeholder={editingId ? 'не змінювати' : ''}
|
||||
value={form.pin_code}
|
||||
onChange={(e) => setForm({ ...form, pin_code: e.target.value })}
|
||||
/>
|
||||
</label>
|
||||
<label>
|
||||
Ключ API Новой Почты (посылки этого кабинета пробиваются через эту кассу)
|
||||
Ключ API Нової Пошти (посилки цього кабінету пробиваються через цю касу)
|
||||
<input
|
||||
required={!editingId}
|
||||
autoComplete="off"
|
||||
placeholder={editingId ? 'не менять' : ''}
|
||||
placeholder={editingId ? 'не змінювати' : ''}
|
||||
value={form.np_api_key}
|
||||
onChange={(e) => setForm({ ...form, np_api_key: e.target.value })}
|
||||
/>
|
||||
</label>
|
||||
<label>
|
||||
Коды налогов (через запятую, пусто — без налога)
|
||||
Коди податків (через кому, порожньо — без податку)
|
||||
<input value={form.tax_codes} onChange={(e) => setForm({ ...form, tax_codes: e.target.value })} />
|
||||
</label>
|
||||
<label className="cr-checkbox">
|
||||
@@ -276,15 +276,15 @@ export function CashRegistersPage() {
|
||||
checked={form.is_default}
|
||||
onChange={(e) => setForm({ ...form, is_default: e.target.checked })}
|
||||
/>
|
||||
Основная касса (первой проверяет новые посылки)
|
||||
Основна каса (першою перевіряє нові посилки)
|
||||
</label>
|
||||
<div className="cr-form-actions">
|
||||
<button type="submit" className="orders-create-btn" disabled={busy}>
|
||||
{editingId ? 'Сохранить' : 'Добавить'}
|
||||
{editingId ? 'Зберегти' : 'Додати'}
|
||||
</button>
|
||||
{editingId && (
|
||||
<button type="button" className="orders-view-btn" onClick={resetForm}>
|
||||
Отмена
|
||||
Скасувати
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user