From 3b0d536ae59cc694b01cc22191da6fad82e4ca51 Mon Sep 17 00:00:00 2001 From: gakshita Date: Mon, 22 Jul 2024 18:28:50 +0530 Subject: [PATCH 1/6] fix: cycles loading optimization --- .../cycles/active-cycle/cycle-stats.tsx | 25 +++++++++++------- .../cycles/active-cycle/productivity.tsx | 13 +++++++--- .../cycles/active-cycle/progress.tsx | 26 ++++++++++++------- .../components/cycles/active-cycle/root.tsx | 13 +++++++--- web/core/store/cycle.store.ts | 12 ++++++++- 5 files changed, 60 insertions(+), 29 deletions(-) diff --git a/web/core/components/cycles/active-cycle/cycle-stats.tsx b/web/core/components/cycles/active-cycle/cycle-stats.tsx index d8232375034..38555494d65 100644 --- a/web/core/components/cycles/active-cycle/cycle-stats.tsx +++ b/web/core/components/cycles/active-cycle/cycle-stats.tsx @@ -30,11 +30,12 @@ import useLocalStorage from "@/hooks/use-local-storage"; export type ActiveCycleStatsProps = { workspaceSlug: string; projectId: string; - cycle: ICycle; + cycle: ICycle | null; + cycleId?: string | null; }; export const ActiveCycleStats: FC = observer((props) => { - const { workspaceSlug, projectId, cycle } = props; + const { workspaceSlug, projectId, cycle, cycleId } = props; const { storedValue: tab, setValue: setTab } = useLocalStorage("activeCycleTab", "Assignees"); @@ -63,22 +64,22 @@ export const ActiveCycleStats: FC = observer((props) => { const { currentProjectDetails } = useProject(); useSWR( - workspaceSlug && projectId && cycle.id ? CYCLE_ISSUES_WITH_PARAMS(cycle.id, { priority: "urgent,high" }) : null, - workspaceSlug && projectId && cycle.id - ? () => fetchActiveCycleIssues(workspaceSlug, projectId, 30, cycle.id) - : null, + workspaceSlug && projectId && cycleId ? CYCLE_ISSUES_WITH_PARAMS(cycleId, { priority: "urgent,high" }) : null, + workspaceSlug && projectId && cycleId ? () => fetchActiveCycleIssues(workspaceSlug, projectId, 30, cycleId) : null, { revalidateIfStale: false, revalidateOnFocus: false } ); - const cycleIssueDetails = getActiveCycleById(cycle.id); + const cycleIssueDetails = cycleId && getActiveCycleById(cycleId); const loadMoreIssues = useCallback(() => { - fetchNextActiveCycleIssues(workspaceSlug, projectId, cycle.id); - }, [workspaceSlug, projectId, cycle.id, issuesLoaderElement, cycleIssueDetails?.nextPageResults]); + if (!cycleId) return; + fetchNextActiveCycleIssues(workspaceSlug, projectId, cycleId); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [workspaceSlug, projectId, cycleId, issuesLoaderElement, cycleIssueDetails?.nextPageResults]); useIntersectionObserver(issuesContainerRef, issuesLoaderElement, loadMoreIssues, `0% 0% 100% 0%`); - return ( + return cycle && cycleId ? (
= observer((props) => {
+ ) : ( + + + ); }); diff --git a/web/core/components/cycles/active-cycle/productivity.tsx b/web/core/components/cycles/active-cycle/productivity.tsx index cc10fb808ac..0e44e66f8ba 100644 --- a/web/core/components/cycles/active-cycle/productivity.tsx +++ b/web/core/components/cycles/active-cycle/productivity.tsx @@ -2,7 +2,7 @@ import { FC, Fragment, useState } from "react"; import { observer } from "mobx-react"; import Link from "next/link"; import { ICycle, TCyclePlotType } from "@plane/types"; -import { CustomSelect, Spinner } from "@plane/ui"; +import { CustomSelect, Loader, Spinner } from "@plane/ui"; // components import ProgressChart from "@/components/core/sidebar/progress-chart"; import { EmptyState } from "@/components/empty-state"; @@ -15,7 +15,7 @@ import { EEstimateSystem } from "@/plane-web/constants/estimates"; export type ActiveCycleProductivityProps = { workspaceSlug: string; projectId: string; - cycle: ICycle; + cycle: ICycle | null; }; const cycleBurnDownChartOptions = [ @@ -51,10 +51,11 @@ export const ActiveCycleProductivity: FC = observe isCurrentProjectEstimateEnabled && currentActiveEstimateId && estimateById(currentActiveEstimateId); const isCurrentEstimateTypeIsPoints = estimateDetails && estimateDetails?.type === EEstimateSystem.POINTS; - const chartDistributionData = plotType === "points" ? cycle?.estimate_distribution : cycle?.distribution || undefined; + const chartDistributionData = + cycle && plotType === "points" ? cycle?.estimate_distribution : cycle?.distribution || undefined; const completionChartDistributionData = chartDistributionData?.completion_chart || undefined; - return ( + return cycle ? (
@@ -135,5 +136,9 @@ export const ActiveCycleProductivity: FC = observe )}
+ ) : ( + + + ); }); diff --git a/web/core/components/cycles/active-cycle/progress.tsx b/web/core/components/cycles/active-cycle/progress.tsx index ca03e2c0bc9..4f876b106e6 100644 --- a/web/core/components/cycles/active-cycle/progress.tsx +++ b/web/core/components/cycles/active-cycle/progress.tsx @@ -5,7 +5,7 @@ import Link from "next/link"; // types import { ICycle } from "@plane/types"; // ui -import { LinearProgressIndicator } from "@plane/ui"; +import { LinearProgressIndicator, Loader } from "@plane/ui"; // components import { EmptyState } from "@/components/empty-state"; // constants @@ -15,7 +15,7 @@ import { EmptyStateType } from "@/constants/empty-state"; export type ActiveCycleProgressProps = { workspaceSlug: string; projectId: string; - cycle: ICycle; + cycle: ICycle | null; }; export const ActiveCycleProgress: FC = (props) => { @@ -24,18 +24,20 @@ export const ActiveCycleProgress: FC = (props) => { const progressIndicatorData = CYCLE_STATE_GROUPS_DETAILS.map((group, index) => ({ id: index, name: group.title, - value: cycle.total_issues > 0 ? (cycle[group.key as keyof ICycle] as number) : 0, + value: cycle && cycle.total_issues > 0 ? (cycle[group.key as keyof ICycle] as number) : 0, color: group.color, })); - const groupedIssues: any = { - completed: cycle.completed_issues, - started: cycle.started_issues, - unstarted: cycle.unstarted_issues, - backlog: cycle.backlog_issues, - }; + const groupedIssues: any = cycle + ? { + completed: cycle.completed_issues, + started: cycle.started_issues, + unstarted: cycle.unstarted_issues, + backlog: cycle.backlog_issues, + } + : {}; - return ( + return cycle ? ( = (props) => {
)} + ) : ( + + + ); }; diff --git a/web/core/components/cycles/active-cycle/root.tsx b/web/core/components/cycles/active-cycle/root.tsx index 6294ae3cfac..56db623099f 100644 --- a/web/core/components/cycles/active-cycle/root.tsx +++ b/web/core/components/cycles/active-cycle/root.tsx @@ -28,7 +28,7 @@ export const ActiveCycleRoot: React.FC = observer((props) = // props const { workspaceSlug, projectId } = props; // store hooks - const { fetchActiveCycle, currentProjectActiveCycleId, getActiveCycleById } = useCycle(); + const { currentProjectActiveCycle, fetchActiveCycle, currentProjectActiveCycleId, getActiveCycleById } = useCycle(); // derived values const activeCycle = currentProjectActiveCycleId ? getActiveCycleById(currentProjectActiveCycleId) : null; // fetch active cycle details @@ -38,7 +38,7 @@ export const ActiveCycleRoot: React.FC = observer((props) = ); // show loader if active cycle is loading - if (!activeCycle && isLoading) + if (!currentProjectActiveCycle && isLoading) return ( @@ -54,7 +54,7 @@ export const ActiveCycleRoot: React.FC = observer((props) = - {!activeCycle ? ( + {!currentProjectActiveCycle ? ( ) : (
@@ -75,7 +75,12 @@ export const ActiveCycleRoot: React.FC = observer((props) = projectId={projectId} cycle={activeCycle} /> - + {" "}
diff --git a/web/core/store/cycle.store.ts b/web/core/store/cycle.store.ts index 4c1541a7897..e47bac6ef02 100644 --- a/web/core/store/cycle.store.ts +++ b/web/core/store/cycle.store.ts @@ -32,6 +32,8 @@ export interface ICycleStore { currentProjectDraftCycleIds: string[] | null; currentProjectActiveCycleId: string | null; currentProjectArchivedCycleIds: string[] | null; + currentProjectActiveCycle: ICycle | null; + // computed actions getFilteredCycleIds: (projectId: string, sortByManual: boolean) => string[] | null; getFilteredCompletedCycleIds: (projectId: string) => string[] | null; @@ -100,6 +102,8 @@ export class CycleStore implements ICycleStore { currentProjectDraftCycleIds: computed, currentProjectActiveCycleId: computed, currentProjectArchivedCycleIds: computed, + currentProjectActiveCycle: computed, + // actions setPlotType: action, fetchWorkspaceCycles: action, @@ -208,7 +212,7 @@ export class CycleStore implements ICycleStore { get currentProjectActiveCycleId() { const projectId = this.rootStore.router.projectId; if (!projectId) return null; - const activeCycle = Object.keys(this.activeCycleIdMap ?? {}).find( + const activeCycle = Object.keys(this.cycleMap ?? {}).find( (cycleId) => this.cycleMap?.[cycleId]?.project_id === projectId ); return activeCycle || null; @@ -228,6 +232,12 @@ export class CycleStore implements ICycleStore { return archivedCycleIds; } + get currentProjectActiveCycle() { + const projectId = this.rootStore.router.projectId; + if (!projectId && !this.currentProjectActiveCycleId) return null; + return this.cycleMap?.[this.currentProjectActiveCycleId!] ?? null; + } + /** * @description returns filtered cycle ids based on display filters and filters * @param {TCycleDisplayFilters} displayFilters From d59d9ba32f247332a513fc8a3c3f880fd62063db Mon Sep 17 00:00:00 2001 From: gakshita Date: Mon, 22 Jul 2024 19:47:20 +0530 Subject: [PATCH 2/6] fix: ts error --- web/core/components/cycles/active-cycle/cycle-stats.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web/core/components/cycles/active-cycle/cycle-stats.tsx b/web/core/components/cycles/active-cycle/cycle-stats.tsx index 38555494d65..6d1b5b757e6 100644 --- a/web/core/components/cycles/active-cycle/cycle-stats.tsx +++ b/web/core/components/cycles/active-cycle/cycle-stats.tsx @@ -69,7 +69,7 @@ export const ActiveCycleStats: FC = observer((props) => { { revalidateIfStale: false, revalidateOnFocus: false } ); - const cycleIssueDetails = cycleId && getActiveCycleById(cycleId); + const cycleIssueDetails = cycleId ? getActiveCycleById(cycleId) : { nextPageResults: false }; const loadMoreIssues = useCallback(() => { if (!cycleId) return; @@ -155,7 +155,7 @@ export const ActiveCycleStats: FC = observer((props) => { ref={issuesContainerRef} className="flex flex-col gap-1 h-full w-full overflow-y-auto vertical-scrollbar scrollbar-sm" > - {cycleIssueDetails && cycleIssueDetails.issueIds ? ( + {cycleIssueDetails && "issueIds" in cycleIssueDetails ? ( cycleIssueDetails.issueCount > 0 ? ( <> {cycleIssueDetails.issueIds.map((issueId: string) => { From 2909d72ca5bae1639fc86451b5f0074d9624767f Mon Sep 17 00:00:00 2001 From: gakshita Date: Tue, 23 Jul 2024 12:11:21 +0530 Subject: [PATCH 3/6] fix: types added along with apis --- packages/types/src/cycle/cycle.d.ts | 155 ++++++------- packages/types/src/workspace.d.ts | 331 +++++++++++++++------------- web/core/services/cycle.service.ts | 55 ++++- 3 files changed, 310 insertions(+), 231 deletions(-) diff --git a/packages/types/src/cycle/cycle.d.ts b/packages/types/src/cycle/cycle.d.ts index b0528ccc113..9ea68be8e87 100644 --- a/packages/types/src/cycle/cycle.d.ts +++ b/packages/types/src/cycle/cycle.d.ts @@ -1,114 +1,119 @@ -import type { TIssue, IIssueFilterOptions } from "@plane/types"; +import type {TIssue, IIssueFilterOptions} from "@plane/types"; export type TCycleGroups = "current" | "upcoming" | "completed" | "draft"; export type TCycleCompletionChartDistribution = { - [key: string]: number | null; + [key: string]: number | null; }; export type TCycleDistributionBase = { - total_issues: number; - pending_issues: number; - completed_issues: number; + total_issues: number; + pending_issues: number; + completed_issues: number; }; export type TCycleEstimateDistributionBase = { - total_estimates: number; - pending_estimates: number; - completed_estimates: number; + total_estimates: number; + pending_estimates: number; + completed_estimates: number; }; export type TCycleAssigneesDistribution = { - assignee_id: string | null; - avatar: string | null; - first_name: string | null; - last_name: string | null; - display_name: string | null; + assignee_id: string | null; + avatar: string | null; + first_name: string | null; + last_name: string | null; + display_name: string | null; }; export type TCycleLabelsDistribution = { - color: string | null; - label_id: string | null; - label_name: string | null; + color: string | null; + label_id: string | null; + label_name: string | null; }; export type TCycleDistribution = { - assignees: (TCycleAssigneesDistribution & TCycleDistributionBase)[]; - completion_chart: TCycleCompletionChartDistribution; - labels: (TCycleLabelsDistribution & TCycleDistributionBase)[]; + assignees: (TCycleAssigneesDistribution & TCycleDistributionBase)[]; + completion_chart: TCycleCompletionChartDistribution; + labels: (TCycleLabelsDistribution & TCycleDistributionBase)[]; }; export type TCycleEstimateDistribution = { - assignees: (TCycleAssigneesDistribution & TCycleEstimateDistributionBase)[]; - completion_chart: TCycleCompletionChartDistribution; - labels: (TCycleLabelsDistribution & TCycleEstimateDistributionBase)[]; + assignees: (TCycleAssigneesDistribution & TCycleEstimateDistributionBase)[]; + completion_chart: TCycleCompletionChartDistribution; + labels: (TCycleLabelsDistribution & TCycleEstimateDistributionBase)[]; }; export type TProgressSnapshot = { - total_issues: number; - completed_issues: number; - backlog_issues: number; - started_issues: number; - unstarted_issues: number; - cancelled_issues: number; - total_estimate_points?: number; - completed_estimate_points?: number; - backlog_estimate_points: number; - started_estimate_points: number; - unstarted_estimate_points: number; - cancelled_estimate_points: number; - distribution?: TCycleDistribution; - estimate_distribution?: TCycleEstimateDistribution; + total_issues: number; + completed_issues: number; + backlog_issues: number; + started_issues: number; + unstarted_issues: number; + cancelled_issues: number; + total_estimate_points?: number; + completed_estimate_points?: number; + backlog_estimate_points: number; + started_estimate_points: number; + unstarted_estimate_points: number; + cancelled_estimate_points: number; + distribution?: TCycleDistribution; + estimate_distribution?: TCycleEstimateDistribution; }; +export interface IProjectDetails { + id: string; +} + export interface ICycle extends TProgressSnapshot { - progress_snapshot: TProgressSnapshot | undefined; - - created_at?: string; - created_by?: string; - description: string; - end_date: string | null; - id: string; - is_favorite?: boolean; - name: string; - owned_by_id: string; - project_id: string; - status?: TCycleGroups; - sort_order: number; - start_date: string | null; - sub_issues?: number; - updated_at?: string; - updated_by?: string; - archived_at: string | null; - assignee_ids?: string[]; - view_props: { - filters: IIssueFilterOptions; - }; - workspace_id: string; + progress_snapshot: TProgressSnapshot | undefined; + + created_at?: string; + created_by?: string; + description: string; + end_date: string | null; + id: string; + is_favorite?: boolean; + name: string; + owned_by_id: string; + project_id: string; + status?: TCycleGroups; + sort_order: number; + start_date: string | null; + sub_issues?: number; + updated_at?: string; + updated_by?: string; + archived_at: string | null; + assignee_ids?: string[]; + view_props: { + filters: IIssueFilterOptions; + }; + workspace_id: string; + project_detail: IProjectDetails; } export interface CycleIssueResponse { - id: string; - issue_detail: TIssue; - created_at: Date; - updated_at: Date; - created_by: string; - updated_by: string; - project: string; - workspace: string; - issue: string; - cycle: string; - sub_issues_count: number; + id: string; + issue_detail: TIssue; + created_at: Date; + updated_at: Date; + created_by: string; + updated_by: string; + project: string; + workspace: string; + issue: string; + cycle: string; + sub_issues_count: number; } export type SelectCycleType = - | (ICycle & { actionType: "edit" | "delete" | "create-issue" }) - | undefined; + | (ICycle & {actionType: "edit" | "delete" | "create-issue"}) + | undefined; export type CycleDateCheckData = { - start_date: string; - end_date: string; - cycle_id?: string; + start_date: string; + end_date: string; + cycle_id?: string; }; export type TCyclePlotType = "burndown" | "points"; diff --git a/packages/types/src/workspace.d.ts b/packages/types/src/workspace.d.ts index de653a36030..9acab9b9e49 100644 --- a/packages/types/src/workspace.d.ts +++ b/packages/types/src/workspace.d.ts @@ -1,199 +1,224 @@ -import { EUserWorkspaceRoles } from "@/constants/workspace"; +import {EUserWorkspaceRoles} from "@/constants/workspace"; import type { - IProjectMember, - IUser, - IUserLite, - IWorkspaceViewProps, + ICycle, + IProjectMember, + IUser, + IUserLite, + IWorkspaceViewProps, } from "@plane/types"; export interface IWorkspace { - readonly id: string; - readonly owner: IUser; - readonly created_at: Date; - readonly updated_at: Date; - name: string; - url: string; - logo: string | null; - slug: string; - readonly total_members: number; - readonly slug: string; - readonly created_by: string; - readonly updated_by: string; - organization_size: string; - total_issues: number; + readonly id: string; + readonly owner: IUser; + readonly created_at: Date; + readonly updated_at: Date; + name: string; + url: string; + logo: string | null; + slug: string; + readonly total_members: number; + readonly slug: string; + readonly created_by: string; + readonly updated_by: string; + organization_size: string; + total_issues: number; } export interface IWorkspaceLite { - readonly id: string; - name: string; - slug: string; + readonly id: string; + name: string; + slug: string; } export interface IWorkspaceMemberInvitation { - accepted: boolean; - email: string; - id: string; - message: string; - responded_at: Date; - role: EUserWorkspaceRoles; - token: string; - workspace: { - id: string; - logo: string; - name: string; - slug: string; - }; + accepted: boolean; + email: string; + id: string; + message: string; + responded_at: Date; + role: EUserWorkspaceRoles; + token: string; + workspace: { + id: string; + logo: string; + name: string; + slug: string; + }; } export interface IWorkspaceBulkInviteFormData { - emails: { email: string; role: EUserWorkspaceRoles }[]; + emails: {email: string; role: EUserWorkspaceRoles}[]; } export type Properties = { - assignee: boolean; - start_date: boolean; - due_date: boolean; - labels: boolean; - key: boolean; - priority: boolean; - state: boolean; - sub_issue_count: boolean; - link: boolean; - attachment_count: boolean; - estimate: boolean; - created_on: boolean; - updated_on: boolean; + assignee: boolean; + start_date: boolean; + due_date: boolean; + labels: boolean; + key: boolean; + priority: boolean; + state: boolean; + sub_issue_count: boolean; + link: boolean; + attachment_count: boolean; + estimate: boolean; + created_on: boolean; + updated_on: boolean; }; export interface IWorkspaceMember { - id: string; - member: IUserLite; - role: EUserWorkspaceRoles; - created_at?: string; - avatar?: string; - email?: string; - first_name?: string; - last_name?: string; - joining_date?: string; - display_name?: string; + id: string; + member: IUserLite; + role: EUserWorkspaceRoles; + created_at?: string; + avatar?: string; + email?: string; + first_name?: string; + last_name?: string; + joining_date?: string; + display_name?: string; } export interface IWorkspaceMemberMe { - company_role: string | null; - created_at: Date; - created_by: string; - default_props: IWorkspaceViewProps; - id: string; - member: string; - role: EUserWorkspaceRoles; - updated_at: Date; - updated_by: string; - view_props: IWorkspaceViewProps; - workspace: string; + company_role: string | null; + created_at: Date; + created_by: string; + default_props: IWorkspaceViewProps; + id: string; + member: string; + role: EUserWorkspaceRoles; + updated_at: Date; + updated_by: string; + view_props: IWorkspaceViewProps; + workspace: string; } export interface ILastActiveWorkspaceDetails { - workspace_details: IWorkspace; - project_details?: IProjectMember[]; + workspace_details: IWorkspace; + project_details?: IProjectMember[]; } export interface IWorkspaceDefaultSearchResult { - id: string; - name: string; - project_id: string; - project__identifier: string; - workspace__slug: string; + id: string; + name: string; + project_id: string; + project__identifier: string; + workspace__slug: string; } export interface IWorkspaceSearchResult { - id: string; - name: string; - slug: string; + id: string; + name: string; + slug: string; } export interface IWorkspaceIssueSearchResult { - id: string; - name: string; - project__identifier: string; - project_id: string; - sequence_id: number; - workspace__slug: string; + id: string; + name: string; + project__identifier: string; + project_id: string; + sequence_id: number; + workspace__slug: string; } export interface IWorkspacePageSearchResult { - id: string; - name: string; - project_ids: string[]; - project__identifiers: string[]; - workspace__slug: string; + id: string; + name: string; + project_ids: string[]; + project__identifiers: string[]; + workspace__slug: string; } export interface IWorkspaceProjectSearchResult { - id: string; - identifier: string; - name: string; - workspace__slug: string; + id: string; + identifier: string; + name: string; + workspace__slug: string; } export interface IWorkspaceSearchResults { - results: { - workspace: IWorkspaceSearchResult[]; - project: IWorkspaceProjectSearchResult[]; - issue: IWorkspaceIssueSearchResult[]; - cycle: IWorkspaceDefaultSearchResult[]; - module: IWorkspaceDefaultSearchResult[]; - issue_view: IWorkspaceDefaultSearchResult[]; - page: IWorkspacePageSearchResult[]; - }; + results: { + workspace: IWorkspaceSearchResult[]; + project: IWorkspaceProjectSearchResult[]; + issue: IWorkspaceIssueSearchResult[]; + cycle: IWorkspaceDefaultSearchResult[]; + module: IWorkspaceDefaultSearchResult[]; + issue_view: IWorkspaceDefaultSearchResult[]; + page: IWorkspacePageSearchResult[]; + }; } export interface IProductUpdateResponse { - url: string; - assets_url: string; - upload_url: string; - html_url: string; - id: number; - author: { - login: string; - id: string; - node_id: string; - avatar_url: string; - gravatar_id: ""; - url: string; - html_url: string; - followers_url: string; - following_url: string; - gists_url: string; - starred_url: string; - subscriptions_url: string; - organizations_url: string; - repos_url: string; - events_url: string; - received_events_url: string; - type: string; - site_admin: false; - }; - node_id: string; - tag_name: string; - target_commitish: string; - name: string; - draft: boolean; - prerelease: true; - created_at: string; - published_at: string; - assets: []; - tarball_url: string; - zipball_url: string; - body: string; - reactions: { - url: string; - total_count: number; - "+1": number; - "-1": number; - laugh: number; - hooray: number; - confused: number; - heart: number; - rocket: number; - eyes: number; - }; + url: string; + assets_url: string; + upload_url: string; + html_url: string; + id: number; + author: { + login: string; + id: string; + node_id: string; + avatar_url: string; + gravatar_id: ""; + url: string; + html_url: string; + followers_url: string; + following_url: string; + gists_url: string; + starred_url: string; + subscriptions_url: string; + organizations_url: string; + repos_url: string; + events_url: string; + received_events_url: string; + type: string; + site_admin: false; + }; + node_id: string; + tag_name: string; + target_commitish: string; + name: string; + draft: boolean; + prerelease: true; + created_at: string; + published_at: string; + assets: []; + tarball_url: string; + zipball_url: string; + body: string; + reactions: { + url: string; + total_count: number; + "+1": number; + "-1": number; + laugh: number; + hooray: number; + confused: number; + heart: number; + rocket: number; + eyes: number; + }; +} + +export interface IWorkspaceActiveCyclesResponse { + count: number; + extra_stats: null; + next_cursor: string; + next_page_results: boolean; + prev_cursor: string; + prev_page_results: boolean; + results: ICycle[]; + total_pages: number; +} + +export interface IWorkspaceProgressResponse { + completed_issues: number; + cancelled_issues: number; + total_issues: number; + started_issues: number; + cancelled_issues: number; + unstarted_issues: number; + unstarted_issues: number; +} +export interface IWorkspaceAnalyticsResponse { + completion_chart: any; } diff --git a/web/core/services/cycle.service.ts b/web/core/services/cycle.service.ts index c74f6485ad8..5d4b9d7b8b3 100644 --- a/web/core/services/cycle.service.ts +++ b/web/core/services/cycle.service.ts @@ -1,15 +1,64 @@ // services -import type { CycleDateCheckData, ICycle, TIssuesResponse } from "@plane/types"; +import type { + CycleDateCheckData, + ICycle, + TIssuesResponse, + IWorkspaceActiveCyclesResponse, + IWorkspaceProgressResponse, + IWorkspaceAnalyticsResponse, +} from "@plane/types"; import { API_BASE_URL } from "@/helpers/common.helper"; import { APIService } from "@/services/api.service"; -// types -// helpers export class CycleService extends APIService { constructor() { super(API_BASE_URL); } + async workspaceActiveCyclesAnalytics( + workspaceSlug: string, + projectId: string, + cycleId: string, + analytic_type: string = "points" + ): Promise { + return this.get( + `/api/workspaces/${workspaceSlug}/projects/${projectId}/cycles/${cycleId}/analytics?type=${analytic_type}` + ) + .then((res) => res?.data) + .catch((err) => { + throw err?.response?.data; + }); + } + + async workspaceActiveCyclesProgress( + workspaceSlug: string, + projectId: string, + cycleId: string + ): Promise { + return this.get(`/api/workspaces/${workspaceSlug}/projects/${projectId}/cycles/${cycleId}/progress/`) + .then((res) => res?.data) + .catch((err) => { + throw err?.response?.data; + }); + } + + async workspaceActiveCycles( + workspaceSlug: string, + cursor: string, + per_page: number + ): Promise { + return this.get(`/api/workspaces/${workspaceSlug}/active-cycles/`, { + params: { + per_page, + cursor, + }, + }) + .then((res) => res?.data) + .catch((err) => { + throw err?.response?.data; + }); + } + async getWorkspaceCycles(workspaceSlug: string): Promise { return this.get(`/api/workspaces/${workspaceSlug}/cycles/`) .then((response) => response?.data) From f06f884c2b86f7105f1af36e0aeabff4fb3b8134 Mon Sep 17 00:00:00 2001 From: gakshita Date: Tue, 23 Jul 2024 12:20:19 +0530 Subject: [PATCH 4/6] fix: formatting --- packages/types/src/cycle/cycle.d.ts | 152 ++++++------- packages/types/src/workspace.d.ts | 336 ++++++++++++++-------------- 2 files changed, 243 insertions(+), 245 deletions(-) diff --git a/packages/types/src/cycle/cycle.d.ts b/packages/types/src/cycle/cycle.d.ts index 9ea68be8e87..a2b3814ed3c 100644 --- a/packages/types/src/cycle/cycle.d.ts +++ b/packages/types/src/cycle/cycle.d.ts @@ -3,117 +3,117 @@ import type {TIssue, IIssueFilterOptions} from "@plane/types"; export type TCycleGroups = "current" | "upcoming" | "completed" | "draft"; export type TCycleCompletionChartDistribution = { - [key: string]: number | null; + [key: string]: number | null; }; export type TCycleDistributionBase = { - total_issues: number; - pending_issues: number; - completed_issues: number; + total_issues: number; + pending_issues: number; + completed_issues: number; }; export type TCycleEstimateDistributionBase = { - total_estimates: number; - pending_estimates: number; - completed_estimates: number; + total_estimates: number; + pending_estimates: number; + completed_estimates: number; }; export type TCycleAssigneesDistribution = { - assignee_id: string | null; - avatar: string | null; - first_name: string | null; - last_name: string | null; - display_name: string | null; + assignee_id: string | null; + avatar: string | null; + first_name: string | null; + last_name: string | null; + display_name: string | null; }; export type TCycleLabelsDistribution = { - color: string | null; - label_id: string | null; - label_name: string | null; + color: string | null; + label_id: string | null; + label_name: string | null; }; export type TCycleDistribution = { - assignees: (TCycleAssigneesDistribution & TCycleDistributionBase)[]; - completion_chart: TCycleCompletionChartDistribution; - labels: (TCycleLabelsDistribution & TCycleDistributionBase)[]; + assignees: (TCycleAssigneesDistribution & TCycleDistributionBase)[]; + completion_chart: TCycleCompletionChartDistribution; + labels: (TCycleLabelsDistribution & TCycleDistributionBase)[]; }; export type TCycleEstimateDistribution = { - assignees: (TCycleAssigneesDistribution & TCycleEstimateDistributionBase)[]; - completion_chart: TCycleCompletionChartDistribution; - labels: (TCycleLabelsDistribution & TCycleEstimateDistributionBase)[]; + assignees: (TCycleAssigneesDistribution & TCycleEstimateDistributionBase)[]; + completion_chart: TCycleCompletionChartDistribution; + labels: (TCycleLabelsDistribution & TCycleEstimateDistributionBase)[]; }; export type TProgressSnapshot = { - total_issues: number; - completed_issues: number; - backlog_issues: number; - started_issues: number; - unstarted_issues: number; - cancelled_issues: number; - total_estimate_points?: number; - completed_estimate_points?: number; - backlog_estimate_points: number; - started_estimate_points: number; - unstarted_estimate_points: number; - cancelled_estimate_points: number; - distribution?: TCycleDistribution; - estimate_distribution?: TCycleEstimateDistribution; + total_issues: number; + completed_issues: number; + backlog_issues: number; + started_issues: number; + unstarted_issues: number; + cancelled_issues: number; + total_estimate_points?: number; + completed_estimate_points?: number; + backlog_estimate_points: number; + started_estimate_points: number; + unstarted_estimate_points: number; + cancelled_estimate_points: number; + distribution?: TCycleDistribution; + estimate_distribution?: TCycleEstimateDistribution; }; export interface IProjectDetails { - id: string; + id: string; } export interface ICycle extends TProgressSnapshot { - progress_snapshot: TProgressSnapshot | undefined; - - created_at?: string; - created_by?: string; - description: string; - end_date: string | null; - id: string; - is_favorite?: boolean; - name: string; - owned_by_id: string; - project_id: string; - status?: TCycleGroups; - sort_order: number; - start_date: string | null; - sub_issues?: number; - updated_at?: string; - updated_by?: string; - archived_at: string | null; - assignee_ids?: string[]; - view_props: { - filters: IIssueFilterOptions; - }; - workspace_id: string; - project_detail: IProjectDetails; + progress_snapshot: TProgressSnapshot | undefined; + + created_at?: string; + created_by?: string; + description: string; + end_date: string | null; + id: string; + is_favorite?: boolean; + name: string; + owned_by_id: string; + project_id: string; + status?: TCycleGroups; + sort_order: number; + start_date: string | null; + sub_issues?: number; + updated_at?: string; + updated_by?: string; + archived_at: string | null; + assignee_ids?: string[]; + view_props: { + filters: IIssueFilterOptions; + }; + workspace_id: string; + project_detail: IProjectDetails; } export interface CycleIssueResponse { - id: string; - issue_detail: TIssue; - created_at: Date; - updated_at: Date; - created_by: string; - updated_by: string; - project: string; - workspace: string; - issue: string; - cycle: string; - sub_issues_count: number; + id: string; + issue_detail: TIssue; + created_at: Date; + updated_at: Date; + created_by: string; + updated_by: string; + project: string; + workspace: string; + issue: string; + cycle: string; + sub_issues_count: number; } export type SelectCycleType = - | (ICycle & {actionType: "edit" | "delete" | "create-issue"}) - | undefined; + | (ICycle & {actionType: "edit" | "delete" | "create-issue"}) + | undefined; export type CycleDateCheckData = { - start_date: string; - end_date: string; - cycle_id?: string; + start_date: string; + end_date: string; + cycle_id?: string; }; export type TCyclePlotType = "burndown" | "points"; diff --git a/packages/types/src/workspace.d.ts b/packages/types/src/workspace.d.ts index 9acab9b9e49..f5cec8eadb4 100644 --- a/packages/types/src/workspace.d.ts +++ b/packages/types/src/workspace.d.ts @@ -1,224 +1,222 @@ import {EUserWorkspaceRoles} from "@/constants/workspace"; import type { - ICycle, - IProjectMember, - IUser, - IUserLite, - IWorkspaceViewProps, + ICycle, + IProjectMember, + IUser, + IUserLite, + IWorkspaceViewProps, } from "@plane/types"; export interface IWorkspace { - readonly id: string; - readonly owner: IUser; - readonly created_at: Date; - readonly updated_at: Date; - name: string; - url: string; - logo: string | null; - slug: string; - readonly total_members: number; - readonly slug: string; - readonly created_by: string; - readonly updated_by: string; - organization_size: string; - total_issues: number; + readonly id: string; + readonly owner: IUser; + readonly created_at: Date; + readonly updated_at: Date; + name: string; + url: string; + logo: string | null; + slug: string; + readonly total_members: number; + readonly slug: string; + readonly created_by: string; + readonly updated_by: string; + organization_size: string; + total_issues: number; } export interface IWorkspaceLite { - readonly id: string; - name: string; - slug: string; + readonly id: string; + name: string; + slug: string; } export interface IWorkspaceMemberInvitation { - accepted: boolean; - email: string; - id: string; - message: string; - responded_at: Date; - role: EUserWorkspaceRoles; - token: string; - workspace: { - id: string; - logo: string; - name: string; - slug: string; - }; + accepted: boolean; + email: string; + id: string; + message: string; + responded_at: Date; + role: EUserWorkspaceRoles; + token: string; + workspace: { + id: string; + logo: string; + name: string; + slug: string; + }; } export interface IWorkspaceBulkInviteFormData { - emails: {email: string; role: EUserWorkspaceRoles}[]; + emails: {email: string; role: EUserWorkspaceRoles}[]; } export type Properties = { - assignee: boolean; - start_date: boolean; - due_date: boolean; - labels: boolean; - key: boolean; - priority: boolean; - state: boolean; - sub_issue_count: boolean; - link: boolean; - attachment_count: boolean; - estimate: boolean; - created_on: boolean; - updated_on: boolean; + assignee: boolean; + start_date: boolean; + due_date: boolean; + labels: boolean; + key: boolean; + priority: boolean; + state: boolean; + sub_issue_count: boolean; + link: boolean; + attachment_count: boolean; + estimate: boolean; + created_on: boolean; + updated_on: boolean; }; export interface IWorkspaceMember { - id: string; - member: IUserLite; - role: EUserWorkspaceRoles; - created_at?: string; - avatar?: string; - email?: string; - first_name?: string; - last_name?: string; - joining_date?: string; - display_name?: string; + id: string; + member: IUserLite; + role: EUserWorkspaceRoles; + created_at?: string; + avatar?: string; + email?: string; + first_name?: string; + last_name?: string; + joining_date?: string; + display_name?: string; } export interface IWorkspaceMemberMe { - company_role: string | null; - created_at: Date; - created_by: string; - default_props: IWorkspaceViewProps; - id: string; - member: string; - role: EUserWorkspaceRoles; - updated_at: Date; - updated_by: string; - view_props: IWorkspaceViewProps; - workspace: string; + company_role: string | null; + created_at: Date; + created_by: string; + default_props: IWorkspaceViewProps; + id: string; + member: string; + role: EUserWorkspaceRoles; + updated_at: Date; + updated_by: string; + view_props: IWorkspaceViewProps; + workspace: string; } export interface ILastActiveWorkspaceDetails { - workspace_details: IWorkspace; - project_details?: IProjectMember[]; + workspace_details: IWorkspace; + project_details?: IProjectMember[]; } export interface IWorkspaceDefaultSearchResult { - id: string; - name: string; - project_id: string; - project__identifier: string; - workspace__slug: string; + id: string; + name: string; + project_id: string; + project__identifier: string; + workspace__slug: string; } export interface IWorkspaceSearchResult { - id: string; - name: string; - slug: string; + id: string; + name: string; + slug: string; } export interface IWorkspaceIssueSearchResult { - id: string; - name: string; - project__identifier: string; - project_id: string; - sequence_id: number; - workspace__slug: string; + id: string; + name: string; + project__identifier: string; + project_id: string; + sequence_id: number; + workspace__slug: string; } export interface IWorkspacePageSearchResult { - id: string; - name: string; - project_ids: string[]; - project__identifiers: string[]; - workspace__slug: string; + id: string; + name: string; + project_ids: string[]; + project__identifiers: string[]; + workspace__slug: string; } export interface IWorkspaceProjectSearchResult { - id: string; - identifier: string; - name: string; - workspace__slug: string; + id: string; + identifier: string; + name: string; + workspace__slug: string; } export interface IWorkspaceSearchResults { - results: { - workspace: IWorkspaceSearchResult[]; - project: IWorkspaceProjectSearchResult[]; - issue: IWorkspaceIssueSearchResult[]; - cycle: IWorkspaceDefaultSearchResult[]; - module: IWorkspaceDefaultSearchResult[]; - issue_view: IWorkspaceDefaultSearchResult[]; - page: IWorkspacePageSearchResult[]; - }; + results: { + workspace: IWorkspaceSearchResult[]; + project: IWorkspaceProjectSearchResult[]; + issue: IWorkspaceIssueSearchResult[]; + cycle: IWorkspaceDefaultSearchResult[]; + module: IWorkspaceDefaultSearchResult[]; + issue_view: IWorkspaceDefaultSearchResult[]; + page: IWorkspacePageSearchResult[]; + }; } export interface IProductUpdateResponse { - url: string; - assets_url: string; - upload_url: string; - html_url: string; - id: number; - author: { - login: string; - id: string; - node_id: string; - avatar_url: string; - gravatar_id: ""; - url: string; - html_url: string; - followers_url: string; - following_url: string; - gists_url: string; - starred_url: string; - subscriptions_url: string; - organizations_url: string; - repos_url: string; - events_url: string; - received_events_url: string; - type: string; - site_admin: false; - }; - node_id: string; - tag_name: string; - target_commitish: string; - name: string; - draft: boolean; - prerelease: true; - created_at: string; - published_at: string; - assets: []; - tarball_url: string; - zipball_url: string; - body: string; - reactions: { - url: string; - total_count: number; - "+1": number; - "-1": number; - laugh: number; - hooray: number; - confused: number; - heart: number; - rocket: number; - eyes: number; - }; + url: string; + assets_url: string; + upload_url: string; + html_url: string; + id: number; + author: { + login: string; + id: string; + node_id: string; + avatar_url: string; + gravatar_id: ""; + url: string; + html_url: string; + followers_url: string; + following_url: string; + gists_url: string; + starred_url: string; + subscriptions_url: string; + organizations_url: string; + repos_url: string; + events_url: string; + received_events_url: string; + type: string; + site_admin: false; + }; + node_id: string; + tag_name: string; + target_commitish: string; + name: string; + draft: boolean; + prerelease: true; + created_at: string; + published_at: string; + assets: []; + tarball_url: string; + zipball_url: string; + body: string; + reactions: { + url: string; + total_count: number; + "+1": number; + "-1": number; + laugh: number; + hooray: number; + confused: number; + heart: number; + rocket: number; + eyes: number; + }; } export interface IWorkspaceActiveCyclesResponse { - count: number; - extra_stats: null; - next_cursor: string; - next_page_results: boolean; - prev_cursor: string; - prev_page_results: boolean; - results: ICycle[]; - total_pages: number; + count: number; + extra_stats: null; + next_cursor: string; + next_page_results: boolean; + prev_cursor: string; + prev_page_results: boolean; + results: ICycle[]; + total_pages: number; } export interface IWorkspaceProgressResponse { - completed_issues: number; - cancelled_issues: number; - total_issues: number; - started_issues: number; - cancelled_issues: number; - unstarted_issues: number; - unstarted_issues: number; + completed_issues: number; + total_issues: number; + started_issues: number; + cancelled_issues: number; + unstarted_issues: number; } export interface IWorkspaceAnalyticsResponse { - completion_chart: any; + completion_chart: any; } From f25f776497e481a45915a0d6145e2a80d86b7783 Mon Sep 17 00:00:00 2001 From: gakshita Date: Tue, 23 Jul 2024 12:36:18 +0530 Subject: [PATCH 5/6] fix: removed bottom border --- web/core/components/cycles/active-cycle/root.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/core/components/cycles/active-cycle/root.tsx b/web/core/components/cycles/active-cycle/root.tsx index 56db623099f..d65b5c585de 100644 --- a/web/core/components/cycles/active-cycle/root.tsx +++ b/web/core/components/cycles/active-cycle/root.tsx @@ -57,7 +57,7 @@ export const ActiveCycleRoot: React.FC = observer((props) = {!currentProjectActiveCycle ? ( ) : ( -
+
{currentProjectActiveCycleId && ( Date: Tue, 23 Jul 2024 12:55:46 +0530 Subject: [PATCH 6/6] fix: fixed loading state for cycle-stats --- .../cycles/active-cycle/cycle-stats.tsx | 141 ++++++++++-------- .../components/cycles/active-cycle/root.tsx | 2 +- 2 files changed, 79 insertions(+), 64 deletions(-) diff --git a/web/core/components/cycles/active-cycle/cycle-stats.tsx b/web/core/components/cycles/active-cycle/cycle-stats.tsx index 6d1b5b757e6..e93658de264 100644 --- a/web/core/components/cycles/active-cycle/cycle-stats.tsx +++ b/web/core/components/cycles/active-cycle/cycle-stats.tsx @@ -79,7 +79,14 @@ export const ActiveCycleStats: FC = observer((props) => { useIntersectionObserver(issuesContainerRef, issuesLoaderElement, loadMoreIssues, `0% 0% 100% 0%`); - return cycle && cycleId ? ( + const loaders = ( + + + + + + ); + return cycleId ? (
= observer((props) => {
) ) : ( - - - - - + loaders )}
@@ -243,44 +246,52 @@ export const ActiveCycleStats: FC = observer((props) => { as="div" className="flex h-52 w-full flex-col gap-1 overflow-y-auto text-custom-text-200 vertical-scrollbar scrollbar-sm" > - {cycle?.distribution?.assignees && cycle.distribution.assignees.length > 0 ? ( - cycle.distribution?.assignees?.map((assignee, index) => { - if (assignee.assignee_id) - return ( - - + {cycle ? ( + cycle?.distribution?.assignees && cycle.distribution.assignees.length > 0 ? ( + cycle.distribution?.assignees?.map((assignee, index) => { + if (assignee.assignee_id) + return ( + + - {assignee.display_name} -
- } - completed={assignee.completed_issues} - total={assignee.total_issues} - /> - ); - else - return ( - -
- User + {assignee.display_name}
- No assignee - - } - completed={assignee.completed_issues} - total={assignee.total_issues} - /> - ); - }) + } + completed={assignee.completed_issues} + total={assignee.total_issues} + /> + ); + else + return ( + +
+ User +
+ No assignee + + } + completed={assignee.completed_issues} + total={assignee.total_issues} + /> + ); + }) + ) : ( +
+ +
+ ) ) : ( -
- -
+ loaders )} @@ -288,29 +299,33 @@ export const ActiveCycleStats: FC = observer((props) => { as="div" className="flex h-52 w-full flex-col gap-1 overflow-y-auto text-custom-text-200 vertical-scrollbar scrollbar-sm" > - {cycle?.distribution?.labels && cycle.distribution.labels.length > 0 ? ( - cycle.distribution.labels?.map((label, index) => ( - - - {label.label_name ?? "No labels"} - - } - completed={label.completed_issues} - total={label.total_issues} - /> - )) + {cycle ? ( + cycle?.distribution?.labels && cycle.distribution.labels.length > 0 ? ( + cycle.distribution.labels?.map((label, index) => ( + + + {label.label_name ?? "No labels"} + + } + completed={label.completed_issues} + total={label.total_issues} + /> + )) + ) : ( +
+ +
+ ) ) : ( -
- -
+ loaders )} diff --git a/web/core/components/cycles/active-cycle/root.tsx b/web/core/components/cycles/active-cycle/root.tsx index d65b5c585de..c654e011dbd 100644 --- a/web/core/components/cycles/active-cycle/root.tsx +++ b/web/core/components/cycles/active-cycle/root.tsx @@ -80,7 +80,7 @@ export const ActiveCycleRoot: React.FC = observer((props) = projectId={projectId} cycle={activeCycle} cycleId={currentProjectActiveCycleId} - />{" "} + />