From 14c62a0e8bb3e0e5bca6e43e5e6439892dd1eeb1 Mon Sep 17 00:00:00 2001 From: Alessandro Amantini Date: Tue, 26 Nov 2024 17:08:47 +0000 Subject: [PATCH] ISSUE #5257 - refactor filters data structure --- .../cards/cardFilters/cardFilters.types.ts | 7 +- .../filterChip/filterChip.component.tsx | 10 +-- .../filterForm/filterForm.component.tsx | 12 ++-- .../filtersSection.component.tsx | 71 +++++++++---------- .../ticketFiltersSelection.component.tsx | 4 +- .../cards/tickets/ticketFiltersContext.tsx | 42 ++++------- .../ticketsList/ticketsListCard.component.tsx | 36 +++++----- 7 files changed, 82 insertions(+), 100 deletions(-) diff --git a/frontend/src/v5/ui/components/viewer/cards/cardFilters/cardFilters.types.ts b/frontend/src/v5/ui/components/viewer/cards/cardFilters/cardFilters.types.ts index e50f87ed05..3518412837 100644 --- a/frontend/src/v5/ui/components/viewer/cards/cardFilters/cardFilters.types.ts +++ b/frontend/src/v5/ui/components/viewer/cards/cardFilters/cardFilters.types.ts @@ -16,15 +16,16 @@ */ export type TicketFilterListItemType = { module: string, property: string, type: CardFilterType }; -export type FormFilter = { values: CardFilterValue[], type?: CardFilterType }; export type CardFilterOperator = 'ex' | 'nex' | 'eq' | 'neq' | 'ss' | 'nss' | 'rng' | 'nrng' | 'gt' | 'gte' | 'lt' | 'lte'; export type CardFilterType = 'text' | 'longText' | 'date' | 'pastDate' | 'sequencing' | 'oneOf' | 'manyOf' | 'boolean' | 'number' | 'ticketTitle' | 'ticketId' | 'template'; export type CardFilterValue = string | number | Date; -export type CardFiltersByOperator = Partial>; +export type FormFilter = { operator: CardFilterOperator, values: CardFilterValue[] }; + +export type CardFiltersByType = Partial>; export type CardFilter = { module: string, property: string, - operator: CardFilterOperator, + type: CardFilterType, filter: FormFilter, }; diff --git a/frontend/src/v5/ui/components/viewer/cards/cardFilters/filterChip/filterChip.component.tsx b/frontend/src/v5/ui/components/viewer/cards/cardFilters/filterChip/filterChip.component.tsx index 020c08e947..21e99bd919 100644 --- a/frontend/src/v5/ui/components/viewer/cards/cardFilters/filterChip/filterChip.component.tsx +++ b/frontend/src/v5/ui/components/viewer/cards/cardFilters/filterChip/filterChip.component.tsx @@ -20,18 +20,18 @@ import { ChipContainer, DeleteButton, TextWrapper, OperatorIconContainer, Displa import { FILTER_OPERATOR_ICON, FILTER_OPERATOR_LABEL } from '../cardFilters.helpers'; import { Tooltip } from '@mui/material'; import { FormattedMessage } from 'react-intl'; -import { CardFilterOperator, CardFilterType, CardFilterValue } from '../cardFilters.types'; +import { CardFilterType, FormFilter } from '../cardFilters.types'; import { formatSimpleDate } from '@/v5/helpers/intl.helper'; type FilterChipProps = { property: string; - operator: CardFilterOperator; - values: CardFilterValue[]; - type: CardFilterType; + type: CardFilterType, + filter: FormFilter, selected?: boolean; onDelete: () => void; }; -export const FilterChip = ({ property, values, onDelete, operator, selected, type }: FilterChipProps) => { +export const FilterChip = ({ property, onDelete, selected, type, filter }: FilterChipProps) => { + const { operator, values } = filter; const OperatorIcon = FILTER_OPERATOR_ICON[operator]; const hasMultipleValues = values.length > 1; const isDate = ['date', 'pastDate'].includes(type); diff --git a/frontend/src/v5/ui/components/viewer/cards/cardFilters/filterForm/filterForm.component.tsx b/frontend/src/v5/ui/components/viewer/cards/cardFilters/filterForm/filterForm.component.tsx index 2317dc9d27..bd72833eef 100644 --- a/frontend/src/v5/ui/components/viewer/cards/cardFilters/filterForm/filterForm.component.tsx +++ b/frontend/src/v5/ui/components/viewer/cards/cardFilters/filterForm/filterForm.component.tsx @@ -17,7 +17,7 @@ import { formatSimpleDate } from '@/v5/helpers/intl.helper'; import { FormattedMessage } from 'react-intl'; -import { CardFilterOperator, CardFilterValue, CardFilter, CardFilterType } from '../cardFilters.types'; +import { CardFilterOperator, CardFilterValue, CardFilter, CardFilterType, FormFilter } from '../cardFilters.types'; import { FILTER_OPERATOR_LABEL, getFilterFormTitle } from '../cardFilters.helpers'; import { Container, ButtonsContainer, Button } from './filterForm.styles'; import { MenuItem } from '@mui/material'; @@ -33,21 +33,21 @@ type FilterFormProps = { module: string, property: string, type: CardFilterType, - values?: CardFilterValue[] - operator?: CardFilterOperator, + filter?: FormFilter, onSubmit: (newFilter: CardFilter) => void, onCancel: () => void, }; -export const FilterForm = ({ module, property, operator, type, values = [], onSubmit, onCancel }: FilterFormProps) => { +export const FilterForm = ({ module, property, type, filter, onSubmit, onCancel }: FilterFormProps) => { + const { operator, values = [] } = filter || {}; const formData = useForm({ defaultValues: _.defaults({ values, operator }, DEFAULT_VALUES) }); const handleSubmit = formData.handleSubmit((body: FormType) => { // TODO - remove this line const newValues = body.values.filter((x) => ![undefined, ''].includes(x as any)); - onSubmit({ module, property, operator: body.operator, filter: { values: newValues, type } }); + onSubmit({ module, property, type, filter: { operator: body.operator, values: newValues } }); }); - const isUpdatingFilter = !!operator; + const isUpdatingFilter = !!filter; const canSubmit = formData.formState.isValid && !isEmpty(formData.formState.dirtyFields); const formOperator = formData.watch('operator'); const valuesInputsCount = Math.min(getOperatorMaxSupportedValues(formOperator), 3); diff --git a/frontend/src/v5/ui/components/viewer/cards/cardFilters/filtersSection/filtersSection.component.tsx b/frontend/src/v5/ui/components/viewer/cards/cardFilters/filtersSection/filtersSection.component.tsx index ceefb40ea8..c0d5fe09ba 100644 --- a/frontend/src/v5/ui/components/viewer/cards/cardFilters/filtersSection/filtersSection.component.tsx +++ b/frontend/src/v5/ui/components/viewer/cards/cardFilters/filtersSection/filtersSection.component.tsx @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -import { CardFiltersByOperator, CardFilterOperator, FormFilter, CardFilter } from '../cardFilters.types'; +import { CardFiltersByType } from '../cardFilters.types'; import { FilterChip } from '../filterChip/filterChip.component'; import { Section } from './filtersSection.styles'; import { FilterForm } from '../filterForm/filterForm.component'; @@ -26,20 +26,18 @@ import { TicketFiltersContext } from '../../tickets/ticketFiltersContext'; type FiltersSectionProps = { module?: string; - filters: Record; + filters: Record; }; export const FiltersSection = ({ module, filters }: FiltersSectionProps) => { - const [selectedChip, setSelectedChip] = useState(''); - const { deleteFilter, editFilter } = useContext(TicketFiltersContext); + const [selectedProperty, setSelectedProperty] = useState(''); + const { deleteFilter, upsertFilter } = useContext(TicketFiltersContext); - const onDeleteFilter = (property, operator) => deleteFilter({ module, property, operator }); - - const handleEditFilter = (oldOperator: CardFilterOperator) => (newFilter: CardFilter) => editFilter(newFilter, oldOperator); + const onDeleteFilter = (property, type) => deleteFilter({ module, property, type }); const filtersToChips = () => { const filterChips = []; - Object.entries(filters).forEach(([property, operatorAndFilter]) => { - const moduleFilterChips = Object.entries(operatorAndFilter).map(([operator, filter]) => [property, operator, filter]); + Object.entries(filters).forEach(([property, typeAndFilter]) => { + const moduleFilterChips = Object.entries(typeAndFilter).map(([type, filter]) => [property, type, filter]); filterChips.push(...moduleFilterChips); }); return filterChips; @@ -47,38 +45,35 @@ export const FiltersSection = ({ module, filters }: FiltersSectionProps) => { return (
- {filtersToChips().map(([property, operator, filter]) => { - const filterKey = `${property}.${operator}`; - return ( - setSelectedChip(filterKey)} - onClose={() => setSelectedChip('')} - TriggerButton={( - ( + setSelectedProperty(property)} + onClose={() => setSelectedProperty('')} + TriggerButton={( + onDeleteFilter(property, type)} + /> + )} + > + + {({ close }) => ( + onDeleteFilter(property, operator)} + type={type} + filter={filter} /> )} - > - - {({ close }) => ( - - )} - - - ); - })} + + + ))}
); }; diff --git a/frontend/src/v5/ui/components/viewer/cards/cardFilters/filtersSelection/tickets/ticketFiltersSelection.component.tsx b/frontend/src/v5/ui/components/viewer/cards/cardFilters/filtersSelection/tickets/ticketFiltersSelection.component.tsx index 218f0ca3cc..236231fa0d 100644 --- a/frontend/src/v5/ui/components/viewer/cards/cardFilters/filtersSelection/tickets/ticketFiltersSelection.component.tsx +++ b/frontend/src/v5/ui/components/viewer/cards/cardFilters/filtersSelection/tickets/ticketFiltersSelection.component.tsx @@ -36,7 +36,7 @@ import { CardFilterActionMenu } from '../../filterForm/filterForm.styles'; import { TicketFiltersContext } from '@components/viewer/cards/tickets/ticketFiltersContext'; export const FilterSelection = () => { - const { addFilter } = useContext(TicketFiltersContext); + const { upsertFilter } = useContext(TicketFiltersContext); const [active, setActive] = useState(false); const [selectedItem, setSelectedItem] = useState(null); const { containerOrFederation } = useParams(); @@ -81,7 +81,7 @@ export const FilterSelection = () => { diff --git a/frontend/src/v5/ui/components/viewer/cards/tickets/ticketFiltersContext.tsx b/frontend/src/v5/ui/components/viewer/cards/tickets/ticketFiltersContext.tsx index e694547707..34cd7ced1f 100644 --- a/frontend/src/v5/ui/components/viewer/cards/tickets/ticketFiltersContext.tsx +++ b/frontend/src/v5/ui/components/viewer/cards/tickets/ticketFiltersContext.tsx @@ -17,20 +17,18 @@ import { get, isEmpty, set, unset } from 'lodash'; import { createContext, useState } from 'react'; -import { CardFilterOperator, CardFilter } from '../cardFilters/cardFilters.types'; +import { CardFilter } from '../cardFilters/cardFilters.types'; export interface TicketFiltersContextType { filters: Record; - addFilter: (filter: CardFilter) => void; - editFilter: (filter: CardFilter, oldOperator: CardFilterOperator) => void; + upsertFilter: (filter: CardFilter) => void; deleteFilter: (filter: Omit) => void; deleteAllFilters: () => void; } const defaultValue: TicketFiltersContextType = { filters: {}, - addFilter: () => {}, - editFilter: () => {}, + upsertFilter: () => {}, deleteFilter: () => {}, deleteAllFilters: () => {}, }; @@ -52,41 +50,29 @@ export const TicketFiltersContextComponent = ({ filters: initialFilters, childre path.pop(); } while (path.length && isEmpty(get(fltrs, path))); }; - - const addFilter = ({ module, property, operator, filter }: CardFilter) => { + + const upsertFilter = ({ module, property, type, filter }: CardFilter) => { let newFilters = { ...filters }; - const path = [module, property, operator]; + const path = [module, property, type]; set(newFilters, path, filter); setFilters({ ...newFilters }); }; - - const editFilter = ({ module, property, operator, filter }: CardFilter, oldOperator?: CardFilterOperator) => { - let newFilters = { ...filters }; - const path = [module, property, operator]; - set(newFilters, path, filter); - if (operator !== oldOperator) { - const oldPath = [module, property, oldOperator]; - deleteFilterAndEmptyAncestors(newFilters, oldPath); - } - setFilters({ ...newFilters }); - }; - - const deleteFilter = ({ module, property, operator }: CardFilter) => { + + const deleteFilter = ({ module, property, type }: CardFilter) => { const newFilters = { ...filters }; - const path = [module, property, operator]; + const path = [module, property, type]; deleteFilterAndEmptyAncestors(newFilters, path); setFilters(newFilters); }; - - const handleDeleteAllFilters = () => setFilters({}); + + const deleteAllFilters = () => setFilters({}); return ( {children} diff --git a/frontend/src/v5/ui/routes/viewer/tickets/ticketsList/ticketsListCard.component.tsx b/frontend/src/v5/ui/routes/viewer/tickets/ticketsList/ticketsListCard.component.tsx index 37e7c37f15..25d9598f76 100644 --- a/frontend/src/v5/ui/routes/viewer/tickets/ticketsList/ticketsListCard.component.tsx +++ b/frontend/src/v5/ui/routes/viewer/tickets/ticketsList/ticketsListCard.component.tsx @@ -38,48 +38,48 @@ import { TicketBaseKeys } from '../tickets.constants'; const defaultFilters = { '': { 'templateId': { - 'eq': { + 'templateId': { values: ['template id'], - type: 'templateId', + operator: 'eq', }, }, }, [TicketBaseKeys.PROPERTIES]: { 'createdAt': { - 'eq': { + 'pastDate': { values: [new Date('12/12/2024')], - type: 'pastDate', + operator: 'eq', }, }, 'property1': { - 'rng': { + 'date': { values: [new Date('12/12/2024'), new Date('12/20/2024')], - type: 'date', - }, - 'nrng': { - values: [3], - type: 'number', + operator:'rng', }, }, 'property2': { - 'eq': { + 'number': { values: [4], - type: 'number', + operator: 'eq', }, }, 'assignees': { - 'ss': { + 'manyOf': { values: ['Ale', 'San', 'Dan'], - type: 'manyOf', + operator: 'ss', }, }, }, 'module1': { - 'property1': { - 'ex': { values: [] }, - 'ss': { + 'numberOrStringProperty': { + 'number': { + values: [], + operator: 'ex', + }, + // This is + 'text': { values: [2, 3], - type: 'oneOf', + operator:'ss', }, }, },