From 7d07afd59c1ccf1447c907ccb47de236610db2ad Mon Sep 17 00:00:00 2001 From: Anmol Singh Bhatia <121005188+anmolsinghbhatia@users.noreply.github.com> Date: Mon, 5 Feb 2024 12:34:53 +0530 Subject: [PATCH] chore: cycle and module sidebar analytics improvement (#3559) * chore: cycle and module store update action updated * chore: cycle and module issue store actions updated * chore: cycle and module retrieve endpoints updated * fix: app sidebar z index and priority icon fix * chore: cycle and module sidebar and stats updated --- apiserver/plane/app/views/cycle.py | 24 +++++++-------- apiserver/plane/app/views/module.py | 12 ++++---- packages/ui/src/icons/priority-icon.tsx | 1 - .../core/sidebar/progress-chart.tsx | 19 +++++++----- web/components/cycles/sidebar.tsx | 30 ++++++++----------- web/components/modules/sidebar.tsx | 16 ++++------ web/layouts/app-layout/sidebar.tsx | 22 ++++++-------- web/store/cycle.store.ts | 1 + web/store/issue/cycle/issue.store.ts | 6 ++++ web/store/issue/module/issue.store.ts | 6 ++++ web/store/module.store.ts | 1 + 11 files changed, 70 insertions(+), 68 deletions(-) diff --git a/apiserver/plane/app/views/cycle.py b/apiserver/plane/app/views/cycle.py index 23a227fef..32f593e1e 100644 --- a/apiserver/plane/app/views/cycle.py +++ b/apiserver/plane/app/views/cycle.py @@ -242,13 +242,13 @@ class CycleViewSet(WebhookMixin, BaseViewSet): .values("display_name", "assignee_id", "avatar") .annotate( total_issues=Count( - "assignee_id", + "id", filter=Q(archived_at__isnull=True, is_draft=False), ), ) .annotate( completed_issues=Count( - "assignee_id", + "id", filter=Q( completed_at__isnull=False, archived_at__isnull=True, @@ -258,7 +258,7 @@ class CycleViewSet(WebhookMixin, BaseViewSet): ) .annotate( pending_issues=Count( - "assignee_id", + "id", filter=Q( completed_at__isnull=True, archived_at__isnull=True, @@ -281,13 +281,13 @@ class CycleViewSet(WebhookMixin, BaseViewSet): .values("label_name", "color", "label_id") .annotate( total_issues=Count( - "label_id", + "id", filter=Q(archived_at__isnull=True, is_draft=False), ) ) .annotate( completed_issues=Count( - "label_id", + "id", filter=Q( completed_at__isnull=False, archived_at__isnull=True, @@ -297,7 +297,7 @@ class CycleViewSet(WebhookMixin, BaseViewSet): ) .annotate( pending_issues=Count( - "label_id", + "id", filter=Q( completed_at__isnull=True, archived_at__isnull=True, @@ -419,13 +419,13 @@ class CycleViewSet(WebhookMixin, BaseViewSet): ) .annotate( total_issues=Count( - "assignee_id", + "id", filter=Q(archived_at__isnull=True, is_draft=False), ), ) .annotate( completed_issues=Count( - "assignee_id", + "id", filter=Q( completed_at__isnull=False, archived_at__isnull=True, @@ -435,7 +435,7 @@ class CycleViewSet(WebhookMixin, BaseViewSet): ) .annotate( pending_issues=Count( - "assignee_id", + "id", filter=Q( completed_at__isnull=True, archived_at__isnull=True, @@ -459,13 +459,13 @@ class CycleViewSet(WebhookMixin, BaseViewSet): .values("label_name", "color", "label_id") .annotate( total_issues=Count( - "label_id", + "id", filter=Q(archived_at__isnull=True, is_draft=False), ), ) .annotate( completed_issues=Count( - "label_id", + "id", filter=Q( completed_at__isnull=False, archived_at__isnull=True, @@ -475,7 +475,7 @@ class CycleViewSet(WebhookMixin, BaseViewSet): ) .annotate( pending_issues=Count( - "label_id", + "id", filter=Q( completed_at__isnull=True, archived_at__isnull=True, diff --git a/apiserver/plane/app/views/module.py b/apiserver/plane/app/views/module.py index 1f055129a..fafcfed4b 100644 --- a/apiserver/plane/app/views/module.py +++ b/apiserver/plane/app/views/module.py @@ -197,7 +197,7 @@ class ModuleViewSet(WebhookMixin, BaseViewSet): ) .annotate( total_issues=Count( - "assignee_id", + "id", filter=Q( archived_at__isnull=True, is_draft=False, @@ -206,7 +206,7 @@ class ModuleViewSet(WebhookMixin, BaseViewSet): ) .annotate( completed_issues=Count( - "assignee_id", + "id", filter=Q( completed_at__isnull=False, archived_at__isnull=True, @@ -216,7 +216,7 @@ class ModuleViewSet(WebhookMixin, BaseViewSet): ) .annotate( pending_issues=Count( - "assignee_id", + "id", filter=Q( completed_at__isnull=True, archived_at__isnull=True, @@ -239,7 +239,7 @@ class ModuleViewSet(WebhookMixin, BaseViewSet): .values("label_name", "color", "label_id") .annotate( total_issues=Count( - "label_id", + "id", filter=Q( archived_at__isnull=True, is_draft=False, @@ -248,7 +248,7 @@ class ModuleViewSet(WebhookMixin, BaseViewSet): ) .annotate( completed_issues=Count( - "label_id", + "id", filter=Q( completed_at__isnull=False, archived_at__isnull=True, @@ -258,7 +258,7 @@ class ModuleViewSet(WebhookMixin, BaseViewSet): ) .annotate( pending_issues=Count( - "label_id", + "id", filter=Q( completed_at__isnull=True, archived_at__isnull=True, diff --git a/packages/ui/src/icons/priority-icon.tsx b/packages/ui/src/icons/priority-icon.tsx index caa821305..0b98b3e6b 100644 --- a/packages/ui/src/icons/priority-icon.tsx +++ b/packages/ui/src/icons/priority-icon.tsx @@ -47,7 +47,6 @@ export const PriorityIcon: React.FC = (props) => { > = ({ distribution, startDate, endDate, tota { id: "pending", color: "#3F76FF", - data: chartData.map((item, index) => ({ - index, - x: item.currentDate, - y: item.pending, - color: "#3F76FF", - })), + data: + chartData.length > 0 + ? chartData.map((item, index) => ({ + index, + x: item.currentDate, + y: item.pending, + color: "#3F76FF", + })) + : [], enableArea: true, }, { @@ -121,7 +124,9 @@ const ProgressChart: React.FC = ({ distribution, startDate, endDate, tota enableArea colors={(datum) => datum.color ?? "#3F76FF"} customYAxisTickValues={[0, totalIssues]} - gridXValues={chartData.map((item, index) => (index % 2 === 0 ? item.currentDate : ""))} + gridXValues={ + chartData.length > 0 ? chartData.map((item, index) => (index % 2 === 0 ? item.currentDate : "")) : undefined + } enableSlices="x" sliceTooltip={(datum) => (
diff --git a/web/components/cycles/sidebar.tsx b/web/components/cycles/sidebar.tsx index 4bf76f91f..5fa1d2459 100644 --- a/web/components/cycles/sidebar.tsx +++ b/web/components/cycles/sidebar.tsx @@ -319,13 +319,7 @@ export const CycleDetailsSidebar: React.FC = observer((props) => { const currentCycle = CYCLE_STATUS.find((status) => status.value === cycleStatus); const issueCount = - cycleDetails.total_issues === 0 - ? "0 Issue" - : cycleDetails.total_issues === cycleDetails.completed_issues - ? cycleDetails.total_issues > 1 - ? `${cycleDetails.total_issues}` - : `${cycleDetails.total_issues}` - : `${cycleDetails.completed_issues}/${cycleDetails.total_issues}`; + cycleDetails.total_issues === 0 ? "0 Issue" : `${cycleDetails.completed_issues}/${cycleDetails.total_issues}`; const isEditingAllowed = !!currentProjectRole && currentProjectRole >= EUserWorkspaceRoles.MEMBER; @@ -575,7 +569,9 @@ export const CycleDetailsSidebar: React.FC = observer((props) => {
- {isStartValid && isEndValid ? ( + {cycleDetails.distribution?.completion_chart && + cycleDetails.start_date && + cycleDetails.end_date ? (
@@ -589,16 +585,14 @@ export const CycleDetailsSidebar: React.FC = observer((props) => {
- {cycleDetails && cycleDetails.distribution && ( -
- -
- )} +
+ +
) : ( "" diff --git a/web/components/modules/sidebar.tsx b/web/components/modules/sidebar.tsx index f43326b6b..8b1aa28e8 100644 --- a/web/components/modules/sidebar.tsx +++ b/web/components/modules/sidebar.tsx @@ -262,13 +262,7 @@ export const ModuleDetailsSidebar: React.FC = observer((props) => { const moduleStatus = MODULE_STATUS.find((status) => status.value === moduleDetails.status); const issueCount = - moduleDetails.total_issues === 0 - ? "0 Issue" - : moduleDetails.total_issues === moduleDetails.completed_issues - ? moduleDetails.total_issues > 1 - ? `${moduleDetails.total_issues}` - : `${moduleDetails.total_issues}` - : `${moduleDetails.completed_issues}/${moduleDetails.total_issues}`; + moduleDetails.total_issues === 0 ? "0 Issue" : `${moduleDetails.completed_issues}/${moduleDetails.total_issues}`; const isEditingAllowed = !!currentProjectRole && currentProjectRole >= EUserProjectRoles.MEMBER; @@ -582,7 +576,7 @@ export const ModuleDetailsSidebar: React.FC = observer((props) => {
- {isStartValid && isEndValid ? ( + {moduleDetails.start_date && moduleDetails.target_date ? (
@@ -598,9 +592,9 @@ export const ModuleDetailsSidebar: React.FC = observer((props) => {
diff --git a/web/layouts/app-layout/sidebar.tsx b/web/layouts/app-layout/sidebar.tsx index d9ce3c3f0..e211e7884 100644 --- a/web/layouts/app-layout/sidebar.tsx +++ b/web/layouts/app-layout/sidebar.tsx @@ -12,7 +12,7 @@ import { ProjectSidebarList } from "components/project"; import { useApplication } from "hooks/store"; import useOutsideClickDetector from "hooks/use-outside-click-detector"; -export interface IAppSidebar { } +export interface IAppSidebar {} export const AppSidebar: FC = observer(() => { // store hooks @@ -34,24 +34,23 @@ export const AppSidebar: FC = observer(() => { } }; handleResize(); - window.addEventListener('resize', handleResize); + window.addEventListener("resize", handleResize); return () => { - window.removeEventListener('resize', handleResize); + window.removeEventListener("resize", handleResize); }; }, [themStore]); return (
-
+ md:ml-0 ${themStore.sidebarCollapsed ? "w-[80px]" : "w-[280px]"} + lg:ml-0 ${themStore.sidebarCollapsed ? "w-[80px]" : "w-[280px]"} + `} + > +
@@ -61,6 +60,3 @@ export const AppSidebar: FC = observer(() => {
); }); - - - diff --git a/web/store/cycle.store.ts b/web/store/cycle.store.ts index ed5077385..bb6824c08 100644 --- a/web/store/cycle.store.ts +++ b/web/store/cycle.store.ts @@ -304,6 +304,7 @@ export class CycleStore implements ICycleStore { set(this.cycleMap, [cycleId], { ...this.cycleMap?.[cycleId], ...data }); }); const response = await this.cycleService.patchCycle(workspaceSlug, projectId, cycleId, data); + this.fetchCycleDetails(workspaceSlug, projectId, cycleId); return response; } catch (error) { console.log("Failed to patch cycle from cycle store"); diff --git a/web/store/issue/cycle/issue.store.ts b/web/store/issue/cycle/issue.store.ts index 286222e4a..71618d51c 100644 --- a/web/store/issue/cycle/issue.store.ts +++ b/web/store/issue/cycle/issue.store.ts @@ -152,6 +152,7 @@ export class CycleIssues extends IssueHelperStore implements ICycleIssues { const params = this.rootIssueStore?.cycleIssuesFilter?.appliedFilters; const response = await this.cycleService.getCycleIssuesWithParams(workspaceSlug, projectId, cycleId, params); + this.rootIssueStore.rootStore.cycle.fetchCycleDetails(workspaceSlug, projectId, cycleId); runInAction(() => { set( @@ -182,6 +183,7 @@ export class CycleIssues extends IssueHelperStore implements ICycleIssues { const response = await this.rootIssueStore.projectIssues.createIssue(workspaceSlug, projectId, data); await this.addIssueToCycle(workspaceSlug, projectId, cycleId, [response.id]); + this.rootIssueStore.rootStore.cycle.fetchCycleDetails(workspaceSlug, projectId, cycleId); return response; } catch (error) { @@ -218,6 +220,7 @@ export class CycleIssues extends IssueHelperStore implements ICycleIssues { if (!cycleId) throw new Error("Cycle Id is required"); const response = await this.rootIssueStore.projectIssues.removeIssue(workspaceSlug, projectId, issueId); + this.rootIssueStore.rootStore.cycle.fetchCycleDetails(workspaceSlug, projectId, cycleId); const issueIndex = this.issues[cycleId].findIndex((_issueId) => _issueId === issueId); if (issueIndex >= 0) @@ -246,6 +249,7 @@ export class CycleIssues extends IssueHelperStore implements ICycleIssues { }); const response = await this.createIssue(workspaceSlug, projectId, data, cycleId); + this.rootIssueStore.rootStore.cycle.fetchCycleDetails(workspaceSlug, projectId, cycleId); const quickAddIssueIndex = this.issues[cycleId].findIndex((_issueId) => _issueId === data.id); if (quickAddIssueIndex >= 0) @@ -273,6 +277,7 @@ export class CycleIssues extends IssueHelperStore implements ICycleIssues { issueIds.forEach((issueId) => { this.rootStore.issues.updateIssue(issueId, { cycle_id: cycleId }); }); + this.rootIssueStore.rootStore.cycle.fetchCycleDetails(workspaceSlug, projectId, cycleId); return issueToCycle; } catch (error) { @@ -289,6 +294,7 @@ export class CycleIssues extends IssueHelperStore implements ICycleIssues { this.rootStore.issues.updateIssue(issueId, { cycle_id: null }); const response = await this.issueService.removeIssueFromCycle(workspaceSlug, projectId, cycleId, issueId); + this.rootIssueStore.rootStore.cycle.fetchCycleDetails(workspaceSlug, projectId, cycleId); return response; } catch (error) { diff --git a/web/store/issue/module/issue.store.ts b/web/store/issue/module/issue.store.ts index e32b97df0..c9ed459a1 100644 --- a/web/store/issue/module/issue.store.ts +++ b/web/store/issue/module/issue.store.ts @@ -156,6 +156,7 @@ export class ModuleIssues extends IssueHelperStore implements IModuleIssues { const params = this.rootIssueStore?.moduleIssuesFilter?.appliedFilters; const response = await this.moduleService.getModuleIssues(workspaceSlug, projectId, moduleId, params); + this.rootIssueStore.rootStore.module.fetchModuleDetails(workspaceSlug, projectId, moduleId); runInAction(() => { set( @@ -187,6 +188,7 @@ export class ModuleIssues extends IssueHelperStore implements IModuleIssues { const response = await this.rootIssueStore.projectIssues.createIssue(workspaceSlug, projectId, data); await this.addIssuesToModule(workspaceSlug, projectId, moduleId, [response.id]); + this.rootIssueStore.rootStore.module.fetchModuleDetails(workspaceSlug, projectId, moduleId); return response; } catch (error) { @@ -223,6 +225,7 @@ export class ModuleIssues extends IssueHelperStore implements IModuleIssues { if (!moduleId) throw new Error("Module Id is required"); const response = await this.rootIssueStore.projectIssues.removeIssue(workspaceSlug, projectId, issueId); + this.rootIssueStore.rootStore.module.fetchModuleDetails(workspaceSlug, projectId, moduleId); const issueIndex = this.issues[moduleId].findIndex((_issueId) => _issueId === issueId); if (issueIndex >= 0) @@ -251,6 +254,7 @@ export class ModuleIssues extends IssueHelperStore implements IModuleIssues { }); const response = await this.createIssue(workspaceSlug, projectId, data, moduleId); + this.rootIssueStore.rootStore.module.fetchModuleDetails(workspaceSlug, projectId, moduleId); const quickAddIssueIndex = this.issues[moduleId].findIndex((_issueId) => _issueId === data.id); if (quickAddIssueIndex >= 0) @@ -284,6 +288,7 @@ export class ModuleIssues extends IssueHelperStore implements IModuleIssues { else return uniq(concat(issueModuleIds, [moduleId])); }); }); + this.rootIssueStore.rootStore.module.fetchModuleDetails(workspaceSlug, projectId, moduleId); return issueToModule; } catch (error) { @@ -314,6 +319,7 @@ export class ModuleIssues extends IssueHelperStore implements IModuleIssues { moduleId, issueIds ); + this.rootIssueStore.rootStore.module.fetchModuleDetails(workspaceSlug, projectId, moduleId); return response; } catch (error) { diff --git a/web/store/module.store.ts b/web/store/module.store.ts index f0f576cbc..7ebccc23c 100644 --- a/web/store/module.store.ts +++ b/web/store/module.store.ts @@ -196,6 +196,7 @@ export class ModulesStore implements IModuleStore { set(this.moduleMap, [moduleId], { ...originalModuleDetails, ...data }); }); const response = await this.moduleService.patchModule(workspaceSlug, projectId, moduleId, data); + this.fetchModuleDetails(workspaceSlug, projectId, moduleId); return response; } catch (error) { console.error("Failed to update module in module store", error);