mirror of
https://github.com/makeplane/plane.git
synced 2026-07-13 05:49:40 +02:00
@@ -198,6 +198,14 @@ class IssuePropertyEndpoint(BaseAPIView):
|
||||
# Reset the default options if the property is required
|
||||
if issue_property.is_required:
|
||||
self.reset_options_default(issue_property)
|
||||
# Reset the default options if property is not multi and more than one default value
|
||||
if not issue_property.is_multi and IssuePropertyOption.objects.filter(
|
||||
property_id=issue_property.id,
|
||||
workspace_id=issue_property.workspace_id,
|
||||
project_id=issue_property.project_id,
|
||||
is_default=True,
|
||||
).count() > 1:
|
||||
self.reset_options_default(issue_property)
|
||||
self.update_property_default_options(issue_property)
|
||||
|
||||
except IntegrityError:
|
||||
@@ -297,6 +305,14 @@ class IssuePropertyEndpoint(BaseAPIView):
|
||||
# Reset the default options if the property is required
|
||||
if issue_property.is_required:
|
||||
self.reset_options_default(issue_property)
|
||||
# Reset the default options if property is not multi and more than one default value
|
||||
if not issue_property.is_multi and IssuePropertyOption.objects.filter(
|
||||
property_id=issue_property.id,
|
||||
workspace_id=issue_property.workspace_id,
|
||||
project_id=issue_property.project_id,
|
||||
is_default=True,
|
||||
).count() > 1:
|
||||
self.reset_options_default(issue_property)
|
||||
self.update_property_default_options(issue_property)
|
||||
|
||||
except IntegrityError:
|
||||
|
||||
@@ -30,23 +30,26 @@ export const DropdownAttributes = observer((props: TDropdownAttributesProps) =>
|
||||
const { issueTypeId, dropdownPropertyDetail, currentOperationMode, onDropdownDetailChange, error } = props;
|
||||
// store hooks
|
||||
const issueType = useIssueType(issueTypeId);
|
||||
const { setPropertyOptions } = usePropertyOptions();
|
||||
const { propertyOptions, setPropertyOptions } = usePropertyOptions();
|
||||
// derived values
|
||||
const isAnyIssueAttached = issueType?.issue_exists;
|
||||
const isOptionDefaultDisabled = dropdownPropertyDetail.is_multi === undefined || !!dropdownPropertyDetail.is_required;
|
||||
// helpers
|
||||
const resetToSingleSelectDefault = () => {
|
||||
const firstDefaultOption = dropdownPropertyDetail.default_value?.[0];
|
||||
const firstDefaultOption = propertyOptions?.find((option) => option.is_default);
|
||||
const firstDefaultOptionIdentifier = firstDefaultOption?.id ?? firstDefaultOption?.key;
|
||||
// Update property options
|
||||
setPropertyOptions((prevOptions) => {
|
||||
if (!prevOptions) return [];
|
||||
return prevOptions.map((option) => ({
|
||||
...option,
|
||||
is_default: option.id === firstDefaultOption,
|
||||
is_default:
|
||||
!!firstDefaultOptionIdentifier &&
|
||||
(option.id === firstDefaultOptionIdentifier || option.key === firstDefaultOptionIdentifier),
|
||||
}));
|
||||
});
|
||||
// Update default value
|
||||
const newDefaultValue = firstDefaultOption ? [firstDefaultOption] : [];
|
||||
const newDefaultValue = firstDefaultOptionIdentifier ? [firstDefaultOptionIdentifier] : [];
|
||||
onDropdownDetailChange("default_value", newDefaultValue);
|
||||
};
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import { TIssuePropertyOptionCreateUpdateData } from "@/plane-web/types";
|
||||
type TIssuePropertyCreateOptionItem = {
|
||||
propertyOptionCreateListData: TIssuePropertyOptionCreateUpdateData;
|
||||
updateCreateListData: (value: TIssuePropertyOptionCreateUpdateData) => void;
|
||||
scrollIntoNewOptionView: () => void;
|
||||
error?: string;
|
||||
};
|
||||
|
||||
@@ -16,13 +17,14 @@ export const IssuePropertyCreateOptionItem = observer(
|
||||
props: TIssuePropertyCreateOptionItem,
|
||||
ref: React.Ref<HTMLDivElement>
|
||||
) {
|
||||
const { propertyOptionCreateListData, updateCreateListData, error } = props;
|
||||
const { propertyOptionCreateListData, updateCreateListData, scrollIntoNewOptionView, error } = props;
|
||||
|
||||
return (
|
||||
<div ref={ref} className="w-full pr-2.5">
|
||||
<IssuePropertyOptionItem
|
||||
propertyOptionData={propertyOptionCreateListData}
|
||||
updateOptionData={updateCreateListData}
|
||||
scrollIntoNewOptionView={scrollIntoNewOptionView}
|
||||
error={error}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -15,11 +15,12 @@ type TIssuePropertyOptionItem = {
|
||||
optionId?: string;
|
||||
propertyOptionData: TIssuePropertyOptionCreateUpdateData;
|
||||
updateOptionData: (value: TIssuePropertyOptionCreateUpdateData) => void;
|
||||
scrollIntoNewOptionView: () => void;
|
||||
error?: string;
|
||||
};
|
||||
|
||||
export const IssuePropertyOptionItem: FC<TIssuePropertyOptionItem> = observer((props) => {
|
||||
const { optionId, propertyOptionData, updateOptionData, error: optionsError } = props;
|
||||
const { optionId, propertyOptionData, updateOptionData, scrollIntoNewOptionView, error: optionsError } = props;
|
||||
// store hooks
|
||||
const { propertyOptions } = usePropertyOptions();
|
||||
// derived values
|
||||
@@ -71,7 +72,12 @@ export const IssuePropertyOptionItem: FC<TIssuePropertyOptionItem> = observer((p
|
||||
id={`option-${optionId}-${key}`}
|
||||
value={optionData.name}
|
||||
onChange={(e) => handleOptionDataChange("name", e.target.value)}
|
||||
onKeyDown={(e) => e.key === "Enter" && !!optionData.name && e.currentTarget.blur()}
|
||||
onKeyDown={(e) => {
|
||||
if (["Enter", "Tab"].includes(e.key) && !!optionData.name) {
|
||||
e.currentTarget.blur();
|
||||
scrollIntoNewOptionView();
|
||||
}
|
||||
}}
|
||||
onBlur={() => handleCreateUpdate()}
|
||||
placeholder={"Add option"}
|
||||
className={cn("w-full text-sm bg-custom-background-100 border-[0.5px] rounded", {
|
||||
|
||||
@@ -92,6 +92,11 @@ export const IssuePropertyOptionsRoot: FC<TIssuePropertyOptionsRoot> = observer(
|
||||
updateOptionData={(value) => {
|
||||
handlePropertyOptionsList("update", value);
|
||||
}}
|
||||
scrollIntoNewOptionView={() => {
|
||||
setTimeout(() => {
|
||||
scrollIntoElementView();
|
||||
}, 0);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
@@ -117,6 +122,8 @@ export const IssuePropertyOptionsRoot: FC<TIssuePropertyOptionsRoot> = observer(
|
||||
propertyOptionCreateListData={issuePropertyOption}
|
||||
updateCreateListData={(value) => {
|
||||
handlePropertyOptionsList("update", value);
|
||||
}}
|
||||
scrollIntoNewOptionView={() => {
|
||||
setTimeout(() => {
|
||||
scrollIntoElementView();
|
||||
}, 0);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
// ui
|
||||
import { TextArea } from "@plane/ui";
|
||||
@@ -23,11 +24,23 @@ type TTextAttributesProps = {
|
||||
|
||||
export const TextAttributes = observer((props: TTextAttributesProps) => {
|
||||
const { issueTypeId, textPropertyDetail, currentOperationMode, onTextDetailChange } = props;
|
||||
const [data, setData] = useState<string[]>([]);
|
||||
// store hooks
|
||||
const issueType = useIssueType(issueTypeId);
|
||||
// derived values
|
||||
const isAnyIssueAttached = issueType?.issue_exists;
|
||||
|
||||
useEffect(() => {
|
||||
setData(textPropertyDetail.default_value ?? []);
|
||||
}, [textPropertyDetail.default_value]);
|
||||
|
||||
const handleReadOnlyFieldChange = () => {
|
||||
// trim and filter empty values
|
||||
const trimmedValue = data.map((val) => val.trim()).filter((val) => val);
|
||||
// update readonly data
|
||||
onTextDetailChange("default_value", trimmedValue);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="px-1">
|
||||
@@ -52,8 +65,10 @@ export const TextAttributes = observer((props: TTextAttributesProps) => {
|
||||
<div className="text-xs font-medium text-custom-text-300">Read only data</div>
|
||||
<TextArea
|
||||
id="default_value"
|
||||
value={textPropertyDetail.default_value?.[0]}
|
||||
onChange={(e) => onTextDetailChange("default_value", [e.target.value])}
|
||||
value={data?.[0] ?? ""}
|
||||
onChange={(e) => setData([e.target.value])}
|
||||
onKeyDown={(e) => e.key === "Enter" && !!data[0] && e.currentTarget.blur()}
|
||||
onBlur={() => handleReadOnlyFieldChange()}
|
||||
className="w-full max-h-28 resize-none text-sm bg-custom-background-100 border-[0.5px] border-custom-border-300 rounded"
|
||||
tabIndex={1}
|
||||
textAreaSize="xs"
|
||||
|
||||
@@ -42,6 +42,15 @@ export const NumberValueInput = observer((props: TNumberValueInputProps) => {
|
||||
setData([newValue]);
|
||||
};
|
||||
|
||||
const handleNumberValueChange = () => {
|
||||
// trim and filter empty values
|
||||
const trimmedValue = data.map((val) => val.trim()).filter((val) => val);
|
||||
// update property data
|
||||
if (!isEqual(value, trimmedValue)) {
|
||||
onNumberValueChange(trimmedValue);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Input
|
||||
@@ -63,13 +72,7 @@ export const NumberValueInput = observer((props: TNumberValueInputProps) => {
|
||||
}}
|
||||
onWheel={(e) => e.currentTarget.blur()}
|
||||
onBlur={() => {
|
||||
if (!isEqual(value, data)) {
|
||||
if (data[0].trim() === "") {
|
||||
onNumberValueChange([]);
|
||||
} else {
|
||||
onNumberValueChange(data);
|
||||
}
|
||||
}
|
||||
handleNumberValueChange();
|
||||
document.body?.removeAttribute("data-delay-outside-click");
|
||||
}}
|
||||
placeholder="Add number"
|
||||
|
||||
@@ -62,6 +62,15 @@ export const TextValueInput = observer((props: TTextValueInputProps) => {
|
||||
className
|
||||
);
|
||||
|
||||
const handleTextValueChange = () => {
|
||||
// trim and filter empty values
|
||||
const trimmedValue = data.map((val) => val.trim()).filter((val) => val);
|
||||
// update property data
|
||||
if (!isEqual(value, trimmedValue)) {
|
||||
onTextValueChange(trimmedValue);
|
||||
}
|
||||
};
|
||||
|
||||
switch (display_format) {
|
||||
case "single-line":
|
||||
return (
|
||||
@@ -77,9 +86,7 @@ export const TextValueInput = observer((props: TTextValueInputProps) => {
|
||||
document.body?.setAttribute("data-delay-outside-click", "true");
|
||||
}}
|
||||
onBlur={() => {
|
||||
if (!isEqual(value, data)) {
|
||||
onTextValueChange(data);
|
||||
}
|
||||
handleTextValueChange();
|
||||
document.body?.removeAttribute("data-delay-outside-click");
|
||||
}}
|
||||
placeholder="Add text"
|
||||
@@ -109,9 +116,7 @@ export const TextValueInput = observer((props: TTextValueInputProps) => {
|
||||
document.body?.setAttribute("data-delay-outside-click", "true");
|
||||
}}
|
||||
onBlur={() => {
|
||||
if (!isEqual(value, data)) {
|
||||
onTextValueChange(data);
|
||||
}
|
||||
handleTextValueChange();
|
||||
document.body?.removeAttribute("data-delay-outside-click");
|
||||
}}
|
||||
placeholder="Describe..."
|
||||
|
||||
@@ -21,9 +21,19 @@ type Props = {
|
||||
currentWorkspace: IWorkspace;
|
||||
cta?: React.ReactNode;
|
||||
dateClassname?: string;
|
||||
containerClass?: string;
|
||||
};
|
||||
const Attributes: React.FC<Props> = observer((props) => {
|
||||
const { project, isArchived, handleUpdateProject, workspaceSlug, currentWorkspace, cta, dateClassname } = props;
|
||||
const {
|
||||
project,
|
||||
isArchived,
|
||||
handleUpdateProject,
|
||||
workspaceSlug,
|
||||
currentWorkspace,
|
||||
cta,
|
||||
dateClassname,
|
||||
containerClass = "",
|
||||
} = props;
|
||||
const projectMembersIds = project.members?.map((member) => member.member_id);
|
||||
|
||||
const { getUserDetails } = useMember();
|
||||
@@ -41,7 +51,7 @@ const Attributes: React.FC<Props> = observer((props) => {
|
||||
e.preventDefault();
|
||||
};
|
||||
return (
|
||||
<div className="flex gap-2 flex-wrap p-2 " data-prevent-nprogress>
|
||||
<div className={cn("flex gap-2 flex-wrap p-2", containerClass)} data-prevent-nprogress>
|
||||
<div className="h-5 my-auto" onFocus={handleEventPropagation} onClick={handleEventPropagation}>
|
||||
<StateDropdown
|
||||
value={project.state_id || ""}
|
||||
|
||||
@@ -46,7 +46,7 @@ export const ProjectBlock = observer((props: ProjectBlockProps) => {
|
||||
<div
|
||||
ref={projectRef}
|
||||
className={cn(
|
||||
"group/list-block min-h-11 relative flex flex-col gap-x-3 bg-custom-background-100 hover:bg-custom-background-90 p-3 pl-1.5 text-sm transition-colors border border-transparent border-b border-b-custom-border-200 md:py-1",
|
||||
"group/list-block min-h-[52px] relative flex flex-col gap-2 bg-custom-background-100 hover:bg-custom-background-90 p-3 px-4 py-4 text-sm transition-colors border border-transparent border-b border-b-custom-border-200 md:py-0",
|
||||
{
|
||||
"bg-custom-background-80": isCurrentBlockDragging,
|
||||
"md:flex-row md:items-center": isSidebarCollapsed,
|
||||
@@ -64,10 +64,10 @@ export const ProjectBlock = observer((props: ProjectBlockProps) => {
|
||||
}}
|
||||
>
|
||||
<div className="flex w-full truncate">
|
||||
<div className="flex flex-grow items-center gap-0.5 truncate p-2 pb-0 lg:pb-2">
|
||||
<div className="flex flex-grow items-center gap-0.5 truncate pb-0 md:pt-2 lg:py-2">
|
||||
<div className="flex items-center gap-1">
|
||||
<div className="h-8 w-8 flex-shrink-0 grid place-items-center rounded bg-custom-background-90 mr-2">
|
||||
<Logo logo={projectDetails.logo_props} size={18} />
|
||||
<div className="h-6 w-6 flex-shrink-0 grid place-items-center rounded bg-custom-background-90 mr-2">
|
||||
<Logo logo={projectDetails.logo_props} size={14} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -83,7 +83,7 @@ export const ProjectBlock = observer((props: ProjectBlockProps) => {
|
||||
className={cn("w-full truncate cursor-pointer text-sm text-custom-text-100", {})}
|
||||
>
|
||||
<Tooltip tooltipContent={projectDetails.name} isMobile={isMobile} position="top-left">
|
||||
<p className="truncate">{projectDetails.name}</p>
|
||||
<p className="truncate mr-2">{projectDetails.name}</p>
|
||||
</Tooltip>
|
||||
</Link>
|
||||
) : (
|
||||
@@ -98,7 +98,7 @@ export const ProjectBlock = observer((props: ProjectBlockProps) => {
|
||||
)}
|
||||
</div>
|
||||
<div
|
||||
className={cn("block border border-custom-border-300 rounded h-full m-auto", {
|
||||
className={cn("block border border-custom-border-300 rounded h-full m-2", {
|
||||
"md:hidden": isSidebarCollapsed,
|
||||
"lg:hidden": !isSidebarCollapsed,
|
||||
})}
|
||||
@@ -115,6 +115,7 @@ export const ProjectBlock = observer((props: ProjectBlockProps) => {
|
||||
workspaceSlug={workspaceSlug.toString()}
|
||||
currentWorkspace={currentWorkspace}
|
||||
cta={filters?.scope === EProjectScope.ALL_PROJECTS && <JoinButton project={projectDetails as TProject} />}
|
||||
containerClass="px-0 py-0 md:pb-4 lg:py-2"
|
||||
/>
|
||||
<div
|
||||
className={cn("hidden", {
|
||||
|
||||
@@ -70,7 +70,7 @@ export const ListGroup = observer((props: Props) => {
|
||||
|
||||
return groupedProjectIds.length > 0 ? (
|
||||
<div ref={groupRef} className={cn(`relative flex flex-shrink-0 flex-col border-[1px] border-transparent`)}>
|
||||
<div className="sticky top-0 z-[2] w-full flex-shrink-0 border-b border-custom-border-200 bg-custom-background-90 pl-2 pr-3 py-1">
|
||||
<div className="sticky top-0 z-[2] w-full flex-shrink-0 border-b border-custom-border-200 bg-custom-background-90 pl-4 pr-3 py-1">
|
||||
<HeaderGroupByCard
|
||||
groupID={group}
|
||||
icon={details?.icon}
|
||||
|
||||
@@ -40,17 +40,19 @@ export interface IProjectFilterStore extends IProjectFilterHelper {
|
||||
// helpers actions
|
||||
// actions
|
||||
initWorkspaceFilters: (workspaceSlug: string, scope?: EProjectScope) => void;
|
||||
updateScope: (workspaceSlug: string, scope: TProjectScope) => void;
|
||||
updateLayout: (workspaceSlug: string, layout: TProjectLayouts) => void;
|
||||
updateScope: (workspaceSlug: string, scope: TProjectScope, setLocalStorage?: boolean) => void;
|
||||
updateLayout: (workspaceSlug: string, layout: TProjectLayouts, setLocalStorage?: boolean) => void;
|
||||
updateAttributes: <T extends keyof TProjectAttributes>(
|
||||
workspaceSlug: string,
|
||||
key: T,
|
||||
values: TProjectAttributes[T]
|
||||
values: TProjectAttributes[T],
|
||||
setLocalStorage?: boolean
|
||||
) => void;
|
||||
updateDisplayFilters: <T extends keyof TProjectDisplayFilters>(
|
||||
workspaceSlug: string,
|
||||
key: T,
|
||||
values: TProjectDisplayFilters[T]
|
||||
values: TProjectDisplayFilters[T],
|
||||
setLocalStorage?: boolean
|
||||
) => void;
|
||||
bulkUpdateDisplayFilters: (workspaceSlug: string, values: Partial<TProjectDisplayFilters>) => void;
|
||||
updateSearchQuery: (query: string | undefined) => void;
|
||||
@@ -215,22 +217,19 @@ export class ProjectFilterStore extends ProjectFilterHelper implements IProjectF
|
||||
);
|
||||
|
||||
if (!this.layoutMap[workspaceSlug]) {
|
||||
this.updateLayout(workspaceSlug, savedFilters.layout || EProjectLayouts.GALLERY);
|
||||
}
|
||||
if (!this.layoutMap[workspaceSlug]) {
|
||||
this.updateLayout(workspaceSlug, savedFilters.layout || EProjectLayouts.GALLERY);
|
||||
this.updateLayout(workspaceSlug, savedFilters?.layout || EProjectLayouts.GALLERY, false);
|
||||
}
|
||||
if (!this.attributesMap[workspaceSlug]) {
|
||||
this.updateAttributes(workspaceSlug, "priority", []);
|
||||
this.updateAttributes(workspaceSlug, "state", []);
|
||||
this.updateAttributes(workspaceSlug, "lead", []);
|
||||
this.updateAttributes(workspaceSlug, "members", []);
|
||||
this.updateAttributes(workspaceSlug, "access", []);
|
||||
this.updateAttributes(workspaceSlug, "priority", savedFilters?.attributes?.priority || [], false);
|
||||
this.updateAttributes(workspaceSlug, "state", savedFilters?.attributes?.state || [], false);
|
||||
this.updateAttributes(workspaceSlug, "lead", savedFilters?.attributes?.lead || [], false);
|
||||
this.updateAttributes(workspaceSlug, "members", savedFilters?.attributes?.members || [], false);
|
||||
this.updateAttributes(workspaceSlug, "access", savedFilters?.attributes?.access || [], false);
|
||||
}
|
||||
if (!this.displayFiltersMap[workspaceSlug]) {
|
||||
this.updateDisplayFilters(workspaceSlug, "group_by", "states");
|
||||
this.updateDisplayFilters(workspaceSlug, "sort_by", "manual");
|
||||
this.updateDisplayFilters(workspaceSlug, "sort_order", "asc");
|
||||
this.updateDisplayFilters(workspaceSlug, "group_by", savedFilters?.display_filters?.group_by || "states", false);
|
||||
this.updateDisplayFilters(workspaceSlug, "sort_by", savedFilters?.display_filters?.sort_by || "manual", false);
|
||||
this.updateDisplayFilters(workspaceSlug, "sort_order", savedFilters?.display_filters?.sort_order || "asc", false);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -240,9 +239,9 @@ export class ProjectFilterStore extends ProjectFilterHelper implements IProjectF
|
||||
* @param { TProjectScope } scope
|
||||
* @returns { void }
|
||||
*/
|
||||
updateScope = (workspaceSlug: string, scope: TProjectScope): void => {
|
||||
updateScope = (workspaceSlug: string, scope: TProjectScope, setLocalStorage = true): void => {
|
||||
set(this.scopeMap, workspaceSlug, scope);
|
||||
this.handleProjectLocalFilters.set("scope", workspaceSlug, { scope });
|
||||
setLocalStorage && this.handleProjectLocalFilters.set("scope", workspaceSlug, { scope });
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -251,9 +250,9 @@ export class ProjectFilterStore extends ProjectFilterHelper implements IProjectF
|
||||
* @param { TProjectLayouts } layout
|
||||
* @returns { void }
|
||||
*/
|
||||
updateLayout = (workspaceSlug: string, layout: TProjectLayouts): void => {
|
||||
updateLayout = (workspaceSlug: string, layout: TProjectLayouts, setLocalStorage = true): void => {
|
||||
set(this.layoutMap, workspaceSlug, layout);
|
||||
this.handleProjectLocalFilters.set("layout", workspaceSlug, { layout });
|
||||
setLocalStorage && this.handleProjectLocalFilters.set("layout", workspaceSlug, { layout });
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -266,9 +265,14 @@ export class ProjectFilterStore extends ProjectFilterHelper implements IProjectF
|
||||
updateAttributes = <T extends keyof TProjectAttributes>(
|
||||
workspaceSlug: string,
|
||||
key: T,
|
||||
values: TProjectAttributes[T]
|
||||
values: TProjectAttributes[T],
|
||||
setLocalStorage = true
|
||||
): void => {
|
||||
set(this.attributesMap, [workspaceSlug, key], values);
|
||||
setLocalStorage &&
|
||||
this.handleProjectLocalFilters.set("attributes", workspaceSlug, {
|
||||
attributes: { ...this.attributesMap[workspaceSlug], [key]: values },
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -281,9 +285,14 @@ export class ProjectFilterStore extends ProjectFilterHelper implements IProjectF
|
||||
updateDisplayFilters = <T extends keyof TProjectDisplayFilters>(
|
||||
workspaceSlug: string,
|
||||
key: T,
|
||||
values: TProjectDisplayFilters[T]
|
||||
values: TProjectDisplayFilters[T],
|
||||
setLocalStorage = true
|
||||
): void => {
|
||||
set(this.displayFiltersMap, [workspaceSlug, key], values);
|
||||
setLocalStorage &&
|
||||
this.handleProjectLocalFilters.set("display_filters", workspaceSlug, {
|
||||
display_filters: { ...this.displayFiltersMap[workspaceSlug], [key]: values },
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user