diff --git a/src/hooks/timers.ts b/src/hooks/timers.ts index 4bda6727..dc379233 100644 --- a/src/hooks/timers.ts +++ b/src/hooks/timers.ts @@ -18,3 +18,17 @@ export function useDebouncedValue(value: T, ms: number): T { return debounced; } + +export function useNow() { + const [now, setNow] = useState(new Date()); + + useEffect(() => { + const interval = setInterval(() => { + setNow(new Date()); + }, 1000); + + return () => clearInterval(interval); + }, []); + + return now; +} diff --git a/src/intl/en.json b/src/intl/en.json index 49b8849f..e8962355 100644 --- a/src/intl/en.json +++ b/src/intl/en.json @@ -152,6 +152,7 @@ "build": { "title": "Build", "buildSkipped": "This deployment uses a previous build originally created during deployment {deploymentId}", + "duration": "Duration: {duration}s", "completed": "Completed in {elapsed}s", "waitingForLogs": { "title": "Getting ready to build your service", diff --git a/src/modules/deployment/deployment-logs/deployment-logs.tsx b/src/modules/deployment/deployment-logs/deployment-logs.tsx index cb504c66..2289f662 100644 --- a/src/modules/deployment/deployment-logs/deployment-logs.tsx +++ b/src/modules/deployment/deployment-logs/deployment-logs.tsx @@ -19,6 +19,7 @@ import { import { hasBuild } from 'src/application/service-functions'; import { useObserve } from 'src/hooks/lifecycle'; import { useLogs } from 'src/hooks/logs'; +import { useNow } from 'src/hooks/timers'; import { Translate } from 'src/intl/translate'; import { BuildLogs } from './build-logs'; @@ -168,7 +169,6 @@ type BuildSectionHeaderProps = { }; function BuildSectionHeader({ expanded, setExpanded, deployment, lines }: BuildSectionHeaderProps) { - const build = deployment.build; const status = getBuildStatus(deployment); const [StatusIcon, statusColorClassName] = buildStatusMap[status]; @@ -181,18 +181,44 @@ function BuildSectionHeader({ expanded, setExpanded, deployment, lines }: BuildS StatusIcon={StatusIcon} statusColorClassName={statusColorClassName} lastLogLine={status === 'running' ? lines[lines.length - 1] : undefined} - end={ - status === 'completed' && - build !== undefined && ( -
- -
- ) - } + end={} /> ); } +type BuildSectionHeaderEndProps = { + expanded: boolean; + deployment: ComputeDeployment; +}; + +function BuildSectionHeaderEnd({ expanded, deployment }: BuildSectionHeaderEndProps) { + const build = deployment.build; + const status = getBuildStatus(deployment); + const now = useNow(); + + if (build === undefined) { + return; + } + + if (status === 'running' && expanded) { + const duration = Math.floor((now.getTime() - new Date(build.startedAt).getTime()) / 1000); + + return ( +
+ +
+ ); + } + + if (status === 'completed') { + return ( +
+ +
+ ); + } +} + function getBuildStatus(deployment: ComputeDeployment): DeploymentBuildStatus | 'pending' { const { build } = deployment;