From 8cf5b65c3f20c5bb72b820cfb044f5aed0d56e18 Mon Sep 17 00:00:00 2001 From: vamsikrishnamathala Date: Tue, 7 Oct 2025 17:16:21 +0530 Subject: [PATCH 1/3] feat: added activity filters for state and assignee --- .../issue/issue-details/activity.store.ts | 43 +++++++++++++++---- .../issue-activity/activity-comment-root.tsx | 11 ++++- packages/constants/src/issue/filter.ts | 26 +++++++++-- packages/types/src/issues/activity/base.ts | 15 +++++++ .../ui/src/popovers/popover-menu.stories.tsx | 2 +- 5 files changed, 82 insertions(+), 15 deletions(-) diff --git a/apps/web/ce/store/issue/issue-details/activity.store.ts b/apps/web/ce/store/issue/issue-details/activity.store.ts index 95a9ed792fb..0d3e6f62408 100644 --- a/apps/web/ce/store/issue/issue-details/activity.store.ts +++ b/apps/web/ce/store/issue/issue-details/activity.store.ts @@ -11,7 +11,6 @@ import { TIssueActivityIdMap, TIssueServiceType, } from "@plane/types"; -// plane web constants // services import { IssueActivityService } from "@/services/issue"; // store @@ -34,10 +33,12 @@ export interface IIssueActivityStore extends IIssueActivityStoreActions { loader: TActivityLoader; activities: TIssueActivityIdMap; activityMap: TIssueActivityMap; + sortOrder: E_SORT_ORDER; // helper methods getActivitiesByIssueId: (issueId: string) => string[] | undefined; getActivityById: (activityId: string) => TIssueActivity | undefined; getActivityAndCommentsByIssueId: (issueId: string, sortOrder: E_SORT_ORDER) => TIssueActivityComment[] | undefined; + toggleSortOrder: () => void; } export class IssueActivityStore implements IIssueActivityStore { @@ -45,7 +46,7 @@ export class IssueActivityStore implements IIssueActivityStore { loader: TActivityLoader = "fetch"; activities: TIssueActivityIdMap = {}; activityMap: TIssueActivityMap = {}; - + sortOrder: E_SORT_ORDER = E_SORT_ORDER.ASC; // services serviceType; issueActivityService; @@ -67,6 +68,14 @@ export class IssueActivityStore implements IIssueActivityStore { this.issueActivityService = new IssueActivityService(this.serviceType); } + toggleSortOrder = () => { + if (this.sortOrder === E_SORT_ORDER.ASC) { + this.sortOrder = E_SORT_ORDER.DESC; + } else { + this.sortOrder = E_SORT_ORDER.ASC; + } + }; + // helper methods getActivitiesByIssueId = (issueId: string) => { if (!issueId) return undefined; @@ -78,10 +87,10 @@ export class IssueActivityStore implements IIssueActivityStore { return this.activityMap[activityId] ?? undefined; }; - getActivityAndCommentsByIssueId = computedFn((issueId: string, sortOrder: E_SORT_ORDER) => { + protected buildActivityAndCommentItems(issueId: string): TIssueActivityComment[] | undefined { if (!issueId) return undefined; - let activityComments: TIssueActivityComment[] = []; + const activityComments: TIssueActivityComment[] = []; const currentStore = this.serviceType === EIssueServiceType.EPICS ? this.store.issue.epicDetail : this.store.issue.issueDetail; @@ -91,17 +100,25 @@ export class IssueActivityStore implements IIssueActivityStore { if (!activities || !comments) return undefined; - activities?.forEach((activityId) => { + activities.forEach((activityId) => { const activity = this.getActivityById(activityId); if (!activity) return; + const type = + activity.field === "state" + ? EActivityFilterType.STATE + : activity.field === "assignees" + ? EActivityFilterType.ASSIGNEE + : activity.field === null + ? EActivityFilterType.DEFAULT + : EActivityFilterType.ACTIVITY; activityComments.push({ id: activity.id, - activity_type: EActivityFilterType.ACTIVITY, + activity_type: type, created_at: activity.created_at, }); }); - comments?.forEach((commentId) => { + comments.forEach((commentId) => { const comment = currentStore.comment.getCommentById(commentId); if (!comment) return; activityComments.push({ @@ -111,9 +128,17 @@ export class IssueActivityStore implements IIssueActivityStore { }); }); - activityComments = orderBy(activityComments, (e) => new Date(e.created_at || 0), sortOrder); - return activityComments; + } + + protected sortActivityComments(items: TIssueActivityComment[], sortOrder: E_SORT_ORDER): TIssueActivityComment[] { + return orderBy(items, (e) => new Date(e.created_at || 0), sortOrder); + } + + getActivityAndCommentsByIssueId = computedFn((issueId: string, sortOrder: E_SORT_ORDER) => { + const baseItems = this.buildActivityAndCommentItems(issueId); + if (!baseItems) return undefined; + return this.sortActivityComments(baseItems, sortOrder); }); // actions diff --git a/apps/web/core/components/issues/issue-detail/issue-activity/activity-comment-root.tsx b/apps/web/core/components/issues/issue-detail/issue-activity/activity-comment-root.tsx index 8dd4e681bd0..9defb1b715c 100644 --- a/apps/web/core/components/issues/issue-detail/issue-activity/activity-comment-root.tsx +++ b/apps/web/core/components/issues/issue-detail/issue-activity/activity-comment-root.tsx @@ -1,7 +1,7 @@ import { FC } from "react"; import { observer } from "mobx-react"; // plane imports -import { E_SORT_ORDER, TActivityFilters, filterActivityOnSelectedFilters } from "@plane/constants"; +import { EActivityFilterType, E_SORT_ORDER, TActivityFilters, filterActivityOnSelectedFilters } from "@plane/constants"; import { TCommentsOperations } from "@plane/types"; // components import { CommentCard } from "@/components/comments/card/root"; @@ -52,6 +52,13 @@ export const IssueActivityCommentRoot: FC = observer( const filteredActivityAndComments = filterActivityOnSelectedFilters(activityAndComments, selectedFilters); + const BASE_ACTIVITY_FILTER_TYPES = [ + EActivityFilterType.ACTIVITY, + EActivityFilterType.STATE, + EActivityFilterType.ASSIGNEE, + EActivityFilterType.DEFAULT, + ]; + return (
{filteredActivityAndComments.map((activityComment, index) => { @@ -68,7 +75,7 @@ export const IssueActivityCommentRoot: FC = observer( disabled={disabled} projectId={projectId} /> - ) : activityComment.activity_type === "ACTIVITY" ? ( + ) : BASE_ACTIVITY_FILTER_TYPES.includes(activityComment.activity_type as EActivityFilterType) ? ( = { +export type TActivityFilterOptionsKey = Exclude; + +export const ACTIVITY_FILTER_TYPE_OPTIONS: Record = { [EActivityFilterType.ACTIVITY]: { labelTranslationKey: "common.updates", }, [EActivityFilterType.COMMENT]: { labelTranslationKey: "common.comments", }, + [EActivityFilterType.STATE]: { + labelTranslationKey: "common.state", + }, + [EActivityFilterType.ASSIGNEE]: { + labelTranslationKey: "common.assignee", + }, }; export type TActivityFilterOption = { @@ -328,12 +339,21 @@ export type TActivityFilterOption = { onClick: () => void; }; -export const defaultActivityFilters: TActivityFilters[] = [EActivityFilterType.ACTIVITY, EActivityFilterType.COMMENT]; +export const defaultActivityFilters: TActivityFilters[] = [ + EActivityFilterType.ACTIVITY, + EActivityFilterType.COMMENT, + EActivityFilterType.STATE, + EActivityFilterType.ASSIGNEE, + EActivityFilterType.DEFAULT, +]; export const filterActivityOnSelectedFilters = ( activity: TIssueActivityComment[], filters: TActivityFilters[] ): TIssueActivityComment[] => - activity.filter((activity) => filters.includes(activity.activity_type as TActivityFilters)); + activity.filter((activity) => { + if (activity.activity_type === EActivityFilterType.DEFAULT) return true; + return filters.includes(activity.activity_type as TActivityFilters); + }); export const ENABLE_ISSUE_DEPENDENCIES = false; diff --git a/packages/types/src/issues/activity/base.ts b/packages/types/src/issues/activity/base.ts index 7b649334958..e239ef9f012 100644 --- a/packages/types/src/issues/activity/base.ts +++ b/packages/types/src/issues/activity/base.ts @@ -56,6 +56,21 @@ export type TIssueActivityComment = activity_type: "ACTIVITY"; created_at?: string; } + | { + id: string; + activity_type: "STATE"; + created_at?: string; + } + | { + id: string; + activity_type: "ASSIGNEE"; + created_at?: string; + } + | { + id: string; + activity_type: "DEFAULT"; + created_at?: string; + } | { id: string; activity_type: "WORKLOG"; diff --git a/packages/ui/src/popovers/popover-menu.stories.tsx b/packages/ui/src/popovers/popover-menu.stories.tsx index b868c9d01e9..4e8a56cda6b 100644 --- a/packages/ui/src/popovers/popover-menu.stories.tsx +++ b/packages/ui/src/popovers/popover-menu.stories.tsx @@ -1,5 +1,5 @@ -import React from "react"; import type { Meta, StoryObj } from "@storybook/react"; +import React from "react"; import { PopoverMenu } from "./popover-menu"; type TPopoverMenu = { From 429548bd657b67414af036edb1a2fa00045f05c1 Mon Sep 17 00:00:00 2001 From: vamsikrishnamathala Date: Tue, 7 Oct 2025 17:24:34 +0530 Subject: [PATCH 2/3] chore: removed unused funtion --- .../ce/store/issue/issue-details/activity.store.ts | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/apps/web/ce/store/issue/issue-details/activity.store.ts b/apps/web/ce/store/issue/issue-details/activity.store.ts index 0d3e6f62408..b96c26fbd4c 100644 --- a/apps/web/ce/store/issue/issue-details/activity.store.ts +++ b/apps/web/ce/store/issue/issue-details/activity.store.ts @@ -33,12 +33,10 @@ export interface IIssueActivityStore extends IIssueActivityStoreActions { loader: TActivityLoader; activities: TIssueActivityIdMap; activityMap: TIssueActivityMap; - sortOrder: E_SORT_ORDER; // helper methods getActivitiesByIssueId: (issueId: string) => string[] | undefined; getActivityById: (activityId: string) => TIssueActivity | undefined; getActivityAndCommentsByIssueId: (issueId: string, sortOrder: E_SORT_ORDER) => TIssueActivityComment[] | undefined; - toggleSortOrder: () => void; } export class IssueActivityStore implements IIssueActivityStore { @@ -46,7 +44,6 @@ export class IssueActivityStore implements IIssueActivityStore { loader: TActivityLoader = "fetch"; activities: TIssueActivityIdMap = {}; activityMap: TIssueActivityMap = {}; - sortOrder: E_SORT_ORDER = E_SORT_ORDER.ASC; // services serviceType; issueActivityService; @@ -68,14 +65,6 @@ export class IssueActivityStore implements IIssueActivityStore { this.issueActivityService = new IssueActivityService(this.serviceType); } - toggleSortOrder = () => { - if (this.sortOrder === E_SORT_ORDER.ASC) { - this.sortOrder = E_SORT_ORDER.DESC; - } else { - this.sortOrder = E_SORT_ORDER.ASC; - } - }; - // helper methods getActivitiesByIssueId = (issueId: string) => { if (!issueId) return undefined; From 37a84893879e2c507b948c43877b17d60669f238 Mon Sep 17 00:00:00 2001 From: vamsikrishnamathala Date: Wed, 15 Oct 2025 15:31:54 +0530 Subject: [PATCH 3/3] chore: lint fix --- packages/propel/src/toast/toast.stories.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/propel/src/toast/toast.stories.tsx b/packages/propel/src/toast/toast.stories.tsx index 607100497ce..475cde44d78 100644 --- a/packages/propel/src/toast/toast.stories.tsx +++ b/packages/propel/src/toast/toast.stories.tsx @@ -1,6 +1,6 @@ +import { useState } from "react"; import type { Meta, StoryObj } from "@storybook/react-vite"; import { Toast, setToast, updateToast, setPromiseToast, TOAST_TYPE } from "./toast"; -import { useState } from "react"; const meta = { title: "Components/Toast",