import { MouseEvent } from "react"; import { observer } from "mobx-react-lite"; import Link from "next/link"; import useSWR from "swr"; // hooks import { ArrowRight, CalendarCheck, CalendarDays, Star, Target } from "lucide-react"; import { ICycle, TCycleGroups } from "@plane/types"; import { AvatarGroup, Loader, Tooltip, LinearProgressIndicator, LayersIcon, StateGroupIcon, PriorityIcon, Avatar, CycleGroupIcon, setPromiseToast, getButtonStyling, } from "@plane/ui"; import { SingleProgressStats } from "@/components/core"; // ui // components import ProgressChart from "@/components/core/sidebar/progress-chart"; import { ActiveCycleProgressStats, UpcomingCyclesList } from "@/components/cycles"; import { StateDropdown } from "@/components/dropdowns"; import { EmptyState } from "@/components/empty-state"; // icons // helpers // types // constants import { CYCLE_STATE_GROUPS_DETAILS } from "@/constants/cycle"; import { EmptyStateType } from "@/constants/empty-state"; import { CYCLE_ISSUES_WITH_PARAMS } from "@/constants/fetch-keys"; import { EIssuesStoreType } from "@/constants/issue"; import { cn } from "@/helpers/common.helper"; import { renderFormattedDate, findHowManyDaysLeft, renderFormattedDateWithoutYear, getDate, } from "@/helpers/date-time.helper"; import { truncateText } from "@/helpers/string.helper"; import { useCycle, useCycleFilter, useIssues, useMember, useProject } from "@/hooks/store"; import { usePlatformOS } from "@/hooks/use-platform-os"; interface IActiveCycleDetails { workspaceSlug: string; projectId: string; } export const ActiveCycleRoot: React.FC = observer((props) => { // props const { workspaceSlug, projectId } = props; // hooks const { isMobile } = usePlatformOS(); // store hooks const { issues: { fetchActiveCycleIssues }, } = useIssues(EIssuesStoreType.CYCLE); const { currentProjectActiveCycleId, currentProjectUpcomingCycleIds, fetchActiveCycle, getActiveCycleById, addCycleToFavorites, removeCycleFromFavorites, } = useCycle(); const { currentProjectDetails } = useProject(); const { getUserDetails } = useMember(); // cycle filters hook const { updateDisplayFilters } = useCycleFilter(); // derived values const activeCycle = currentProjectActiveCycleId ? getActiveCycleById(currentProjectActiveCycleId) : null; const cycleOwnerDetails = activeCycle ? getUserDetails(activeCycle.owned_by_id) : undefined; // fetch active cycle details const { isLoading } = useSWR( workspaceSlug && projectId ? `PROJECT_ACTIVE_CYCLE_${projectId}` : null, workspaceSlug && projectId ? () => fetchActiveCycle(workspaceSlug, projectId) : null ); // fetch active cycle issues const { data: activeCycleIssues } = useSWR( workspaceSlug && projectId && currentProjectActiveCycleId ? CYCLE_ISSUES_WITH_PARAMS(currentProjectActiveCycleId, { priority: "urgent,high" }) : null, workspaceSlug && projectId && currentProjectActiveCycleId ? () => fetchActiveCycleIssues(workspaceSlug, projectId, currentProjectActiveCycleId) : null ); // show loader if active cycle is loading if (!activeCycle && isLoading) return ( ); if (!activeCycle) { // show empty state if no active cycle is present if (currentProjectUpcomingCycleIds?.length === 0) return ; // show upcoming cycles list, if present else return ( <>
No active cycle

Create new cycles to find them here or check
{"'"}All{"'"} cycles tab to see all cycles or{" "}

); } const endDate = getDate(activeCycle.end_date); const startDate = getDate(activeCycle.start_date); const daysLeft = findHowManyDaysLeft(activeCycle.end_date) ?? 0; const cycleStatus = activeCycle.status.toLowerCase() as TCycleGroups; const groupedIssues: any = { backlog: activeCycle.backlog_issues, unstarted: activeCycle.unstarted_issues, started: activeCycle.started_issues, completed: activeCycle.completed_issues, cancelled: activeCycle.cancelled_issues, }; const handleAddToFavorites = (e: MouseEvent) => { e.preventDefault(); if (!workspaceSlug || !projectId) return; const addToFavoritePromise = addCycleToFavorites(workspaceSlug?.toString(), projectId.toString(), activeCycle.id); setPromiseToast(addToFavoritePromise, { loading: "Adding cycle to favorites...", success: { title: "Success!", message: () => "Cycle added to favorites.", }, error: { title: "Error!", message: () => "Couldn't add the cycle to favorites. Please try again.", }, }); }; const handleRemoveFromFavorites = (e: MouseEvent) => { e.preventDefault(); if (!workspaceSlug || !projectId) return; const removeFromFavoritePromise = removeCycleFromFavorites( workspaceSlug?.toString(), projectId.toString(), activeCycle.id ); setPromiseToast(removeFromFavoritePromise, { loading: "Removing cycle from favorites...", success: { title: "Success!", message: () => "Cycle removed from favorites.", }, error: { title: "Error!", message: () => "Couldn't remove the cycle from favorites. Please try again.", }, }); }; const progressIndicatorData = CYCLE_STATE_GROUPS_DETAILS.map((group, index) => ({ id: index, name: group.title, value: activeCycle.total_issues > 0 ? ((activeCycle[group.key as keyof ICycle] as number) / activeCycle.total_issues) * 100 : 0, color: group.color, })); return (

{truncateText(activeCycle.name, 70)}

{`${daysLeft} ${daysLeft > 1 ? "days" : "day"} left`} {activeCycle.is_favorite ? ( ) : ( )}
{renderFormattedDate(startDate)}
{renderFormattedDate(endDate)}
{cycleOwnerDetails?.display_name}
{activeCycle.assignee_ids.length > 0 && (
{activeCycle.assignee_ids.map((assignee_id) => { const member = getUserDetails(assignee_id); return ; })}
)}
{activeCycle.total_issues} issues
{activeCycle.completed_issues} issues
View cycle
Progress
{Object.keys(groupedIssues).map((group, index) => ( {group}
} completed={groupedIssues[group]} total={activeCycle.total_issues} /> ))}
High priority issues
{activeCycleIssues ? ( activeCycleIssues.length > 0 ? ( activeCycleIssues.map((issue) => (
{currentProjectDetails?.identifier}-{issue.sequence_id} {truncateText(issue.name, 30)}
{}} projectId={projectId} disabled buttonVariant="background-with-text" /> {issue.target_date && (
{renderFormattedDateWithoutYear(issue.target_date)}
)}
)) ) : (
There are no high priority issues present in this cycle.
) ) : ( )}
Ideal
Current
Pending issues-{" "} {activeCycle.total_issues - (activeCycle.completed_issues + activeCycle.cancelled_issues)}
); });