Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WEB-2380] chore: cycle sidebar refactor #5759

Merged
merged 4 commits into from
Oct 8, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 69 additions & 0 deletions web/ce/components/cycles/analytics-sidebar/base.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
"use client";
import { FC, Fragment } from "react";
import { observer } from "mobx-react";
// plane ui
import { Loader } from "@plane/ui";
// components
import ProgressChart from "@/components/core/sidebar/progress-chart";
import { validateCycleSnapshot } from "@/components/cycles";
// helpers
import { getDate } from "@/helpers/date-time.helper";
// hooks
import { useCycle } from "@/hooks/store";

type ProgressChartProps = {
workspaceSlug: string;
projectId: string;
cycleId: string;
};
export const SidebarChart: FC<ProgressChartProps> = observer((props) => {
const { workspaceSlug, projectId, cycleId } = props;

// hooks
const { getEstimateTypeByCycleId, getCycleById } = useCycle();

// derived data
const cycleDetails = validateCycleSnapshot(getCycleById(cycleId));
const cycleStartDate = getDate(cycleDetails?.start_date);
const cycleEndDate = getDate(cycleDetails?.end_date);
const totalEstimatePoints = cycleDetails?.total_estimate_points || 0;
const totalIssues = cycleDetails?.total_issues || 0;
const estimateType = getEstimateTypeByCycleId(cycleId);

const chartDistributionData =
estimateType === "points" ? cycleDetails?.estimate_distribution : cycleDetails?.distribution || undefined;
Comment on lines +33 to +34
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Simplify 'chartDistributionData' assignment

The use of || undefined is redundant since cycleDetails?.distribution will return undefined if it doesn't exist.

Consider simplifying the code as follows:

- const chartDistributionData =
-   estimateType === "points" ? cycleDetails?.estimate_distribution : cycleDetails?.distribution || undefined;
+ const chartDistributionData =
+   estimateType === "points" ? cycleDetails?.estimate_distribution : cycleDetails?.distribution;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const chartDistributionData =
estimateType === "points" ? cycleDetails?.estimate_distribution : cycleDetails?.distribution || undefined;
const chartDistributionData =
estimateType === "points" ? cycleDetails?.estimate_distribution : cycleDetails?.distribution;


const completionChartDistributionData = chartDistributionData?.completion_chart || undefined;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Remove unnecessary || undefined

Similarly, the || undefined in completionChartDistributionData is unnecessary because if chartDistributionData?.completion_chart doesn't exist, it will already be undefined.

Simplify the code as:

- const completionChartDistributionData = chartDistributionData?.completion_chart || undefined;
+ const completionChartDistributionData = chartDistributionData?.completion_chart;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const completionChartDistributionData = chartDistributionData?.completion_chart || undefined;
const completionChartDistributionData = chartDistributionData?.completion_chart;


if (!workspaceSlug || !projectId || !cycleId) return null;

return (
<div>
<div className="relative flex items-center gap-2">
<div className="flex items-center justify-center gap-1 text-xs">
<span className="h-2.5 w-2.5 rounded-full bg-[#A9BBD0]" />
<span>Ideal</span>
</div>
<div className="flex items-center justify-center gap-1 text-xs">
<span className="h-2.5 w-2.5 rounded-full bg-[#4C8FFF]" />
<span>Current</span>
</div>
</div>
{cycleStartDate && cycleEndDate && completionChartDistributionData ? (
<Fragment>
<ProgressChart
distribution={completionChartDistributionData}
startDate={cycleStartDate}
endDate={cycleEndDate}
totalIssues={estimateType === "points" ? totalEstimatePoints : totalIssues}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Consider renaming totalIssues prop for clarity

When estimateType is "points", you're passing totalEstimatePoints to a prop named totalIssues, which might be confusing.

Consider renaming the prop to better reflect its purpose, such as totalCount:

- totalIssues={estimateType === "points" ? totalEstimatePoints : totalIssues}
+ totalCount={estimateType === "points" ? totalEstimatePoints : totalIssues}

You'll also need to update the ProgressChart component to accept totalCount instead of totalIssues.

Committable suggestion was skipped due to low confidence.

plotTitle={estimateType === "points" ? "points" : "issues"}
/>
</Fragment>
) : (
<Loader className="w-full h-[160px] mt-4">
<Loader.Item width="100%" height="100%" />
</Loader>
)}
Comment on lines +52 to +66
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Enhance error handling for missing data

If cycleStartDate, cycleEndDate, or completionChartDistributionData are missing, the loader is displayed. However, there may be situations where data fetching fails, and the user is left waiting indefinitely.

Consider adding error handling to inform the user if data cannot be loaded:

{cycleStartDate && cycleEndDate && completionChartDistributionData ? (
  // existing code
) : hasError ? (
  <div className="text-center text-sm text-red-500">Failed to load data.</div>
) : (
  <Loader className="w-full h-[160px] mt-4">
    <Loader.Item width="100%" height="100%" />
  </Loader>
)}

You'll need to implement hasError based on your data fetching logic.

</div>
);
});
2 changes: 1 addition & 1 deletion web/ce/components/cycles/analytics-sidebar/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export * from "./sidebar-chart";
export * from "./root";
12 changes: 12 additions & 0 deletions web/ce/components/cycles/analytics-sidebar/root.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
"use client";
import React, { FC } from "react";
// components
import { SidebarChart } from "./base";

type Props = {
workspaceSlug: string;
projectId: string;
cycleId: string;
};

