Skip to content

Commit

Permalink
Fix: Cycle graphs refactor (#5745)
Browse files Browse the repository at this point in the history
* fix: community changes for cycle graphs

* fix: added dependency from root package.json
  • Loading branch information
gakshita authored Oct 3, 2024
1 parent ee0dce4 commit f1a0a8d
Show file tree
Hide file tree
Showing 8 changed files with 91 additions and 176 deletions.
3 changes: 2 additions & 1 deletion packages/types/src/cycle/cycle.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,11 @@ export type TCycleEstimateDistribution = {
export type TCycleProgress = {
date: string;
started: number;
actual: number;
pending: number;
ideal: number | null;
scope: number;
completed: number;
actual: number;
unstarted: number;
backlog: number;
cancelled: number;
Expand Down Expand Up @@ -103,6 +103,7 @@ export interface ICycle extends TProgressSnapshot {
workspace_id: string;
project_detail: IProjectDetails;
progress: any[];
version: number;
}

export interface CycleIssueResponse {
Expand Down
89 changes: 0 additions & 89 deletions web/core/components/cycles/active-cycle/root.tsx

This file was deleted.

2 changes: 1 addition & 1 deletion web/core/components/cycles/list/root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export const CyclesList: FC<ICyclesList> = observer((props) => {
</>
) : (
<>
<ActiveCycleRoot workspaceSlug={workspaceSlug} projectId={projectId} />
<ActiveCycleRoot />

{upcomingCycleIds && (
<Disclosure as="div" className="flex flex-shrink-0 flex-col" defaultOpen>
Expand Down
2 changes: 1 addition & 1 deletion web/core/constants/cycle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ export const CYCLE_STATUS: {
{
label: "day left",
value: "current",
title: "Active",
title: "In progress",
color: "#F59E0B",
textColor: "text-amber-500",
bgColor: "bg-amber-50",
Expand Down
19 changes: 7 additions & 12 deletions web/core/store/cycle.store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,11 +71,7 @@ export interface ICycleStore {
fetchArchivedCycleDetails: (workspaceSlug: string, projectId: string, cycleId: string) => Promise<ICycle>;
fetchCycleDetails: (workspaceSlug: string, projectId: string, cycleId: string) => Promise<ICycle>;
fetchActiveCycleProgress: (workspaceSlug: string, projectId: string, cycleId: string) => Promise<TProgressSnapshot>;
fetchActiveCycleProgressPro: (
workspaceSlug: string,
projectId: string,
cycleId: string
) => Promise<TProgressSnapshot> | Promise<null>;
fetchActiveCycleProgressPro: (workspaceSlug: string, projectId: string, cycleId: string) => Promise<void>;
fetchActiveCycleAnalytics: (
workspaceSlug: string,
projectId: string,
Expand Down Expand Up @@ -146,7 +142,6 @@ export class CycleStore implements ICycleStore {
fetchArchivedCycles: action,
fetchArchivedCycleDetails: action,
fetchActiveCycleProgress: action,
fetchActiveCycleProgressPro: action,
fetchActiveCycleAnalytics: action,
fetchCycleDetails: action,
createCycle: action,
Expand Down Expand Up @@ -282,7 +277,7 @@ export class CycleStore implements ICycleStore {
*/
getActiveCycleProgress = computedFn((cycleId?: string) => {
const cycle = cycleId ? this.cycleMap[cycleId] : this.currentProjectActiveCycle;
if (!cycle?.progress) return null;
if (!cycle) return null;

const isTypeIssue = this.getEstimateTypeByCycleId(cycle.id) === "issues";
const isBurnDown = this.getPlotTypeByCycleId(cycle.id) === "burndown";
Expand Down Expand Up @@ -403,13 +398,13 @@ export class CycleStore implements ICycleStore {
await this.cycleService.cycleDateCheck(workspaceSlug, projectId, payload);

/**
* @description gets the plot type for the module store
* @description gets the plot type for the cycle store
* @param {TCyclePlotType} plotType
*/
getPlotTypeByCycleId = computedFn((cycleId: string) => this.plotType[cycleId] || "burndown");

/**
* @description gets the estimate type for the module store
* @description gets the estimate type for the cycle store
* @param {TCycleEstimateType} estimateType
*/
getEstimateTypeByCycleId = computedFn((cycleId: string) => {
Expand All @@ -421,15 +416,15 @@ export class CycleStore implements ICycleStore {
});

/**
* @description updates the plot type for the module store
* @description updates the plot type for the cycle store
* @param {TCyclePlotType} plotType
*/
setPlotType = (cycleId: string, plotType: TCyclePlotType) => {
set(this.plotType, [cycleId], plotType);
};

/**
* @description updates the estimate type for the module store
* @description updates the estimate type for the cycle store
* @param {TCycleEstimateType} estimateType
*/
setEstimateType = (cycleId: string, estimateType: TCycleEstimateType) => {
Expand Down Expand Up @@ -545,7 +540,7 @@ export class CycleStore implements ICycleStore {
* @param cycleId
* @returns
*/
fetchActiveCycleProgressPro = async (workspaceSlug: string, projectId: string, cycleId: string) => null;
fetchActiveCycleProgressPro = action(async (workspaceSlug: string, projectId: string, cycleId: string) => {});

/**
* @description fetches active cycle analytics
Expand Down
103 changes: 77 additions & 26 deletions web/helpers/cycle.helper.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { startOfToday, format } from "date-fns";
import { isEmpty, orderBy, uniqBy } from "lodash";
import sortBy from "lodash/sortBy";
import { ICycle, TCycleFilters } from "@plane/types";
// helpers
import { generateDateArray, getDate, getToday } from "@/helpers/date-time.helper";
import { findTotalDaysInRange, generateDateArray, getDate } from "@/helpers/date-time.helper";
import { satisfiesDateFilter } from "@/helpers/filter.helper";

export type TProgressChartData = {
Expand Down Expand Up @@ -75,47 +76,97 @@ export const shouldFilterCycle = (cycle: ICycle, filter: TCycleFilters): boolean
return fallsInFilters;
};

export const formatActiveCycle = (args: {
cycle: ICycle;
isBurnDown?: boolean | undefined;
isTypeIssue?: boolean | undefined;
}) => {
const { cycle, isBurnDown, isTypeIssue } = args;
let today = getToday();
const endDate: Date | string = new Date(cycle.end_date!);
const scope = (p: any, isTypeIssue: boolean) => (isTypeIssue ? p.total_issues : p.total_estimate_points);
const ideal = (date: string, scope: number, cycle: ICycle) =>
Math.floor(
((findTotalDaysInRange(date, cycle.end_date) || 0) /
(findTotalDaysInRange(cycle.start_date, cycle.end_date) || 0)) *
scope
);

const extendedArray = endDate > today ? generateDateArray(today as Date, endDate) : [];
if (isEmpty(cycle.progress)) return extendedArray;
today = getToday(true);
const formatV1Data = (isTypeIssue: boolean, cycle: ICycle, isBurnDown: boolean, endDate: Date | string) => {
const today = format(startOfToday(), "yyyy-MM-dd");
const data = isTypeIssue ? cycle.distribution : cycle.estimate_distribution;
const extendedArray = generateDateArray(endDate, endDate).map((d) => d.date);

if (isEmpty(data)) return [];
const progress = [...Object.keys(data.completion_chart), ...extendedArray].map((p) => {
const pending = data.completion_chart[p] || 0;
const total = isTypeIssue ? cycle.total_issues : cycle.total_estimate_points;
const completed = scope(cycle, isTypeIssue) - pending;

return {
date: p,
scope: p! < today ? scope(cycle, isTypeIssue) : null,
completed,
backlog: isTypeIssue ? cycle.backlog_issues : cycle.backlog_estimate_points,
started: p === today ? cycle[isTypeIssue ? "started_issues" : "started_estimate_points"] : undefined,
unstarted: p === today ? cycle[isTypeIssue ? "unstarted_issues" : "unstarted_estimate_points"] : undefined,
cancelled: p === today ? cycle[isTypeIssue ? "cancelled_issues" : "cancelled_estimate_points"] : undefined,
pending: Math.abs(pending || 0),
ideal:
p < today
? ideal(p, total || 0, cycle)
: p <= cycle.end_date!
? ideal(today as string, total || 0, cycle)
: null,
actual: p <= today ? (isBurnDown ? Math.abs(pending) : completed) : undefined,
};
});

const scope = (p: any) => (isTypeIssue ? p.total_issues : p.total_estimate_points);
const ideal = (p: any) =>
isTypeIssue
? Math.abs(p.total_issues - p.completed_issues + (Math.random() < 0.5 ? -1 : 1))
: Math.abs(p.total_estimate_points - p.completed_estimate_points + (Math.random() < 0.5 ? -1 : 1));
return progress;
};

const formatV2Data = (isTypeIssue: boolean, cycle: ICycle, isBurnDown: boolean, endDate: Date | string) => {
if (!cycle.progress) return [];
let today: Date | string = startOfToday();

const scopeToday = scope(cycle?.progress[cycle?.progress.length - 1]);
const idealToday = ideal(cycle?.progress[cycle?.progress.length - 1]);
const extendedArray = endDate > today ? generateDateArray(today as Date, endDate) : [];
if (isEmpty(cycle.progress)) return extendedArray;
today = format(startOfToday(), "yyyy-MM-dd");
const todaysData = cycle?.progress[cycle?.progress.length - 1];
const scopeToday = scope(todaysData, isTypeIssue);
const idealToday = ideal(todaysData.date, scopeToday, cycle);

const progress = [...orderBy(cycle?.progress, "date"), ...extendedArray].map((p) => {
let progress = [...orderBy(cycle?.progress, "date"), ...extendedArray].map((p) => {
const pending = isTypeIssue
? p.total_issues - p.completed_issues - p.cancelled_issues
: p.total_estimate_points - p.completed_estimate_points - p.cancelled_estimate_points;
const completed = isTypeIssue ? p.completed_issues : p.completed_estimate_points;
const dataDate = p.progress_date ? format(new Date(p.progress_date), "yyyy-MM-dd") : p.date;

return {
date: p.date,
scope: p.date! < today ? scope(p) : p.date! < cycle.end_date! ? scopeToday : null,
date: dataDate,
scope: dataDate! < today ? scope(p, isTypeIssue) : dataDate! <= cycle.end_date! ? scopeToday : null,
completed,
backlog: isTypeIssue ? p.backlog_issues : p.backlog_estimate_points,
started: isTypeIssue ? p.started_issues : p.started_estimate_points,
unstarted: isTypeIssue ? p.unstarted_issues : p.unstarted_estimate_points,
cancelled: isTypeIssue ? p.cancelled_issues : p.cancelled_estimate_points,
pending: Math.abs(pending),
// TODO: This is a temporary logic to show the ideal line in the cycle chart
ideal: p.date! < today ? ideal(p) : p.date! < cycle.end_date! ? idealToday : null,
actual: p.date! <= today ? (isBurnDown ? Math.abs(pending) : completed) : undefined,
ideal:
dataDate! < today
? ideal(dataDate, scope(p, isTypeIssue), cycle)
: dataDate! < cycle.end_date!
? idealToday
: null,
actual: dataDate! <= today ? (isBurnDown ? Math.abs(pending) : completed) : undefined,
};
});
return uniqBy(progress, "date");
progress = uniqBy(progress, "date");

return progress;
};

export const formatActiveCycle = (args: {
cycle: ICycle;
isBurnDown?: boolean | undefined;
isTypeIssue?: boolean | undefined;
}) => {
const { cycle, isBurnDown, isTypeIssue } = args;
const endDate: Date | string = new Date(cycle.end_date!);

return cycle.version === 1
? formatV1Data(isTypeIssue!, cycle, isBurnDown!, endDate)
: formatV2Data(isTypeIssue!, cycle, isBurnDown!, endDate);
};
46 changes: 1 addition & 45 deletions web/helpers/date-time.helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -358,57 +358,13 @@ export const getReadTimeFromWordsCount = (wordsCount: number): number => {
return minutes * 60;
};

/**
* @description calculates today's date
* @param {boolean} format
* @returns {Date | string} today's date
* @example getToday() // Output: 2024-09-29T00:00:00.000Z
* @example getToday(true) // Output: 2024-09-29
*/
export const getToday = (format: boolean = false) => {
const today = new Date();
today.setHours(0, 0, 0, 0);
if (!format) return today;

const year = today.getFullYear();
const month = String(today.getMonth() + 1).padStart(2, "0"); // Months are 0-based, so add 1
const day = String(today.getDate()).padStart(2, "0"); // Add leading zero for single digits
return `${year}-${month}-${day}`;
};

/**
* @description calculates the date of the day before today
* @param {boolean} format
* @returns {Date | string} date of the day before today
* @example dateFormatter() // Output: "Sept 20, 2024"
*/
export const dateFormatter = (dateString: string) => {
// Convert to Date object
const date = new Date(dateString);

// Options for the desired format (Month Day, Year)
const options: Intl.DateTimeFormatOptions = { year: "numeric", month: "short", day: "numeric" };

// Format the date
const formattedDate = date.toLocaleDateString("en-US", options);

return formattedDate;
};

/**
* @description calculates days left from today to the end date
* @returns {Date | string} number of days left
*/
export const daysLeft = (end_date: string) =>
end_date ? Math.ceil((new Date(end_date).getTime() - new Date().getTime()) / (1000 * 3600 * 24)) : 0;

/**
* @description generates an array of dates between the start and end dates
* @param startDate
* @param endDate
* @returns
*/
export const generateDateArray = (startDate: Date, endDate: Date) => {
export const generateDateArray = (startDate: string | Date, endDate: string | Date) => {
// Convert the start and end dates to Date objects if they aren't already
const start = new Date(startDate);
// start.setDate(start.getDate() + 1);
Expand Down
Loading

0 comments on commit f1a0a8d

Please sign in to comment.