From d0b8e9c4b52a735ddb1952f6db0c5b8d912f790c Mon Sep 17 00:00:00 2001 From: Vamsi krishna Date: Mon, 13 Jan 2025 11:27:16 +0530 Subject: [PATCH 1/5] fix: updated cancelled issues count into pending issues --- web/core/components/cycles/list/cycle-list-item-action.tsx | 6 +++++- web/core/components/cycles/quick-actions.tsx | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/web/core/components/cycles/list/cycle-list-item-action.tsx b/web/core/components/cycles/list/cycle-list-item-action.tsx index 2fa3d4fd346..ba81212f8ad 100644 --- a/web/core/components/cycles/list/cycle-list-item-action.tsx +++ b/web/core/components/cycles/list/cycle-list-item-action.tsx @@ -81,7 +81,11 @@ export const CycleListItemAction: FC = observer((props) => { // derived values const cycleStatus = cycleDetails.status ? (cycleDetails.status.toLocaleLowerCase() as TCycleGroups) : "draft"; const showIssueCount = useMemo(() => cycleStatus === "draft" || cycleStatus === "upcoming", [cycleStatus]); - const transferableIssuesCount = cycleDetails ? cycleDetails.total_issues - cycleDetails.completed_issues : 0; + const transferableIssuesCount = cycleDetails + ? cycleDetails.total_issues - + cycleDetails.completed_issues - + (cycleDetails.progress_snapshot?.cancelled_issues || 0) + : 0; const showTransferIssues = transferableIssuesCount > 0 && cycleStatus === "completed"; const isEditingAllowed = allowPermissions( [EUserPermissions.ADMIN, EUserPermissions.MEMBER], diff --git a/web/core/components/cycles/quick-actions.tsx b/web/core/components/cycles/quick-actions.tsx index b72d7b7bd61..78ec6c0c1e7 100644 --- a/web/core/components/cycles/quick-actions.tsx +++ b/web/core/components/cycles/quick-actions.tsx @@ -42,7 +42,11 @@ export const CycleQuickActions: React.FC = observer((props) => { const isArchived = !!cycleDetails?.archived_at; const isCompleted = cycleDetails?.status?.toLowerCase() === "completed"; const isCurrentCycle = cycleDetails?.status?.toLowerCase() === "current"; - const transferableIssuesCount = cycleDetails ? cycleDetails.total_issues - cycleDetails.completed_issues : 0; + const transferableIssuesCount = cycleDetails + ? cycleDetails.total_issues - + cycleDetails.completed_issues - + (cycleDetails.progress_snapshot?.cancelled_issues || 0) + : 0; // auth const isEditingAllowed = allowPermissions( [EUserPermissions.ADMIN, EUserPermissions.MEMBER], From 7a91cc17a33c2279712f204aeb15e635a11756c6 Mon Sep 17 00:00:00 2001 From: Vamsi krishna Date: Mon, 13 Jan 2025 12:40:55 +0530 Subject: [PATCH 2/5] chore: code refactor --- .../cycles/list/cycle-list-item-action.tsx | 8 ++------ web/core/components/cycles/quick-actions.tsx | 8 ++------ web/core/store/cycle.store.ts | 13 +++++++++++++ 3 files changed, 17 insertions(+), 12 deletions(-) diff --git a/web/core/components/cycles/list/cycle-list-item-action.tsx b/web/core/components/cycles/list/cycle-list-item-action.tsx index ba81212f8ad..bb075243b6d 100644 --- a/web/core/components/cycles/list/cycle-list-item-action.tsx +++ b/web/core/components/cycles/list/cycle-list-item-action.tsx @@ -67,7 +67,7 @@ export const CycleListItemAction: FC = observer((props) => { const searchParams = useSearchParams(); const pathname = usePathname(); // store hooks - const { addCycleToFavorites, removeCycleFromFavorites, updateCycleDetails } = useCycle(); + const { addCycleToFavorites, removeCycleFromFavorites, updateCycleDetails, getPendingIssueCount } = useCycle(); const { captureEvent } = useEventTracker(); const { allowPermissions } = useUserPermissions(); @@ -81,11 +81,7 @@ export const CycleListItemAction: FC = observer((props) => { // derived values const cycleStatus = cycleDetails.status ? (cycleDetails.status.toLocaleLowerCase() as TCycleGroups) : "draft"; const showIssueCount = useMemo(() => cycleStatus === "draft" || cycleStatus === "upcoming", [cycleStatus]); - const transferableIssuesCount = cycleDetails - ? cycleDetails.total_issues - - cycleDetails.completed_issues - - (cycleDetails.progress_snapshot?.cancelled_issues || 0) - : 0; + const transferableIssuesCount = getPendingIssueCount(cycleId); const showTransferIssues = transferableIssuesCount > 0 && cycleStatus === "completed"; const isEditingAllowed = allowPermissions( [EUserPermissions.ADMIN, EUserPermissions.MEMBER], diff --git a/web/core/components/cycles/quick-actions.tsx b/web/core/components/cycles/quick-actions.tsx index 78ec6c0c1e7..951f6be6fbe 100644 --- a/web/core/components/cycles/quick-actions.tsx +++ b/web/core/components/cycles/quick-actions.tsx @@ -36,17 +36,13 @@ export const CycleQuickActions: React.FC = observer((props) => { // store hooks const { setTrackElement } = useEventTracker(); const { allowPermissions } = useUserPermissions(); - const { getCycleById, restoreCycle } = useCycle(); + const { getCycleById, restoreCycle, getPendingIssueCount } = useCycle(); // derived values const cycleDetails = getCycleById(cycleId); const isArchived = !!cycleDetails?.archived_at; const isCompleted = cycleDetails?.status?.toLowerCase() === "completed"; const isCurrentCycle = cycleDetails?.status?.toLowerCase() === "current"; - const transferableIssuesCount = cycleDetails - ? cycleDetails.total_issues - - cycleDetails.completed_issues - - (cycleDetails.progress_snapshot?.cancelled_issues || 0) - : 0; + const transferableIssuesCount = getPendingIssueCount(cycleId); // auth const isEditingAllowed = allowPermissions( [EUserPermissions.ADMIN, EUserPermissions.MEMBER], diff --git a/web/core/store/cycle.store.ts b/web/core/store/cycle.store.ts index 4e56e56c11d..b68941bb57a 100644 --- a/web/core/store/cycle.store.ts +++ b/web/core/store/cycle.store.ts @@ -56,6 +56,7 @@ export interface ICycleStore { getPlotTypeByCycleId: (cycleId: string) => TCyclePlotType; getEstimateTypeByCycleId: (cycleId: string) => TCycleEstimateType; getIsPointsDataAvailable: (cycleId: string) => boolean; + getPendingIssueCount: (cycleId: string) => number; // actions updateCycleDistribution: (distributionUpdates: DistributionUpdates, cycleId: string) => void; @@ -367,6 +368,18 @@ export class CycleStore implements ICycleStore { : "issues"; }); + getPendingIssueCount = (cycleId: string) => { + let pending_issues = 0; + const cycleDetails = this.cycleMap[cycleId]; + if (!cycleDetails) return pending_issues; + pending_issues = + cycleDetails.total_issues - + cycleDetails.completed_issues - + (cycleDetails.progress_snapshot?.cancelled_issues || 0); + + return pending_issues; + }; + /** * @description updates the plot type for the cycle store * @param {TCyclePlotType} plotType From 1e6876b82738ea944123eb8c2ae56c23c78cc862 Mon Sep 17 00:00:00 2001 From: NarayanBavisetti Date: Fri, 10 Jan 2025 17:09:55 +0530 Subject: [PATCH 3/5] chore: added pending issues count --- apiserver/plane/app/views/cycle/base.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/apiserver/plane/app/views/cycle/base.py b/apiserver/plane/app/views/cycle/base.py index f30f498265f..806fb61ca4d 100644 --- a/apiserver/plane/app/views/cycle/base.py +++ b/apiserver/plane/app/views/cycle/base.py @@ -132,6 +132,18 @@ def get_queryset(self): ), ) ) + .annotate( + pending_issues=Count( + "issue_cycle__issue__id", + distinct=True, + filter=Q( + issue_cycle__issue__state__group__in=["backlog", "unstarted", "started"], + issue_cycle__issue__archived_at__isnull=True, + issue_cycle__issue__is_draft=False, + issue_cycle__deleted_at__isnull=True, + ), + ) + ) .annotate( status=Case( When( @@ -214,6 +226,7 @@ def list(self, request, slug, project_id): "is_favorite", "total_issues", "completed_issues", + "pending_issues", "assignee_ids", "status", "version", From 6cb65d2af28387ed2223d9f16c59d5f165778562 Mon Sep 17 00:00:00 2001 From: NarayanBavisetti Date: Fri, 10 Jan 2025 17:11:16 +0530 Subject: [PATCH 4/5] chore: added pending issues count --- apiserver/plane/app/views/cycle/base.py | 1 + 1 file changed, 1 insertion(+) diff --git a/apiserver/plane/app/views/cycle/base.py b/apiserver/plane/app/views/cycle/base.py index 806fb61ca4d..3b2985f18c5 100644 --- a/apiserver/plane/app/views/cycle/base.py +++ b/apiserver/plane/app/views/cycle/base.py @@ -258,6 +258,7 @@ def list(self, request, slug, project_id): # meta fields "is_favorite", "total_issues", + "pending_issues", "completed_issues", "assignee_ids", "status", From c68e36c81fd951a0e989976a4f6c6d1fcdf3d988 Mon Sep 17 00:00:00 2001 From: Vamsi krishna Date: Wed, 15 Jan 2025 12:46:02 +0530 Subject: [PATCH 5/5] chore: added pending_issues to api response --- packages/types/src/cycle/cycle.d.ts | 5 ++--- .../cycles/list/cycle-list-item-action.tsx | 7 +++---- web/core/components/cycles/quick-actions.tsx | 21 ++++++++++--------- .../cycles/transfer-issues-modal.tsx | 6 +++--- web/core/store/cycle.store.ts | 13 ------------ 5 files changed, 19 insertions(+), 33 deletions(-) diff --git a/packages/types/src/cycle/cycle.d.ts b/packages/types/src/cycle/cycle.d.ts index 0aa3fc2804e..e325b81ad6b 100644 --- a/packages/types/src/cycle/cycle.d.ts +++ b/packages/types/src/cycle/cycle.d.ts @@ -104,6 +104,7 @@ export interface ICycle extends TProgressSnapshot { project_detail: IProjectDetails; progress: any[]; version: number; + pending_issues: number; } export interface CycleIssueResponse { @@ -120,9 +121,7 @@ export interface CycleIssueResponse { sub_issues_count: number; } -export type SelectCycleType = - | (ICycle & { actionType: "edit" | "delete" | "create-issue" }) - | undefined; +export type SelectCycleType = (ICycle & { actionType: "edit" | "delete" | "create-issue" }) | undefined; export type CycleDateCheckData = { start_date: string; diff --git a/web/core/components/cycles/list/cycle-list-item-action.tsx b/web/core/components/cycles/list/cycle-list-item-action.tsx index bb075243b6d..ca9fefd4682 100644 --- a/web/core/components/cycles/list/cycle-list-item-action.tsx +++ b/web/core/components/cycles/list/cycle-list-item-action.tsx @@ -67,7 +67,7 @@ export const CycleListItemAction: FC = observer((props) => { const searchParams = useSearchParams(); const pathname = usePathname(); // store hooks - const { addCycleToFavorites, removeCycleFromFavorites, updateCycleDetails, getPendingIssueCount } = useCycle(); + const { addCycleToFavorites, removeCycleFromFavorites, updateCycleDetails } = useCycle(); const { captureEvent } = useEventTracker(); const { allowPermissions } = useUserPermissions(); @@ -81,8 +81,7 @@ export const CycleListItemAction: FC = observer((props) => { // derived values const cycleStatus = cycleDetails.status ? (cycleDetails.status.toLocaleLowerCase() as TCycleGroups) : "draft"; const showIssueCount = useMemo(() => cycleStatus === "draft" || cycleStatus === "upcoming", [cycleStatus]); - const transferableIssuesCount = getPendingIssueCount(cycleId); - const showTransferIssues = transferableIssuesCount > 0 && cycleStatus === "completed"; + const showTransferIssues = cycleDetails.pending_issues > 0 && cycleStatus === "completed"; const isEditingAllowed = allowPermissions( [EUserPermissions.ADMIN, EUserPermissions.MEMBER], EUserPermissionsLevel.PROJECT, @@ -254,7 +253,7 @@ export const CycleListItemAction: FC = observer((props) => { }} > - Transfer {transferableIssuesCount} issues + Transfer {cycleDetails.pending_issues} issues )} diff --git a/web/core/components/cycles/quick-actions.tsx b/web/core/components/cycles/quick-actions.tsx index 951f6be6fbe..0f6a27979f0 100644 --- a/web/core/components/cycles/quick-actions.tsx +++ b/web/core/components/cycles/quick-actions.tsx @@ -36,13 +36,12 @@ export const CycleQuickActions: React.FC = observer((props) => { // store hooks const { setTrackElement } = useEventTracker(); const { allowPermissions } = useUserPermissions(); - const { getCycleById, restoreCycle, getPendingIssueCount } = useCycle(); + const { getCycleById, restoreCycle } = useCycle(); // derived values const cycleDetails = getCycleById(cycleId); const isArchived = !!cycleDetails?.archived_at; const isCompleted = cycleDetails?.status?.toLowerCase() === "completed"; const isCurrentCycle = cycleDetails?.status?.toLowerCase() === "current"; - const transferableIssuesCount = getPendingIssueCount(cycleId); // auth const isEditingAllowed = allowPermissions( [EUserPermissions.ADMIN, EUserPermissions.MEMBER], @@ -170,14 +169,16 @@ export const CycleQuickActions: React.FC = observer((props) => { workspaceSlug={workspaceSlug} projectId={projectId} /> - setEndCycleModalOpen(false)} - cycleId={cycleId} - projectId={projectId} - workspaceSlug={workspaceSlug} - transferrableIssuesCount={transferableIssuesCount} - /> + {isCurrentCycle && ( + setEndCycleModalOpen(false)} + cycleId={cycleId} + projectId={projectId} + workspaceSlug={workspaceSlug} + transferrableIssuesCount={cycleDetails.pending_issues} + /> + )} )} diff --git a/web/core/components/cycles/transfer-issues-modal.tsx b/web/core/components/cycles/transfer-issues-modal.tsx index fc069687321..0e9b56c0f75 100644 --- a/web/core/components/cycles/transfer-issues-modal.tsx +++ b/web/core/components/cycles/transfer-issues-modal.tsx @@ -26,7 +26,7 @@ export const TransferIssuesModal: React.FC = observer((props) => { const [query, setQuery] = useState(""); // store hooks - const { currentProjectIncompleteCycleIds, getCycleById, fetchCycleDetails } = useCycle(); + const { currentProjectIncompleteCycleIds, getCycleById, fetchActiveCycleProgress } = useCycle(); const { issues: { transferIssuesFromCycle }, } = useIssues(EIssuesStoreType.CYCLE); @@ -57,8 +57,8 @@ export const TransferIssuesModal: React.FC = observer((props) => { /**To update issue counts in target cycle and current cycle */ const getCycleDetails = async (newCycleId: string) => { const cyclesFetch = [ - fetchCycleDetails(workspaceSlug.toString(), projectId.toString(), cycleId), - fetchCycleDetails(workspaceSlug.toString(), projectId.toString(), newCycleId), + fetchActiveCycleProgress(workspaceSlug.toString(), projectId.toString(), cycleId), + fetchActiveCycleProgress(workspaceSlug.toString(), projectId.toString(), newCycleId), ]; await Promise.all(cyclesFetch).catch((error) => { setToast({ diff --git a/web/core/store/cycle.store.ts b/web/core/store/cycle.store.ts index b68941bb57a..4e56e56c11d 100644 --- a/web/core/store/cycle.store.ts +++ b/web/core/store/cycle.store.ts @@ -56,7 +56,6 @@ export interface ICycleStore { getPlotTypeByCycleId: (cycleId: string) => TCyclePlotType; getEstimateTypeByCycleId: (cycleId: string) => TCycleEstimateType; getIsPointsDataAvailable: (cycleId: string) => boolean; - getPendingIssueCount: (cycleId: string) => number; // actions updateCycleDistribution: (distributionUpdates: DistributionUpdates, cycleId: string) => void; @@ -368,18 +367,6 @@ export class CycleStore implements ICycleStore { : "issues"; }); - getPendingIssueCount = (cycleId: string) => { - let pending_issues = 0; - const cycleDetails = this.cycleMap[cycleId]; - if (!cycleDetails) return pending_issues; - pending_issues = - cycleDetails.total_issues - - cycleDetails.completed_issues - - (cycleDetails.progress_snapshot?.cancelled_issues || 0); - - return pending_issues; - }; - /** * @description updates the plot type for the cycle store * @param {TCyclePlotType} plotType