new destruction flow

This commit is contained in:
2026-09-17 16:57:08 -04:00
parent 5f8e03c693
commit a611850649
13 changed files with 349 additions and 96 deletions
+6 -63
View File
@@ -2,7 +2,7 @@
import RoundTelemetry from '$ui/components/RoundTelemetry.svelte';
import { onMount, onDestroy } from 'svelte';
import { page } from '$app/stores';
import { goto } from '$app/navigation';
import DestroyServerModal from '$ui/components/DestroyServerModal.svelte';
import { statusByServer, uiNow } from '$ui/stores/servers.js';
import type { RosterPlayer, Health, RconEndpoint, RconFailure } from '$ui/stores/servers.js';
import { elapsedSince } from '$ui/duration.js';
@@ -90,9 +90,7 @@
let diagnosing = $state(false);
let rconProbe = $state<RconProbeResult | null>(null);
let probingRcon = $state(false);
let destroying = $state(false);
let destroyError = $state<string | null>(null);
let destroyWarnings = $state<string[]>([]);
let destroyOpen = $state(false);
let activeTab = $state<'roster' | 'logs' | 'inventory' | 'diagnostics' | 'history'>('roster');
async function loadServer(): Promise<void> {
@@ -207,35 +205,6 @@
rconProbe = { ok: false, reason: 'send-error', detail: (err as Error).message };
} finally { probingRcon = false; }
}
async function destroyThisServer(): Promise<void> {
if (destroying || !name) return;
const confirmed = confirm(
`Destroy ${name}?\n\nThis stops the container, deletes the Komodo deployment, ` +
`removes the host directory, and removes this server?s campaign details. Server deployment history is retained. Cannot be undone.`
);
if (!confirmed) return;
destroying = true; destroyError = null; destroyWarnings = [];
try {
const res = await fetch(`/api/servers/${encodeURIComponent(name)}`, { method: 'DELETE' });
const body = (await res.json()) as { ok: boolean; error?: string; warnings?: string[] };
if (!body.ok) {
destroyError = body.error ?? 'destroy failed';
destroyWarnings = body.warnings ?? [];
destroying = false;
return;
}
if (body.warnings && body.warnings.length > 0) {
destroyWarnings = body.warnings;
setTimeout(() => goto('/'), 1500);
return;
}
await goto('/');
} catch (err) {
destroyError = (err as Error).message;
destroying = false;
}
}
// Tab config — Diagnostics gets a warning badge when there's an issue.
let tabs = $derived([
{ id: 'roster', label: 'Roster', badge: effectiveRoster?.length ?? null },
@@ -246,6 +215,8 @@
]);
</script>
<DestroyServerModal bind:open={destroyOpen} {name} players={effectivePlayers} />
{#if server}
<div class="server-page" style="view-transition-name: server-{name.replace(/[^a-z0-9_-]/gi, '-')};">
<!-- Hero: name + status + player count + primary actions -->
@@ -255,7 +226,7 @@
<Breadcrumbs items={[{ label: 'Fleet', href: '/' }, { label: name }]} />
{/snippet}
{#snippet actions()}
<Button variant="danger" onclick={destroyThisServer} disabled={destroying} loading={destroying}>
<Button variant="danger" onclick={() => destroyOpen = true}>
Destroy
</Button>
{/snippet}
@@ -307,14 +278,6 @@
<RoundTelemetry {name} />
{#if destroyError}
<div class="destroy-error" role="alert">{destroyError}</div>
{/if}
{#if destroyWarnings.length > 0}
<ul class="destroy-warn">
{#each destroyWarnings as w}<li>{w}</li>{/each}
</ul>
{/if}
</Card>
<div class="tabs-row">
@@ -604,27 +567,7 @@
color: var(--text-tertiary);
}
.destroy-error {
margin-top: var(--space-4);
padding: var(--space-3) var(--space-4);
background: rgba(255, 86, 112, 0.08);
border: 1px solid rgba(255, 86, 112, 0.28);
border-radius: var(--radius-md);
color: var(--state-danger);
font-size: var(--text-sm);
}
.destroy-warn {
margin: var(--space-3) 0 0;
padding: var(--space-3) var(--space-5);
background: rgba(255, 176, 32, 0.08);
border: 1px solid rgba(255, 176, 32, 0.28);
border-radius: var(--radius-md);
color: var(--state-warning);
font-size: var(--text-sm);
list-style: disc inside;
}
/* Tabs row */
/* Tabs row */
.tabs-row {
display: flex;
justify-content: center;