feat(dashboard): delete button on server detail page
All checks were successful
Deploy to Production / deploy (push) Successful in 1m27s
All checks were successful
Deploy to Production / deploy (push) Successful in 1m27s
The DELETE /v1/servers/:id endpoint existed (tears down the runner container + removes the row) but nothing in the UI called it, so servers could only be removed via SSH+psql. Adds a danger-variant button in the top-right of the detail header with a native confirm, spinner state, and inline error surfacing. Redirects to /servers on success.
This commit is contained in:
parent
ec819082a6
commit
31bfeed9dd
@ -1,7 +1,7 @@
|
||||
'use client';
|
||||
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useParams } from 'next/navigation';
|
||||
import { useParams, useRouter } from 'next/navigation';
|
||||
import { apiFetch } from '@/lib/api';
|
||||
import { StatusPill } from '@/components/status-pill';
|
||||
import { CodeBlock } from '@/components/code-block';
|
||||
@ -38,11 +38,14 @@ type Tab = 'overview' | 'tools' | 'logs' | 'metrics' | 'secrets' | 'iterate' | '
|
||||
|
||||
export default function ServerDetailPage() {
|
||||
const params = useParams<{ id: string }>();
|
||||
const router = useRouter();
|
||||
const [server, setServer] = useState<ServerDetail | null>(null);
|
||||
const [builds, setBuilds] = useState<BuildSummary[]>([]);
|
||||
const [tab, setTab] = useState<Tab>('overview');
|
||||
const [iteratePrompt, setIteratePrompt] = useState('');
|
||||
const [latestBuildId, setLatestBuildId] = useState<string | null>(null);
|
||||
const [deleting, setDeleting] = useState(false);
|
||||
const [deleteError, setDeleteError] = useState<string | null>(null);
|
||||
|
||||
async function refresh() {
|
||||
const r = await apiFetch<{ server: ServerDetail; builds: BuildSummary[] }>(
|
||||
@ -72,6 +75,27 @@ export default function ServerDetailPage() {
|
||||
setTab('logs');
|
||||
}
|
||||
|
||||
async function onDelete() {
|
||||
if (!server) return;
|
||||
// Destructive — the running container is torn down and the row is gone.
|
||||
// Browser confirm is enough at this scope (single operator, no users yet);
|
||||
// upgrade to a typed-confirmation dialog once we have customer-tier data.
|
||||
const sure = window.confirm(
|
||||
`Delete "${server.name}" (${server.slug})? The running container is stopped and the server row is removed. This cannot be undone.`,
|
||||
);
|
||||
if (!sure) return;
|
||||
setDeleting(true);
|
||||
setDeleteError(null);
|
||||
try {
|
||||
await apiFetch(`/v1/servers/${server.id}`, { method: 'DELETE' });
|
||||
router.push('/servers');
|
||||
} catch (e) {
|
||||
const detail = (e as { detail?: { error?: string; detail?: string } }).detail;
|
||||
setDeleteError(detail?.detail ?? detail?.error ?? (e as Error).message);
|
||||
setDeleting(false);
|
||||
}
|
||||
}
|
||||
|
||||
if (!server) {
|
||||
return (
|
||||
<div className="mx-auto max-w-7xl px-6 py-8 text-[12.5px] text-[--color-fg-muted]">Loading…</div>
|
||||
@ -114,6 +138,16 @@ export default function ServerDetailPage() {
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
{deleteError && (
|
||||
<div className="mt-2 text-[12px] text-[--color-danger]">
|
||||
Delete failed: {deleteError}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex shrink-0 items-center gap-2">
|
||||
<Button variant="danger" size="sm" onClick={onDelete} disabled={deleting}>
|
||||
{deleting ? 'Deleting…' : 'Delete server'}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user