import React from "react";
import Link from "next/link";
import { useRouter } from "next/router";
import useSWR, { mutate } from "swr";
// services
import { CycleService } from "services/cycle.service";
// hooks
import useToast from "hooks/use-toast";
// ui
import { AssigneesList } from "components/ui/avatar";
import { SingleProgressStats } from "components/core";
import { Loader, Tooltip, LinearProgressIndicator } from "@plane/ui";
// components
import ProgressChart from "components/core/sidebar/progress-chart";
import { ActiveCycleProgressStats } from "components/cycles";
// icons
import { CalendarDaysIcon } from "@heroicons/react/20/solid";
import { PriorityIcon } from "components/icons/priority-icon";
import {
TargetIcon,
ContrastIcon,
PersonRunningIcon,
ArrowRightIcon,
TriangleExclamationIcon,
AlarmClockIcon,
LayerDiagonalIcon,
StateGroupIcon,
} from "components/icons";
import { StarIcon } from "@heroicons/react/24/outline";
// components
import { ViewIssueLabel } from "components/issues";
// helpers
import { getDateRangeStatus, renderShortDateWithYearFormat, findHowManyDaysLeft } from "helpers/date-time.helper";
import { truncateText } from "helpers/string.helper";
// types
import { ICycle, IIssue } from "types";
// fetch-keys
import { CURRENT_CYCLE_LIST, CYCLES_LIST, CYCLE_ISSUES_WITH_PARAMS } from "constants/fetch-keys";
const stateGroups = [
{
key: "backlog_issues",
title: "Backlog",
color: "#dee2e6",
},
{
key: "unstarted_issues",
title: "Unstarted",
color: "#26b5ce",
},
{
key: "started_issues",
title: "Started",
color: "#f7ae59",
},
{
key: "cancelled_issues",
title: "Cancelled",
color: "#d687ff",
},
{
key: "completed_issues",
title: "Completed",
color: "#09a953",
},
];
// services
const cycleService = new CycleService();
export const ActiveCycleDetails: React.FC = () => {
const router = useRouter();
const { workspaceSlug, projectId } = router.query;
const { setToastAlert } = useToast();
const { data: currentCycle } = useSWR(
workspaceSlug && projectId ? CURRENT_CYCLE_LIST(projectId as string) : null,
workspaceSlug && projectId
? () => cycleService.getCyclesWithParams(workspaceSlug as string, projectId as string, "current")
: null
);
const cycle = currentCycle ? currentCycle[0] : null;
const { data: issues } = useSWR(
workspaceSlug && projectId && cycle?.id ? CYCLE_ISSUES_WITH_PARAMS(cycle?.id, { priority: "urgent,high" }) : null,
workspaceSlug && projectId && cycle?.id
? () =>
cycleService.getCycleIssuesWithParams(workspaceSlug as string, projectId as string, cycle.id, {
priority: "urgent,high",
})
: null
) as { data: IIssue[] | undefined };
if (!currentCycle)
return (
);
if (!cycle)
return (
No active cycle
);
const endDate = new Date(cycle.end_date ?? "");
const startDate = new Date(cycle.start_date ?? "");
const groupedIssues: any = {
backlog: cycle.backlog_issues,
unstarted: cycle.unstarted_issues,
started: cycle.started_issues,
completed: cycle.completed_issues,
cancelled: cycle.cancelled_issues,
};
const cycleStatus = getDateRangeStatus(cycle.start_date, cycle.end_date);
const handleAddToFavorites = () => {
if (!workspaceSlug || !projectId || !cycle) return;
mutate(
CURRENT_CYCLE_LIST(projectId as string),
(prevData) =>
(prevData ?? []).map((c) => ({
...c,
is_favorite: c.id === cycle.id ? true : c.is_favorite,
})),
false
);
mutate(
CYCLES_LIST(projectId as string),
(prevData: any) =>
(prevData ?? []).map((c: any) => ({
...c,
is_favorite: c.id === cycle.id ? true : c.is_favorite,
})),
false
);
cycleService
.addCycleToFavorites(workspaceSlug as string, projectId as string, {
cycle: cycle.id,
})
.catch(() => {
setToastAlert({
type: "error",
title: "Error!",
message: "Couldn't add the cycle to favorites. Please try again.",
});
});
};
const handleRemoveFromFavorites = () => {
if (!workspaceSlug || !projectId || !cycle) return;
mutate(
CURRENT_CYCLE_LIST(projectId as string),
(prevData) =>
(prevData ?? []).map((c) => ({
...c,
is_favorite: c.id === cycle.id ? false : c.is_favorite,
})),
false
);
mutate(
CYCLES_LIST(projectId as string),
(prevData: any) =>
(prevData ?? []).map((c: any) => ({
...c,
is_favorite: c.id === cycle.id ? false : c.is_favorite,
})),
false
);
cycleService.removeCycleFromFavorites(workspaceSlug as string, projectId as string, cycle.id).catch(() => {
setToastAlert({
type: "error",
title: "Error!",
message: "Couldn't remove the cycle from favorites. Please try again.",
});
});
};
const progressIndicatorData = stateGroups.map((group, index) => ({
id: index,
name: group.title,
value: cycle.total_issues > 0 ? ((cycle[group.key as keyof ICycle] as number) / cycle.total_issues) * 100 : 0,
color: group.color,
}));
return (
{truncateText(cycle.name, 70)}
{cycleStatus === "current" ? (
{findHowManyDaysLeft(cycle.end_date ?? new Date())} Days Left
) : cycleStatus === "upcoming" ? (
{findHowManyDaysLeft(cycle.start_date ?? new Date())} Days Left
) : cycleStatus === "completed" ? (
{cycle.total_issues - cycle.completed_issues > 0 && (
)}{" "}
Completed
) : (
cycleStatus
)}
{cycle.is_favorite ? (
) : (
)}
{renderShortDateWithYearFormat(startDate)}
{renderShortDateWithYearFormat(endDate)}
{cycle.owned_by.avatar && cycle.owned_by.avatar !== "" ? (
) : (
{cycle.owned_by.display_name.charAt(0)}
)}
{cycle.owned_by.display_name}
{cycle.assignees.length > 0 && (
)}
{cycle.total_issues} issues
{cycle.completed_issues} issues
View Cycle
Progress
{Object.keys(groupedIssues).map((group, index) => (
{group}
}
completed={groupedIssues[group]}
total={cycle.total_issues}
/>
))}
High Priority Issues
{issues ? (
issues.length > 0 ? (
issues.map((issue) => (
router.push(`/${workspaceSlug}/projects/${projectId}/issues/${issue.id}`)}
className="flex flex-wrap cursor-pointer rounded-md items-center justify-between gap-2 border border-custom-border-200 bg-custom-background-90 px-3 py-1.5"
>
{issue.project_detail?.identifier}-{issue.sequence_id}
{truncateText(issue.name, 30)}
{issue.assignees && issue.assignees.length > 0 && Array.isArray(issue.assignees) ? (
) : (
""
)}
))
) : (
No issues present in the cycle.
)
) : (
)}
{issues && issues.length > 0 && (
issue?.state_detail?.group === "completed")?.length / issues.length) *
100 ?? 0
}%`,
}}
/>
{issues?.filter((issue) => issue?.state_detail?.group === "completed")?.length} of {issues?.length}
)}
Pending Issues - {cycle.total_issues - (cycle.completed_issues + cycle.cancelled_issues)}
);
};