plane/apps/app/components/core/calendar-view/calendar.tsx

244 lines
6.8 KiB
TypeScript
Raw Normal View History

2023-05-15 06:05:07 +00:00
import React, { useEffect, useState } from "react";
import { useRouter } from "next/router";
2023-05-15 06:05:07 +00:00
import { mutate } from "swr";
2023-05-15 06:05:07 +00:00
// react-beautiful-dnd
import { DragDropContext, DropResult } from "react-beautiful-dnd";
// services
import issuesService from "services/issues.service";
2023-05-15 06:05:07 +00:00
// hooks
import useCalendarIssuesView from "hooks/use-calendar-issues-view";
// components
import { SingleCalendarDate, CalendarHeader } from "components/core";
// ui
import { Spinner } from "components/ui";
// helpers
import { renderDateFormat } from "helpers/date-time.helper";
import {
startOfWeek,
lastDayOfWeek,
eachDayOfInterval,
weekDayInterval,
formatDate,
} from "helpers/calendar.helper";
2023-05-15 06:05:07 +00:00
// types
import { ICalendarRange, ICurrentUserResponse, IIssue, UserAuth } from "types";
2023-05-15 06:05:07 +00:00
// fetch-keys
import {
CYCLE_ISSUES_WITH_PARAMS,
MODULE_ISSUES_WITH_PARAMS,
PROJECT_ISSUES_LIST_WITH_PARAMS,
VIEW_ISSUES,
} from "constants/fetch-keys";
2023-04-20 08:41:11 +00:00
type Props = {
handleEditIssue: (issue: IIssue) => void;
handleDeleteIssue: (issue: IIssue) => void;
2023-04-20 08:41:11 +00:00
addIssueToDate: (date: string) => void;
isCompleted: boolean;
user: ICurrentUserResponse | undefined;
userAuth: UserAuth;
2023-04-20 08:41:11 +00:00
};
export const CalendarView: React.FC<Props> = ({
handleEditIssue,
handleDeleteIssue,
addIssueToDate,
isCompleted = false,
user,
userAuth,
}) => {
const [showWeekEnds, setShowWeekEnds] = useState(false);
const [currentDate, setCurrentDate] = useState(new Date());
const [isMonthlyView, setIsMonthlyView] = useState(true);
2023-05-15 06:05:07 +00:00
const [calendarDates, setCalendarDates] = useState<ICalendarRange>({
startDate: startOfWeek(currentDate),
endDate: lastDayOfWeek(currentDate),
});
2023-05-15 06:05:07 +00:00
const router = useRouter();
const { workspaceSlug, projectId, cycleId, moduleId, viewId } = router.query;
const { calendarIssues, params, setCalendarDateRange } = useCalendarIssuesView();
const totalDate = eachDayOfInterval({
2023-05-15 06:05:07 +00:00
start: calendarDates.startDate,
end: calendarDates.endDate,
});
const onlyWeekDays = weekDayInterval({
2023-05-15 06:05:07 +00:00
start: calendarDates.startDate,
end: calendarDates.endDate,
});
const currentViewDays = showWeekEnds ? totalDate : onlyWeekDays;
const currentViewDaysData = currentViewDays.map((date: Date) => {
const filterIssue =
2023-05-15 06:05:07 +00:00
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;
2023-05-15 06:05:07 +00:00
if (source.droppableId === destination.droppableId) return;
2023-03-30 21:13:38 +00:00
const fetchKey = cycleId
2023-05-15 06:05:07 +00:00
? CYCLE_ISSUES_WITH_PARAMS(cycleId.toString(), params)
2023-03-30 21:13:38 +00:00
: moduleId
2023-05-15 06:05:07 +00:00
? MODULE_ISSUES_WITH_PARAMS(moduleId.toString(), params)
: viewId
? VIEW_ISSUES(viewId.toString(), params)
: PROJECT_ISSUES_LIST_WITH_PARAMS(projectId.toString(), params);
2023-03-30 21:13:38 +00:00
mutate<IIssue[]>(
fetchKey,
(prevData) =>
(prevData ?? []).map((p) => {
if (p.id === draggableId)
return {
...p,
target_date: destination.droppableId,
};
2023-05-15 06:05:07 +00:00
2023-03-30 21:13:38 +00:00
return p;
}),
false
);
2023-05-15 06:05:07 +00:00
issuesService
.patchIssue(
workspaceSlug as string,
projectId as string,
draggableId,
{
target_date: destination?.droppableId,
},
user
)
2023-05-15 06:05:07 +00:00
.then(() => mutate(fetchKey));
};
const changeDateRange = (startDate: Date, endDate: Date) => {
setCalendarDates({
startDate,
endDate,
});
2023-05-15 06:05:07 +00:00
setCalendarDateRange(
`${renderDateFormat(startDate)};after,${renderDateFormat(endDate)};before`
);
};
2023-05-15 06:05:07 +00:00
useEffect(() => {
setCalendarDateRange(
`${renderDateFormat(startOfWeek(currentDate))};after,${renderDateFormat(
lastDayOfWeek(currentDate)
)};before`
);
}, [currentDate]);
const isNotAllowed = userAuth.isGuest || userAuth.isViewer || isCompleted;
return calendarIssues ? (
<div className="h-full overflow-y-auto">
<DragDropContext onDragEnd={onDragEnd}>
<div className="h-full rounded-lg p-8 text-custom-text-200">
<CalendarHeader
isMonthlyView={isMonthlyView}
setIsMonthlyView={setIsMonthlyView}
showWeekEnds={showWeekEnds}
setShowWeekEnds={setShowWeekEnds}
currentDate={currentDate}
setCurrentDate={setCurrentDate}
2023-05-15 06:05:07 +00:00
changeDateRange={changeDateRange}
/>
<div
className={`grid auto-rows-[minmax(36px,1fr)] rounded-lg ${
showWeekEnds ? "grid-cols-7" : "grid-cols-5"
}`}
>
{weeks.map((date, index) => (
<div
key={index}
className={`flex items-center justify-start gap-2 border-custom-border-200 bg-custom-background-90 p-1.5 text-base font-medium text-custom-text-200 ${
!isMonthlyView
? showWeekEnds
? (index + 1) % 7 === 0
? ""
: "border-r"
: (index + 1) % 5 === 0
? ""
: "border-r"
: ""
}`}
>
<span>
{isMonthlyView
? formatDate(date, "eee").substring(0, 3)
: formatDate(date, "eee")}
</span>
{!isMonthlyView && <span>{formatDate(date, "d")}</span>}
</div>
))}
</div>
<div
className={`grid h-full ${isMonthlyView ? "auto-rows-min" : ""} ${
showWeekEnds ? "grid-cols-7" : "grid-cols-5"
} `}
>
{currentViewDaysData.map((date, index) => (
<SingleCalendarDate
index={index}
date={date}
handleEditIssue={handleEditIssue}
handleDeleteIssue={handleDeleteIssue}
addIssueToDate={addIssueToDate}
isMonthlyView={isMonthlyView}
showWeekEnds={showWeekEnds}
user={user}
isNotAllowed={isNotAllowed}
/>
))}
</div>
</div>
</DragDropContext>
</div>
) : (
<div className="flex h-full w-full items-center justify-center">
<Spinner />
</div>
);
};