Add operation flows and activity logs for betas and campaigns
This commit is contained in:
@@ -8,6 +8,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 { campaignFlow, type ActivityEvent } from '$lib/operation-flow.js';
|
||||
|
||||
interface FileRow {
|
||||
id: number; server: string; verb: string; rel_path: string;
|
||||
@@ -27,20 +29,32 @@
|
||||
};
|
||||
servers: ServerRow[];
|
||||
files: FileRow[];
|
||||
events: ActivityEvent[];
|
||||
}
|
||||
|
||||
let detail = $state<Detail | null>(null);
|
||||
let loadError = $state('');
|
||||
let request = 0;
|
||||
let openFile = $state<{ id: number; before: string | null; after: string | null; verb: string; relPath: string } | null>(null);
|
||||
|
||||
async function loadDetail(): Promise<void> {
|
||||
const id = $page.params.id;
|
||||
const res = await fetch(`/api/campaigns/${id}`);
|
||||
detail = (await res.json()) as Detail;
|
||||
const current = ++request;
|
||||
try {
|
||||
const res = await fetch(`/api/campaigns/${encodeURIComponent(id ?? '')}`);
|
||||
if (!res.ok) throw new Error(res.status === 404 ? 'Campaign not found.' : 'Could not refresh campaign.');
|
||||
const body = await res.json() as Detail;
|
||||
if (current !== request || id !== $page.params.id) return;
|
||||
detail = body; loadError = '';
|
||||
} catch (err) {
|
||||
if (current === request && id === $page.params.id) loadError = (err as Error).message;
|
||||
}
|
||||
}
|
||||
|
||||
$effect(() => { $page.params.id; detail = null; openFile = null; loadError = ''; void loadDetail(); });
|
||||
|
||||
let poll: ReturnType<typeof setInterval> | null = null;
|
||||
onMount(() => {
|
||||
loadDetail();
|
||||
poll = setInterval(loadDetail, 5000);
|
||||
});
|
||||
onDestroy(() => {
|
||||
@@ -86,6 +100,9 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:head><title>Campaign | Electric Cherry</title></svelte:head>
|
||||
{#if loadError}<p role="alert" class="err">{loadError} <button onclick={loadDetail}>Retry</button></p>{/if}
|
||||
|
||||
{#if detail}
|
||||
{@const action = detail.deployment.action}
|
||||
<div class="page">
|
||||
@@ -107,6 +124,12 @@
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
<OperationHistory
|
||||
steps={campaignFlow(detail.servers, Boolean(detail.deployment.finished_at))}
|
||||
events={detail.events}
|
||||
active={detail.servers.some(s => s.restart_state === 'pending' || s.restart_state === 'failed')}
|
||||
/>
|
||||
|
||||
<section class="section">
|
||||
<div class="section-head">
|
||||
<h2>Servers</h2>
|
||||
@@ -189,7 +212,7 @@
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{:else}
|
||||
{:else if !loadError}
|
||||
<div class="loading">Loading…</div>
|
||||
{/if}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user