- {issueCount && issueCount > 0 ? (
+ {issuesCount && issuesCount > 0 ? (
) : null}
diff --git a/web/components/headers/module-issues.tsx b/web/components/headers/module-issues.tsx
index 9a911103d..538eca2cd 100644
--- a/web/components/headers/module-issues.tsx
+++ b/web/components/headers/module-issues.tsx
@@ -71,6 +71,7 @@ export const ModuleIssuesHeader: React.FC = observer(() => {
// store hooks
const {
issuesFilter: { issueFilters },
+ issues: { issuesCount },
} = useIssues(EIssuesStoreType.MODULE);
const { updateFilters } = useIssuesActions(EIssuesStoreType.MODULE);
const { projectModuleIds, getModuleById } = useModule();
@@ -145,12 +146,6 @@ export const ModuleIssuesHeader: React.FC = observer(() => {
const canUserCreateIssue =
currentProjectRole && [EUserProjectRoles.ADMIN, EUserProjectRoles.MEMBER].includes(currentProjectRole);
- const issueCount = moduleDetails
- ? !issueFilters?.displayFilters?.sub_issue && moduleDetails.sub_issues
- ? moduleDetails.total_issues - moduleDetails.sub_issues
- : moduleDetails.total_issues
- : undefined;
-
const isFiltersApplied = calculateTotalFilters(issueFilters?.filters ?? {}) !== 0;
return (
@@ -209,16 +204,16 @@ export const ModuleIssuesHeader: React.FC = observer(() => {
{moduleDetails?.name && moduleDetails.name}
- {issueCount && issueCount > 0 ? (
+ {issuesCount && issuesCount > 0 ? (
1 ? "issues" : "issue"
+ tooltipContent={`There are ${issuesCount} ${
+ issuesCount > 1 ? "issues" : "issue"
} in this module`}
position="bottom"
>
- {issueCount}
+ {issuesCount}
) : null}
diff --git a/web/components/headers/project-issues.tsx b/web/components/headers/project-issues.tsx
index 95983d85a..8ba44719e 100644
--- a/web/components/headers/project-issues.tsx
+++ b/web/components/headers/project-issues.tsx
@@ -43,6 +43,7 @@ export const ProjectIssuesHeader: React.FC = observer(() => {
} = useMember();
const {
issuesFilter: { issueFilters, updateFilters },
+ issues: { issuesCount },
} = useIssues(EIssuesStoreType.PROJECT);
const { toggleCreateIssueModal } = useCommandPalette();
const { setTrackElement } = useEventTracker();
@@ -105,12 +106,6 @@ export const ProjectIssuesHeader: React.FC = observer(() => {
const canUserCreateIssue =
currentProjectRole && [EUserProjectRoles.ADMIN, EUserProjectRoles.MEMBER].includes(currentProjectRole);
- const issueCount = currentProjectDetails
- ? !issueFilters?.displayFilters?.sub_issue && currentProjectDetails?.sub_issues
- ? currentProjectDetails?.total_issues - currentProjectDetails?.sub_issues
- : currentProjectDetails?.total_issues
- : undefined;
-
const isFiltersApplied = calculateTotalFilters(issueFilters?.filters ?? {}) !== 0;
return (
@@ -153,14 +148,14 @@ export const ProjectIssuesHeader: React.FC = observer(() => {
link={
} />}
/>
- {issueCount && issueCount > 0 ? (
+ {issuesCount && issuesCount > 0 ? (
1 ? "issues" : "issue"} in this project`}
+ tooltipContent={`There are ${issuesCount} ${issuesCount > 1 ? "issues" : "issue"} in this project`}
position="bottom"
>
- {issueCount}
+ {issuesCount}
) : null}
diff --git a/web/helpers/issue.helper.ts b/web/helpers/issue.helper.ts
index 614994767..d01f996c7 100644
--- a/web/helpers/issue.helper.ts
+++ b/web/helpers/issue.helper.ts
@@ -2,12 +2,15 @@ import differenceInCalendarDays from "date-fns/differenceInCalendarDays";
import { v4 as uuidv4 } from "uuid";
// types
import {
+ TGroupedIssues,
TIssue,
TIssueGroupByOptions,
TIssueLayouts,
TIssueOrderByOptions,
TIssueParams,
TStateGroups,
+ TSubGroupedIssues,
+ TUnGroupedIssues,
} from "@plane/types";
import { IGanttBlock } from "@/components/gantt-chart";
// constants
@@ -211,3 +214,43 @@ export const getDescriptionPlaceholder = (isFocused: boolean, description: strin
if (!isDescriptionEmpty || isFocused) return "Press '/' for commands...";
else return "Click to add description";
};
+
+export const issueCountBasedOnFilters = (
+ issueIds: TUnGroupedIssues | TGroupedIssues | TSubGroupedIssues,
+ layout: TIssueLayouts,
+ groupBy: string | undefined,
+ subGroupBy: string | undefined
+): number => {
+ let issuesCount = 0;
+ if (!layout) return issuesCount;
+
+ if (["spreadsheet", "gantt_chart"].includes(layout)) {
+ issuesCount = (issueIds as TUnGroupedIssues)?.length;
+ } else if (layout === "calendar") {
+ Object.keys(issueIds || {}).map((groupId) => {
+ issuesCount += (issueIds as TGroupedIssues)?.[groupId]?.length;
+ });
+ } else if (layout === "list") {
+ if (groupBy) {
+ Object.keys(issueIds || {}).map((groupId) => {
+ issuesCount += (issueIds as TGroupedIssues)?.[groupId]?.length;
+ });
+ } else {
+ issuesCount = (issueIds as TUnGroupedIssues)?.length;
+ }
+ } else if (layout === "kanban") {
+ if (groupBy && subGroupBy) {
+ Object.keys(issueIds || {}).map((groupId) => {
+ Object.keys((issueIds as TSubGroupedIssues)?.[groupId] || {}).map((subGroupId) => {
+ issuesCount += (issueIds as TSubGroupedIssues)?.[groupId]?.[subGroupId]?.length || 0;
+ });
+ });
+ } else if (groupBy) {
+ Object.keys(issueIds || {}).map((groupId) => {
+ issuesCount += (issueIds as TGroupedIssues)?.[groupId]?.length;
+ });
+ }
+ }
+
+ return issuesCount;
+};
diff --git a/web/store/issue/cycle/issue.store.ts b/web/store/issue/cycle/issue.store.ts
index 7a264dad0..3632d03f7 100644
--- a/web/store/issue/cycle/issue.store.ts
+++ b/web/store/issue/cycle/issue.store.ts
@@ -6,6 +6,8 @@ import update from "lodash/update";
import { action, observable, makeObservable, computed, runInAction } from "mobx";
// types
import { TIssue, TSubGroupedIssues, TGroupedIssues, TLoader, TUnGroupedIssues, ViewFlags } from "@plane/types";
+// helpers
+import { issueCountBasedOnFilters } from "@/helpers/issue.helper";
// services
import { CycleService } from "@/services/cycle.service";
import { IssueService } from "@/services/issue";
@@ -21,6 +23,7 @@ export interface ICycleIssues {
issues: { [cycle_id: string]: string[] };
viewFlags: ViewFlags;
// computed
+ issuesCount: number;
groupedIssueIds: TGroupedIssues | TSubGroupedIssues | TUnGroupedIssues | undefined;
// actions
getIssueIds: (groupId?: string, subGroupId?: string) => string[] | undefined;
@@ -60,7 +63,7 @@ export interface ICycleIssues {
) => Promise
;
removeIssueFromCycle: (workspaceSlug: string, projectId: string, cycleId: string, issueId: string) => Promise;
addCycleToIssue: (workspaceSlug: string, projectId: string, cycleId: string, issueId: string) => Promise;
- removeCycleFromIssue: (workspaceSlug: string, projectId: string, issueId: string) => Promise
+ removeCycleFromIssue: (workspaceSlug: string, projectId: string, issueId: string) => Promise;
transferIssuesFromCycle: (
workspaceSlug: string,
projectId: string,
@@ -93,6 +96,7 @@ export class CycleIssues extends IssueHelperStore implements ICycleIssues {
loader: observable.ref,
issues: observable,
// computed
+ issuesCount: computed,
groupedIssueIds: computed,
// action
fetchIssues: action,
@@ -113,6 +117,22 @@ export class CycleIssues extends IssueHelperStore implements ICycleIssues {
this.cycleService = new CycleService();
}
+ get issuesCount() {
+ let issuesCount = 0;
+
+ const displayFilters = this.rootStore?.cycleIssuesFilter?.issueFilters?.displayFilters;
+ const groupedIssueIds = this.groupedIssueIds;
+ if (!displayFilters || !groupedIssueIds) return issuesCount;
+
+ const layout = displayFilters?.layout || undefined;
+ const groupBy = displayFilters?.group_by || undefined;
+ const subGroupBy = displayFilters?.sub_group_by || undefined;
+
+ if (!layout) return issuesCount;
+ issuesCount = issueCountBasedOnFilters(groupedIssueIds, layout, groupBy, subGroupBy);
+ return issuesCount;
+ }
+
get groupedIssueIds() {
const cycleId = this.rootIssueStore?.cycleId;
if (!cycleId) return undefined;
@@ -336,14 +356,14 @@ export class CycleIssues extends IssueHelperStore implements ICycleIssues {
/**
* Remove a cycle from issue
- * @param workspaceSlug
- * @param projectId
- * @param issueId
- * @returns
+ * @param workspaceSlug
+ * @param projectId
+ * @param issueId
+ * @returns
*/
removeCycleFromIssue = async (workspaceSlug: string, projectId: string, issueId: string) => {
const issueCycleId = this.rootIssueStore.issues.getIssueById(issueId)?.cycle_id;
- if(!issueCycleId) return;
+ if (!issueCycleId) return;
try {
// perform optimistic update, update store
runInAction(() => {
diff --git a/web/store/issue/module/issue.store.ts b/web/store/issue/module/issue.store.ts
index e5c7b1cc1..35066a052 100644
--- a/web/store/issue/module/issue.store.ts
+++ b/web/store/issue/module/issue.store.ts
@@ -7,6 +7,8 @@ import update from "lodash/update";
import { action, observable, makeObservable, computed, runInAction } from "mobx";
// types
import { TIssue, TLoader, TGroupedIssues, TSubGroupedIssues, TUnGroupedIssues, ViewFlags } from "@plane/types";
+// helpers
+import { issueCountBasedOnFilters } from "@/helpers/issue.helper";
// services
import { IssueService } from "@/services/issue";
import { ModuleService } from "@/services/module.service";
@@ -21,6 +23,7 @@ export interface IModuleIssues {
issues: { [module_id: string]: string[] };
viewFlags: ViewFlags;
// computed
+ issuesCount: number;
groupedIssueIds: TGroupedIssues | TSubGroupedIssues | TUnGroupedIssues | undefined;
// actions
getIssueIds: (groupId?: string, subGroupId?: string) => string[] | undefined;
@@ -95,6 +98,7 @@ export class ModuleIssues extends IssueHelperStore implements IModuleIssues {
loader: observable.ref,
issues: observable,
// computed
+ issuesCount: computed,
groupedIssueIds: computed,
// action
fetchIssues: action,
@@ -113,6 +117,22 @@ export class ModuleIssues extends IssueHelperStore implements IModuleIssues {
this.moduleService = new ModuleService();
}
+ get issuesCount() {
+ let issuesCount = 0;
+
+ const displayFilters = this.rootIssueStore?.moduleIssuesFilter?.issueFilters?.displayFilters;
+ const groupedIssueIds = this.groupedIssueIds;
+ if (!displayFilters || !groupedIssueIds) return issuesCount;
+
+ const layout = displayFilters?.layout || undefined;
+ const groupBy = displayFilters?.group_by || undefined;
+ const subGroupBy = displayFilters?.sub_group_by || undefined;
+
+ if (!layout) return issuesCount;
+ issuesCount = issueCountBasedOnFilters(groupedIssueIds, layout, groupBy, subGroupBy);
+ return issuesCount;
+ }
+
get groupedIssueIds() {
const moduleId = this.rootIssueStore?.moduleId;
if (!moduleId) return undefined;
@@ -370,9 +390,9 @@ export class ModuleIssues extends IssueHelperStore implements IModuleIssues {
/**
* change modules array in issue
- * @param workspaceSlug
- * @param projectId
- * @param issueId
+ * @param workspaceSlug
+ * @param projectId
+ * @param issueId
* @param addModuleIds array of modules to be added
* @param removeModuleIds array of modules to be removed
*/
@@ -404,7 +424,7 @@ export class ModuleIssues extends IssueHelperStore implements IModuleIssues {
});
});
});
- if(originalModuleIds){
+ if (originalModuleIds) {
// update the root issue map with the new module ids
let currentModuleIds = concat([...originalModuleIds], addModuleIds);
currentModuleIds = pull(currentModuleIds, ...removeModuleIds);
@@ -420,7 +440,6 @@ export class ModuleIssues extends IssueHelperStore implements IModuleIssues {
if (!isEmpty(removeModuleIds)) {
await this.moduleService.removeModulesFromIssueBulk(workspaceSlug, projectId, issueId, removeModuleIds);
}
-
} catch (error) {
// revert the issue back to its original module ids
set(this.rootStore.issues.issuesMap, [issueId, "module_ids"], originalModuleIds);
diff --git a/web/store/issue/project/issue.store.ts b/web/store/issue/project/issue.store.ts
index 6e137b9d8..8591f494c 100644
--- a/web/store/issue/project/issue.store.ts
+++ b/web/store/issue/project/issue.store.ts
@@ -5,6 +5,8 @@ import update from "lodash/update";
import { action, makeObservable, observable, runInAction, computed } from "mobx";
// types
import { TIssue, TGroupedIssues, TSubGroupedIssues, TLoader, TUnGroupedIssues, ViewFlags } from "@plane/types";
+// helpers
+import { issueCountBasedOnFilters } from "@/helpers/issue.helper";
// base class
import { IssueService, IssueArchiveService } from "@/services/issue";
import { IssueHelperStore } from "../helpers/issue-helper.store";
@@ -17,6 +19,7 @@ export interface IProjectIssues {
issues: Record; // Record of project_id as key and issue_ids as value
viewFlags: ViewFlags;
// computed
+ issuesCount: number;
groupedIssueIds: TGroupedIssues | TSubGroupedIssues | TUnGroupedIssues | undefined;
getIssueIds: (groupId?: string, subGroupId?: string) => string[] | undefined;
// action
@@ -51,6 +54,7 @@ export class ProjectIssues extends IssueHelperStore implements IProjectIssues {
loader: observable.ref,
issues: observable,
// computed
+ issuesCount: computed,
groupedIssueIds: computed,
// action
fetchIssues: action,
@@ -68,6 +72,22 @@ export class ProjectIssues extends IssueHelperStore implements IProjectIssues {
this.issueArchiveService = new IssueArchiveService();
}
+ get issuesCount() {
+ let issuesCount = 0;
+
+ const displayFilters = this.rootStore?.projectIssuesFilter?.issueFilters?.displayFilters;
+ const groupedIssueIds = this.groupedIssueIds;
+ if (!displayFilters || !groupedIssueIds) return issuesCount;
+
+ const layout = displayFilters?.layout || undefined;
+ const groupBy = displayFilters?.group_by || undefined;
+ const subGroupBy = displayFilters?.sub_group_by || undefined;
+
+ if (!layout) return issuesCount;
+ issuesCount = issueCountBasedOnFilters(groupedIssueIds, layout, groupBy, subGroupBy);
+ return issuesCount;
+ }
+
get groupedIssueIds() {
const projectId = this.rootStore?.projectId;
if (!projectId) return undefined;