Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ISSUE #5271 - Ticket filters: jobsandusers select filter form #5391

Merged
merged 36 commits into from
Mar 7, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
7ac733d
ISSUE #5271 - selector accepts multiple values
The-Daniel Jan 15, 2025
1946be8
Merge branch 'ISSUE_5266' into ISSUE_5271
The-Daniel Jan 22, 2025
cda906f
ISSUE #5271 - owner filter uses assignees select for value
The-Daniel Jan 23, 2025
a5b206b
Merge branch 'ISSUE_5266' into ISSUE_5271
The-Daniel Jan 29, 2025
eb0cbf9
ISSUE #5271 - remove duplicate Owner with text type
The-Daniel Jan 31, 2025
57423aa
ISSUE #5271 - jobsAndUsers types use assignee select circles input
The-Daniel Jan 31, 2025
67a3e35
ISSUE #5271 - options values are sorted
The-Daniel Feb 3, 2025
227f679
ISSUE #5271 - in a mixed select users are displayed with their full name
The-Daniel Feb 3, 2025
3148aff
ISSUE #5271 - fix bad type name
The-Daniel Feb 3, 2025
076ffca
ISSUE #5271 - fixed duplicate values appearing in select
The-Daniel Feb 3, 2025
dcfe119
ISSUE #5271 - fix many select values pushing out of action menu
The-Daniel Feb 4, 2025
5b7c0cb
ISSUE #5271 - values that are users are displayed as a full name
The-Daniel Feb 4, 2025
b5e2860
ISSUE #5271 - rename props to transformInputValue and transformOutput…
The-Daniel Feb 5, 2025
a95ee07
ISSUE #5271 - remove unnecessary return
The-Daniel Feb 5, 2025
40af17d
ISSUE #5271 - remove unnecessary formControl and helper text as it ex…
The-Daniel Feb 5, 2025
3c96d63
ISSUE #5271 - fix failing tests after adding jobsAndUsers filter
The-Daniel Feb 5, 2025
e0bea9b
ISSUE #5271 - fix templates filter showing blank values
The-Daniel Feb 5, 2025
668afe6
Merge branch 'ISSUE_5188' into ISSUE_5271
The-Daniel Feb 5, 2025
9ff9d9b
ISSUE #5271 - move owner property filter to propertiesToValidFilters
The-Daniel Feb 10, 2025
7d328fc
Merge branch 'ISSUE_5188' into ISSUE_5271
The-Daniel Feb 11, 2025
a708671
ISSUE #5271 - only use templates that have been used on the current m…
The-Daniel Feb 12, 2025
43d44c9
ISSUE #5271 - give helper function more agnostic name
The-Daniel Feb 12, 2025
a0fa774
ISSUE #5271 - fix template filter not working
The-Daniel Feb 12, 2025
2fe9203
ISSUE #5271 - owner filter now displays full names
The-Daniel Feb 12, 2025
0abecd0
ISSUE #5271 - exclude jobs from Owner filter
The-Daniel Feb 12, 2025
4e7a125
ISSUE #5271 - tidy selector using switch
The-Daniel Feb 12, 2025
4adfaf1
ISSUE #5271 - fix jobsAndUsers error text causing UI problems
The-Daniel Feb 12, 2025
d213c11
ISSUE #5271 - default ticket setter only uses custom statuses if they…
The-Daniel Feb 12, 2025
b7f75d8
ISSUE #5271 - fix default filters displaying as undefined because the…
The-Daniel Feb 12, 2025
a061df0
ISSUE #5271 - fix crash when changing select filter to ex/nex
The-Daniel Feb 12, 2025
8a5c185
ISSUE #5271 - rename formassignees -> formjobsandusers
The-Daniel Mar 5, 2025
4fa859e
ISSUE #5271 - do not use filtered tickets when getting a select's pro…
The-Daniel Mar 5, 2025
b117264
ISSUE #5271 - simplify duplicated code
The-Daniel Mar 5, 2025
da849d7
ISSUE #5271 - use only direct children within assignees select
The-Daniel Mar 5, 2025
ddb7780
ISSUE #5271 - template and owner filters use is/nis instead of eq/neq
The-Daniel Mar 5, 2025
9d0d3bf
ISSUE #5271 - fixed default operator sometimes being an invalid opera…
The-Daniel Mar 6, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions frontend/src/v5/store/jobs/jobs.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,12 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