export const SidebarChartRoot: FC<Props> = (props) => <SidebarChart {...props} />;
41 changes: 14 additions & 27 deletions web/core/components/cycles/analytics-sidebar/issue-progress.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"use client";

import { FC, Fragment, useCallback, useMemo, useState } from "react";
import { FC, Fragment, useCallback, useMemo } from "react";
import isEmpty from "lodash/isEmpty";
import isEqual from "lodash/isEqual";
import { observer } from "mobx-react";
Expand All @@ -16,18 +16,17 @@ import { EIssueFilterType, EIssuesStoreType } from "@/constants/issue";
// helpers
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";
import { EEstimateSystem } from "@/plane-web/constants/estimates";
import { useIssues, useCycle } from "@/hooks/store";
// plane web components
import { SidebarChartRoot } from "@/plane-web/components/cycles";

type TCycleAnalyticsProgress = {
workspaceSlug: string;
projectId: string;
cycleId: string;
};

const validateCycleSnapshot = (cycleDetails: ICycle | null): ICycle | null => {
export const validateCycleSnapshot = (cycleDetails: ICycle | null): ICycle | null => {
if (!cycleDetails || cycleDetails === null) return cycleDetails;

const updatedCycleDetails: any = { ...cycleDetails };
Expand Down Expand Up @@ -60,12 +59,9 @@ export const CycleAnalyticsProgress: FC<TCycleAnalyticsProgress> = observer((pro
// router
const searchParams = useSearchParams();
const peekCycle = searchParams.get("peekCycle") || undefined;
// hooks
const { areEstimateEnabledByProjectId, currentActiveEstimateId, estimateById } = useProjectEstimates();
const {
getPlotTypeByCycleId,
getEstimateTypeByCycleId,
setPlotType,
getCycleById,
fetchCycleDetails,
fetchArchivedCycleDetails,
Expand All @@ -74,17 +70,11 @@ export const CycleAnalyticsProgress: FC<TCycleAnalyticsProgress> = observer((pro
const {
issuesFilter: { issueFilters, updateFilters },
} = useIssues(EIssuesStoreType.CYCLE);
// state
const [loader, setLoader] = useState(false);

// 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);
const isCurrentEstimateTypeIsPoints = estimateDetails && estimateDetails?.type === EEstimateSystem.POINTS;

const completedIssues = cycleDetails?.completed_issues || 0;
const totalIssues = cycleDetails?.total_issues || 0;
Expand Down Expand Up @@ -132,15 +122,13 @@ export const CycleAnalyticsProgress: FC<TCycleAnalyticsProgress> = observer((pro
setEstimateType(cycleId, value);
if (!workspaceSlug || !projectId || !cycleId) return;
try {
setLoader(true);
if (isArchived) {
await fetchArchivedCycleDetails(workspaceSlug, projectId, cycleId);
} else {
await fetchCycleDetails(workspaceSlug, projectId, cycleId);
}
setLoader(false);
} catch (error) {
setLoader(false);
} catch (err) {
console.error(err);
setEstimateType(cycleId, estimateType);
}
};
Expand Down Expand Up @@ -218,16 +206,15 @@ export const CycleAnalyticsProgress: FC<TCycleAnalyticsProgress> = observer((pro
</CustomSelect.Option>
))}
</CustomSelect>
<div className="flex items-center justify-center gap-2">
<div className="flex items-center gap-1 text-xs">
<span className="text-custom-text-300">Done</span>
<span className="font-semibold text-custom-text-400">{progressHeaderPercentage}%</span>
</div>
</div>
</div>
<div className="py-4">
<SidebarBaseChart
chartDistributionData={chartDistributionData}
cycleStartDate={cycleStartDate}
cycleEndDate={cycleEndDate}
totalEstimatePoints={totalEstimatePoints}
totalIssues={totalIssues}
plotType={plotType}
/>
<SidebarChartRoot workspaceSlug={workspaceSlug} projectId={projectId} cycleId={cycleId} />
</div>
{/* progress detailed view */}
{chartDistributionData && (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ type ProgressChartProps = {
totalIssues: number;
plotType: string;
};
export const SidebarBaseChart = (props: ProgressChartProps) => {
const SidebarChart = (props: ProgressChartProps) => {
const { chartDistributionData, cycleStartDate, cycleEndDate, totalEstimatePoints, totalIssues, plotType } = props;
const completionChartDistributionData = chartDistributionData?.completion_chart || undefined;

Expand Down Expand Up @@ -55,3 +55,4 @@ export const SidebarBaseChart = (props: ProgressChartProps) => {
</div>
);
};
export default SidebarChart;
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ import React, { FC } from "react";
import isEmpty from "lodash/isEmpty";
import { observer } from "mobx-react";
import { LayersIcon, SquareUser, Users } from "lucide-react";
// ui
// types
import { ICycle } from "@plane/types";
// ui
import { Avatar, AvatarGroup, TextArea } from "@plane/ui";
// types
// hooks
import { useMember, useProjectEstimates } from "@/hooks/store";
// plane web
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ export const CycleSidebarHeader: FC<Props> = observer((props) => {
end_date: renderFormattedPayloadDate(endDate),
};

if (cycleDetails?.start_date && cycleDetails.end_date)
if (cycleDetails && cycleDetails.start_date && cycleDetails.end_date)
anmolsinghbhatia marked this conversation as resolved.
Show resolved Hide resolved
isDateValid = await dateChecker({
...payload,
cycle_id: cycleDetails.id,
Expand Down
Loading