Fix failed provisioning progress states (Refs #80)
This commit is contained in:
@@ -1,12 +1,13 @@
|
||||
<script lang="ts">
|
||||
import OperationTime from '$ui/components/OperationTime.svelte';
|
||||
import type { ProvisioningDetail } from '$lib/provisioning-detail.js';
|
||||
import { provisioningCheckValue, provisioningGroups, progressIcons, progressStateLabels } from '$ui/provisioning-presentation.js';
|
||||
import { activeProvisioningStates, provisioningCheckValue, provisioningGroups, progressIcons, progressStateLabels } from '$ui/provisioning-presentation.js';
|
||||
import Badge from './primitives/Badge.svelte';
|
||||
|
||||
let { detail }: { detail: ProvisioningDetail } = $props();
|
||||
const groups = $derived(provisioningGroups(detail));
|
||||
const progress = $derived(detail.progress);
|
||||
const active = $derived(activeProvisioningStates.includes(detail.deployment.state));
|
||||
let expanded = $state<Record<string, boolean>>({});
|
||||
const evidenceLabels = { pass: 'Passed', waiting: 'Waiting', fail: 'Failed', unknown: 'Unknown' };
|
||||
const sources = { agent: 'Host agent', rcon: 'RCON', telemetry: 'Telemetry', combined: 'RCON and telemetry', manifest: 'Plutonium manifest' };
|
||||
@@ -19,7 +20,7 @@
|
||||
{:else}
|
||||
<ol class="phases">
|
||||
{#each groups as group (group.key)}
|
||||
<li class:current={group.state === 'current' || group.state === 'waiting'} class:failed={group.state === 'failed'}>
|
||||
<li class:current={active && (group.state === 'current' || group.state === 'waiting')} class:failed={group.state === 'failed'}>
|
||||
<details open={expanded[group.key] ?? ['current', 'waiting', 'failed'].includes(group.state)}>
|
||||
<summary
|
||||
onclick={event => { event.preventDefault(); expanded[group.key] = !(expanded[group.key] ?? ['current', 'waiting', 'failed'].includes(group.state)); }}
|
||||
@@ -35,7 +36,7 @@
|
||||
</summary>
|
||||
<ol class="steps">
|
||||
{#each group.steps as step (step.key)}
|
||||
<li aria-current={step.state === 'current' || step.state === 'waiting' ? 'step' : undefined}>
|
||||
<li aria-current={active && (step.state === 'current' || step.state === 'waiting') ? 'step' : undefined}>
|
||||
<span class="step-icon state-{step.state}" aria-hidden="true">{progressIcons[step.state]}</span>
|
||||
<div><strong>{step.label}</strong><span class="step-meta state-{step.state}">{progressStateLabels[step.state]}{#if step.at}{' · '}<span class="time"><OperationTime start={detail.deployment.started_at} value={step.at} /></span>{/if}</span></div>
|
||||
</li>
|
||||
|
||||
@@ -45,6 +45,7 @@ export function provisioningGroups(detail: ProvisioningDetail): ProgressGroup[]
|
||||
const phases = groups.flatMap(group => group.phases);
|
||||
const current = progress.phase ? phases.indexOf(progress.phase) : -1;
|
||||
const active = activeProvisioningStates.includes(detail.deployment.state);
|
||||
const failure = ['failed', 'interrupted'].includes(detail.deployment.state);
|
||||
return groups.map(group => {
|
||||
const steps = group.phases.map((key): ProgressStep => {
|
||||
const checkpoint = progress.checkpoints.find(item => item.key === key);
|
||||
@@ -53,12 +54,14 @@ export function provisioningGroups(detail: ProvisioningDetail): ProgressGroup[]
|
||||
const check = checkKey ? progress.checks.find(item => item.key === checkKey) : null;
|
||||
const index = phases.indexOf(key);
|
||||
const isCurrent = key === progress.phase;
|
||||
const state: ProgressState = check ? ({ pass: 'done', fail: 'failed', waiting: 'waiting', unknown: 'unknown' } as const)[check.result]
|
||||
let state: ProgressState = check ? ({ pass: 'done', fail: 'failed', waiting: 'waiting', unknown: 'unknown' } as const)[check.result]
|
||||
: checkpoint ? 'done'
|
||||
: isCurrent && ['failed', 'interrupted'].includes(detail.deployment.state) ? 'failed'
|
||||
: isCurrent && failure ? 'failed'
|
||||
: isCurrent && active ? progress.blocker || ['waiting', 'queued'].includes(detail.deployment.state) ? 'waiting' : 'current'
|
||||
: current >= 0 && index >= current && detail.deployment.state === 'cancelled' ? 'cancelled'
|
||||
: current >= 0 && index >= current && (failure || detail.deployment.state === 'cancelled') ? 'cancelled'
|
||||
: active && current >= 0 && index > current ? 'pending' : 'unknown';
|
||||
// Preserve completed evidence, but outstanding work cannot keep waiting after failure.
|
||||
if (failure && (state === 'waiting' || (isCurrent && state === 'unknown'))) state = 'failed';
|
||||
const label = key === 'startup-queue' ? 'Admit to startup capacity'
|
||||
: key === 'prepare' ? 'Prepare launch payload'
|
||||
: key === 'container-action' ? `${actionLabels[detail.deployment.action]} instance through host agent`
|
||||
@@ -73,7 +76,7 @@ export function provisioningGroups(detail: ProvisioningDetail): ProgressGroup[]
|
||||
}
|
||||
|
||||
export const progressStateLabels: Record<ProgressState, string> = {
|
||||
done: 'Completed', current: 'In progress', waiting: 'Waiting', failed: 'Needs attention', pending: 'Not started', cancelled: 'Not run', unknown: 'Not recorded'
|
||||
done: 'Completed', current: 'In progress', waiting: 'Waiting', failed: 'Stopped here', pending: 'Not started', cancelled: 'Not run', unknown: 'Not recorded'
|
||||
};
|
||||
export const progressIcons: Record<ProgressState, string> = { done: '✓', current: '•', waiting: '◷', failed: '!', pending: '○', cancelled: '–', unknown: '?' };
|
||||
|
||||
|
||||
Reference in New Issue
Block a user