);
});
diff --git a/web/components/issues/issue-layouts/filters/applied-filters/roots/module-root.tsx b/web/components/issues/issue-layouts/filters/applied-filters/roots/module-root.tsx
index 0f85b837f..bbd554d41 100644
--- a/web/components/issues/issue-layouts/filters/applied-filters/roots/module-root.tsx
+++ b/web/components/issues/issue-layouts/filters/applied-filters/roots/module-root.tsx
@@ -4,7 +4,7 @@ import { observer } from "mobx-react-lite";
// mobx store
import { useMobxStore } from "lib/mobx/store-provider";
// components
-import { AppliedFiltersList } from "components/issues";
+import { AppliedFiltersList, SaveFilterView } from "components/issues";
// types
import { IIssueFilterOptions } from "types";
import { EFilterType } from "store/issues/types";
@@ -76,7 +76,7 @@ export const ModuleAppliedFiltersRoot: React.FC = observer(() => {
if (Object.keys(appliedFilters).length === 0) return null;
return (
-
+
{
members={projectMembers?.map((m) => m.member)}
states={projectStateStore.states?.[moduleId ?? ""]}
/>
+
+
);
});
diff --git a/web/components/issues/issue-layouts/filters/applied-filters/roots/project-root.tsx b/web/components/issues/issue-layouts/filters/applied-filters/roots/project-root.tsx
index ac71259f2..90e49d54d 100644
--- a/web/components/issues/issue-layouts/filters/applied-filters/roots/project-root.tsx
+++ b/web/components/issues/issue-layouts/filters/applied-filters/roots/project-root.tsx
@@ -4,14 +4,18 @@ import { observer } from "mobx-react-lite";
// mobx store
import { useMobxStore } from "lib/mobx/store-provider";
// components
-import { AppliedFiltersList } from "components/issues";
+import { AppliedFiltersList, SaveFilterView } from "components/issues";
+
// types
import { IIssueFilterOptions } from "types";
import { EFilterType } from "store/issues/types";
export const ProjectAppliedFiltersRoot: React.FC = observer(() => {
const router = useRouter();
- const { workspaceSlug, projectId } = router.query;
+ const { workspaceSlug, projectId } = router.query as {
+ workspaceSlug: string;
+ projectId: string;
+ };
const {
projectLabel: { projectLabels },
@@ -60,7 +64,7 @@ export const ProjectAppliedFiltersRoot: React.FC = observer(() => {
if (Object.keys(appliedFilters).length === 0) return null;
return (
-
+
{
members={projectMembers?.map((m) => m.member)}
states={projectStateStore.states?.[projectId?.toString() ?? ""]}
/>
+
+
);
});
diff --git a/web/components/issues/issue-layouts/filters/applied-filters/roots/project-view-root.tsx b/web/components/issues/issue-layouts/filters/applied-filters/roots/project-view-root.tsx
index 873a1aea5..5e4a53b87 100644
--- a/web/components/issues/issue-layouts/filters/applied-filters/roots/project-view-root.tsx
+++ b/web/components/issues/issue-layouts/filters/applied-filters/roots/project-view-root.tsx
@@ -31,7 +31,6 @@ export const ProjectViewAppliedFiltersRoot: React.FC = observer(() => {
} = useMobxStore();
const viewDetails = viewId ? projectViewsStore.viewDetails[viewId.toString()] : undefined;
- const storedFilters = viewId ? projectViewFiltersStore.storedFilters[viewId.toString()] : undefined;
const userFilters = issueFilters?.filters;
// filters whose value not null or empty array
@@ -89,7 +88,7 @@ export const ProjectViewAppliedFiltersRoot: React.FC = observer(() => {
projectViewsStore.updateView(workspaceSlug.toString(), projectId.toString(), viewId.toString(), {
query_data: {
...viewDetails.query_data,
- ...(storedFilters ?? {}),
+ ...(appliedFilters ?? {}),
},
});
};
@@ -104,13 +103,16 @@ export const ProjectViewAppliedFiltersRoot: React.FC = observer(() => {
members={projectMembers?.map((m) => m.member)}
states={projectStateStore.states?.[projectId?.toString() ?? ""]}
/>
- {storedFilters && viewDetails && areFiltersDifferent(storedFilters, viewDetails.query_data ?? {}) && (
-
-
-
- )}
+
+ {appliedFilters &&
+ viewDetails?.query_data &&
+ areFiltersDifferent(appliedFilters, viewDetails?.query_data ?? {}) && (
+
+
+
+ )}
);
});
diff --git a/web/components/issues/issue-layouts/index.ts b/web/components/issues/issue-layouts/index.ts
index 3a8a2baac..fc294b084 100644
--- a/web/components/issues/issue-layouts/index.ts
+++ b/web/components/issues/issue-layouts/index.ts
@@ -15,3 +15,6 @@ export * from "./spreadsheet";
// properties
export * from "./properties";
+
+// save view
+export * from "./save-filter-view";
diff --git a/web/components/issues/issue-layouts/kanban/block.tsx b/web/components/issues/issue-layouts/kanban/block.tsx
index f03101b12..e86235b36 100644
--- a/web/components/issues/issue-layouts/kanban/block.tsx
+++ b/web/components/issues/issue-layouts/kanban/block.tsx
@@ -1,16 +1,14 @@
-import { memo, useRef, useState } from "react";
+import { memo } from "react";
import { Draggable } from "@hello-pangea/dnd";
+import isEqual from "lodash/isEqual";
// components
import { KanBanProperties } from "./properties";
// ui
import { Tooltip } from "@plane/ui";
-// hooks
-import useOutsideClickDetector from "hooks/use-outside-click-detector";
// types
import { IIssueDisplayProperties, IIssue } from "types";
import { EIssueActions } from "../types";
import { useRouter } from "next/router";
-import { MoreHorizontal } from "lucide-react";
interface IssueBlockProps {
sub_group_id: string;
@@ -20,37 +18,28 @@ interface IssueBlockProps {
isDragDisabled: boolean;
showEmptyGroup: boolean;
handleIssues: (sub_group_by: string | null, group_by: string | null, issue: IIssue, action: EIssueActions) => void;
- quickActions: (
- sub_group_by: string | null,
- group_by: string | null,
- issue: IIssue,
- customActionButton?: React.ReactElement
- ) => React.ReactNode;
+ quickActions: (sub_group_by: string | null, group_by: string | null, issue: IIssue) => React.ReactNode;
displayProperties: IIssueDisplayProperties | null;
isReadOnly: boolean;
}
-export const KanBanIssueMemoBlock: React.FC
= (props) => {
- const {
- sub_group_id,
- columnId,
- index,
- issue,
- isDragDisabled,
- showEmptyGroup,
- handleIssues,
- quickActions,
- displayProperties,
- isReadOnly,
- } = props;
- // router
+interface IssueDetailsBlockProps {
+ sub_group_id: string;
+ columnId: string;
+ issue: IIssue;
+ showEmptyGroup: boolean;
+ handleIssues: (sub_group_by: string | null, group_by: string | null, issue: IIssue, action: EIssueActions) => void;
+ quickActions: (sub_group_by: string | null, group_by: string | null, issue: IIssue) => React.ReactNode;
+ displayProperties: IIssueDisplayProperties | null;
+ isReadOnly: boolean;
+}
+
+const KanbanIssueDetailsBlock: React.FC = (props) => {
+ const { sub_group_id, columnId, issue, showEmptyGroup, handleIssues, quickActions, displayProperties, isReadOnly } =
+ props;
+
const router = useRouter();
- // states
- const [isMenuActive, setIsMenuActive] = useState(false);
-
- const menuActionRef = useRef(null);
-
const updateIssue = (sub_group_by: string | null, group_by: string | null, issueToUpdate: IIssue) => {
if (issueToUpdate) handleIssues(sub_group_by, group_by, issueToUpdate, EIssueActions.UPDATE);
};
@@ -64,24 +53,70 @@ export const KanBanIssueMemoBlock: React.FC = (props) => {
});
};
+ return (
+ <>
+ {displayProperties && displayProperties?.key && (
+
+
+ {issue.project_detail.identifier}-{issue.sequence_id}
+
+
+ {quickActions(
+ !sub_group_id && sub_group_id === "null" ? null : sub_group_id,
+ !columnId && columnId === "null" ? null : columnId,
+ issue
+ )}
+
+
+ )}
+
+
+ {issue.name}
+
+
+
+
+
+ >
+ );
+};
+
+const validateMemo = (prevProps: IssueDetailsBlockProps, nextProps: IssueDetailsBlockProps) => {
+ if (prevProps.issue !== nextProps.issue) return false;
+ if (!isEqual(prevProps.displayProperties, nextProps.displayProperties)) {
+ return false;
+ }
+ return true;
+};
+
+const KanbanIssueMemoBlock = memo(KanbanIssueDetailsBlock, validateMemo);
+
+export const KanbanIssueBlock: React.FC = (props) => {
+ const {
+ sub_group_id,
+ columnId,
+ index,
+ issue,
+ isDragDisabled,
+ showEmptyGroup,
+ handleIssues,
+ quickActions,
+ displayProperties,
+ isReadOnly,
+ } = props;
+
let draggableId = issue.id;
if (columnId) draggableId = `${draggableId}__${columnId}`;
if (sub_group_id) draggableId = `${draggableId}__${sub_group_id}`;
- useOutsideClickDetector(menuActionRef, () => setIsMenuActive(false));
-
- const customActionButton = (
- setIsMenuActive(!isMenuActive)}
- >
-
-
- );
-
return (
<>
@@ -100,44 +135,16 @@ export const KanBanIssueMemoBlock: React.FC = (props) => {
isDragDisabled ? "" : "hover:cursor-grab"
} ${snapshot.isDragging ? `border-custom-primary-100` : `border-transparent`}`}
>
- {displayProperties && displayProperties?.key && (
-
-
- {issue.project_detail.identifier}-{issue.sequence_id}
-
-
- {quickActions(
- !sub_group_id && sub_group_id === "null" ? null : sub_group_id,
- !columnId && columnId === "null" ? null : columnId,
- issue,
- customActionButton
- )}
-
-
- )}
-
-
- {issue.name}
-
-
-
-
-
+