diff --git a/src/lib/deployment/status.ts b/src/lib/deployment/status.ts new file mode 100644 index 00000000..7293bcc2 --- /dev/null +++ b/src/lib/deployment/status.ts @@ -0,0 +1,42 @@ +type DeploymentStatusFields = { + action: Api.DeploymentAction + status: Api.DeploymentStatus +} + +export type DeploymentStatusTone = 'positive' | 'warning' | 'negative' + +export type DeploymentStatusView = { + label: string + tone: DeploymentStatusTone +} + +/** + * Canonical status copy + tone for a deployment. Shared by the list page and + * the detail header so the two cannot drift. + * + * Action is checked before status: a pending delete also has `status: 'pending'`, + * which would otherwise label as Pending. + */ +export function deploymentStatus (d: DeploymentStatusFields): DeploymentStatusView { + let label: string + if (d.action === 'pause') label = 'Paused' + else if (d.action === 'delete') label = 'Deleting' + else if (d.status === 'pending') label = 'Pending' + else if (d.status === 'error') label = 'Error' + else if (d.status === 'cancelled') label = 'Cancelled' + else label = 'Success' + + let tone: DeploymentStatusTone + if (d.status === 'success' && d.action === 'pause') tone = 'warning' + else if (d.status === 'success') tone = 'positive' + else if (d.status === 'pending') tone = 'warning' + else tone = 'negative' + + return { label, tone } +} + +/** Pill next to the name. Hidden for a healthy running deployment. */ +export function deploymentStatusPill (d: DeploymentStatusFields): DeploymentStatusView | null { + if (d.action === 'deploy' && d.status === 'success') return null + return deploymentStatus(d) +} diff --git a/src/routes/(auth)/(project)/deployment/+page.svelte b/src/routes/(auth)/(project)/deployment/+page.svelte index 5ed0afd8..192f57c2 100644 --- a/src/routes/(auth)/(project)/deployment/+page.svelte +++ b/src/routes/(auth)/(project)/deployment/+page.svelte @@ -8,6 +8,7 @@ import { onMount } from 'svelte' import { getPermissionContext } from '$lib/permission' import { registerPageActions } from '$lib/pageactions/store.svelte' + import { deploymentStatusPill } from '$lib/deployment/status' import type { PageData } from './$types' const { data }: { data: PageData } = $props() @@ -55,31 +56,6 @@ Worker: { icon: 'fa-gears', label: 'Worker' }, CronJob: { icon: 'fa-clock', label: 'Cron Job' } } - - /** - * Static status label for the pill next to the name. A healthy running - * deployment returns null so its row stays clean — only noteworthy states - * (deleting / paused / deploying / failed / cancelled) get a labelled pill, - * mirroring the live icon to its left. - * - * A pending delete reuses the same `status: 'pending'` as a fresh deploy, so - * the action has to be checked first — otherwise a deployment being torn down - * would mislabel as "Deploying". - */ - function statusPill (it: Api.DeploymentListItem): { label: string, tone: string } | null { - if (it.action === 'delete') { - return { label: 'Deleting', tone: 'negative' } - } - if (it.action === 'pause') { - return { label: 'Paused', tone: 'warning' } - } - switch (it.status) { - case 'pending': return { label: 'Deploying', tone: 'info' } - case 'error': return { label: 'Failed', tone: 'negative' } - case 'cancelled': return { label: 'Cancelled', tone: 'muted' } - default: return null - } - }