Skip to content

Commit

Permalink
Add column duration and triggered_by
Browse files Browse the repository at this point in the history
  • Loading branch information
RemiBonnet committed Jan 23, 2025
1 parent 2258efc commit ca7317e
Show file tree
Hide file tree
Showing 3 changed files with 97 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@ import {
getCoreRowModel,
useReactTable,
} from '@tanstack/react-table'
import { StateEnum } from 'qovery-typescript-axios'
import { OrganizationEventOrigin, StateEnum } from 'qovery-typescript-axios'
import { Fragment, useMemo, useState } from 'react'
import { useLocation } from 'react-router-dom'
import { match } from 'ts-pattern'
import { IconEnum } from '@qovery/shared/enums'
import { ENVIRONMENT_LOGS_URL, ENVIRONMENT_STAGES_URL } from '@qovery/shared/routes'
import {
ActionToolbar,
Expand All @@ -18,10 +19,11 @@ import {
TableFilter,
TablePrimitives,
Tooltip,
Truncate,
useModalConfirmation,
} from '@qovery/shared/ui'
import { dateFullFormat } from '@qovery/shared/util-dates'
import { isCancelBuildAvailable, twMerge } from '@qovery/shared/util-js'
import { dateFullFormat, formatDuration } from '@qovery/shared/util-dates'
import { isCancelBuildAvailable, twMerge, upperCaseFirstLetter } from '@qovery/shared/util-js'
import { useCancelDeploymentEnvironment } from '../hooks/use-cancel-deployment-environment/use-cancel-deployment-environment'
import { useDeploymentHistory } from '../hooks/use-deployment-history/use-deployment-history'
import { useEnvironment } from '../hooks/use-environment/use-environment'
Expand Down Expand Up @@ -68,13 +70,13 @@ export function EnvironmentDeploymentList({ environmentId }: EnvironmentDeployme
enableColumnFilter: true,
enableSorting: false,
filterFn: 'arrIncludesSome',
size: 57,
size: 40,
cell: (info) => {
const state = info.row.original.status

return (
<div className="flex items-center justify-between">
<div className="flex flex-col gap-1.5">
<div className="flex flex-col gap-1">
<span className="text-sm font-medium text-neutral-400">
{dateFullFormat(info.row.original.auditing_data.created_at, undefined, 'dd MMM, HH:mm a')}
</span>
Expand Down Expand Up @@ -130,7 +132,7 @@ export function EnvironmentDeploymentList({ environmentId }: EnvironmentDeployme
)
.otherwise(() => null)}
<Tooltip content="Logs">
<ActionToolbar.Button asChild>
<ActionToolbar.Button asChild className="justify-center px-2">
<Link
to={
ENVIRONMENT_LOGS_URL(environment?.organization.id, environment?.project.id, environment?.id) +
Expand All @@ -153,29 +155,78 @@ export function EnvironmentDeploymentList({ environmentId }: EnvironmentDeployme
enableColumnFilter: true,
enableSorting: false,
filterFn: 'arrIncludesSome',
size: 15,
size: 10,
cell: (info) => <span>{info.getValue()}</span>,
}),
columnHelper.accessor((row) => row.stages[0]?.name ?? '', {
header: 'Pipeline',
enableColumnFilter: false,
enableSorting: false,
size: 30,
size: 20,
cell: (info) => <span>{info.row.original.stages.map((v) => v.name)}</span>,
}),
columnHelper.accessor('total_duration', {
header: 'Duration',
enableColumnFilter: false,
enableSorting: true,
size: 3,
cell: (info) => <p>{info.getValue()}</p>,
size: 10,
cell: (info) => {
const state = info.row.original.status

return match(state)
.with(
'DEPLOYING',
'RESTARTING',
'BUILDING',
'DELETING',
'CANCELING',
'STOPPING',
'DEPLOYMENT_QUEUED',
'DELETE_QUEUED',
'STOP_QUEUED',
'RESTART_QUEUED',
() => <span className="text-neutral-350">--</span>
)
.otherwise(() => (
<span className="flex items-center gap-1 text-neutral-350">
<Icon iconName="clock" iconStyle="regular" />
{formatDuration(info.getValue())}
</span>
))
},
}),
columnHelper.accessor('auditing_data.triggered_by', {
columnHelper.accessor('auditing_data.origin', {
header: 'Trigger by',
enableColumnFilter: false,
enableSorting: true,
size: 10,
cell: (info) => <p>{info.getValue()}</p>,
size: 20,
cell: (info) => {
const origin = info.getValue()
const triggeredBy = info.row.original.auditing_data.triggered_by

return (
<div className="flex items-center gap-3">
<div className="flex h-7 w-7 min-w-7 items-center justify-center rounded-full bg-neutral-150 text-neutral-350">
{match(origin)
.with(OrganizationEventOrigin.GIT, () => <Icon iconName="code-branch" />)
.with(OrganizationEventOrigin.CONSOLE, () => <Icon iconName="browser" />)
.with(OrganizationEventOrigin.QOVERY_INTERNAL, () => <Icon iconName="wave-pulse" />)
.with(OrganizationEventOrigin.API, () => <Icon iconName="cloud-arrow-up" />)
.with(OrganizationEventOrigin.CLI, () => <Icon iconName="terminal" />)
.with(OrganizationEventOrigin.TERRAFORM_PROVIDER, () => <Icon name={IconEnum.TERRAFORM} width="12" />)
.otherwise(() => null)}
</div>
<div className="flex flex-col gap-1.5 text-ssm">
<span className="text-neutral-400">
<Truncate text={triggeredBy} truncateLimit={20} />
</span>
<span className="text-neutral-350">
{origin !== 'CLI' && origin !== 'API' ? upperCaseFirstLetter(origin?.replace('_', ' ')) : origin}
</span>
</div>
</div>
)
},
}),
],
[columnHelper]
Expand Down Expand Up @@ -204,7 +255,7 @@ export function EnvironmentDeploymentList({ environmentId }: EnvironmentDeployme
<Table.Row key={headerGroup.id}>
{headerGroup.headers.map((header, i) => (
<Table.ColumnHeaderCell
className={`px-6 ${i === 0 ? 'pl-4' : ''} ${i === 1 ? 'border-r pl-0' : ''} font-medium`}
className={`px-6 ${i === 0 ? 'border-r pl-4' : ''} font-medium`}
key={header.id}
style={{ width: i === 0 ? '20px' : `${header.getSize()}%` }}
>
Expand Down
16 changes: 16 additions & 0 deletions libs/shared/util-dates/src/lib/util-dates.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
dateToFormat,
dateToHours,
dateYearMonthDayHourMinuteSecond,
formatDuration,
} from './util-dates'

describe('util-dates', () => {
Expand Down Expand Up @@ -58,4 +59,19 @@ describe('util-dates', () => {
const result = convertDatetoTimestamp('2023-08-30T10:23:20Z')
expect(result).toBe(1693391000)
})

it('should format a duration with hours, minutes, and seconds correctly', () => {
const result = formatDuration('PT2H30M15S')
expect(result).toBe('02:30:15')
})

it('should format a duration with only minutes and seconds', () => {
const result = formatDuration('PT45M20S')
expect(result).toBe('00:45:20')
})

it('should format a duration with only seconds', () => {
const result = formatDuration('PT50S')
expect(result).toBe('00:00:50')
})
})
16 changes: 16 additions & 0 deletions libs/shared/util-dates/src/lib/util-dates.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,3 +82,19 @@ export function setDayOfTheMonth(date: Date, dayOfTheMonth: number) {
date.setMonth(date.getMonth(), Math.min(dayOfTheMonth, getDaysInMonth(date)))
return date
}

// Format ISO 8601 Duration to HH:mm:ss
export function formatDuration(isoDuration: string): string {
const regex = /PT(?:(\d+)H)?(?:(\d+)M)?(?:(\d+(?:\.\d+)?)S)?/
const matches = isoDuration.match(regex)

if (!matches) {
throw new Error('Invalid ISO 8601 duration format')
}

const hours = parseInt(matches[1] || '0', 10)
const minutes = parseInt(matches[2] || '0', 10)
const seconds = Math.floor(parseFloat(matches[3] || '0'))

return `${addZero(hours)}:${addZero(minutes)}:${addZero(seconds)}`
}

0 comments on commit ca7317e

Please sign in to comment.