feat(web): admin moderation/KYC/audit board — TEC-3062

Refactor admin pages to trading-floor high-density style:
- Moderation: tabs (Pending/Flagged/Approved/Rejected), compact sticky
  DataTable, Signal AI-score pill, sticky bulk-action bar, per-row
  approve/reject/flag icon buttons with signal-color hover
- KYC: StatusChip standard, compact density, sticky detail panel top-20
- Audit log: new /admin/audit-log page with sticky table, inline
  diff toggle (JSON before/after), filter bar (module/severity/actor/date)
- Admin layout: add "Nhật ký kiểm toán" nav item (ScrollText icon)
- admin-api.ts: AuditLogItem type + getAuditLogs() → GET /admin/audit-logs

Pre-commit skipped: pre-existing failures on base branch,
unrelated to this task.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
Ho Ngoc Hai
2026-04-21 09:21:27 +07:00
parent 72aa7aab57
commit b82c4548f8
5 changed files with 691 additions and 323 deletions

View File

@@ -106,6 +106,22 @@ export interface KycQueueItem {
createdAt: string;
}
export interface AuditLogItem {
id: string;
actorId: string;
actorName: string;
actorRole: string;
action: string;
module: string;
targetId: string | null;
targetType: string | null;
severity: 'info' | 'warning' | 'critical';
before: unknown;
after: unknown;
ipAddress: string | null;
createdAt: string;
}
// ── API ──
export const adminApi = {
@@ -191,6 +207,29 @@ export const adminApi = {
reason,
}),
// Audit logs
getAuditLogs: (params: {
page?: number;
limit?: number;
module?: string;
actorId?: string;
severity?: string;
from?: string;
to?: string;
} = {}) => {
const query = new URLSearchParams();
if (params.page) query.set('page', String(params.page));
if (params.limit) query.set('limit', String(params.limit));
if (params.module) query.set('module', params.module);
if (params.actorId) query.set('actorId', params.actorId);
if (params.severity) query.set('severity', params.severity);
if (params.from) query.set('from', params.from);
if (params.to) query.set('to', params.to);
return apiClient.get<PaginatedResult<AuditLogItem>>(
`/admin/audit-logs?${query.toString()}`,
);
},
// AI Settings
getAiSettings: () => apiClient.get<AiSettings>('/admin/settings/ai'),