0.35.0: per-stack Update button on the Stacks page
Adds an inline "Update (pull latest images & recreate)" action to each row of the stacks table, next to start/stop/restart/edit — for both the local host and remote agents. Wires the existing updateImages action and the agent "update" lifecycle action through StacksTable's new onUpdate prop. Also bumps backend/version.py to 0.35.0 so it tracks the frontend version again (it had drifted to 0.33.0 while package.json moved to 0.34.x). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
844655d1c8
commit
a4b1bbcdd1
+1
-1
@@ -1,3 +1,3 @@
|
|||||||
"""Single source of truth for the StackPilot release version."""
|
"""Single source of truth for the StackPilot release version."""
|
||||||
|
|
||||||
APP_VERSION = "0.33.0"
|
APP_VERSION = "0.35.0"
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "stackpilot-frontend",
|
"name": "stackpilot-frontend",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.34.1",
|
"version": "0.35.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
|
|||||||
@@ -81,6 +81,7 @@ export function AgentStacksSection({ agent, isAdmin }: { agent: Agent; isAdmin:
|
|||||||
onStart={(id) => run("start", "Starting", id)}
|
onStart={(id) => run("start", "Starting", id)}
|
||||||
onStop={(id) => run("stop", "Stopping", id)}
|
onStop={(id) => run("stop", "Stopping", id)}
|
||||||
onRestart={(id) => run("restart", "Restarting", id)}
|
onRestart={(id) => run("restart", "Restarting", id)}
|
||||||
|
onUpdate={isAdmin ? (id) => run("update", "Updating", id) : undefined}
|
||||||
emptyText="No stacks on this host."
|
emptyText="No stacks on this host."
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
import { useQueryClient } from "@tanstack/react-query";
|
import { useQueryClient } from "@tanstack/react-query";
|
||||||
import { Play, Square, RotateCw, Pencil, Trash2 } from "lucide-react";
|
import { Play, Square, RotateCw, ArrowUpCircle, Pencil, Trash2 } from "lucide-react";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
import { Card, Spinner, StatusDot, Badge } from "@/components/ui";
|
import { Card, Spinner, StatusDot, Badge } from "@/components/ui";
|
||||||
import { ConfirmDialog } from "@/components/ui/ConfirmDialog";
|
import { ConfirmDialog } from "@/components/ui/ConfirmDialog";
|
||||||
@@ -26,6 +26,7 @@ export function StacksTable({
|
|||||||
onStart,
|
onStart,
|
||||||
onStop,
|
onStop,
|
||||||
onRestart,
|
onRestart,
|
||||||
|
onUpdate,
|
||||||
emptyText,
|
emptyText,
|
||||||
}: {
|
}: {
|
||||||
stacks: StackSummary[] | undefined;
|
stacks: StackSummary[] | undefined;
|
||||||
@@ -41,6 +42,7 @@ export function StacksTable({
|
|||||||
onStart: (id: string) => void;
|
onStart: (id: string) => void;
|
||||||
onStop: (id: string) => void;
|
onStop: (id: string) => void;
|
||||||
onRestart: (id: string) => void;
|
onRestart: (id: string) => void;
|
||||||
|
onUpdate?: (id: string) => void;
|
||||||
emptyText: string;
|
emptyText: string;
|
||||||
}) {
|
}) {
|
||||||
if (loading) return <Spinner />;
|
if (loading) return <Spinner />;
|
||||||
@@ -78,6 +80,7 @@ export function StacksTable({
|
|||||||
onStart={onStart}
|
onStart={onStart}
|
||||||
onStop={onStop}
|
onStop={onStop}
|
||||||
onRestart={onRestart}
|
onRestart={onRestart}
|
||||||
|
onUpdate={onUpdate}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</tbody>
|
</tbody>
|
||||||
@@ -99,6 +102,7 @@ function StackRow({
|
|||||||
onStart,
|
onStart,
|
||||||
onStop,
|
onStop,
|
||||||
onRestart,
|
onRestart,
|
||||||
|
onUpdate,
|
||||||
}: {
|
}: {
|
||||||
stack: StackSummary;
|
stack: StackSummary;
|
||||||
stats?: StackStats;
|
stats?: StackStats;
|
||||||
@@ -112,6 +116,7 @@ function StackRow({
|
|||||||
onStart: (id: string) => void;
|
onStart: (id: string) => void;
|
||||||
onStop: (id: string) => void;
|
onStop: (id: string) => void;
|
||||||
onRestart: (id: string) => void;
|
onRestart: (id: string) => void;
|
||||||
|
onUpdate?: (id: string) => void;
|
||||||
}) {
|
}) {
|
||||||
const qc = useQueryClient();
|
const qc = useQueryClient();
|
||||||
const running = stack.running_count > 0;
|
const running = stack.running_count > 0;
|
||||||
@@ -195,6 +200,15 @@ function StackRow({
|
|||||||
<Play className="h-4 w-4 text-green-500" />
|
<Play className="h-4 w-4 text-green-500" />
|
||||||
</IconBtn>
|
</IconBtn>
|
||||||
)}
|
)}
|
||||||
|
{onUpdate && (
|
||||||
|
<IconBtn
|
||||||
|
title="Update (pull latest images & recreate)"
|
||||||
|
onClick={() => onUpdate(stack.id)}
|
||||||
|
disabled={busy}
|
||||||
|
>
|
||||||
|
<ArrowUpCircle className="h-4 w-4" />
|
||||||
|
</IconBtn>
|
||||||
|
)}
|
||||||
{showEdit && (
|
{showEdit && (
|
||||||
<Link
|
<Link
|
||||||
to={`${linkBase}/${stack.id}/edit`}
|
to={`${linkBase}/${stack.id}/edit`}
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ const STATUS_FILTERS: Record<string, StatusFilter> = {
|
|||||||
|
|
||||||
export function Stacks() {
|
export function Stacks() {
|
||||||
const isAdmin = useAuthStore((s) => s.user?.role === "admin");
|
const isAdmin = useAuthStore((s) => s.user?.role === "admin");
|
||||||
const { busyId, start, stop, restart } = useStackActions();
|
const { busyId, start, stop, restart, updateImages } = useStackActions();
|
||||||
// The dashboard explore bar deep-links here with ?q= / ?filter=.
|
// The dashboard explore bar deep-links here with ?q= / ?filter=.
|
||||||
const [params] = useSearchParams();
|
const [params] = useSearchParams();
|
||||||
const [q, setQ] = useState(params.get("q") ?? "");
|
const [q, setQ] = useState(params.get("q") ?? "");
|
||||||
@@ -137,6 +137,7 @@ export function Stacks() {
|
|||||||
onStart={start}
|
onStart={start}
|
||||||
onStop={stop}
|
onStop={stop}
|
||||||
onRestart={restart}
|
onRestart={restart}
|
||||||
|
onUpdate={isAdmin ? updateImages : undefined}
|
||||||
emptyText={q ? "No stacks match your search." : "No stacks yet. Create one with “New Stack”."}
|
emptyText={q ? "No stacks match your search." : "No stacks yet. Create one with “New Stack”."}
|
||||||
/>
|
/>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
Reference in New Issue
Block a user