fix(admin): Protokoll formuliert Aktionen am eigenen Konto natürlich
„hat die eigenen Daten exportiert“ statt „die Daten von sich selbst“. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
0504d284e2
commit
ed0b4666bc
3 files changed
+21
-9
No files matched your search
@@ -51,7 +51,7 @@ const CATEGORIES = Object.keys(AUDIT_CATEGORY_LABEL) as AuditCategory[];
|
||||
function EntryRow({ entry }: { entry: Entry }) {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const isSelf = entry.targetType === "USER" && entry.targetId === entry.moderatorId;
|
||||
const view = formatAuditEntry({ ...entry, targetLabel: isSelf ? "sich selbst" : entry.targetLabel });
|
||||
const view = formatAuditEntry({ ...entry, isSelf });
|
||||
const Icon = entry.isAutomatic ? Bot : CATEGORY_ICON[view.category];
|
||||
const removal = isRemoval(entry.action);
|
||||
const actor = entry.isAutomatic ? "Automatik" : (entry.moderatorName ?? "Unbekannt");
|
||||
|
||||
@@ -43,6 +43,14 @@ describe("formatAuditEntry", () => {
|
||||
expect(text(fmt("GRANT_ROLE", "MODERATOR", "Lea").parts)).toBe("hat **Lea** die Rolle **Moderator** gegeben");
|
||||
});
|
||||
|
||||
it("phrases actions on the moderator's own account naturally", () => {
|
||||
const self = (action: string, reason: string | null = null) =>
|
||||
text(formatAuditEntry({ action, reason, targetLabel: "Daniel", rankName: "Pfoten-Legende", isSelf: true }).parts);
|
||||
expect(self("DATA_EXPORT")).toBe("hat die eigenen Daten exportiert");
|
||||
expect(self("GRANT_REFERRAL_RANK", "rank-1")).toBe("hat sich selbst den Rang **Pfoten-Legende** verliehen");
|
||||
expect(self("UNBAN_USER")).toBe("hat die eigene Sperre aufgehoben");
|
||||
});
|
||||
|
||||
it("never shows a bare code for unknown actions", () => {
|
||||
const v = fmt("SOMETHING_NEW");
|
||||
expect(v.category).toBe("OTHER");
|
||||
|
||||
@@ -67,6 +67,8 @@ export interface AuditInput {
|
||||
targetLabel: string | null;
|
||||
/** Vom Server aufgelöster Rangname bei Empfehlungs-Aktionen. */
|
||||
rankName?: string | null;
|
||||
/** Halter-Aktion am eigenen Konto → „sich selbst“ bzw. „die eigenen Daten“ statt zweimal derselbe Name. */
|
||||
isSelf?: boolean;
|
||||
}
|
||||
|
||||
export interface AuditView {
|
||||
@@ -107,13 +109,15 @@ function splitPrefix(reason: string | null): [string | null, string | null] {
|
||||
const reasonLine = (text: string | null | undefined) => (text && text.trim() ? [`Grund: ${text.trim()}`] : []);
|
||||
const formatValue = (v: unknown) => (v === true ? "an" : v === false ? "aus" : String(v));
|
||||
|
||||
export function formatAuditEntry({ action, reason, targetLabel, rankName }: AuditInput): AuditView {
|
||||
export function formatAuditEntry({ action, reason, targetLabel, rankName, isSelf = false }: AuditInput): AuditView {
|
||||
const category = CATEGORY_BY_ACTION.get(action) ?? "OTHER";
|
||||
const s = (label: string | null | undefined, fallback: string): AuditPart => (label ? { strong: label } : fallback);
|
||||
const view = (parts: AuditPart[], details: string[] = []): AuditView => ({ category, parts, details });
|
||||
|
||||
const post = targetLabel ? ["den Beitrag von ", { strong: targetLabel }] : ["einen Beitrag"];
|
||||
const owner = s(targetLabel, "einen Halter");
|
||||
const owner: AuditPart = isSelf ? "sich selbst" : s(targetLabel, "einen Halter");
|
||||
/** „die Sperre von X“ bzw. am eigenen Konto „die eigene Sperre“. */
|
||||
const ofOwner = (noun: string, ownNoun: string): AuditPart[] => (isSelf ? [ownNoun] : [`${noun} von `, owner]);
|
||||
const pet = s(targetLabel, "ein Tier");
|
||||
const rank = s(rankName, "einen Rang");
|
||||
|
||||
@@ -142,17 +146,17 @@ export function formatAuditEntry({ action, reason, targetLabel, rankName }: Audi
|
||||
case "BAN_USER":
|
||||
return view(["hat ", owner, " gesperrt"], reasonLine(reason));
|
||||
case "UNBAN_USER":
|
||||
return view(["hat die Sperre von ", owner, " aufgehoben"]);
|
||||
return view(["hat ", ...ofOwner("die Sperre", "die eigene Sperre"), " aufgehoben"]);
|
||||
case "SHADOWBAN_USER":
|
||||
return view(["hat ", owner, " unsichtbar geschaltet (Shadowban)"], reasonLine(reason));
|
||||
case "UNSHADOWBAN_USER":
|
||||
return view(["hat den Shadowban von ", owner, " aufgehoben"]);
|
||||
return view(["hat ", ...ofOwner("den Shadowban", "den eigenen Shadowban"), " aufgehoben"]);
|
||||
case "WARN_USER":
|
||||
return view(["hat ", owner, " verwarnt"], reasonLine(reason));
|
||||
case "DELETE_ACCOUNT":
|
||||
return view(["hat das Konto von ", owner, " gelöscht"], reasonLine(reason));
|
||||
return view(["hat ", ...ofOwner("das Konto", "das eigene Konto"), " gelöscht"], reasonLine(reason));
|
||||
case "DATA_EXPORT":
|
||||
return view(["hat die Daten von ", owner, " exportiert"]);
|
||||
return view(["hat ", ...ofOwner("die Daten", "die eigenen Daten"), " exportiert"]);
|
||||
|
||||
// Tiere
|
||||
case "VERIFY_PET":
|
||||
@@ -241,9 +245,9 @@ export function formatAuditEntry({ action, reason, targetLabel, rankName }: Audi
|
||||
return view(["hat die Einstellungen des ", { strong: "Empfehlungsprogramms" }, " geändert"], details);
|
||||
}
|
||||
case "APPROVE_REFERRAL":
|
||||
return view(["hat die Einladung von ", owner, " freigegeben"]);
|
||||
return view(["hat ", ...ofOwner("die Einladung", "die eigene Einladung"), " freigegeben"]);
|
||||
case "REVOKE_REFERRAL":
|
||||
return view(["hat die Einladung von ", owner, " aberkannt"], reasonLine(reason));
|
||||
return view(["hat ", ...ofOwner("die Einladung", "die eigene Einladung"), " aberkannt"], reasonLine(reason));
|
||||
case "GRANT_REFERRAL_RANK":
|
||||
return view(["hat ", owner, " den Rang ", rank, " verliehen"], splitPrefix(reason)[1] ? [`Notiz: ${splitPrefix(reason)[1]}`] : []);
|
||||
case "RESTORE_REFERRAL_RANK":
|
||||
|
||||
Reference in new issue
Block a user