Refactor home page: extract components, add toast system, debounce search
Major restructuring of the monolithic App.js (2484 lines) into focused,
testable components:
Architecture:
- App.js is now a 189-line routing shell (header, nav, page switching)
- HomePage.js orchestrates all home page state and layout
- Each visual section is its own component with clear props API
Extracted components:
- StatsBar: clickable stat cards that filter by severity
- QuickCVELookup: CVE existence check with inline results
- CVEFilters: search + vendor/severity dropdowns
- CVECard: expandable CVE with vendor entries, docs, tickets
- OpenTicketsPanel: right sidebar open JIRA tickets
- IvantiWorkflowPanel: right sidebar Ivanti workflow status + archive
Extracted modals:
- AddCVEModal: self-contained add form with NVD auto-fill
- EditCVEModal: self-contained edit form with NVD update
- JiraTicketModal: unified add/edit JIRA ticket modal
- ArcherTicketModal: unified add/edit Archer ticket modal
Performance optimizations:
- Debounced search (300ms) via useDebounce hook — eliminates
redundant API calls on every keystroke
- Memoized groupedCVEs, openTicketCount, criticalCount via useMemo
- Proper state updates (no direct mutation of cveDocuments)
- useCallback on fetch functions to stabilize effect dependencies
UX improvements:
- Toast notification system replaces all alert() calls
- Stat cards are now clickable to filter CVE list by severity
- onKeyDown replaces deprecated onKeyPress
- aria-labels added to interactive elements
Infrastructure:
- ToastContext with auto-dismiss, typed toasts (success/error/warning/info)
- useDebounce custom hook for reuse across the app
- Toast slide-in animation in App.css
2026-06-23 11:46:39 -06:00
|
|
|
import React, { useState } from 'react';
|
|
|
|
|
import { CheckCircle, XCircle, AlertCircle } from 'lucide-react';
|
|
|
|
|
import { useToast } from '../contexts/ToastContext';
|
|
|
|
|
|
|
|
|
|
const API_BASE = process.env.REACT_APP_API_BASE || 'http://localhost:3001/api';
|
|
|
|
|
|
|
|
|
|
export default function QuickCVELookup() {
|
|
|
|
|
const [query, setQuery] = useState('');
|
|
|
|
|
const [result, setResult] = useState(null);
|
|
|
|
|
const [loading, setLoading] = useState(false);
|
|
|
|
|
const toast = useToast();
|
|
|
|
|
|
|
|
|
|
const handleLookup = async () => {
|
|
|
|
|
const trimmed = query.trim();
|
|
|
|
|
if (!trimmed) return;
|
|
|
|
|
|
|
|
|
|
setLoading(true);
|
|
|
|
|
try {
|
|
|
|
|
const response = await fetch(`${API_BASE}/cves/check/${encodeURIComponent(trimmed)}`, {
|
|
|
|
|
credentials: 'include'
|
|
|
|
|
});
|
|
|
|
|
if (!response.ok) throw new Error('Failed to check CVE');
|
|
|
|
|
const data = await response.json();
|
|
|
|
|
setResult(data);
|
|
|
|
|
} catch (err) {
|
|
|
|
|
toast.error(err.message);
|
|
|
|
|
setResult({ error: err.message });
|
|
|
|
|
} finally {
|
|
|
|
|
setLoading(false);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return (
|
Extract inline styles to CSS classes
Move JavaScript style objects from home page components into reusable
CSS classes in App.css. This follows the existing pattern (intel-button,
intel-card, intel-input) and consolidates all visual styling in one place.
New CSS classes added:
- .panel-card (--accent, --warning, --teal) — sidebar panels
- .section-heading (--accent, --warning, --teal) — monospace headings
- .stat-card modifiers (--clickable, --active, --warning, --danger)
- .stat-card__label / .stat-card__value (--accent, --neutral, etc.)
- .severity-badge (--critical, --high, --medium, --low)
- .glow-dot (--critical, --high, --medium, --low)
- .sidebar-ticket — compact ticket cards
- .workflow-item — Ivanti workflow entries
- .workflow-state-badge — teal state pill
- .ticket-status-badge — small status indicator
- .archive-item (--active, --resolved) — finding archive entries
- .big-counter (--warning, --teal) — large centered stat numbers
Benefits:
- 578 fewer lines of JavaScript across components
- Styles are browser-cached separately from JS bundle
- Single source of truth for the design system
- Easier to update colors/spacing project-wide
2026-06-23 11:58:44 -06:00
|
|
|
<div className="panel-card">
|
Refactor home page: extract components, add toast system, debounce search
Major restructuring of the monolithic App.js (2484 lines) into focused,
testable components:
Architecture:
- App.js is now a 189-line routing shell (header, nav, page switching)
- HomePage.js orchestrates all home page state and layout
- Each visual section is its own component with clear props API
Extracted components:
- StatsBar: clickable stat cards that filter by severity
- QuickCVELookup: CVE existence check with inline results
- CVEFilters: search + vendor/severity dropdowns
- CVECard: expandable CVE with vendor entries, docs, tickets
- OpenTicketsPanel: right sidebar open JIRA tickets
- IvantiWorkflowPanel: right sidebar Ivanti workflow status + archive
Extracted modals:
- AddCVEModal: self-contained add form with NVD auto-fill
- EditCVEModal: self-contained edit form with NVD update
- JiraTicketModal: unified add/edit JIRA ticket modal
- ArcherTicketModal: unified add/edit Archer ticket modal
Performance optimizations:
- Debounced search (300ms) via useDebounce hook — eliminates
redundant API calls on every keystroke
- Memoized groupedCVEs, openTicketCount, criticalCount via useMemo
- Proper state updates (no direct mutation of cveDocuments)
- useCallback on fetch functions to stabilize effect dependencies
UX improvements:
- Toast notification system replaces all alert() calls
- Stat cards are now clickable to filter CVE list by severity
- onKeyDown replaces deprecated onKeyPress
- aria-labels added to interactive elements
Infrastructure:
- ToastContext with auto-dismiss, typed toasts (success/error/warning/info)
- useDebounce custom hook for reuse across the app
- Toast slide-in animation in App.css
2026-06-23 11:46:39 -06:00
|
|
|
<div className="scan-line"></div>
|
Extract inline styles to CSS classes
Move JavaScript style objects from home page components into reusable
CSS classes in App.css. This follows the existing pattern (intel-button,
intel-card, intel-input) and consolidates all visual styling in one place.
New CSS classes added:
- .panel-card (--accent, --warning, --teal) — sidebar panels
- .section-heading (--accent, --warning, --teal) — monospace headings
- .stat-card modifiers (--clickable, --active, --warning, --danger)
- .stat-card__label / .stat-card__value (--accent, --neutral, etc.)
- .severity-badge (--critical, --high, --medium, --low)
- .glow-dot (--critical, --high, --medium, --low)
- .sidebar-ticket — compact ticket cards
- .workflow-item — Ivanti workflow entries
- .workflow-state-badge — teal state pill
- .ticket-status-badge — small status indicator
- .archive-item (--active, --resolved) — finding archive entries
- .big-counter (--warning, --teal) — large centered stat numbers
Benefits:
- 578 fewer lines of JavaScript across components
- Styles are browser-cached separately from JS bundle
- Single source of truth for the design system
- Easier to update colors/spacing project-wide
2026-06-23 11:58:44 -06:00
|
|
|
<h2 className="section-heading section-heading--accent" style={{ marginBottom: '0.75rem' }}>
|
Refactor home page: extract components, add toast system, debounce search
Major restructuring of the monolithic App.js (2484 lines) into focused,
testable components:
Architecture:
- App.js is now a 189-line routing shell (header, nav, page switching)
- HomePage.js orchestrates all home page state and layout
- Each visual section is its own component with clear props API
Extracted components:
- StatsBar: clickable stat cards that filter by severity
- QuickCVELookup: CVE existence check with inline results
- CVEFilters: search + vendor/severity dropdowns
- CVECard: expandable CVE with vendor entries, docs, tickets
- OpenTicketsPanel: right sidebar open JIRA tickets
- IvantiWorkflowPanel: right sidebar Ivanti workflow status + archive
Extracted modals:
- AddCVEModal: self-contained add form with NVD auto-fill
- EditCVEModal: self-contained edit form with NVD update
- JiraTicketModal: unified add/edit JIRA ticket modal
- ArcherTicketModal: unified add/edit Archer ticket modal
Performance optimizations:
- Debounced search (300ms) via useDebounce hook — eliminates
redundant API calls on every keystroke
- Memoized groupedCVEs, openTicketCount, criticalCount via useMemo
- Proper state updates (no direct mutation of cveDocuments)
- useCallback on fetch functions to stabilize effect dependencies
UX improvements:
- Toast notification system replaces all alert() calls
- Stat cards are now clickable to filter CVE list by severity
- onKeyDown replaces deprecated onKeyPress
- aria-labels added to interactive elements
Infrastructure:
- ToastContext with auto-dismiss, typed toasts (success/error/warning/info)
- useDebounce custom hook for reuse across the app
- Toast slide-in animation in App.css
2026-06-23 11:46:39 -06:00
|
|
|
Quick CVE Lookup
|
|
|
|
|
</h2>
|
|
|
|
|
<div className="flex gap-3">
|
|
|
|
|
<input
|
|
|
|
|
type="text"
|
|
|
|
|
placeholder="Enter CVE ID (e.g., CVE-2024-1234)"
|
|
|
|
|
value={query}
|
|
|
|
|
onChange={(e) => setQuery(e.target.value)}
|
|
|
|
|
onKeyDown={(e) => e.key === 'Enter' && handleLookup()}
|
|
|
|
|
className="flex-1 intel-input"
|
|
|
|
|
aria-label="CVE ID to look up"
|
|
|
|
|
/>
|
|
|
|
|
<button
|
|
|
|
|
onClick={handleLookup}
|
|
|
|
|
disabled={loading}
|
|
|
|
|
className="intel-button intel-button-primary"
|
|
|
|
|
>
|
|
|
|
|
{loading ? 'Scanning...' : 'Scan'}
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{result && (
|
|
|
|
|
<div className={`mt-4 p-4 rounded border ${result.exists ? 'bg-intel-success/10 border-intel-success/30' : 'bg-intel-warning/10 border-intel-warning/30'}`}>
|
|
|
|
|
{result.error ? (
|
|
|
|
|
<div className="flex items-start gap-3">
|
|
|
|
|
<XCircle className="w-5 h-5 text-intel-danger mt-0.5" />
|
|
|
|
|
<div>
|
|
|
|
|
<p className="font-medium text-intel-danger font-mono">Error</p>
|
|
|
|
|
<p className="text-sm text-gray-300">{result.error}</p>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
) : result.exists ? (
|
|
|
|
|
<div className="flex items-start gap-3">
|
|
|
|
|
<CheckCircle className="w-5 h-5 text-intel-success mt-0.5" />
|
|
|
|
|
<div className="flex-1">
|
|
|
|
|
<p className="font-medium text-intel-success font-mono">
|
|
|
|
|
✓ CVE Addressed ({result.vendors.length} vendor{result.vendors.length > 1 ? 's' : ''})
|
|
|
|
|
</p>
|
|
|
|
|
<div className="mt-3 space-y-3">
|
|
|
|
|
{result.vendors.map((vendorInfo, idx) => (
|
|
|
|
|
<div key={idx} className="p-3 bg-intel-dark/70 rounded border border-intel-accent/30 shadow-lg">
|
|
|
|
|
<p className="font-semibold text-white mb-2 font-sans">{vendorInfo.vendor}</p>
|
|
|
|
|
<div className="grid grid-cols-2 gap-2 text-sm text-gray-300 mb-2 font-mono">
|
|
|
|
|
<p><strong className="text-white">Severity:</strong> {vendorInfo.severity}</p>
|
|
|
|
|
<p><strong className="text-white">Status:</strong> {vendorInfo.status}</p>
|
|
|
|
|
<p><strong className="text-white">Documents:</strong> {vendorInfo.total_documents} attached</p>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
))}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
) : (
|
|
|
|
|
<div className="flex items-start gap-3">
|
|
|
|
|
<AlertCircle className="w-5 h-5 text-intel-warning mt-0.5" />
|
|
|
|
|
<div>
|
|
|
|
|
<p className="font-medium text-intel-warning font-mono">Not Found</p>
|
|
|
|
|
<p className="text-sm text-gray-300">This CVE has not been addressed yet. No entry exists in the database.</p>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|