Fix Ivanti panel bugs: Invalid Date, wrong workflow count, crash on archive click, BU scope filtering
This commit is contained in:
@@ -167,7 +167,7 @@ const API_HOST = process.env.REACT_APP_API_HOST || 'http://localhost:3001';
|
||||
const severityLevels = ['All Severities', 'Critical', 'High', 'Medium', 'Low'];
|
||||
|
||||
export default function App() {
|
||||
const { isAuthenticated, loading: authLoading, canWrite, canDelete, canExport, isAdmin } = useAuth();
|
||||
const { isAuthenticated, loading: authLoading, canWrite, canDelete, canExport, isAdmin, getActiveTeamsParam, adminScope } = useAuth();
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
const [selectedVendor, setSelectedVendor] = useState('All Vendors');
|
||||
const [selectedSeverity, setSelectedSeverity] = useState('All Severities');
|
||||
@@ -402,7 +402,11 @@ export default function App() {
|
||||
setArchiveFilter(newFilter);
|
||||
if (newFilter) {
|
||||
setArchiveListLoading(true);
|
||||
fetch(`${API_BASE}/ivanti/archive?state=${newFilter}`, { credentials: 'include' })
|
||||
const teamsParam = getActiveTeamsParam();
|
||||
const url = teamsParam
|
||||
? `${API_BASE}/ivanti/archive?state=${newFilter}&teams=${encodeURIComponent(teamsParam)}`
|
||||
: `${API_BASE}/ivanti/archive?state=${newFilter}`;
|
||||
fetch(url, { credentials: 'include' })
|
||||
.then(res => res.ok ? res.json() : Promise.reject())
|
||||
.then(data => setArchiveList(data.archives || []))
|
||||
.catch(() => setArchiveList([]))
|
||||
@@ -2357,12 +2361,12 @@ export default function App() {
|
||||
{/* Last synced line */}
|
||||
<div className="text-xs text-gray-500 font-mono mb-4">
|
||||
{ivantiSyncedAt
|
||||
? `Synced ${new Date(ivantiSyncedAt.replace(' ', 'T') + 'Z').toLocaleString()}`
|
||||
? `Synced ${new Date(ivantiSyncedAt).toLocaleString()}`
|
||||
: 'Never synced'}
|
||||
</div>
|
||||
|
||||
{/* Archive Summary Bar */}
|
||||
<ArchiveSummaryBar onStateClick={handleArchiveStateClick} activeFilter={archiveFilter} refreshKey={archiveRefreshKey} />
|
||||
<ArchiveSummaryBar onStateClick={handleArchiveStateClick} activeFilter={archiveFilter} refreshKey={archiveRefreshKey} teamsParam={getActiveTeamsParam()} />
|
||||
|
||||
{/* Archive list — shown when a state card is clicked */}
|
||||
{archiveFilter && (
|
||||
@@ -2408,7 +2412,7 @@ export default function App() {
|
||||
</div>
|
||||
</div>
|
||||
<span style={{ fontFamily: 'monospace', fontSize: '0.55rem', padding: '0.15rem 0.35rem', borderRadius: '0.25rem', background: 'rgba(100, 116, 139, 0.2)', border: '1px solid rgba(100, 116, 139, 0.4)', color: '#94A3B8', whiteSpace: 'nowrap' }}>
|
||||
Last seen: {(a.last_severity && a.last_severity !== 0) ? a.last_severity.toFixed(1) : '—'}
|
||||
Last seen: {(a.last_severity && Number(a.last_severity) !== 0) ? Number(a.last_severity).toFixed(1) : '—'}
|
||||
</span>
|
||||
</div>
|
||||
<div style={{ fontFamily: 'monospace', fontSize: '0.65rem', color: '#64748B', marginLeft: '1.375rem' }}>
|
||||
@@ -2416,7 +2420,7 @@ export default function App() {
|
||||
</div>
|
||||
{a.related_active && (
|
||||
<div style={{ fontFamily: 'monospace', fontSize: '0.6rem', color: '#0EA5E9', marginTop: '0.35rem', marginLeft: '1.375rem', padding: '0.2rem 0.4rem', background: 'rgba(14, 165, 233, 0.1)', border: '1px solid rgba(14, 165, 233, 0.25)', borderRadius: '0.25rem', display: 'inline-block' }}>
|
||||
Similar finding active — ID: {a.related_active.id} ({a.related_active.severity?.toFixed(1) ?? '—'})
|
||||
Similar finding active — ID: {a.related_active.id} ({a.related_active.severity ? Number(a.related_active.severity).toFixed(1) : '—'})
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -121,7 +121,7 @@ function hexToRgb(hex) {
|
||||
return `${r}, ${g}, ${b}`;
|
||||
}
|
||||
|
||||
export default function ArchiveSummaryBar({ onStateClick, activeFilter, refreshKey }) {
|
||||
export default function ArchiveSummaryBar({ onStateClick, activeFilter, refreshKey, teamsParam }) {
|
||||
const [stats, setStats] = useState(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState(false);
|
||||
@@ -132,7 +132,10 @@ export default function ArchiveSummaryBar({ onStateClick, activeFilter, refreshK
|
||||
setLoading(true);
|
||||
setError(false);
|
||||
try {
|
||||
const res = await fetch(`${API_BASE}/ivanti/archive/stats`, { credentials: 'include' });
|
||||
const url = teamsParam
|
||||
? `${API_BASE}/ivanti/archive/stats?teams=${encodeURIComponent(teamsParam)}`
|
||||
: `${API_BASE}/ivanti/archive/stats`;
|
||||
const res = await fetch(url, { credentials: 'include' });
|
||||
if (res.ok && !cancelled) {
|
||||
const data = await res.json();
|
||||
setStats(data);
|
||||
@@ -150,7 +153,7 @@ export default function ArchiveSummaryBar({ onStateClick, activeFilter, refreshK
|
||||
// Re-fetch every 60s so stats stay reasonably fresh after syncs
|
||||
const interval = setInterval(load, 60000);
|
||||
return () => { cancelled = true; clearInterval(interval); };
|
||||
}, [refreshKey]);
|
||||
}, [refreshKey, teamsParam]);
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
|
||||
@@ -4109,8 +4109,13 @@ function FpEditModal({ open, onClose, submission, queueItems, onSuccess }) {
|
||||
)) : null;
|
||||
})()}
|
||||
{history.length === 0 ? (
|
||||
<div style={{ fontFamily: 'monospace', fontSize: '0.72rem', color: '#475569', textAlign: 'center', padding: '2rem 0' }}>
|
||||
No history entries.
|
||||
<div style={{ fontFamily: 'monospace', fontSize: '0.72rem', color: '#64748B', textAlign: 'center', padding: '2rem 0' }}>
|
||||
{/* ⚠️ CONVENTION: Use lucide-react icons instead of raw HTML entities/emoji */}
|
||||
<div style={{ fontSize: '1.5rem', marginBottom: '0.5rem', opacity: 0.4 }}>📋</div>
|
||||
No history entries yet.
|
||||
<div style={{ fontSize: '0.65rem', color: '#475569', marginTop: '0.35rem' }}>
|
||||
Changes to this submission will appear here.
|
||||
</div>
|
||||
</div>
|
||||
) : history.map((entry, idx) => {
|
||||
const details = (() => {
|
||||
@@ -5773,7 +5778,7 @@ export default function VulnerabilityTriagePage({ filterDate, filterEXC }) {
|
||||
}, [buildExportRows]);
|
||||
|
||||
const syncedDisplay = syncedAt
|
||||
? `Synced ${new Date(syncedAt.replace(' ', 'T') + 'Z').toLocaleString()}`
|
||||
? `Synced ${new Date(syncedAt).toLocaleString()}`
|
||||
: 'Never synced';
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user