diff --git a/web/components/command-palette/command-pallette.tsx b/web/components/command-palette/command-pallette.tsx index c223b0cf7..33c958b6c 100644 --- a/web/components/command-palette/command-pallette.tsx +++ b/web/components/command-palette/command-pallette.tsx @@ -8,7 +8,7 @@ import useUser from "hooks/use-user"; // components import { CommandModal, ShortcutsModal } from "components/command-palette"; import { BulkDeleteIssuesModal } from "components/core"; -import { CreateUpdateCycleModal } from "components/cycles"; +import { CycleCreateUpdateModal } from "components/cycles"; import { CreateUpdateIssueModal, DeleteIssueModal } from "components/issues"; import { CreateUpdateModuleModal } from "components/modules"; import { CreateProjectModal } from "components/project"; @@ -180,7 +180,7 @@ export const CommandPalette: FC = observer(() => { )} {workspaceSlug && projectId && ( <> - toggleCreateCycleModal(false)} workspaceSlug={workspaceSlug.toString()} diff --git a/web/components/cycles/cycle-create-edit-modal.tsx b/web/components/cycles/cycle-create-edit-modal.tsx deleted file mode 100644 index f44b745ce..000000000 --- a/web/components/cycles/cycle-create-edit-modal.tsx +++ /dev/null @@ -1,163 +0,0 @@ -import { Fragment, useEffect, useState } from "react"; -import { Dialog, Transition } from "@headlessui/react"; -import { observer } from "mobx-react-lite"; -// components -import { CycleForm } from "./form"; -// hooks -import useToast from "hooks/use-toast"; -import { useMobxStore } from "lib/mobx/store-provider"; -// types -import { CycleDateCheckData, ICycle } from "types"; - -interface ICycleCreateEdit { - cycle?: ICycle | null; - modal: boolean; - modalClose: () => void; - onSubmit?: () => void; - workspaceSlug: string; - projectId: string; -} - -export const CycleCreateEditModal: React.FC = observer((props) => { - const { modal, modalClose, cycle = null, onSubmit, workspaceSlug, projectId } = props; - const [activeProject, setActiveProject] = useState(null); - - const { project: projectStore, cycle: cycleStore } = useMobxStore(); - const projects = workspaceSlug ? projectStore.projects[workspaceSlug.toString()] : undefined; - - const { setToastAlert } = useToast(); - - const validateCycleDate = async (payload: CycleDateCheckData) => { - let status = false; - await cycleStore.validateDate(workspaceSlug as string, projectId as string, payload).then((res) => { - status = res.status; - }); - return status; - }; - - const formSubmit = async (data: Partial) => { - let isDateValid: boolean = true; - - if (data?.start_date && data?.end_date) { - if (cycle?.id && cycle?.start_date && cycle?.end_date) - isDateValid = await validateCycleDate({ - start_date: data.start_date, - end_date: data.end_date, - cycle_id: cycle.id, - }); - else - isDateValid = await validateCycleDate({ - start_date: data.start_date, - end_date: data.end_date, - }); - } - - if (isDateValid) - if (cycle) { - try { - await cycleStore.updateCycle(workspaceSlug, projectId, cycle.id, data); - if (modalClose) modalClose(); - if (onSubmit) onSubmit(); - setToastAlert({ - type: "success", - title: "Success!", - message: "Cycle updated successfully.", - }); - } catch (error) { - console.log("error", error); - setToastAlert({ - type: "error", - title: "Warning!", - message: "Something went wrong please try again later.", - }); - } - } else { - try { - await cycleStore.createCycle(workspaceSlug, projectId, data); - if (modalClose) modalClose(); - if (onSubmit) onSubmit(); - setToastAlert({ - type: "success", - title: "Success!", - message: "Cycle created successfully.", - }); - } catch (error) { - console.log("error", error); - setToastAlert({ - type: "error", - title: "Warning!", - message: "Something went wrong please try again later.", - }); - } - } - else - setToastAlert({ - type: "error", - title: "Error!", - message: "You already have a cycle on the given dates, if you want to create a draft cycle, remove the dates.", - }); - }; - - useEffect(() => { - // if modal is closed, reset active project to null - // and return to avoid activeProject being set to some other project - if (!modal) { - setActiveProject(null); - return; - } - - // if data is present, set active project to the project of the - // issue. This has more priority than the project in the url. - if (cycle && cycle.project) { - setActiveProject(cycle.project); - return; - } - - // if data is not present, set active project to the project - // in the url. This has the least priority. - if (projects && projects.length > 0 && !activeProject) - setActiveProject(projects?.find((p) => p.id === projectId)?.id ?? projects?.[0].id ?? null); - }, [activeProject, cycle, projectId, projects, modal]); - - return ( - - - -
- - -
-
- - - - - -
-
-
-
- ); -}); diff --git a/web/components/cycles/cycles-board-card.tsx b/web/components/cycles/cycles-board-card.tsx index 3caf34698..f2f921365 100644 --- a/web/components/cycles/cycles-board-card.tsx +++ b/web/components/cycles/cycles-board-card.tsx @@ -7,8 +7,7 @@ import { Disclosure, Transition } from "@headlessui/react"; import useToast from "hooks/use-toast"; // components import { SingleProgressStats } from "components/core"; -import { CycleCreateEditModal } from "./cycle-create-edit-modal"; -import { CycleDeleteModal } from "./cycle-delete-modal"; +import { CycleCreateUpdateModal, CycleDeleteModal } from "components/cycles"; // ui import { AssigneesList } from "components/ui/avatar"; import { CustomMenu, Tooltip, LinearProgressIndicator, ContrastIcon, RunningIcon } from "@plane/ui"; @@ -69,19 +68,14 @@ export interface ICyclesBoardCard { export const CyclesBoardCard: FC = (props) => { const { cycle, workspaceSlug, projectId } = props; - - const [updateModal, setUpdateModal] = useState(false); - const updateModalCallback = () => {}; - - const [deleteModal, setDeleteModal] = useState(false); - const deleteModalCallback = () => {}; - // store const { cycle: cycleStore } = useMobxStore(); - // toast const { setToastAlert } = useToast(); - + // states + const [updateModal, setUpdateModal] = useState(false); + const [deleteModal, setDeleteModal] = useState(false); + // computed const cycleStatus = getDateRangeStatus(cycle.start_date, cycle.end_date); const isCompleted = cycleStatus === "completed"; const endDate = new Date(cycle.end_date ?? ""); @@ -142,20 +136,18 @@ export const CyclesBoardCard: FC = (props) => { return (
- setUpdateModal(false)} - onSubmit={updateModalCallback} + setUpdateModal(false)} workspaceSlug={workspaceSlug} projectId={projectId} /> setDeleteModal(false)} - onSubmit={deleteModalCallback} + isOpen={deleteModal} + handleClose={() => setDeleteModal(false)} workspaceSlug={workspaceSlug} projectId={projectId} /> diff --git a/web/components/cycles/cycles-list-item.tsx b/web/components/cycles/cycles-list-item.tsx index 1a58ab0db..9b381a03d 100644 --- a/web/components/cycles/cycles-list-item.tsx +++ b/web/components/cycles/cycles-list-item.tsx @@ -3,8 +3,7 @@ import Link from "next/link"; // hooks import useToast from "hooks/use-toast"; // components -import { CycleCreateEditModal } from "./cycle-create-edit-modal"; -import { CycleDeleteModal } from "./cycle-delete-modal"; +import { CycleCreateUpdateModal, CycleDeleteModal } from "components/cycles"; // ui import { CustomMenu, RadialProgressBar, Tooltip, LinearProgressIndicator, ContrastIcon, RunningIcon } from "@plane/ui"; // icons @@ -66,19 +65,14 @@ const stateGroups = [ export const CyclesListItem: FC = (props) => { const { cycle, workspaceSlug, projectId } = props; - - const [updateModal, setUpdateModal] = useState(false); - const updateModalCallback = () => {}; - - const [deleteModal, setDeleteModal] = useState(false); - const deleteModalCallback = () => {}; - // store const { cycle: cycleStore } = useMobxStore(); - // toast const { setToastAlert } = useToast(); - + // states + const [updateModal, setUpdateModal] = useState(false); + const [deleteModal, setDeleteModal] = useState(false); + // computed const cycleStatus = getDateRangeStatus(cycle.start_date, cycle.end_date); const isCompleted = cycleStatus === "completed"; const endDate = new Date(cycle.end_date ?? ""); @@ -347,20 +341,18 @@ export const CyclesListItem: FC = (props) => {
- setUpdateModal(false)} - onSubmit={updateModalCallback} + setUpdateModal(false)} workspaceSlug={workspaceSlug} projectId={projectId} /> setDeleteModal(false)} - onSubmit={deleteModalCallback} + isOpen={deleteModal} + handleClose={() => setDeleteModal(false)} workspaceSlug={workspaceSlug} projectId={projectId} /> diff --git a/web/components/cycles/cycle-delete-modal.tsx b/web/components/cycles/delete-modal.tsx similarity index 90% rename from web/components/cycles/cycle-delete-modal.tsx rename to web/components/cycles/delete-modal.tsx index c2c84d6bb..02d6126fe 100644 --- a/web/components/cycles/cycle-delete-modal.tsx +++ b/web/components/cycles/delete-modal.tsx @@ -13,24 +13,23 @@ import { useMobxStore } from "lib/mobx/store-provider"; interface ICycleDelete { cycle: ICycle; - modal: boolean; - modalClose: () => void; - onSubmit?: () => void; + isOpen: boolean; + handleClose: () => void; workspaceSlug: string; projectId: string; } export const CycleDeleteModal: React.FC = observer((props) => { - const { modal, modalClose, cycle, onSubmit, workspaceSlug, projectId } = props; - + const { isOpen, handleClose, cycle, workspaceSlug, projectId } = props; + // store const { cycle: cycleStore } = useMobxStore(); - + // toast const { setToastAlert } = useToast(); - + // states const [loader, setLoader] = useState(false); + const formSubmit = async () => { setLoader(true); - if (cycle?.id) try { await cycleStore.removeCycle(workspaceSlug, projectId, cycle?.id); @@ -39,8 +38,7 @@ export const CycleDeleteModal: React.FC = observer((props) => { title: "Success!", message: "Cycle deleted successfully.", }); - if (modalClose) modalClose(); - if (onSubmit) onSubmit(); + handleClose(); } catch (error) { setToastAlert({ type: "error", @@ -61,8 +59,8 @@ export const CycleDeleteModal: React.FC = observer((props) => { return (
- - + + = observer((props) => {

- Cancel + Cancel {loader ? "Deleting..." : "Delete Cycle"} diff --git a/web/components/cycles/form.tsx b/web/components/cycles/form.tsx index adcee376b..b262edb15 100644 --- a/web/components/cycles/form.tsx +++ b/web/components/cycles/form.tsx @@ -10,7 +10,7 @@ type Props = { handleFormSubmit: (values: Partial) => Promise; handleClose: () => void; projectId: string; - setActiveProject: React.Dispatch>; + setActiveProject: (projectId: string) => void; data?: ICycle | null; }; diff --git a/web/components/cycles/index.ts b/web/components/cycles/index.ts index 76f0d9773..20bbfb627 100644 --- a/web/components/cycles/index.ts +++ b/web/components/cycles/index.ts @@ -16,3 +16,4 @@ export * from "./cycles-list-item"; export * from "./cycles-board"; export * from "./cycles-board-card"; export * from "./cycles-gantt"; +export * from "./delete-modal"; diff --git a/web/components/cycles/modal.tsx b/web/components/cycles/modal.tsx index 24becd9e3..bb266e9eb 100644 --- a/web/components/cycles/modal.tsx +++ b/web/components/cycles/modal.tsx @@ -1,5 +1,4 @@ -import React, { useEffect, useState } from "react"; -import { mutate } from "swr"; +import React, { useState } from "react"; import { Dialog, Transition } from "@headlessui/react"; // services import { CycleService } from "services/cycle.service"; @@ -8,20 +7,8 @@ import useToast from "hooks/use-toast"; import { useMobxStore } from "lib/mobx/store-provider"; // components import { CycleForm } from "components/cycles"; -// helper -import { getDateRangeStatus } from "helpers/date-time.helper"; // types -import type { CycleDateCheckData, ICycle, IProject, IUser } from "types"; -// fetch keys -import { - COMPLETED_CYCLES_LIST, - CURRENT_CYCLE_LIST, - CYCLES_LIST, - DRAFT_CYCLES_LIST, - INCOMPLETE_CYCLES_LIST, - PROJECT_DETAILS, - UPCOMING_CYCLES_LIST, -} from "constants/fetch-keys"; +import type { CycleDateCheckData, ICycle } from "types"; type CycleModalProps = { isOpen: boolean; @@ -34,49 +21,19 @@ type CycleModalProps = { // services const cycleService = new CycleService(); -export const CreateUpdateCycleModal: React.FC = (props) => { +export const CycleCreateUpdateModal: React.FC = (props) => { const { isOpen, handleClose, data, workspaceSlug, projectId } = props; - const [activeProject, setActiveProject] = useState(null); - - const { project: projectStore } = useMobxStore(); - const projects = workspaceSlug ? projectStore.projects[workspaceSlug.toString()] : undefined; - + // store + const { cycle: cycleStore } = useMobxStore(); + // states + const [activeProject, setActiveProject] = useState(projectId); + // toast const { setToastAlert } = useToast(); - const createCycle = async (payload: Partial) => { - await cycleService - .createCycle(workspaceSlug.toString(), projectId.toString(), payload, {} as IUser) - .then((res) => { - switch (getDateRangeStatus(res.start_date, res.end_date)) { - case "completed": - mutate(COMPLETED_CYCLES_LIST(projectId.toString())); - break; - case "current": - mutate(CURRENT_CYCLE_LIST(projectId.toString())); - break; - case "upcoming": - mutate(UPCOMING_CYCLES_LIST(projectId.toString())); - break; - default: - mutate(DRAFT_CYCLES_LIST(projectId.toString())); - } - mutate(INCOMPLETE_CYCLES_LIST(projectId.toString())); - mutate(CYCLES_LIST(projectId.toString())); - - // update total cycles count in the project details - mutate( - PROJECT_DETAILS(projectId.toString()), - (prevData) => { - if (!prevData) return prevData; - - return { - ...prevData, - total_cycles: prevData.total_cycles + 1, - }; - }, - false - ); - + const createCycle = async (payload: Partial) => + cycleStore + .createCycle(workspaceSlug, projectId, payload) + .then(() => { setToastAlert({ type: "success", title: "Success!", @@ -90,42 +47,11 @@ export const CreateUpdateCycleModal: React.FC = (props) => { message: "Error in creating cycle. Please try again.", }); }); - }; - - const updateCycle = async (cycleId: string, payload: Partial) => { - await cycleService - .updateCycle(workspaceSlug.toString(), projectId.toString(), cycleId, payload, {} as IUser) - .then((res) => { - switch (getDateRangeStatus(data?.start_date, data?.end_date)) { - case "completed": - mutate(COMPLETED_CYCLES_LIST(projectId.toString())); - break; - case "current": - mutate(CURRENT_CYCLE_LIST(projectId.toString())); - break; - case "upcoming": - mutate(UPCOMING_CYCLES_LIST(projectId.toString())); - break; - default: - mutate(DRAFT_CYCLES_LIST(projectId.toString())); - } - mutate(CYCLES_LIST(projectId.toString())); - if (getDateRangeStatus(data?.start_date, data?.end_date) != getDateRangeStatus(res.start_date, res.end_date)) { - switch (getDateRangeStatus(res.start_date, res.end_date)) { - case "completed": - mutate(COMPLETED_CYCLES_LIST(projectId.toString())); - break; - case "current": - mutate(CURRENT_CYCLE_LIST(projectId.toString())); - break; - case "upcoming": - mutate(UPCOMING_CYCLES_LIST(projectId.toString())); - break; - default: - mutate(DRAFT_CYCLES_LIST(projectId.toString())); - } - } + const updateCycle = async (cycleId: string, payload: Partial) => + cycleStore + .updateCycle(workspaceSlug, projectId, cycleId, payload) + .then(() => { setToastAlert({ type: "success", title: "Success!", @@ -139,7 +65,6 @@ export const CreateUpdateCycleModal: React.FC = (props) => { message: "Error in updating cycle. Please try again.", }); }); - }; const dateChecker = async (payload: CycleDateCheckData) => { let status = false; @@ -186,27 +111,6 @@ export const CreateUpdateCycleModal: React.FC = (props) => { }); }; - useEffect(() => { - // if modal is closed, reset active project to null - // and return to avoid activeProject being set to some other project - if (!isOpen) { - setActiveProject(null); - return; - } - - // if data is present, set active project to the project of the - // issue. This has more priority than the project in the url. - if (data && data.project) { - setActiveProject(data.project); - return; - } - - // if data is not present, set active project to the project - // in the url. This has the least priority. - if (projects && projects.length > 0 && !activeProject) - setActiveProject(projects?.find((p) => p.id === projectId)?.id ?? projects?.[0].id ?? null); - }, [activeProject, data, projectId, projects, isOpen]); - return ( @@ -237,7 +141,7 @@ export const CreateUpdateCycleModal: React.FC = (props) => { diff --git a/web/components/cycles/select.tsx b/web/components/cycles/select.tsx index ca7eb80de..9944a39b2 100644 --- a/web/components/cycles/select.tsx +++ b/web/components/cycles/select.tsx @@ -1,7 +1,6 @@ import React, { useState } from "react"; import { useRouter } from "next/router"; import useSWR from "swr"; -import useUserAuth from "hooks/use-user-auth"; import { Listbox, Transition } from "@headlessui/react"; // icons import { ContrastIcon } from "@plane/ui"; @@ -9,7 +8,7 @@ import { Plus } from "lucide-react"; // services import { CycleService } from "services/cycle.service"; // components -import { CreateUpdateCycleModal } from "components/cycles"; +import { CycleCreateUpdateModal } from "components/cycles"; // fetch-keys import { CYCLES_LIST } from "constants/fetch-keys"; @@ -29,8 +28,6 @@ export const CycleSelect: React.FC = ({ projectId, value, const router = useRouter(); const { workspaceSlug } = router.query; - const { user } = useUserAuth(); - const { data: cycles } = useSWR( workspaceSlug && projectId ? CYCLES_LIST(projectId) : null, workspaceSlug && projectId @@ -51,7 +48,7 @@ export const CycleSelect: React.FC = ({ projectId, value, return ( <> {workspaceSlug && projectId && ( - = observer((props) => { {cycleDetails && workspaceSlug && projectId && ( setCycleDeleteModal(false)} - onSubmit={() => {}} + isOpen={cycleDeleteModal} + handleClose={() => setCycleDeleteModal(false)} workspaceSlug={workspaceSlug.toString()} projectId={projectId.toString()} /> diff --git a/web/pages/[workspaceSlug]/projects/[projectId]/cycles/index.tsx b/web/pages/[workspaceSlug]/projects/[projectId]/cycles/index.tsx index eb2d07dfa..5fb79fb36 100644 --- a/web/pages/[workspaceSlug]/projects/[projectId]/cycles/index.tsx +++ b/web/pages/[workspaceSlug]/projects/[projectId]/cycles/index.tsx @@ -10,8 +10,7 @@ import { useMobxStore } from "lib/mobx/store-provider"; import { AppLayout } from "layouts/app-layout"; // components import { CyclesHeader } from "components/headers"; -import { CyclesView, ActiveCycleDetails } from "components/cycles"; -import { CycleCreateEditModal } from "components/cycles/cycle-create-edit-modal"; +import { CyclesView, ActiveCycleDetails, CycleCreateUpdateModal } from "components/cycles"; // ui import { EmptyState } from "components/common"; // images @@ -26,15 +25,12 @@ import { setLocalStorage, getLocalStorage } from "lib/local-storage"; const ProjectCyclesPage: NextPage = observer(() => { const [createModal, setCreateModal] = useState(false); - const createOnSubmit = () => {}; - // store const { project: projectStore, cycle: cycleStore } = useMobxStore(); - // router const router = useRouter(); const { workspaceSlug, projectId } = router.query as { workspaceSlug: string; projectId: string }; - + // fetching project details useSWR( workspaceSlug && projectId ? `PROJECT_DETAILS_${projectId}` : null, workspaceSlug && projectId ? () => projectStore.fetchProjectDetails(workspaceSlug, projectId) : null @@ -84,14 +80,12 @@ const ProjectCyclesPage: NextPage = observer(() => { return ( } withProjectWrapper> - setCreateModal(false)} - onSubmit={createOnSubmit} + isOpen={createModal} + handleClose={() => setCreateModal(false)} /> - {projectDetails?.total_cycles === 0 ? (
{ 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 = {