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 { CustomMenu, LinearProgressIndicator, Tooltip } from "components/ui"; import { Disclosure, Transition } from "@headlessui/react"; // icons import { CalendarDaysIcon } from "@heroicons/react/20/solid"; import { ChevronDownIcon, DocumentDuplicateIcon, PencilIcon, StarIcon, TrashIcon, } from "@heroicons/react/24/outline"; // helpers import { getDateRangeStatus, renderShortDateWithYearFormat } from "helpers/date-time.helper"; import { groupBy } from "helpers/array.helper"; import { capitalizeFirstLetter, copyTextToClipboard, truncateText } from "helpers/string.helper"; // types import { CompletedCyclesResponse, CurrentAndUpcomingCyclesResponse, DraftCyclesResponse, ICycle, } from "types"; // fetch-keys import { CYCLE_COMPLETE_LIST, CYCLE_CURRENT_AND_UPCOMING_LIST, CYCLE_DRAFT_LIST, CYCLE_ISSUES, } from "constants/fetch-keys"; type TSingleStatProps = { cycle: ICycle; handleEditCycle: () => void; handleDeleteCycle: () => void; }; const stateGroupColors: { [key: string]: string; } = { backlog: "#DEE2E6", unstarted: "#26B5CE", started: "#F7AE59", cancelled: "#D687FF", completed: "#09A953", }; export const SingleCycleCard: React.FC = (props) => { const { cycle, handleEditCycle, handleDeleteCycle } = props; const router = useRouter(); const { workspaceSlug, projectId } = router.query; const { setToastAlert } = useToast(); const { data: cycleIssues } = 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) : null ); const endDate = new Date(cycle.end_date ?? ""); const startDate = new Date(cycle.start_date ?? ""); const groupedIssues = { backlog: [], unstarted: [], started: [], cancelled: [], completed: [], ...groupBy(cycleIssues ?? [], "issue_detail.state_detail.group"), }; const handleAddToFavorites = () => { if (!workspaceSlug && !projectId && !cycle) return; cyclesService .addCycleToFavorites(workspaceSlug as string, projectId as string, { cycle: cycle.id, }) .then(() => { const cycleStatus = getDateRangeStatus(cycle.start_date, cycle.end_date); if (cycleStatus === "current" || cycleStatus === "upcoming") mutate( CYCLE_CURRENT_AND_UPCOMING_LIST(projectId as string), (prevData) => ({ current_cycle: (prevData?.current_cycle ?? []).map((c) => ({ ...c, is_favorite: c.id === cycle.id ? true : c.is_favorite, })), upcoming_cycle: (prevData?.upcoming_cycle ?? []).map((c) => ({ ...c, is_favorite: c.id === cycle.id ? true : c.is_favorite, })), }), false ); else if (cycleStatus === "completed") mutate( CYCLE_COMPLETE_LIST(projectId as string), (prevData) => ({ completed_cycles: (prevData?.completed_cycles ?? []).map((c) => ({ ...c, is_favorite: c.id === cycle.id ? true : c.is_favorite, })), }), false ); else mutate( CYCLE_DRAFT_LIST(projectId as string), (prevData) => ({ draft_cycles: (prevData?.draft_cycles ?? []).map((c) => ({ ...c, is_favorite: c.id === cycle.id ? true : c.is_favorite, })), }), false ); setToastAlert({ type: "success", title: "Success!", message: "Successfully added the cycle to favorites.", }); }) .catch(() => { setToastAlert({ type: "error", title: "Error!", message: "Couldn't add the cycle to favorites. Please try again.", }); }); }; const handleRemoveFromFavorites = () => { if (!workspaceSlug || !cycle) return; cyclesService .removeCycleFromFavorites(workspaceSlug as string, projectId as string, cycle.id) .then(() => { const cycleStatus = getDateRangeStatus(cycle.start_date, cycle.end_date); if (cycleStatus === "current" || cycleStatus === "upcoming") mutate( CYCLE_CURRENT_AND_UPCOMING_LIST(projectId as string), (prevData) => ({ current_cycle: (prevData?.current_cycle ?? []).map((c) => ({ ...c, is_favorite: c.id === cycle.id ? false : c.is_favorite, })), upcoming_cycle: (prevData?.upcoming_cycle ?? []).map((c) => ({ ...c, is_favorite: c.id === cycle.id ? false : c.is_favorite, })), }), false ); else if (cycleStatus === "completed") mutate( CYCLE_COMPLETE_LIST(projectId as string), (prevData) => ({ completed_cycles: (prevData?.completed_cycles ?? []).map((c) => ({ ...c, is_favorite: c.id === cycle.id ? false : c.is_favorite, })), }), false ); else mutate( CYCLE_DRAFT_LIST(projectId as string), (prevData) => ({ draft_cycles: (prevData?.draft_cycles ?? []).map((c) => ({ ...c, is_favorite: c.id === cycle.id ? false : c.is_favorite, })), }), false ); setToastAlert({ type: "success", title: "Success!", message: "Successfully removed the cycle from favorites.", }); }) .catch(() => { setToastAlert({ type: "error", title: "Error!", message: "Couldn't remove the cycle from favorites. Please try again.", }); }); }; const handleCopyText = () => { const originURL = typeof window !== "undefined" && window.location.origin ? window.location.origin : ""; copyTextToClipboard( `${originURL}/${workspaceSlug}/projects/${projectId}/cycles/${cycle.id}` ).then(() => { setToastAlert({ type: "success", title: "Link Copied!", message: "Cycle link copied to clipboard.", }); }); }; const progressIndicatorData = Object.keys(groupedIssues).map((group, index) => ({ id: index, name: capitalizeFirstLetter(group), value: cycleIssues && cycleIssues.length > 0 ? (groupedIssues[group].length / cycleIssues.length) * 100 : 0, color: stateGroupColors[group], })); return (

{truncateText(cycle.name, 75)}

{cycle.is_favorite ? ( ) : ( )}
Start : {renderShortDateWithYearFormat(startDate)}
End : {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}
Delete Cycle Copy Cycle Link
{({ open }) => (
Progress
{Object.keys(groupedIssues).map((group) => (
{group}
{groupedIssues[group].length}{" "} -{" "} {cycleIssues && cycleIssues.length > 0 ? `${Math.round( (groupedIssues[group].length / cycleIssues.length) * 100 )}%` : "0%"}
))}
)}
); };