Simplify launch files history card (Refs #87)
This commit is contained in:
@@ -1,11 +1,11 @@
|
||||
<script lang="ts">
|
||||
import RelativeTime from '$ui/components/RelativeTime.svelte';
|
||||
import type { LaunchRecipeSummary } from '$lib/deployment-plan.js';
|
||||
let { recipe, historical = false, selectionStatus = null }: { recipe: LaunchRecipeSummary; historical?: boolean; selectionStatus?: 'queued' | 'attempted' | null } = $props();
|
||||
let { recipe, historical = false }: { recipe: LaunchRecipeSummary; historical?: boolean } = $props();
|
||||
</script>
|
||||
|
||||
<div class="recipe">
|
||||
<div class="recipe-heading"><div><span class="eyebrow">{historical ? selectionStatus === 'queued' ? 'Queued setup capture' : selectionStatus === 'attempted' ? 'Selected for this attempt' : 'Recorded launch files' : 'Current promoted setup'}</span><h3>{recipe.mapName}</h3></div><span class="game">{recipe.game === 't6' ? 'Black Ops II' : 'Black Ops I'}</span></div>
|
||||
<div class="recipe-heading"><div>{#if !historical}<span class="eyebrow">Current promoted setup</span>{/if}<h3>{recipe.mapName}</h3></div><span class="game">{recipe.game === 't6' ? 'Black Ops II' : 'Black Ops I'}</span></div>
|
||||
<dl>
|
||||
<div><dt>Configuration</dt><dd>{recipe.configSource}</dd></div>
|
||||
<div><dt>Container image</dt><dd class="mono">{recipe.image}</dd></div>
|
||||
@@ -14,7 +14,7 @@
|
||||
<details>
|
||||
<summary>{recipe.files.length} managed file{recipe.files.length === 1 ? '' : 's'} {historical ? 'recorded for this deployment' : 'in the current setup'}</summary>
|
||||
<ul>{#each recipe.files as file}<li><div><strong>{file.name}</strong><span>{file.version !== null ? `v${file.version}` : 'Generated'} · {file.kind} · {file.size.toLocaleString()} bytes</span></div><code>{file.path}</code><small title={file.sha256}>SHA-256 {file.sha256.slice(0, 12)}</small></li>{/each}</ul>
|
||||
<p>{historical ? 'Recorded' : 'Previewed'} <RelativeTime value={recipe.capturedAt} />. {historical ? selectionStatus === 'queued' ? 'Versions are selected again when creation begins. This queued capture does not pin them.' : selectionStatus === 'attempted' ? 'These files were selected for this attempt; this does not confirm installation succeeded. Launch again uses the latest promoted setup.' : 'This records the saved setup for this deployment. Launch again uses the latest promoted setup for this map.' : 'This preview shows the promoted setup at the time above. The deployment history records the files selected when provisioning begins.'}</p>
|
||||
{#if !historical}<p>Previewed <RelativeTime value={recipe.capturedAt} />. This preview shows the promoted setup at the time above. The deployment history records the files selected when provisioning begins.</p>{/if}
|
||||
<small class="fingerprint" title={recipe.fingerprint}>Recipe {recipe.fingerprint.slice(0, 16)}</small>
|
||||
</details>
|
||||
</div>
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
import ProvisioningProgress from '$ui/components/ProvisioningProgress.svelte';
|
||||
import ProvisioningActivity from '$ui/components/ProvisioningActivity.svelte';
|
||||
import LaunchRecipe from '$ui/components/LaunchRecipe.svelte';
|
||||
import DeployServerDialog from '$ui/components/DeployServerDialog.svelte';
|
||||
import Freshness from '$ui/components/Freshness.svelte';
|
||||
import RelativeTime from '$ui/components/RelativeTime.svelte';
|
||||
import { timestampISO, type Timestamp } from '$lib/time-zone.js';
|
||||
@@ -33,7 +32,6 @@
|
||||
loadError: '', actionError: '', notice: '', refreshedAt: initial.generatedAt
|
||||
});
|
||||
let now = $state(Date.parse(initial.generatedAt));
|
||||
let launchOpen = $state(false);
|
||||
let copyStatus = $state('');
|
||||
const controller = createProvisioningRefresh<ProvisioningDetail>({ validate: isProvisioningDetail, onChange: value => { view = value; } });
|
||||
const detail = $derived(view.detail);
|
||||
@@ -47,11 +45,10 @@
|
||||
const duration = $derived(finishedAt || active ? elapsedTime(startedAt, finishedAt ? Date.parse(finishedAt) : now) : 'Not recorded');
|
||||
const serverHref = $derived(deployment && deployment.server_exists && !deployment.server_deleted_at ? `/servers/${encodeURIComponent(deployment.server)}` : null);
|
||||
const recipeStatus = $derived(detail?.progress?.recipeStatus ?? null);
|
||||
const recipeHeading = $derived(recipeStatus === 'queued' ? 'Setup captured when queued' : recipeStatus === 'attempted' ? 'Files selected for this attempt' : 'Recorded launch files');
|
||||
const recipeSelection = $derived(recipeStatus === 'queued' ? 'Captured when queued' : recipeStatus === 'attempted' ? 'Selected when creation began' : 'Recorded setup; selection time unknown');
|
||||
$effect(() => {
|
||||
const snapshot = data.detail;
|
||||
untrack(() => {
|
||||
launchOpen = false;
|
||||
copyStatus = '';
|
||||
now = Date.parse(snapshot.generatedAt);
|
||||
controller.setOperation(snapshot.deployment.id, snapshot, snapshot.generatedAt);
|
||||
@@ -186,13 +183,11 @@
|
||||
{#if pool.holders.length}<details><summary>Current slot holders</summary><ul>{#each pool.holders as holder}<li>{#if holder.id}<a href={`/provisioning/${encodeURIComponent(holder.id)}`}>{serverTitle(holder.server)}</a>{:else}<span>{serverTitle(holder.server)} · operation not recorded</span>{/if}</li>{/each}</ul></details>{/if}
|
||||
</div>{/each}<p class="small">Capacity snapshot <RelativeTime value={detail.capacity.evaluatedAt} missing="Not recorded" /></p>
|
||||
</div></Card>{/if}
|
||||
<Card as="section" padding="md" ariaLabel="Recorded launch inputs"><div class="context-section recipe"><h2>{recipeHeading}</h2>
|
||||
<Card as="section" padding="md" ariaLabel="Launch files"><div class="context-section recipe"><h2>Launch files</h2>
|
||||
{#if detail.recipe}
|
||||
{#if detail.blueprintName}<p>From {detail.blueprintName}</p>{/if}
|
||||
<p>{recipeStatus === 'queued' ? 'This capture does not pin versions. The latest promoted setup is selected again when creation begins.' : recipeStatus === 'attempted' ? 'These files were selected when creation began. Selection alone does not confirm successful installation.' : 'This is the saved recipe for this operation. The point at which it was selected was not recorded.'}</p>
|
||||
<p class="small">Captured {@render dateTime(detail.recipe.capturedAt)}</p>
|
||||
<LaunchRecipe recipe={detail.recipe} historical selectionStatus={recipeStatus} />
|
||||
<div class="launch-another"><Button variant="ghost" onclick={() => launchOpen = true}>Launch another server →</Button><p>Creates a separate server with a fresh ID and port using the latest promoted files. It does not repair this instance.</p></div>
|
||||
<p class="small">{recipeSelection} · <RelativeTime value={detail.recipe.capturedAt} missing="Capture time not recorded" /></p>
|
||||
<LaunchRecipe recipe={detail.recipe} historical />
|
||||
{:else}<p>{['create', 'rebuild'].includes(deployment.action) ? 'No launch recipe was recorded for this operation.' : 'This operation uses the existing instance. No new launch files were selected.'}</p>{/if}
|
||||
</div></Card>
|
||||
<details class="technical-details">
|
||||
@@ -212,7 +207,6 @@
|
||||
</details>
|
||||
</aside>
|
||||
</div>
|
||||
{#if detail.recipe}<DeployServerDialog bind:open={launchOpen} sourceDeploymentId={deployment.id} />{/if}
|
||||
{/key}
|
||||
{:else}<div class="loading" role="status">Loading provisioning operation…</div>{/if}
|
||||
</div>
|
||||
@@ -244,7 +238,6 @@
|
||||
.copy-link { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; font-size: var(--text-xs); color: var(--text-secondary); }
|
||||
.related, .capacity-pool ul { list-style: none; padding: 0; margin: 0; display: grid; gap: var(--space-4); }.related li { display: grid; gap: var(--space-1); font-size: var(--text-sm); overflow-wrap: anywhere; }.related span { color: var(--text-tertiary); font-size: var(--text-xs); }.related strong { font-weight: var(--weight-medium); }
|
||||
.capacity-pool { display: grid; gap: var(--space-3); font-size: var(--text-xs); }.capacity-pool + .capacity-pool { border-top: 1px solid var(--border-subtle); padding-top: var(--space-4); }.capacity-heading { display: flex; justify-content: space-between; gap: var(--space-2); flex-wrap: wrap; }.capacity-heading strong { font-weight: var(--weight-medium); }.capacity-heading span { color: var(--text-secondary); }.capacity-pool summary { cursor: pointer; color: var(--text-secondary); }.capacity-pool ul { gap: var(--space-2); margin-top: var(--space-3); line-height: 1.6; }
|
||||
.launch-another { display: grid; gap: var(--space-2); justify-items: start; border-top: 1px solid var(--border-subtle); padding-top: var(--space-4); }.launch-another p { font-size: var(--text-xs); }
|
||||
.loading { min-height: 240px; padding: var(--space-6); border: 1px solid var(--border-subtle); border-radius: var(--radius-lg); color: var(--text-secondary); }
|
||||
@media (max-width: 1000px) { .detail-grid { grid-template-columns: minmax(0, 1fr); }.context-column { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
|
||||
@media (max-width: 600px) { .page { gap: var(--space-5); }.outcome { padding: var(--space-4); gap: var(--space-4); }.outcome h2 { font-size: var(--text-lg); }.outcome-message p { font-size: var(--text-sm); }.context-column { grid-template-columns: minmax(0, 1fr); }.technical-details > summary { padding: var(--space-4); }.technical-content { padding: 0 var(--space-4) var(--space-4); }.timing { gap: var(--space-4); }.timing > div:last-child { flex-basis: 100%; }.detail-grid { gap: var(--space-6); } }
|
||||
|
||||
Reference in New Issue
Block a user