Skip to content

Commit

Permalink
feat: add TreeDetailsFilter drawer on TreeDetails page
Browse files Browse the repository at this point in the history
  • Loading branch information
lfjnascimento committed Jul 19, 2024
1 parent b916561 commit dae99d0
Show file tree
Hide file tree
Showing 4 changed files with 171 additions and 12 deletions.
10 changes: 10 additions & 0 deletions dashboard/src/locales/messages/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down Expand Up @@ -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:',
},
},
};
31 changes: 19 additions & 12 deletions dashboard/src/routes/TreeDetails/TreeDetails.tsx
Original file line number Diff line number Diff line change
@@ -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<string, never>
>({});
const [configs, setConfigs] = useState<IListingItem[]>();
const [archictectures, setArchitectures] = useState<ISummaryItem[]>();
const initialDataRef = useRef<TreeDetailsType | undefined>();

const { data } = useTreeDetails(treeId ?? '', filter);

if (!initialDataRef.current) {
initialDataRef.current = data;
}

useEffect(() => {
if (data) {
Expand Down Expand Up @@ -47,10 +57,7 @@ const TreeDetails = (): JSX.Element => {
<div className="flex flex-col pt-8">
<div className="flex flex-row pb-2 border-b border-darkGray">
<TreeDetailsTab />
<ButtonWithIcon
icon={<MdExpandMore />}
label={<FormattedMessage id="global.filters" />}
/>
<TreeDetailsFilter data={initialDataRef.current} onFilter={setFilter} />
</div>
<div className="pt-4">
<CardsGroup
Expand Down
132 changes: 132 additions & 0 deletions dashboard/src/routes/TreeDetails/TreeDetailsFilter.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
import { useCallback, useMemo } from 'react';
import { useIntl } from 'react-intl';

import FilterDrawer from '@/components/Filter/Drawer';
import FilterSummarySection from '@/components/Filter/SummarySection';
import FilterCheckboxSection, {
ICheckboxSection,
} from '@/components/Filter/CheckboxSection';
import {
TreeDetails as TreeDetailsType,
TTreeDetailsFilter,
} from '@/types/tree/TreeDetails';

interface ITreeDetailsFilter {
data?: TreeDetailsType;
onFilter: (filter: TTreeDetailsFilter) => 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 => (
<FilterCheckboxSection key={props.title} {...props} />
)),
[checkboxSectionsProps],
);

return (
<FilterDrawer treeURL={treeUrl} onFilter={onClickFilterHandle}>
<FilterSummarySection {...summarySectionProps} />
{checkboxSectionsComponents}
</FilterDrawer>
);
};

export default TreeDetailsFilter;

const summarySectionProps = {
title: 'Tree',
columns: [
{ title: 'Tree', value: 'stable-rc' },
{ title: 'Matainer', value: '' },
{ title: 'Estimate to complete', value: '' },
{
title: 'Commit/tag',
value: '5.15.150-rc1 - 3ab4d9c9e190217ee7e974c70b96795cd2f74611',
},
],
};
10 changes: 10 additions & 0 deletions dashboard/src/types/tree/TreeDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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[];
}

0 comments on commit dae99d0

Please sign in to comment.