added panels
This commit is contained in:
@@ -1255,212 +1255,206 @@ export default function App() {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Quick Check */}
|
||||
<div style={{...STYLES.intelCard, padding: '1.5rem', marginBottom: '1.5rem'}} className="rounded-lg">
|
||||
<div className="scan-line"></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>
|
||||
<div className="flex gap-3">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Enter CVE ID (e.g., CVE-2024-1234)"
|
||||
value={quickCheckCVE}
|
||||
onChange={(e) => setQuickCheckCVE(e.target.value)}
|
||||
onKeyPress={(e) => e.key === 'Enter' && quickCheckCVEStatus()}
|
||||
className="flex-1 intel-input"
|
||||
/>
|
||||
<button
|
||||
onClick={quickCheckCVEStatus}
|
||||
className="intel-button intel-button-primary"
|
||||
>
|
||||
Scan
|
||||
</button>
|
||||
{/* Three Column Layout */}
|
||||
<div className="grid grid-cols-12 gap-6">
|
||||
{/* LEFT PANEL - Wiki/Knowledge Base */}
|
||||
<div className="col-span-12 lg:col-span-3 space-y-4">
|
||||
<div style={{...STYLES.intelCard, padding: '1.5rem', borderLeft: '4px solid #00FF88'}} className="rounded-lg">
|
||||
<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)' }}>
|
||||
Knowledge Base
|
||||
</h2>
|
||||
|
||||
{/* Wiki/Blog Style Entries */}
|
||||
<div className="space-y-3">
|
||||
<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>
|
||||
<p className="text-gray-400 text-xs mb-2">Standard operating procedures for vulnerability response and escalation...</p>
|
||||
<span className="text-xs text-intel-success font-mono">Last updated: 2024-02-08</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">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>
|
||||
|
||||
{quickCheckResult && (
|
||||
<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'}`}>
|
||||
{quickCheckResult.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">{quickCheckResult.error}</p>
|
||||
</div>
|
||||
</div>
|
||||
) : quickCheckResult.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 ({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() && (
|
||||
{/* CENTER PANEL - Main Content */}
|
||||
<div className="col-span-12 lg:col-span-6 space-y-4">
|
||||
{/* Quick Check */}
|
||||
<div style={{...STYLES.intelCard, padding: '1.5rem'}} className="rounded-lg">
|
||||
<div className="scan-line"></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>
|
||||
<div className="flex gap-3">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Enter CVE ID (e.g., CVE-2024-1234)"
|
||||
value={quickCheckCVE}
|
||||
onChange={(e) => setQuickCheckCVE(e.target.value)}
|
||||
onKeyPress={(e) => e.key === 'Enter' && quickCheckCVEStatus()}
|
||||
className="flex-1 intel-input"
|
||||
/>
|
||||
<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"
|
||||
onClick={quickCheckCVEStatus}
|
||||
className="intel-button intel-button-primary"
|
||||
>
|
||||
<Plus className="w-4 h-4" />
|
||||
Add
|
||||
Scan
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
{jiraTickets.filter(t => t.status !== 'Closed').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.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">
|
||||
<div className="flex items-center gap-4 flex-1">
|
||||
<a
|
||||
href={ticket.url || '#'}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="font-mono text-sm font-semibold text-intel-accent hover:text-intel-warning transition-colors"
|
||||
>
|
||||
{ticket.ticket_key}
|
||||
</a>
|
||||
<span className="text-sm text-white font-mono">{ticket.cve_id}</span>
|
||||
<span style={{ fontSize: '0.875rem', color: '#E4E8F1', fontFamily: 'monospace' }}>({ticket.vendor})</span>
|
||||
{ticket.summary && <span style={{ fontSize: '0.875rem', color: '#E4E8F1', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', maxWidth: '20rem' }}>{ticket.summary}</span>}
|
||||
<span style={ticket.status === 'Open' ? STYLES.badgeCritical : STYLES.badgeHigh}>
|
||||
<span style={STYLES.glowDot(ticket.status === 'Open' ? '#FF3366' : '#FFB800')}></span>
|
||||
{ticket.status}
|
||||
</span>
|
||||
</div>
|
||||
{canWrite() && (
|
||||
<div className="flex gap-2">
|
||||
<button onClick={() => handleEditTicket(ticket)} className="text-gray-400 hover:text-intel-warning transition-colors">
|
||||
<Edit2 className="w-4 h-4" />
|
||||
</button>
|
||||
<button onClick={() => handleDeleteTicket(ticket)} className="text-gray-400 hover:text-intel-danger transition-colors">
|
||||
<Trash2 className="w-4 h-4" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{quickCheckResult && (
|
||||
<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'}`}>
|
||||
{quickCheckResult.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">{quickCheckResult.error}</p>
|
||||
</div>
|
||||
</div>
|
||||
) : quickCheckResult.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 ({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>
|
||||
</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>
|
||||
<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>
|
||||
{/* Search and Filters */}
|
||||
<div style={{...STYLES.intelCard, padding: '1.5rem'}} className="rounded-lg">
|
||||
<div className="grid grid-cols-1 gap-4">
|
||||
<div>
|
||||
<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 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>
|
||||
<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>
|
||||
{/* Results Summary */}
|
||||
<div className="flex justify-between items-center">
|
||||
<p className="text-gray-400 font-mono text-sm">
|
||||
<span className="text-intel-accent font-bold">{Object.keys(filteredGroupedCVEs).length}</span> CVE{Object.keys(filteredGroupedCVEs).length !== 1 ? 's' : ''}
|
||||
<span className="text-gray-500 mx-2">•</span>
|
||||
<span className="text-gray-300">{cves.length}</span> vendor entr{cves.length !== 1 ? 'ies' : 'y'}
|
||||
</p>
|
||||
{selectedDocuments.length > 0 && (
|
||||
<button
|
||||
onClick={exportSelectedDocuments}
|
||||
className="intel-button intel-button-primary flex items-center gap-2"
|
||||
>
|
||||
<Download className="w-4 h-4" />
|
||||
Export {selectedDocuments.length} Doc{selectedDocuments.length !== 1 ? 's' : ''}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Results Summary */}
|
||||
<div className="mb-4 flex justify-between items-center">
|
||||
<p className="text-gray-400 font-mono text-sm">
|
||||
<span className="text-intel-accent font-bold">{Object.keys(filteredGroupedCVEs).length}</span> CVE{Object.keys(filteredGroupedCVEs).length !== 1 ? 's' : ''}
|
||||
<span className="text-gray-500 mx-2">•</span>
|
||||
<span className="text-gray-300">{cves.length}</span> vendor entr{cves.length !== 1 ? 'ies' : 'y'}
|
||||
</p>
|
||||
{selectedDocuments.length > 0 && (
|
||||
<button
|
||||
onClick={exportSelectedDocuments}
|
||||
className="intel-button intel-button-primary flex items-center gap-2"
|
||||
>
|
||||
<Download className="w-4 h-4" />
|
||||
Export {selectedDocuments.length} Doc{selectedDocuments.length !== 1 ? 's' : ''}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* CVE List - Grouped by CVE ID */}
|
||||
{loading ? (
|
||||
<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">
|
||||
{/* CVE List - Grouped by CVE ID */}
|
||||
{loading ? (
|
||||
<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]) => {
|
||||
const isCVEExpanded = expandedCVEs[cveId];
|
||||
const severityOrder = { 'Critical': 0, 'High': 1, 'Medium': 2, 'Low': 3 };
|
||||
@@ -1733,16 +1727,155 @@ export default function App() {
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{Object.keys(filteredGroupedCVEs).length === 0 && !loading && (
|
||||
<div className="intel-card rounded-lg p-12 text-center">
|
||||
<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>
|
||||
<p className="text-gray-300">Try adjusting your search criteria or filters</p>
|
||||
{Object.keys(filteredGroupedCVEs).length === 0 && !loading && (
|
||||
<div className="intel-card rounded-lg p-12 text-center">
|
||||
<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>
|
||||
<p className="text-gray-300">Try adjusting your search criteria or filters</p>
|
||||
</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>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user