feat(fleet): card-only UI with progressive load and restart ops
Drop the redundant advanced table so fleet stays resource-first cards. Keep multi-select batch service/system restart via prepare→execute, card restarts, overview system ops, and progressive fleet loading.
This commit is contained in:
@@ -78,6 +78,8 @@ export interface InstanceContext {
|
||||
status: 'online' | 'offline' | 'auth-required' | 'degraded' | 'unknown';
|
||||
authentication: 'authenticated' | 'auth-required' | 'unknown';
|
||||
freshness: 'fresh' | 'stale' | 'expired' | 'unknown';
|
||||
/** Control-plane config revision for prepare/execute CAS when known. */
|
||||
revision?: number;
|
||||
resources?: Readonly<{
|
||||
cpuPercent?: number;
|
||||
memoryPercent?: number;
|
||||
@@ -283,6 +285,7 @@ function Page({
|
||||
instance={instance}
|
||||
{...(overviewDataSource ? { dataSource: overviewDataSource } : {})}
|
||||
refreshSignal={detailRefreshSignal}
|
||||
{...(instance.revision !== undefined ? { revision: instance.revision } : {})}
|
||||
/>
|
||||
),
|
||||
}
|
||||
@@ -450,10 +453,51 @@ export function AppShell({
|
||||
const controller = new AbortController();
|
||||
let active = true;
|
||||
setInstanceLoading(true);
|
||||
const fleetLoad = (fleetDataSource ?? defaultFleetDataSource).load(controller.signal).then(
|
||||
(fleet) => ({ ok: true as const, fleet }),
|
||||
() => ({ ok: false as const }),
|
||||
);
|
||||
const fleetLoad = (fleetDataSource ?? defaultFleetDataSource)
|
||||
.load(controller.signal, (partial) => {
|
||||
if (!active) return;
|
||||
const fleetOwner = partial.instances.find((candidate) => candidate.id === routeInstanceId);
|
||||
const status = partial.statuses.get(routeInstanceId);
|
||||
if (!fleetOwner && !status) return;
|
||||
setLoadedInstance((current) => {
|
||||
if (!current || current.id !== routeInstanceId) return current;
|
||||
const routeStatus = !status
|
||||
? current.status
|
||||
: !status.reachable
|
||||
? 'offline'
|
||||
: status.authenticated === false
|
||||
? 'auth-required'
|
||||
: 'online';
|
||||
const freshness = status?.summary?.freshness;
|
||||
const nextRevision = fleetOwner?.revision ?? current.revision;
|
||||
return {
|
||||
...current,
|
||||
origin: fleetOwner?.url ?? current.origin,
|
||||
status: routeStatus,
|
||||
authentication:
|
||||
status?.authenticated === true
|
||||
? 'authenticated'
|
||||
: status?.authenticated === false
|
||||
? 'auth-required'
|
||||
: current.authentication,
|
||||
freshness:
|
||||
freshness === 'fresh' || freshness === 'stale' || freshness === 'expired'
|
||||
? freshness
|
||||
: current.freshness,
|
||||
...(nextRevision === undefined ? {} : { revision: nextRevision }),
|
||||
...(status?.summary?.resources ? { resources: status.summary.resources } : {}),
|
||||
resourceSummaryState: status?.summary?.resources
|
||||
? 'ready'
|
||||
: current.resourceSummaryState === 'ready'
|
||||
? 'ready'
|
||||
: 'loading',
|
||||
};
|
||||
});
|
||||
})
|
||||
.then(
|
||||
(fleet) => ({ ok: true as const, fleet }),
|
||||
() => ({ ok: false as const }),
|
||||
);
|
||||
void resolvedInstanceDataSource.get(routeInstanceId).then(
|
||||
(owner) => {
|
||||
if (!active) return;
|
||||
@@ -469,6 +513,7 @@ export function AppShell({
|
||||
status: 'unknown',
|
||||
authentication: 'unknown',
|
||||
freshness: 'unknown',
|
||||
...(owner.revision === undefined ? {} : { revision: owner.revision }),
|
||||
resourceSummaryState: 'loading',
|
||||
});
|
||||
setInstanceLoading(false);
|
||||
@@ -494,6 +539,7 @@ export function AppShell({
|
||||
? 'auth-required'
|
||||
: 'online';
|
||||
const freshness = status?.summary?.freshness;
|
||||
const nextRevision = fleetOwner?.revision ?? owner.revision;
|
||||
setLoadedInstance({
|
||||
id: owner.id,
|
||||
name: owner.name,
|
||||
@@ -509,6 +555,7 @@ export function AppShell({
|
||||
freshness === 'fresh' || freshness === 'stale' || freshness === 'expired'
|
||||
? freshness
|
||||
: 'unknown',
|
||||
...(nextRevision === undefined ? {} : { revision: nextRevision }),
|
||||
...(status?.summary?.resources ? { resources: status.summary.resources } : {}),
|
||||
resourceSummaryState: status ? 'ready' : 'unavailable',
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user