feat(fleet): §16 orchestrator tab foundation (Deployments, Federation, Secrets) (#856)

Reserves three navigable but non-functional tab slots on the Fleet page so
each future orchestrator surface can land as a tab content swap rather than
a navigation redesign. Each tab opens a coming-soon placeholder card listing
the planned actions for that surface.
This commit is contained in:
Anso
2026-05-01 00:10:56 -04:00
committed by GitHub
parent a25acbec7c
commit b8437e8780
2 changed files with 115 additions and 0 deletions
+48
View File
@@ -4,6 +4,7 @@ import {
Layers, Wifi, WifiOff, Search, ArrowUpDown, AlertTriangle,
Play, Square, RotateCcw, ExternalLink, Camera, Download, Loader2, Check,
CircleCheck, CircleAlert, Globe, Monitor, X, LayoutGrid, Network, SlidersHorizontal,
Send, KeyRound,
} from 'lucide-react';
import { FleetMasthead } from './fleet/FleetMasthead';
import { FleetTopology } from './fleet/FleetTopology';
@@ -28,6 +29,7 @@ import { useLicense } from '@/context/LicenseContext';
import { PaidGate } from './PaidGate';
import FleetSnapshots from './FleetSnapshots';
import { FleetConfiguration } from './fleet/FleetConfiguration';
import { FleetSoonPlaceholder, SoonBadge } from './fleet/FleetSoonPlaceholder';
import { toast } from '@/components/ui/toast-store';
import { LabelDot } from './LabelPill';
import { type Label as StackLabel, type LabelColor } from './label-types';
@@ -1047,6 +1049,25 @@ export function FleetView({ onNavigateToNode }: FleetViewProps) {
<SlidersHorizontal className="w-4 h-4 mr-1.5" />Status
</TabsTrigger>
</TabsHighlightItem>
<span aria-hidden className="self-center mx-1 h-4 w-px bg-border" />
<TabsHighlightItem value="deployments">
<TabsTrigger value="deployments">
<Send className="w-4 h-4 mr-1.5" />Deployments
<SoonBadge />
</TabsTrigger>
</TabsHighlightItem>
<TabsHighlightItem value="federation">
<TabsTrigger value="federation">
<Network className="w-4 h-4 mr-1.5" />Federation
<SoonBadge />
</TabsTrigger>
</TabsHighlightItem>
<TabsHighlightItem value="secrets">
<TabsTrigger value="secrets">
<KeyRound className="w-4 h-4 mr-1.5" />Secrets
<SoonBadge />
</TabsTrigger>
</TabsHighlightItem>
</TabsHighlight>
</TabsList>
<div className="flex items-center gap-2">
@@ -1346,6 +1367,33 @@ export function FleetView({ onNavigateToNode }: FleetViewProps) {
<TabsContent value="configuration">
<FleetConfiguration />
</TabsContent>
<TabsContent value="deployments">
<FleetSoonPlaceholder
icon={<Send className="h-4 w-4" />}
kicker="Deployments · Coming soon"
title="Push stacks across the fleet"
description="Push a stack from local to a remote in one move. Side-by-side compose diff, env reconciliation, volume migration plan."
plannedActions={['Push stack', 'Promote env', 'Diff config', 'Plan migration']}
/>
</TabsContent>
<TabsContent value="federation">
<FleetSoonPlaceholder
icon={<Network className="h-4 w-4" />}
kicker="Federation · Coming soon"
title="The fleet as one logical surface"
description="Pin policies, drain a node for maintenance, weight-aware scheduling. This stack runs on whichever node has capacity."
plannedActions={['Pin policy', 'Drain node', 'Cordon', 'Capacity plan']}
/>
</TabsContent>
<TabsContent value="secrets">
<FleetSoonPlaceholder
icon={<KeyRound className="h-4 w-4" />}
kicker="Secrets · Coming soon"
title="One source of truth for env, creds and certs"
description="Push to selected nodes, rotate centrally, audit who-saw-what. Solves silent drift across copies."
plannedActions={['Sync env', 'Rotate', 'Audit', 'Pin to nodes']}
/>
</TabsContent>
</Tabs>
{/* Reconnecting overlay shown when local node is updating */}
@@ -0,0 +1,67 @@
import type { ReactNode } from 'react';
export function SoonBadge() {
return (
<span className="ml-1.5 font-mono text-[9px] uppercase tracking-[0.2em] text-brand">
SOON
</span>
);
}
interface FleetSoonPlaceholderProps {
icon: ReactNode;
kicker: string;
title: string;
description: string;
plannedActions: string[];
}
export function FleetSoonPlaceholder({
icon,
kicker,
title,
description,
plannedActions,
}: FleetSoonPlaceholderProps) {
return (
<div className="mx-auto max-w-3xl rounded-xl border border-card-border border-t-card-border-top bg-card shadow-card-bevel">
<div className="flex flex-col gap-5 p-8">
<div className="flex items-center justify-between gap-3">
<div className="inline-flex items-center gap-2 text-brand">
<span className="inline-flex h-4 w-4 items-center justify-center" aria-hidden>
{icon}
</span>
<span className="font-mono text-[10px] uppercase tracking-[0.2em]">{kicker}</span>
</div>
<span className="rounded border border-brand/20 bg-brand/10 px-1.5 py-0.5 font-mono text-[9px] uppercase tracking-[0.2em] text-brand">
Coming soon
</span>
</div>
<h3 className="font-serif text-2xl italic leading-tight tracking-[-0.01em] text-stat-value">
{title}
</h3>
<p className="font-sans text-sm leading-relaxed text-stat-subtitle">
{description}
</p>
<div className="flex flex-col gap-2 border-t border-border pt-4">
<span className="font-mono text-[10px] uppercase tracking-[0.2em] text-stat-icon">
Planned actions
</span>
<div className="flex flex-wrap gap-1.5">
{plannedActions.map((action) => (
<span
key={action}
className="rounded border border-border bg-muted/40 px-2 py-1 font-mono text-[9px] uppercase tracking-[0.14em] text-stat-subtitle"
>
{action}
</span>
))}
</div>
</div>
</div>
</div>
);
}