Files
AetherForge/server/web/src/components/Fleet/FleetToolbar.tsx
AetherForge 07fdb39b63
Some checks failed
CI Docker Mining Proof / Linux agent hashrate proof (push) Has been cancelled
Add fleet registry removal with confirm modal and agent_removed WS.
Operators can remove machines from Crucible and dashboard rosters with honest messaging that deletion is registry-only; bulk select, oath ledger entries, and Go/Vitest coverage included.
2026-06-07 18:23:41 -07:00

146 lines
5.5 KiB
TypeScript

import type { FleetFilterState } from '../../help/fleetFilters';
import { collectFleetSubnets, collectFleetTags } from '../../help/fleetFilters';
import type { Agent } from '../../types';
import { HelpTip } from '../HelpTip';
import './FleetToolbar.css';
interface Props {
agents: Agent[];
filters: FleetFilterState;
onChange: (next: FleetFilterState) => void;
selectedCount: number;
selectedAgents?: Agent[];
onBulkAction: (action: string) => void;
onSelectAllFiltered?: () => void;
onCreateGroup?: () => void;
filteredCount?: number;
bulkBusy: boolean;
}
export default function FleetToolbar({
agents,
filters,
onChange,
selectedCount,
selectedAgents = [],
onBulkAction,
onSelectAllFiltered,
onCreateGroup,
filteredCount,
bulkBusy,
}: Props) {
const tags = collectFleetTags(agents);
const subnets = collectFleetSubnets(agents);
return (
<div className="fleet-toolbar card">
<div className="fleet-toolbar-filters">
<HelpTip field="fl_filter_chips" />
<input
type="search"
className="input fleet-filter-search"
placeholder="Search name, IP, notes, tags…"
value={filters.search}
onChange={(e) => onChange({ ...filters, search: e.target.value })}
/>
<select
className="select fleet-filter-select"
value={filters.tag}
onChange={(e) => onChange({ ...filters, tag: e.target.value })}
title="Filter by tag"
>
<option value="">All tags</option>
{tags.map((t) => (
<option key={t} value={t}>{t}</option>
))}
</select>
<select
className="select fleet-filter-select"
value={filters.subnet}
onChange={(e) => onChange({ ...filters, subnet: e.target.value })}
title="Filter by subnet"
>
<option value="">All subnets</option>
{subnets.map((s) => (
<option key={s} value={s}>{s}</option>
))}
</select>
<select
className="select fleet-filter-select"
value={String(filters.hashrateMin)}
onChange={(e) => onChange({ ...filters, hashrateMin: Number(e.target.value) })}
title="Minimum 15m hashrate"
>
<option value="0">Any hashrate</option>
<option value="1000"> 1 KH/s</option>
<option value="10000"> 10 KH/s</option>
<option value="100000"> 100 KH/s</option>
<option value="1000000"> 1 MH/s</option>
</select>
<label className="checkbox-label fleet-filter-attn">
<input
type="checkbox"
className="checkbox"
checked={filters.needsAttention}
onChange={(e) => onChange({ ...filters, needsAttention: e.target.checked })}
/>
<span>Needs attention</span>
</label>
</div>
{onSelectAllFiltered && (filteredCount ?? 0) > 0 && selectedCount === 0 && (
<div className="fleet-bulk-bar">
<button type="button" className="btn btn-outline btn-sm" onClick={onSelectAllFiltered}>
Select all filtered ({filteredCount})
</button>
</div>
)}
{selectedCount > 0 && (
<div className="fleet-bulk-bar">
<span className="font-tech">{selectedCount} selected</span>
<HelpTip field="fl_bulk_actions" />
{onCreateGroup && (
<button
type="button"
className="btn btn-primary btn-sm"
disabled={bulkBusy}
onClick={onCreateGroup}
>
Create group
</button>
)}
{selectedCount === 1 && (
<button
type="button"
className="btn btn-outline btn-sm"
disabled={bulkBusy || !selectedAgents.some((a) => a.status === 'online')}
onClick={() => onBulkAction('screenshot')}
title="Capture desktop on the selected machine and download JPEG here"
>
Screenshot
</button>
)}
<button type="button" className="btn btn-outline btn-sm" disabled={bulkBusy || !selectedAgents.some((a) => a.status === 'online')} onClick={() => onBulkAction('pause')} title="Fleet health: power down hashing">Pause</button>
<button type="button" className="btn btn-outline btn-sm" disabled={bulkBusy || !selectedAgents.some((a) => a.status === 'online')} onClick={() => onBulkAction('resume')} title="Fleet health: restore hashing">Resume</button>
<button type="button" className="btn btn-outline btn-sm" disabled={bulkBusy || !selectedAgents.some((a) => a.status === 'online')} onClick={() => onBulkAction('stop')}>Stop</button>
<button type="button" className="btn btn-outline btn-sm" disabled={bulkBusy || !selectedAgents.some((a) => a.status === 'online')} onClick={() => onBulkAction('restart_idle')}>Restart idle</button>
<span style={{ display: 'inline-flex', alignItems: 'center', gap: '0.25rem' }}>
<button
type="button"
className="btn btn-sm"
disabled={bulkBusy}
style={{ background: 'rgba(255,40,40,0.15)', border: '1px solid #ff4444', color: '#ff6666' }}
onClick={() => onBulkAction('delete')}
title="Remove selected machines from fleet registry — does not uninstall on host"
>
Remove from fleet
</button>
<HelpTip field="fl_delete_roster" />
</span>
</div>
)}
</div>
);
}