fix(ui,i18n): E30 review round 1 — audit rows in the reader's language, one HU name per thing
- audit log: a job run named from its result data (nested phases too), a skipped run says so, a secret's reset reads as a reset, choices named as Configuration names them, interval / batch / counts localized; the inbox shares the same jobRunText - HU: remux is "Közvetlen közvetítés" (it shared "Közvetlen lejátszás" with direct play); backfill is behúzás, enrich is dúsítás, collection is kollekció, the allow-list is fehérlista; the last "jobnak" / "transzkódolás"; a test holds the options of one choice to distinct names - contract: every skipped / reason word a job returns is named in both languages - person page: the bio wait is timed from the open and a failing ask stops the polling; ClampedText (ui) shared with the collection page, whose short summary lost its dead More - a playlist names season 0 the Specials; the channel re-slot compares in the reader's alphabet; an invite's date is in the admin's language; the language-ordered lists are asked again once a language switch is saved (saveLanguage) - poster grids' column minimum in rem; lint refuses minmax(Npx,…)
This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
import type { TFunction } from "i18next";
|
||||
import type { AuditRow } from "../lib/api";
|
||||
import { formatNumber } from "../lib/format";
|
||||
import { jobResultText, parseSummary } from "../lib/jobResult";
|
||||
import type { JobResult } from "../lib/api";
|
||||
import { jobResultText, jobRunText } from "../lib/jobResult";
|
||||
import type { Column } from "./ui/DataTable";
|
||||
import { RelativeTime } from "./ui/RelativeTime";
|
||||
|
||||
@@ -14,17 +15,27 @@ function fmt(v: unknown): string {
|
||||
return v == null || v === "" ? "—" : String(v);
|
||||
}
|
||||
|
||||
/** A logged config value in the reader's language: a switch reads on/off, a count is grouped. */
|
||||
function configValue(t: TFunction, v: unknown, lang: string): string {
|
||||
/** A logged config value in the reader's language: a switch reads on/off, a count is grouped, a
|
||||
* choice is named as the Configuration page names it. */
|
||||
function configValue(t: TFunction, key: string, v: unknown, lang: string): string {
|
||||
if (typeof v === "boolean") return t(v ? "audit.on" : "audit.off");
|
||||
if (typeof v === "number") return formatNumber(v, lang);
|
||||
if (typeof v === "string" && v) return t(`config.choices.${key}.${v}`, { defaultValue: v });
|
||||
return fmt(v);
|
||||
}
|
||||
|
||||
/** A row's `after` as a job-style result (counts), or null when it holds none. */
|
||||
function counts(after: Record<string, unknown>): JobResult | null {
|
||||
const out: JobResult = {};
|
||||
for (const [k, v] of Object.entries(after)) if (typeof v === "number") out[k] = v;
|
||||
return Object.keys(out).length ? out : null;
|
||||
}
|
||||
|
||||
/** The details of the rows whose server `summary` is a fixed English line (E30), rebuilt from the row's
|
||||
* data in the reader's language: a job run names the job and its result (as the inbox does), a config
|
||||
* change names the field and its values, a pause/resume says nothing the action has not. `undefined` =
|
||||
* not one of these; the generic summary + change rendering applies. */
|
||||
* change names the field and its values, an interval or batch change its new value, a count-bearing
|
||||
* action its counts; a pause/resume says nothing the action has not. `undefined` = not one of these (or
|
||||
* an older row without the data); the generic summary + change rendering applies. */
|
||||
function localDetails(
|
||||
t: TFunction,
|
||||
row: AuditRow,
|
||||
@@ -33,23 +44,40 @@ function localDetails(
|
||||
const after = row.after ?? {};
|
||||
const before = row.before ?? {};
|
||||
if (row.action === "job_run" && row.target_type === "job" && row.target_id) {
|
||||
const job = t(`scheduler.jobs.${row.target_id}`, row.target_id);
|
||||
const failed = after.status === "error";
|
||||
const summary = typeof after.summary === "string" ? after.summary : null;
|
||||
// A nested phase (a Python dict repr) or an error text does not parse, and shows as it was.
|
||||
const result = failed ? null : parseSummary(summary);
|
||||
return {
|
||||
head: t(failed ? "inbox.jobDone.titleError" : "inbox.jobDone.titleOk", { job }),
|
||||
sub: result ? jobResultText(t, result, lang) : summary,
|
||||
};
|
||||
const run = jobRunText(t, { job_id: row.target_id, ...after }, lang);
|
||||
return { head: run.title, sub: run.body };
|
||||
}
|
||||
if ((row.action === "config_set" || row.action === "config_reset") && row.target_id) {
|
||||
const head = t(`config.fields.${row.target_id}.label`, row.target_id);
|
||||
const key = row.target_id;
|
||||
const head = t(`config.fields.${key}.label`, key);
|
||||
const reset = row.action === "config_reset";
|
||||
// A secret's value is never logged: there is no before/after to show.
|
||||
if (!row.before && !row.after) return { head, sub: t("audit.secretUpdated") };
|
||||
const to =
|
||||
row.action === "config_reset" ? t("audit.default") : configValue(t, after.value, lang);
|
||||
return { head, sub: `${configValue(t, before.value, lang)} → ${to}` };
|
||||
if (!row.before && !row.after)
|
||||
return { head, sub: t(reset ? "audit.secretReset" : "audit.secretUpdated") };
|
||||
const to = reset ? t("audit.default") : configValue(t, key, after.value, lang);
|
||||
return { head, sub: `${configValue(t, key, before.value, lang)} → ${to}` };
|
||||
}
|
||||
if (
|
||||
row.action === "scheduler_interval" &&
|
||||
row.target_id &&
|
||||
typeof after.interval_minutes === "number"
|
||||
)
|
||||
return {
|
||||
head: t(`scheduler.jobs.${row.target_id}`, row.target_id),
|
||||
sub: t("scheduler.everyMin", { count: after.interval_minutes }),
|
||||
};
|
||||
if (row.action === "maintenance_tune" && typeof after.revalidate_batch === "number")
|
||||
return {
|
||||
head: t("scheduler.maintenance.batchLabel"),
|
||||
sub: formatNumber(after.revalidate_batch, lang),
|
||||
};
|
||||
if (
|
||||
row.action === "audit_clear" ||
|
||||
row.action === "demo_reset" ||
|
||||
row.action === "discovery_purge"
|
||||
) {
|
||||
const result = counts(after);
|
||||
return result ? { head: null, sub: jobResultText(t, result, lang) } : undefined;
|
||||
}
|
||||
if (row.action === "sync_pause" || row.action === "sync_resume") return { head: null, sub: null };
|
||||
return undefined;
|
||||
|
||||
Reference in New Issue
Block a user