[WEB-5132] feat: add label-based grouping and filtering for initiatives (#4467)

This commit is contained in:
Jayash Tripathy
2025-10-14 21:01:18 +05:30
committed by GitHub
parent 541db7d25a
commit 0852fe38bc
22 changed files with 207 additions and 50 deletions
@@ -9,6 +9,8 @@ from plane.utils.filters.filterset import IssueFilterSet, UUIDInFilter, CharInFi
from plane.utils.exception_logger import log_exception
from plane.ee.models import Initiative
from plane.utils.uuid import is_valid_uuid
class ExtendedIssueFilterSet(IssueFilterSet):
team_project_id = filters.UUIDFilter(field_name="project_id")
team_project_id__in = UUIDInFilter(field_name="project_id", lookup_expr="in")
@@ -62,7 +64,6 @@ class ExtendedIssueFilterSet(IssueFilterSet):
return fields
return []
def filter_custom_property_value(self, queryset, name, value):
"""Filter by custom property value (exact match)"""
return self._filter_custom_property_value_with_lookup(queryset, "exact", value)
@@ -113,7 +114,7 @@ class ExtendedIssueFilterSet(IssueFilterSet):
def _filter_custom_property_value_with_lookup(self, queryset, lookup, value):
"""Helper method to filter by custom property value with a specific lookup.
This method handles the actual filtering logic for custom properties by:
1. Getting the property_id from the request context (set by the filter backend)
2. Joining with the IssuePropertyValue table
@@ -136,12 +137,13 @@ class ExtendedIssueFilterSet(IssueFilterSet):
return Q()
# Get the property to determine its type
property_obj = IssueProperty.objects.get(
id=property_id
)
property_obj = IssueProperty.objects.get(id=property_id)
# Build the filter based on the property type and lookup
if property_obj.property_type in [PropertyTypeEnum.TEXT.value, PropertyTypeEnum.URL.value,]:
if property_obj.property_type in [
PropertyTypeEnum.TEXT.value,
PropertyTypeEnum.URL.value,
]:
field_name = "value_text"
elif property_obj.property_type == PropertyTypeEnum.DECIMAL:
field_name = "value_decimal"
@@ -171,7 +173,7 @@ class ExtendedIssueFilterSet(IssueFilterSet):
properties__property_id=property_id,
properties__property__deleted_at__isnull=True,
properties__deleted_at__isnull=True,
**{f"properties__{field_name}": value}
**{f"properties__{field_name}": value},
)
elif lookup == "in" and property_obj.property_type in [
PropertyTypeEnum.OPTION.value,
@@ -182,7 +184,7 @@ class ExtendedIssueFilterSet(IssueFilterSet):
properties__property_id=property_id,
properties__property__deleted_at__isnull=True,
properties__deleted_at__isnull=True,
**{f"properties__{field_name}__in": value}
**{f"properties__{field_name}__in": value},
)
elif lookup == "gte" and property_obj.property_type in [
PropertyTypeEnum.DECIMAL.value,
@@ -193,7 +195,7 @@ class ExtendedIssueFilterSet(IssueFilterSet):
properties__property_id=property_id,
properties__property__deleted_at__isnull=True,
properties__deleted_at__isnull=True,
**{f"properties__{field_name}__gte": value}
**{f"properties__{field_name}__gte": value},
)
elif lookup == "gt" and property_obj.property_type in [
PropertyTypeEnum.DECIMAL.value,
@@ -204,7 +206,7 @@ class ExtendedIssueFilterSet(IssueFilterSet):
properties__property_id=property_id,
properties__property__deleted_at__isnull=True,
properties__deleted_at__isnull=True,
**{f"properties__{field_name}__gt": value}
**{f"properties__{field_name}__gt": value},
)
elif lookup == "lte" and property_obj.property_type in [
PropertyTypeEnum.DECIMAL.value,
@@ -215,7 +217,7 @@ class ExtendedIssueFilterSet(IssueFilterSet):
properties__property_id=property_id,
properties__property__deleted_at__isnull=True,
properties__deleted_at__isnull=True,
**{f"properties__{field_name}__lte": value}
**{f"properties__{field_name}__lte": value},
)
elif lookup == "lt" and property_obj.property_type in [
PropertyTypeEnum.DECIMAL.value,
@@ -226,7 +228,7 @@ class ExtendedIssueFilterSet(IssueFilterSet):
properties__property_id=property_id,
properties__property__deleted_at__isnull=True,
properties__deleted_at__isnull=True,
**{f"properties__{field_name}__lt": value}
**{f"properties__{field_name}__lt": value},
)
elif lookup == "icontains" and property_obj.property_type in [
PropertyTypeEnum.TEXT.value,
@@ -237,7 +239,7 @@ class ExtendedIssueFilterSet(IssueFilterSet):
properties__property_id=property_id,
properties__property__deleted_at__isnull=True,
properties__deleted_at__isnull=True,
**{f"properties__{field_name}__icontains": value}
**{f"properties__{field_name}__icontains": value},
)
elif lookup == "contains" and property_obj.property_type in [
PropertyTypeEnum.TEXT.value,
@@ -248,7 +250,7 @@ class ExtendedIssueFilterSet(IssueFilterSet):
properties__property_id=property_id,
properties__property__deleted_at__isnull=True,
properties__deleted_at__isnull=True,
**{f"properties__{field_name}__contains": value}
**{f"properties__{field_name}__contains": value},
)
elif lookup == "startswith" and property_obj.property_type in [
PropertyTypeEnum.TEXT.value,
@@ -259,7 +261,7 @@ class ExtendedIssueFilterSet(IssueFilterSet):
properties__property_id=property_id,
properties__property__deleted_at__isnull=True,
properties__deleted_at__isnull=True,
**{f"properties__{field_name}__startswith": value}
**{f"properties__{field_name}__startswith": value},
)
elif lookup == "endswith" and property_obj.property_type in [
PropertyTypeEnum.TEXT.value,
@@ -270,7 +272,7 @@ class ExtendedIssueFilterSet(IssueFilterSet):
properties__property_id=property_id,
properties__property__deleted_at__isnull=True,
properties__deleted_at__isnull=True,
**{f"properties__{field_name}__endswith": value}
**{f"properties__{field_name}__endswith": value},
)
elif lookup == "range" and property_obj.property_type in [
PropertyTypeEnum.DECIMAL.value,
@@ -283,7 +285,7 @@ class ExtendedIssueFilterSet(IssueFilterSet):
properties__property_id=property_id,
properties__property__deleted_at__isnull=True,
properties__deleted_at__isnull=True,
**{f"properties__{field_name}__range": value}
**{f"properties__{field_name}__range": value},
)
return Q()
else:
@@ -296,6 +298,8 @@ class ExtendedIssueFilterSet(IssueFilterSet):
class InitiativeFilterSet(BaseFilterSet):
lead = filters.UUIDFilter(field_name="lead")
lead__in = UUIDInFilter(field_name="lead", lookup_expr="in")
label_id = filters.UUIDFilter(method="filter_label_id")
label_id__in = UUIDInFilter(method="filter_label_id_in", lookup_expr="in")
class Meta:
model = Initiative
@@ -304,3 +308,17 @@ class InitiativeFilterSet(BaseFilterSet):
"end_date": ["exact", "gte", "gt", "lte", "lt", "range"],
"state": ["exact", "in"],
}
def filter_label_id(self, queryset, name, value):
"""Filter by label IDs (in), excluding soft deleted labels"""
return Q(
initiative_label_associations__label_id=value,
initiative_label_associations__deleted_at__isnull=True,
)
def filter_label_id_in(self, queryset, name, value):
"""Filter by label IDs (in), excluding soft deleted labels"""
return Q(
initiative_label_associations__label_id__in=value,
initiative_label_associations__deleted_at__isnull=True,
)
@@ -10,11 +10,9 @@ import { Popover, Transition } from "@headlessui/react";
// plane imports
import { getRandomLabelColor, LABEL_COLOR_OPTIONS } from "@plane/constants";
import { useTranslation } from "@plane/i18n";
import type { TInitiativeLabel } from "@plane/types";
import { Button, Input } from "@plane/ui";
// local imports
import type { TInitiativeLabel } from "@/plane-web/types";
export type TInitiativeLabelOperationsCallbacks = {
createLabel: (data: Partial<TInitiativeLabel>) => Promise<TInitiativeLabel | undefined>;
updateLabel: (labelId: string, data: Partial<TInitiativeLabel>) => Promise<TInitiativeLabel | undefined>;
@@ -11,7 +11,7 @@ import { AlertModalCore } from "@plane/ui";
// local imports
import { useInitiatives } from "@/plane-web/hooks/store/use-initiatives";
import type { TInitiativeLabel } from "@/plane-web/types";
import type { TInitiativeLabel } from "@plane/types";
type Props = {
isOpen: boolean;
@@ -7,9 +7,9 @@ import { X } from "lucide-react";
// plane imports
import { useOutsideClickDetector } from "@plane/hooks";
import type { TInitiativeLabel } from "@plane/types";
import { CustomMenu, DragHandle } from "@plane/ui";
import { cn } from "@plane/utils";
import type { TInitiativeLabel } from "@/plane-web/types";
// local imports
import { InitiativeLabelName } from "./initiative-label-name";
@@ -12,13 +12,12 @@ import { createRoot } from "react-dom/client";
// plane imports
import { EUserPermissionsLevel } from "@plane/constants";
import type { InstructionType } from "@plane/types";
import type { InstructionType, TInitiativeLabel } from "@plane/types";
import { EUserWorkspaceRoles } from "@plane/types";
import { DropIndicator } from "@plane/ui";
// local imports
import { useUserPermissions } from "@/hooks/store/user";
import type { TInitiativeLabel } from "@/plane-web/types/initiative";
import { InitiativeLabelName } from "./initiative-label-name";
import { getInitiativeCanDrop } from "./initiative-label-utils";
@@ -10,11 +10,11 @@ import { EUserPermissionsLevel } from "@plane/constants";
import { useTranslation } from "@plane/i18n";
import { Combobox } from "@plane/propel/combobox";
import { EUserWorkspaceRoles } from "@plane/types";
import type { TInitiativeLabel } from "@plane/types";
import { cn } from "@plane/utils";
// types
import { useUserPermissions } from "@/hooks/store/user";
import type { TInitiativeLabel } from "@/plane-web/types/initiative";
export type TInitiativeLabelDropdownProps = {
value: string[];
@@ -3,7 +3,7 @@ import React, { useState } from "react";
import { observer } from "mobx-react";
import { Pencil, Trash2 } from "lucide-react";
// plane imports
import type { TInitiativeLabel } from "@/plane-web/types/initiative";
import type { TInitiativeLabel } from "@plane/types";
// components
import type { TInitiativeLabelOperationsCallbacks } from "./create-update-initiative-label-inline";
import { CreateUpdateInitiativeLabelInline } from "./create-update-initiative-label-inline";
@@ -8,6 +8,7 @@ import { useParams } from "next/navigation";
import { EUserPermissions, EUserPermissionsLevel } from "@plane/constants";
import { useTranslation } from "@plane/i18n";
import { TOAST_TYPE, setToast } from "@plane/propel/toast";
import type { TInitiativeLabel } from "@plane/types";
import { Loader } from "@plane/ui";
import { DetailedEmptyState } from "@/components/empty-state/detailed-empty-state-root";
import SettingsHeading from "@/components/settings/heading";
@@ -18,7 +19,6 @@ import { useResolvedAssetPath } from "@/hooks/use-resolved-asset-path";
// local imports
import { useInitiatives } from "@/plane-web/hooks/store/use-initiatives";
import type { TInitiativeLabel } from "@/plane-web/types";
import type { TInitiativeLabelOperationsCallbacks } from "./create-update-initiative-label-inline";
import { CreateUpdateInitiativeLabelInline } from "./create-update-initiative-label-inline";
import { DeleteInitiativeLabelModal } from "./delete-initiative-label-modal";
@@ -12,6 +12,7 @@ import { EUserPermissionsLevel, getRandomLabelColor } from "@plane/constants";
import { useOutsideClickDetector } from "@plane/hooks";
import { useTranslation } from "@plane/i18n";
import { EUserWorkspaceRoles } from "@plane/types";
import type { TInitiativeLabel } from "@plane/types";
import { ComboDropDown } from "@plane/ui";
// local imports
@@ -19,7 +20,6 @@ import { useUserPermissions } from "@/hooks/store/user";
import { useDropdownKeyDown } from "@/hooks/use-dropdown-key-down";
import { usePlatformOS } from "@/hooks/use-platform-os";
import { useInitiatives } from "@/plane-web/hooks/store/use-initiatives";
import type { TInitiativeLabel } from "@/plane-web/types";
import { getInitiativeLabelsArray } from "./initiative-label-utils";
export interface IInitiativeLabelPropertyDropdownProps {
@@ -9,12 +9,12 @@ import { Tags } from "lucide-react";
import { useOutsideClickDetector } from "@plane/hooks";
import { useTranslation } from "@plane/i18n";
import { Tooltip } from "@plane/propel/tooltip";
import type { TInitiativeLabel } from "@plane/types";
// local imports
import { cn } from "@plane/utils";
import { usePlatformOS } from "@/hooks/use-platform-os";
import { useInitiatives } from "@/plane-web/hooks/store/use-initiatives";
import type { TInitiativeLabel } from "@/plane-web/types";
import { InitiativeLabelPropertyDropdown } from "./initiative-label-property-dropdown";
export interface IInitiativeLabelProperty {
@@ -1,5 +1,4 @@
import type { TDropTarget } from "@plane/types";
import type { TInitiativeLabel } from "@/plane-web/types/initiative";
import type { TDropTarget, TInitiativeLabel } from "@plane/types";
/**
* This provides a boolean to indicate if the label can be dropped onto the droptarget
@@ -14,7 +14,7 @@ import { Tooltip } from "@plane/propel/tooltip";
import { cn } from "@plane/utils";
import { usePlatformOS } from "@/hooks/use-platform-os";
import { useInitiatives } from "@/plane-web/hooks/store/use-initiatives";
import type { TInitiativeLabel } from "@/plane-web/types/initiative";
import type { TInitiativeLabel } from "@plane/types";
import { InitiativeLabelPropertyDropdown } from "./initiative-label-property-dropdown";
import { getInitiativeLabelsArray } from "./initiative-label-utils";
@@ -43,6 +43,7 @@ const InitiativesFilterInstanceProvider = observer(
}) => {
const {
initiativeFilters: { updateFilters },
initiative: { getInitiativesLabels },
} = useInitiatives();
const [filterInstance] = useState(() =>
@@ -57,15 +58,18 @@ const InitiativesFilterInstanceProvider = observer(
getUserDetails,
} = useMember();
const labels = getInitiativesLabels(workspaceSlug);
const workspaceMembers = useMemo(() => {
if (!workspaceMemberIds) return [];
return workspaceMemberIds.map((memberId) => getUserDetails(memberId)).filter(Boolean) as IUserLite[];
}, [getUserDetails, workspaceMemberIds]);
const { leadFilterConfig, startDateFilterConfig, endDateFilterConfig, statesFilterConfig } =
const { leadFilterConfig, startDateFilterConfig, endDateFilterConfig, statesFilterConfig, labelsFilterConfig } =
useInitiativesFilterConfigs({
workspaceMembers,
operatorConfigs,
labels: Array.from(labels?.values() || []),
});
// Register all filter configs
@@ -74,6 +78,7 @@ const InitiativesFilterInstanceProvider = observer(
startDateFilterConfig,
endDateFilterConfig,
statesFilterConfig,
labelsFilterConfig,
]);
const value = useMemo(
@@ -1,25 +1,28 @@
import { useMemo } from "react";
import { CalendarCheck2, CalendarClock, Users } from "lucide-react";
import { CalendarCheck2, CalendarClock, Tags, Users } from "lucide-react";
import { INITIATIVE_STATES } from "@plane/constants";
import { InitiativeStateIcon } from "@plane/propel/icons";
import type { IUserLite } from "@plane/types";
import type { IUserLite, TInitiativeLabel } from "@plane/types";
import { Avatar } from "@plane/ui";
import {
getInitiativeLeadFilterConfig,
getInitiativeStartDateFilterConfig,
getInitiativeEndDateFilterConfig,
getInitiativeStatesFilterConfig,
getInitiativeLabelsFilterConfig,
} from "@plane/utils";
import type { TFiltersOperatorConfigs } from "@/ce/hooks/rich-filters/use-filters-operator-configs";
interface UseInitiativesFilterConfigsProps {
workspaceMembers: IUserLite[];
operatorConfigs: TFiltersOperatorConfigs;
labels: TInitiativeLabel[];
}
export const useInitiativesFilterConfigs = ({
workspaceMembers,
operatorConfigs,
labels,
}: UseInitiativesFilterConfigsProps) => {
const leadFilterConfig = useMemo(
() =>
@@ -68,10 +71,26 @@ export const useInitiativesFilterConfigs = ({
}),
[operatorConfigs]
);
const labelsFilterConfig = useMemo(
() =>
getInitiativeLabelsFilterConfig("label_id")({
isEnabled: true,
filterIcon: Tags,
labels: labels,
getOptionIcon: (color: string) => (
<span className="flex flex-shrink-0 size-2.5 rounded-full" style={{ backgroundColor: color }} />
),
...operatorConfigs,
}),
[labels, operatorConfigs]
);
return {
leadFilterConfig,
startDateFilterConfig,
endDateFilterConfig,
statesFilterConfig,
labelsFilterConfig,
};
};
@@ -31,6 +31,14 @@ export const getGroupList = (
(a, b) =>
INITIATIVE_STATES[a as TInitiativeStates].sortOrder - INITIATIVE_STATES[b as TInitiativeStates].sortOrder
);
case "label_ids":
// Sort labels alphabetically, but "None" first
sortedGroupIds = sortedGroupIds.sort((a, b) => {
if (a === "None") return -1;
if (b === "None") return 1;
return a.localeCompare(b);
});
break;
default:
sortedGroupIds = sortedGroupIds.sort((a) => {
if (a === "none") return -1;
@@ -80,6 +88,36 @@ export const getGroupList = (
}
}
if (groupBy === "label_ids") {
const workspaceSlug = rootStore.router.workspaceSlug;
const { getInitiativesLabels } = rootStore.initiativeStore;
if (!workspaceSlug) return groupList;
const labelsMap = getInitiativesLabels(workspaceSlug);
for (const groupId of sortedGroupIds) {
if (groupId === "None") {
groupList.push({
id: groupId,
name: "None",
icon: <div className="h-3 w-3 rounded-full flex-shrink-0" style={{ backgroundColor: "#666" }} />,
});
continue;
}
const label = labelsMap?.get(groupId);
if (!label) continue;
groupList.push({
id: groupId,
name: label.name,
icon: <div className="h-3 w-3 rounded-full flex-shrink-0" style={{ backgroundColor: label.color }} />,
});
}
}
return groupList;
};
+1
View File
@@ -13,6 +13,7 @@ export const INITIATIVE_GROUP_BY_OPTIONS: {
{ key: "lead", title: "Lead" },
{ key: "created_by", title: "Created By" },
{ key: "state", title: "States" },
{ key: "label_ids", title: "Labels" },
{ key: undefined, title: "None" },
];
@@ -1,7 +1,7 @@
import { API_BASE_URL } from "@plane/constants";
// services
import type { TInitiativeLabel } from "@/plane-web/types";
import type { TInitiativeLabel } from "@plane/types";
// types
import { APIService } from "@/services/api.service";
@@ -4,7 +4,13 @@ import { computedFn } from "mobx-utils";
// plane imports
import { E_FEATURE_FLAGS } from "@plane/constants";
import type { TEpicStats, TLoader, TInitiativeGroupByOptions, TInitiativeOrderByOptions } from "@plane/types";
import type {
TEpicStats,
TLoader,
TInitiativeGroupByOptions,
TInitiativeOrderByOptions,
TInitiativeLabel,
} from "@plane/types";
import { convertToISODateString } from "@plane/utils";
// plane-web imports
@@ -14,7 +20,6 @@ import type {
TExternalInitiativeFilterExpression,
TInitiative,
TInitiativeAnalytics,
TInitiativeLabel,
TInitiativeReaction,
TInitiativeStats,
} from "@/plane-web/types/initiative";
@@ -406,6 +411,10 @@ export class InitiativeStore implements IInitiativeStore {
if (!this.initiativesMap) this.initiativesMap = {};
this.initiativesMap[response.id] = response;
if (this.filteredInitiativesMap) {
this.filteredInitiativesMap[response.id] = response;
}
});
this.fetchInitiativesStats(workspaceSlug);
@@ -736,6 +745,26 @@ const getGroupedInitiativeIds = (initiatives: TInitiative[], key: TInitiativeGro
if (!key) return { [ALL_INITIATIVES]: initiatives.map((initiative) => initiative.id) };
if (key === "label_ids") {
for (const initiative of initiatives) {
const labelIds = initiative.label_ids;
if (!labelIds || labelIds.length === 0) {
if (!groupedInitiativeIds["None"]) {
groupedInitiativeIds["None"] = [];
}
groupedInitiativeIds["None"].push(initiative.id);
} else {
labelIds.forEach((labelId) => {
if (!groupedInitiativeIds[labelId]) {
groupedInitiativeIds[labelId] = [];
}
groupedInitiativeIds[labelId].push(initiative.id);
});
}
}
return groupedInitiativeIds;
}
for (const initiative of initiatives) {
const groupId = initiative[key] ?? "None";
@@ -13,7 +13,7 @@ export type TInitiativeStoredFilters = {
// initiative rich filters
export type TInitiativeFilterKeys = "lead" | "start_date" | "end_date" | "state";
export type TInitiativeFilterKeys = "lead" | "start_date" | "end_date" | "state" | "label_id";
export type TExternalInitiativeFilterOperator =
| typeof CORE_OPERATORS.EXACT
@@ -42,8 +42,8 @@ export type TExternalInitiativeFilterGroup =
export type TExternalInitiativeFilterExpressionData =
| {
[key in TInitiativeFilterKeys]?: TFilterValue;
}
[key in TInitiativeFilterKeys]?: TFilterValue;
}
| TExternalInitiativeFilterGroup;
export type TInternalInitiativeFilterExpression = TFilterExpression<TInitiativeFilterKeys>;
@@ -202,9 +202,3 @@ export type TInitiativeStats = {
off_track_updates: number;
};
export type TInitiativeLabel = {
id: string;
name: string;
color: string;
sort_order: number;
};
@@ -26,6 +26,13 @@ export type TInitiativeAttachment = {
updated_by: string;
};
export type TInitiativeLabel = {
id: string;
name: string;
color: string;
sort_order: number;
};
export type TInitiativeAttachmentUploadResponse = TFileSignedURLResponse & {
attachment: TInitiativeAttachment;
};
@@ -49,5 +56,5 @@ export type TInitiativeDisplayFilters = {
order_by?: TInitiativeOrderByOptions;
};
export type TInitiativeGroupByOptions = "lead" | "created_by" | "state" | undefined;
export type TInitiativeGroupByOptions = "lead" | "created_by" | "state" | "label_ids" | undefined;
export type TInitiativeOrderByOptions = "-updated_at" | "-created_at" | "sort_order";
@@ -1,5 +1,12 @@
// plane imports
import { COLLECTION_OPERATOR, EQUALITY_OPERATOR, IUserLite, TInitiativeStates, TFilterProperty } from "@plane/types";
import {
COLLECTION_OPERATOR,
EQUALITY_OPERATOR,
IUserLite,
TInitiativeStates,
TFilterProperty,
TInitiativeLabel,
} from "@plane/types";
// local imports
import {
createFilterConfig,
@@ -24,6 +31,11 @@ export type TCreateInitiativeStatesFilterParams = TCreateFilterConfigParams &
items: Array<{ key: TInitiativeStates; title: string; icon: React.FC<React.SVGAttributes<SVGElement>> }>;
};
export type TCreateInitiativeLabelsFilterParams = TCreateFilterConfigParams &
IFilterIconConfig<string> & {
labels: TInitiativeLabel[];
};
// ------------ Lead filter ------------
/**
@@ -89,6 +101,44 @@ export const getInitiativeStatesFilterConfig =
]),
});
// ------------ Labels filter ------------
/**
* Get the labels filter config for initiatives
* @template K - The filter key
* @param key - The filter key to use
* @returns A function that takes parameters and returns the labels filter config
*/
export const getInitiativeLabelsFilterConfig =
<P extends string>(key: P) =>
(params: TCreateInitiativeLabelsFilterParams) =>
createFilterConfig<P, string>({
id: key,
label: "Labels",
icon: params.filterIcon,
isEnabled: params.isEnabled,
supportedOperatorConfigsMap: new Map([
createOperatorConfigEntry(COLLECTION_OPERATOR.IN, params, (updatedParams) =>
getMultiSelectConfig<TInitiativeLabel, string, string>(
{
items: params.labels,
getId: (label) => label.id,
getLabel: (label) => label.name,
getValue: (label) => label.id,
getIconData: (label) => label.color,
},
{
singleValueOperator: EQUALITY_OPERATOR.EXACT,
...updatedParams,
},
{
getOptionIcon: params.getOptionIcon,
}
)
),
]),
});
// ------------ Date filters ------------
/**