mirror of
https://github.com/Studio-Saelix/sencho.git
synced 2026-08-28 03:06:57 +00:00
feat(blueprints): add Fleet > Deployments tab UI, node labels, and docs (#861)
Implements the frontend layer for the Blueprint Model feature (backend landed in PR #860). Fleet > Deployments tab is now live for Skipper+ users; Community users see the existing locked badge. Key additions: - blueprintsApi.ts: typed apiFetch wrappers (localOnly: true on all calls) - BlueprintCatalog: featured hero, filter pills, classification-chipped tile grid - BlueprintEditor: Monaco YAML editor with debounced live classification, label/node selector, three-mode drift radio cards, create/edit modes - BlueprintDeploymentTable: per-node status rows with action buttons (Confirm deploy, Retry, Withdraw/Evict, DATA PINNED HERE for stateful) - EvictionDialog: dual-affordance (Snapshot then evict / Evict and destroy) - StateReviewDialog: fresh-deploy acceptance gate for stateful blueprints - BlueprintClassificationBanner: real-time stateless/stateful/unknown banner - DeploymentsTab: wires catalog, empty state, and create dialog - BlueprintDetail: Sheet with Apply/Edit/overflow, themed delete dialog - NodeLabelPicker + NodeLabelPill: label CRUD per node in NodeManager - FleetView: gates Deployments tab behind isPaid; mounts DeploymentsTab - NodeManager: Labels column with NodeLabelPicker (Skipper+ users) - docs/features/blueprint-model.mdx + docs.json entry + 6 screenshots
This commit is contained in:
@@ -19,6 +19,9 @@ import { Plus, Trash2, Wifi, WifiOff, Star, Pencil, Monitor, Globe, Copy, KeyRou
|
||||
import { formatTimeUntil, formatTimeAgo } from '@/lib/relativeTime';
|
||||
import { SettingsPrimaryButton } from './settings/SettingsActions';
|
||||
import { useMastheadStats } from './settings/MastheadStatsContext';
|
||||
import { NodeLabelPicker } from './blueprints/NodeLabelPicker';
|
||||
import { useLicense } from '@/context/LicenseContext';
|
||||
import { useAuth } from '@/context/AuthContext';
|
||||
|
||||
interface NodeSchedulingSummary {
|
||||
active_tasks: number;
|
||||
@@ -60,6 +63,9 @@ const defaultFormData: NodeFormData = {
|
||||
};
|
||||
|
||||
export function NodeManager() {
|
||||
const { isPaid } = useLicense();
|
||||
const { isAdmin } = useAuth();
|
||||
const canEditLabels = isPaid && isAdmin;
|
||||
const { nodes, refreshNodes } = useNodes();
|
||||
useMastheadStats([
|
||||
{ label: 'NODES', value: `${nodes.length}` },
|
||||
@@ -497,6 +503,7 @@ export function NodeManager() {
|
||||
<TableHead>Mode</TableHead>
|
||||
<TableHead>Endpoint</TableHead>
|
||||
<TableHead>Status</TableHead>
|
||||
<TableHead>Labels</TableHead>
|
||||
<TableHead>Schedules</TableHead>
|
||||
<TableHead>Updates</TableHead>
|
||||
<TableHead className="text-right">Actions</TableHead>
|
||||
@@ -551,6 +558,13 @@ export function NodeManager() {
|
||||
: (node.api_url || '-')}
|
||||
</TableCell>
|
||||
<TableCell>{getStatusBadge(node.status)}</TableCell>
|
||||
<TableCell>
|
||||
{isPaid ? (
|
||||
<NodeLabelPicker nodeId={node.id} canEdit={canEditLabels} />
|
||||
) : (
|
||||
<span className="text-[10px] uppercase tracking-[0.18em] font-mono text-muted-foreground">Upgrade</span>
|
||||
)}
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
{(() => {
|
||||
const summary = nodeSummary[node.id];
|
||||
|
||||
Reference in New Issue
Block a user