small feat: svg animation for servers, codebuild, codePipleline etc...
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
import {useEffect, useState} from 'react'
|
||||
import {useMutation, useQuery, useQueryClient} from '@tanstack/react-query'
|
||||
import {ExternalLink, Play, RefreshCw} from 'lucide-react'
|
||||
import pipelineReleaseAnimation from '../../../../svgAsset/Pipleline.svg'
|
||||
import {getCodePipelineState, listCodePipelineExecutions, startCodePipeline, type PipelineState} from '@/api/cloudProxyClient'
|
||||
import {formatDateTime} from '@/lib/format'
|
||||
import type {CloudResource} from '@/types/resource'
|
||||
@@ -8,14 +10,20 @@ interface CodePipelinePanelProps { resource?: CloudResource; runtimeReachable: b
|
||||
|
||||
export function CodePipelinePanel({resource, runtimeReachable}: CodePipelinePanelProps) {
|
||||
const client = useQueryClient()
|
||||
const [releaseExecutionId, setReleaseExecutionId] = useState<string>()
|
||||
const enabled = Boolean(resource && runtimeReachable)
|
||||
const executionsKey = ['codepipeline-executions', resource?.id]
|
||||
const executions = useQuery({queryKey: executionsKey, queryFn: ({signal}) => listCodePipelineExecutions(resource!.id, signal), enabled, refetchInterval: (query) => query.state.data?.some((execution) => active(execution.status)) ? 2_000 : false})
|
||||
const state = useQuery({queryKey: ['codepipeline-state', resource?.id], queryFn: ({signal}) => getCodePipelineState(resource!.id, signal), enabled, refetchInterval: (query) => query.state.data && activeState(query.state.data) ? 2_000 : false})
|
||||
const start = useMutation({mutationFn: () => startCodePipeline(resource!.id), onSuccess: () => { void client.invalidateQueries({queryKey: executionsKey}); void client.invalidateQueries({queryKey: ['codepipeline-state', resource?.id]}) }})
|
||||
const executions = useQuery({queryKey: executionsKey, queryFn: ({signal}) => listCodePipelineExecutions(resource!.id, signal), enabled, refetchInterval: (query) => Boolean(releaseExecutionId) || query.state.data?.some((execution) => active(execution.status)) ? 2_000 : false})
|
||||
const state = useQuery({queryKey: ['codepipeline-state', resource?.id], queryFn: ({signal}) => getCodePipelineState(resource!.id, signal), enabled, refetchInterval: (query) => Boolean(releaseExecutionId) || query.state.data && activeState(query.state.data) ? 2_000 : false})
|
||||
const start = useMutation({mutationFn: () => startCodePipeline(resource!.id), onSuccess: ({id}) => { setReleaseExecutionId(id); void client.invalidateQueries({queryKey: executionsKey}); void client.invalidateQueries({queryKey: ['codepipeline-state', resource?.id]}) }})
|
||||
useEffect(() => { setReleaseExecutionId(undefined) }, [resource?.id])
|
||||
const releaseExecution = executions.data?.find((execution) => execution.id === releaseExecutionId)
|
||||
const releaseInProgress = Boolean(releaseExecutionId && (!releaseExecution || active(releaseExecution.status)))
|
||||
useEffect(() => { if (releaseExecutionId && releaseExecution && !active(releaseExecution.status)) setReleaseExecutionId(undefined) }, [releaseExecution, releaseExecutionId])
|
||||
if (!resource) return null
|
||||
return <section className="table-panel">
|
||||
<div className="dynamic-stage-header"><div><p className="eyebrow">Pipeline execution</p><h3>Run and inspect stages</h3><p className="muted compact-text">Local actions: S3 source, CodeBuild, and CodeDeployToECS for ECS EC2 and Fargate services.</p></div><div className="header-actions"><button className="button" type="button" disabled={!enabled || executions.isFetching || state.isFetching} onClick={() => { void executions.refetch(); void state.refetch() }}><RefreshCw size={13}/>Refresh</button><button className="button primary" type="button" disabled={!enabled || start.isPending} onClick={() => start.mutate()}><Play size={13}/>{start.isPending ? 'Starting' : 'Release change'}</button></div></div>
|
||||
{releaseInProgress && <PipelineReleaseProgress executionId={releaseExecutionId} execution={releaseExecution}/>}
|
||||
{start.isError && <p className="error-text compact-text">{message(start.error, 'Unable to start pipeline execution.')}</p>}
|
||||
{(executions.isError || state.isError) && <p className="error-text compact-text">{message(executions.error ?? state.error, 'Unable to load pipeline execution state.')}</p>}
|
||||
<div className="workflow-execution-list"><div className="workflow-execution-list-heading"><h4>Execution history</h4><span>{executions.data?.length ?? 0}</span></div><div className="workflow-execution-list-table"><table className="table"><thead><tr><th>Execution</th><th>Status</th><th>Started</th><th>Updated</th></tr></thead><tbody>{(executions.data ?? []).map((execution) => <tr key={execution.id}><td><code>{execution.id}</code>{execution.statusSummary && <p className="muted compact-text">{execution.statusSummary}</p>}</td><td>{execution.status ?? '-'}</td><td>{formatDateTime(execution.startTime) ?? '-'}</td><td>{formatDateTime(execution.lastUpdateTime) ?? '-'}</td></tr>)}</tbody></table></div>{!executions.isLoading && executions.data?.length === 0 && <p className="muted compact-text">No executions yet.</p>}</div>
|
||||
@@ -23,6 +31,17 @@ export function CodePipelinePanel({resource, runtimeReachable}: CodePipelinePane
|
||||
</section>
|
||||
}
|
||||
|
||||
function PipelineReleaseProgress({executionId, execution}: {executionId?: string; execution?: {status?: string; statusSummary?: string}}) {
|
||||
return <div className="pipeline-release-progress" role="status" aria-live="polite">
|
||||
<PipelineReleaseAnimation/>
|
||||
<div><p className="eyebrow">Release in progress</p><h3>{execution?.statusSummary || execution?.status || 'Starting pipeline execution'}</h3><p className="muted compact-text">Pipeline execution <code>{executionId}</code> is moving through source, build, and deployment stages.</p></div>
|
||||
</div>
|
||||
}
|
||||
|
||||
function PipelineReleaseAnimation() {
|
||||
return <img className="pipeline-release-art" src={pipelineReleaseAnimation} alt="Animated pipeline release icon"/>
|
||||
}
|
||||
|
||||
function PipelineTimeline({state, loading}: {state?: PipelineState; loading: boolean}) {
|
||||
if (loading) return <p className="muted compact-text">Loading stage state...</p>
|
||||
if (!state) return null
|
||||
|
||||
Reference in New Issue
Block a user