small feat: svg animation for servers, codebuild, codePipleline etc...

This commit is contained in:
Zakaria
2026-10-15 10:25:16 -04:00
parent ca1e845cb6
commit 668911ed6e
29 changed files with 10034 additions and 117 deletions
@@ -3,7 +3,7 @@ import {useMutation, useQuery, useQueryClient} from '@tanstack/react-query'
import {useEffect, useState} from 'react'
import {getLoadBalancingDependencies, listFlatItems, putFlatItem, deleteFlatItem} from '@/api/cloudProxyClient'
import {useAccountId} from '@/lib/accountStore'
import {LoadBalancingProgress} from '@/components/LoadBalancingProgress'
import {TargetHealthIcon} from '@/components/LoadBalancingProgress'
import type {CloudResource} from '@/types/resource'
type Props = {
@@ -12,16 +12,19 @@ type Props = {
}
type TargetInstance = {id: string; name: string; privateIp: string | null}
type PendingTarget = {id: string; name: string; port: number}
export function TargetGroupPanel({resource, runtimeReachable}: Props) {
const accountId = useAccountId()
const queryClient = useQueryClient()
const [instanceId, setInstanceId] = useState('')
const [port, setPort] = useState('')
const [pendingTargets, setPendingTargets] = useState<PendingTarget[]>([])
const dependenciesQuery = useQuery({
queryKey: ['target-group-instances', accountId],
queryFn: ({signal}) => getTargetGroupInstances(signal),
enabled: Boolean(resource) && runtimeReachable,
refetchInterval: pendingTargets.length ? 500 : false,
})
const healthQuery = useQuery({
queryKey: ['target-group-health', accountId, resource?.id],
@@ -34,26 +37,33 @@ export function TargetGroupPanel({resource, runtimeReachable}: Props) {
setPort(typeof resource?.metadata.port === 'number' ? String(resource.metadata.port) : '')
}, [resource?.id, resource?.metadata.port])
useEffect(() => {
const targets = healthQuery.data?.items ?? []
setPendingTargets((current) => current.filter((pending) => {
const state = stringValue(targets.find((target) => stringValue(target.body.id) === pending.id && Number(target.body.port) === pending.port)?.body.state).toLowerCase()
return state !== 'healthy' && state !== 'unhealthy'
}))
}, [healthQuery.data])
useEffect(() => {
if (!pendingTargets.length) {
return
}
const interval = window.setInterval(() => {
void healthQuery.refetch()
}, 1_000)
return () => window.clearInterval(interval)
}, [pendingTargets.length, healthQuery.refetch])
const refresh = () => {
void Promise.all([dependenciesQuery.refetch(), healthQuery.refetch()])
}
const registerMutation = useMutation({
mutationFn: async () => {
const startedAt = Date.now()
await putFlatItem('aws', 'targetgroups', resource?.id ?? '', {id: instanceId, port: Number(port)})
while (true) {
const health = await listFlatItems('aws', 'targetgroups', resource?.id ?? '')
const target = health.items.find((item) => stringValue(item.body.id) === instanceId && Number(item.body.port) === Number(port))
const state = stringValue(target?.body.state).toLowerCase()
if (state === 'healthy' || state === 'unhealthy') {
const remaining = 5_000 - (Date.now() - startedAt)
if (remaining > 0) await new Promise<void>((resolve) => setTimeout(resolve, remaining))
return
}
await new Promise<void>((resolve) => setTimeout(resolve, 500))
}
mutationFn: (target: PendingTarget) => putFlatItem('aws', 'targetgroups', resource?.id ?? '', {id: target.id, port: target.port}),
onSuccess: (_, target) => {
setPendingTargets((current) => [...current, target])
void queryClient.invalidateQueries({queryKey: ['target-group-health', accountId, resource?.id]})
},
onSuccess: () => queryClient.invalidateQueries({queryKey: ['target-group-health', accountId, resource?.id]}),
})
const deregisterMutation = useMutation({
mutationFn: (targetKey: string) => deleteFlatItem('aws', 'targetgroups', resource?.id ?? '', targetKey),
@@ -68,22 +78,23 @@ export function TargetGroupPanel({resource, runtimeReachable}: Props) {
const targets = healthQuery.data?.items ?? []
const validPort = /^\d+$/.test(port) && Number(port) >= 1 && Number(port) <= 65535
const error = healthQuery.error ?? dependenciesQuery.error ?? registerMutation.error ?? deregisterMutation.error
const pendingRows = pendingTargets.filter((pending) => !targets.some((target) => stringValue(target.body.id) === pending.id && Number(target.body.port) === pending.port))
return <section className="table-panel" style={{marginTop: 16}}>
<div className="input-row resource-table-bar">
<div><p className="eyebrow">Target health</p><span className="muted">{targets.length} registered targets in {resource.name}</span></div>
<button className="button" type="button" disabled={!runtimeReachable || healthQuery.isFetching} onClick={refresh}><RefreshCw size={14}/>{healthQuery.isFetching ? 'Loading' : 'Refresh'}</button>
</div>
<form className="dynamic-form" onSubmit={(event) => { event.preventDefault(); if (instanceId && validPort) registerMutation.mutate() }}>
<label className="dynamic-field"><span>Running instance</span><select className="input" value={instanceId} onChange={(event) => setInstanceId(event.target.value)} disabled={dependenciesQuery.isLoading || registerMutation.isPending}><option value="">Select an instance</option>{instances.map((instance) => <option key={instance.id} value={instance.id}>{instance.name} ({instance.id}{instance.privateIp ? `, ${instance.privateIp}` : ''})</option>)}</select></label>
<label className="dynamic-field"><span>Target port</span><input className="input" type="number" min="1" max="65535" value={port} onChange={(event) => setPort(event.target.value)} disabled={registerMutation.isPending}/></label>
<div style={{alignSelf: 'end'}}><button className="button primary" type="submit" disabled={!runtimeReachable || !instanceId || !validPort || registerMutation.isPending}>{registerMutation.isPending ? 'Registering' : 'Register target'}</button></div>
<form className="dynamic-form" onSubmit={(event) => { event.preventDefault(); const instance = instances.find((candidate) => candidate.id === instanceId); if (instance && validPort) registerMutation.mutate({id: instance.id, name: instance.name, port: Number(port)}) }}>
<label className="dynamic-field"><span>Running instance</span><select className="input" value={instanceId} onChange={(event) => setInstanceId(event.target.value)} disabled={dependenciesQuery.isLoading}><option value="">Select an instance</option>{instances.map((instance) => <option key={instance.id} value={instance.id}>{instance.name} ({instance.id}{instance.privateIp ? `, ${instance.privateIp}` : ''})</option>)}</select></label>
<label className="dynamic-field"><span>Target port</span><input className="input" type="number" min="1" max="65535" value={port} onChange={(event) => setPort(event.target.value)}/></label>
<div style={{alignSelf: 'end'}}><button className="button primary" type="submit" disabled={!runtimeReachable || !instanceId || !validPort}>Register target</button></div>
{instances.length === 0 && dependenciesQuery.isSuccess && <p className="muted">No running instances are available in this account.</p>}
{registerMutation.isPending && <div className="dynamic-field--span"><LoadBalancingProgress kind="target" description="Waiting for the target health check to become healthy or unhealthy."/></div>}
</form>
{error instanceof Error && <p className="error-text compact-text" role="alert">{error.message}</p>}
{healthQuery.isLoading && <div className="muted padded">Loading target health</div>}
{healthQuery.isSuccess && targets.length === 0 && <div className="empty compact"><h3>No registered targets</h3><p>Register a running instance to see its ELB health state.</p></div>}
{targets.length > 0 && <div className="cosmos-items-table"><table><thead><tr><th>Instance</th><th>Port</th><th>State</th><th>Reason</th><th>Description</th><th/></tr></thead><tbody>{targets.map((target) => <tr key={target.id}><td><code>{stringValue(target.body.id)}</code></td><td>{stringValue(target.body.port)}</td><td>{stringValue(target.body.state) || '-'}</td><td>{stringValue(target.body.reason) || '-'}</td><td>{stringValue(target.body.description) || '-'}</td><td><button className="icon-btn danger" type="button" title="Deregister target" aria-label={`Deregister ${stringValue(target.body.id)}`} disabled={!runtimeReachable || deregisterMutation.isPending} onClick={() => deregisterMutation.mutate(target.id)}><Trash2 size={14}/></button></td></tr>)}</tbody></table></div>}
{healthQuery.isSuccess && targets.length === 0 && pendingRows.length === 0 && <div className="empty compact"><h3>No registered targets</h3><p>Register a running instance to see its ELB health state.</p></div>}
{(targets.length > 0 || pendingRows.length > 0) && <div className="cosmos-items-table target-health-table"><table><thead><tr><th>Instance</th><th>Port</th><th>State</th><th>Reason</th><th>Description</th><th/></tr></thead><tbody>{pendingRows.map((target) => <tr key={`${target.id}:${target.port}`}><td><code>{target.id}</code></td><td>{target.port}</td><td><span className="target-state"><TargetHealthIcon state="checking"/>Checking</span></td><td>-</td><td>Waiting for target health.</td><td/></tr>)}{targets.map((target) => { const state = stringValue(target.body.state); return <tr key={target.id}><td><code>{stringValue(target.body.id)}</code></td><td>{stringValue(target.body.port)}</td><td><span className="target-state"><TargetHealthIcon state={state}/>{state || '-'}</span></td><td>{stringValue(target.body.reason) || '-'}</td><td>{stringValue(target.body.description) || '-'}</td><td><button className="icon-btn danger" type="button" title="Deregister target" aria-label={`Deregister ${stringValue(target.body.id)}`} disabled={!runtimeReachable || deregisterMutation.isPending} onClick={() => deregisterMutation.mutate(target.id)}><Trash2 size={14}/></button></td></tr>})}</tbody></table></div>}
</section>
}