@agenticmail/enterprise 0.5.280 → 0.5.281

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -175,9 +175,12 @@ export function DatabaseAccessPage() {
175
175
 
176
176
  // Tabs
177
177
  h('div', { style: s.tabs },
178
- h('div', { style: tab === 'connections' ? s.tabActive : s.tab, onClick: function() { setTab('connections'); } }, 'Connections'),
179
- h('div', { style: tab === 'agents' ? s.tabActive : s.tab, onClick: function() { setTab('agents'); } }, 'Agent Access'),
180
- h('div', { style: tab === 'audit' ? s.tabActive : s.tab, onClick: function() { setTab('audit'); } }, 'Audit Log'),
178
+ h('div', { style: tab === 'connections' ? s.tabActive : s.tab, onClick: function() { setTab('connections'); } },
179
+ h('span', { style: css('display: inline-flex; align-items: center; gap: 6px;') }, h('span', { style: css('display: flex; transform: scale(0.7);') }, I.database()), 'Connections')),
180
+ h('div', { style: tab === 'agents' ? s.tabActive : s.tab, onClick: function() { setTab('agents'); } },
181
+ h('span', { style: css('display: inline-flex; align-items: center; gap: 6px;') }, h('span', { style: css('display: flex; transform: scale(0.7);') }, I.shield()), 'Agent Access')),
182
+ h('div', { style: tab === 'audit' ? s.tabActive : s.tab, onClick: function() { setTab('audit'); } },
183
+ h('span', { style: css('display: inline-flex; align-items: center; gap: 6px;') }, h('span', { style: css('display: flex; transform: scale(0.7);') }, I.audit()), 'Audit Log')),
181
184
  ),
182
185
 
183
186
  // Content
@@ -305,7 +308,45 @@ function AgentAccessTab(props) {
305
308
 
306
309
  // ─── Audit Tab ───────────────────────────────────────────────────────────────
307
310
 
311
+ var AUDIT_PAGE_SIZE = 15;
312
+
308
313
  function AuditTab(props) {
314
+ var [search, setSearch] = useState('');
315
+ var [opFilter, setOpFilter] = useState('all');
316
+ var [statusFilter, setStatusFilter] = useState('all');
317
+ var [agentFilter, setAgentFilter] = useState('all');
318
+ var [page, setPage] = useState(0);
319
+ var [expanded, setExpanded] = useState(null);
320
+
321
+ // Get unique agents from audit log
322
+ var agents = [];
323
+ var agentSet = {};
324
+ props.auditLog.forEach(function(e) {
325
+ var name = e.agent_name || e.agent_id;
326
+ if (name && !agentSet[name]) { agentSet[name] = true; agents.push(name); }
327
+ });
328
+
329
+ // Filter entries
330
+ var filtered = props.auditLog.filter(function(e) {
331
+ if (opFilter !== 'all' && e.operation !== opFilter) return false;
332
+ if (statusFilter === 'ok' && !e.success) return false;
333
+ if (statusFilter === 'fail' && e.success) return false;
334
+ if (agentFilter !== 'all' && (e.agent_name || e.agent_id) !== agentFilter) return false;
335
+ if (search) {
336
+ var q = search.toLowerCase();
337
+ var haystack = ((e.query || '') + ' ' + (e.agent_name || '') + ' ' + (e.connection_name || '') + ' ' + (e.error || '')).toLowerCase();
338
+ if (haystack.indexOf(q) < 0) return false;
339
+ }
340
+ return true;
341
+ });
342
+
343
+ var totalPages = Math.max(1, Math.ceil(filtered.length / AUDIT_PAGE_SIZE));
344
+ if (page >= totalPages) page = totalPages - 1;
345
+ var paged = filtered.slice(page * AUDIT_PAGE_SIZE, (page + 1) * AUDIT_PAGE_SIZE);
346
+
347
+ // Reset page when filters change
348
+ var resetPage = function() { setPage(0); };
349
+
309
350
  if (props.auditLog.length === 0) {
310
351
  return h('div', { style: s.emptyState },
311
352
  h('div', { style: s.emptyIcon }, I.audit()),
@@ -314,37 +355,114 @@ function AuditTab(props) {
314
355
  );
315
356
  }
316
357
 
317
- return h('div', { style: css('overflow-x: auto;') },
318
- h('table', { style: s.auditTable },
319
- h('thead', null, h('tr', null,
320
- h('th', { style: s.auditTh }, 'Time'),
321
- h('th', { style: s.auditTh }, 'Agent'),
322
- h('th', { style: s.auditTh }, 'Database'),
323
- h('th', { style: s.auditTh }, 'Op'),
324
- h('th', { style: s.auditTh }, 'Query'),
325
- h('th', { style: s.auditTh }, 'Rows'),
326
- h('th', { style: s.auditTh }, 'Time'),
327
- h('th', { style: s.auditTh }, 'Status'),
328
- )),
329
- h('tbody', null,
330
- props.auditLog.map(function(entry) {
331
- var opColor = entry.operation === 'read' ? 'var(--success)' : entry.operation === 'write' ? 'var(--warning)' : entry.operation === 'delete' ? 'var(--danger)' : 'var(--text-muted)';
332
- return h('tr', { key: entry.id },
333
- h('td', { style: s.auditTd }, new Date(entry.timestamp).toLocaleString()),
334
- h('td', { style: s.auditTd }, entry.agent_name || entry.agent_id?.slice(0, 8)),
335
- h('td', { style: s.auditTd }, entry.connection_name || entry.connection_id?.slice(0, 8)),
336
- h('td', { style: Object.assign({}, s.auditTd, { fontWeight: 600, color: opColor }) }, entry.operation),
337
- h('td', { style: Object.assign({}, s.auditTd, { fontFamily: 'monospace', fontSize: '11px', maxWidth: '300px', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }) }, entry.query),
338
- h('td', { style: s.auditTd }, entry.rows_affected),
339
- h('td', { style: s.auditTd }, entry.execution_time_ms + 'ms'),
340
- h('td', { style: s.auditTd }, entry.success
341
- ? h('span', { style: Object.assign({}, s.badge, s.badgeActive) }, 'OK')
342
- : h('span', { style: Object.assign({}, s.badge, s.badgeError), title: entry.error }, 'FAIL')
343
- ),
344
- );
345
- })
358
+ var opColor = function(op) {
359
+ return op === 'read' ? 'var(--success)' : op === 'write' ? 'var(--warning)' : op === 'delete' ? 'var(--danger)' : op === 'schema' ? 'var(--accent)' : 'var(--text-muted)';
360
+ };
361
+
362
+ var filterBar = css('display: flex; gap: 8px; margin-bottom: 16px; flex-wrap: wrap; align-items: center;');
363
+ var filterSelect = css('padding: 6px 10px; border-radius: 6px; border: 1px solid var(--border); background: var(--bg-secondary); color: var(--text-primary); font-size: 12px;');
364
+ var searchInput = css('padding: 6px 10px; border-radius: 6px; border: 1px solid var(--border); background: var(--bg-primary); color: var(--text-primary); font-size: 12px; flex: 1; min-width: 180px;');
365
+ var countBadge = css('font-size: 11px; color: var(--text-muted); margin-left: auto; white-space: nowrap;');
366
+
367
+ return h('div', null,
368
+ // Filter bar
369
+ h('div', { style: filterBar },
370
+ h('div', { style: css('display: flex; align-items: center; gap: 4px; color: var(--text-muted);') },
371
+ h('span', { style: css('display: flex; transform: scale(0.65);') }, I.search()),
372
+ ),
373
+ h('input', { style: searchInput, placeholder: 'Search queries, agents, databases, errors...', value: search, onInput: function(e) { setSearch(e.target.value); resetPage(); } }),
374
+ h('select', { style: filterSelect, value: opFilter, onChange: function(e) { setOpFilter(e.target.value); resetPage(); } },
375
+ h('option', { value: 'all' }, 'All Operations'),
376
+ h('option', { value: 'read' }, 'Read'),
377
+ h('option', { value: 'write' }, 'Write'),
378
+ h('option', { value: 'delete' }, 'Delete'),
379
+ h('option', { value: 'schema' }, 'Schema'),
380
+ h('option', { value: 'execute' }, 'Execute'),
381
+ ),
382
+ h('select', { style: filterSelect, value: statusFilter, onChange: function(e) { setStatusFilter(e.target.value); resetPage(); } },
383
+ h('option', { value: 'all' }, 'All Status'),
384
+ h('option', { value: 'ok' }, 'Success'),
385
+ h('option', { value: 'fail' }, 'Failed'),
386
+ ),
387
+ agents.length > 1 && h('select', { style: filterSelect, value: agentFilter, onChange: function(e) { setAgentFilter(e.target.value); resetPage(); } },
388
+ h('option', { value: 'all' }, 'All Agents'),
389
+ agents.map(function(a) { return h('option', { key: a, value: a }, a); })
390
+ ),
391
+ h('span', { style: countBadge }, filtered.length + ' of ' + props.auditLog.length + ' entries'),
392
+ h('button', { style: Object.assign({}, s.btn, { padding: '4px 10px', fontSize: '11px' }), onClick: props.onRefresh }, 'Refresh'),
393
+ ),
394
+
395
+ // Table
396
+ h('div', { style: css('overflow-x: auto; border: 1px solid var(--border); border-radius: 8px;') },
397
+ h('table', { style: s.auditTable },
398
+ h('thead', null, h('tr', null,
399
+ h('th', { style: s.auditTh }, 'Time'),
400
+ h('th', { style: s.auditTh }, 'Agent'),
401
+ h('th', { style: s.auditTh }, 'Database'),
402
+ h('th', { style: s.auditTh }, 'Operation'),
403
+ h('th', { style: s.auditTh }, 'Query'),
404
+ h('th', { style: s.auditTh }, 'Rows'),
405
+ h('th', { style: s.auditTh }, 'Latency'),
406
+ h('th', { style: s.auditTh }, 'Status'),
407
+ )),
408
+ h('tbody', null,
409
+ paged.length === 0 && h('tr', null,
410
+ h('td', { colSpan: 8, style: Object.assign({}, s.auditTd, { textAlign: 'center', color: 'var(--text-muted)', padding: '24px' }) }, 'No entries match your filters')
411
+ ),
412
+ paged.map(function(entry) {
413
+ var isExpanded = expanded === entry.id;
414
+ return h(Fragment, { key: entry.id },
415
+ h('tr', { style: css('cursor: pointer; transition: background 0.1s;'), onClick: function() { setExpanded(isExpanded ? null : entry.id); } },
416
+ h('td', { style: s.auditTd }, new Date(entry.timestamp).toLocaleString()),
417
+ h('td', { style: Object.assign({}, s.auditTd, { fontWeight: 500 }) }, entry.agent_name || (entry.agent_id ? entry.agent_id.slice(0, 8) + '...' : '—')),
418
+ h('td', { style: s.auditTd }, entry.connection_name || (entry.connection_id ? entry.connection_id.slice(0, 8) + '...' : '—')),
419
+ h('td', { style: Object.assign({}, s.auditTd, { fontWeight: 600, color: opColor(entry.operation) }) }, entry.operation),
420
+ h('td', { style: Object.assign({}, s.auditTd, { fontFamily: 'monospace', fontSize: '11px', maxWidth: '300px', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }) }, entry.query),
421
+ h('td', { style: Object.assign({}, s.auditTd, { textAlign: 'right' }) }, entry.rows_affected != null ? entry.rows_affected : '—'),
422
+ h('td', { style: Object.assign({}, s.auditTd, { textAlign: 'right', whiteSpace: 'nowrap' }) }, entry.execution_time_ms != null ? entry.execution_time_ms + 'ms' : '—'),
423
+ h('td', { style: s.auditTd }, entry.success
424
+ ? h('span', { style: Object.assign({}, s.badge, s.badgeActive) }, 'OK')
425
+ : h('span', { style: Object.assign({}, s.badge, s.badgeError) }, 'FAIL')
426
+ ),
427
+ ),
428
+ // Expanded row detail
429
+ isExpanded && h('tr', null,
430
+ h('td', { colSpan: 8, style: css('padding: 12px 16px; background: var(--bg-secondary); border-bottom: 1px solid var(--border);') },
431
+ h('div', { style: css('display: grid; grid-template-columns: 1fr 1fr; gap: 12px; font-size: 12px;') },
432
+ h('div', null,
433
+ h('div', { style: css('font-weight: 600; margin-bottom: 4px; color: var(--text-muted);') }, 'Full Query'),
434
+ h('pre', { style: css('margin: 0; padding: 8px; background: var(--bg-primary); border-radius: 6px; overflow-x: auto; font-size: 11px; white-space: pre-wrap; word-break: break-all; max-height: 200px;') }, entry.query || '—'),
435
+ ),
436
+ h('div', null,
437
+ h('div', { style: css('font-weight: 600; margin-bottom: 4px; color: var(--text-muted);') }, 'Details'),
438
+ h('div', { style: css('display: flex; flex-direction: column; gap: 4px;') },
439
+ h('div', null, h('strong', null, 'Agent ID: '), entry.agent_id || '—'),
440
+ h('div', null, h('strong', null, 'Connection ID: '), entry.connection_id || '—'),
441
+ h('div', null, h('strong', null, 'Rows Affected: '), entry.rows_affected != null ? String(entry.rows_affected) : '—'),
442
+ h('div', null, h('strong', null, 'Execution Time: '), entry.execution_time_ms != null ? entry.execution_time_ms + 'ms' : '—'),
443
+ h('div', null, h('strong', null, 'IP: '), entry.ip_address || '—'),
444
+ !entry.success && entry.error && h('div', { style: css('margin-top: 4px; padding: 6px 8px; background: rgba(239,68,68,0.1); border-radius: 4px; color: var(--danger);') },
445
+ h('strong', null, 'Error: '), entry.error
446
+ ),
447
+ ),
448
+ ),
449
+ ),
450
+ ),
451
+ ),
452
+ );
453
+ })
454
+ ),
346
455
  ),
347
456
  ),
457
+
458
+ // Pagination
459
+ totalPages > 1 && h('div', { style: css('display: flex; align-items: center; justify-content: center; gap: 8px; margin-top: 16px;') },
460
+ h('button', { style: Object.assign({}, s.btn, { padding: '4px 10px', fontSize: '12px' }), disabled: page === 0, onClick: function() { setPage(0); } }, '«'),
461
+ h('button', { style: Object.assign({}, s.btn, { padding: '4px 10px', fontSize: '12px' }), disabled: page === 0, onClick: function() { setPage(page - 1); } }, '‹'),
462
+ h('span', { style: css('font-size: 12px; color: var(--text-secondary);') }, 'Page ' + (page + 1) + ' of ' + totalPages),
463
+ h('button', { style: Object.assign({}, s.btn, { padding: '4px 10px', fontSize: '12px' }), disabled: page >= totalPages - 1, onClick: function() { setPage(page + 1); } }, '›'),
464
+ h('button', { style: Object.assign({}, s.btn, { padding: '4px 10px', fontSize: '12px' }), disabled: page >= totalPages - 1, onClick: function() { setPage(totalPages - 1); } }, '»'),
465
+ ),
348
466
  );
349
467
  }
350
468
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agenticmail/enterprise",
3
- "version": "0.5.280",
3
+ "version": "0.5.281",
4
4
  "description": "AgenticMail Enterprise — cloud-hosted AI agent identity, email, auth & compliance for organizations",
5
5
  "type": "module",
6
6
  "bin": {
@@ -175,9 +175,12 @@ export function DatabaseAccessPage() {
175
175
 
176
176
  // Tabs
177
177
  h('div', { style: s.tabs },
178
- h('div', { style: tab === 'connections' ? s.tabActive : s.tab, onClick: function() { setTab('connections'); } }, 'Connections'),
179
- h('div', { style: tab === 'agents' ? s.tabActive : s.tab, onClick: function() { setTab('agents'); } }, 'Agent Access'),
180
- h('div', { style: tab === 'audit' ? s.tabActive : s.tab, onClick: function() { setTab('audit'); } }, 'Audit Log'),
178
+ h('div', { style: tab === 'connections' ? s.tabActive : s.tab, onClick: function() { setTab('connections'); } },
179
+ h('span', { style: css('display: inline-flex; align-items: center; gap: 6px;') }, h('span', { style: css('display: flex; transform: scale(0.7);') }, I.database()), 'Connections')),
180
+ h('div', { style: tab === 'agents' ? s.tabActive : s.tab, onClick: function() { setTab('agents'); } },
181
+ h('span', { style: css('display: inline-flex; align-items: center; gap: 6px;') }, h('span', { style: css('display: flex; transform: scale(0.7);') }, I.shield()), 'Agent Access')),
182
+ h('div', { style: tab === 'audit' ? s.tabActive : s.tab, onClick: function() { setTab('audit'); } },
183
+ h('span', { style: css('display: inline-flex; align-items: center; gap: 6px;') }, h('span', { style: css('display: flex; transform: scale(0.7);') }, I.audit()), 'Audit Log')),
181
184
  ),
182
185
 
183
186
  // Content
@@ -305,7 +308,45 @@ function AgentAccessTab(props) {
305
308
 
306
309
  // ─── Audit Tab ───────────────────────────────────────────────────────────────
307
310
 
311
+ var AUDIT_PAGE_SIZE = 15;
312
+
308
313
  function AuditTab(props) {
314
+ var [search, setSearch] = useState('');
315
+ var [opFilter, setOpFilter] = useState('all');
316
+ var [statusFilter, setStatusFilter] = useState('all');
317
+ var [agentFilter, setAgentFilter] = useState('all');
318
+ var [page, setPage] = useState(0);
319
+ var [expanded, setExpanded] = useState(null);
320
+
321
+ // Get unique agents from audit log
322
+ var agents = [];
323
+ var agentSet = {};
324
+ props.auditLog.forEach(function(e) {
325
+ var name = e.agent_name || e.agent_id;
326
+ if (name && !agentSet[name]) { agentSet[name] = true; agents.push(name); }
327
+ });
328
+
329
+ // Filter entries
330
+ var filtered = props.auditLog.filter(function(e) {
331
+ if (opFilter !== 'all' && e.operation !== opFilter) return false;
332
+ if (statusFilter === 'ok' && !e.success) return false;
333
+ if (statusFilter === 'fail' && e.success) return false;
334
+ if (agentFilter !== 'all' && (e.agent_name || e.agent_id) !== agentFilter) return false;
335
+ if (search) {
336
+ var q = search.toLowerCase();
337
+ var haystack = ((e.query || '') + ' ' + (e.agent_name || '') + ' ' + (e.connection_name || '') + ' ' + (e.error || '')).toLowerCase();
338
+ if (haystack.indexOf(q) < 0) return false;
339
+ }
340
+ return true;
341
+ });
342
+
343
+ var totalPages = Math.max(1, Math.ceil(filtered.length / AUDIT_PAGE_SIZE));
344
+ if (page >= totalPages) page = totalPages - 1;
345
+ var paged = filtered.slice(page * AUDIT_PAGE_SIZE, (page + 1) * AUDIT_PAGE_SIZE);
346
+
347
+ // Reset page when filters change
348
+ var resetPage = function() { setPage(0); };
349
+
309
350
  if (props.auditLog.length === 0) {
310
351
  return h('div', { style: s.emptyState },
311
352
  h('div', { style: s.emptyIcon }, I.audit()),
@@ -314,37 +355,114 @@ function AuditTab(props) {
314
355
  );
315
356
  }
316
357
 
317
- return h('div', { style: css('overflow-x: auto;') },
318
- h('table', { style: s.auditTable },
319
- h('thead', null, h('tr', null,
320
- h('th', { style: s.auditTh }, 'Time'),
321
- h('th', { style: s.auditTh }, 'Agent'),
322
- h('th', { style: s.auditTh }, 'Database'),
323
- h('th', { style: s.auditTh }, 'Op'),
324
- h('th', { style: s.auditTh }, 'Query'),
325
- h('th', { style: s.auditTh }, 'Rows'),
326
- h('th', { style: s.auditTh }, 'Time'),
327
- h('th', { style: s.auditTh }, 'Status'),
328
- )),
329
- h('tbody', null,
330
- props.auditLog.map(function(entry) {
331
- var opColor = entry.operation === 'read' ? 'var(--success)' : entry.operation === 'write' ? 'var(--warning)' : entry.operation === 'delete' ? 'var(--danger)' : 'var(--text-muted)';
332
- return h('tr', { key: entry.id },
333
- h('td', { style: s.auditTd }, new Date(entry.timestamp).toLocaleString()),
334
- h('td', { style: s.auditTd }, entry.agent_name || entry.agent_id?.slice(0, 8)),
335
- h('td', { style: s.auditTd }, entry.connection_name || entry.connection_id?.slice(0, 8)),
336
- h('td', { style: Object.assign({}, s.auditTd, { fontWeight: 600, color: opColor }) }, entry.operation),
337
- h('td', { style: Object.assign({}, s.auditTd, { fontFamily: 'monospace', fontSize: '11px', maxWidth: '300px', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }) }, entry.query),
338
- h('td', { style: s.auditTd }, entry.rows_affected),
339
- h('td', { style: s.auditTd }, entry.execution_time_ms + 'ms'),
340
- h('td', { style: s.auditTd }, entry.success
341
- ? h('span', { style: Object.assign({}, s.badge, s.badgeActive) }, 'OK')
342
- : h('span', { style: Object.assign({}, s.badge, s.badgeError), title: entry.error }, 'FAIL')
343
- ),
344
- );
345
- })
358
+ var opColor = function(op) {
359
+ return op === 'read' ? 'var(--success)' : op === 'write' ? 'var(--warning)' : op === 'delete' ? 'var(--danger)' : op === 'schema' ? 'var(--accent)' : 'var(--text-muted)';
360
+ };
361
+
362
+ var filterBar = css('display: flex; gap: 8px; margin-bottom: 16px; flex-wrap: wrap; align-items: center;');
363
+ var filterSelect = css('padding: 6px 10px; border-radius: 6px; border: 1px solid var(--border); background: var(--bg-secondary); color: var(--text-primary); font-size: 12px;');
364
+ var searchInput = css('padding: 6px 10px; border-radius: 6px; border: 1px solid var(--border); background: var(--bg-primary); color: var(--text-primary); font-size: 12px; flex: 1; min-width: 180px;');
365
+ var countBadge = css('font-size: 11px; color: var(--text-muted); margin-left: auto; white-space: nowrap;');
366
+
367
+ return h('div', null,
368
+ // Filter bar
369
+ h('div', { style: filterBar },
370
+ h('div', { style: css('display: flex; align-items: center; gap: 4px; color: var(--text-muted);') },
371
+ h('span', { style: css('display: flex; transform: scale(0.65);') }, I.search()),
372
+ ),
373
+ h('input', { style: searchInput, placeholder: 'Search queries, agents, databases, errors...', value: search, onInput: function(e) { setSearch(e.target.value); resetPage(); } }),
374
+ h('select', { style: filterSelect, value: opFilter, onChange: function(e) { setOpFilter(e.target.value); resetPage(); } },
375
+ h('option', { value: 'all' }, 'All Operations'),
376
+ h('option', { value: 'read' }, 'Read'),
377
+ h('option', { value: 'write' }, 'Write'),
378
+ h('option', { value: 'delete' }, 'Delete'),
379
+ h('option', { value: 'schema' }, 'Schema'),
380
+ h('option', { value: 'execute' }, 'Execute'),
381
+ ),
382
+ h('select', { style: filterSelect, value: statusFilter, onChange: function(e) { setStatusFilter(e.target.value); resetPage(); } },
383
+ h('option', { value: 'all' }, 'All Status'),
384
+ h('option', { value: 'ok' }, 'Success'),
385
+ h('option', { value: 'fail' }, 'Failed'),
386
+ ),
387
+ agents.length > 1 && h('select', { style: filterSelect, value: agentFilter, onChange: function(e) { setAgentFilter(e.target.value); resetPage(); } },
388
+ h('option', { value: 'all' }, 'All Agents'),
389
+ agents.map(function(a) { return h('option', { key: a, value: a }, a); })
390
+ ),
391
+ h('span', { style: countBadge }, filtered.length + ' of ' + props.auditLog.length + ' entries'),
392
+ h('button', { style: Object.assign({}, s.btn, { padding: '4px 10px', fontSize: '11px' }), onClick: props.onRefresh }, 'Refresh'),
393
+ ),
394
+
395
+ // Table
396
+ h('div', { style: css('overflow-x: auto; border: 1px solid var(--border); border-radius: 8px;') },
397
+ h('table', { style: s.auditTable },
398
+ h('thead', null, h('tr', null,
399
+ h('th', { style: s.auditTh }, 'Time'),
400
+ h('th', { style: s.auditTh }, 'Agent'),
401
+ h('th', { style: s.auditTh }, 'Database'),
402
+ h('th', { style: s.auditTh }, 'Operation'),
403
+ h('th', { style: s.auditTh }, 'Query'),
404
+ h('th', { style: s.auditTh }, 'Rows'),
405
+ h('th', { style: s.auditTh }, 'Latency'),
406
+ h('th', { style: s.auditTh }, 'Status'),
407
+ )),
408
+ h('tbody', null,
409
+ paged.length === 0 && h('tr', null,
410
+ h('td', { colSpan: 8, style: Object.assign({}, s.auditTd, { textAlign: 'center', color: 'var(--text-muted)', padding: '24px' }) }, 'No entries match your filters')
411
+ ),
412
+ paged.map(function(entry) {
413
+ var isExpanded = expanded === entry.id;
414
+ return h(Fragment, { key: entry.id },
415
+ h('tr', { style: css('cursor: pointer; transition: background 0.1s;'), onClick: function() { setExpanded(isExpanded ? null : entry.id); } },
416
+ h('td', { style: s.auditTd }, new Date(entry.timestamp).toLocaleString()),
417
+ h('td', { style: Object.assign({}, s.auditTd, { fontWeight: 500 }) }, entry.agent_name || (entry.agent_id ? entry.agent_id.slice(0, 8) + '...' : '—')),
418
+ h('td', { style: s.auditTd }, entry.connection_name || (entry.connection_id ? entry.connection_id.slice(0, 8) + '...' : '—')),
419
+ h('td', { style: Object.assign({}, s.auditTd, { fontWeight: 600, color: opColor(entry.operation) }) }, entry.operation),
420
+ h('td', { style: Object.assign({}, s.auditTd, { fontFamily: 'monospace', fontSize: '11px', maxWidth: '300px', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }) }, entry.query),
421
+ h('td', { style: Object.assign({}, s.auditTd, { textAlign: 'right' }) }, entry.rows_affected != null ? entry.rows_affected : '—'),
422
+ h('td', { style: Object.assign({}, s.auditTd, { textAlign: 'right', whiteSpace: 'nowrap' }) }, entry.execution_time_ms != null ? entry.execution_time_ms + 'ms' : '—'),
423
+ h('td', { style: s.auditTd }, entry.success
424
+ ? h('span', { style: Object.assign({}, s.badge, s.badgeActive) }, 'OK')
425
+ : h('span', { style: Object.assign({}, s.badge, s.badgeError) }, 'FAIL')
426
+ ),
427
+ ),
428
+ // Expanded row detail
429
+ isExpanded && h('tr', null,
430
+ h('td', { colSpan: 8, style: css('padding: 12px 16px; background: var(--bg-secondary); border-bottom: 1px solid var(--border);') },
431
+ h('div', { style: css('display: grid; grid-template-columns: 1fr 1fr; gap: 12px; font-size: 12px;') },
432
+ h('div', null,
433
+ h('div', { style: css('font-weight: 600; margin-bottom: 4px; color: var(--text-muted);') }, 'Full Query'),
434
+ h('pre', { style: css('margin: 0; padding: 8px; background: var(--bg-primary); border-radius: 6px; overflow-x: auto; font-size: 11px; white-space: pre-wrap; word-break: break-all; max-height: 200px;') }, entry.query || '—'),
435
+ ),
436
+ h('div', null,
437
+ h('div', { style: css('font-weight: 600; margin-bottom: 4px; color: var(--text-muted);') }, 'Details'),
438
+ h('div', { style: css('display: flex; flex-direction: column; gap: 4px;') },
439
+ h('div', null, h('strong', null, 'Agent ID: '), entry.agent_id || '—'),
440
+ h('div', null, h('strong', null, 'Connection ID: '), entry.connection_id || '—'),
441
+ h('div', null, h('strong', null, 'Rows Affected: '), entry.rows_affected != null ? String(entry.rows_affected) : '—'),
442
+ h('div', null, h('strong', null, 'Execution Time: '), entry.execution_time_ms != null ? entry.execution_time_ms + 'ms' : '—'),
443
+ h('div', null, h('strong', null, 'IP: '), entry.ip_address || '—'),
444
+ !entry.success && entry.error && h('div', { style: css('margin-top: 4px; padding: 6px 8px; background: rgba(239,68,68,0.1); border-radius: 4px; color: var(--danger);') },
445
+ h('strong', null, 'Error: '), entry.error
446
+ ),
447
+ ),
448
+ ),
449
+ ),
450
+ ),
451
+ ),
452
+ );
453
+ })
454
+ ),
346
455
  ),
347
456
  ),
457
+
458
+ // Pagination
459
+ totalPages > 1 && h('div', { style: css('display: flex; align-items: center; justify-content: center; gap: 8px; margin-top: 16px;') },
460
+ h('button', { style: Object.assign({}, s.btn, { padding: '4px 10px', fontSize: '12px' }), disabled: page === 0, onClick: function() { setPage(0); } }, '«'),
461
+ h('button', { style: Object.assign({}, s.btn, { padding: '4px 10px', fontSize: '12px' }), disabled: page === 0, onClick: function() { setPage(page - 1); } }, '‹'),
462
+ h('span', { style: css('font-size: 12px; color: var(--text-secondary);') }, 'Page ' + (page + 1) + ' of ' + totalPages),
463
+ h('button', { style: Object.assign({}, s.btn, { padding: '4px 10px', fontSize: '12px' }), disabled: page >= totalPages - 1, onClick: function() { setPage(page + 1); } }, '›'),
464
+ h('button', { style: Object.assign({}, s.btn, { padding: '4px 10px', fontSize: '12px' }), disabled: page >= totalPages - 1, onClick: function() { setPage(totalPages - 1); } }, '»'),
465
+ ),
348
466
  );
349
467
  }
350
468