Add operation flows and activity logs for betas and campaigns
This commit is contained in:
@@ -7,6 +7,8 @@
|
||||
import SectionHeader from '$ui/components/primitives/SectionHeader.svelte';
|
||||
import Breadcrumbs from '$ui/components/primitives/Breadcrumbs.svelte';
|
||||
import EmptyState from '$ui/components/primitives/EmptyState.svelte';
|
||||
import OperationHistory from '$ui/components/OperationHistory.svelte';
|
||||
import { betaFlow, type ActivityEvent } from '$lib/operation-flow.js';
|
||||
|
||||
interface BetaRow {
|
||||
id: number; script_id: number; script_version_id: number;
|
||||
@@ -31,6 +33,9 @@
|
||||
let beta = $state<BetaRow | null>(null);
|
||||
let scripts = $state<BetaScript[]>([]);
|
||||
let servers = $state<BetaServerRow[]>([]);
|
||||
let events = $state<ActivityEvent[]>([]);
|
||||
let loadError = $state('');
|
||||
let request = 0;
|
||||
let loading = $state(true);
|
||||
let flash = $state<string | null>(null);
|
||||
let acting = $state<string | null>(null);
|
||||
@@ -38,18 +43,24 @@
|
||||
let id = $derived(Number.parseInt($page.params.id ?? '0', 10));
|
||||
|
||||
async function load(): Promise<void> {
|
||||
const res = await fetch(`/api/betas/${id}`);
|
||||
if (!res.ok) { loading = false; return; }
|
||||
const body = (await res.json()) as { beta: BetaRow; scripts: BetaScript[]; servers: BetaServerRow[]; };
|
||||
beta = body.beta;
|
||||
scripts = body.scripts;
|
||||
servers = body.servers;
|
||||
loading = false;
|
||||
const current = ++request;
|
||||
const requestedId = id;
|
||||
try {
|
||||
const res = await fetch(`/api/betas/${requestedId}`);
|
||||
if (!res.ok) throw new Error(res.status === 404 ? 'Beta not found.' : 'Could not refresh beta.');
|
||||
const body = await res.json();
|
||||
if (current !== request || requestedId !== id) return;
|
||||
beta = body.beta; scripts = body.scripts; servers = body.servers; events = body.events;
|
||||
loadError = '';
|
||||
} catch (err) {
|
||||
if (current === request && requestedId === id) loadError = (err as Error).message;
|
||||
} finally { if (current === request && requestedId === id) loading = false; }
|
||||
}
|
||||
|
||||
$effect(() => { id; beta = null; loading = true; loadError = ''; void load(); });
|
||||
|
||||
let poll: ReturnType<typeof setInterval> | null = null;
|
||||
onMount(() => {
|
||||
load();
|
||||
poll = setInterval(load, 5000);
|
||||
});
|
||||
onDestroy(() => { if (poll) clearInterval(poll); });
|
||||
@@ -106,6 +117,9 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:head><title>Beta #{id} | Electric Cherry</title></svelte:head>
|
||||
{#if loadError}<p role="alert" class="err">{loadError} <button onclick={load}>Retry</button></p>{/if}
|
||||
|
||||
{#if loading}
|
||||
<div class="loading">Loading…</div>
|
||||
{:else if !beta}
|
||||
@@ -122,6 +136,15 @@
|
||||
{/snippet}
|
||||
</SectionHeader>
|
||||
|
||||
<Card padding="lg">
|
||||
<div class="opts">
|
||||
<div class="opt"><span class="opt-k">Started</span><span class="opt-v">{new Date(b.opened_at).toLocaleString()}</span></div>
|
||||
<div class="opt"><span class="opt-k">Finished</span><span class="opt-v">{b.closed_at ? new Date(b.closed_at).toLocaleString() : 'In progress'}</span></div>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
<OperationHistory steps={betaFlow(b, servers)} {events} active={b.state === 'running' || b.state === 'ready-promote'} />
|
||||
|
||||
<!-- Artifacts -->
|
||||
<Card padding="lg">
|
||||
<div class="panel-head">
|
||||
@@ -173,9 +196,9 @@
|
||||
<Badge variant={stageVariant(bs.stage)} size="sm" uppercase>{bs.stage}</Badge>
|
||||
<div class="ticks" aria-label="pipeline progress">
|
||||
<span class:on={bs.deployed_at}>Deploy</span>
|
||||
<span class:on={bs.stage !== 'awaiting-empty' && bs.stage !== 'deploying'}>Empty</span>
|
||||
<span class:on={bs.restarted_at || bs.stage === 'restarting'}>Empty</span>
|
||||
<span class:on={bs.restarted_at}>Restart</span>
|
||||
<span class:on={bs.healthy_at}>Healthy</span>
|
||||
{#if b.wait_for_health}<span class:on={bs.healthy_at}>Healthy</span>{/if}
|
||||
{#if b.wait_for_match}
|
||||
<span class:on={bs.match_seen_at}>Match</span>
|
||||
{/if}
|
||||
|
||||
Reference in New Issue
Block a user