diff --git a/web/components/cycles/active-cycle-details.tsx b/web/components/cycles/active-cycle-details.tsx index c18399101..ea7ffa748 100644 --- a/web/components/cycles/active-cycle-details.tsx +++ b/web/components/cycles/active-cycle-details.tsx @@ -75,12 +75,12 @@ export const ActiveCycleDetails: React.FC = observer((props const { setToastAlert } = useToast(); - const { isLoading } = useSWR( + useSWR( workspaceSlug && projectId ? `ACTIVE_CYCLE_ISSUE_${projectId}_CURRENT` : null, workspaceSlug && projectId ? () => cycleStore.fetchCycles(workspaceSlug, projectId, "current") : null ); - const activeCycle = cycleStore.cycles?.[projectId] || null; + const activeCycle = cycleStore.cycles?.[projectId]?.active || null; const cycle = activeCycle ? activeCycle[0] : null; const issues = (cycleStore?.active_cycle_issues as any) || null; @@ -94,7 +94,7 @@ export const ActiveCycleDetails: React.FC = observer((props // : null // ) as { data: IIssue[] | undefined }; - if (isLoading) + if (!cycle) return ( @@ -143,7 +143,7 @@ export const ActiveCycleDetails: React.FC = observer((props e.preventDefault(); if (!workspaceSlug || !projectId) return; - cycleStore.addCycleToFavorites(workspaceSlug?.toString(), projectId.toString(), cycle.id).catch(() => { + cycleStore.addCycleToFavorites(workspaceSlug?.toString(), projectId.toString(), cycle).catch(() => { setToastAlert({ type: "error", title: "Error!", @@ -156,7 +156,7 @@ export const ActiveCycleDetails: React.FC = observer((props e.preventDefault(); if (!workspaceSlug || !projectId) return; - cycleStore.removeCycleFromFavorites(workspaceSlug?.toString(), projectId.toString(), cycle.id).catch(() => { + cycleStore.removeCycleFromFavorites(workspaceSlug?.toString(), projectId.toString(), cycle).catch(() => { setToastAlert({ type: "error", title: "Error!", diff --git a/web/components/cycles/cycles-board-card.tsx b/web/components/cycles/cycles-board-card.tsx index 89e1bef89..b9398c4a7 100644 --- a/web/components/cycles/cycles-board-card.tsx +++ b/web/components/cycles/cycles-board-card.tsx @@ -87,7 +87,7 @@ export const CyclesBoardCard: FC = (props) => { e.preventDefault(); if (!workspaceSlug || !projectId) return; - cycleStore.addCycleToFavorites(workspaceSlug?.toString(), projectId.toString(), cycle.id).catch(() => { + cycleStore.addCycleToFavorites(workspaceSlug?.toString(), projectId.toString(), cycle).catch(() => { setToastAlert({ type: "error", title: "Error!", @@ -100,7 +100,7 @@ export const CyclesBoardCard: FC = (props) => { e.preventDefault(); if (!workspaceSlug || !projectId) return; - cycleStore.removeCycleFromFavorites(workspaceSlug?.toString(), projectId.toString(), cycle.id).catch(() => { + cycleStore.removeCycleFromFavorites(workspaceSlug?.toString(), projectId.toString(), cycle).catch(() => { setToastAlert({ type: "error", title: "Error!", diff --git a/web/components/cycles/cycles-board.tsx b/web/components/cycles/cycles-board.tsx index 61d21c2e0..85033ff5f 100644 --- a/web/components/cycles/cycles-board.tsx +++ b/web/components/cycles/cycles-board.tsx @@ -12,7 +12,7 @@ export interface ICyclesBoard { filter: string; workspaceSlug: string; projectId: string; - peekCycle: string; + peekCycle: string | undefined; } export const CyclesBoard: FC = observer((props) => { diff --git a/web/components/cycles/cycles-gantt.tsx b/web/components/cycles/cycles-gantt.tsx deleted file mode 100644 index 7b370ba30..000000000 --- a/web/components/cycles/cycles-gantt.tsx +++ /dev/null @@ -1 +0,0 @@ -export const CycleGantt = () => <>; diff --git a/web/components/cycles/cycles-list-item.tsx b/web/components/cycles/cycles-list-item.tsx index 0d2991933..f93e428e7 100644 --- a/web/components/cycles/cycles-list-item.tsx +++ b/web/components/cycles/cycles-list-item.tsx @@ -87,7 +87,7 @@ export const CyclesListItem: FC = (props) => { e.preventDefault(); if (!workspaceSlug || !projectId) return; - cycleStore.addCycleToFavorites(workspaceSlug?.toString(), projectId.toString(), cycle.id).catch(() => { + cycleStore.addCycleToFavorites(workspaceSlug?.toString(), projectId.toString(), cycle).catch(() => { setToastAlert({ type: "error", title: "Error!", @@ -100,7 +100,7 @@ export const CyclesListItem: FC = (props) => { e.preventDefault(); if (!workspaceSlug || !projectId) return; - cycleStore.removeCycleFromFavorites(workspaceSlug?.toString(), projectId.toString(), cycle.id).catch(() => { + cycleStore.removeCycleFromFavorites(workspaceSlug?.toString(), projectId.toString(), cycle).catch(() => { setToastAlert({ type: "error", title: "Error!", diff --git a/web/components/cycles/cycles-view.tsx b/web/components/cycles/cycles-view.tsx index 4eea43e6d..a4522f711 100644 --- a/web/components/cycles/cycles-view.tsx +++ b/web/components/cycles/cycles-view.tsx @@ -15,7 +15,7 @@ export interface ICyclesView { layout: TCycleLayout; workspaceSlug: string; projectId: string; - peekCycle: string; + peekCycle: string | undefined; } export const CyclesView: FC = observer((props) => { @@ -30,7 +30,14 @@ export const CyclesView: FC = observer((props) => { workspaceSlug && projectId && filter ? () => cycleStore.fetchCycles(workspaceSlug, projectId, filter) : null ); - const cyclesList = cycleStore.cycles?.[projectId]; + const cyclesList = + filter === "completed" + ? cycleStore.projectCompletedCycles + : filter === "draft" + ? cycleStore.projectDraftCycles + : filter === "upcoming" + ? cycleStore.projectUpcomingCycles + : cycleStore.projectCycles; return ( <> diff --git a/web/components/cycles/index.ts b/web/components/cycles/index.ts index ea9568478..db5e9de9e 100644 --- a/web/components/cycles/index.ts +++ b/web/components/cycles/index.ts @@ -5,7 +5,6 @@ export * from "./gantt-chart"; export * from "./cycles-view"; export * from "./form"; export * from "./modal"; -export * from "./select"; export * from "./sidebar"; export * from "./transfer-issues-modal"; export * from "./transfer-issues"; @@ -13,7 +12,6 @@ export * from "./cycles-list"; export * from "./cycles-list-item"; export * from "./cycles-board"; export * from "./cycles-board-card"; -export * from "./cycles-gantt"; export * from "./delete-modal"; export * from "./cycle-peek-overview"; export * from "./cycles-list-item"; diff --git a/web/components/cycles/modal.tsx b/web/components/cycles/modal.tsx index bb266e9eb..d941e5e4b 100644 --- a/web/components/cycles/modal.tsx +++ b/web/components/cycles/modal.tsx @@ -50,7 +50,7 @@ export const CycleCreateUpdateModal: React.FC = (props) => { const updateCycle = async (cycleId: string, payload: Partial) => cycleStore - .updateCycle(workspaceSlug, projectId, cycleId, payload) + .patchCycle(workspaceSlug, projectId, cycleId, payload) .then(() => { setToastAlert({ type: "success", diff --git a/web/components/cycles/select.tsx b/web/components/cycles/select.tsx deleted file mode 100644 index 9944a39b2..000000000 --- a/web/components/cycles/select.tsx +++ /dev/null @@ -1,122 +0,0 @@ -import React, { useState } from "react"; -import { useRouter } from "next/router"; -import useSWR from "swr"; -import { Listbox, Transition } from "@headlessui/react"; -// icons -import { ContrastIcon } from "@plane/ui"; -import { Plus } from "lucide-react"; -// services -import { CycleService } from "services/cycle.service"; -// components -import { CycleCreateUpdateModal } from "components/cycles"; -// fetch-keys -import { CYCLES_LIST } from "constants/fetch-keys"; - -export type IssueCycleSelectProps = { - projectId: string; - value: any; - onChange: (value: any) => void; - multiple?: boolean; -}; - -const cycleService = new CycleService(); - -export const CycleSelect: React.FC = ({ projectId, value, onChange, multiple = false }) => { - // states - const [isCycleModalActive, setCycleModalActive] = useState(false); - - const router = useRouter(); - const { workspaceSlug } = router.query; - - const { data: cycles } = useSWR( - workspaceSlug && projectId ? CYCLES_LIST(projectId) : null, - workspaceSlug && projectId - ? () => cycleService.getCyclesWithParams(workspaceSlug as string, projectId as string, "all") - : null - ); - - const options = cycles?.map((cycle) => ({ value: cycle.id, display: cycle.name })); - - const openCycleModal = () => { - setCycleModalActive(true); - }; - - const closeCycleModal = () => { - setCycleModalActive(false); - }; - - return ( - <> - {workspaceSlug && projectId && ( - - )} - - {({ open }) => ( - <> - - -
- {cycles?.find((c) => c.id === value)?.name ?? "Cycles"} -
-
- - - -
- {options ? ( - options.length > 0 ? ( - options.map((option) => ( - - `${ - selected || (Array.isArray(value) ? value.includes(option.value) : value === option.value) - ? "bg-indigo-50 font-medium" - : "" - } ${ - active ? "bg-indigo-50" : "" - } relative cursor-pointer select-none p-2 text-custom-text-100` - } - value={option.value} - > - {option.display} - - )) - ) : ( -

No options

- ) - ) : ( -

Loading...

- )} - -
-
-
- - )} -
- - ); -}; diff --git a/web/components/cycles/sidebar.tsx b/web/components/cycles/sidebar.tsx index 1c7d0c58b..3288dfe0b 100644 --- a/web/components/cycles/sidebar.tsx +++ b/web/components/cycles/sidebar.tsx @@ -502,7 +502,7 @@ export const CycleDetailsSidebar: React.FC = observer((props) => {
= observer((props) => { ) : ( "" )} - {cycleDetails.total_issues > 0 && ( + {cycleDetails.total_issues > 0 && cycleDetails.distribution && (
{ [issueFilterStore, projectId, workspaceSlug] ); - const cyclesList = projectId ? cycleStore.cycles[projectId.toString()] : undefined; + const cyclesList = cycleStore.projectCycles; const cycleDetails = cycleId ? cycleStore.getCycleById(cycleId.toString()) : undefined; return ( diff --git a/web/components/issues/select/cycle.tsx b/web/components/issues/select/cycle.tsx index 742774851..c931ec5d8 100644 --- a/web/components/issues/select/cycle.tsx +++ b/web/components/issues/select/cycle.tsx @@ -34,7 +34,7 @@ export const IssueCycleSelect: React.FC = observer((props if (workspaceSlug && projectId) cycleStore.fetchCycles(workspaceSlug, projectId, "all"); }; - const cycles = projectId ? cycleStore.cycles[projectId] : undefined; + const cycles = cycleStore.projectCycles; const selectedCycle = cycles ? cycles?.find((i) => i.id === value) : undefined; diff --git a/web/components/views/view-list-item.tsx b/web/components/views/view-list-item.tsx index ea5649c70..3f145be53 100644 --- a/web/components/views/view-list-item.tsx +++ b/web/components/views/view-list-item.tsx @@ -2,20 +2,18 @@ import React, { useState } from "react"; import Link from "next/link"; import { useRouter } from "next/router"; import { observer } from "mobx-react-lite"; - +import { PencilIcon, StarIcon, TrashIcon } from "lucide-react"; // mobx store import { useMobxStore } from "lib/mobx/store-provider"; // components import { CreateUpdateProjectViewModal, DeleteProjectViewModal } from "components/views"; // ui -import { CustomMenu } from "@plane/ui"; -// icons -import { PencilIcon, Sparkles, StarIcon, TrashIcon } from "lucide-react"; -// types -import { IProjectView } from "types"; +import { CustomMenu, PhotoFilterIcon } from "@plane/ui"; // helpers import { truncateText } from "helpers/string.helper"; import { calculateTotalFilters } from "helpers/filter.helper"; +// types +import { IProjectView } from "types"; type Props = { view: IProjectView; @@ -64,7 +62,7 @@ export const ProjectViewListItem: React.FC = observer((props) => {
- +

{truncateText(view.name, 75)}

diff --git a/web/components/workspace/views/view-list-item.tsx b/web/components/workspace/views/view-list-item.tsx index b6b62a428..867ce3b5e 100644 --- a/web/components/workspace/views/view-list-item.tsx +++ b/web/components/workspace/views/view-list-item.tsx @@ -1,14 +1,12 @@ +import { useState } from "react"; import { useRouter } from "next/router"; import Link from "next/link"; import { observer } from "mobx-react-lite"; -import { useState } from "react"; - +import { Pencil, Trash2 } from "lucide-react"; // components import { CreateUpdateWorkspaceViewModal, DeleteGlobalViewModal } from "components/workspace"; // ui -import { CustomMenu } from "@plane/ui"; -// icons -import { Pencil, Sparkles, Trash2 } from "lucide-react"; +import { CustomMenu, PhotoFilterIcon } from "@plane/ui"; // helpers import { truncateText } from "helpers/string.helper"; import { calculateTotalFilters } from "helpers/filter.helper"; @@ -38,7 +36,7 @@ export const GlobalViewListItem: React.FC = observer((props) => {
- +

{truncateText(view.name, 75)}

diff --git a/web/helpers/date-time.helper.ts b/web/helpers/date-time.helper.ts index 461737915..b49cb8814 100644 --- a/web/helpers/date-time.helper.ts +++ b/web/helpers/date-time.helper.ts @@ -133,18 +133,13 @@ export const formatDateDistance = (date: string | Date) => { export const getDateRangeStatus = (startDate: string | null | undefined, endDate: string | null | undefined) => { if (!startDate || !endDate) return "draft"; - const today = renderDateFormat(new Date()); - const now = new Date(today); + const now = new Date(); const start = new Date(startDate); const end = new Date(endDate); - if (start <= now && end >= now) { - return "current"; - } else if (start > now) { - return "upcoming"; - } else { - return "completed"; - } + if (start <= now && end >= now) return "current"; + else if (start > now) return "upcoming"; + else return "completed"; }; export const renderShortDateWithYearFormat = (date: string | Date, placeholder?: string) => { diff --git a/web/pages/[workspaceSlug]/projects/[projectId]/cycles/index.tsx b/web/pages/[workspaceSlug]/projects/[projectId]/cycles/index.tsx index 21fd5fe8b..a4e1eb296 100644 --- a/web/pages/[workspaceSlug]/projects/[projectId]/cycles/index.tsx +++ b/web/pages/[workspaceSlug]/projects/[projectId]/cycles/index.tsx @@ -1,8 +1,7 @@ import { Fragment, useCallback, useEffect, useState, ReactElement } from "react"; import { useRouter } from "next/router"; -import { Tab } from "@headlessui/react"; -import useSWR from "swr"; import { observer } from "mobx-react-lite"; +import { Tab } from "@headlessui/react"; import { Plus } from "lucide-react"; // hooks import { useMobxStore } from "lib/mobx/store-provider"; @@ -31,16 +30,7 @@ const ProjectCyclesPage: NextPageWithLayout = observer(() => { const { currentProjectDetails } = projectStore; // router const router = useRouter(); - const { workspaceSlug, projectId, peekCycle } = router.query as { - workspaceSlug: string; - projectId: string; - peekCycle: string; - }; - // fetching project details - useSWR( - workspaceSlug && projectId ? `PROJECT_DETAILS_${projectId}` : null, - workspaceSlug && projectId ? () => projectStore.fetchProjectDetails(workspaceSlug, projectId) : null - ); + const { workspaceSlug, projectId, peekCycle } = router.query; const handleCurrentLayout = useCallback( (_layout: TCycleLayout) => { @@ -83,11 +73,13 @@ const ProjectCyclesPage: NextPageWithLayout = observer(() => { const cycleView = cycleStore?.cycleView; const cycleLayout = cycleStore?.cycleLayout; + if (!workspaceSlug || !projectId) return null; + return ( <> setCreateModal(false)} /> @@ -163,29 +155,29 @@ const ProjectCyclesPage: NextPageWithLayout = observer(() => { - {cycleView && cycleLayout && workspaceSlug && projectId && ( + {cycleView && cycleLayout && ( )} - + - {cycleView && cycleLayout && workspaceSlug && projectId && ( + {cycleView && cycleLayout && ( )} @@ -195,9 +187,9 @@ const ProjectCyclesPage: NextPageWithLayout = observer(() => { )} @@ -207,9 +199,9 @@ const ProjectCyclesPage: NextPageWithLayout = observer(() => { )} diff --git a/web/services/cycle.service.ts b/web/services/cycle.service.ts index 65188515c..2e8b0b998 100644 --- a/web/services/cycle.service.ts +++ b/web/services/cycle.service.ts @@ -71,23 +71,6 @@ export class CycleService extends APIService { }); } - async updateCycle( - workspaceSlug: string, - projectId: string, - cycleId: string, - data: any, - user: IUser | undefined - ): Promise { - return this.put(`/api/workspaces/${workspaceSlug}/projects/${projectId}/cycles/${cycleId}/`, data) - .then((response) => { - trackEventService.trackCycleEvent(response?.data, "CYCLE_UPDATE", user as IUser); - return response?.data; - }) - .catch((error) => { - throw error?.response?.data; - }); - } - async patchCycle( workspaceSlug: string, projectId: string, @@ -97,7 +80,7 @@ export class CycleService extends APIService { ): Promise { return this.patch(`/api/workspaces/${workspaceSlug}/projects/${projectId}/cycles/${cycleId}/`, data) .then((response) => { - trackEventService.trackCycleEvent(response?.data, "CYCLE_UPDATE", user as IUser); + if (user) trackEventService.trackCycleEvent(response?.data, "CYCLE_UPDATE", user); return response?.data; }) .catch((error) => { diff --git a/web/store/cycle/cycle_issue_filters.store.ts b/web/store/cycle/cycle_issue_filters.store.ts index c6818b6df..95fad6d98 100644 --- a/web/store/cycle/cycle_issue_filters.store.ts +++ b/web/store/cycle/cycle_issue_filters.store.ts @@ -133,7 +133,9 @@ export class CycleIssueFilterStore implements ICycleIssueFilterStore { }, }; - await this.cycleService.updateCycle(workspaceSlug, projectId, cycleId, payload, undefined); + const user = this.rootStore.user.currentUser ?? undefined; + + await this.cycleService.patchCycle(workspaceSlug, projectId, cycleId, payload, user); } catch (error) { this.fetchCycleFilters(workspaceSlug, projectId, cycleId); diff --git a/web/store/cycle/cycles.store.ts b/web/store/cycle/cycles.store.ts index 15cf4a28e..67e3fc2ca 100644 --- a/web/store/cycle/cycles.store.ts +++ b/web/store/cycle/cycles.store.ts @@ -7,6 +7,7 @@ import { RootStore } from "../root"; import { ProjectService } from "services/project"; import { IssueService } from "services/issue"; import { CycleService } from "services/cycle.service"; +import { getDateRangeStatus } from "helpers/date-time.helper"; export interface ICycleStore { loader: boolean; @@ -17,17 +18,23 @@ export interface ICycleStore { cycleId: string | null; cycles: { - [project_id: string]: ICycle[]; + [projectId: string]: { + [filterType: string]: ICycle[]; + }; }; cycle_details: { - [cycle_id: string]: ICycle; + [cycleId: string]: ICycle; }; active_cycle_issues: { - [cycle_id: string]: IIssue[]; + [cycleId: string]: IIssue[]; }; // computed getCycleById: (cycleId: string) => ICycle | null; + projectCycles: ICycle[] | null; + projectCompletedCycles: ICycle[] | null; + projectUpcomingCycles: ICycle[] | null; + projectDraftCycles: ICycle[] | null; // actions setCycleView: (_cycleView: TCycleView) => void; @@ -44,12 +51,12 @@ export interface ICycleStore { fetchCycleWithId: (workspaceSlug: string, projectId: string, cycleId: string) => Promise; fetchActiveCycleIssues: (workspaceSlug: string, projectId: string, cycleId: string) => Promise; - createCycle: (workspaceSlug: string, projectId: string, data: any) => Promise; - updateCycle: (workspaceSlug: string, projectId: string, cycleId: string, data: any) => Promise; + createCycle: (workspaceSlug: string, projectId: string, data: Partial) => Promise; + patchCycle: (workspaceSlug: string, projectId: string, cycleId: string, data: Partial) => Promise; removeCycle: (workspaceSlug: string, projectId: string, cycleId: string) => Promise; - addCycleToFavorites: (workspaceSlug: string, projectId: string, cycleId: string) => Promise; - removeCycleFromFavorites: (workspaceSlug: string, projectId: string, cycleId: string) => Promise; + addCycleToFavorites: (workspaceSlug: string, projectId: string, cycle: ICycle) => Promise; + removeCycleFromFavorites: (workspaceSlug: string, projectId: string, cycle: ICycle) => Promise; } export class CycleStore implements ICycleStore { @@ -61,15 +68,17 @@ export class CycleStore implements ICycleStore { cycleId: string | null = null; cycles: { - [project_id: string]: ICycle[]; + [projectId: string]: { + [filterType: string]: ICycle[]; + }; } = {}; cycle_details: { - [cycle_id: string]: ICycle; + [cycleId: string]: ICycle; } = {}; active_cycle_issues: { - [cycle_id: string]: IIssue[]; + [cycleId: string]: IIssue[]; } = {}; // root store @@ -94,6 +103,9 @@ export class CycleStore implements ICycleStore { // computed projectCycles: computed, + projectCompletedCycles: computed, + projectUpcomingCycles: computed, + projectDraftCycles: computed, // actions setCycleView: action, @@ -107,7 +119,6 @@ export class CycleStore implements ICycleStore { fetchActiveCycleIssues: action, createCycle: action, - updateCycle: action, removeCycle: action, addCycleToFavorites: action, @@ -122,8 +133,34 @@ export class CycleStore implements ICycleStore { // computed get projectCycles() { - if (!this.rootStore.project.projectId) return null; - return this.cycles[this.rootStore.project.projectId] || null; + const projectId = this.rootStore.project.projectId; + + if (!projectId) return null; + return this.cycles[projectId]?.all || null; + } + + get projectCompletedCycles() { + const projectId = this.rootStore.project.projectId; + + if (!projectId) return null; + + return this.cycles[projectId]?.completed || null; + } + + get projectUpcomingCycles() { + const projectId = this.rootStore.project.projectId; + + if (!projectId) return null; + + return this.cycles[projectId]?.upcoming || null; + } + + get projectDraftCycles() { + const projectId = this.rootStore.project.projectId; + + if (!projectId) return null; + + return this.cycles[projectId]?.draft || null; } getCycleById = (cycleId: string) => this.cycle_details[cycleId] || null; @@ -159,7 +196,7 @@ export class CycleStore implements ICycleStore { runInAction(() => { this.cycles = { ...this.cycles, - [projectId]: cyclesResponse, + [projectId]: { ...this.cycles[projectId], [params]: cyclesResponse }, }; this.loader = false; this.error = null; @@ -209,16 +246,14 @@ export class CycleStore implements ICycleStore { } }; - createCycle = async (workspaceSlug: string, projectId: string, data: any) => { + createCycle = async (workspaceSlug: string, projectId: string, data: Partial) => { try { - console.log("Cycle Creating"); const response = await this.cycleService.createCycle( workspaceSlug, projectId, data, this.rootStore.user.currentUser ); - console.log("Cycle created"); runInAction(() => { this.cycle_details = { @@ -237,30 +272,7 @@ export class CycleStore implements ICycleStore { } }; - updateCycle = async (workspaceSlug: string, projectId: string, cycleId: string, data: any) => { - try { - const response = await this.cycleService.updateCycle(workspaceSlug, projectId, cycleId, data, undefined); - - const _cycleDetails = { - ...this.cycle_details, - [cycleId]: { ...this.cycle_details[cycleId], ...response }, - }; - - runInAction(() => { - this.cycle_details = _cycleDetails; - }); - - const _currentView = this.cycleView === "active" ? "current" : this.cycleView; - this.fetchCycles(workspaceSlug, projectId, _currentView); - - return response; - } catch (error) { - console.log("Failed to update cycle from cycle store"); - throw error; - } - }; - - patchCycle = async (workspaceSlug: string, projectId: string, cycleId: string, data: any) => { + patchCycle = async (workspaceSlug: string, projectId: string, cycleId: string, data: Partial) => { try { const _response = await this.cycleService.patchCycle(workspaceSlug, projectId, cycleId, data, undefined); @@ -297,30 +309,60 @@ export class CycleStore implements ICycleStore { } }; - addCycleToFavorites = async (workspaceSlug: string, projectId: string, cycleId: string) => { + addCycleToFavorites = async (workspaceSlug: string, projectId: string, cycle: ICycle) => { + const cycleStatus = getDateRangeStatus(cycle.start_date, cycle.end_date); + + const statusCyclesList = this.cycles[projectId]?.[cycleStatus] ?? []; + const allCyclesList = this.projectCycles ?? []; + try { runInAction(() => { this.cycles = { ...this.cycles, - [projectId]: this.cycles[projectId].map((cycle) => { - if (cycle.id === cycleId) return { ...cycle, is_favorite: true }; - return cycle; - }), + [projectId]: { + ...this.cycles[projectId], + [cycleStatus]: statusCyclesList?.map((c) => { + if (c.id === cycle.id) return { ...c, is_favorite: true }; + return c; + }), + all: allCyclesList?.map((c) => { + if (c.id === cycle.id) return { ...c, is_favorite: true }; + return c; + }), + }, + }; + this.cycle_details = { + ...this.cycle_details, + [cycle.id]: { ...this.cycle_details[cycle.id], is_favorite: true }, }; }); + // updating through api. - const response = await this.cycleService.addCycleToFavorites(workspaceSlug, projectId, { cycle: cycleId }); + const response = await this.cycleService.addCycleToFavorites(workspaceSlug, projectId, { cycle: cycle.id }); + return response; } catch (error) { console.log("Failed to add cycle to favorites in the cycles store", error); - // resetting the local state + + // reset on error runInAction(() => { this.cycles = { ...this.cycles, - [projectId]: this.cycles[projectId].map((cycle) => { - if (cycle.id === cycleId) return { ...cycle, is_favorite: false }; - return cycle; - }), + [projectId]: { + ...this.cycles[projectId], + [cycleStatus]: statusCyclesList?.map((c) => { + if (c.id === cycle.id) return { ...c, is_favorite: false }; + return c; + }), + all: allCyclesList?.map((c) => { + if (c.id === cycle.id) return { ...c, is_favorite: false }; + return c; + }), + }, + }; + this.cycle_details = { + ...this.cycle_details, + [cycle.id]: { ...this.cycle_details[cycle.id], is_favorite: false }, }; }); @@ -328,30 +370,62 @@ export class CycleStore implements ICycleStore { } }; - removeCycleFromFavorites = async (workspaceSlug: string, projectId: string, cycleId: string) => { + removeCycleFromFavorites = async (workspaceSlug: string, projectId: string, cycle: ICycle) => { + const cycleStatus = getDateRangeStatus(cycle.start_date, cycle.end_date); + + const statusCyclesList = this.cycles[projectId]?.[cycleStatus] ?? []; + const allCyclesList = this.projectCycles ?? []; + try { runInAction(() => { this.cycles = { ...this.cycles, - [projectId]: this.cycles[projectId].map((cycle) => { - if (cycle.id === cycleId) return { ...cycle, is_favorite: false }; - return cycle; - }), + [projectId]: { + ...this.cycles[projectId], + [cycleStatus]: statusCyclesList?.map((c) => { + if (c.id === cycle.id) return { ...c, is_favorite: false }; + return c; + }), + all: allCyclesList?.map((c) => { + if (c.id === cycle.id) return { ...c, is_favorite: false }; + return c; + }), + }, + }; + this.cycle_details = { + ...this.cycle_details, + [cycle.id]: { ...this.cycle_details[cycle.id], is_favorite: false }, }; }); - const response = await this.cycleService.removeCycleFromFavorites(workspaceSlug, projectId, cycleId); + + const response = await this.cycleService.removeCycleFromFavorites(workspaceSlug, projectId, cycle.id); + return response; } catch (error) { console.log("Failed to remove cycle from favorites - Cycle Store", error); + + // reset on error runInAction(() => { this.cycles = { ...this.cycles, - [projectId]: this.cycles[projectId].map((cycle) => { - if (cycle.id === cycleId) return { ...cycle, is_favorite: true }; - return cycle; - }), + [projectId]: { + ...this.cycles[projectId], + [cycleStatus]: statusCyclesList?.map((c) => { + if (c.id === cycle.id) return { ...c, is_favorite: true }; + return c; + }), + all: allCyclesList?.map((c) => { + if (c.id === cycle.id) return { ...c, is_favorite: true }; + return c; + }), + }, + }; + this.cycle_details = { + ...this.cycle_details, + [cycle.id]: { ...this.cycle_details[cycle.id], is_favorite: true }, }; }); + throw error; } }; diff --git a/web/types/cycles.d.ts b/web/types/cycles.d.ts index e97aa2133..c3c5248aa 100644 --- a/web/types/cycles.d.ts +++ b/web/types/cycles.d.ts @@ -1,13 +1,4 @@ -import type { - IUser, - IIssue, - IProject, - IProjectLite, - IWorkspace, - IWorkspaceLite, - IIssueFilterOptions, - IUserLite, -} from "types"; +import type { IUser, IIssue, IProjectLite, IWorkspaceLite, IIssueFilterOptions, IUserLite } from "types"; export type TCycleView = "all" | "active" | "upcoming" | "completed" | "draft"; @@ -20,7 +11,7 @@ export interface ICycle { created_at: Date; created_by: string; description: string; - distribution: { + distribution?: { assignees: TAssigneesDistribution[]; completion_chart: TCompletionChartDistribution; labels: TLabelsDistribution[];