From 9cea0cf4b70a0790c8c99cb7490582025de8fdb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Ricks?= Date: Tue, 16 Apr 2024 12:11:50 +0200 Subject: [PATCH] Add: Add hooks for handling the filter dialog state Using the new hooks allows to rewrite all filter dialogs to function components more easily. --- .../powerfilter/useFilterDialog.jsx | 86 ++++++++++++++++++ .../powerfilter/useFilterDialogSave.jsx | 87 +++++++++++++++++++ 2 files changed, 173 insertions(+) create mode 100644 src/web/components/powerfilter/useFilterDialog.jsx create mode 100644 src/web/components/powerfilter/useFilterDialogSave.jsx diff --git a/src/web/components/powerfilter/useFilterDialog.jsx b/src/web/components/powerfilter/useFilterDialog.jsx new file mode 100644 index 0000000000..dbf9432dae --- /dev/null +++ b/src/web/components/powerfilter/useFilterDialog.jsx @@ -0,0 +1,86 @@ +/* SPDX-FileCopyrightText: 2024 Greenbone AG + * + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +import {useCallback, useState} from 'react'; + +import Filter from 'gmp/models/filter'; + +import {isDefined} from 'gmp/utils/identity'; + +/** + * React hook for handling filter dialog state + * + * @param {Filter} initialFilter + * @returns Object + */ +const useFilterDialog = initialFilter => { + const [originalFilter] = useState(initialFilter); + const [filter, setFilter] = useState(() => + isDefined(initialFilter) ? initialFilter.copy() : new Filter(), + ); + const [filterDialogState, setFilterDialogState] = useState({}); + + const [filterString, setFilterString] = useState(() => + isDefined(initialFilter) ? initialFilter.toFilterCriteriaString() : '', + ); + + // eslint-disable-next-line no-shadow + const handleFilterChange = useCallback(filter => { + setFilter(filter); + }, []); + + const handleFilterValueChange = useCallback((value, name, relation = '=') => { + setFilter(filter => filter.copy().set(name, value, relation)); // eslint-disable-line no-shadow + }, []); + + const handleSearchTermChange = useCallback((value, name, relation = '~') => { + setFilter(filter => filter.copy().set(name, `"${value}"`, relation)); // eslint-disable-line no-shadow + }, []); + + const handleFilterStringChange = useCallback(value => { + setFilterString(value); + }, []); + + const handleSortByChange = useCallback(value => { + setFilter(filter => filter.copy().setSortBy(value)); // eslint-disable-line no-shadow + }, []); + + const handleSortOrderChange = useCallback(value => { + setFilter(filter => filter.copy().setSortOrder(value)); // eslint-disable-line no-shadow + }, []); + + const handleChange = useCallback((value, name) => { + setFilterDialogState(state => ({...state, [name]: value})); + }, []); + + const {filterName, saveNamedFilter, ...other} = filterDialogState; + + return { + ...other, + filter, + filterString, + filterName, + originalFilter, + saveNamedFilter, + handleFilterChange, + handleFilterValueChange, + handleSearchTermChange, + handleFilterStringChange, + handleSortByChange, + handleSortOrderChange, + handleChange, + // provide old names + filterstring: filterString, + onFilterChange: handleFilterChange, + onFilterValueChange: handleFilterValueChange, + onSearchTermChange: handleSearchTermChange, + onFilterStringChange: handleFilterStringChange, + onSortOrderChange: handleSortOrderChange, + onSortByChange: handleSortByChange, + onValueChange: handleChange, + }; +}; + +export default useFilterDialog; diff --git a/src/web/components/powerfilter/useFilterDialogSave.jsx b/src/web/components/powerfilter/useFilterDialogSave.jsx new file mode 100644 index 0000000000..9adaa8df8f --- /dev/null +++ b/src/web/components/powerfilter/useFilterDialogSave.jsx @@ -0,0 +1,87 @@ +/* SPDX-FileCopyrightText: 2024 Greenbone AG + * + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +import {useCallback} from 'react'; + +import Filter from 'gmp/models/filter'; + +import {apiType} from 'gmp/utils/entitytype'; +import {isDefined} from 'gmp/utils/identity'; + +import useTranslation from 'web/hooks/useTranslation'; +import useGmp from 'web/utils/useGmp'; + +const useFilterDialogSave = ( + createFilterType, + {onClose, onFilterChanged, onFilterCreated}, + {filterName, filter, filterString, originalFilter, saveNamedFilter}, +) => { + const [_] = useTranslation(); + const gmp = useGmp(); + + const createFilter = useCallback( + newFilter => { + return gmp.filter + .create({ + term: newFilter.toFilterString(), + type: apiType(createFilterType), + name: filterName, + }) + .then(response => { + const {data} = response; + // load new filter + return gmp.filter.get(data); + }) + .then(response => { + const {data: f} = response; + + if (isDefined(onFilterCreated)) { + onFilterCreated(f); + } + }); + }, + [gmp, createFilterType, filterName, onFilterCreated], + ); + + const handleSave = useCallback(() => { + const newFilter = filter + .copy() + .mergeKeywords(Filter.fromString(filterString)); + + if (saveNamedFilter) { + if (isDefined(filterName) && filterName.trim().length > 0) { + return createFilter(newFilter).then(onClose); + } + return Promise.reject( + new Error(_('Please insert a name for the new filter')), + ); + } + + if (isDefined(onFilterChanged) && !newFilter.equals(originalFilter)) { + onFilterChanged(newFilter); + } + + if (isDefined(onClose)) { + onClose(); + } + return Promise.resolve(); + }, [ + onClose, + onFilterChanged, + createFilter, + filter, + filterString, + filterName, + originalFilter, + saveNamedFilter, + _, + ]); + + const ret = [handleSave]; + ret.handleSave = handleSave; + return ret; +}; + +export default useFilterDialogSave;