import { IUser } from '../users/users.redux';

export type IJob = {
_id: string;
color: string;
isViewer?: boolean;
};

export type IJobOrUserList = Partial<IJob & IUser>[];
59 changes: 45 additions & 14 deletions frontend/src/v5/store/tickets/card/ticketsCard.selectors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,11 @@ import { ITicketsCardState } from './ticketsCard.redux';
import { DEFAULT_PIN, getPinColorHex, formatPin, getTicketPins } from '@/v5/ui/routes/viewer/tickets/ticketsForm/properties/coordsProperty/coordsProperty.helpers';
import { IPin } from '@/v4/services/viewer/viewer';
import { selectSelectedDate } from '@/v4/modules/sequences';
import { uniq } from 'lodash';
import { toTicketCardFilter, templatesToFilters } from '@components/viewer/cards/cardFilters/filtersSelection/tickets/ticketFilters.helpers';
import { sortBy, uniq, sortedUniqBy } from 'lodash';
import { toTicketCardFilter, templatesToFilters, getFiltersFromJobsAndUsers } from '@components/viewer/cards/cardFilters/filtersSelection/tickets/ticketFilters.helpers';
import { selectFederationById, selectFederationJobs, selectFederationUsers } from '../../federations/federations.selectors';
import { selectContainerJobs, selectContainerUsers } from '../../containers/containers.selectors';
import { IJobOrUserList } from '../../jobs/jobs.types';

const selectTicketsCardDomain = (state): ITicketsCardState => state.ticketsCard || {};

Expand Down Expand Up @@ -139,20 +142,28 @@ export const selectFilteredTickets = createSelector(
},
);

