mirror of
https://github.com/makeplane/plane
synced 2024-06-14 14:31:34 +00:00
[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:
parent
e2ac60e259
commit
4feec35773
@ -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}
|
||||
|
@ -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}
|
||||
|
@ -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}
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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;
|
||||
@ -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(() => {
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user