diff --git a/internal/admin/dashboard/static/css/dashboard.css b/internal/admin/dashboard/static/css/dashboard.css index acc1cf02..b428ac08 100644 --- a/internal/admin/dashboard/static/css/dashboard.css +++ b/internal/admin/dashboard/static/css/dashboard.css @@ -3187,6 +3187,12 @@ textarea:focus { } /* Audit Log Section */ +.audit-retention-note { + margin-top: 5px; + color: var(--text-muted); + font-size: 13px; +} + .audit-log-section { background: var(--bg-surface); border: 1px solid var(--border); diff --git a/internal/admin/dashboard/static/js/modules/audit-list.js b/internal/admin/dashboard/static/js/modules/audit-list.js index 1317185f..76be97ad 100644 --- a/internal/admin/dashboard/static/js/modules/audit-list.js +++ b/internal/admin/dashboard/static/js/modules/audit-list.js @@ -123,6 +123,17 @@ : null; return { + auditRetentionText() { + const raw = this.workflowRuntimeConfig && this.workflowRuntimeConfig.LOGGING_RETENTION_DAYS; + if (raw === undefined || raw === null || String(raw).trim() === '') return ''; + + const days = Number(raw); + if (!Number.isInteger(days) || days < 0) return ''; + if (days === 0) return 'Audit logs are retained indefinitely.'; + if (days === 1) return 'Audit logs are retained for 1 day.'; + return 'Audit logs are retained for ' + days + ' days.'; + }, + _auditQueryStr() { if (this.customStartDate && this.customEndDate) { return 'start_date=' + this._formatDate(this.customStartDate) + diff --git a/internal/admin/dashboard/static/js/modules/audit-list.test.cjs b/internal/admin/dashboard/static/js/modules/audit-list.test.cjs index 61ad8129..97030954 100644 --- a/internal/admin/dashboard/static/js/modules/audit-list.test.cjs +++ b/internal/admin/dashboard/static/js/modules/audit-list.test.cjs @@ -34,6 +34,32 @@ function loadConversationHelpers() { return context.window.DashboardConversationHelpers; } +test('auditRetentionText describes finite and indefinite retention', () => { + const module = createAuditListModule(); + + module.workflowRuntimeConfig = { LOGGING_RETENTION_DAYS: '30' }; + assert.equal(module.auditRetentionText(), 'Audit logs are retained for 30 days.'); + + module.workflowRuntimeConfig.LOGGING_RETENTION_DAYS = '1'; + assert.equal(module.auditRetentionText(), 'Audit logs are retained for 1 day.'); + + module.workflowRuntimeConfig.LOGGING_RETENTION_DAYS = '0'; + assert.equal(module.auditRetentionText(), 'Audit logs are retained indefinitely.'); +}); + +test('auditRetentionText hides missing or invalid retention values', () => { + const module = createAuditListModule(); + + module.workflowRuntimeConfig = {}; + assert.equal(module.auditRetentionText(), ''); + + module.workflowRuntimeConfig.LOGGING_RETENTION_DAYS = '-1'; + assert.equal(module.auditRetentionText(), ''); + + module.workflowRuntimeConfig.LOGGING_RETENTION_DAYS = 'unknown'; + assert.equal(module.auditRetentionText(), ''); +}); + test('auditRequestPane returns the shared request-pane contract', () => { const module = createAuditListModule(); const entry = { diff --git a/internal/admin/dashboard/static/js/modules/dashboard-layout.test.cjs b/internal/admin/dashboard/static/js/modules/dashboard-layout.test.cjs index 5f5454de..c5fe94c3 100644 --- a/internal/admin/dashboard/static/js/modules/dashboard-layout.test.cjs +++ b/internal/admin/dashboard/static/js/modules/dashboard-layout.test.cjs @@ -862,6 +862,14 @@ test("audit toolbar uses a full-width search row above the select row with a rig const iconTemplate = readFixture("../../../templates/x-icon.html"); const css = readFixture("../../css/dashboard.css"); + assert.match( + indexTemplate, + /
<\/p>/, + ); + const retentionRule = readCSSRule(css, ".audit-retention-note"); + assert.match(retentionRule, /color:\s*var\(--text-muted\)/); + assert.match(retentionRule, /font-size:\s*13px/); + assert.match( indexTemplate, /