From b9e774bd20402c17bd53be452f4637660e280cd9 Mon Sep 17 00:00:00 2001 From: gakshita Date: Thu, 26 Sep 2024 16:43:24 +0530 Subject: [PATCH] fix: progress chart code splitting --- .../components/cycles/active-cycle/index.ts | 1 + .../components/cycles/active-cycle/root.tsx | 89 ++++ .../cycles/analytics-sidebar/index.ts | 1 + .../analytics-sidebar/sidebar-chart.tsx | 57 +++ web/ce/components/cycles/index.ts | 2 + .../components/cycles/active-cycle/index.ts | 1 - .../cycles/active-cycle/productivity.tsx | 22 +- .../cycles/active-cycle/use-cycles-details.ts | 2 +- .../cycles/analytics-sidebar/index.ts | 3 + .../analytics-sidebar/issue-progress.tsx | 165 +++---- .../analytics-sidebar/progress-stats.tsx | 24 +- .../cycles/analytics-sidebar/root.tsx | 441 ++---------------- .../analytics-sidebar/sidebar-details.tsx | 138 ++++++ .../analytics-sidebar/sidebar-header.tsx | 326 +++++++++++++ .../components/cycles/cycle-peek-overview.tsx | 8 +- .../cycles/list/cycle-list-item-action.tsx | 145 +++--- .../cycles/list/cycles-list-item.tsx | 8 - web/core/components/cycles/list/root.tsx | 8 +- web/core/services/cycle.service.ts | 12 + web/core/store/cycle.store.ts | 72 ++- web/core/store/issue/cycle/issue.store.ts | 4 + 21 files changed, 909 insertions(+), 620 deletions(-) create mode 100644 web/ce/components/cycles/active-cycle/index.ts create mode 100644 web/ce/components/cycles/active-cycle/root.tsx create mode 100644 web/ce/components/cycles/analytics-sidebar/index.ts create mode 100644 web/ce/components/cycles/analytics-sidebar/sidebar-chart.tsx create mode 100644 web/ce/components/cycles/index.ts create mode 100644 web/core/components/cycles/analytics-sidebar/sidebar-details.tsx create mode 100644 web/core/components/cycles/analytics-sidebar/sidebar-header.tsx diff --git a/web/ce/components/cycles/active-cycle/index.ts b/web/ce/components/cycles/active-cycle/index.ts new file mode 100644 index 00000000000..1efe34c51ec --- /dev/null +++ b/web/ce/components/cycles/active-cycle/index.ts @@ -0,0 +1 @@ +export * from "./root"; diff --git a/web/ce/components/cycles/active-cycle/root.tsx b/web/ce/components/cycles/active-cycle/root.tsx new file mode 100644 index 00000000000..a173cfda03a --- /dev/null +++ b/web/ce/components/cycles/active-cycle/root.tsx @@ -0,0 +1,89 @@ +"use client"; + +import { observer } from "mobx-react"; +import { Disclosure } from "@headlessui/react"; +// ui +import { Row } from "@plane/ui"; +// components +import { + ActiveCycleProductivity, + ActiveCycleProgress, + ActiveCycleStats, + CycleListGroupHeader, + CyclesListItem, +} from "@/components/cycles"; +import useCyclesDetails from "@/components/cycles/active-cycle/use-cycles-details"; +import { EmptyState } from "@/components/empty-state"; +// constants +import { EmptyStateType } from "@/constants/empty-state"; +import { useCycle } from "@/hooks/store"; +import { ActiveCycleIssueDetails } from "@/store/issue/cycle"; + +interface IActiveCycleDetails { + workspaceSlug: string; + projectId: string; +} + +export const ActiveCycleRoot: React.FC = observer((props) => { + const { workspaceSlug, projectId } = props; + const { currentProjectActiveCycle, currentProjectActiveCycleId } = useCycle(); + const { + handleFiltersUpdate, + cycle: activeCycle, + cycleIssueDetails, + } = useCyclesDetails({ workspaceSlug, projectId, cycleId: currentProjectActiveCycleId }); + + return ( + <> + + {({ open }) => ( + <> + + + + + {!currentProjectActiveCycle ? ( + + ) : ( +
+ {currentProjectActiveCycleId && ( + + )} + +
+ + + +
+
+
+ )} +
+ + )} +
+ + ); +}); diff --git a/web/ce/components/cycles/analytics-sidebar/index.ts b/web/ce/components/cycles/analytics-sidebar/index.ts new file mode 100644 index 00000000000..3ba38c61be5 --- /dev/null +++ b/web/ce/components/cycles/analytics-sidebar/index.ts @@ -0,0 +1 @@ +export * from "./sidebar-chart"; diff --git a/web/ce/components/cycles/analytics-sidebar/sidebar-chart.tsx b/web/ce/components/cycles/analytics-sidebar/sidebar-chart.tsx new file mode 100644 index 00000000000..e5b69ef24b1 --- /dev/null +++ b/web/ce/components/cycles/analytics-sidebar/sidebar-chart.tsx @@ -0,0 +1,57 @@ +import { Fragment } from "react"; +import { TCycleDistribution, TCycleEstimateDistribution } from "@plane/types"; +import { Loader } from "@plane/ui"; +import ProgressChart from "@/components/core/sidebar/progress-chart"; + +type ProgressChartProps = { + chartDistributionData: TCycleEstimateDistribution | TCycleDistribution | undefined; + cycleStartDate: Date | undefined; + cycleEndDate: Date | undefined; + totalEstimatePoints: number; + totalIssues: number; + plotType: string; +}; +export const SidebarBaseChart = (props: ProgressChartProps) => { + const { chartDistributionData, cycleStartDate, cycleEndDate, totalEstimatePoints, totalIssues, plotType } = props; + const completionChartDistributionData = chartDistributionData?.completion_chart || undefined; + + return ( +
+
+
+ + Ideal +
+
+ + Current +
+
+ {cycleStartDate && cycleEndDate && completionChartDistributionData ? ( + + {plotType === "points" ? ( + + ) : ( + + )} + + ) : ( + + + + )} +
+ ); +}; diff --git a/web/ce/components/cycles/index.ts b/web/ce/components/cycles/index.ts new file mode 100644 index 00000000000..89934687567 --- /dev/null +++ b/web/ce/components/cycles/index.ts @@ -0,0 +1,2 @@ +export * from "./active-cycle"; +export * from "./analytics-sidebar"; diff --git a/web/core/components/cycles/active-cycle/index.ts b/web/core/components/cycles/active-cycle/index.ts index d88ccc3e8b6..c2197825207 100644 --- a/web/core/components/cycles/active-cycle/index.ts +++ b/web/core/components/cycles/active-cycle/index.ts @@ -1,4 +1,3 @@ -export * from "./root"; export * from "./header"; export * from "./stats"; export * from "./upcoming-cycles-list-item"; diff --git a/web/core/components/cycles/active-cycle/productivity.tsx b/web/core/components/cycles/active-cycle/productivity.tsx index 31a865eae38..1e70f326f40 100644 --- a/web/core/components/cycles/active-cycle/productivity.tsx +++ b/web/core/components/cycles/active-cycle/productivity.tsx @@ -1,7 +1,7 @@ import { FC, Fragment } from "react"; import { observer } from "mobx-react"; import Link from "next/link"; -import { ICycle, TCyclePlotType } from "@plane/types"; +import { ICycle, TCycleEstimateType, TCyclePlotType } from "@plane/types"; import { CustomSelect, Loader } from "@plane/ui"; // components import ProgressChart from "@/components/core/sidebar/progress-chart"; @@ -19,22 +19,22 @@ export type ActiveCycleProductivityProps = { }; const cycleBurnDownChartOptions = [ - { value: "burndown", label: "Issues" }, + { value: "issues", label: "Issues" }, { value: "points", label: "Points" }, ]; export const ActiveCycleProductivity: FC = observer((props) => { const { workspaceSlug, projectId, cycle } = props; // hooks - const { getPlotTypeByCycleId, setPlotType } = useCycle(); + const { getEstimateTypeByCycleId, setEstimateType } = useCycle(); const { currentActiveEstimateId, areEstimateEnabledByProjectId, estimateById } = useProjectEstimates(); // derived values - const plotType: TCyclePlotType = (cycle && getPlotTypeByCycleId(cycle.id)) || "burndown"; + const estimateType: TCycleEstimateType = (cycle && getEstimateTypeByCycleId(cycle.id)) || "issues"; - const onChange = async (value: TCyclePlotType) => { + const onChange = async (value: TCycleEstimateType) => { if (!workspaceSlug || !projectId || !cycle || !cycle.id) return; - setPlotType(cycle.id, value); + setEstimateType(cycle.id, value); }; const isCurrentProjectEstimateEnabled = projectId && areEstimateEnabledByProjectId(projectId) ? true : false; @@ -43,7 +43,7 @@ export const ActiveCycleProductivity: FC = observe const isCurrentEstimateTypeIsPoints = estimateDetails && estimateDetails?.type === EEstimateSystem.POINTS; const chartDistributionData = - cycle && plotType === "points" ? cycle?.estimate_distribution : cycle?.distribution || undefined; + cycle && estimateType === "points" ? cycle?.estimate_distribution : cycle?.distribution || undefined; const completionChartDistributionData = chartDistributionData?.completion_chart || undefined; return cycle && completionChartDistributionData ? ( @@ -55,8 +55,8 @@ export const ActiveCycleProductivity: FC = observe {isCurrentEstimateTypeIsPoints && (
{cycleBurnDownChartOptions.find((v) => v.value === plotType)?.label ?? "None"}} + value={estimateType} + label={{cycleBurnDownChartOptions.find((v) => v.value === estimateType)?.label ?? "None"}} onChange={onChange} maxHeight="lg" > @@ -85,7 +85,7 @@ export const ActiveCycleProductivity: FC = observe Current
- {plotType === "points" ? ( + {estimateType === "points" ? ( {`Pending points - ${cycle.backlog_estimate_points + cycle.unstarted_estimate_points + cycle.started_estimate_points}`} ) : ( {`Pending issues - ${cycle.backlog_issues + cycle.unstarted_issues + cycle.started_issues}`} @@ -95,7 +95,7 @@ export const ActiveCycleProductivity: FC = observe
{completionChartDistributionData && ( - {plotType === "points" ? ( + {estimateType === "points" ? ( { diff --git a/web/core/components/cycles/analytics-sidebar/index.ts b/web/core/components/cycles/analytics-sidebar/index.ts index c509152a2bf..eb3dc868e0a 100644 --- a/web/core/components/cycles/analytics-sidebar/index.ts +++ b/web/core/components/cycles/analytics-sidebar/index.ts @@ -1,3 +1,6 @@ export * from "./root"; export * from "./issue-progress"; export * from "./progress-stats"; +export * from "./root"; +export * from "./sidebar-header"; +export * from "./sidebar-details"; diff --git a/web/core/components/cycles/analytics-sidebar/issue-progress.tsx b/web/core/components/cycles/analytics-sidebar/issue-progress.tsx index ea44cce8156..f88df77b35d 100644 --- a/web/core/components/cycles/analytics-sidebar/issue-progress.tsx +++ b/web/core/components/cycles/analytics-sidebar/issue-progress.tsx @@ -5,12 +5,11 @@ import isEmpty from "lodash/isEmpty"; import isEqual from "lodash/isEqual"; import { observer } from "mobx-react"; import { useSearchParams } from "next/navigation"; -import { AlertCircle, ChevronUp, ChevronDown } from "lucide-react"; +import { ChevronUp, ChevronDown } from "lucide-react"; import { Disclosure, Transition } from "@headlessui/react"; -import { ICycle, IIssueFilterOptions, TCyclePlotType, TProgressSnapshot } from "@plane/types"; -import { CustomSelect, Loader, Spinner } from "@plane/ui"; +import { ICycle, IIssueFilterOptions, TCycleEstimateType, TCyclePlotType, TProgressSnapshot } from "@plane/types"; +import { CustomSelect } from "@plane/ui"; // components -import ProgressChart from "@/components/core/sidebar/progress-chart"; import { CycleProgressStats } from "@/components/cycles"; // constants import { EIssueFilterType, EIssuesStoreType } from "@/constants/issue"; @@ -19,6 +18,7 @@ import { getDate } from "@/helpers/date-time.helper"; // hooks import { useIssues, useCycle, useProjectEstimates } from "@/hooks/store"; // plane web constants +import { SidebarBaseChart } from "@/plane-web/components/cycles/analytics-sidebar/sidebar-chart"; import { EEstimateSystem } from "@/plane-web/constants/estimates"; type TCycleAnalyticsProgress = { @@ -27,11 +27,6 @@ type TCycleAnalyticsProgress = { cycleId: string; }; -const cycleBurnDownChartOptions = [ - { value: "burndown", label: "Issues" }, - { value: "points", label: "Points" }, -]; - const validateCycleSnapshot = (cycleDetails: ICycle | null): ICycle | null => { if (!cycleDetails || cycleDetails === null) return cycleDetails; @@ -47,6 +42,18 @@ const validateCycleSnapshot = (cycleDetails: ICycle | null): ICycle | null => { return updatedCycleDetails; }; +type options = { + value: string; + label: string; +}; +export const cycleChartOptions: options[] = [ + { value: "burndown", label: "Burn-down" }, + { value: "burnup", label: "Burn-up" }, +]; +export const cycleEstimateOptions: options[] = [ + { value: "issues", label: "issues" }, + { value: "points", label: "points" }, +]; export const CycleAnalyticsProgress: FC = observer((props) => { // props const { workspaceSlug, projectId, cycleId } = props; @@ -55,7 +62,15 @@ export const CycleAnalyticsProgress: FC = observer((pro const peekCycle = searchParams.get("peekCycle") || undefined; // hooks const { areEstimateEnabledByProjectId, currentActiveEstimateId, estimateById } = useProjectEstimates(); - const { getPlotTypeByCycleId, setPlotType, getCycleById, fetchCycleDetails, fetchArchivedCycleDetails } = useCycle(); + const { + getPlotTypeByCycleId, + getEstimateTypeByCycleId, + setPlotType, + getCycleById, + fetchCycleDetails, + fetchArchivedCycleDetails, + setEstimateType, + } = useCycle(); const { issuesFilter: { issueFilters, updateFilters }, } = useIssues(EIssuesStoreType.CYCLE); @@ -65,6 +80,7 @@ export const CycleAnalyticsProgress: FC = observer((pro // derived values const cycleDetails = validateCycleSnapshot(getCycleById(cycleId)); const plotType: TCyclePlotType = getPlotTypeByCycleId(cycleId); + const estimateType = getEstimateTypeByCycleId(cycleId); const isCurrentProjectEstimateEnabled = projectId && areEstimateEnabledByProjectId(projectId) ? true : false; const estimateDetails = isCurrentProjectEstimateEnabled && currentActiveEstimateId && estimateById(currentActiveEstimateId); @@ -76,7 +92,7 @@ export const CycleAnalyticsProgress: FC = observer((pro const totalEstimatePoints = cycleDetails?.total_estimate_points || 0; const progressHeaderPercentage = cycleDetails - ? plotType === "points" + ? estimateType === "points" ? completedEstimatePoints != 0 && totalEstimatePoints != 0 ? Math.round((completedEstimatePoints / totalEstimatePoints) * 100) : 0 @@ -86,21 +102,22 @@ export const CycleAnalyticsProgress: FC = observer((pro : 0; const chartDistributionData = - plotType === "points" ? cycleDetails?.estimate_distribution : cycleDetails?.distribution || undefined; - const completionChartDistributionData = chartDistributionData?.completion_chart || undefined; + estimateType === "points" ? cycleDetails?.estimate_distribution : cycleDetails?.distribution || undefined; const groupedIssues = useMemo( () => ({ - backlog: plotType === "points" ? cycleDetails?.backlog_estimate_points || 0 : cycleDetails?.backlog_issues || 0, + backlog: + estimateType === "points" ? cycleDetails?.backlog_estimate_points || 0 : cycleDetails?.backlog_issues || 0, unstarted: - plotType === "points" ? cycleDetails?.unstarted_estimate_points || 0 : cycleDetails?.unstarted_issues || 0, - started: plotType === "points" ? cycleDetails?.started_estimate_points || 0 : cycleDetails?.started_issues || 0, + estimateType === "points" ? cycleDetails?.unstarted_estimate_points || 0 : cycleDetails?.unstarted_issues || 0, + started: + estimateType === "points" ? cycleDetails?.started_estimate_points || 0 : cycleDetails?.started_issues || 0, completed: - plotType === "points" ? cycleDetails?.completed_estimate_points || 0 : cycleDetails?.completed_issues || 0, + estimateType === "points" ? cycleDetails?.completed_estimate_points || 0 : cycleDetails?.completed_issues || 0, cancelled: - plotType === "points" ? cycleDetails?.cancelled_estimate_points || 0 : cycleDetails?.cancelled_issues || 0, + estimateType === "points" ? cycleDetails?.cancelled_estimate_points || 0 : cycleDetails?.cancelled_issues || 0, }), - [plotType, cycleDetails] + [estimateType, cycleDetails] ); const cycleStartDate = getDate(cycleDetails?.start_date); @@ -111,8 +128,8 @@ export const CycleAnalyticsProgress: FC = observer((pro const isArchived = !!cycleDetails?.archived_at; // handlers - const onChange = async (value: TCyclePlotType) => { - setPlotType(cycleId, value); + const onChange = async (value: TCycleEstimateType) => { + setEstimateType(cycleId, value); if (!workspaceSlug || !projectId || !cycleId) return; try { setLoader(true); @@ -124,7 +141,7 @@ export const CycleAnalyticsProgress: FC = observer((pro setLoader(false); } catch (error) { setLoader(false); - setPlotType(cycleId, plotType); + setEstimateType(cycleId, estimateType); } }; @@ -161,40 +178,16 @@ export const CycleAnalyticsProgress: FC = observer((pro if (!cycleDetails) return <>; return ( -
+
{({ open }) => ( -
+
{/* progress bar header */} {isCycleDateValid ? (
Progress
- {progressHeaderPercentage > 0 && ( -
{`${progressHeaderPercentage}%`}
- )}
- {isCurrentEstimateTypeIsPoints && ( - <> -
- {cycleBurnDownChartOptions.find((v) => v.value === plotType)?.label ?? "None"} - } - onChange={onChange} - maxHeight="lg" - > - {cycleBurnDownChartOptions.map((item) => ( - - {item.label} - - ))} - -
- {loader && } - - )} {open ? (