Show action status on the stacks list and dashboard, not just detail (0.41.0)
CI / build-and-push (push) Successful in 1m56s
CI / build-and-push (push) Successful in 1m56s
The status banner added in9d28e12only rendered on the stack detail page, but Update is most often clicked from the stacks list — so in practice the status was invisible. Render it on the stacks list and dashboard too. Actions on different stacks run concurrently from the list, so busy state and status are now keyed by stack id instead of a single value: previously the first action to finish cleared every row's spinner, and each new action overwrote the previous one's status. StacksTable takes an isBusy(id) predicate in place of the single busyId prop. Also bumps the version so the newly version-tagged CI images (f8bfc91) actually differ from the running release — self-update compares tags against APP_VERSION, so shipping without a bump shows no update. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016pMmFFkdfxkoYjcEcpZTa5
This commit is contained in:
@@ -4,20 +4,24 @@ import type { StackActionStatus } from "@/hooks/useStackActions";
|
||||
const TONE = {
|
||||
running: "border-sp-border bg-sp-surface-2 text-sp-text-1",
|
||||
success: "border-sp-green/30 bg-sp-green/10 text-sp-green",
|
||||
error: "border-red-300 bg-red-50 text-red-700 dark:border-red-900/60 dark:bg-red-950/40 dark:text-red-300",
|
||||
error:
|
||||
"border-red-300 bg-red-50 text-red-700 dark:border-red-900/60 dark:bg-red-950/40 dark:text-red-300",
|
||||
} as const;
|
||||
|
||||
/** Persistent status line for a stack action (start/stop/pull/update/…), shown
|
||||
* in addition to the transient toast so the outcome doesn't disappear with it. */
|
||||
/** Persistent status line for one stack action (start/stop/pull/update/…),
|
||||
* shown in addition to the transient toast so the outcome doesn't vanish
|
||||
* with it — and so a failure stays readable until acknowledged. */
|
||||
export function ActionStatusBanner({
|
||||
status,
|
||||
onDismiss,
|
||||
}: {
|
||||
status: StackActionStatus;
|
||||
onDismiss: () => void;
|
||||
onDismiss: (id: string) => void;
|
||||
}) {
|
||||
return (
|
||||
<div className={`flex items-center gap-2 rounded-card border px-3 py-2 text-sm ${TONE[status.phase]}`}>
|
||||
<div
|
||||
className={`flex items-center gap-2 rounded-card border px-3 py-2 text-sm ${TONE[status.phase]}`}
|
||||
>
|
||||
{status.phase === "running" && (
|
||||
<Loader2 className="h-4 w-4 shrink-0 animate-spin text-accent dark:text-accent-dark" />
|
||||
)}
|
||||
@@ -31,7 +35,7 @@ export function ActionStatusBanner({
|
||||
</span>
|
||||
{status.phase !== "running" && (
|
||||
<button
|
||||
onClick={onDismiss}
|
||||
onClick={() => onDismiss(status.id)}
|
||||
className="shrink-0 opacity-60 transition-opacity hover:opacity-100"
|
||||
aria-label="Dismiss"
|
||||
>
|
||||
@@ -41,3 +45,22 @@ export function ActionStatusBanner({
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/** Stacked banners, one per stack currently being acted on. Renders nothing
|
||||
* when idle. */
|
||||
export function ActionStatusList({
|
||||
statuses,
|
||||
onDismiss,
|
||||
}: {
|
||||
statuses: StackActionStatus[];
|
||||
onDismiss: (id: string) => void;
|
||||
}) {
|
||||
if (statuses.length === 0) return null;
|
||||
return (
|
||||
<div className="space-y-2">
|
||||
{statuses.map((s) => (
|
||||
<ActionStatusBanner key={s.id} status={s} onDismiss={onDismiss} />
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -83,7 +83,7 @@ export function AgentStacksSection({ agent, isAdmin }: { agent: Agent; isAdmin:
|
||||
hostCpus={sys.data?.cpu_cores ?? 0}
|
||||
hostMem={sys.data?.mem_total ?? 0}
|
||||
isAdmin={isAdmin}
|
||||
busyId={busyId}
|
||||
isBusy={(id) => busyId === id}
|
||||
loading={stacks.isLoading}
|
||||
linkBase={`/hosts/${agent.id}/stacks`}
|
||||
onStart={(id) => run("start", "Starting", id)}
|
||||
|
||||
@@ -19,7 +19,7 @@ export function StacksTable({
|
||||
hostCpus,
|
||||
hostMem,
|
||||
isAdmin,
|
||||
busyId,
|
||||
isBusy,
|
||||
loading,
|
||||
linkBase = "/stacks",
|
||||
showEdit = false,
|
||||
@@ -36,7 +36,7 @@ export function StacksTable({
|
||||
hostCpus: number;
|
||||
hostMem: number;
|
||||
isAdmin: boolean;
|
||||
busyId: string | null;
|
||||
isBusy: (id: string) => boolean;
|
||||
loading: boolean;
|
||||
linkBase?: string;
|
||||
showEdit?: boolean;
|
||||
@@ -76,7 +76,7 @@ export function StacksTable({
|
||||
hostCpus={hostCpus}
|
||||
hostMem={hostMem}
|
||||
isAdmin={isAdmin}
|
||||
busy={busyId === s.id}
|
||||
busy={isBusy(s.id)}
|
||||
linkBase={linkBase}
|
||||
showEdit={showEdit}
|
||||
showDelete={showDelete}
|
||||
|
||||
Reference in New Issue
Block a user