diff --git a/frontend/packages/shipwright-plugin/src/actions/useBuildActions.ts b/frontend/packages/shipwright-plugin/src/actions/useBuildActions.ts index 79602299c5b..fc3d9fdcacc 100644 --- a/frontend/packages/shipwright-plugin/src/actions/useBuildActions.ts +++ b/frontend/packages/shipwright-plugin/src/actions/useBuildActions.ts @@ -16,8 +16,9 @@ const useBuildActions = (build: Build) => { const history = useHistory(); const [kindObj, inFlight] = useK8sModel(referenceFor(build)); - const actions = React.useMemo(() => { - const start: Action = { + const actionsMenu = React.useMemo(() => { + const actions: Action[] = []; + actions.push({ id: 'shipwright-build-start', label: t('shipwright-plugin~Start'), cta: () => { @@ -35,34 +36,36 @@ const useBuildActions = (build: Build) => { resource: BuildRunModel.plural, namespace: build.metadata?.namespace, }, - }; - const startLastRun: Action = { - id: 'shipwright-build-start-last-run', - label: t('shipwright-plugin~Start last run'), - disabled: !build.latestBuild, - cta: () => { - rerunBuildRun(build.latestBuild) - .then((newBuildRun) => { - history.push(resourceObjPath(newBuildRun, referenceFor(newBuildRun))); - }) - .catch((err) => { - const error = err.message; - errorModal({ error }); - }); - }, - accessReview: { - verb: 'create', - group: BuildRunModel.apiGroup, - resource: BuildRunModel.plural, - namespace: build.metadata?.namespace, - }, - }; - return build.latestBuild - ? [start, startLastRun, ...getCommonResourceActions(kindObj, build)] - : [start, ...getCommonResourceActions(kindObj, build)]; + }); + + if (build.latestBuild) { + actions.push({ + id: 'shipwright-build-start-last-run', + label: t('shipwright-plugin~Start last run'), + disabled: !build.latestBuild, + cta: () => { + rerunBuildRun(build.latestBuild) + .then((newBuildRun) => { + history.push(resourceObjPath(newBuildRun, referenceFor(newBuildRun))); + }) + .catch((err) => { + const error = err.message; + errorModal({ error }); + }); + }, + accessReview: { + verb: 'create', + group: BuildRunModel.apiGroup, + resource: BuildRunModel.plural, + namespace: build.metadata?.namespace, + }, + }); + } + actions.push(...getCommonResourceActions(kindObj, build)); + return actions; }, [t, build, kindObj, history]); - return [actions, !inFlight, undefined]; + return [actionsMenu, !inFlight, undefined]; }; export default useBuildActions; diff --git a/frontend/packages/shipwright-plugin/src/components/build-list/BuildTable.tsx b/frontend/packages/shipwright-plugin/src/components/build-list/BuildTable.tsx index 2ef66646093..a72bfc8b791 100644 --- a/frontend/packages/shipwright-plugin/src/components/build-list/BuildTable.tsx +++ b/frontend/packages/shipwright-plugin/src/components/build-list/BuildTable.tsx @@ -99,8 +99,8 @@ export const BuildRow: React.FC> = ({ obj: build }) => { {build.latestBuild ? ( ) : ( '-' @@ -147,7 +147,7 @@ export const BuildTable: React.FC = (props) => { isList: true, }); - const customData = React.useMemo( + const data = React.useMemo( () => ({ buildRuns: { latestByBuildName: buildRuns.reduce>((acc, buildRun) => { @@ -168,9 +168,7 @@ export const BuildTable: React.FC = (props) => { ); const buildResource = props.data.map((sBuild) => { sBuild.latestBuild = - customData.buildRuns.latestByBuildName[ - `${sBuild.metadata.name}-${sBuild.metadata.namespace}` - ]; + data.buildRuns.latestByBuildName[`${sBuild.metadata.name}-${sBuild.metadata.namespace}`]; return sBuild; }); diff --git a/frontend/packages/shipwright-plugin/src/components/buildrun-details/BuildRunSection.tsx b/frontend/packages/shipwright-plugin/src/components/buildrun-details/BuildRunSection.tsx index 3eb87b83b31..d5f0e4901bc 100644 --- a/frontend/packages/shipwright-plugin/src/components/buildrun-details/BuildRunSection.tsx +++ b/frontend/packages/shipwright-plugin/src/components/buildrun-details/BuildRunSection.tsx @@ -45,14 +45,10 @@ const BuildRunSection: React.FC = ({ buildRun }) => { - {buildRun.status?.startTime && buildRun.status?.completionTime ? ( - <> -
{t('shipwright-plugin~Duration')}
-
- -
- - ) : null} + <> +
{t('shipwright-plugin~Duration')}
+
{buildRun.status?.startTime ? : '-'}
+ ); }; diff --git a/frontend/public/components/build-config.tsx b/frontend/public/components/build-config.tsx index f198130d1f3..8b481f8df4d 100644 --- a/frontend/public/components/build-config.tsx +++ b/frontend/public/components/build-config.tsx @@ -38,7 +38,7 @@ import { BuildConfigModel, BuildModel } from '../models'; import Helmet from 'react-helmet'; import { useK8sWatchResource } from './utils/k8s-watch-hook'; import { Status } from '@console/shared'; -import i18next from 'i18next'; +import { displayDurationInWords } from './utils/build-utils'; const BuildConfigsReference: K8sResourceKindReference = 'BuildConfig'; const BuildsReference: K8sResourceKindReference = 'Build'; @@ -65,8 +65,6 @@ const startBuildAction: KebabAction = (kind, buildConfig) => ({ }, }); -const isLastbuildPresent = (build) => (build ? true : false); - const startLastBuildAction: KebabAction = (kind, buildConfig: BuildConfig) => { return { // t('public~Start last run') @@ -80,7 +78,7 @@ const startLastBuildAction: KebabAction = (kind, buildConfig: BuildConfig) => { const error = err.message; errorModal({ error }); }), - hidden: !isLastbuildPresent(buildConfig.latestBuild), + hidden: !!buildConfig.latestBuild, accessReview: { group: kind.apiGroup, resource: kind.plural, @@ -153,38 +151,6 @@ const tableColumnClasses = [ Kebab.columnClass, ]; -const displayDurationInWords = (start: string, stop: string): string => { - if (!start) { - return '-'; - } - const startTime = new Date(start).getTime(); - const stopTime = stop ? new Date(stop).getTime() : new Date().getTime(); - let duration = (stopTime - startTime) / 1000; - const time = []; - let durationInWords = ''; - while (duration >= 60) { - time.push(duration % 60); - duration = Math.floor(duration / 60); - } - time.push(duration); - if (time[2]) { - durationInWords += `${time[2]} ${ - time[2] > 1 ? i18next.t('public~hours') : i18next.t('public~hour') - } `; - } - if (time[1]) { - durationInWords += `${time[1]} ${ - time[1] > 1 ? i18next.t('public~minutes') : i18next.t('public~minute') - } `; - } - if (time[0]) { - durationInWords += `${time[0]} ${ - time[0] > 1 ? i18next.t('public~seconds') : i18next.t('public~second') - } `; - } - return durationInWords.trim(); -}; - const BuildConfigsTableRow: React.FC> = ({ obj }) => { const latestBuild = obj?.latestBuild; @@ -243,11 +209,8 @@ const isBuildNewerThen = (newBuild: K8sResourceKind, prevBuild: K8sResourceKind const buildStrategy = (buildConfig: K8sResourceKind): BuildStrategyType => buildConfig.spec.strategy.type; -const buildStatus = (buildConfig: BuildConfig) => { - if (buildConfig.latestBuild) { - return buildConfig.latestBuild?.status?.phase; - } - return 'Unknown'; +const getBuildStatus = (buildConfig: BuildConfig) => { + return buildConfig?.latestBuild?.status?.phase || 'Unknown'; }; export const BuildConfigsList: React.SFC = (props) => { @@ -305,7 +268,7 @@ export const BuildConfigsList: React.SFC = (props) => { namespace: props.namespace, isList: true, }); - const customData = React.useMemo( + const data = React.useMemo( () => ({ builds: { latestByBuildName: builds.reduce>((acc, build) => { @@ -327,7 +290,7 @@ export const BuildConfigsList: React.SFC = (props) => { const buildResource = props.data.map((buildConfig) => { buildConfig.latestBuild = - customData.builds.latestByBuildName[ + data.builds.latestByBuildName[ `${buildConfig.metadata.name}-${buildConfig.metadata.namespace}` ]; return buildConfig; @@ -383,9 +346,9 @@ export const BuildConfigsPage: React.FC = (props) => { items: allStrategies, }, { - filterGroupName: t('public~BuildRun status'), + filterGroupName: t('public~Build status'), type: 'build-run-status', - reducer: buildStatus, + reducer: getBuildStatus, items: statusFilters, filter: (filterValue, build: BuildConfig): boolean => { const status = build?.latestBuild?.status?.phase ?? 'Unknown'; diff --git a/frontend/public/components/build.tsx b/frontend/public/components/build.tsx index 8dc1ba1164d..f19765c8a19 100644 --- a/frontend/public/components/build.tsx +++ b/frontend/public/components/build.tsx @@ -24,7 +24,7 @@ import { k8sPatch, K8sKind, } from '../module/k8s'; -import { cloneBuild, formatBuildDuration, getBuildNumber } from '../module/k8s/builds'; +import { cloneBuild, getBuildNumber } from '../module/k8s/builds'; import { DetailsPage, ListPage, Table, TableData, RowFunctionArgs } from './factory'; import { errorModal, confirmModal } from './modals'; import { @@ -54,6 +54,7 @@ import { Area } from './graphs'; import { BuildConfigModel, BuildModel } from '../models'; import { timeFormatter, timeFormatterWithSeconds } from './utils/datetime'; import Dashboard from '@console/shared/src/components/dashboard/Dashboard'; +import { displayDurationInWords } from './utils/build-utils'; const BuildsReference: K8sResourceKindReference = 'Build'; @@ -253,7 +254,6 @@ export const PipelineBuildStrategyAlert: React.FC = () => { export const BuildsDetails: React.SFC = ({ obj: build }) => { const { logSnippet, message, startTimestamp, completionTimestamp } = build.status; const triggeredBy = _.map(build.spec.triggeredBy, 'message').join(', '); - const duration = formatBuildDuration(build); const hasPipeline = build.spec.strategy.type === BuildStrategyType.JenkinsPipeline; const { t } = useTranslation(); const BUILDCONFIG_TO_BUILD_REFERENCE_LABEL = 'openshift.io/build-config.name'; @@ -316,7 +316,10 @@ export const BuildsDetails: React.SFC = ({ obj: build }) => - {duration} + {displayDurationInWords( + build?.status?.startTimestamp, + build?.status?.completionTimestamp, + )} {message} @@ -442,7 +445,10 @@ const BuildsTableRow: React.FC> = ({ obj }) => - + + + + {displayDurationInWords(obj.status?.startTimestamp, obj.status?.completionTimestamp)} @@ -475,8 +481,14 @@ export const BuildsList: React.SFC = (props) => { props: { className: tableColumnClasses[2] }, }, { - title: t('public~Created'), - sortField: 'metadata.creationTimestamp', + title: t('public~Start time'), + sortField: 'status.startTimestamp', + transforms: [sortable], + props: { className: tableColumnClasses[3] }, + }, + { + title: t('public~Duration'), + sortField: 'status.duration', transforms: [sortable], props: { className: tableColumnClasses[3] }, }, diff --git a/frontend/public/components/utils/build-utils.ts b/frontend/public/components/utils/build-utils.ts new file mode 100644 index 00000000000..68c45759328 --- /dev/null +++ b/frontend/public/components/utils/build-utils.ts @@ -0,0 +1,33 @@ +import i18next from 'i18next'; + +export const displayDurationInWords = (start: string, stop: string): string => { + if (!start) { + return '-'; + } + const startTime = new Date(start).getTime(); + const stopTime = stop ? new Date(stop).getTime() : new Date().getTime(); + let duration = Math.round((stopTime - startTime) / 1000); + const time = []; + let durationInWords = ''; + while (duration >= 60) { + time.push(duration % 60); + duration = Math.floor(duration / 60); + } + time.push(duration); + if (time[2]) { + durationInWords += `${time[2]} ${ + time[2] > 1 ? i18next.t('public~hours') : i18next.t('public~hour') + } `; + } + if (time[1]) { + durationInWords += `${time[1]} ${ + time[1] > 1 ? i18next.t('public~minutes') : i18next.t('public~minute') + } `; + } + if (time[0]) { + durationInWords += `${time[0]} ${ + time[0] > 1 ? i18next.t('public~seconds') : i18next.t('public~second') + } `; + } + return durationInWords.trim(); +}; diff --git a/frontend/public/locales/en/public.json b/frontend/public/locales/en/public.json index e044ab688de..9006f659313 100644 --- a/frontend/public/locales/en/public.json +++ b/frontend/public/locales/en/public.json @@ -59,12 +59,6 @@ "Start build": "Start build", "Start last run": "Start last run", "BuildConfig details": "BuildConfig details", - "hours": "hours", - "hour": "hour", - "minutes": "minutes", - "minute": "minute", - "seconds": "seconds", - "second": "second", "Last run": "Last run", "Last run status": "Last run status", "Last run time": "Last run time", @@ -84,7 +78,7 @@ "Cancelled": "Cancelled", "Unknown": "Unknown", "Build strategy": "Build strategy", - "BuildRun status": "BuildRun status", + "Build status": "Build status", "Pipeline build logs are available through Jenkins (linked below)": "Pipeline build logs are available through Jenkins (linked below)", "A link to the Jenkins pipeline build logs will appear below when the build starts": "A link to the Jenkins pipeline build logs will appear below when the build starts", "See Jenkins log": "See Jenkins log", @@ -114,7 +108,6 @@ "Message": "Message", "Log snippet": "Log snippet", "The environment variable editor does not support build strategy: {{ type }}": "The environment variable editor does not support build strategy: {{ type }}", - "Created": "Created", "Builds": "Builds", "Icon": "Icon", "Values for certificate and key should both be either excluded or provided.": "Values for certificate and key should both be either excluded or provided.", @@ -311,6 +304,7 @@ "Hide values": "Hide values", "Reveal values": "Reveal values", "Size": "Size", + "Created": "Created", "ConfigMaps": "ConfigMaps", "ConfigMap details": "ConfigMap details", "Binary data": "Binary data", @@ -1708,6 +1702,12 @@ "Output to": "Output to", "Push secret": "Push secret", "Run policy": "Run policy", + "hours": "hours", + "hour": "hour", + "minutes": "minutes", + "minute": "minute", + "seconds": "seconds", + "second": "second", "Copied": "Copied", "Copy to clipboard": "Copy to clipboard", "Just now": "Just now",