fix(ui): follow-up polish — icons, dashboard bar, filter redesign, bounty/creds sort
- Dashboard: fix invisible bar at bottom of LIVE FEED by constraining max-height on the section instead of the inner container; same fix for side panels - Page icons: add violet-accent icon beside h1 on all 9 missing pages (CanaryTokens, RealismConfig, SyntheticFiles, PersonaGeneration, Attackers, Webhooks, LiveLogs, Topologies, DecoyFleet) - Attackers filter chips: replace ad-hoc chip buttons with seg-group tabs (ALL / ACTIVE N / PASSIVE N / INACTIVE N) matching Credential Vault style; country chips use same seg-group treatment - Credential Vault: add sortable headers to REUSE tab (LAST SEEN, PRINCIPAL, KIND, TARGETS, ATTEMPTS); reuses same SortTh pattern - Bounty: remove CREDENTIALS and PAYLOADS tabs; keep ALL, ARTIFACTS, FINGERPRINTS; add EMAIL (artifact subtype, filtered client-side)
This commit is contained in:
@@ -170,6 +170,23 @@ const Credentials: React.FC = () => {
|
||||
});
|
||||
}, [creds, sortCol, sortDir]);
|
||||
|
||||
const sortedReuseRows = useMemo(() => {
|
||||
if (!sortCol) return reuseRows;
|
||||
return [...reuseRows].sort((a, b) => {
|
||||
let av: string | number = '';
|
||||
let bv: string | number = '';
|
||||
if (sortCol === 'seen') { av = a.last_seen; bv = b.last_seen; }
|
||||
else if (sortCol === 'principal') { av = a.principal ?? ''; bv = b.principal ?? ''; }
|
||||
else if (sortCol === 'kind') { av = a.secret_kind; bv = b.secret_kind; }
|
||||
else if (sortCol === 'targets') { av = a.target_count; bv = b.target_count; }
|
||||
else if (sortCol === 'attempts') { av = a.attempt_count; bv = b.attempt_count; }
|
||||
const cmp = typeof av === 'number' && typeof bv === 'number'
|
||||
? av - bv
|
||||
: String(av).localeCompare(String(bv));
|
||||
return sortDir === 'asc' ? cmp : -cmp;
|
||||
});
|
||||
}, [reuseRows, sortCol, sortDir]);
|
||||
|
||||
const SortTh: React.FC<{ col: string; children: React.ReactNode }> = ({ col, children }) => (
|
||||
<th
|
||||
style={{ cursor: 'pointer', userSelect: 'none', whiteSpace: 'nowrap' }}
|
||||
@@ -382,18 +399,18 @@ const Credentials: React.FC = () => {
|
||||
<table className="logs-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>LAST SEEN</th>
|
||||
<th>PRINCIPAL</th>
|
||||
<th>KIND</th>
|
||||
<th>TARGETS</th>
|
||||
<th>ATTEMPTS</th>
|
||||
<SortTh col="seen">LAST SEEN</SortTh>
|
||||
<SortTh col="principal">PRINCIPAL</SortTh>
|
||||
<SortTh col="kind">KIND</SortTh>
|
||||
<SortTh col="targets">TARGETS</SortTh>
|
||||
<SortTh col="attempts">ATTEMPTS</SortTh>
|
||||
<th>DECKIES</th>
|
||||
<th>SERVICES</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{reuseRows.length > 0 ? reuseRows.map(r => {
|
||||
{sortedReuseRows.length > 0 ? sortedReuseRows.map(r => {
|
||||
const isPlain = r.secret_kind === 'plaintext';
|
||||
const moreDeckies = Math.max(0, r.deckies.length - 3);
|
||||
const moreServices = Math.max(0, r.services.length - 3);
|
||||
|
||||
Reference in New Issue
Block a user