const selectTemplatesFilters = createSelector(
export const selectTemplatesWithTickets = createSelector(
selectCurrentTemplates,
selectCurrentTickets,
(templates, tickets) => {
const idsOfTemplatesWithAtLeastOneTicket = uniq(tickets.map((t) => t.type));
return templates.filter((t) => idsOfTemplatesWithAtLeastOneTicket.includes(t._id));
},
);

export const selectTemplatesWithFilteredTickets = createSelector(
selectCurrentTemplates,
selectFilteredTickets,
(templates, tickets) => {
const idsOfTemplatesWithAtLeastOneTicket = uniq(tickets.map((t) => t.type));
const templatesWithAtLeastOneTicket = templates.filter((t) => idsOfTemplatesWithAtLeastOneTicket.includes(t._id));
return templatesToFilters(templatesWithAtLeastOneTicket);
return templates.filter((t) => idsOfTemplatesWithAtLeastOneTicket.includes(t._id));
},
);

export const selectAvailableTemplatesFilters = createSelector(
selectFilters,
selectTemplatesFilters,
(usedFilters, allFilters) => allFilters.filter(({ module, property, type }) => !usedFilters[`${module}.${property}.${type}`]),
selectTemplatesWithFilteredTickets,
(usedFilters, allFilters) => templatesToFilters(allFilters).filter(({ module, property, type }) => !usedFilters[`${module}.${property}.${type}`]),
);

export const selectIsShowingPins = createSelector(
Expand Down Expand Up @@ -207,24 +218,44 @@ export const selectNewTicketPins = createSelector(
selectSelectedTicketPinId,
getTicketPins,
);
const selectJobsAndUsersByModelId = createSelector(
selectFederationById,
selectFederationJobs,
selectContainerJobs,
selectFederationUsers,
selectContainerUsers,
(fed, fedJobs, contJobs, fedUsers, contUsers) => {
const isFed = !!fed;
const jobs = isFed ? fedJobs : contJobs;
const users = isFed ? fedUsers : contUsers;
return [...jobs, ...users] as IJobOrUserList;
},
);

export const selectPropertyOptions = createSelector(
selectTemplates,
selectTemplatesWithTickets,
selectRiskCategories,
selectJobsAndUsersByModelId,
(state, modelId, module) => module,
(state, modelId, module, property) => property,
(templates, riskCategories, module, property) => {
(templates, riskCategories, jobsAndUsers, module, property) => {
const allValues = [];
if (!module && property === 'Owner') return getFiltersFromJobsAndUsers(jobsAndUsers.filter((ju) => !!ju.firstName));
templates.forEach((template) => {
const matchingModule = module ? template.modules.find((mod) => (mod.name || mod.type) === module)?.properties : template.properties;
const matchingProperty = matchingModule?.find(({ name, type: t }) => (name === property) && (['manyOf', 'oneOf'].includes(t)));
if (!matchingProperty) return;
if (matchingProperty.values === 'riskCategories') {
allValues.push(...riskCategories);
return;
switch (matchingProperty.values) {
case 'riskCategories':
allValues.push(...riskCategories.map((value) => ({ value, type: 'riskCategories' })));
break;
case 'jobsAndUsers':
allValues.push(...getFiltersFromJobsAndUsers(jobsAndUsers));
break;
default:
allValues.push(...matchingProperty.values.map((value) => ({ value, type: 'default' })));
}
allValues.push(...matchingProperty.values);
});
return uniq(allValues);
return sortedUniqBy(sortBy(allValues, 'value'), 'value');
},
);
5 changes: 0 additions & 5 deletions frontend/src/v5/store/tickets/tickets.selectors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,6 @@ export const selectTemplates = createSelector(
(state, modelId) => state.templatesByModelId[modelId] || [],
);

export const selectTemplatesNames = createSelector(
selectTemplates,
(templates) => templates.map(({ name }) => name),
);

export const selectTemplateById = createSelector(
selectTicketsDomain,
selectTemplates,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ const DATE_FILTER_OPERATOR_LABEL: Record<CardFilterOperator, string> = {

export const isDateType = (type: CardFilterType) => ['date', 'pastDate', 'sequencing'].includes(type);
export const isTextType = (type: CardFilterType) => ['ticketCode', 'title', 'text', 'longText'].includes(type);
export const isSelectType = (type: CardFilterType) => ['template', 'oneOf', 'manyOf'].includes(type);
export const isSelectType = (type: CardFilterType) => ['template', 'oneOf', 'manyOf', 'owner'].includes(type);

export const getFilterOperatorLabels = (type: CardFilterType) => isDateType(type) ? DATE_FILTER_OPERATOR_LABEL : FILTER_OPERATOR_LABEL;

Expand All @@ -90,7 +90,7 @@ export const getValidOperators = (type: CardFilterType): CardFilterOperator[] =>
if (isDateType(type)) return ['ex', 'nex', 'eq', 'neq', 'gte', 'lte', 'rng', 'nrng'];
if (type === 'boolean') return ['eq', 'ex', 'nex'];
if (isSelectType(type)) {
if (type === 'template') return ['is', 'nis'];
if (['template', 'owner'].includes(type)) return ['is', 'nis'];
return ['ex', 'nex', 'is', 'nis'];
}
return Object.keys(FILTER_OPERATOR_LABEL) as CardFilterOperator[];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@
*/

export type CardFilterOperator = 'ex' | 'nex' | 'is' | 'nis' | 'eq' | 'neq' | 'ss' | 'nss' | 'rng' | 'nrng' | 'gt' | 'gte' | 'lt' | 'lte';
export type CardFilterType = 'text' | 'longText' | 'date' | 'sequencing' | 'pastDate' | 'oneOf' | 'manyOf' | 'boolean' | 'number' | 'title' | 'ticketCode' | 'template';
export type CardFilterType = 'text' | 'longText' | 'date' | 'sequencing' | 'pastDate' | 'oneOf' | 'manyOf' | 'boolean' | 'number' | 'title' | 'ticketCode' | 'template' | 'owner';
type ValueType = string | number | Date;
export type CardFilterValue = ValueType | ValueType[];
export type BaseFilter = { operator: CardFilterOperator, values: CardFilterValue[] };
export type BaseFilter = { operator: CardFilterOperator, values: CardFilterValue[], displayValues?: string };
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think the Filter should be aware of the "displayValues"


export type CardFilter = {
property: string,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,27 +17,10 @@

import CloseIcon from '@assets/icons/outlined/close-outlined.svg';
import { ChipContainer, DeleteButton, TextWrapper, OperatorIconContainer, DisplayValue, Property } from './filterChip.styles';
import { FILTER_OPERATOR_ICON, getFilterOperatorLabels, isDateType, isRangeOperator } from '../cardFilters.helpers';
import { FILTER_OPERATOR_ICON, getFilterOperatorLabels } from '../cardFilters.helpers';
import { Tooltip } from '@mui/material';
import { FormattedMessage } from 'react-intl';
import { CardFilterType, BaseFilter, CardFilterOperator, CardFilterValue } from '../cardFilters.types';
import { formatSimpleDate } from '@/v5/helpers/intl.helper';
import { formatMessage } from '@/v5/services/intl';
import { isBoolean } from 'lodash';
import { FALSE_LABEL, TRUE_LABEL } from '@controls/inputs/booleanSelect/booleanSelect.component';

const valueToDisplayDate = (value) => formatSimpleDate(new Date(value));
const formatDateRange = ([from, to]) => formatMessage(
{ defaultMessage: '{from} to {to}', id: 'cardFilter.dateRange.join' },
{ from: valueToDisplayDate(from), to: valueToDisplayDate(to) },
);

const getDisplayValue = (values: CardFilterValue[], operator: CardFilterOperator, type: CardFilterType) => {
const isRange = isRangeOperator(operator);
if (isDateType(type)) return values.map(isRange ? formatDateRange : valueToDisplayDate);
if (type === 'boolean' && isBoolean(values[0])) return values[0] ? TRUE_LABEL : FALSE_LABEL;
return (isRange ? values.map(([a, b]: any) => `[${a}, ${b}]`) : values).join(', ') ?? '';
};
import { CardFilterType, BaseFilter } from '../cardFilters.types';

type FilterChipProps = {
property: string;
Expand All @@ -47,10 +30,9 @@ type FilterChipProps = {
onDelete: () => void;
};
export const FilterChip = ({ property, onDelete, selected, type, filter }: FilterChipProps) => {
const { operator, values } = filter;
const { operator, values, displayValues = values.join(', ') } = filter;
const OperatorIcon = FILTER_OPERATOR_ICON[operator];
const hasMultipleValues = values.length > 1;
const displayValue = getDisplayValue(values, operator, type);
const labels = getFilterOperatorLabels(type);

const handleDelete = (e) => {
Expand All @@ -61,7 +43,7 @@ export const FilterChip = ({ property, onDelete, selected, type, filter }: Filte

return (
<ChipContainer selected={selected}>
<Tooltip title={`${property} ${labels[operator]} ${displayValue}`}>
<Tooltip title={`${property} ${labels[operator]} ${displayValues}`}>
<TextWrapper>
<Property>{property}</Property>
<OperatorIconContainer>
Expand All @@ -73,7 +55,7 @@ export const FilterChip = ({ property, onDelete, selected, type, filter }: Filte
</DisplayValue>
)}
{!hasMultipleValues && !!values?.length && (
<DisplayValue>{displayValue}</DisplayValue>
<DisplayValue>{displayValues}</DisplayValue>
)}
</TextWrapper>
</Tooltip>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,24 @@

import { FormattedMessage } from 'react-intl';
import { CardFilterOperator, CardFilterValue, CardFilterType, BaseFilter, CardFilter } from '../cardFilters.types';
import { getFilterFormTitle } from '../cardFilters.helpers';
import { getFilterFormTitle, getValidOperators, isDateType, isRangeOperator } from '../cardFilters.helpers';
import { Container, ButtonsContainer, Button, TitleContainer } from './filterForm.styles';
import { FormProvider, useForm } from 'react-hook-form';
import { isEmpty } from 'lodash';
import { intersection, isBoolean, isEmpty } from 'lodash';
import { ActionMenuItem } from '@controls/actionMenu';
import { FilterFormValues } from './filterFormValues/filterFormValues.component';
import { mapArrayToFormArray, mapFormArrayToArray } from '@/v5/helpers/form.helper';
import { yupResolver } from '@hookform/resolvers/yup';
import { FilterSchema } from '@/v5/validation/ticketSchemes/validators';
import { FilterFormOperators } from './filterFormValues/operators/filterFormOperators.component';
import { getOptionFromValue } from '../filtersSelection/tickets/ticketFilters.helpers';
import { formatSimpleDate } from '@/v5/helpers/intl.helper';
import { formatMessage } from '@/v5/services/intl';
import { TRUE_LABEL, FALSE_LABEL } from '@controls/inputs/booleanSelect/booleanSelect.component';

const DEFAULT_OPERATOR = 'is';
const DEFAULT_OPERATORS: CardFilterOperator[] = ['is', 'eq'];
const DEFAULT_VALUES = [''];
type FormType = { values: { value: CardFilterValue }[], operator: CardFilterOperator };
type FormType = { values: { value: CardFilterValue, displayValue?: string }[], operator: CardFilterOperator };
type FilterFormProps = {
module: string,
property: string,
Expand All @@ -39,9 +43,16 @@ type FilterFormProps = {
onSubmit: (newFilter: CardFilter) => void,
onCancel: () => void,
};

const valueToDisplayDate = (value) => formatSimpleDate(new Date(value));
const formatDateRange = ([from, to]) => formatMessage(
{ defaultMessage: '{from} to {to}', id: 'cardFilter.dateRange.join' },
{ from: valueToDisplayDate(from), to: valueToDisplayDate(to) },
);

export const FilterForm = ({ module, property, type, filter, onSubmit, onCancel }: FilterFormProps) => {
const defaultValues = {
operator: filter?.operator || DEFAULT_OPERATOR,
const defaultValues: FormType = {
operator: filter?.operator || intersection(getValidOperators(type), DEFAULT_OPERATORS)[0],
values: mapArrayToFormArray(filter?.values || DEFAULT_VALUES),
};

Expand All @@ -50,16 +61,33 @@ export const FilterForm = ({ module, property, type, filter, onSubmit, onCancel
mode: 'onChange',
resolver: yupResolver(FilterSchema),
context: { type },
shouldUnregister: true,
});
const { formState: { isValid, dirtyFields }, reset } = formData;
const { formState: { isValid, dirtyFields }, reset, getValues } = formData;

const operatorValue = getValues('operator');
if (!getValidOperators(type).includes(operatorValue)) {
reset(defaultValues);
}

const isUpdatingFilter = !!filter;
const canSubmit = isValid && !isEmpty(dirtyFields);

const handleSubmit = formData.handleSubmit((body: FormType) => {
const newValues = mapFormArrayToArray(body.values)
.filter((x) => ![undefined, ''].includes(x as any));
onSubmit({ module, property, type, filter: { operator: body.operator, values: newValues } });
const isRange = isRangeOperator(body.operator);
const displayValues = newValues.map((newVal) => {
const option = getOptionFromValue(newVal, body.values);
if (isDateType(type)) return (isRange ? formatDateRange(newVal) : valueToDisplayDate(newVal));
if (type === 'boolean' && isBoolean(newValues[0])) return newValues[0] ? TRUE_LABEL : FALSE_LABEL;
if (isRange) {
const [a, b] = newVal;
return `[${a}, ${b}]`;
}
return option.displayValue ?? newVal;
}).join(', ');
onSubmit({ module, property, type, filter: { operator: body.operator, values: newValues, displayValues } });
});

const handleCancel = () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export const Container = styled.div`
display: flex;
flex-direction: column;
gap: 10px;
max-width: 365px;
`;

export const TitleContainer = styled.div`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,20 +18,21 @@
import { useFieldArray, useFormContext } from 'react-hook-form';
import { getOperatorMaxFieldsAllowed } from '../filterForm.helpers';
import { isRangeOperator, isTextType, isSelectType, isDateType } from '../../cardFilters.helpers';
import { FormBooleanSelect, FormMultiSelect, FormDateTime, FormNumberField, FormTextField } from '@controls/inputs/formInputs.component';
import { FormBooleanSelect, FormMultiSelect, FormDateTime, FormNumberField, FormTextField, FormJobsAndUsersSelect } from '@controls/inputs/formInputs.component';
import { ArrayFieldContainer } from '@controls/inputs/arrayFieldContainer/arrayFieldContainer.component';
import { useEffect } from 'react';
import { compact, isArray, isEmpty } from 'lodash';
import { CardFilterType } from '../../cardFilters.types';
import { TicketsCardHooksSelectors, TicketsHooksSelectors } from '@/v5/services/selectorsHooks';
import { TicketsCardHooksSelectors } from '@/v5/services/selectorsHooks';
import { useParams } from 'react-router-dom';
import { ViewerParams } from '@/v5/ui/routes/routes.constants';
import { MultiSelectMenuItem } from '@controls/inputs/multiSelect/multiSelectMenuItem/multiSelectMenuItem.component';
import { DateRangeInput } from './rangeInput/dateRangeInput.component';
import { NumberRangeInput } from './rangeInput/numberRangeInput.component';
import { mapArrayToFormArray, mapFormArrayToArray } from '@/v5/helpers/form.helper';
import { mapFormArrayToArray } from '@/v5/helpers/form.helper';
import { getOptionFromValue, getFilterFromEvent } from '../../filtersSelection/tickets/ticketFilters.helpers';

type FilterFolrmValuesType = {
type FilterFormValuesProps = {
module: string,
property: string,
type: CardFilterType,
Expand All @@ -44,7 +45,7 @@ const getInputField = (type: CardFilterType) => {
};

const name = 'values';
export const FilterFormValues = ({ module, property, type }: FilterFolrmValuesType) => {
export const FilterFormValues = ({ module, property, type }: FilterFormValuesProps) => {
const { containerOrFederation } = useParams<ViewerParams>();
const { control, watch, formState: { errors, dirtyFields } } = useFormContext();
const { fields, append, remove } = useFieldArray({
Expand All @@ -58,7 +59,7 @@ export const FilterFormValues = ({ module, property, type }: FilterFolrmValuesTy
const isRangeOp = isRangeOperator(operator);
const emptyValue = { value: (isRangeOp ? ['', ''] : '') };
const selectOptions = type === 'template' ?
TicketsHooksSelectors.selectTemplatesNames(containerOrFederation)
TicketsCardHooksSelectors.selectTemplatesWithTickets().map(({ code: value, name: displayValue }) => ({ value, displayValue, type: 'template' }))
: TicketsCardHooksSelectors.selectPropertyOptions(containerOrFederation, module, property);

useEffect(() => {
Expand Down Expand Up @@ -122,15 +123,32 @@ export const FilterFormValues = ({ module, property, type }: FilterFolrmValuesTy
</>
);
}

if (isSelectType(type)) {
const allJobsAndUsers = selectOptions.every(({ type: t }) => t === 'jobsAndUsers');
if (allJobsAndUsers) return (
<FormJobsAndUsersSelect
multiple
showAddButton
excludeJobs={type === 'owner'}
maxItems={19}
name={name}
transformInputValue={(v) => compact(mapFormArrayToArray(v))}
transformOutputValue={(e) => getFilterFromEvent(e, selectOptions)}
formError={error?.[0]}
/>
);
return (
<FormMultiSelect
name={name}
transformInputValue={mapFormArrayToArray}
transformOutputValue={(e) => getFilterFromEvent(e, selectOptions)}
renderValue={(values: string[]) => values.map((value) => getOptionFromValue(value, selectOptions)?.displayValue ?? value).join(', ')}
formError={error?.[0]}
transformValueIn={mapFormArrayToArray}
transformChangeEvent={(e) => mapArrayToFormArray(compact(e.target.value))}
>
{(selectOptions || []).map((val) => <MultiSelectMenuItem key={val} value={val}>{val}</MultiSelectMenuItem>)}
{selectOptions.map(
(option) => <MultiSelectMenuItem key={option.value} value={option.value}>{option.displayValue ?? option.value}</MultiSelectMenuItem>,
)}
</FormMultiSelect>
);
}
Expand All @@ -143,4 +161,4 @@ export const FilterFormValues = ({ module, property, type }: FilterFolrmValuesTy
<FormTextField name={`${name}.0.value`} />
</>
);
};
};
Loading
Loading