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';
|
'use client';
|
||||||
|
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { useParams } from 'next/navigation';
|
import { useParams, useRouter } from 'next/navigation';
|
||||||
import { apiFetch } from '@/lib/api';
|
import { apiFetch } from '@/lib/api';
|
||||||
import { StatusPill } from '@/components/status-pill';
|
import { StatusPill } from '@/components/status-pill';
|
||||||
import { CodeBlock } from '@/components/code-block';
|
import { CodeBlock } from '@/components/code-block';
|
||||||
@ -38,11 +38,14 @@ type Tab = 'overview' | 'tools' | 'logs' | 'metrics' | 'secrets' | 'iterate' | '
|
|||||||
|
|
||||||
export default function ServerDetailPage() {
|
export default function ServerDetailPage() {
|
||||||
const params = useParams<{ id: string }>();
|
const params = useParams<{ id: string }>();
|
||||||
|
const router = useRouter();
|
||||||
const [server, setServer] = useState<ServerDetail | null>(null);
|
const [server, setServer] = useState<ServerDetail | null>(null);
|
||||||
const [builds, setBuilds] = useState<BuildSummary[]>([]);
|
const [builds, setBuilds] = useState<BuildSummary[]>([]);
|
||||||
const [tab, setTab] = useState<Tab>('overview');
|
const [tab, setTab] = useState<Tab>('overview');
|
||||||
const [iteratePrompt, setIteratePrompt] = useState('');
|
const [iteratePrompt, setIteratePrompt] = useState('');
|
||||||
const [latestBuildId, setLatestBuildId] = useState<string | null>(null);
|
const [latestBuildId, setLatestBuildId] = useState<string | null>(null);
|
||||||
|
const [deleting, setDeleting] = useState(false);
|
||||||
|
const [deleteError, setDeleteError] = useState<string | null>(null);
|
||||||
|
|
||||||
async function refresh() {
|
async function refresh() {
|
||||||
const r = await apiFetch<{ server: ServerDetail; builds: BuildSummary[] }>(
|
const r = await apiFetch<{ server: ServerDetail; builds: BuildSummary[] }>(
|
||||||
@ -72,6 +75,27 @@ export default function ServerDetailPage() {
|
|||||||
setTab('logs');
|
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) {
|
if (!server) {
|
||||||
return (
|
return (
|
||||||
<div className="mx-auto max-w-7xl px-6 py-8 text-[12.5px] text-[--color-fg-muted]">Loading…</div>
|
<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>
|
</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>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user