[WEB-1301] chore: handled issues count in project, module, and cycle issues (#4538)

* chore: handled issues count in project, module, and cycle issues

* chore: changed the typo from getIssuesCount to issuesCount
This commit is contained in:
guru_sainath 2024-05-21 14:55:29 +05:30 committed by GitHub
parent e2ac60e259
commit 4feec35773
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 127 additions and 40 deletions

View File

@ -70,6 +70,7 @@ export const CycleIssuesHeader: React.FC = observer(() => {
// store hooks
const {
issuesFilter: { issueFilters, updateFilters },
issues: { issuesCount },
} = useIssues(EIssuesStoreType.CYCLE);
const { currentProjectCycleIds, getCycleById } = useCycle();
const { toggleCreateIssueModal } = useCommandPalette();
@ -145,12 +146,6 @@ export const CycleIssuesHeader: React.FC = observer(() => {
const canUserCreateIssue =
currentProjectRole && [EUserProjectRoles.ADMIN, EUserProjectRoles.MEMBER].includes(currentProjectRole);
const issueCount = cycleDetails
? !issueFilters?.displayFilters?.sub_issue && cycleDetails?.sub_issues
? cycleDetails.total_issues - cycleDetails?.sub_issues
: cycleDetails.total_issues
: undefined;
const isFiltersApplied = calculateTotalFilters(issueFilters?.filters ?? {}) !== 0;
return (
@ -209,16 +204,16 @@ export const CycleIssuesHeader: React.FC = observer(() => {
<ContrastIcon className="h-3 w-3" />
<div className="flex w-auto max-w-[70px] items-center gap-2 truncate sm:max-w-[200px]">
<p className="truncate">{cycleDetails?.name && cycleDetails.name}</p>
{issueCount && issueCount > 0 ? (
{issuesCount && issuesCount > 0 ? (
<Tooltip
isMobile={isMobile}
tooltipContent={`There are ${issueCount} ${
issueCount > 1 ? "issues" : "issue"
tooltipContent={`There are ${issuesCount} ${
issuesCount > 1 ? "issues" : "issue"
} in this cycle`}
position="bottom"
>
<span className="flex flex-shrink-0 cursor-default items-center justify-center rounded-xl bg-custom-primary-100/20 px-2 text-center text-xs font-semibold text-custom-primary-100">
{issueCount}
{issuesCount}
</span>
</Tooltip>
) : null}

View File

@ -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(() => {
<DiceIcon className="h-3 w-3" />
<div className="flex w-auto max-w-[70px] items-center gap-2 truncate sm:max-w-[200px]">
<p className="truncate">{moduleDetails?.name && moduleDetails.name}</p>
{issueCount && issueCount > 0 ? (
{issuesCount && issuesCount > 0 ? (
<Tooltip
isMobile={isMobile}
tooltipContent={`There are ${issueCount} ${
issueCount > 1 ? "issues" : "issue"
tooltipContent={`There are ${issuesCount} ${
issuesCount > 1 ? "issues" : "issue"
} in this module`}
position="bottom"
>
<span className="flex flex-shrink-0 cursor-default items-center justify-center rounded-xl bg-custom-primary-100/20 px-2 text-center text-xs font-semibold text-custom-primary-100">
{issueCount}
{issuesCount}
</span>
</Tooltip>
) : null}

View File

@ -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={<BreadcrumbLink label="Issues" icon={<LayersIcon className="h-4 w-4 text-custom-text-300" />} />}
/>
</Breadcrumbs>
{issueCount && issueCount > 0 ? (
{issuesCount && issuesCount > 0 ? (
<Tooltip
isMobile={isMobile}
tooltipContent={`There are ${issueCount} ${issueCount > 1 ? "issues" : "issue"} in this project`}
tooltipContent={`There are ${issuesCount} ${issuesCount > 1 ? "issues" : "issue"} in this project`}
position="bottom"
>
<span className="cursor-default flex items-center text-center justify-center px-2.5 py-0.5 flex-shrink-0 bg-custom-primary-100/20 text-custom-primary-100 text-xs font-semibold rounded-xl">
{issueCount}
{issuesCount}
</span>
</Tooltip>
) : null}

View File

@ -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;
};

View File

@ -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<void>;
removeIssueFromCycle: (workspaceSlug: string, projectId: string, cycleId: string, issueId: string) => Promise<void>;
addCycleToIssue: (workspaceSlug: string, projectId: string, cycleId: string, issueId: string) => Promise<void>;
removeCycleFromIssue: (workspaceSlug: string, projectId: string, issueId: string) => Promise<void>
removeCycleFromIssue: (workspaceSlug: string, projectId: string, issueId: string) => Promise<void>;
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;
@ -343,7 +363,7 @@ export class CycleIssues extends IssueHelperStore implements ICycleIssues {
*/
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(() => {

View File

@ -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;
@ -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);

View File

@ -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<string, string[]>; // 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;