diff --git a/src/components/pipelines-metrics/PipelinesMetricsPage.tsx b/src/components/pipelines-metrics/PipelinesMetricsPage.tsx index e141dbb6..f3353167 100644 --- a/src/components/pipelines-metrics/PipelinesMetricsPage.tsx +++ b/src/components/pipelines-metrics/PipelinesMetricsPage.tsx @@ -20,24 +20,6 @@ const PipelinesMetricsPage: React.FC = () => { parsePrometheusDuration('30s'), ); - const sampleData = { - summary: { - total: 120, - 'avg-duration': '544m', - success: 76, - failed: 24, - pending: 3, - running: 3, - cancelled: 14, - 'max-duration': '2m 8s', - 'total-duration': '1h 23m', - 'runs-in-pipelines': 4535, - 'runs-in-repositories': 2342, - 'last-runtime': '7 min ago', - 'success-rate': 100, - }, - }; - return ( <> { = ({ ); }; -export default PipelinesRunsStatusCard; +export default PipelinesRunsStatusCard; \ No newline at end of file diff --git a/src/components/pipelines-overview/PipelinesOverviewPage.tsx b/src/components/pipelines-overview/PipelinesOverviewPage.tsx index b0cc326f..025d591e 100644 --- a/src/components/pipelines-overview/PipelinesOverviewPage.tsx +++ b/src/components/pipelines-overview/PipelinesOverviewPage.tsx @@ -26,63 +26,6 @@ const PipelinesOverviewPage: React.FC = () => { setActiveNamespace(namespace); }, [namespace]); - // TO-DO delete the sample data - const sampleData = { - summary: { - total: 120, - 'avg-duration': '54m', - succeeded: 76, - failed: 24, - completed: 3, - unkwown: 3, - cancelled: 14, - 'max-duration': '2m 8s', - 'total-duration': '1h 23m', - 'runs-in-pipelines': 4535, - 'runs-in-repositories': 2342, - 'last-runtime': '7 min ago', - 'success-rate': 100, - }, - }; - - const mainData = [ - { - repoName: 'repo-1', - pipelineName: 'pipeline-1', - projectName: 'project-1', - summary: sampleData.summary, - }, - { - repoName: 'repo-2', - pipelineName: 'pipeline-2', - projectName: 'project-2', - summary: sampleData.summary, - }, - { - repoName: 'repo-3', - pipelineName: 'pipeline-3', - projectName: 'project-3', - summary: sampleData.summary, - }, - { - repoName: 'repo-4', - pipelineName: 'pipeline-4', - projectName: 'project-4', - summary: sampleData.summary, - }, - { - repoName: 'repo-5', - pipelineName: 'pipeline-5', - projectName: 'project-5', - summary: sampleData.summary, - }, - { - repoName: 'repo-6', - pipelineName: 'pipeline-6', - projectName: 'project-6', - summary: sampleData.summary, - }, - ]; return ( <> @@ -106,7 +49,6 @@ const PipelinesOverviewPage: React.FC = () => { { namespace={namespace} timespan={timespan} interval={interval} - summaryData={sampleData.summary} bordered={true} /> @@ -151,7 +92,7 @@ const PipelinesOverviewPage: React.FC = () => {
- +
); diff --git a/src/components/pipelines-overview/SearchInput.tsx b/src/components/pipelines-overview/SearchInput.tsx index ac3874bb..2cd5f69a 100644 --- a/src/components/pipelines-overview/SearchInput.tsx +++ b/src/components/pipelines-overview/SearchInput.tsx @@ -2,7 +2,12 @@ import { SearchInput } from '@patternfly/react-core'; import * as React from 'react'; import { useTranslation } from 'react-i18next'; -const SearchInputField: React.FC<{ pageFlag: number }> = ({ pageFlag }) => { +type SearchInputProps = { + pageFlag: number, + handleNameChange: (searchKeyword: string) => void; +} + +const SearchInputField: React.FC = ({ pageFlag, handleNameChange }) => { const { t } = useTranslation('plugin__pipeline-console-plugin'); return ( = ({ pageFlag }) => { ? t('Search by pipeline name') : t('Search by repository name') } + onChange={(event, text) => handleNameChange(text)} + onClear={() => handleNameChange('')} /> ); }; diff --git a/src/components/pipelines-overview/dateTime.ts b/src/components/pipelines-overview/dateTime.ts index 3b3e0fc2..a9cdc0c2 100644 --- a/src/components/pipelines-overview/dateTime.ts +++ b/src/components/pipelines-overview/dateTime.ts @@ -77,8 +77,8 @@ export const getDropDownDate = (timespan: number): Date => { export const formatTime = (time: string): string => { const t = time?.split(/[:.]+/); - if (t == undefined) { - return ''; + if (t === undefined) { + return '-'; } let timestring = ''; @@ -97,6 +97,43 @@ export const formatTime = (time: string): string => { return timestring; }; +export const formatTimeLastRunTime = (time: string): string => { + let timeValue = time?.split(/\s+/); + if (timeValue === undefined) { + return '-'; + } + if(timeValue.length > 1){ + // Check for the presence of each component + const hasYear = timeValue.includes('year') || timeValue.includes('years'); + const hasMonth = timeValue.includes('month') || timeValue.includes('months'); + const hasDay = timeValue.includes('day') || timeValue.includes('days'); + + // Return the most significant time component + if (hasYear) { + return timeValue.includes('year') ? timeValue[timeValue.indexOf('year') - 1] + ' year ago' : timeValue[timeValue.indexOf('years') - 1] + ' years ago'; + } else if (hasMonth) { + return timeValue.includes('month') ? timeValue[timeValue.indexOf('month') - 1] + ' month ago' : timeValue[timeValue.indexOf('months') - 1] + ' months ago'; + } else if (hasDay) { + return timeValue.includes('day') ? timeValue[timeValue.indexOf('day') - 1] + ' day ago' : timeValue[timeValue.indexOf('days') - 1] + ' days ago'; + } + else { + return `${timeValue.pop()} ago`; + } + } + else{ + const [hours, minutes, seconds] = time.split(/[:.]/).map(Number); + if (!isNaN(hours) && hours > 0) { + return hours === 1 ? `${hours} hour ago` : `${hours} hours ago`; + } else if (!isNaN(minutes) && minutes > 0) { + return minutes === 1 ? `${minutes} min ago` : `${minutes} mins ago`; + } else if (!isNaN(seconds) && seconds > 0) { + return seconds === 1 ? `${seconds} sec ago` : `${seconds} secs ago`; + } else { + return null; // No significant time component found + } + } +}; + export const dateFormatterNoYear = new Intl.DateTimeFormat( getLastLanguage() || undefined, { diff --git a/src/components/pipelines-overview/list-pages/PipelineRunsForPipelinesList.tsx b/src/components/pipelines-overview/list-pages/PipelineRunsForPipelinesList.tsx index 5b64add5..5bf923a0 100644 --- a/src/components/pipelines-overview/list-pages/PipelineRunsForPipelinesList.tsx +++ b/src/components/pipelines-overview/list-pages/PipelineRunsForPipelinesList.tsx @@ -1,77 +1,78 @@ import * as React from 'react'; -import { EmptyState, EmptyStateVariant } from '@patternfly/react-core'; import { useTranslation } from 'react-i18next'; +import { EmptyState, EmptyStateVariant } from '@patternfly/react-core'; import { sortable } from '@patternfly/react-table'; import { TableColumn, VirtualizedTable, + useActiveColumns, } from '@openshift-console/dynamic-plugin-sdk'; -import { mainDataType } from '../utils'; +import { SummaryProps, sortByNumbers, sortByProperty, sortTimeStrings, listPageTableColumnClasses as tableColumnClasses } from '../utils'; import PipelineRunsForPipelinesRow from './PipelineRunsForPipelinesRow'; type PipelineRunsForPipelinesListProps = { - mainData?: mainDataType[]; + summaryData: SummaryProps[]; + summaryDataFiltered?: SummaryProps[]; }; const PipelineRunsForPipelinesList: React.FC< PipelineRunsForPipelinesListProps -> = ({ mainData }) => { +> = ({ summaryData, summaryDataFiltered }) => { const { t } = useTranslation('plugin__pipeline-console-plugin'); const EmptyMsg = () => ( {t('No PipelineRuns found')} ); - const tableColumnClasses = ['', '', '', '', '', '', '']; - const columns = React.useMemo[]>( + const plrColumns = React.useMemo[]>( () => [ { id: 'pipelineName', title: t('Pipeline'), - sort: 'pipelineName', + sort: (summary, direction: 'asc' | 'desc') => sortByProperty(summary, 'pipelineName', direction), transforms: [sortable], props: { className: tableColumnClasses[0] }, }, { - id: 'projectName', + id: 'namespace', title: t('Project'), - sort: 'projectName', + sort: (summary, direction: 'asc' | 'desc') => sortByProperty(summary, 'namespace', direction), transforms: [sortable], props: { className: tableColumnClasses[1] }, }, { - id: 'totalPipelineruns', + id: 'total', title: t('Total Pipelineruns'), - sort: 'summary.total', + sort: 'total', transforms: [sortable], props: { className: tableColumnClasses[2] }, }, { id: 'totalDuration', title: t('Total duration'), - sort: "summary['total-duration']", + sort: (summary, direction: 'asc' | 'desc') => sortTimeStrings(summary, 'total_duration',direction), transforms: [sortable], props: { className: tableColumnClasses[3] }, }, { id: 'avgDuration', title: t('Average duration'), - sort: "summary['avg-duration']", + sort: (summary, direction: 'asc' | 'desc') => sortTimeStrings(summary, 'avg_duration',direction), transforms: [sortable], props: { className: tableColumnClasses[4] }, }, { id: 'successRate', title: t('Success rate'), - sort: "summary['success-rate']", + sort: (summary, direction: 'asc' | 'desc') => sortByNumbers(summary, 'succeeded',direction), transforms: [sortable], props: { className: tableColumnClasses[5] }, }, { id: 'lastRunTime', title: t('Last run time'), - sort: "summary['last-runtime']", + sort: (summary, direction: 'asc' | 'desc') => sortTimeStrings(summary, 'last_runtime',direction), transforms: [sortable], props: { className: tableColumnClasses[6] }, }, @@ -79,14 +80,17 @@ const PipelineRunsForPipelinesList: React.FC< [t], ); + const [columns] = useActiveColumns({ columns: plrColumns, showNamespaceOverride: false, columnManagementID: '' }); + + return ( ); diff --git a/src/components/pipelines-overview/list-pages/PipelineRunsForPipelinesRow.tsx b/src/components/pipelines-overview/list-pages/PipelineRunsForPipelinesRow.tsx index e2861b5b..77886603 100644 --- a/src/components/pipelines-overview/list-pages/PipelineRunsForPipelinesRow.tsx +++ b/src/components/pipelines-overview/list-pages/PipelineRunsForPipelinesRow.tsx @@ -1,23 +1,38 @@ import * as React from 'react'; -import { mainDataType } from '../utils'; -import { RowProps } from '@openshift-console/dynamic-plugin-sdk'; +import { SummaryProps, getReferenceForModel, listPageTableColumnClasses as tableColumnClasses } from '../utils'; +import { ResourceLink, RowProps, useActiveNamespace } from '@openshift-console/dynamic-plugin-sdk'; +import { formatTime, formatTimeLastRunTime } from '../dateTime'; +import { ALL_NAMESPACES_KEY } from '../../../consts'; +import { PipelineModel } from '../../../models'; -const tableColumnClasses = ['', '', '', '', '', '', '']; -const PipelineRunsForPipelinesRow: React.FC> = ({ +const pipelineReference = getReferenceForModel(PipelineModel); + +const PipelineRunsForPipelinesRow: React.FC> = ({ obj, }) => { + const [activeNamespace] = useActiveNamespace(); + return ( <> - {obj.pipelineName} - {obj.projectName} + + + + {activeNamespace === ALL_NAMESPACES_KEY && + + + } - {obj.summary['runs-in-repositories']} + {obj.total} - {obj.summary['total-duration']} - {obj.summary['avg-duration']} - {obj.summary['success-rate']} - {obj.summary['last-runtime']} + {formatTime(obj.total_duration)} + {formatTime(obj.avg_duration)} + {`${Math.round((100 * obj.succeeded) / obj.total)}%`} + {`${formatTimeLastRunTime(obj.last_runtime)}`} ); }; diff --git a/src/components/pipelines-overview/list-pages/PipelineRunsForRepositoriesList.tsx b/src/components/pipelines-overview/list-pages/PipelineRunsForRepositoriesList.tsx index f3718668..b2d9b3ee 100644 --- a/src/components/pipelines-overview/list-pages/PipelineRunsForRepositoriesList.tsx +++ b/src/components/pipelines-overview/list-pages/PipelineRunsForRepositoriesList.tsx @@ -1,77 +1,79 @@ import * as React from 'react'; -import { EmptyState, EmptyStateVariant } from '@patternfly/react-core'; import { useTranslation } from 'react-i18next'; +import { EmptyState, EmptyStateVariant } from '@patternfly/react-core'; import { sortable } from '@patternfly/react-table'; -import PipelineRunsForRepositoriesRow from './PipelineRunsForRepositoriesRow'; -import { mainDataType } from '../utils'; import { TableColumn, VirtualizedTable, + useActiveColumns, } from '@openshift-console/dynamic-plugin-sdk'; +import PipelineRunsForRepositoriesRow from './PipelineRunsForRepositoriesRow'; +import { SummaryProps, sortByNumbers, sortByProperty, sortTimeStrings, listPageTableColumnClasses as tableColumnClasses } from '../utils'; + type PipelineRunsForRepositoriesListProps = { - mainData?: mainDataType[]; + summaryData: SummaryProps[]; + summaryDataFiltered: SummaryProps[]; }; const PipelineRunsForRepositoriesList: React.FC< PipelineRunsForRepositoriesListProps -> = ({ mainData }) => { +> = ({ summaryData, summaryDataFiltered }) => { const { t } = useTranslation('plugin__pipeline-console-plugin'); const EmptyMsg = () => ( {t('No PipelineRuns found')} ); - const tableColumnClasses = ['', '', '', '', '', '', '']; - const columns = React.useMemo[]>( + const plrColumns = React.useMemo[]>( () => [ { id: 'repoName', title: t('Repository'), - sort: 'repoName', + sort: (summary, direction: 'asc' | 'desc') => sortByProperty(summary, 'repoName', direction), transforms: [sortable], props: { className: tableColumnClasses[0] }, }, { - id: 'projectName', + id: 'namespace', title: t('Project'), - sort: 'projectName', + sort: (summary, direction: 'asc' | 'desc') => sortByProperty(summary, 'namespace', direction), transforms: [sortable], props: { className: tableColumnClasses[1] }, }, { - id: 'totalPipelineruns', + id: 'total', title: t('Total Pipelineruns'), - sort: 'summary.total', + sort: 'total', transforms: [sortable], props: { className: tableColumnClasses[2] }, }, { id: 'totalDuration', title: t('Total duration'), - sort: "summary['total-duration']", + sort: (summary, direction: 'asc' | 'desc') => sortTimeStrings(summary, 'total_duration',direction), transforms: [sortable], props: { className: tableColumnClasses[3] }, }, { id: 'avgDuration', title: t('Average duration'), - sort: "summary['avg-duration']", + sort: (summary, direction: 'asc' | 'desc') => sortTimeStrings(summary, 'avg_duration',direction), transforms: [sortable], props: { className: tableColumnClasses[4] }, }, { id: 'successRate', title: t('Success rate'), - sort: "summary['success-rate']", + sort: (summary, direction: 'asc' | 'desc') => sortByNumbers(summary, 'succeeded',direction), transforms: [sortable], props: { className: tableColumnClasses[5] }, }, { id: 'lastRunTime', title: t('Last run time'), - sort: "summary['last-runtime']", + sort: (summary, direction: 'asc' | 'desc') => sortTimeStrings(summary, 'last_runtime',direction), transforms: [sortable], props: { className: tableColumnClasses[6] }, }, @@ -79,14 +81,16 @@ const PipelineRunsForRepositoriesList: React.FC< [t], ); + const [columns] = useActiveColumns({ columns: plrColumns, showNamespaceOverride: false, columnManagementID: '' }); + return ( ); diff --git a/src/components/pipelines-overview/list-pages/PipelineRunsForRepositoriesRow.tsx b/src/components/pipelines-overview/list-pages/PipelineRunsForRepositoriesRow.tsx index 9aba513f..743757c4 100644 --- a/src/components/pipelines-overview/list-pages/PipelineRunsForRepositoriesRow.tsx +++ b/src/components/pipelines-overview/list-pages/PipelineRunsForRepositoriesRow.tsx @@ -1,23 +1,36 @@ import * as React from 'react'; -import { mainDataType } from '../utils'; -import { RowProps } from '@openshift-console/dynamic-plugin-sdk'; +import { ResourceLink, RowProps, useActiveNamespace } from '@openshift-console/dynamic-plugin-sdk'; +import { SummaryProps, getReferenceForModel, listPageTableColumnClasses as tableColumnClasses } from '../utils'; +import { formatTime, formatTimeLastRunTime } from '../dateTime'; +import { ALL_NAMESPACES_KEY } from '../../../consts'; +import { RepositoryModel } from '../../../models'; -const tableColumnClasses = ['', '', '', '', '', '', '']; +const repositoryReference = getReferenceForModel(RepositoryModel); -const PipelineRunsForRepositoriesRow: React.FC> = ({ +const PipelineRunsForRepositoriesRow: React.FC> = ({ obj, }) => { + const [activeNamespace] = useActiveNamespace(); + return ( <> - {obj.repoName} - {obj.projectName} + + + {activeNamespace === ALL_NAMESPACES_KEY && + + + } - {obj.summary['runs-in-repositories']} + {obj.total} - {obj.summary['total-duration']} - {obj.summary['avg-duration']} - {obj.summary['success-rate']} - {obj.summary['last-runtime']} + {formatTime(obj.total_duration)} + {formatTime(obj.avg_duration)} + {`${Math.round((100 * obj.succeeded) / obj.total)}%`} + {`${formatTimeLastRunTime(obj.last_runtime)}`} ); }; diff --git a/src/components/pipelines-overview/list-pages/PipelineRunsListPage.tsx b/src/components/pipelines-overview/list-pages/PipelineRunsListPage.tsx index 07a109cc..973d83cf 100644 --- a/src/components/pipelines-overview/list-pages/PipelineRunsListPage.tsx +++ b/src/components/pipelines-overview/list-pages/PipelineRunsListPage.tsx @@ -9,37 +9,85 @@ import { ToggleGroup, ToggleGroupItem, } from '@patternfly/react-core'; -import { mainDataType } from '../utils'; import PipelineRunsForRepositoriesList from './PipelineRunsForRepositoriesList'; import PipelineRunsForPipelinesList from './PipelineRunsForPipelinesList'; import StatusDropdown from '../StatusDropdown'; import SearchInputField from '../SearchInput'; +import { SummaryProps, useInterval } from '../utils'; +import { getResultsSummary } from '../../../components/utils/summary-api'; +import { DataType } from '../../../components/utils/tekton-results'; +import { getDropDownDate } from '../dateTime'; +import { ALL_NAMESPACES_KEY } from '../../../consts'; type PipelineRunsForPipelinesListProps = { - mainData?: mainDataType[]; bordered?: boolean; + namespace: string; + timespan: number; + interval: number; }; const PipelineRunsListPage: React.FC = ({ - mainData, bordered, + namespace, + timespan, + interval }) => { const { t } = useTranslation('plugin__pipeline-console-plugin'); const [pageFlag, setPageFlag] = React.useState(1); + const [summaryData, setSummaryData] = React.useState([]); + const [summaryDataFiltered, setSummaryDataFiltered] = React.useState([]); + + const date = getDropDownDate(timespan).toISOString(); + if (namespace == ALL_NAMESPACES_KEY) { + namespace = '-'; + } + const getSummaryData = () => { + getResultsSummary( + namespace, + pageFlag === 1 ? { + summary: 'total_duration,avg_duration,total,succeeded,last_runtime', + data_type: DataType.PipelineRun, + groupBy: 'pipeline', + filter: `data.status.startTime>timestamp("${date}")&&!data.metadata.labels.contains('pipelinesascode.tekton.dev/repository')`, + } : + { + summary: 'total_duration,avg_duration,total,succeeded,last_runtime', + data_type: DataType.PipelineRun, + groupBy: 'repository', + filter: `data.status.startTime>timestamp("${date}")&&data.metadata.labels.contains('pipelinesascode.tekton.dev/repository')`, + }, + ) + .then((response) => { + setSummaryData(response.summary); + setSummaryDataFiltered(response.summary); + }) + .catch((e) => { + throw e; + }); + }; + + useInterval(getSummaryData, interval, namespace, date, pageFlag); + const handlePageChange = (pageNumber: number) => { + setSummaryData([]); + setSummaryDataFiltered([]); setPageFlag(pageNumber); }; + const handleNameChange = (value: string) => { + const filteredData = summaryData.filter((summary) => summary.group_value.split('/')[1].toLowerCase().includes(value.toLowerCase())) + setSummaryDataFiltered(filteredData); + } return ( - + - + @@ -61,9 +109,9 @@ const PipelineRunsListPage: React.FC = ({ {pageFlag === 1 ? ( - + ) : ( - + )} diff --git a/src/components/pipelines-overview/utils.ts b/src/components/pipelines-overview/utils.ts index ae0f48b4..3028d86e 100644 --- a/src/components/pipelines-overview/utils.ts +++ b/src/components/pipelines-overview/utils.ts @@ -1,5 +1,6 @@ import * as React from 'react'; import { useTranslation } from 'react-i18next'; +import { K8sGroupVersionKind, K8sModel, K8sResourceKindReference } from '@openshift-console/dynamic-plugin-sdk'; export const alphanumericCompare = (a: string, b: string): number => { return a.localeCompare(b, undefined, { @@ -20,7 +21,8 @@ export type SummaryProps = { min_duration?: string; total_duration?: string; others?: number; - group_value?: number; + group_value?: any; + last_runtime?: string; }; export type mainDataType = { @@ -30,6 +32,16 @@ export type mainDataType = { summary?: SummaryProps; }; +export const listPageTableColumnClasses = [ + '', //name + '', //namespace + 'pf-m-hidden pf-m-visible-on-md', //total plr + 'pf-m-hidden pf-m-visible-on-md', //total duration + 'pf-m-hidden pf-m-visible-on-xl', //avg duration + 'pf-m-hidden pf-m-visible-on-xl', //success rate + 'pf-m-hidden pf-m-visible-on-xl' //last run time +]; + export const TimeRangeOptions = () => { const { t } = useTranslation('plugin__pipeline-console-plugin'); return { @@ -68,11 +80,92 @@ export const LAST_LANGUAGE_LOCAL_STORAGE_KEY = 'bridge/last-language'; export const getLastLanguage = (): string => localStorage.getItem(LAST_LANGUAGE_LOCAL_STORAGE_KEY); + +export const getReferenceForModel = (model: K8sModel): K8sResourceKindReference => + getReference({ group: model.apiGroup, version: model.apiVersion, kind: model.kind }); + +export const getReference = ({ + group, + version, + kind, +}: K8sGroupVersionKind): K8sResourceKindReference => [group || 'core', version, kind].join('~'); + +export const sortByProperty = (array: SummaryProps[], prop: string, direction: string) => { + return array.sort((a:SummaryProps, b:SummaryProps) => { + const nameA = prop === 'namespace' ? a.group_value.split('/')[0].toLowerCase() : a.group_value.split('/')[1].toLowerCase(); + const nameB = prop === 'namespace' ? b.group_value.split('/')[0].toLowerCase() : b.group_value.split('/')[1].toLowerCase(); + + const numberA = parseInt(nameA.replace(/^\D+/g, '')) || 0; + const numberB = parseInt(nameB.replace(/^\D+/g, '')) || 0; + + const nameComparison = nameA.localeCompare(nameB); + + if (nameComparison === 0) { + return (direction === 'desc' ? numberB - numberA : numberA - numberB); + } + + return (direction === 'desc' ? nameComparison * -1 : nameComparison); + }) +}; + +export const sortTimeStrings = (array: SummaryProps[], prop: string ,direction: string) => { + return array.slice().sort((a, b) => { + const getTimeValue = (timeString) => { + const components = timeString?.split(/\s+/); + let totalSeconds = 0; + + for (const component of components) { + const value = parseInt(component); + if (!isNaN(value)) { + if (component.includes('year')) { + totalSeconds += value * 365 * 24 * 3600; // Assuming 1 year = 365 days + } else if (component.includes('month')) { + totalSeconds += value * 30 * 24 * 3600; // Assuming 1 month = 30 days + } else if (component.includes('day')) { + totalSeconds += value * 24 * 3600; + } else if (component.includes(':')) { + // Parse HH:mm:ss.ms + const timeParts = component.split(':').map(Number); + totalSeconds += timeParts[0] * 3600 + timeParts[1] * 60 + timeParts[2]; + if (timeParts.length === 4) { + totalSeconds += timeParts[3] / 1000; + } + } + } + } + + return totalSeconds; + }; + + const timeA = getTimeValue(a[prop]); + const timeB = getTimeValue(b[prop]); + + return direction === 'asc' ? timeA - timeB : timeB - timeA; + }); +}; + +export const sortByNumbers = (array: SummaryProps[], prop: string ,direction: string) => { + const modifier = direction === 'desc' ? -1 : 1; + + return array.slice().sort((a, b) => { + const valueA = a[prop]; + const valueB = b[prop]; + + // If 0 is a valid value, handle it separately + if (valueA === 0 || valueB === 0) { + return modifier * (valueA - valueB); + } + + return modifier * (valueA || Infinity) - (valueB || Infinity); + }); +}; + export const useInterval = ( getData: () => void, interval: number, namespace: string, date: string, + pageFlag?: number ) => { React.useEffect(() => { getData(); @@ -80,5 +173,6 @@ export const useInterval = ( const intervalID = setInterval(() => getData(), interval); return () => clearInterval(intervalID); } - }, [interval, namespace, date]); + }, [interval, namespace, date, pageFlag]); }; + diff --git a/src/components/utils/tekton-results.ts b/src/components/utils/tekton-results.ts index ff78306a..b1ff3e46 100644 --- a/src/components/utils/tekton-results.ts +++ b/src/components/utils/tekton-results.ts @@ -383,7 +383,7 @@ export const createTektonResultsSummaryUrl = async ( const URL = `https://${tektonResultUrl}/apis/results.tekton.dev/v1alpha2/parents/${namespace}/results/-/records/summary?${new URLSearchParams( { summary: `${options?.summary}`, - ...(options.groupBy ? { group_by: `${options.groupBy}` } : {}), + ...(options?.groupBy ? { group_by: `${options.groupBy}` } : {}), // default sort should always be by `create_time desc` // order_by: 'create_time desc', not supported yet page_size: `${Math.max(