feat(fleet): refine the Fleet Overview toolbar (#1376)

* feat(fleet): tidy the Overview toolbar and shorten tab labels

- Move the Add node button into the Overview toolbar beside the
  Grid/Topology toggle so it sits with the view it acts on instead of
  showing on every Fleet tab. It stays admin-only.
- Collapse the node search to an icon button that expands to the full
  input on click and collapses again on blur once the query is empty,
  reclaiming toolbar width. An active query keeps it open.
- Match the sort-direction toggle and the sort dropdown to the outlined
  dark fill of the Filters button for a consistent toolbar row.
- Swap the Check Updates icon to the refresh-with-dot glyph.
- Rename two tabs: Fleet Actions to Actions, Dependencies to Map
  (display labels only; internal keys unchanged).

Update the feature docs to the new tab labels.

* feat(fleet): move Check Updates into the Overview tab toolbar

Check Updates is an Overview-specific action, but it lived in the shared
Fleet header toolbar that renders across every tab. Move it into the
Overview tab's own toolbar, to the right of the Add node button, so it
only appears where it applies.

The relocated button now spins and disables while a check is in flight,
matching the Refresh button's loading feedback. The shared header keeps
Refresh and Export Dossier.
This commit is contained in:
Anso
2026-06-14 22:35:45 -04:00
committed by GitHub
parent 85bcd1341e
commit 02c3b006eb
4 changed files with 44 additions and 11 deletions
+3 -10
View File
@@ -1,5 +1,5 @@
import {
RefreshCw, RefreshCcwDot, Camera, FileDown,
RefreshCw, Camera, FileDown,
Network, SlidersHorizontal,
Send, KeyRound, ArrowLeftRight, Wrench, Workflow,
} from 'lucide-react';
@@ -133,15 +133,6 @@ export function FleetView({ onNavigateToNode }: FleetViewProps) {
</TabsHighlight>
</TabsList>
<div className="flex items-center gap-2 max-md:w-full max-md:flex-wrap">
<Button
variant="outline"
size="sm"
onClick={updateStatus.checkUpdates}
className="gap-2"
>
<RefreshCcwDot className="w-4 h-4" />
Check Updates
</Button>
<Button
variant="outline"
size="sm"
@@ -195,6 +186,8 @@ export function FleetView({ onNavigateToNode }: FleetViewProps) {
onEditNode={isAdmin ? openEdit : undefined}
onDeleteNode={isAdmin ? openDelete : undefined}
onAddNode={isAdmin ? openCreate : undefined}
onCheckUpdates={updateStatus.checkUpdates}
checkingUpdates={updateStatus.checkingUpdates}
topologyMode={topology.prefs.mode}
onTopologyModeChange={topology.setMode}
topologyPositions={topology.prefs.positions}
@@ -40,6 +40,8 @@ interface OverviewTabProps {
topologyPositions: SavedPositions;
onTopologyPositionsChange: (positions: SavedPositions) => void;
onAddNode?: () => void;
onCheckUpdates?: () => void;
checkingUpdates?: boolean;
}
export function OverviewTab({
@@ -73,6 +75,8 @@ export function OverviewTab({
topologyPositions,
onTopologyPositionsChange,
onAddNode,
onCheckUpdates,
checkingUpdates,
}: OverviewTabProps) {
return (
<>
@@ -116,6 +120,8 @@ export function OverviewTab({
onLabelFiltersChange={onLabelFiltersChange}
onClearFilters={onClearFilters}
onAddNode={onAddNode}
onCheckUpdates={onCheckUpdates}
checkingUpdates={checkingUpdates}
/>
{viewMode === 'topology' && processedNodes.length > 0 ? (
@@ -1,7 +1,7 @@
import { useEffect, useMemo, useRef, useState } from 'react';
import {
Search, ArrowUpDown, AlertTriangle, Play, Square,
LayoutGrid, Network, SlidersHorizontal, Plus,
LayoutGrid, Network, SlidersHorizontal, Plus, RefreshCcwDot,
} from 'lucide-react';
import { Button } from '@/components/ui/button';
import { Badge } from '@/components/ui/badge';
@@ -50,6 +50,8 @@ interface OverviewToolbarProps {
onLabelFiltersChange: (filters: Set<string>) => void;
onClearFilters: () => void;
onAddNode?: () => void;
onCheckUpdates?: () => void;
checkingUpdates?: boolean;
}
export function OverviewToolbar({
@@ -64,6 +66,8 @@ export function OverviewToolbar({
onLabelFiltersChange,
onClearFilters,
onAddNode,
onCheckUpdates,
checkingUpdates,
}: OverviewToolbarProps) {
const showGridControls = viewMode === 'grid';
const activeFilterCount =
@@ -257,6 +261,19 @@ export function OverviewToolbar({
Add node
</Button>
)}
{onCheckUpdates && (
<Button
variant="outline"
size="sm"
className="gap-2 shrink-0 h-9"
onClick={onCheckUpdates}
disabled={checkingUpdates}
>
<RefreshCcwDot className={`w-4 h-4 ${checkingUpdates ? 'animate-spin' : ''}`} />
Check Updates
</Button>
)}
</div>
);
}
@@ -93,4 +93,21 @@ describe('OverviewToolbar', () => {
render(<OverviewToolbar {...props()} />);
expect(screen.queryByRole('button', { name: 'Add node' })).not.toBeInTheDocument();
});
it('renders the Check Updates button and fires onCheckUpdates when provided', () => {
const onCheckUpdates = vi.fn();
render(<OverviewToolbar {...props({ onCheckUpdates })} />);
fireEvent.click(screen.getByRole('button', { name: /Check Updates/ }));
expect(onCheckUpdates).toHaveBeenCalledTimes(1);
});
it('omits the Check Updates button when onCheckUpdates is not provided', () => {
render(<OverviewToolbar {...props()} />);
expect(screen.queryByRole('button', { name: /Check Updates/ })).not.toBeInTheDocument();
});
it('disables the Check Updates button while a check is in flight', () => {
render(<OverviewToolbar {...props({ onCheckUpdates: vi.fn(), checkingUpdates: true })} />);
expect(screen.getByRole('button', { name: /Check Updates/ })).toBeDisabled();
});
});