Add themed admin page with user management, audit log, and system info panels; add compliance note delete functionality
This commit is contained in:
@@ -12,6 +12,7 @@ import VulnerabilityTriagePage from './components/pages/ReportingPage';
|
||||
import KnowledgeBasePage from './components/pages/KnowledgeBasePage';
|
||||
import ExportsPage from './components/pages/ExportsPage';
|
||||
import CompliancePage from './components/pages/CompliancePage';
|
||||
import AdminPage from './components/pages/AdminPage';
|
||||
import ArchiveSummaryBar from './components/pages/ArchiveSummaryBar';
|
||||
import './App.css';
|
||||
|
||||
@@ -1012,11 +1013,8 @@ export default function App() {
|
||||
{currentPage === 'compliance' && <CompliancePage onNavigate={setCurrentPage} />}
|
||||
{currentPage === 'knowledge-base' && <KnowledgeBasePage />}
|
||||
{currentPage === 'exports' && <ExportsPage />}
|
||||
{currentPage === 'admin' && isAdmin() && (
|
||||
<div className="space-y-6">
|
||||
<UserManagement onClose={() => setCurrentPage('home')} />
|
||||
</div>
|
||||
)}
|
||||
{currentPage === 'admin' && isAdmin() && <AdminPage />}
|
||||
{currentPage === 'admin' && !isAdmin() && (() => { setCurrentPage('home'); return null; })()}
|
||||
|
||||
{/* User Management Modal */}
|
||||
{showUserManagement && (
|
||||
|
||||
1382
frontend/src/components/pages/AdminPage.js
Normal file
1382
frontend/src/components/pages/AdminPage.js
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
||||
import React, { useState, useEffect, useCallback } from 'react';
|
||||
import { X, MessageSquare, Send, Loader, AlertCircle, Clock, Shield } from 'lucide-react';
|
||||
import { X, MessageSquare, Send, Loader, AlertCircle, Clock, Shield, Trash2 } from 'lucide-react';
|
||||
|
||||
const API_BASE = process.env.REACT_APP_API_BASE || 'http://localhost:3001/api';
|
||||
|
||||
@@ -90,6 +90,22 @@ export default function ComplianceDetailPanel({ hostname, onClose, onNoteAdded,
|
||||
}
|
||||
};
|
||||
|
||||
const handleDeleteNote = async (noteId, hasGroup) => {
|
||||
if (!window.confirm('Delete this note?')) return;
|
||||
try {
|
||||
const url = hasGroup
|
||||
? `${API_BASE}/compliance/notes/${noteId}?group=true`
|
||||
: `${API_BASE}/compliance/notes/${noteId}`;
|
||||
const res = await fetch(url, { method: 'DELETE', credentials: 'include' });
|
||||
const data = await res.json();
|
||||
if (!res.ok) throw new Error(data.error || 'Failed to delete note');
|
||||
await fetchDetail();
|
||||
if (onNoteAdded) onNoteAdded();
|
||||
} catch (err) {
|
||||
setNoteError(err.message);
|
||||
}
|
||||
};
|
||||
|
||||
const activeMetrics = detail?.metrics?.filter(m => m.status === 'active') || [];
|
||||
const resolvedMetrics = detail?.metrics?.filter(m => m.status === 'resolved') || [];
|
||||
|
||||
@@ -227,9 +243,25 @@ export default function ComplianceDetailPanel({ hostname, onClose, onNoteAdded,
|
||||
<MetricChip key={n.id} metricId={n.metric_id} category={metricMap[n.metric_id] || ''} />
|
||||
))}
|
||||
</div>
|
||||
<span style={{ fontSize: '0.68rem', color: '#334155', fontFamily: 'monospace', flexShrink: 0, marginLeft: '0.5rem', whiteSpace: 'nowrap' }}>
|
||||
{g.created_by && `${g.created_by} · `}{g.created_at?.slice(0, 10)}
|
||||
</span>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: '0.4rem', flexShrink: 0, marginLeft: '0.5rem' }}>
|
||||
<span style={{ fontSize: '0.68rem', color: '#334155', fontFamily: 'monospace', whiteSpace: 'nowrap' }}>
|
||||
{g.created_by && `${g.created_by} · `}{g.created_at?.slice(0, 10)}
|
||||
</span>
|
||||
<button
|
||||
onClick={() => handleDeleteNote(g.notes[0].id, !!g.notes[0].group_id)}
|
||||
title="Delete note"
|
||||
style={{
|
||||
background: 'none', border: '1px solid rgba(239,68,68,0.15)',
|
||||
borderRadius: '0.25rem', padding: '0.2rem',
|
||||
cursor: 'pointer', color: '#334155',
|
||||
transition: 'all 0.15s', lineHeight: 1,
|
||||
}}
|
||||
onMouseEnter={e => { e.currentTarget.style.color = '#EF4444'; e.currentTarget.style.borderColor = 'rgba(239,68,68,0.5)'; }}
|
||||
onMouseLeave={e => { e.currentTarget.style.color = '#334155'; e.currentTarget.style.borderColor = 'rgba(239,68,68,0.15)'; }}
|
||||
>
|
||||
<Trash2 style={{ width: '11px', height: '11px' }} />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ fontSize: '0.8rem', color: '#CBD5E1', lineHeight: 1.5 }}>{g.note}</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user