Files
DECNET/decnet_web/src/components/AttackerDetail/ui.tsx
anti 95e1a4ab7a refactor(decnet_web/AttackerDetail): extract TimelineSection
Lift the TIMELINE collapsible (timestamps, ASN, reverse DNS,
leaked-IPs row with rotation detection) into its own section.
LeakedIPsRow + the rotation/inline-limit constants come along
since they were only ever used here.

Also moves the shared `Section` collapsible primitive into
AttackerDetail/ui.tsx so the remaining sections can adopt the
template without re-importing through the parent module.

- New AttackerDetail/sections/TimelineSection.tsx (LeakedIPsRow
  inline as a private helper)
- AttackerDetail/ui.tsx now exports both Tag and Section
- AttackerDetail.tsx loses LeakedIPsRow, the Section helper, the
  Timeline JSX block, and now-unused imports (ChevronUp, ChevronDown,
  AttackerData)
- TimelineSection.test.tsx covers timestamps, unknown-origin path,
  rotation badge, empty leaks, collapse, and toggle callback
2026-05-09 04:43:13 -04:00

51 lines
1.6 KiB
TypeScript

import React from 'react';
import { ChevronDown, ChevronUp } from '../../icons';
/** Pill-style tag chip used throughout the AttackerDetail surface
* for badges, filters, and category labels. Color drives both the
* border and a 15%-alpha fill (the suffix is hex alpha). */
export const Tag: React.FC<{ children: React.ReactNode; color?: string }> = ({
children,
color,
}) => (
<span
style={{
fontSize: '0.7rem',
padding: '2px 8px',
letterSpacing: '1px',
border: `1px solid ${color || 'var(--text-color)'}`,
color: color || 'var(--text-color)',
background: `${color || 'var(--text-color)'}15`,
}}
>
{children}
</span>
);
/** Collapsible panel used by every section on the AttackerDetail page.
* The header is the toggle; an optional `right` slot hosts controls
* (filters, action buttons) whose clicks are stopped from bubbling
* to the toggle handler. */
export const Section: React.FC<{
title: React.ReactNode;
right?: React.ReactNode;
open: boolean;
onToggle: () => void;
children: React.ReactNode;
}> = ({ title, right, open, onToggle, children }) => (
<div className="logs-section">
<div
className="section-header"
style={{ justifyContent: 'space-between', cursor: 'pointer', userSelect: 'none' }}
onClick={onToggle}
>
<div style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
{open ? <ChevronUp size={16} className="dim" /> : <ChevronDown size={16} className="dim" />}
<h2>{title}</h2>
</div>
{right && <div onClick={(e) => e.stopPropagation()}>{right}</div>}
</div>
{open && children}
</div>
);