fix: calendar view mutation (#1042)

This commit is contained in:
Aaryan Khandelwal 2023-05-15 11:35:07 +05:30 committed by GitHub
parent dbbd9add99
commit 290318603d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 337 additions and 207 deletions

View File

@ -1,4 +1,17 @@
import React from "react";
// headless ui
import { Popover, Transition } from "@headlessui/react"; import { Popover, Transition } from "@headlessui/react";
// ui
import { CustomMenu, ToggleSwitch } from "components/ui";
// icons
import {
CheckIcon,
ChevronDownIcon,
ChevronLeftIcon,
ChevronRightIcon,
} from "@heroicons/react/24/outline";
// helpers
import { import {
addMonths, addMonths,
addSevenDaysToDate, addSevenDaysToDate,
@ -14,26 +27,17 @@ import {
updateDateWithMonth, updateDateWithMonth,
updateDateWithYear, updateDateWithYear,
} from "helpers/calendar.helper"; } from "helpers/calendar.helper";
import React from "react"; // constants
import { MONTHS_LIST, YEARS_LIST } from "constants/calendar"; import { MONTHS_LIST, YEARS_LIST } from "constants/calendar";
import { ICalendarRange } from "types";
import {
CheckIcon,
ChevronDownIcon,
ChevronLeftIcon,
ChevronRightIcon,
} from "@heroicons/react/24/outline";
import { CustomMenu, ToggleSwitch } from "components/ui";
type Props = { type Props = {
isMonthlyView: boolean; isMonthlyView: boolean;
setIsMonthlyView: React.Dispatch<React.SetStateAction<boolean>>; setIsMonthlyView: React.Dispatch<React.SetStateAction<boolean>>;
currentDate: Date; currentDate: Date;
setCurrentDate: React.Dispatch<React.SetStateAction<Date>>; setCurrentDate: React.Dispatch<React.SetStateAction<Date>>;
setCalendarDateRange: React.Dispatch<React.SetStateAction<ICalendarRange>>;
showWeekEnds: boolean; showWeekEnds: boolean;
setShowWeekEnds: React.Dispatch<React.SetStateAction<boolean>>; setShowWeekEnds: React.Dispatch<React.SetStateAction<boolean>>;
changeDateRange: (startDate: Date, endDate: Date) => void;
}; };
export const CalendarHeader: React.FC<Props> = ({ export const CalendarHeader: React.FC<Props> = ({
@ -41,18 +45,16 @@ export const CalendarHeader: React.FC<Props> = ({
isMonthlyView, isMonthlyView,
currentDate, currentDate,
setCurrentDate, setCurrentDate,
setCalendarDateRange,
showWeekEnds, showWeekEnds,
setShowWeekEnds, setShowWeekEnds,
changeDateRange,
}) => { }) => {
const updateDate = (date: Date) => { const updateDate = (date: Date) => {
setCurrentDate(date); setCurrentDate(date);
setCalendarDateRange({ changeDateRange(startOfWeek(date), lastDayOfWeek(date));
startDate: startOfWeek(date),
endDate: lastDayOfWeek(date),
});
}; };
return ( return (
<div className="mb-4 flex items-center justify-between"> <div className="mb-4 flex items-center justify-between">
<div className="relative flex h-full w-full items-center justify-start gap-2 text-sm "> <div className="relative flex h-full w-full items-center justify-start gap-2 text-sm ">
@ -93,9 +95,13 @@ export const CalendarHeader: React.FC<Props> = ({
<div className="grid grid-cols-4 border-t border-brand-base px-2"> <div className="grid grid-cols-4 border-t border-brand-base px-2">
{MONTHS_LIST.map((month) => ( {MONTHS_LIST.map((month) => (
<button <button
onClick={() => updateDate(updateDateWithMonth(`${month.value}`, currentDate))} onClick={() =>
updateDate(updateDateWithMonth(`${month.value}`, currentDate))
}
className={`px-2 py-2 text-xs text-brand-secondary hover:font-medium hover:text-brand-base ${ className={`px-2 py-2 text-xs text-brand-secondary hover:font-medium hover:text-brand-base ${
isSameMonth(`${month.value}`, currentDate) ? "font-medium text-brand-base" : "" isSameMonth(`${month.value}`, currentDate)
? "font-medium text-brand-base"
: ""
}`} }`}
> >
{month.label} {month.label}
@ -116,10 +122,10 @@ export const CalendarHeader: React.FC<Props> = ({
updateDate(subtractMonths(currentDate, 1)); updateDate(subtractMonths(currentDate, 1));
} else { } else {
setCurrentDate(subtract7DaysToDate(currentDate)); setCurrentDate(subtract7DaysToDate(currentDate));
setCalendarDateRange({ changeDateRange(
startDate: getCurrentWeekStartDate(subtract7DaysToDate(currentDate)), getCurrentWeekStartDate(subtract7DaysToDate(currentDate)),
endDate: getCurrentWeekEndDate(subtract7DaysToDate(currentDate)), getCurrentWeekEndDate(subtract7DaysToDate(currentDate))
}); );
} }
}} }}
> >
@ -132,10 +138,10 @@ export const CalendarHeader: React.FC<Props> = ({
updateDate(addMonths(currentDate, 1)); updateDate(addMonths(currentDate, 1));
} else { } else {
setCurrentDate(addSevenDaysToDate(currentDate)); setCurrentDate(addSevenDaysToDate(currentDate));
setCalendarDateRange({ changeDateRange(
startDate: getCurrentWeekStartDate(addSevenDaysToDate(currentDate)), getCurrentWeekStartDate(addSevenDaysToDate(currentDate)),
endDate: getCurrentWeekEndDate(addSevenDaysToDate(currentDate)), getCurrentWeekEndDate(addSevenDaysToDate(currentDate))
}); );
} }
}} }}
> >
@ -152,10 +158,10 @@ export const CalendarHeader: React.FC<Props> = ({
updateDate(new Date()); updateDate(new Date());
} else { } else {
setCurrentDate(new Date()); setCurrentDate(new Date());
setCalendarDateRange({ changeDateRange(
startDate: getCurrentWeekStartDate(new Date()), getCurrentWeekStartDate(new Date()),
endDate: getCurrentWeekEndDate(new Date()), getCurrentWeekEndDate(new Date())
}); );
} }
}} }}
> >
@ -173,10 +179,7 @@ export const CalendarHeader: React.FC<Props> = ({
<CustomMenu.MenuItem <CustomMenu.MenuItem
onClick={() => { onClick={() => {
setIsMonthlyView(true); setIsMonthlyView(true);
setCalendarDateRange({ changeDateRange(startOfWeek(currentDate), lastDayOfWeek(currentDate));
startDate: startOfWeek(currentDate),
endDate: lastDayOfWeek(currentDate),
});
}} }}
className="w-52 text-sm text-brand-secondary" className="w-52 text-sm text-brand-secondary"
> >
@ -190,10 +193,10 @@ export const CalendarHeader: React.FC<Props> = ({
<CustomMenu.MenuItem <CustomMenu.MenuItem
onClick={() => { onClick={() => {
setIsMonthlyView(false); setIsMonthlyView(false);
setCalendarDateRange({ changeDateRange(
startDate: getCurrentWeekStartDate(currentDate), getCurrentWeekStartDate(currentDate),
endDate: getCurrentWeekEndDate(currentDate), getCurrentWeekEndDate(currentDate)
}); );
}} }}
className="w-52 text-sm text-brand-secondary" className="w-52 text-sm text-brand-secondary"
> >

View File

@ -1,28 +1,20 @@
import React, { useState } from "react"; import React, { useEffect, useState } from "react";
// swr
import useSWR, { mutate } from "swr";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
// ui import { mutate } from "swr";
import { DragDropContext, Draggable, DropResult } from "react-beautiful-dnd";
import { SingleCalendarDate, CalendarHeader } from "components/core";
import { Spinner } from "components/ui"; // react-beautiful-dnd
// hooks import { DragDropContext, DropResult } from "react-beautiful-dnd";
import useIssuesView from "hooks/use-issues-view";
// services // services
import issuesService from "services/issues.service"; import issuesService from "services/issues.service";
import cyclesService from "services/cycles.service"; // hooks
import modulesService from "services/modules.service"; import useCalendarIssuesView from "hooks/use-calendar-issues-view";
// fetch key // components
import { import { SingleCalendarDate, CalendarHeader } from "components/core";
CYCLE_CALENDAR_ISSUES, // ui
MODULE_CALENDAR_ISSUES, import { Spinner } from "components/ui";
PROJECT_CALENDAR_ISSUES, // helpers
} from "constants/fetch-keys";
// helper
import { renderDateFormat } from "helpers/date-time.helper"; import { renderDateFormat } from "helpers/date-time.helper";
import { import {
startOfWeek, startOfWeek,
@ -31,8 +23,15 @@ import {
weekDayInterval, weekDayInterval,
formatDate, formatDate,
} from "helpers/calendar.helper"; } from "helpers/calendar.helper";
// type // types
import { ICalendarRange, IIssue, UserAuth } from "types"; import { ICalendarRange, IIssue, UserAuth } from "types";
// fetch-keys
import {
CYCLE_ISSUES_WITH_PARAMS,
MODULE_ISSUES_WITH_PARAMS,
PROJECT_ISSUES_LIST_WITH_PARAMS,
VIEW_ISSUES,
} from "constants/fetch-keys";
type Props = { type Props = {
handleEditIssue: (issue: IIssue) => void; handleEditIssue: (issue: IIssue) => void;
@ -53,93 +52,31 @@ export const CalendarView: React.FC<Props> = ({
const [currentDate, setCurrentDate] = useState(new Date()); const [currentDate, setCurrentDate] = useState(new Date());
const [isMonthlyView, setIsMonthlyView] = useState(true); const [isMonthlyView, setIsMonthlyView] = useState(true);
const router = useRouter(); const [calendarDates, setCalendarDates] = useState<ICalendarRange>({
const { workspaceSlug, projectId, cycleId, moduleId } = router.query;
const { params } = useIssuesView();
const [calendarDateRange, setCalendarDateRange] = useState<ICalendarRange>({
startDate: startOfWeek(currentDate), startDate: startOfWeek(currentDate),
endDate: lastDayOfWeek(currentDate), endDate: lastDayOfWeek(currentDate),
}); });
const { data: projectCalendarIssues } = useSWR( const router = useRouter();
workspaceSlug && projectId ? PROJECT_CALENDAR_ISSUES(projectId as string) : null, const { workspaceSlug, projectId, cycleId, moduleId, viewId } = router.query;
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( const { calendarIssues, params, setCalendarDateRange } = useCalendarIssuesView();
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({ const totalDate = eachDayOfInterval({
start: calendarDateRange.startDate, start: calendarDates.startDate,
end: calendarDateRange.endDate, end: calendarDates.endDate,
}); });
const onlyWeekDays = weekDayInterval({ const onlyWeekDays = weekDayInterval({
start: calendarDateRange.startDate, start: calendarDates.startDate,
end: calendarDateRange.endDate, end: calendarDates.endDate,
}); });
const currentViewDays = showWeekEnds ? totalDate : onlyWeekDays; 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 currentViewDaysData = currentViewDays.map((date: Date) => {
const filterIssue = const filterIssue =
calendarIssues && calendarIssues.length > 0 calendarIssues.length > 0
? calendarIssues.filter( ? calendarIssues.filter(
(issue) => (issue) =>
issue.target_date && renderDateFormat(issue.target_date) === renderDateFormat(date) issue.target_date && renderDateFormat(issue.target_date) === renderDateFormat(date)
@ -170,13 +107,16 @@ export const CalendarView: React.FC<Props> = ({
const { source, destination, draggableId } = result; const { source, destination, draggableId } = result;
if (!destination || !workspaceSlug || !projectId) return; if (!destination || !workspaceSlug || !projectId) return;
if (source.droppableId === destination.droppableId) return; if (source.droppableId === destination.droppableId) return;
const fetchKey = cycleId const fetchKey = cycleId
? CYCLE_CALENDAR_ISSUES(projectId as string, cycleId as string) ? CYCLE_ISSUES_WITH_PARAMS(cycleId.toString(), params)
: moduleId : moduleId
? MODULE_CALENDAR_ISSUES(projectId as string, moduleId as string) ? MODULE_ISSUES_WITH_PARAMS(moduleId.toString(), params)
: PROJECT_CALENDAR_ISSUES(projectId as string); : viewId
? VIEW_ISSUES(viewId.toString(), params)
: PROJECT_ISSUES_LIST_WITH_PARAMS(projectId.toString(), params);
mutate<IIssue[]>( mutate<IIssue[]>(
fetchKey, fetchKey,
@ -187,16 +127,38 @@ export const CalendarView: React.FC<Props> = ({
...p, ...p,
target_date: destination.droppableId, target_date: destination.droppableId,
}; };
return p; return p;
}), }),
false false
); );
issuesService.patchIssue(workspaceSlug as string, projectId as string, draggableId, { issuesService
.patchIssue(workspaceSlug as string, projectId as string, draggableId, {
target_date: destination?.droppableId, target_date: destination?.droppableId,
}); })
.then(() => mutate(fetchKey));
}; };
const changeDateRange = (startDate: Date, endDate: Date) => {
setCalendarDates({
startDate,
endDate,
});
setCalendarDateRange(
`${renderDateFormat(startDate)};after,${renderDateFormat(endDate)};before`
);
};
useEffect(() => {
setCalendarDateRange(
`${renderDateFormat(startOfWeek(currentDate))};after,${renderDateFormat(
lastDayOfWeek(currentDate)
)};before`
);
}, [currentDate]);
const isNotAllowed = userAuth.isGuest || userAuth.isViewer || isCompleted; const isNotAllowed = userAuth.isGuest || userAuth.isViewer || isCompleted;
return calendarIssues ? ( return calendarIssues ? (
@ -210,7 +172,7 @@ export const CalendarView: React.FC<Props> = ({
setShowWeekEnds={setShowWeekEnds} setShowWeekEnds={setShowWeekEnds}
currentDate={currentDate} currentDate={currentDate}
setCurrentDate={setCurrentDate} setCurrentDate={setCurrentDate}
setCalendarDateRange={setCalendarDateRange} changeDateRange={changeDateRange}
/> />
<div <div

View File

@ -10,6 +10,7 @@ import { DraggableProvided, DraggableStateSnapshot } from "react-beautiful-dnd";
// services // services
import issuesService from "services/issues.service"; import issuesService from "services/issues.service";
// hooks // hooks
import useCalendarIssuesView from "hooks/use-calendar-issues-view";
import useIssuesProperties from "hooks/use-issue-properties"; import useIssuesProperties from "hooks/use-issue-properties";
import useToast from "hooks/use-toast"; import useToast from "hooks/use-toast";
// components // components
@ -29,9 +30,10 @@ import { copyTextToClipboard, truncateText } from "helpers/string.helper";
import { IIssue } from "types"; import { IIssue } from "types";
// fetch-keys // fetch-keys
import { import {
CYCLE_CALENDAR_ISSUES, CYCLE_ISSUES_WITH_PARAMS,
MODULE_CALENDAR_ISSUES, MODULE_ISSUES_WITH_PARAMS,
PROJECT_CALENDAR_ISSUES, PROJECT_ISSUES_LIST_WITH_PARAMS,
VIEW_ISSUES,
} from "constants/fetch-keys"; } from "constants/fetch-keys";
type Props = { type Props = {
@ -54,10 +56,12 @@ export const SingleCalendarIssue: React.FC<Props> = ({
isNotAllowed, isNotAllowed,
}) => { }) => {
const router = useRouter(); const router = useRouter();
const { workspaceSlug, projectId, cycleId, moduleId } = router.query; const { workspaceSlug, projectId, cycleId, moduleId, viewId } = router.query;
const { setToastAlert } = useToast(); const { setToastAlert } = useToast();
const { params } = useCalendarIssuesView();
const [properties] = useIssuesProperties(workspaceSlug as string, projectId as string); const [properties] = useIssuesProperties(workspaceSlug as string, projectId as string);
const partialUpdateIssue = useCallback( const partialUpdateIssue = useCallback(
@ -65,20 +69,25 @@ export const SingleCalendarIssue: React.FC<Props> = ({
if (!workspaceSlug || !projectId) return; if (!workspaceSlug || !projectId) return;
const fetchKey = cycleId const fetchKey = cycleId
? CYCLE_CALENDAR_ISSUES(projectId as string, cycleId as string) ? CYCLE_ISSUES_WITH_PARAMS(cycleId.toString(), params)
: moduleId : moduleId
? MODULE_CALENDAR_ISSUES(projectId as string, moduleId as string) ? MODULE_ISSUES_WITH_PARAMS(moduleId.toString(), params)
: PROJECT_CALENDAR_ISSUES(projectId as string); : viewId
? VIEW_ISSUES(viewId.toString(), params)
: PROJECT_ISSUES_LIST_WITH_PARAMS(projectId.toString(), params);
mutate<IIssue[]>( mutate<IIssue[]>(
fetchKey, fetchKey,
(prevData) => (prevData) =>
(prevData ?? []).map((p) => { (prevData ?? []).map((p) => {
if (p.id === issueId) if (p.id === issueId) {
return { return {
...p, ...p,
formData, ...formData,
assignees: formData?.assignees_list ?? p.assignees,
}; };
}
return p; return p;
}), }),
false false
@ -87,13 +96,13 @@ export const SingleCalendarIssue: React.FC<Props> = ({
issuesService issuesService
.patchIssue(workspaceSlug as string, projectId as string, issueId as string, formData) .patchIssue(workspaceSlug as string, projectId as string, issueId as string, formData)
.then(() => { .then(() => {
mutate<IIssue[]>(fetchKey); mutate(fetchKey);
}) })
.catch((error) => { .catch((error) => {
console.log(error); console.log(error);
}); });
}, },
[workspaceSlug, projectId, cycleId, moduleId] [workspaceSlug, projectId, cycleId, moduleId, params]
); );
const handleCopyText = () => { const handleCopyText = () => {

View File

@ -9,22 +9,21 @@ import { Dialog, Transition } from "@headlessui/react";
// services // services
import issueServices from "services/issues.service"; import issueServices from "services/issues.service";
// hooks // hooks
import useToast from "hooks/use-toast";
import useIssuesView from "hooks/use-issues-view"; import useIssuesView from "hooks/use-issues-view";
import useCalendarIssuesView from "hooks/use-calendar-issues-view";
import useToast from "hooks/use-toast";
// icons // icons
import { ExclamationTriangleIcon } from "@heroicons/react/24/outline"; import { ExclamationTriangleIcon } from "@heroicons/react/24/outline";
// ui // ui
import { SecondaryButton, DangerButton } from "components/ui"; import { SecondaryButton, DangerButton } from "components/ui";
// types // types
import type { CycleIssueResponse, IIssue, ModuleIssueResponse } from "types"; import type { IIssue } from "types";
// fetch-keys // fetch-keys
import { import {
CYCLE_CALENDAR_ISSUES,
CYCLE_ISSUES_WITH_PARAMS, CYCLE_ISSUES_WITH_PARAMS,
MODULE_CALENDAR_ISSUES,
MODULE_ISSUES_WITH_PARAMS, MODULE_ISSUES_WITH_PARAMS,
PROJECT_CALENDAR_ISSUES,
PROJECT_ISSUES_LIST_WITH_PARAMS, PROJECT_ISSUES_LIST_WITH_PARAMS,
VIEW_ISSUES,
} from "constants/fetch-keys"; } from "constants/fetch-keys";
type Props = { type Props = {
@ -37,9 +36,10 @@ export const DeleteIssueModal: React.FC<Props> = ({ isOpen, handleClose, data })
const [isDeleteLoading, setIsDeleteLoading] = useState(false); const [isDeleteLoading, setIsDeleteLoading] = useState(false);
const router = useRouter(); const router = useRouter();
const { workspaceSlug, projectId, cycleId, moduleId } = router.query; const { workspaceSlug, projectId, cycleId, moduleId, viewId } = router.query;
const { issueView, params } = useIssuesView(); const { issueView, params } = useIssuesView();
const { params: calendarParams } = useCalendarIssuesView();
const { setToastAlert } = useToast(); const { setToastAlert } = useToast();
@ -61,12 +61,18 @@ export const DeleteIssueModal: React.FC<Props> = ({ isOpen, handleClose, data })
.then(() => { .then(() => {
if (issueView === "calendar") { if (issueView === "calendar") {
const calendarFetchKey = cycleId const calendarFetchKey = cycleId
? CYCLE_CALENDAR_ISSUES(projectId as string, cycleId as string) ? CYCLE_ISSUES_WITH_PARAMS(cycleId.toString(), calendarParams)
: moduleId : moduleId
? MODULE_CALENDAR_ISSUES(projectId as string, moduleId as string) ? MODULE_ISSUES_WITH_PARAMS(moduleId.toString(), calendarParams)
: PROJECT_CALENDAR_ISSUES(projectId as string); : viewId
? VIEW_ISSUES(viewId.toString(), calendarParams)
: PROJECT_ISSUES_LIST_WITH_PARAMS(projectId.toString(), calendarParams);
mutate<IIssue[]>(calendarFetchKey); mutate<IIssue[]>(
calendarFetchKey,
(prevData) => (prevData ?? []).filter((p) => p.id !== data.id),
false
);
} else { } else {
if (cycleId) mutate(CYCLE_ISSUES_WITH_PARAMS(cycleId as string, params)); if (cycleId) mutate(CYCLE_ISSUES_WITH_PARAMS(cycleId as string, params));
else if (moduleId) mutate(MODULE_ISSUES_WITH_PARAMS(moduleId as string, params)); else if (moduleId) mutate(MODULE_ISSUES_WITH_PARAMS(moduleId as string, params));

View File

@ -12,14 +12,14 @@ import modulesService from "services/modules.service";
import issuesService from "services/issues.service"; import issuesService from "services/issues.service";
// hooks // hooks
import useUser from "hooks/use-user"; import useUser from "hooks/use-user";
import useIssuesView from "hooks/use-issues-view";
import useCalendarIssuesView from "hooks/use-calendar-issues-view";
import useToast from "hooks/use-toast"; import useToast from "hooks/use-toast";
// components // components
import { IssueForm } from "components/issues"; import { IssueForm } from "components/issues";
// hooks
import useIssuesView from "hooks/use-issues-view";
// types // types
import type { IIssue } from "types"; import type { IIssue } from "types";
// fetch keys // fetch-keys
import { import {
PROJECT_ISSUES_DETAILS, PROJECT_ISSUES_DETAILS,
PROJECT_ISSUES_LIST, PROJECT_ISSUES_LIST,
@ -31,9 +31,7 @@ import {
MODULE_ISSUES_WITH_PARAMS, MODULE_ISSUES_WITH_PARAMS,
CYCLE_DETAILS, CYCLE_DETAILS,
MODULE_DETAILS, MODULE_DETAILS,
PROJECT_CALENDAR_ISSUES, VIEW_ISSUES,
CYCLE_CALENDAR_ISSUES,
MODULE_CALENDAR_ISSUES,
} from "constants/fetch-keys"; } from "constants/fetch-keys";
export interface IssuesModalProps { export interface IssuesModalProps {
@ -56,9 +54,10 @@ export const CreateUpdateIssueModal: React.FC<IssuesModalProps> = ({
const [activeProject, setActiveProject] = useState<string | null>(null); const [activeProject, setActiveProject] = useState<string | null>(null);
const router = useRouter(); const router = useRouter();
const { workspaceSlug, projectId, cycleId, moduleId } = router.query; const { workspaceSlug, projectId, cycleId, moduleId, viewId } = router.query;
const { issueView, params } = useIssuesView(); const { issueView, params } = useIssuesView();
const { params: calendarParams } = useCalendarIssuesView();
if (cycleId) prePopulateData = { ...prePopulateData, cycle: cycleId as string }; if (cycleId) prePopulateData = { ...prePopulateData, cycle: cycleId as string };
if (moduleId) prePopulateData = { ...prePopulateData, module: moduleId as string }; if (moduleId) prePopulateData = { ...prePopulateData, module: moduleId as string };
@ -128,23 +127,24 @@ export const CreateUpdateIssueModal: React.FC<IssuesModalProps> = ({
}); });
}; };
const calendarFetchKey = cycleId
? CYCLE_ISSUES_WITH_PARAMS(cycleId.toString(), calendarParams)
: moduleId
? MODULE_ISSUES_WITH_PARAMS(moduleId.toString(), calendarParams)
: viewId
? VIEW_ISSUES(viewId.toString(), calendarParams)
: PROJECT_ISSUES_LIST_WITH_PARAMS(projectId?.toString() ?? "", calendarParams);
const createIssue = async (payload: Partial<IIssue>) => { const createIssue = async (payload: Partial<IIssue>) => {
await issuesService await issuesService
.createIssues(workspaceSlug as string, activeProject ?? "", payload) .createIssues(workspaceSlug as string, activeProject ?? "", payload)
.then((res) => { .then((res) => {
mutate(PROJECT_ISSUES_LIST_WITH_PARAMS(activeProject ?? "", params)); mutate(PROJECT_ISSUES_LIST_WITH_PARAMS(activeProject ?? "", params));
if (issueView === "calendar") mutate(calendarFetchKey);
if (payload.cycle && payload.cycle !== "") addIssueToCycle(res.id, payload.cycle); if (payload.cycle && payload.cycle !== "") addIssueToCycle(res.id, payload.cycle);
if (payload.module && payload.module !== "") addIssueToModule(res.id, payload.module); if (payload.module && payload.module !== "") addIssueToModule(res.id, payload.module);
const calendarFetchKey = 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<IIssue[]>(calendarFetchKey);
if (!createMore) handleClose(); if (!createMore) handleClose();
setToastAlert({ setToastAlert({
@ -173,29 +173,8 @@ export const CreateUpdateIssueModal: React.FC<IssuesModalProps> = ({
if (isUpdatingSingleIssue) { if (isUpdatingSingleIssue) {
mutate<IIssue>(PROJECT_ISSUES_DETAILS, (prevData) => ({ ...prevData, ...res }), false); mutate<IIssue>(PROJECT_ISSUES_DETAILS, (prevData) => ({ ...prevData, ...res }), false);
} else { } else {
if (issueView === "calendar") { if (issueView === "calendar") mutate(calendarFetchKey);
const calendarFetchKey = cycleId else mutate(PROJECT_ISSUES_LIST_WITH_PARAMS(activeProject ?? "", params));
? 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<IIssue[]>(calendarFetchKey, (prevData) =>
(prevData ?? []).map((i) => {
if (i.id === res.id) return { ...i, ...res };
return i;
})
);
} else {
mutate<IIssue[]>(
PROJECT_ISSUES_LIST_WITH_PARAMS(activeProject ?? "", params),
(prevData) =>
(prevData ?? []).map((i) => {
if (i.id === res.id) return { ...i, ...res };
return i;
})
);
}
} }
if (payload.cycle && payload.cycle !== "") addIssueToCycle(res.id, payload.cycle); if (payload.cycle && payload.cycle !== "") addIssueToCycle(res.id, payload.cycle);

View File

@ -1,13 +1,14 @@
import { IAnalyticsParams, IJiraMetadata } from "types"; import { IAnalyticsParams, IJiraMetadata } from "types";
const paramsToKey = (params: any) => { const paramsToKey = (params: any) => {
const { state, priority, assignees, created_by, labels } = params; const { state, priority, assignees, created_by, labels, target_date } = params;
let stateKey = state ? state.split(",") : []; let stateKey = state ? state.split(",") : [];
let priorityKey = priority ? priority.split(",") : []; let priorityKey = priority ? priority.split(",") : [];
let assigneesKey = assignees ? assignees.split(",") : []; let assigneesKey = assignees ? assignees.split(",") : [];
let createdByKey = created_by ? created_by.split(",") : []; let createdByKey = created_by ? created_by.split(",") : [];
let labelsKey = labels ? labels.split(",") : []; let labelsKey = labels ? labels.split(",") : [];
const targetDateKey = target_date ?? "";
const type = params.type ? params.type.toUpperCase() : "NULL"; const type = params.type ? params.type.toUpperCase() : "NULL";
const groupBy = params.group_by ? params.group_by.toUpperCase() : "NULL"; const groupBy = params.group_by ? params.group_by.toUpperCase() : "NULL";
const orderBy = params.order_by ? params.order_by.toUpperCase() : "NULL"; const orderBy = params.order_by ? params.order_by.toUpperCase() : "NULL";
@ -19,7 +20,7 @@ const paramsToKey = (params: any) => {
createdByKey = createdByKey.sort().join("_"); createdByKey = createdByKey.sort().join("_");
labelsKey = labelsKey.sort().join("_"); labelsKey = labelsKey.sort().join("_");
return `${stateKey}_${priorityKey}_${assigneesKey}_${createdByKey}_${type}_${groupBy}_${orderBy}_${labelsKey}`; return `${stateKey}_${priorityKey}_${assigneesKey}_${createdByKey}_${type}_${groupBy}_${orderBy}_${labelsKey}_${targetDateKey}`;
}; };
export const CURRENT_USER = "CURRENT_USER"; export const CURRENT_USER = "CURRENT_USER";
@ -148,14 +149,6 @@ export const GITHUB_REPOSITORY_INFO = (workspaceSlug: string, repoName: string)
export const SLACK_CHANNEL_INFO = (workspaceSlug: string, projectId: string) => export const SLACK_CHANNEL_INFO = (workspaceSlug: string, projectId: string) =>
`SLACK_CHANNEL_INFO_${workspaceSlug.toString().toUpperCase()}_${projectId.toUpperCase()}`; `SLACK_CHANNEL_INFO_${workspaceSlug.toString().toUpperCase()}_${projectId.toUpperCase()}`;
// Calendar
export const PROJECT_CALENDAR_ISSUES = (projectId: string) =>
`CALENDAR_ISSUES_${projectId.toUpperCase()}`;
export const CYCLE_CALENDAR_ISSUES = (projectId: string, cycleId: string) =>
`CALENDAR_ISSUES_${projectId.toUpperCase()}_${cycleId.toUpperCase()}`;
export const MODULE_CALENDAR_ISSUES = (projectId: string, moduleId: string) =>
`CALENDAR_ISSUES_${projectId.toUpperCase()}_${moduleId.toUpperCase()}`;
// Pages // Pages
export const RECENT_PAGES_LIST = (projectId: string) => export const RECENT_PAGES_LIST = (projectId: string) =>
`RECENT_PAGES_LIST_${projectId.toUpperCase()}`; `RECENT_PAGES_LIST_${projectId.toUpperCase()}`;

View File

@ -34,6 +34,7 @@ type IssueViewProps = {
groupByProperty: TIssueGroupByOptions; groupByProperty: TIssueGroupByOptions;
orderBy: TIssueOrderByOptions; orderBy: TIssueOrderByOptions;
showEmptyGroups: boolean; showEmptyGroups: boolean;
calendarDateRange: string;
filters: IIssueFilterOptions; filters: IIssueFilterOptions;
}; };
@ -43,6 +44,7 @@ type ReducerActionType = {
| "SET_ISSUE_VIEW" | "SET_ISSUE_VIEW"
| "SET_ORDER_BY_PROPERTY" | "SET_ORDER_BY_PROPERTY"
| "SET_SHOW_EMPTY_STATES" | "SET_SHOW_EMPTY_STATES"
| "SET_CALENDAR_DATE_RANGE"
| "SET_FILTERS" | "SET_FILTERS"
| "SET_GROUP_BY_PROPERTY" | "SET_GROUP_BY_PROPERTY"
| "RESET_TO_DEFAULT"; | "RESET_TO_DEFAULT";
@ -53,6 +55,7 @@ type ContextType = IssueViewProps & {
setGroupByProperty: (property: TIssueGroupByOptions) => void; setGroupByProperty: (property: TIssueGroupByOptions) => void;
setOrderBy: (property: TIssueOrderByOptions) => void; setOrderBy: (property: TIssueOrderByOptions) => void;
setShowEmptyGroups: (property: boolean) => void; setShowEmptyGroups: (property: boolean) => void;
setCalendarDateRange: (property: string) => void;
setFilters: (filters: Partial<IIssueFilterOptions>, saveToServer?: boolean) => void; setFilters: (filters: Partial<IIssueFilterOptions>, saveToServer?: boolean) => void;
resetFilterToDefault: () => void; resetFilterToDefault: () => void;
setNewFilterDefaultView: () => void; setNewFilterDefaultView: () => void;
@ -64,6 +67,7 @@ type StateType = {
groupByProperty: TIssueGroupByOptions; groupByProperty: TIssueGroupByOptions;
orderBy: TIssueOrderByOptions; orderBy: TIssueOrderByOptions;
showEmptyGroups: boolean; showEmptyGroups: boolean;
calendarDateRange: string;
filters: IIssueFilterOptions; filters: IIssueFilterOptions;
}; };
type ReducerFunctionType = (state: StateType, action: ReducerActionType) => StateType; type ReducerFunctionType = (state: StateType, action: ReducerActionType) => StateType;
@ -73,6 +77,7 @@ export const initialState: StateType = {
groupByProperty: null, groupByProperty: null,
orderBy: "-created_at", orderBy: "-created_at",
showEmptyGroups: true, showEmptyGroups: true,
calendarDateRange: "",
filters: { filters: {
type: null, type: null,
priority: null, priority: null,
@ -144,6 +149,18 @@ export const reducer: ReducerFunctionType = (state, action) => {
}; };
} }
case "SET_CALENDAR_DATE_RANGE": {
const newState = {
...state,
calendarDateRange: payload?.calendarDateRange || "",
};
return {
...state,
...newState,
};
}
case "SET_FILTERS": { case "SET_FILTERS": {
const newState = { const newState = {
...state, ...state,
@ -410,6 +427,37 @@ export const IssueViewContextProvider: React.FC<{ children: React.ReactNode }> =
[projectId, workspaceSlug, state, mutateMyViewProps] [projectId, workspaceSlug, state, mutateMyViewProps]
); );
const setCalendarDateRange = useCallback(
(value: string) => {
dispatch({
type: "SET_CALENDAR_DATE_RANGE",
payload: {
calendarDateRange: value,
},
});
if (!workspaceSlug || !projectId) return;
mutateMyViewProps((prevData) => {
if (!prevData) return prevData;
return {
...prevData,
view_props: {
...state,
calendarDateRange: value,
},
};
}, false);
saveDataToServer(workspaceSlug as string, projectId as string, {
...state,
calendarDateRange: value,
});
},
[projectId, workspaceSlug, state, mutateMyViewProps]
);
const setFilters = useCallback( const setFilters = useCallback(
(property: Partial<IIssueFilterOptions>, saveToServer = true) => { (property: Partial<IIssueFilterOptions>, saveToServer = true) => {
Object.keys(property).forEach((key) => { Object.keys(property).forEach((key) => {
@ -577,6 +625,8 @@ export const IssueViewContextProvider: React.FC<{ children: React.ReactNode }> =
setGroupByProperty, setGroupByProperty,
orderBy: state.orderBy, orderBy: state.orderBy,
showEmptyGroups: state.showEmptyGroups, showEmptyGroups: state.showEmptyGroups,
calendarDateRange: state.calendarDateRange,
setCalendarDateRange,
setOrderBy, setOrderBy,
setShowEmptyGroups, setShowEmptyGroups,
filters: state.filters, filters: state.filters,

View File

@ -0,0 +1,124 @@
import { useContext } from "react";
import { useRouter } from "next/router";
import useSWR from "swr";
// contexts
import { issueViewContext } from "contexts/issue-view.context";
// services
import issuesService from "services/issues.service";
import cyclesService from "services/cycles.service";
import modulesService from "services/modules.service";
// types
import { IIssue } from "types";
// fetch-keys
import {
CYCLE_ISSUES_WITH_PARAMS,
MODULE_ISSUES_WITH_PARAMS,
PROJECT_ISSUES_LIST_WITH_PARAMS,
VIEW_ISSUES,
} from "constants/fetch-keys";
const useCalendarIssuesView = () => {
const {
issueView,
calendarDateRange,
setCalendarDateRange,
filters,
setFilters,
resetFilterToDefault,
setNewFilterDefaultView,
setIssueView,
} = useContext(issueViewContext);
const router = useRouter();
const { workspaceSlug, projectId, cycleId, moduleId, viewId } = router.query;
const params: any = {
assignees: filters?.assignees ? filters?.assignees.join(",") : undefined,
state: filters?.state ? filters?.state.join(",") : undefined,
priority: filters?.priority ? filters?.priority.join(",") : undefined,
type: filters?.type ? filters?.type : undefined,
labels: filters?.labels ? filters?.labels.join(",") : undefined,
issue__assignees__id: filters?.issue__assignees__id
? filters?.issue__assignees__id.join(",")
: undefined,
issue__labels__id: filters?.issue__labels__id
? filters?.issue__labels__id.join(",")
: undefined,
created_by: filters?.created_by ? filters?.created_by.join(",") : undefined,
target_date: calendarDateRange,
};
const { data: projectCalendarIssues } = useSWR(
workspaceSlug && projectId
? PROJECT_ISSUES_LIST_WITH_PARAMS(projectId.toString(), params)
: null,
workspaceSlug && projectId
? () =>
issuesService.getIssuesWithParams(workspaceSlug.toString(), projectId.toString(), params)
: null
);
const { data: cycleCalendarIssues } = useSWR(
workspaceSlug && projectId && cycleId
? CYCLE_ISSUES_WITH_PARAMS(cycleId.toString(), params)
: null,
workspaceSlug && projectId && cycleId
? () =>
cyclesService.getCycleIssuesWithParams(
workspaceSlug.toString(),
projectId.toString(),
cycleId.toString(),
params
)
: null
);
const { data: moduleCalendarIssues } = useSWR(
workspaceSlug && projectId && moduleId
? MODULE_ISSUES_WITH_PARAMS(moduleId.toString(), params)
: null,
workspaceSlug && projectId && moduleId
? () =>
modulesService.getModuleIssuesWithParams(
workspaceSlug.toString(),
projectId.toString(),
moduleId.toString(),
params
)
: null
);
const { data: viewCalendarIssues } = useSWR(
workspaceSlug && projectId && viewId && params ? VIEW_ISSUES(viewId.toString(), params) : null,
workspaceSlug && projectId && viewId && params
? () =>
issuesService.getIssuesWithParams(workspaceSlug.toString(), projectId.toString(), params)
: null
);
const calendarIssues = cycleId
? (cycleCalendarIssues as IIssue[])
: moduleId
? (moduleCalendarIssues as IIssue[])
: viewId
? (viewCalendarIssues as IIssue[])
: (projectCalendarIssues as IIssue[]);
return {
issueView,
calendarIssues: calendarIssues ?? [],
calendarDateRange,
setCalendarDateRange,
filters,
setFilters,
params,
resetFilterToDefault,
setNewFilterDefaultView,
setIssueView,
} as const;
};
export default useCalendarIssuesView;

View File

@ -33,6 +33,8 @@ const useIssuesView = () => {
setOrderBy, setOrderBy,
showEmptyGroups, showEmptyGroups,
setShowEmptyGroups, setShowEmptyGroups,
calendarDateRange,
setCalendarDateRange,
filters, filters,
setFilters, setFilters,
resetFilterToDefault, resetFilterToDefault,
@ -175,6 +177,8 @@ const useIssuesView = () => {
setOrderBy, setOrderBy,
showEmptyGroups, showEmptyGroups,
setShowEmptyGroups, setShowEmptyGroups,
calendarDateRange,
setCalendarDateRange,
filters, filters,
setFilters, setFilters,
params, params,

View File

@ -273,7 +273,6 @@ export interface IIssueViewOptions {
group_by: TIssueGroupByOptions; group_by: TIssueGroupByOptions;
order_by: TIssueOrderByOptions; order_by: TIssueOrderByOptions;
filters: IIssueFilterOptions; filters: IIssueFilterOptions;
target_date: string;
} }
export interface IIssueAttachment { export interface IIssueAttachment {

View File

@ -55,6 +55,7 @@ type ProjectViewTheme = {
issueView: TIssueViewOptions; issueView: TIssueViewOptions;
groupByProperty: TIssueGroupByOptions; groupByProperty: TIssueGroupByOptions;
orderBy: TIssueOrderByOptions; orderBy: TIssueOrderByOptions;
calendarDateRange: string;
filters: IIssueFilterOptions; filters: IIssueFilterOptions;
}; };