import React, { useState } from "react"; import useSWR, { mutate } from "swr"; import Link from "next/link"; import { useRouter } from "next/router"; // helper import { renderDateFormat } from "helpers/date-time.helper"; import { startOfWeek, lastDayOfWeek, eachDayOfInterval, weekDayInterval, formatDate, getCurrentWeekStartDate, getCurrentWeekEndDate, subtractMonths, addMonths, updateDateWithYear, updateDateWithMonth, isSameMonth, isSameYear, subtract7DaysToDate, addSevenDaysToDate, } from "helpers/calendar.helper"; // ui import { Popover, Transition } from "@headlessui/react"; import { DragDropContext, Draggable, DropResult } from "react-beautiful-dnd"; import StrictModeDroppable from "components/dnd/StrictModeDroppable"; import { CustomMenu, Spinner, ToggleSwitch } from "components/ui"; // icon import { CheckIcon, ChevronDownIcon, ChevronLeftIcon, ChevronRightIcon, PlusIcon, } from "@heroicons/react/24/outline"; // hooks import useIssuesView from "hooks/use-issues-view"; // services import issuesService from "services/issues.service"; import cyclesService from "services/cycles.service"; // fetch key import { CYCLE_CALENDAR_ISSUES, MODULE_CALENDAR_ISSUES, PROJECT_CALENDAR_ISSUES, } from "constants/fetch-keys"; // type import { IIssue } from "types"; // constant import { monthOptions, yearOptions } from "constants/calendar"; import modulesService from "services/modules.service"; import { getStateGroupIcon } from "components/icons"; type Props = { addIssueToDate: (date: string) => void; }; interface ICalendarRange { startDate: Date; endDate: Date; } export const CalendarView: React.FC = ({ addIssueToDate }) => { const [showWeekEnds, setShowWeekEnds] = useState(false); const [currentDate, setCurrentDate] = useState(new Date()); const [isMonthlyView, setIsMonthlyView] = useState(true); const [showAllIssues, setShowAllIssues] = useState(false); const router = useRouter(); const { workspaceSlug, projectId, cycleId, moduleId } = router.query; const { params } = useIssuesView(); const [calendarDateRange, setCalendarDateRange] = useState({ startDate: startOfWeek(currentDate), endDate: lastDayOfWeek(currentDate), }); const targetDateFilter = { target_date: `${renderDateFormat(calendarDateRange.startDate)};after,${renderDateFormat( calendarDateRange.endDate )};before`, }; const { data: projectCalendarIssues } = useSWR( workspaceSlug && projectId ? PROJECT_CALENDAR_ISSUES(projectId as string) : null, workspaceSlug && projectId ? () => issuesService.getIssuesWithParams(workspaceSlug as string, projectId as string, { ...params, target_date: `${renderDateFormat(calendarDateRange.startDate)};after,${renderDateFormat( calendarDateRange.endDate )};before`, group_by: null, }) : null ); const { data: cycleCalendarIssues } = useSWR( workspaceSlug && projectId && cycleId ? CYCLE_CALENDAR_ISSUES(projectId as string, cycleId as string) : null, workspaceSlug && projectId && cycleId ? () => cyclesService.getCycleIssuesWithParams( workspaceSlug as string, projectId as string, cycleId as string, { ...params, target_date: `${renderDateFormat( calendarDateRange.startDate )};after,${renderDateFormat(calendarDateRange.endDate)};before`, group_by: null, } ) : null ); const { data: moduleCalendarIssues } = useSWR( workspaceSlug && projectId && moduleId ? MODULE_CALENDAR_ISSUES(projectId as string, moduleId as string) : null, workspaceSlug && projectId && moduleId ? () => modulesService.getModuleIssuesWithParams( workspaceSlug as string, projectId as string, moduleId as string, { ...params, target_date: `${renderDateFormat( calendarDateRange.startDate )};after,${renderDateFormat(calendarDateRange.endDate)};before`, group_by: null, } ) : null ); const totalDate = eachDayOfInterval({ start: calendarDateRange.startDate, end: calendarDateRange.endDate, }); const onlyWeekDays = weekDayInterval({ start: calendarDateRange.startDate, end: calendarDateRange.endDate, }); const currentViewDays = showWeekEnds ? totalDate : onlyWeekDays; const calendarIssues = cycleId ? (cycleCalendarIssues as IIssue[]) : moduleId ? (moduleCalendarIssues as IIssue[]) : (projectCalendarIssues as IIssue[]); const currentViewDaysData = currentViewDays.map((date: Date) => { const filterIssue = calendarIssues && calendarIssues.length > 0 ? calendarIssues.filter( (issue) => issue.target_date && renderDateFormat(issue.target_date) === renderDateFormat(date) ) : []; return { date: renderDateFormat(date), issues: filterIssue, }; }); const weeks = ((date: Date[]) => { const weeks = []; if (showWeekEnds) { for (let day = 0; day <= 6; day++) { weeks.push(date[day]); } } else { for (let day = 0; day <= 4; day++) { weeks.push(date[day]); } } return weeks; })(currentViewDays); const onDragEnd = (result: DropResult) => { const { source, destination, draggableId } = result; if (!destination || !workspaceSlug || !projectId) return; if (source.droppableId === destination.droppableId) return; const fetchKey = cycleId ? CYCLE_CALENDAR_ISSUES(projectId as string, cycleId as string) : moduleId ? MODULE_CALENDAR_ISSUES(projectId as string, moduleId as string) : PROJECT_CALENDAR_ISSUES(projectId as string); mutate( fetchKey, (prevData) => (prevData ?? []).map((p) => { if (p.id === draggableId) return { ...p, target_date: destination.droppableId, }; return p; }), false ); issuesService.patchIssue(workspaceSlug as string, projectId as string, draggableId, { target_date: destination?.droppableId, }); }; const updateDate = (date: Date) => { setCurrentDate(date); setCalendarDateRange({ startDate: startOfWeek(date), endDate: lastDayOfWeek(date), }); }; return calendarIssues ? (
{({ open }) => ( <>
{formatDate(currentDate, "Month")}{" "} {formatDate(currentDate, "yyyy")}
{yearOptions.map((year) => ( ))}
{monthOptions.map((month) => ( ))}
)}
{isMonthlyView ? "Monthly" : "Weekly"}
} > { setIsMonthlyView(true); setCalendarDateRange({ startDate: startOfWeek(currentDate), endDate: lastDayOfWeek(currentDate), }); }} className="w-52 text-sm text-brand-secondary" >
Monthly View
{ setIsMonthlyView(false); setCalendarDateRange({ startDate: getCurrentWeekStartDate(currentDate), endDate: getCurrentWeekEndDate(currentDate), }); }} className="w-52 text-sm text-brand-secondary" >
Weekly View

Show weekends

setShowWeekEnds(!showWeekEnds)} />
{weeks.map((date, index) => (
{isMonthlyView ? formatDate(date, "eee").substring(0, 3) : formatDate(date, "eee")} {!isMonthlyView && {formatDate(date, "d")}}
))}
{currentViewDaysData.map((date, index) => { const totalIssues = date.issues.length; return ( {(provided) => (
{isMonthlyView && {formatDate(new Date(date.date), "d")}} {totalIssues > 0 && date.issues .slice(0, showAllIssues ? totalIssues : 4) .map((issue: IIssue, index) => ( {(provided, snapshot) => ( )} ))} {totalIssues > 4 && ( )}
{provided.placeholder}
)}
); })}
) : (
); };