Files
TX/artifacts/api-server
riyadhafraa ba7232d663 Task #205: Add "Download CSV" export for role permission history
Backend
- New endpoint: GET /api/roles/:id/audit/export
  - Reuses parseRoleAuditFilters/buildRoleAuditWhere so it honors actor
    and from/to filters identically to the existing /audit list.
  - Resolves added/removed permission ids -> names in a single query.
  - Streams text/csv with a UTF-8 BOM (Excel compatibility), header
    timestamp,actor_username,added_permissions,removed_permissions,
    capped at 50,000 rows, filename role-{name}-history-{YYYY-MM-DD}.csv.
- Extracted csvEscape into artifacts/api-server/src/lib/csv.ts (with
  formula-injection mitigation) and refactored audit.ts to import it.
- OpenAPI spec entry exportRolePermissionAuditCsv added; codegen run to
  regenerate getExportRolePermissionAuditCsvUrl.

Frontend
- artifacts/tx-os/src/pages/admin.tsx RolePermissionHistory: new
  Download button (testid role-history-export-csv) next to Load more,
  with exporting / exportFailed state and a blob download flow that
  respects the active actor/from/to filters.
- exportFailed is a boolean (per code review): the panel only shows a
  localized generic message, so storing the raw error string would
  have been dead state.
- en/ar locale strings: admin.roles.historyExport.button =
  "Download CSV" / "تنزيل CSV", plus a failure message.

Tests
- 4 new server tests in role-permission-audit.test.mjs cover: 404 for
  unknown role, CSV header + rows + UTF-8 BOM bytes, actor/date filter
  honoring, and admin-only enforcement. All audit tests pass.
- e2e UI run via runTest verified: admin login, creating a role and
  saving permissions twice, the History panel shows entries, the
  Download CSV button downloads a valid CSV (correct header, BOM,
  data rows), and the same flow works in the Arabic UI.

Other test failures observed in the workflow (executive-meetings,
app-permissions-impact) are pre-existing flakes unrelated to this
change and do not touch any modified files.

Replit-Task-Id: 8b9814d2-bea8-4b3b-a313-97aec91d458c
2026-05-01 16:30:01 +00:00
..
2026-04-18 02:00:09 +00:00
2026-04-18 02:00:09 +00:00