import { MouseEvent } 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"; import { useMobxStore } from "lib/mobx/store-provider"; // 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"; import { ViewIssueLabel } from "components/issues"; // 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"; // 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", }, ]; interface IActiveCycleDetails { workspaceSlug: string; projectId: string; } export const ActiveCycleDetails: React.FC = (props) => { // services const cycleService = new CycleService(); const router = useRouter(); const { workspaceSlug, projectId } = props; const { cycle: cycleStore } = useMobxStore(); const { setToastAlert } = useToast(); const { isLoading } = useSWR( workspaceSlug && projectId ? `ACTIVE_CYCLE_ISSUE_${projectId}_CURRENT` : null, workspaceSlug && projectId ? () => cycleStore.fetchCycles(workspaceSlug, projectId, "current") : null ); const activeCycle = cycleStore.cycles?.[projectId] || null; const cycle = activeCycle ? activeCycle[0] : null; const issues = (cycleStore?.active_cycle_issues as any) || 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 (isLoading) 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 = (e: MouseEvent) => { e.preventDefault(); if (!workspaceSlug || !projectId) return; cycleStore.addCycleToFavorites(workspaceSlug?.toString(), projectId.toString(), cycle.id).catch(() => { setToastAlert({ type: "error", title: "Error!", message: "Couldn't add the cycle to favorites. Please try again.", }); }); }; const handleRemoveFromFavorites = (e: MouseEvent) => { e.preventDefault(); if (!workspaceSlug || !projectId) return; cycleStore.removeCycleFromFavorites(workspaceSlug?.toString(), projectId.toString(), cycle.id).catch(() => { setToastAlert({ type: "error", title: "Error!", message: "Couldn't add the cycle to 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} ) : ( {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: any) => (
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: any) => issue?.state_detail?.group === "completed")?.length} of {issues?.length}
)}
Ideal
Current
Pending Issues - {cycle.total_issues - (cycle.completed_issues + cycle.cancelled_issues)}
); };