Compare commits
2 Commits
669396f635
...
9893460b64
| Author | SHA1 | Date | |
|---|---|---|---|
| 9893460b64 | |||
| 51b1f99b3a |
@@ -178,6 +178,7 @@ export default function App() {
|
|||||||
const [quickCheckResult, setQuickCheckResult] = useState(null);
|
const [quickCheckResult, setQuickCheckResult] = useState(null);
|
||||||
const [currentPage, setCurrentPage] = useState('home');
|
const [currentPage, setCurrentPage] = useState('home');
|
||||||
const [navOpen, setNavOpen] = useState(false);
|
const [navOpen, setNavOpen] = useState(false);
|
||||||
|
const [calendarFilter, setCalendarFilter] = useState(null);
|
||||||
const [showAddCVE, setShowAddCVE] = useState(false);
|
const [showAddCVE, setShowAddCVE] = useState(false);
|
||||||
const [showUserManagement, setShowUserManagement] = useState(false);
|
const [showUserManagement, setShowUserManagement] = useState(false);
|
||||||
const [showAuditLog, setShowAuditLog] = useState(false);
|
const [showAuditLog, setShowAuditLog] = useState(false);
|
||||||
@@ -961,7 +962,11 @@ export default function App() {
|
|||||||
isOpen={navOpen}
|
isOpen={navOpen}
|
||||||
onClose={() => setNavOpen(false)}
|
onClose={() => setNavOpen(false)}
|
||||||
currentPage={currentPage}
|
currentPage={currentPage}
|
||||||
onNavigate={setCurrentPage}
|
onNavigate={(page) => {
|
||||||
|
// Clear calendar filter when navigating directly via the nav drawer
|
||||||
|
if (page === 'reporting') setCalendarFilter(null);
|
||||||
|
setCurrentPage(page);
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
{/* Scanning line effect */}
|
{/* Scanning line effect */}
|
||||||
<div className="scan-line"></div>
|
<div className="scan-line"></div>
|
||||||
@@ -1036,7 +1041,7 @@ export default function App() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Page content */}
|
{/* Page content */}
|
||||||
{currentPage === 'reporting' && <ReportingPage />}
|
{currentPage === 'reporting' && <ReportingPage filterDate={calendarFilter} />}
|
||||||
{currentPage === 'knowledge-base' && <KnowledgeBasePage />}
|
{currentPage === 'knowledge-base' && <KnowledgeBasePage />}
|
||||||
{currentPage === 'exports' && <ExportsPage />}
|
{currentPage === 'exports' && <ExportsPage />}
|
||||||
|
|
||||||
@@ -2220,7 +2225,12 @@ export default function App() {
|
|||||||
Calendar
|
Calendar
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
<CalendarWidget />
|
<CalendarWidget
|
||||||
|
onDateClick={(dateStr) => {
|
||||||
|
setCalendarFilter(dateStr);
|
||||||
|
setCurrentPage('reporting');
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Open Vendor Tickets */}
|
{/* Open Vendor Tickets */}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ function toLocalDateStr(date) {
|
|||||||
return `${y}-${m}-${d}`;
|
return `${y}-${m}-${d}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function CalendarWidget() {
|
export default function CalendarWidget({ onDateClick }) {
|
||||||
const today = new Date();
|
const today = new Date();
|
||||||
const todayStr = toLocalDateStr(today);
|
const todayStr = toLocalDateStr(today);
|
||||||
|
|
||||||
@@ -116,15 +116,19 @@ export default function CalendarWidget() {
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={idx}
|
key={idx}
|
||||||
title={hasDue ? `${dueCount} finding${dueCount > 1 ? 's' : ''} due` : undefined}
|
title={hasDue ? `${dueCount} finding${dueCount > 1 ? 's' : ''} due — click to view` : undefined}
|
||||||
|
onClick={hasDue && onDateClick ? () => onDateClick(dateStr) : undefined}
|
||||||
style={{
|
style={{
|
||||||
display: 'flex', flexDirection: 'column', alignItems: 'center',
|
display: 'flex', flexDirection: 'column', alignItems: 'center',
|
||||||
gap: '2px', padding: '3px 1px',
|
gap: '2px', padding: '3px 1px',
|
||||||
borderRadius: '4px',
|
borderRadius: '4px',
|
||||||
background: isToday ? 'rgba(14,165,233,0.2)' : 'transparent',
|
background: isToday ? 'rgba(14,165,233,0.2)' : 'transparent',
|
||||||
border: isToday ? '1px solid rgba(14,165,233,0.5)' : '1px solid transparent',
|
border: isToday ? '1px solid rgba(14,165,233,0.5)' : '1px solid transparent',
|
||||||
cursor: hasDue ? 'default' : 'default',
|
cursor: hasDue ? 'pointer' : 'default',
|
||||||
|
transition: hasDue ? 'background 0.15s' : undefined,
|
||||||
}}
|
}}
|
||||||
|
onMouseEnter={hasDue ? (e) => { e.currentTarget.style.background = isToday ? 'rgba(14,165,233,0.35)' : 'rgba(239,68,68,0.15)'; } : undefined}
|
||||||
|
onMouseLeave={hasDue ? (e) => { e.currentTarget.style.background = isToday ? 'rgba(14,165,233,0.2)' : 'transparent'; } : undefined}
|
||||||
>
|
>
|
||||||
<span style={{
|
<span style={{
|
||||||
fontSize: '0.7rem', fontFamily: 'monospace', lineHeight: 1,
|
fontSize: '0.7rem', fontFamily: 'monospace', lineHeight: 1,
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ const STORAGE_KEY = 'steam_findings_columns_v1';
|
|||||||
// Column definitions — source of truth for labels, sort behaviour, rendering
|
// Column definitions — source of truth for labels, sort behaviour, rendering
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
const COLUMN_DEFS = {
|
const COLUMN_DEFS = {
|
||||||
|
findingId: { label: 'Finding ID', sortable: true, filterable: false },
|
||||||
severity: { label: 'Severity', sortable: true, filterable: true },
|
severity: { label: 'Severity', sortable: true, filterable: true },
|
||||||
title: { label: 'Title', sortable: true, filterable: true },
|
title: { label: 'Title', sortable: true, filterable: true },
|
||||||
cves: { label: 'CVEs', sortable: true, filterable: true, multiValue: true },
|
cves: { label: 'CVEs', sortable: true, filterable: true, multiValue: true },
|
||||||
@@ -23,6 +24,7 @@ const COLUMN_DEFS = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const DEFAULT_COLUMN_ORDER = [
|
const DEFAULT_COLUMN_ORDER = [
|
||||||
|
{ key: 'findingId', visible: true },
|
||||||
{ key: 'severity', visible: true },
|
{ key: 'severity', visible: true },
|
||||||
{ key: 'title', visible: true },
|
{ key: 'title', visible: true },
|
||||||
{ key: 'cves', visible: true },
|
{ key: 'cves', visible: true },
|
||||||
@@ -63,6 +65,7 @@ function saveColumnOrder(order) {
|
|||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
function getVal(finding, key) {
|
function getVal(finding, key) {
|
||||||
switch (key) {
|
switch (key) {
|
||||||
|
case 'findingId': return finding.id ?? '';
|
||||||
case 'severity': return finding.severity ?? 0;
|
case 'severity': return finding.severity ?? 0;
|
||||||
case 'title': return finding.title ?? '';
|
case 'title': return finding.title ?? '';
|
||||||
case 'hostName': return finding.hostName ?? '';
|
case 'hostName': return finding.hostName ?? '';
|
||||||
@@ -449,6 +452,12 @@ function FilterDropdown({ anchorEl, colKey, findings, activeFilter, onFilterChan
|
|||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
function TableCell({ colKey, finding }) {
|
function TableCell({ colKey, finding }) {
|
||||||
switch (colKey) {
|
switch (colKey) {
|
||||||
|
case 'findingId':
|
||||||
|
return (
|
||||||
|
<td style={{ padding: '0.45rem 0.75rem', whiteSpace: 'nowrap', color: '#475569', fontFamily: 'monospace', fontSize: '0.68rem' }}>
|
||||||
|
{finding.id || '—'}
|
||||||
|
</td>
|
||||||
|
);
|
||||||
case 'severity': {
|
case 'severity': {
|
||||||
const sc = severityColor(finding.vrrGroup);
|
const sc = severityColor(finding.vrrGroup);
|
||||||
return (
|
return (
|
||||||
@@ -564,7 +573,7 @@ function TableCell({ colKey, finding }) {
|
|||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Main ReportingPage
|
// Main ReportingPage
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
export default function ReportingPage() {
|
export default function ReportingPage({ filterDate }) {
|
||||||
const [findings, setFindings] = useState([]);
|
const [findings, setFindings] = useState([]);
|
||||||
const [total, setTotal] = useState(null);
|
const [total, setTotal] = useState(null);
|
||||||
const [syncedAt, setSyncedAt] = useState(null);
|
const [syncedAt, setSyncedAt] = useState(null);
|
||||||
@@ -574,7 +583,9 @@ export default function ReportingPage() {
|
|||||||
const [syncing, setSyncing] = useState(false);
|
const [syncing, setSyncing] = useState(false);
|
||||||
const [sort, setSort] = useState({ field: 'severity', dir: 'desc' });
|
const [sort, setSort] = useState({ field: 'severity', dir: 'desc' });
|
||||||
const [columnOrder, setColumnOrder] = useState(loadColumnOrder);
|
const [columnOrder, setColumnOrder] = useState(loadColumnOrder);
|
||||||
const [columnFilters, setColumnFilters] = useState({});
|
const [columnFilters, setColumnFilters] = useState(() =>
|
||||||
|
filterDate ? { dueDate: new Set([filterDate]) } : {}
|
||||||
|
);
|
||||||
const [openFilter, setOpenFilter] = useState(null);
|
const [openFilter, setOpenFilter] = useState(null);
|
||||||
const filterBtnRefs = useRef({});
|
const filterBtnRefs = useRef({});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user