added panels
This commit is contained in:
@@ -1255,212 +1255,206 @@ export default function App() {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Quick Check */}
|
{/* Three Column Layout */}
|
||||||
<div style={{...STYLES.intelCard, padding: '1.5rem', marginBottom: '1.5rem'}} className="rounded-lg">
|
<div className="grid grid-cols-12 gap-6">
|
||||||
<div className="scan-line"></div>
|
{/* LEFT PANEL - Wiki/Knowledge Base */}
|
||||||
<h2 style={{ fontSize: '1.125rem', fontWeight: '600', color: '#00D9FF', marginBottom: '0.75rem', fontFamily: 'monospace', textTransform: 'uppercase', letterSpacing: '0.1em', textShadow: '0 0 20px rgba(0, 217, 255, 0.5)' }}>Quick CVE Lookup</h2>
|
<div className="col-span-12 lg:col-span-3 space-y-4">
|
||||||
<div className="flex gap-3">
|
<div style={{...STYLES.intelCard, padding: '1.5rem', borderLeft: '4px solid #00FF88'}} className="rounded-lg">
|
||||||
<input
|
<h2 style={{ fontSize: '1.125rem', fontWeight: '600', color: '#00FF88', marginBottom: '1rem', fontFamily: 'monospace', textTransform: 'uppercase', letterSpacing: '0.1em', textShadow: '0 0 15px rgba(0, 255, 136, 0.5)' }}>
|
||||||
type="text"
|
Knowledge Base
|
||||||
placeholder="Enter CVE ID (e.g., CVE-2024-1234)"
|
</h2>
|
||||||
value={quickCheckCVE}
|
|
||||||
onChange={(e) => setQuickCheckCVE(e.target.value)}
|
{/* Wiki/Blog Style Entries */}
|
||||||
onKeyPress={(e) => e.key === 'Enter' && quickCheckCVEStatus()}
|
<div className="space-y-3">
|
||||||
className="flex-1 intel-input"
|
<div style={{ background: 'linear-gradient(135deg, rgba(19, 25, 55, 0.85) 0%, rgba(30, 39, 73, 0.75) 100%)', border: '1px solid rgba(0, 255, 136, 0.3)', borderRadius: '0.375rem', padding: '0.75rem', cursor: 'pointer', transition: 'all 0.2s' }} className="hover:border-intel-success">
|
||||||
/>
|
<h3 className="text-white font-semibold text-sm mb-1 font-mono">CVE Response Procedures</h3>
|
||||||
<button
|
<p className="text-gray-400 text-xs mb-2">Standard operating procedures for vulnerability response and escalation...</p>
|
||||||
onClick={quickCheckCVEStatus}
|
<span className="text-xs text-intel-success font-mono">Last updated: 2024-02-08</span>
|
||||||
className="intel-button intel-button-primary"
|
</div>
|
||||||
>
|
|
||||||
Scan
|
<div style={{ background: 'linear-gradient(135deg, rgba(19, 25, 55, 0.85) 0%, rgba(30, 39, 73, 0.75) 100%)', border: '1px solid rgba(0, 255, 136, 0.3)', borderRadius: '0.375rem', padding: '0.75rem', cursor: 'pointer', transition: 'all 0.2s' }} className="hover:border-intel-success">
|
||||||
</button>
|
<h3 className="text-white font-semibold text-sm mb-1 font-mono">Vendor Contact Matrix</h3>
|
||||||
|
<p className="text-gray-400 text-xs mb-2">Emergency contacts and escalation paths for security vendors...</p>
|
||||||
|
<span className="text-xs text-intel-success font-mono">Last updated: 2024-02-05</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style={{ background: 'linear-gradient(135deg, rgba(19, 25, 55, 0.85) 0%, rgba(30, 39, 73, 0.75) 100%)', border: '1px solid rgba(0, 255, 136, 0.3)', borderRadius: '0.375rem', padding: '0.75rem', cursor: 'pointer', transition: 'all 0.2s' }} className="hover:border-intel-success">
|
||||||
|
<h3 className="text-white font-semibold text-sm mb-1 font-mono">Severity Classification Guide</h3>
|
||||||
|
<p className="text-gray-400 text-xs mb-2">Guidelines for assessing and classifying vulnerability severity levels...</p>
|
||||||
|
<span className="text-xs text-intel-success font-mono">Last updated: 2024-01-28</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style={{ background: 'linear-gradient(135deg, rgba(19, 25, 55, 0.85) 0%, rgba(30, 39, 73, 0.75) 100%)', border: '1px solid rgba(0, 255, 136, 0.3)', borderRadius: '0.375rem', padding: '0.75rem', cursor: 'pointer', transition: 'all 0.2s' }} className="hover:border-intel-success">
|
||||||
|
<h3 className="text-white font-semibold text-sm mb-1 font-mono">Patching Policy</h3>
|
||||||
|
<p className="text-gray-400 text-xs mb-2">Enterprise patch management timelines and approval workflow...</p>
|
||||||
|
<span className="text-xs text-intel-success font-mono">Last updated: 2024-01-15</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style={{ background: 'linear-gradient(135deg, rgba(19, 25, 55, 0.85) 0%, rgba(30, 39, 73, 0.75) 100%)', border: '1px solid rgba(0, 255, 136, 0.3)', borderRadius: '0.375rem', padding: '0.75rem', cursor: 'pointer', transition: 'all 0.2s' }} className="hover:border-intel-success">
|
||||||
|
<h3 className="text-white font-semibold text-sm mb-1 font-mono">Documentation Standards</h3>
|
||||||
|
<p className="text-gray-400 text-xs mb-2">Required documentation for vulnerability tracking and audit compliance...</p>
|
||||||
|
<span className="text-xs text-intel-success font-mono">Last updated: 2024-01-10</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{quickCheckResult && (
|
{/* CENTER PANEL - Main Content */}
|
||||||
<div className={`mt-4 p-4 rounded border ${quickCheckResult.exists ? 'bg-intel-success/10 border-intel-success/30' : 'bg-intel-warning/10 border-intel-warning/30'}`}>
|
<div className="col-span-12 lg:col-span-6 space-y-4">
|
||||||
{quickCheckResult.error ? (
|
{/* Quick Check */}
|
||||||
<div className="flex items-start gap-3">
|
<div style={{...STYLES.intelCard, padding: '1.5rem'}} className="rounded-lg">
|
||||||
<XCircle className="w-5 h-5 text-intel-danger mt-0.5" />
|
<div className="scan-line"></div>
|
||||||
<div>
|
<h2 style={{ fontSize: '1.125rem', fontWeight: '600', color: '#00D9FF', marginBottom: '0.75rem', fontFamily: 'monospace', textTransform: 'uppercase', letterSpacing: '0.1em', textShadow: '0 0 20px rgba(0, 217, 255, 0.5)' }}>Quick CVE Lookup</h2>
|
||||||
<p className="font-medium text-intel-danger font-mono">Error</p>
|
<div className="flex gap-3">
|
||||||
<p className="text-sm text-gray-300">{quickCheckResult.error}</p>
|
<input
|
||||||
</div>
|
type="text"
|
||||||
</div>
|
placeholder="Enter CVE ID (e.g., CVE-2024-1234)"
|
||||||
) : quickCheckResult.exists ? (
|
value={quickCheckCVE}
|
||||||
<div className="flex items-start gap-3">
|
onChange={(e) => setQuickCheckCVE(e.target.value)}
|
||||||
<CheckCircle className="w-5 h-5 text-intel-success mt-0.5" />
|
onKeyPress={(e) => e.key === 'Enter' && quickCheckCVEStatus()}
|
||||||
<div className="flex-1">
|
className="flex-1 intel-input"
|
||||||
<p className="font-medium text-intel-success font-mono">✓ CVE Addressed ({quickCheckResult.vendors.length} vendor{quickCheckResult.vendors.length > 1 ? 's' : ''})</p>
|
/>
|
||||||
<div className="mt-3 space-y-3">
|
|
||||||
{quickCheckResult.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>
|
|
||||||
|
|
||||||
{/* Open Vendor Tickets Dashboard */}
|
|
||||||
{jiraTickets.filter(t => t.status !== 'Closed').length > 0 && (
|
|
||||||
<div style={{...STYLES.intelCard, padding: '1.5rem', marginBottom: '1.5rem', borderLeft: '4px solid #FFB800'}} className="rounded-lg">
|
|
||||||
<div className="flex justify-between items-center mb-4">
|
|
||||||
<h2 style={{ fontSize: '1.125rem', fontWeight: '600', color: '#FFB800', display: 'flex', alignItems: 'center', gap: '0.5rem', fontFamily: 'monospace', textTransform: 'uppercase', letterSpacing: '0.1em', textShadow: '0 0 15px rgba(255, 184, 0, 0.5)' }}>
|
|
||||||
<AlertCircle className="w-5 h-5" />
|
|
||||||
Open Tickets ({jiraTickets.filter(t => t.status !== 'Closed').length})
|
|
||||||
</h2>
|
|
||||||
{canWrite() && (
|
|
||||||
<button
|
<button
|
||||||
onClick={() => { setAddTicketContext(null); setTicketForm({ cve_id: '', vendor: '', ticket_key: '', url: '', summary: '', status: 'Open' }); setShowAddTicket(true); }}
|
onClick={quickCheckCVEStatus}
|
||||||
className="intel-button intel-button-primary flex items-center gap-1"
|
className="intel-button intel-button-primary"
|
||||||
>
|
>
|
||||||
<Plus className="w-4 h-4" />
|
Scan
|
||||||
Add
|
|
||||||
</button>
|
</button>
|
||||||
)}
|
</div>
|
||||||
</div>
|
|
||||||
<div className="space-y-2">
|
{quickCheckResult && (
|
||||||
{jiraTickets.filter(t => t.status !== 'Closed').map(ticket => (
|
<div className={`mt-4 p-4 rounded border ${quickCheckResult.exists ? 'bg-intel-success/10 border-intel-success/30' : 'bg-intel-warning/10 border-intel-warning/30'}`}>
|
||||||
<div key={ticket.id} style={{ background: 'linear-gradient(135deg, rgba(19, 25, 55, 0.85) 0%, rgba(30, 39, 73, 0.75) 100%)', border: '1px solid rgba(255, 184, 0, 0.3)', borderRadius: '0.375rem', padding: '0.75rem', boxShadow: '0 2px 6px rgba(0, 0, 0, 0.25), inset 0 1px 0 rgba(255, 255, 255, 0.04)' }} className="flex items-center justify-between">
|
{quickCheckResult.error ? (
|
||||||
<div className="flex items-center gap-4 flex-1">
|
<div className="flex items-start gap-3">
|
||||||
<a
|
<XCircle className="w-5 h-5 text-intel-danger mt-0.5" />
|
||||||
href={ticket.url || '#'}
|
<div>
|
||||||
target="_blank"
|
<p className="font-medium text-intel-danger font-mono">Error</p>
|
||||||
rel="noopener noreferrer"
|
<p className="text-sm text-gray-300">{quickCheckResult.error}</p>
|
||||||
className="font-mono text-sm font-semibold text-intel-accent hover:text-intel-warning transition-colors"
|
</div>
|
||||||
>
|
</div>
|
||||||
{ticket.ticket_key}
|
) : quickCheckResult.exists ? (
|
||||||
</a>
|
<div className="flex items-start gap-3">
|
||||||
<span className="text-sm text-white font-mono">{ticket.cve_id}</span>
|
<CheckCircle className="w-5 h-5 text-intel-success mt-0.5" />
|
||||||
<span style={{ fontSize: '0.875rem', color: '#E4E8F1', fontFamily: 'monospace' }}>({ticket.vendor})</span>
|
<div className="flex-1">
|
||||||
{ticket.summary && <span style={{ fontSize: '0.875rem', color: '#E4E8F1', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', maxWidth: '20rem' }}>{ticket.summary}</span>}
|
<p className="font-medium text-intel-success font-mono">✓ CVE Addressed ({quickCheckResult.vendors.length} vendor{quickCheckResult.vendors.length > 1 ? 's' : ''})</p>
|
||||||
<span style={ticket.status === 'Open' ? STYLES.badgeCritical : STYLES.badgeHigh}>
|
<div className="mt-3 space-y-3">
|
||||||
<span style={STYLES.glowDot(ticket.status === 'Open' ? '#FF3366' : '#FFB800')}></span>
|
{quickCheckResult.vendors.map((vendorInfo, idx) => (
|
||||||
{ticket.status}
|
<div key={idx} className="p-3 bg-intel-dark/70 rounded border border-intel-accent/30 shadow-lg">
|
||||||
</span>
|
<p className="font-semibold text-white mb-2 font-sans">{vendorInfo.vendor}</p>
|
||||||
</div>
|
<div className="grid grid-cols-2 gap-2 text-sm text-gray-300 mb-2 font-mono">
|
||||||
{canWrite() && (
|
<p><strong className="text-white">Severity:</strong> {vendorInfo.severity}</p>
|
||||||
<div className="flex gap-2">
|
<p><strong className="text-white">Status:</strong> {vendorInfo.status}</p>
|
||||||
<button onClick={() => handleEditTicket(ticket)} className="text-gray-400 hover:text-intel-warning transition-colors">
|
<p><strong className="text-white">Documents:</strong> {vendorInfo.total_documents} attached</p>
|
||||||
<Edit2 className="w-4 h-4" />
|
</div>
|
||||||
</button>
|
</div>
|
||||||
<button onClick={() => handleDeleteTicket(ticket)} className="text-gray-400 hover:text-intel-danger transition-colors">
|
))}
|
||||||
<Trash2 className="w-4 h-4" />
|
</div>
|
||||||
</button>
|
</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>
|
</div>
|
||||||
))}
|
)}
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Search and Filters */}
|
|
||||||
<div style={{...STYLES.intelCard, padding: '1.5rem', marginBottom: '1.5rem'}} className="rounded-lg">
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
|
||||||
<div className="md:col-span-1">
|
|
||||||
<label className="block text-xs font-medium text-gray-300 mb-2 uppercase tracking-wider">
|
|
||||||
<Search className="inline w-4 h-4 mr-1" />
|
|
||||||
Search CVEs
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
placeholder="CVE ID or description..."
|
|
||||||
value={searchQuery}
|
|
||||||
onChange={(e) => setSearchQuery(e.target.value)}
|
|
||||||
className="intel-input w-full"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
{/* Search and Filters */}
|
||||||
<label className="block text-xs font-medium text-gray-300 mb-2 uppercase tracking-wider">
|
<div style={{...STYLES.intelCard, padding: '1.5rem'}} className="rounded-lg">
|
||||||
<Filter className="inline w-4 h-4 mr-1" />
|
<div className="grid grid-cols-1 gap-4">
|
||||||
Vendor
|
<div>
|
||||||
</label>
|
<label className="block text-xs font-medium text-gray-300 mb-2 uppercase tracking-wider">
|
||||||
<select
|
<Search className="inline w-4 h-4 mr-1" />
|
||||||
value={selectedVendor}
|
Search CVEs
|
||||||
onChange={(e) => setSelectedVendor(e.target.value)}
|
</label>
|
||||||
className="intel-input w-full"
|
<input
|
||||||
>
|
type="text"
|
||||||
{vendors.map(vendor => (
|
placeholder="CVE ID or description..."
|
||||||
<option key={vendor} value={vendor}>{vendor}</option>
|
value={searchQuery}
|
||||||
))}
|
onChange={(e) => setSearchQuery(e.target.value)}
|
||||||
</select>
|
className="intel-input w-full"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="grid grid-cols-2 gap-4">
|
||||||
|
<div>
|
||||||
|
<label className="block text-xs font-medium text-gray-300 mb-2 uppercase tracking-wider">
|
||||||
|
<Filter className="inline w-4 h-4 mr-1" />
|
||||||
|
Vendor
|
||||||
|
</label>
|
||||||
|
<select
|
||||||
|
value={selectedVendor}
|
||||||
|
onChange={(e) => setSelectedVendor(e.target.value)}
|
||||||
|
className="intel-input w-full"
|
||||||
|
>
|
||||||
|
{vendors.map(vendor => (
|
||||||
|
<option key={vendor} value={vendor}>{vendor}</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label className="block text-xs font-medium text-gray-300 mb-2 uppercase tracking-wider">
|
||||||
|
<AlertCircle className="inline w-4 h-4 mr-1" />
|
||||||
|
Severity
|
||||||
|
</label>
|
||||||
|
<select
|
||||||
|
value={selectedSeverity}
|
||||||
|
onChange={(e) => setSelectedSeverity(e.target.value)}
|
||||||
|
className="intel-input w-full"
|
||||||
|
>
|
||||||
|
{severityLevels.map(level => (
|
||||||
|
<option key={level} value={level}>{level}</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
{/* Results Summary */}
|
||||||
<label className="block text-xs font-medium text-gray-300 mb-2 uppercase tracking-wider">
|
<div className="flex justify-between items-center">
|
||||||
<AlertCircle className="inline w-4 h-4 mr-1" />
|
<p className="text-gray-400 font-mono text-sm">
|
||||||
Severity
|
<span className="text-intel-accent font-bold">{Object.keys(filteredGroupedCVEs).length}</span> CVE{Object.keys(filteredGroupedCVEs).length !== 1 ? 's' : ''}
|
||||||
</label>
|
<span className="text-gray-500 mx-2">•</span>
|
||||||
<select
|
<span className="text-gray-300">{cves.length}</span> vendor entr{cves.length !== 1 ? 'ies' : 'y'}
|
||||||
value={selectedSeverity}
|
</p>
|
||||||
onChange={(e) => setSelectedSeverity(e.target.value)}
|
{selectedDocuments.length > 0 && (
|
||||||
className="intel-input w-full"
|
<button
|
||||||
>
|
onClick={exportSelectedDocuments}
|
||||||
{severityLevels.map(level => (
|
className="intel-button intel-button-primary flex items-center gap-2"
|
||||||
<option key={level} value={level}>{level}</option>
|
>
|
||||||
))}
|
<Download className="w-4 h-4" />
|
||||||
</select>
|
Export {selectedDocuments.length} Doc{selectedDocuments.length !== 1 ? 's' : ''}
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Results Summary */}
|
{/* CVE List - Grouped by CVE ID */}
|
||||||
<div className="mb-4 flex justify-between items-center">
|
{loading ? (
|
||||||
<p className="text-gray-400 font-mono text-sm">
|
<div className="intel-card rounded-lg p-12 text-center">
|
||||||
<span className="text-intel-accent font-bold">{Object.keys(filteredGroupedCVEs).length}</span> CVE{Object.keys(filteredGroupedCVEs).length !== 1 ? 's' : ''}
|
<div className="loading-spinner w-12 h-12 mx-auto mb-4"></div>
|
||||||
<span className="text-gray-500 mx-2">•</span>
|
<p className="text-gray-400 font-mono text-sm uppercase tracking-wider">Scanning Vulnerabilities...</p>
|
||||||
<span className="text-gray-300">{cves.length}</span> vendor entr{cves.length !== 1 ? 'ies' : 'y'}
|
</div>
|
||||||
</p>
|
) : error ? (
|
||||||
{selectedDocuments.length > 0 && (
|
<div className="intel-card rounded-lg p-12 text-center border-intel-danger">
|
||||||
<button
|
<XCircle className="w-12 h-12 text-intel-danger mx-auto mb-4" />
|
||||||
onClick={exportSelectedDocuments}
|
<h3 className="text-lg font-medium text-gray-200 mb-2 font-mono">Error Loading CVEs</h3>
|
||||||
className="intel-button intel-button-primary flex items-center gap-2"
|
<p className="text-gray-400 mb-4">{error}</p>
|
||||||
>
|
<button
|
||||||
<Download className="w-4 h-4" />
|
onClick={fetchCVEs}
|
||||||
Export {selectedDocuments.length} Doc{selectedDocuments.length !== 1 ? 's' : ''}
|
className="intel-button intel-button-primary"
|
||||||
</button>
|
>
|
||||||
)}
|
Retry
|
||||||
</div>
|
</button>
|
||||||
|
</div>
|
||||||
{/* CVE List - Grouped by CVE ID */}
|
) : (
|
||||||
{loading ? (
|
<div className="space-y-4">
|
||||||
<div className="intel-card rounded-lg p-12 text-center">
|
|
||||||
<div className="loading-spinner w-12 h-12 mx-auto mb-4"></div>
|
|
||||||
<p className="text-gray-400 font-mono text-sm uppercase tracking-wider">Scanning Vulnerabilities...</p>
|
|
||||||
</div>
|
|
||||||
) : error ? (
|
|
||||||
<div className="intel-card rounded-lg p-12 text-center border-intel-danger">
|
|
||||||
<XCircle className="w-12 h-12 text-intel-danger mx-auto mb-4" />
|
|
||||||
<h3 className="text-lg font-medium text-gray-200 mb-2 font-mono">Error Loading CVEs</h3>
|
|
||||||
<p className="text-gray-400 mb-4">{error}</p>
|
|
||||||
<button
|
|
||||||
onClick={fetchCVEs}
|
|
||||||
className="intel-button intel-button-primary"
|
|
||||||
>
|
|
||||||
Retry
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div className="space-y-4">
|
|
||||||
{Object.entries(filteredGroupedCVEs).map(([cveId, vendorEntries]) => {
|
{Object.entries(filteredGroupedCVEs).map(([cveId, vendorEntries]) => {
|
||||||
const isCVEExpanded = expandedCVEs[cveId];
|
const isCVEExpanded = expandedCVEs[cveId];
|
||||||
const severityOrder = { 'Critical': 0, 'High': 1, 'Medium': 2, 'Low': 3 };
|
const severityOrder = { 'Critical': 0, 'High': 1, 'Medium': 2, 'Low': 3 };
|
||||||
@@ -1733,16 +1727,155 @@ export default function App() {
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{Object.keys(filteredGroupedCVEs).length === 0 && !loading && (
|
{Object.keys(filteredGroupedCVEs).length === 0 && !loading && (
|
||||||
<div className="intel-card rounded-lg p-12 text-center">
|
<div className="intel-card rounded-lg p-12 text-center">
|
||||||
<AlertCircle className="w-12 h-12 text-gray-400 mx-auto mb-4" />
|
<AlertCircle className="w-12 h-12 text-gray-400 mx-auto mb-4" />
|
||||||
<h3 className="text-lg font-medium text-white mb-2 font-mono">No CVEs Found</h3>
|
<h3 className="text-lg font-medium text-white mb-2 font-mono">No CVEs Found</h3>
|
||||||
<p className="text-gray-300">Try adjusting your search criteria or filters</p>
|
<p className="text-gray-300">Try adjusting your search criteria or filters</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
{/* End Center Panel */}
|
||||||
|
|
||||||
|
{/* RIGHT PANEL - Calendar & Open Tickets */}
|
||||||
|
<div className="col-span-12 lg:col-span-3 space-y-4">
|
||||||
|
{/* Calendar Widget */}
|
||||||
|
<div style={{...STYLES.intelCard, padding: '1.5rem', borderLeft: '4px solid #00D9FF'}} className="rounded-lg">
|
||||||
|
<h2 style={{ fontSize: '1.125rem', fontWeight: '600', color: '#00D9FF', marginBottom: '1rem', fontFamily: 'monospace', textTransform: 'uppercase', letterSpacing: '0.1em', textShadow: '0 0 15px rgba(0, 217, 255, 0.5)' }}>
|
||||||
|
Calendar
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
{/* Simple Calendar Grid */}
|
||||||
|
<div className="mb-2">
|
||||||
|
<div className="text-center mb-3">
|
||||||
|
<span className="text-white font-semibold font-mono">February 2024</span>
|
||||||
|
</div>
|
||||||
|
<div className="grid grid-cols-7 gap-1 text-center text-xs mb-2">
|
||||||
|
<div className="text-gray-400 font-mono">Su</div>
|
||||||
|
<div className="text-gray-400 font-mono">Mo</div>
|
||||||
|
<div className="text-gray-400 font-mono">Tu</div>
|
||||||
|
<div className="text-gray-400 font-mono">We</div>
|
||||||
|
<div className="text-gray-400 font-mono">Th</div>
|
||||||
|
<div className="text-gray-400 font-mono">Fr</div>
|
||||||
|
<div className="text-gray-400 font-mono">Sa</div>
|
||||||
|
</div>
|
||||||
|
<div className="grid grid-cols-7 gap-1 text-center">
|
||||||
|
{/* Week 1 */}
|
||||||
|
<div className="text-gray-600 font-mono text-xs p-1">28</div>
|
||||||
|
<div className="text-gray-600 font-mono text-xs p-1">29</div>
|
||||||
|
<div className="text-gray-600 font-mono text-xs p-1">30</div>
|
||||||
|
<div className="text-gray-600 font-mono text-xs p-1">31</div>
|
||||||
|
<div className="text-white font-mono text-xs p-1 hover:bg-intel-accent/20 rounded cursor-pointer">1</div>
|
||||||
|
<div className="text-white font-mono text-xs p-1 hover:bg-intel-accent/20 rounded cursor-pointer">2</div>
|
||||||
|
<div className="text-white font-mono text-xs p-1 hover:bg-intel-accent/20 rounded cursor-pointer">3</div>
|
||||||
|
{/* Week 2 */}
|
||||||
|
<div className="text-white font-mono text-xs p-1 hover:bg-intel-accent/20 rounded cursor-pointer">4</div>
|
||||||
|
<div className="text-white font-mono text-xs p-1 hover:bg-intel-accent/20 rounded cursor-pointer">5</div>
|
||||||
|
<div className="text-white font-mono text-xs p-1 hover:bg-intel-accent/20 rounded cursor-pointer">6</div>
|
||||||
|
<div className="text-white font-mono text-xs p-1 hover:bg-intel-accent/20 rounded cursor-pointer">7</div>
|
||||||
|
<div className="text-white font-mono text-xs p-1 hover:bg-intel-accent/20 rounded cursor-pointer">8</div>
|
||||||
|
<div className="text-white font-mono text-xs p-1 hover:bg-intel-accent/20 rounded cursor-pointer">9</div>
|
||||||
|
<div className="bg-intel-accent/30 text-white font-mono text-xs p-1 rounded font-bold border border-intel-accent">10</div>
|
||||||
|
{/* Week 3 */}
|
||||||
|
<div className="text-white font-mono text-xs p-1 hover:bg-intel-accent/20 rounded cursor-pointer">11</div>
|
||||||
|
<div className="text-white font-mono text-xs p-1 hover:bg-intel-accent/20 rounded cursor-pointer">12</div>
|
||||||
|
<div className="text-white font-mono text-xs p-1 hover:bg-intel-accent/20 rounded cursor-pointer">13</div>
|
||||||
|
<div className="text-white font-mono text-xs p-1 hover:bg-intel-accent/20 rounded cursor-pointer">14</div>
|
||||||
|
<div className="text-white font-mono text-xs p-1 hover:bg-intel-accent/20 rounded cursor-pointer">15</div>
|
||||||
|
<div className="text-white font-mono text-xs p-1 hover:bg-intel-accent/20 rounded cursor-pointer">16</div>
|
||||||
|
<div className="text-white font-mono text-xs p-1 hover:bg-intel-accent/20 rounded cursor-pointer">17</div>
|
||||||
|
{/* Week 4 */}
|
||||||
|
<div className="text-white font-mono text-xs p-1 hover:bg-intel-accent/20 rounded cursor-pointer">18</div>
|
||||||
|
<div className="text-white font-mono text-xs p-1 hover:bg-intel-accent/20 rounded cursor-pointer">19</div>
|
||||||
|
<div className="text-white font-mono text-xs p-1 hover:bg-intel-accent/20 rounded cursor-pointer">20</div>
|
||||||
|
<div className="text-white font-mono text-xs p-1 hover:bg-intel-accent/20 rounded cursor-pointer">21</div>
|
||||||
|
<div className="text-white font-mono text-xs p-1 hover:bg-intel-accent/20 rounded cursor-pointer">22</div>
|
||||||
|
<div className="text-white font-mono text-xs p-1 hover:bg-intel-accent/20 rounded cursor-pointer">23</div>
|
||||||
|
<div className="text-white font-mono text-xs p-1 hover:bg-intel-accent/20 rounded cursor-pointer">24</div>
|
||||||
|
{/* Week 5 */}
|
||||||
|
<div className="text-white font-mono text-xs p-1 hover:bg-intel-accent/20 rounded cursor-pointer">25</div>
|
||||||
|
<div className="text-white font-mono text-xs p-1 hover:bg-intel-accent/20 rounded cursor-pointer">26</div>
|
||||||
|
<div className="text-white font-mono text-xs p-1 hover:bg-intel-accent/20 rounded cursor-pointer">27</div>
|
||||||
|
<div className="text-white font-mono text-xs p-1 hover:bg-intel-accent/20 rounded cursor-pointer">28</div>
|
||||||
|
<div className="text-white font-mono text-xs p-1 hover:bg-intel-accent/20 rounded cursor-pointer">29</div>
|
||||||
|
<div className="text-gray-600 font-mono text-xs p-1">1</div>
|
||||||
|
<div className="text-gray-600 font-mono text-xs p-1">2</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Open Vendor Tickets */}
|
||||||
|
<div style={{...STYLES.intelCard, padding: '1.5rem', borderLeft: '4px solid #FFB800'}} className="rounded-lg">
|
||||||
|
<div className="flex justify-between items-center mb-4">
|
||||||
|
<h2 style={{ fontSize: '1.125rem', fontWeight: '600', color: '#FFB800', display: 'flex', alignItems: 'center', gap: '0.5rem', fontFamily: 'monospace', textTransform: 'uppercase', letterSpacing: '0.1em', textShadow: '0 0 15px rgba(255, 184, 0, 0.5)' }}>
|
||||||
|
<AlertCircle className="w-5 h-5" />
|
||||||
|
Open Tickets
|
||||||
|
</h2>
|
||||||
|
{canWrite() && (
|
||||||
|
<button
|
||||||
|
onClick={() => { setAddTicketContext(null); setTicketForm({ cve_id: '', vendor: '', ticket_key: '', url: '', summary: '', status: 'Open' }); setShowAddTicket(true); }}
|
||||||
|
className="intel-button intel-button-primary flex items-center gap-1 text-xs px-2 py-1"
|
||||||
|
>
|
||||||
|
<Plus className="w-3 h-3" />
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="text-center mb-3">
|
||||||
|
<div style={{ fontSize: '2rem', fontWeight: '700', fontFamily: 'monospace', color: '#FFB800', textShadow: '0 0 20px rgba(255, 184, 0, 0.5)' }}>
|
||||||
|
{jiraTickets.filter(t => t.status !== 'Closed').length}
|
||||||
|
</div>
|
||||||
|
<div className="text-xs text-gray-400 uppercase tracking-wider">Active</div>
|
||||||
|
</div>
|
||||||
|
<div className="space-y-2 max-h-96 overflow-y-auto">
|
||||||
|
{jiraTickets.filter(t => t.status !== 'Closed').slice(0, 10).map(ticket => (
|
||||||
|
<div key={ticket.id} style={{ background: 'linear-gradient(135deg, rgba(19, 25, 55, 0.85) 0%, rgba(30, 39, 73, 0.75) 100%)', border: '1px solid rgba(255, 184, 0, 0.3)', borderRadius: '0.375rem', padding: '0.5rem', boxShadow: '0 2px 6px rgba(0, 0, 0, 0.25), inset 0 1px 0 rgba(255, 255, 255, 0.04)' }}>
|
||||||
|
<div className="flex items-start justify-between gap-2 mb-1">
|
||||||
|
<a
|
||||||
|
href={ticket.url || '#'}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="font-mono text-xs font-semibold text-intel-accent hover:text-intel-warning transition-colors"
|
||||||
|
>
|
||||||
|
{ticket.ticket_key}
|
||||||
|
</a>
|
||||||
|
{canWrite() && (
|
||||||
|
<div className="flex gap-1">
|
||||||
|
<button onClick={() => handleEditTicket(ticket)} className="text-gray-400 hover:text-intel-warning transition-colors">
|
||||||
|
<Edit2 className="w-3 h-3" />
|
||||||
|
</button>
|
||||||
|
<button onClick={() => handleDeleteTicket(ticket)} className="text-gray-400 hover:text-intel-danger transition-colors">
|
||||||
|
<Trash2 className="w-3 h-3" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="text-xs text-white font-mono mb-1">{ticket.cve_id}</div>
|
||||||
|
<div className="text-xs text-gray-400">{ticket.vendor}</div>
|
||||||
|
{ticket.summary && <div className="text-xs text-gray-300 mt-1 truncate">{ticket.summary}</div>}
|
||||||
|
<div className="mt-2">
|
||||||
|
<span style={{ ...STYLES.badgeHigh, fontSize: '0.65rem', padding: '0.25rem 0.5rem' }}>
|
||||||
|
<span style={{...STYLES.glowDot('#FFB800'), width: '6px', height: '6px'}}></span>
|
||||||
|
{ticket.status}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
{jiraTickets.filter(t => t.status !== 'Closed').length === 0 && (
|
||||||
|
<div className="text-center py-8">
|
||||||
|
<CheckCircle className="w-8 h-8 text-intel-success mx-auto mb-2" />
|
||||||
|
<p className="text-sm text-gray-400 italic font-mono">No open tickets</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/* End Right Panel */}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
{/* End Three Column Layout */}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user