diff --git a/dashboard/src/locales/messages/index.ts b/dashboard/src/locales/messages/index.ts index d84cd9f..aa05139 100644 --- a/dashboard/src/locales/messages/index.ts +++ b/dashboard/src/locales/messages/index.ts @@ -5,6 +5,11 @@ export const messages = { global: { filters: 'Filters', cleanAll: 'Clean all', + branch: 'Branch', + status: 'Status', + configs: 'Configs', + architecture: 'Architecture', + timing: 'Timing', }, routes: { deviceMonitor: 'Devices', @@ -39,6 +44,11 @@ export const messages = { filtering: 'Filtering', treeURL: 'Tree URL', refresh: 'Refresh', + branchSubtitle: 'Please select one or more Branches:', + statusSubtitle: 'Please select one or more Status:', + configsSubtitle: 'Please select one or more configs:', + architectureSubtitle: 'Please select one or more Architectures:', + timingSubtitle: 'Please select a range of timing:', }, }, }; diff --git a/dashboard/src/routes/TreeDetails/TreeDetails.tsx b/dashboard/src/routes/TreeDetails/TreeDetails.tsx index 39fedb6..1322418 100644 --- a/dashboard/src/routes/TreeDetails/TreeDetails.tsx +++ b/dashboard/src/routes/TreeDetails/TreeDetails.tsx @@ -1,25 +1,35 @@ import { useParams } from 'react-router-dom'; -import { useEffect, useState } from 'react'; +import { useEffect, useState, useRef } from 'react'; import { FormattedMessage } from 'react-intl'; -import { MdExpandMore } from 'react-icons/md'; - -import TreeDetailsTab from '@/components/Tabs/TreeDetailsTab'; -import ButtonWithIcon from '@/components/Button/ButtonWithIcon'; import { useTreeDetails } from '@/api/TreeDetails'; - +import TreeDetailsTab from '@/components/Tabs/TreeDetailsTab'; import { IListingItem } from '@/components/ListingItem/ListingItem'; import { ISummaryItem } from '@/components/Summary/Summary'; import CardsGroup from '@/components/CardsGroup/CardsGroup'; +import { + TTreeDetailsFilter, + TreeDetails as TreeDetailsType, +} from '@/types/tree/TreeDetails'; + +import TreeDetailsFilter from './TreeDetailsFilter'; const TreeDetails = (): JSX.Element => { const { treeId } = useParams(); - const { data } = useTreeDetails(treeId ?? ''); - + const [filter, setFilter] = useState< + TTreeDetailsFilter | Record + >({}); const [configs, setConfigs] = useState(); const [archictectures, setArchitectures] = useState(); + const initialDataRef = useRef(); + + const { data } = useTreeDetails(treeId ?? '', filter); + + if (!initialDataRef.current) { + initialDataRef.current = data; + } useEffect(() => { if (data) { @@ -47,10 +57,7 @@ const TreeDetails = (): JSX.Element => {
- } - label={} - /> +
void; +} + +type TFilterApplied = { [key: string]: boolean }; + +const sanitizeData = ( + data: TreeDetailsType | undefined, +): [TFilterApplied, TFilterApplied, TFilterApplied, TFilterApplied, string] => { + const status = { TRUE: false, FALSE: false }; + const branches: TFilterApplied = {}; + const configs: TFilterApplied = {}; + const archs: TFilterApplied = {}; + let treeUrl = ''; + + if (data) + data.builds.forEach(b => { + if (b.git_repository_branch) branches[b.git_repository_branch] = false; + if (b.config_name) configs[b.config_name] = false; + if (b.architecture) archs[b.architecture] = false; + if (!treeUrl && b.git_repository_url) treeUrl = b.git_repository_url; + }); + + return [status, branches, configs, archs, treeUrl]; +}; + +const getFilterListFromObj = (filterObj: TFilterApplied): string[] => + Object.keys(filterObj).filter(key => filterObj[key]); + +const TreeDetailsFilter = ({ + data, + onFilter, +}: ITreeDetailsFilter): JSX.Element => { + const intl = useIntl(); + + const [statusObj, branchObj, configObj, archObj, treeUrl] = useMemo( + () => sanitizeData(data), + [data], + ); + + const onClickFilterHandle = useCallback(() => { + const filter: TTreeDetailsFilter = {}; + + filter.config_name = getFilterListFromObj(configObj); + filter.git_repository_branch = getFilterListFromObj(branchObj); + filter.architecture = getFilterListFromObj(archObj); + filter.valid = getFilterListFromObj(statusObj); + + onFilter(filter); + }, [onFilter, configObj, branchObj, archObj, statusObj]); + + const checkboxSectionsProps: ICheckboxSection[] = useMemo(() => { + return [ + { + title: intl.formatMessage({ id: 'global.branch' }), + subtitle: intl.formatMessage({ id: 'filter.branchSubtitle' }), + items: branchObj, + onClickItem: (branch: string, isChecked: boolean) => + (branchObj[branch] = isChecked), + }, + { + title: intl.formatMessage({ id: 'global.status' }), + subtitle: intl.formatMessage({ id: 'filter.statusSubtitle' }), + items: Object.keys(statusObj).reduce((acc, k) => { + const newKey = k == 'TRUE' ? 'valid' : 'invalid'; + acc[newKey] = statusObj[k]; + return acc; + }, {} as TFilterApplied), + onClickItem: (status: string, isChecked: boolean) => + (statusObj[status == 'valid' ? 'TRUE' : 'FALSE'] = isChecked), + }, + { + title: intl.formatMessage({ id: 'global.configs' }), + subtitle: intl.formatMessage({ id: 'filter.configsSubtitle' }), + items: configObj, + onClickItem: (config: string, isChecked: boolean) => + (configObj[config] = isChecked), + }, + { + title: intl.formatMessage({ id: 'global.architecture' }), + subtitle: intl.formatMessage({ id: 'filter.architectureSubtitle' }), + items: archObj, + onClickItem: (arch: string, isChecked: boolean) => + (archObj[arch] = isChecked), + }, + ]; + }, [statusObj, branchObj, configObj, archObj, intl]); + + const checkboxSectionsComponents = useMemo( + () => + checkboxSectionsProps.map(props => ( + + )), + [checkboxSectionsProps], + ); + + return ( + + + {checkboxSectionsComponents} + + ); +}; + +export default TreeDetailsFilter; + +const summarySectionProps = { + title: 'Tree', + columns: [ + { title: 'Tree', value: 'stable-rc' }, + { title: 'Matainer', value: 'Shannon Nelson' }, + { + title: 'Commit/tag', + value: '5.15.150-rc1 - 3ab4d9c9e190217ee7e974c70b96795cd2f74611', + }, + ], +}; diff --git a/dashboard/src/types/tree/TreeDetails.tsx b/dashboard/src/types/tree/TreeDetails.tsx index 89b1e70..bf7d124 100644 --- a/dashboard/src/types/tree/TreeDetails.tsx +++ b/dashboard/src/types/tree/TreeDetails.tsx @@ -35,3 +35,13 @@ export type TreeDetails = { architectures: object; }; }; + +export interface TTreeDetailsFilter + extends Partial<{ + [K in keyof Omit< + TreeDetailsBuild, + 'test_status' | 'misc' | 'valid' + >]: TreeDetailsBuild[K][]; + }> { + valid?: string[]; +}