diff --git a/dashboard/src/components/Filter/CheckboxSection.tsx b/dashboard/src/components/Filter/CheckboxSection.tsx index ef81dfc..a7aadb6 100644 --- a/dashboard/src/components/Filter/CheckboxSection.tsx +++ b/dashboard/src/components/Filter/CheckboxSection.tsx @@ -4,27 +4,29 @@ import { useCallback, useMemo } from 'react'; import Checkbox from '../Checkbox/Checkbox'; -type TOnClickItem = (itemIdx: number, isChecked: boolean) => void; +type TOnClickItem = (value: string, isChecked: boolean) => void; + +type TItems = { [key: string]: boolean }; interface ICheckboxSectionItem { - text: string; + value: string; onClickItem: TOnClickItem; - idx: number; + isSelected: boolean; } interface ICheckboxList { - items: string[]; + items: TItems; onClickItem: TOnClickItem; } interface ICheckboxSubsection { - items: string[]; + items: TItems; title: string; onClickItem: TOnClickItem; } export interface ICheckboxSection { - items?: string[]; + items?: TItems; title: string; subtitle?: string; subsections?: ICheckboxSubsection[]; @@ -33,26 +35,32 @@ export interface ICheckboxSection { } const CheckboxSectionItem = ({ - text, + value, onClickItem, - idx, + isSelected, }: ICheckboxSectionItem): JSX.Element => { const handleOnToggle = useCallback( - (isChecked: boolean) => onClickItem(idx, isChecked), - [idx, onClickItem], + (isChecked: boolean) => onClickItem(value, isChecked), + [value, onClickItem], + ); + return ( + ); - return ; }; const CheckboxList = ({ items, onClickItem }: ICheckboxList): JSX.Element => { const itemComponents = useMemo( () => - items.map((text, idx) => ( + Object.keys(items).map(key => ( )), [items, onClickItem], diff --git a/dashboard/src/components/Filter/stories/CheckboxSection.stories.tsx b/dashboard/src/components/Filter/stories/CheckboxSection.stories.tsx deleted file mode 100644 index a0d6e22..0000000 --- a/dashboard/src/components/Filter/stories/CheckboxSection.stories.tsx +++ /dev/null @@ -1,49 +0,0 @@ -import type { Meta, StoryObj } from '@storybook/react'; -import { fn } from '@storybook/test'; - -import CheckboxSection from '../CheckboxSection'; - -const ActionsData = { - onClickItem: fn(), -}; - -const meta: Meta = { - title: 'Filter CheckboxSection', - component: CheckboxSection, - parameters: { - layout: 'centered', - }, - tags: ['autodocs'], - args: { - ...ActionsData, - }, -}; - -export default meta; -type Story = StoryObj; - -export const Default: Story = { - args: { - items: ['linux-5.15.y', 'Status:failed', 'Status: Warnings'], - title: 'Branch', - subtitle: 'Please select one or more Branches', - }, -}; - -export const WithSubSections: Story = { - args: { - title: 'Error Summary', - subsections: [ - { - title: 'Errors', - items: ['linux-5.15.y', 'Status:failed', 'Status: Warnings'], - onClickItem: ActionsData.onClickItem, - }, - { - title: 'Warnings', - items: ['linux-5.15.y', 'Status:failed', 'Status: Warnings'], - onClickItem: ActionsData.onClickItem, - }, - ], - }, -}; diff --git a/dashboard/src/components/Filter/stories/FilterDrawer.stories.tsx b/dashboard/src/components/Filter/stories/FilterDrawer.stories.tsx deleted file mode 100644 index eafbaf7..0000000 --- a/dashboard/src/components/Filter/stories/FilterDrawer.stories.tsx +++ /dev/null @@ -1,85 +0,0 @@ -import type { Meta, StoryObj } from '@storybook/react'; -import { fn } from '@storybook/test'; - -import { IntlProvider } from 'react-intl'; -import { flatten } from 'flat'; - -import { LOCALES } from '../../../locales/constants'; - -import { messages } from '../../../locales/messages'; - -import FilterDrawer from '../Drawer'; - -import CheckboxSection from '../CheckboxSection'; -import TimeRangeSection from '../TimeRangeSection'; -import SummarySection from '../SummarySection'; - -const ActionsData = { - onRefresh: fn(), - onCancel: fn(), - onFilter: fn(), -}; - -const meta: Meta = { - title: 'FilterDrawer', - component: FilterDrawer, - parameters: { - layout: 'centered', - }, - tags: ['autodocs'], -}; - -export default meta; -type Story = StoryObj; - -export const Default: Story = { - decorators: [ - (): JSX.Element => ( - - - - - - - - - ), - ], -}; - -const summarySectionProps = { - title: 'Tree', - columns: [ - { title: 'Tree', value: 'stable-rc' }, - { title: 'Matainer', value: 'Shannon Nelson' }, - { - title: 'Commit/tag', - value: '5.15.150-rc1 - 3ab4d9c9e190217ee7e974c70b96795cd2f74611', - }, - ], -}; - -const checkboxSectionProps = { - items: ['linux-5.15.y', 'Status:failed', 'Status: Warnings'], - title: 'Branch', - subtitle: 'Please select one or more Branches', - onClickItem: (idx: number, isChecked: boolean): void => - console.log(idx, isChecked), -}; - -const onChangeM = (e: React.FormEvent): void => - console.log(e.target); -const timeRangeSectionProps = { - title: 'Timing', - subtitle: 'Please select a range of timing:', - min: 0, - max: 100, - onMinChange: onChangeM, - onMaxChange: onChangeM, -}; diff --git a/dashboard/src/components/Filter/stories/TimeRangeSection.stories.tsx b/dashboard/src/components/Filter/stories/TimeRangeSection.stories.tsx deleted file mode 100644 index ba7d99c..0000000 --- a/dashboard/src/components/Filter/stories/TimeRangeSection.stories.tsx +++ /dev/null @@ -1,50 +0,0 @@ -import type { Meta, StoryObj } from '@storybook/react'; -import { IntlProvider } from 'react-intl'; -import { flatten } from 'flat'; - -import { fn } from '@storybook/test'; - -import { LOCALES } from '../../../locales/constants'; - -import { messages } from '../../../locales/messages'; - -import TimeRangeSection from '../TimeRangeSection'; - -const ActionsData = { - onMinChange: fn(), - onMaxChange: fn(), -}; - -const meta: Meta = { - title: 'Filter TimeRangeSection', - component: TimeRangeSection, - parameters: { - layout: 'centered', - }, - tags: ['autodocs'], - args: { - ...ActionsData, - }, -}; - -export default meta; -type Story = StoryObj; - -export const Default: Story = { - args: { - title: 'Timing', - subtitle: 'Please select a range of timing:', - min: 0, - max: 10, - }, - decorators: [ - (story): JSX.Element => ( - -
{story()}
-
- ), - ], -}; diff --git a/dashboard/src/routes/TreeDetails/TreeDetailsFilter.tsx b/dashboard/src/routes/TreeDetails/TreeDetailsFilter.tsx index 206766f..94d3866 100644 --- a/dashboard/src/routes/TreeDetails/TreeDetailsFilter.tsx +++ b/dashboard/src/routes/TreeDetails/TreeDetailsFilter.tsx @@ -1,4 +1,4 @@ -import { useCallback, useMemo, useState } from 'react'; +import { useCallback, useMemo } from 'react'; import { useIntl } from 'react-intl'; import FilterDrawer from '@/components/Filter/Drawer'; @@ -16,107 +16,92 @@ interface ITreeDetailsFilter { onFilter: (filter: TTreeDetailsFilter) => void; } +type TFilterApplied = { [key: string]: boolean }; + const sanitizeData = ( data: TreeDetailsType | undefined, -): [string[], string[], string[], string[], string] => { - const validList = ['TRUE', 'FALSE']; - const branchSet = new Set(); - const configSet = new Set(); - const archSet = new Set(); +): [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) branchSet.add(b.git_repository_branch); - if (b.config_name) configSet.add(b.config_name); - if (b.architecture) archSet.add(b.architecture); + 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; }); - //TODO: try to avoid this set < > array thing - return [ - validList, - Array.from(branchSet), - Array.from(configSet), - Array.from(archSet), - treeUrl, - ]; + return [status, branches, configs, archs, treeUrl]; }; -const getNewFilter = ( - value: T, - isChecked: boolean, - currentFilter: T[], -): T[] => { - if (isChecked) return [...currentFilter, value]; - return currentFilter.filter(item => item != value); -}; +const getFilterListFromObj = (filterObj: TFilterApplied): string[] => + Object.keys(filterObj).filter(key => filterObj[key]); const TreeDetailsFilter = ({ data, onFilter, }: ITreeDetailsFilter): JSX.Element => { const intl = useIntl(); - const [branchFilter, setBranchFilter] = useState([]); - const [configFilter, setConfigFilter] = useState([]); - const [archFilter, setArchFilter] = useState([]); - const [statusFilter, setStatusFilter] = useState([]); - const [statusList, branchList, configList, archList, treeUrl] = useMemo( + const [statusObj, branchObj, configObj, archObj, treeUrl] = useMemo( () => sanitizeData(data), [data], ); const onClickFilterHandle = useCallback(() => { const filter: TTreeDetailsFilter = {}; - if (statusFilter.length) filter.valid = statusFilter; - if (branchFilter.length) filter.git_repository_branch = branchFilter; - if (configFilter.length) filter.config_name = configFilter; - if (archFilter.length) filter.architecture = archFilter; + + filter.config_name = getFilterListFromObj(configObj); + filter.git_repository_branch = getFilterListFromObj(branchObj); + filter.architecture = getFilterListFromObj(archObj); + filter.valid = getFilterListFromObj(statusObj); onFilter(filter); - }, [statusFilter, branchFilter, configFilter, archFilter, onFilter]); + }, [onFilter, configObj, branchObj, archObj, statusObj]); + + console.log('validObj:', statusObj); const checkboxSectionsProps: ICheckboxSection[] = useMemo(() => { return [ { title: intl.formatMessage({ id: 'global.branch' }), subtitle: intl.formatMessage({ id: 'filter.branchSubtitle' }), - items: branchList, - onClickItem: (idx: number, isChecked: boolean) => - setBranchFilter(current => - getNewFilter(branchList[idx], isChecked, current), - ), + items: branchObj, + onClickItem: (branch: string, isChecked: boolean) => + (branchObj[branch] = isChecked), }, { title: intl.formatMessage({ id: 'global.status' }), subtitle: intl.formatMessage({ id: 'filter.statusSubtitle' }), - items: statusList.map(v => (v === 'TRUE' ? 'valid' : 'invalid')), - onClickItem: (idx: number, isChecked: boolean) => - setStatusFilter(current => - getNewFilter(statusList[idx], isChecked, current), - ), + items: Object.keys(statusObj).reduce((acc, k) => { + const newKey = k == 'TRUE' ? 'valid' : 'invalid'; + acc[newKey] = statusObj[k]; + console.log('acc:', acc); + 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: configList, - onClickItem: (idx: number, isChecked: boolean) => - setConfigFilter(current => - getNewFilter(configList[idx], isChecked, current), - ), + items: configObj, + onClickItem: (config: string, isChecked: boolean) => + (configObj[config] = isChecked), }, { title: intl.formatMessage({ id: 'global.architecture' }), subtitle: intl.formatMessage({ id: 'filter.architectureSubtitle' }), - items: archList, - onClickItem: (idx: number, isChecked: boolean) => - setArchFilter(current => - getNewFilter(archList[idx], isChecked, current), - ), + items: archObj, + onClickItem: (arch: string, isChecked: boolean) => + (archObj[arch] = isChecked), }, ]; - }, [statusList, branchList, configList, archList, intl]); + }, [statusObj, branchObj, configObj, archObj, intl]); const checkboxSectionsComponents = useMemo( () =>