import React from "react"; import Link from "next/link"; import Image from "next/image"; import { useRouter } from "next/router"; import useSWR, { mutate } from "swr"; // services import cyclesService from "services/cycles.service"; // hooks import useToast from "hooks/use-toast"; // ui import { LinearProgressIndicator, Tooltip } from "components/ui"; import { AssigneesList } from "components/ui/avatar"; import { SingleProgressStats } from "components/core"; // components import ProgressChart from "components/core/sidebar/progress-chart"; import { ActiveCycleProgressStats } from "components/cycles"; // icons import { CalendarDaysIcon } from "@heroicons/react/20/solid"; import { getPriorityIcon } from "components/icons/priority-icon"; import { TargetIcon, ContrastIcon, PersonRunningIcon, ArrowRightIcon, TriangleExclamationIcon, AlarmClockIcon, LayerDiagonalIcon, CompletedStateIcon, } 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 } from "constants/fetch-keys"; type TSingleStatProps = { cycle: ICycle; isCompleted?: boolean; }; 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", }, ]; export const ActiveCycleDetails: React.FC = ({ cycle, isCompleted = false }) => { const router = useRouter(); const { workspaceSlug, projectId } = router.query; const { setToastAlert } = useToast(); 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 ); cyclesService .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 ); cyclesService .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 { data: issues } = useSWR( workspaceSlug && projectId && cycle.id ? CYCLE_ISSUES(cycle.id as string) : null, workspaceSlug && projectId && cycle.id ? () => cyclesService.getCycleIssues( workspaceSlug as string, projectId as string, cycle.id as string ) : null ); 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.first_name} ) : ( {cycle.owned_by.first_name.charAt(0)} )} {cycle.owned_by.first_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 ?.filter((issue) => issue.priority === "urgent" || issue.priority === "high") .map((issue) => (
{issue.project_detail?.identifier}-{issue.sequence_id}
{truncateText(issue.name, 30)}
{getPriorityIcon(issue.priority, "text-sm")}
{issue.label_details.length > 0 ? (
{issue.label_details.map((label) => ( {label.name} ))}
) : ( "" )}
{issue.assignees && issue.assignees.length > 0 && Array.isArray(issue.assignees) ? (
) : ( "" )}
))}
issue?.state_detail?.group === "completed" && (issue?.priority === "urgent" || issue?.priority === "high") )?.length / issues?.filter( (issue) => issue?.priority === "urgent" || issue?.priority === "high" )?.length) * 100 ?? 0 }%`, }} />
{ issues?.filter( (issue) => issue?.state_detail?.group === "completed" && (issue?.priority === "urgent" || issue?.priority === "high") )?.length }{" "} of{" "} { issues?.filter( (issue) => issue?.priority === "urgent" || issue?.priority === "high" )?.length }
Ideal
Current
Pending Issues -{" "} {cycle.total_issues - (cycle.completed_issues + cycle.cancelled_issues)}
); };