mirror of
https://github.com/makeplane/plane
synced 2024-06-14 14:31:34 +00:00
fix: cycle modal redendent component fix (#2528)
This commit is contained in:
parent
08f7ac6da7
commit
07d548ea43
@ -8,7 +8,7 @@ import useUser from "hooks/use-user";
|
|||||||
// components
|
// components
|
||||||
import { CommandModal, ShortcutsModal } from "components/command-palette";
|
import { CommandModal, ShortcutsModal } from "components/command-palette";
|
||||||
import { BulkDeleteIssuesModal } from "components/core";
|
import { BulkDeleteIssuesModal } from "components/core";
|
||||||
import { CreateUpdateCycleModal } from "components/cycles";
|
import { CycleCreateUpdateModal } from "components/cycles";
|
||||||
import { CreateUpdateIssueModal, DeleteIssueModal } from "components/issues";
|
import { CreateUpdateIssueModal, DeleteIssueModal } from "components/issues";
|
||||||
import { CreateUpdateModuleModal } from "components/modules";
|
import { CreateUpdateModuleModal } from "components/modules";
|
||||||
import { CreateProjectModal } from "components/project";
|
import { CreateProjectModal } from "components/project";
|
||||||
@ -180,7 +180,7 @@ export const CommandPalette: FC = observer(() => {
|
|||||||
)}
|
)}
|
||||||
{workspaceSlug && projectId && (
|
{workspaceSlug && projectId && (
|
||||||
<>
|
<>
|
||||||
<CreateUpdateCycleModal
|
<CycleCreateUpdateModal
|
||||||
isOpen={isCreateCycleModalOpen}
|
isOpen={isCreateCycleModalOpen}
|
||||||
handleClose={() => toggleCreateCycleModal(false)}
|
handleClose={() => toggleCreateCycleModal(false)}
|
||||||
workspaceSlug={workspaceSlug.toString()}
|
workspaceSlug={workspaceSlug.toString()}
|
||||||
|
@ -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<ICycleCreateEdit> = observer((props) => {
|
|
||||||
const { modal, modalClose, cycle = null, onSubmit, workspaceSlug, projectId } = props;
|
|
||||||
const [activeProject, setActiveProject] = useState<string | null>(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<ICycle>) => {
|
|
||||||
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 (
|
|
||||||
<Transition.Root show={modal} as={Fragment}>
|
|
||||||
<Dialog as="div" className="relative z-20" onClose={modalClose}>
|
|
||||||
<Transition.Child
|
|
||||||
as={Fragment}
|
|
||||||
enter="ease-out duration-300"
|
|
||||||
enterFrom="opacity-0"
|
|
||||||
enterTo="opacity-100"
|
|
||||||
leave="ease-in duration-200"
|
|
||||||
leaveFrom="opacity-100"
|
|
||||||
leaveTo="opacity-0"
|
|
||||||
>
|
|
||||||
<div className="fixed inset-0 bg-custom-backdrop bg-opacity-50 transition-opacity" />
|
|
||||||
</Transition.Child>
|
|
||||||
|
|
||||||
<div className="fixed inset-0 z-10 overflow-y-auto">
|
|
||||||
<div className="flex min-h-full items-end justify-center p-4 text-center sm:items-center sm:p-0">
|
|
||||||
<Transition.Child
|
|
||||||
as={Fragment}
|
|
||||||
enter="ease-out duration-300"
|
|
||||||
enterFrom="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
|
||||||
enterTo="opacity-100 translate-y-0 sm:scale-100"
|
|
||||||
leave="ease-in duration-200"
|
|
||||||
leaveFrom="opacity-100 translate-y-0 sm:scale-100"
|
|
||||||
leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
|
||||||
>
|
|
||||||
<Dialog.Panel className="relative transform rounded-lg border border-custom-border-200 bg-custom-background-100 text-left shadow-xl transition-all sm:my-8 sm:w-full sm:max-w-2xl p-5">
|
|
||||||
<CycleForm
|
|
||||||
handleFormSubmit={formSubmit}
|
|
||||||
handleClose={modalClose}
|
|
||||||
projectId={activeProject ?? ""}
|
|
||||||
setActiveProject={setActiveProject}
|
|
||||||
data={cycle}
|
|
||||||
/>
|
|
||||||
</Dialog.Panel>
|
|
||||||
</Transition.Child>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Dialog>
|
|
||||||
</Transition.Root>
|
|
||||||
);
|
|
||||||
});
|
|
@ -7,8 +7,7 @@ import { Disclosure, Transition } from "@headlessui/react";
|
|||||||
import useToast from "hooks/use-toast";
|
import useToast from "hooks/use-toast";
|
||||||
// components
|
// components
|
||||||
import { SingleProgressStats } from "components/core";
|
import { SingleProgressStats } from "components/core";
|
||||||
import { CycleCreateEditModal } from "./cycle-create-edit-modal";
|
import { CycleCreateUpdateModal, CycleDeleteModal } from "components/cycles";
|
||||||
import { CycleDeleteModal } from "./cycle-delete-modal";
|
|
||||||
// ui
|
// ui
|
||||||
import { AssigneesList } from "components/ui/avatar";
|
import { AssigneesList } from "components/ui/avatar";
|
||||||
import { CustomMenu, Tooltip, LinearProgressIndicator, ContrastIcon, RunningIcon } from "@plane/ui";
|
import { CustomMenu, Tooltip, LinearProgressIndicator, ContrastIcon, RunningIcon } from "@plane/ui";
|
||||||
@ -69,19 +68,14 @@ export interface ICyclesBoardCard {
|
|||||||
|
|
||||||
export const CyclesBoardCard: FC<ICyclesBoardCard> = (props) => {
|
export const CyclesBoardCard: FC<ICyclesBoardCard> = (props) => {
|
||||||
const { cycle, workspaceSlug, projectId } = props;
|
const { cycle, workspaceSlug, projectId } = props;
|
||||||
|
|
||||||
const [updateModal, setUpdateModal] = useState(false);
|
|
||||||
const updateModalCallback = () => {};
|
|
||||||
|
|
||||||
const [deleteModal, setDeleteModal] = useState(false);
|
|
||||||
const deleteModalCallback = () => {};
|
|
||||||
|
|
||||||
// store
|
// store
|
||||||
const { cycle: cycleStore } = useMobxStore();
|
const { cycle: cycleStore } = useMobxStore();
|
||||||
|
|
||||||
// toast
|
// toast
|
||||||
const { setToastAlert } = useToast();
|
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 cycleStatus = getDateRangeStatus(cycle.start_date, cycle.end_date);
|
||||||
const isCompleted = cycleStatus === "completed";
|
const isCompleted = cycleStatus === "completed";
|
||||||
const endDate = new Date(cycle.end_date ?? "");
|
const endDate = new Date(cycle.end_date ?? "");
|
||||||
@ -142,20 +136,18 @@ export const CyclesBoardCard: FC<ICyclesBoardCard> = (props) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<CycleCreateEditModal
|
<CycleCreateUpdateModal
|
||||||
cycle={cycle}
|
data={cycle}
|
||||||
modal={updateModal}
|
isOpen={updateModal}
|
||||||
modalClose={() => setUpdateModal(false)}
|
handleClose={() => setUpdateModal(false)}
|
||||||
onSubmit={updateModalCallback}
|
|
||||||
workspaceSlug={workspaceSlug}
|
workspaceSlug={workspaceSlug}
|
||||||
projectId={projectId}
|
projectId={projectId}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<CycleDeleteModal
|
<CycleDeleteModal
|
||||||
cycle={cycle}
|
cycle={cycle}
|
||||||
modal={deleteModal}
|
isOpen={deleteModal}
|
||||||
modalClose={() => setDeleteModal(false)}
|
handleClose={() => setDeleteModal(false)}
|
||||||
onSubmit={deleteModalCallback}
|
|
||||||
workspaceSlug={workspaceSlug}
|
workspaceSlug={workspaceSlug}
|
||||||
projectId={projectId}
|
projectId={projectId}
|
||||||
/>
|
/>
|
||||||
|
@ -3,8 +3,7 @@ import Link from "next/link";
|
|||||||
// hooks
|
// hooks
|
||||||
import useToast from "hooks/use-toast";
|
import useToast from "hooks/use-toast";
|
||||||
// components
|
// components
|
||||||
import { CycleCreateEditModal } from "./cycle-create-edit-modal";
|
import { CycleCreateUpdateModal, CycleDeleteModal } from "components/cycles";
|
||||||
import { CycleDeleteModal } from "./cycle-delete-modal";
|
|
||||||
// ui
|
// ui
|
||||||
import { CustomMenu, RadialProgressBar, Tooltip, LinearProgressIndicator, ContrastIcon, RunningIcon } from "@plane/ui";
|
import { CustomMenu, RadialProgressBar, Tooltip, LinearProgressIndicator, ContrastIcon, RunningIcon } from "@plane/ui";
|
||||||
// icons
|
// icons
|
||||||
@ -66,19 +65,14 @@ const stateGroups = [
|
|||||||
|
|
||||||
export const CyclesListItem: FC<TCyclesListItem> = (props) => {
|
export const CyclesListItem: FC<TCyclesListItem> = (props) => {
|
||||||
const { cycle, workspaceSlug, projectId } = props;
|
const { cycle, workspaceSlug, projectId } = props;
|
||||||
|
|
||||||
const [updateModal, setUpdateModal] = useState(false);
|
|
||||||
const updateModalCallback = () => {};
|
|
||||||
|
|
||||||
const [deleteModal, setDeleteModal] = useState(false);
|
|
||||||
const deleteModalCallback = () => {};
|
|
||||||
|
|
||||||
// store
|
// store
|
||||||
const { cycle: cycleStore } = useMobxStore();
|
const { cycle: cycleStore } = useMobxStore();
|
||||||
|
|
||||||
// toast
|
// toast
|
||||||
const { setToastAlert } = useToast();
|
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 cycleStatus = getDateRangeStatus(cycle.start_date, cycle.end_date);
|
||||||
const isCompleted = cycleStatus === "completed";
|
const isCompleted = cycleStatus === "completed";
|
||||||
const endDate = new Date(cycle.end_date ?? "");
|
const endDate = new Date(cycle.end_date ?? "");
|
||||||
@ -347,20 +341,18 @@ export const CyclesListItem: FC<TCyclesListItem> = (props) => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<CycleCreateEditModal
|
<CycleCreateUpdateModal
|
||||||
cycle={cycle}
|
data={cycle}
|
||||||
modal={updateModal}
|
isOpen={updateModal}
|
||||||
modalClose={() => setUpdateModal(false)}
|
handleClose={() => setUpdateModal(false)}
|
||||||
onSubmit={updateModalCallback}
|
|
||||||
workspaceSlug={workspaceSlug}
|
workspaceSlug={workspaceSlug}
|
||||||
projectId={projectId}
|
projectId={projectId}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<CycleDeleteModal
|
<CycleDeleteModal
|
||||||
cycle={cycle}
|
cycle={cycle}
|
||||||
modal={deleteModal}
|
isOpen={deleteModal}
|
||||||
modalClose={() => setDeleteModal(false)}
|
handleClose={() => setDeleteModal(false)}
|
||||||
onSubmit={deleteModalCallback}
|
|
||||||
workspaceSlug={workspaceSlug}
|
workspaceSlug={workspaceSlug}
|
||||||
projectId={projectId}
|
projectId={projectId}
|
||||||
/>
|
/>
|
||||||
|
@ -13,24 +13,23 @@ import { useMobxStore } from "lib/mobx/store-provider";
|
|||||||
|
|
||||||
interface ICycleDelete {
|
interface ICycleDelete {
|
||||||
cycle: ICycle;
|
cycle: ICycle;
|
||||||
modal: boolean;
|
isOpen: boolean;
|
||||||
modalClose: () => void;
|
handleClose: () => void;
|
||||||
onSubmit?: () => void;
|
|
||||||
workspaceSlug: string;
|
workspaceSlug: string;
|
||||||
projectId: string;
|
projectId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const CycleDeleteModal: React.FC<ICycleDelete> = observer((props) => {
|
export const CycleDeleteModal: React.FC<ICycleDelete> = observer((props) => {
|
||||||
const { modal, modalClose, cycle, onSubmit, workspaceSlug, projectId } = props;
|
const { isOpen, handleClose, cycle, workspaceSlug, projectId } = props;
|
||||||
|
// store
|
||||||
const { cycle: cycleStore } = useMobxStore();
|
const { cycle: cycleStore } = useMobxStore();
|
||||||
|
// toast
|
||||||
const { setToastAlert } = useToast();
|
const { setToastAlert } = useToast();
|
||||||
|
// states
|
||||||
const [loader, setLoader] = useState(false);
|
const [loader, setLoader] = useState(false);
|
||||||
|
|
||||||
const formSubmit = async () => {
|
const formSubmit = async () => {
|
||||||
setLoader(true);
|
setLoader(true);
|
||||||
|
|
||||||
if (cycle?.id)
|
if (cycle?.id)
|
||||||
try {
|
try {
|
||||||
await cycleStore.removeCycle(workspaceSlug, projectId, cycle?.id);
|
await cycleStore.removeCycle(workspaceSlug, projectId, cycle?.id);
|
||||||
@ -39,8 +38,7 @@ export const CycleDeleteModal: React.FC<ICycleDelete> = observer((props) => {
|
|||||||
title: "Success!",
|
title: "Success!",
|
||||||
message: "Cycle deleted successfully.",
|
message: "Cycle deleted successfully.",
|
||||||
});
|
});
|
||||||
if (modalClose) modalClose();
|
handleClose();
|
||||||
if (onSubmit) onSubmit();
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
setToastAlert({
|
setToastAlert({
|
||||||
type: "error",
|
type: "error",
|
||||||
@ -61,8 +59,8 @@ export const CycleDeleteModal: React.FC<ICycleDelete> = observer((props) => {
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div>
|
<div>
|
||||||
<Transition.Root show={modal} as={Fragment}>
|
<Transition.Root show={isOpen} as={Fragment}>
|
||||||
<Dialog as="div" className="relative z-20" onClose={modalClose}>
|
<Dialog as="div" className="relative z-20" onClose={handleClose}>
|
||||||
<Transition.Child
|
<Transition.Child
|
||||||
as={Fragment}
|
as={Fragment}
|
||||||
enter="ease-out duration-300"
|
enter="ease-out duration-300"
|
||||||
@ -103,7 +101,7 @@ export const CycleDeleteModal: React.FC<ICycleDelete> = observer((props) => {
|
|||||||
</p>
|
</p>
|
||||||
</span>
|
</span>
|
||||||
<div className="flex justify-end gap-2">
|
<div className="flex justify-end gap-2">
|
||||||
<SecondaryButton onClick={modalClose}>Cancel</SecondaryButton>
|
<SecondaryButton onClick={handleClose}>Cancel</SecondaryButton>
|
||||||
<DangerButton onClick={formSubmit} loading={loader}>
|
<DangerButton onClick={formSubmit} loading={loader}>
|
||||||
{loader ? "Deleting..." : "Delete Cycle"}
|
{loader ? "Deleting..." : "Delete Cycle"}
|
||||||
</DangerButton>
|
</DangerButton>
|
@ -10,7 +10,7 @@ type Props = {
|
|||||||
handleFormSubmit: (values: Partial<ICycle>) => Promise<void>;
|
handleFormSubmit: (values: Partial<ICycle>) => Promise<void>;
|
||||||
handleClose: () => void;
|
handleClose: () => void;
|
||||||
projectId: string;
|
projectId: string;
|
||||||
setActiveProject: React.Dispatch<React.SetStateAction<string | null>>;
|
setActiveProject: (projectId: string) => void;
|
||||||
data?: ICycle | null;
|
data?: ICycle | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -16,3 +16,4 @@ export * from "./cycles-list-item";
|
|||||||
export * from "./cycles-board";
|
export * from "./cycles-board";
|
||||||
export * from "./cycles-board-card";
|
export * from "./cycles-board-card";
|
||||||
export * from "./cycles-gantt";
|
export * from "./cycles-gantt";
|
||||||
|
export * from "./delete-modal";
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import React, { useEffect, useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { mutate } from "swr";
|
|
||||||
import { Dialog, Transition } from "@headlessui/react";
|
import { Dialog, Transition } from "@headlessui/react";
|
||||||
// services
|
// services
|
||||||
import { CycleService } from "services/cycle.service";
|
import { CycleService } from "services/cycle.service";
|
||||||
@ -8,20 +7,8 @@ import useToast from "hooks/use-toast";
|
|||||||
import { useMobxStore } from "lib/mobx/store-provider";
|
import { useMobxStore } from "lib/mobx/store-provider";
|
||||||
// components
|
// components
|
||||||
import { CycleForm } from "components/cycles";
|
import { CycleForm } from "components/cycles";
|
||||||
// helper
|
|
||||||
import { getDateRangeStatus } from "helpers/date-time.helper";
|
|
||||||
// types
|
// types
|
||||||
import type { CycleDateCheckData, ICycle, IProject, IUser } from "types";
|
import type { CycleDateCheckData, ICycle } 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";
|
|
||||||
|
|
||||||
type CycleModalProps = {
|
type CycleModalProps = {
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
@ -34,49 +21,19 @@ type CycleModalProps = {
|
|||||||
// services
|
// services
|
||||||
const cycleService = new CycleService();
|
const cycleService = new CycleService();
|
||||||
|
|
||||||
export const CreateUpdateCycleModal: React.FC<CycleModalProps> = (props) => {
|
export const CycleCreateUpdateModal: React.FC<CycleModalProps> = (props) => {
|
||||||
const { isOpen, handleClose, data, workspaceSlug, projectId } = props;
|
const { isOpen, handleClose, data, workspaceSlug, projectId } = props;
|
||||||
const [activeProject, setActiveProject] = useState<string | null>(null);
|
// store
|
||||||
|
const { cycle: cycleStore } = useMobxStore();
|
||||||
const { project: projectStore } = useMobxStore();
|
// states
|
||||||
const projects = workspaceSlug ? projectStore.projects[workspaceSlug.toString()] : undefined;
|
const [activeProject, setActiveProject] = useState<string>(projectId);
|
||||||
|
// toast
|
||||||
const { setToastAlert } = useToast();
|
const { setToastAlert } = useToast();
|
||||||
|
|
||||||
const createCycle = async (payload: Partial<ICycle>) => {
|
const createCycle = async (payload: Partial<ICycle>) =>
|
||||||
await cycleService
|
cycleStore
|
||||||
.createCycle(workspaceSlug.toString(), projectId.toString(), payload, {} as IUser)
|
.createCycle(workspaceSlug, projectId, payload)
|
||||||
.then((res) => {
|
.then(() => {
|
||||||
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<IProject>(
|
|
||||||
PROJECT_DETAILS(projectId.toString()),
|
|
||||||
(prevData) => {
|
|
||||||
if (!prevData) return prevData;
|
|
||||||
|
|
||||||
return {
|
|
||||||
...prevData,
|
|
||||||
total_cycles: prevData.total_cycles + 1,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
false
|
|
||||||
);
|
|
||||||
|
|
||||||
setToastAlert({
|
setToastAlert({
|
||||||
type: "success",
|
type: "success",
|
||||||
title: "Success!",
|
title: "Success!",
|
||||||
@ -90,42 +47,11 @@ export const CreateUpdateCycleModal: React.FC<CycleModalProps> = (props) => {
|
|||||||
message: "Error in creating cycle. Please try again.",
|
message: "Error in creating cycle. Please try again.",
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
|
||||||
|
|
||||||
const updateCycle = async (cycleId: string, payload: Partial<ICycle>) => {
|
|
||||||
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<ICycle>) =>
|
||||||
|
cycleStore
|
||||||
|
.updateCycle(workspaceSlug, projectId, cycleId, payload)
|
||||||
|
.then(() => {
|
||||||
setToastAlert({
|
setToastAlert({
|
||||||
type: "success",
|
type: "success",
|
||||||
title: "Success!",
|
title: "Success!",
|
||||||
@ -139,7 +65,6 @@ export const CreateUpdateCycleModal: React.FC<CycleModalProps> = (props) => {
|
|||||||
message: "Error in updating cycle. Please try again.",
|
message: "Error in updating cycle. Please try again.",
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
|
||||||
|
|
||||||
const dateChecker = async (payload: CycleDateCheckData) => {
|
const dateChecker = async (payload: CycleDateCheckData) => {
|
||||||
let status = false;
|
let status = false;
|
||||||
@ -186,27 +111,6 @@ export const CreateUpdateCycleModal: React.FC<CycleModalProps> = (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 (
|
return (
|
||||||
<Transition.Root show={isOpen} as={React.Fragment}>
|
<Transition.Root show={isOpen} as={React.Fragment}>
|
||||||
<Dialog as="div" className="relative z-20" onClose={handleClose}>
|
<Dialog as="div" className="relative z-20" onClose={handleClose}>
|
||||||
@ -237,7 +141,7 @@ export const CreateUpdateCycleModal: React.FC<CycleModalProps> = (props) => {
|
|||||||
<CycleForm
|
<CycleForm
|
||||||
handleFormSubmit={handleFormSubmit}
|
handleFormSubmit={handleFormSubmit}
|
||||||
handleClose={handleClose}
|
handleClose={handleClose}
|
||||||
projectId={activeProject ?? ""}
|
projectId={activeProject}
|
||||||
setActiveProject={setActiveProject}
|
setActiveProject={setActiveProject}
|
||||||
data={data}
|
data={data}
|
||||||
/>
|
/>
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import useSWR from "swr";
|
import useSWR from "swr";
|
||||||
import useUserAuth from "hooks/use-user-auth";
|
|
||||||
import { Listbox, Transition } from "@headlessui/react";
|
import { Listbox, Transition } from "@headlessui/react";
|
||||||
// icons
|
// icons
|
||||||
import { ContrastIcon } from "@plane/ui";
|
import { ContrastIcon } from "@plane/ui";
|
||||||
@ -9,7 +8,7 @@ import { Plus } from "lucide-react";
|
|||||||
// services
|
// services
|
||||||
import { CycleService } from "services/cycle.service";
|
import { CycleService } from "services/cycle.service";
|
||||||
// components
|
// components
|
||||||
import { CreateUpdateCycleModal } from "components/cycles";
|
import { CycleCreateUpdateModal } from "components/cycles";
|
||||||
// fetch-keys
|
// fetch-keys
|
||||||
import { CYCLES_LIST } from "constants/fetch-keys";
|
import { CYCLES_LIST } from "constants/fetch-keys";
|
||||||
|
|
||||||
@ -29,8 +28,6 @@ export const CycleSelect: React.FC<IssueCycleSelectProps> = ({ projectId, value,
|
|||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { workspaceSlug } = router.query;
|
const { workspaceSlug } = router.query;
|
||||||
|
|
||||||
const { user } = useUserAuth();
|
|
||||||
|
|
||||||
const { data: cycles } = useSWR(
|
const { data: cycles } = useSWR(
|
||||||
workspaceSlug && projectId ? CYCLES_LIST(projectId) : null,
|
workspaceSlug && projectId ? CYCLES_LIST(projectId) : null,
|
||||||
workspaceSlug && projectId
|
workspaceSlug && projectId
|
||||||
@ -51,7 +48,7 @@ export const CycleSelect: React.FC<IssueCycleSelectProps> = ({ projectId, value,
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{workspaceSlug && projectId && (
|
{workspaceSlug && projectId && (
|
||||||
<CreateUpdateCycleModal
|
<CycleCreateUpdateModal
|
||||||
isOpen={isCycleModalActive}
|
isOpen={isCycleModalActive}
|
||||||
handleClose={closeCycleModal}
|
handleClose={closeCycleModal}
|
||||||
workspaceSlug={workspaceSlug.toString()}
|
workspaceSlug={workspaceSlug.toString()}
|
||||||
|
@ -13,7 +13,7 @@ import useToast from "hooks/use-toast";
|
|||||||
// components
|
// components
|
||||||
import { SidebarProgressStats } from "components/core";
|
import { SidebarProgressStats } from "components/core";
|
||||||
import ProgressChart from "components/core/sidebar/progress-chart";
|
import ProgressChart from "components/core/sidebar/progress-chart";
|
||||||
import { CycleDeleteModal } from "components/cycles/cycle-delete-modal";
|
import { CycleDeleteModal } from "components/cycles/delete-modal";
|
||||||
// ui
|
// ui
|
||||||
import { CustomRangeDatePicker } from "components/ui";
|
import { CustomRangeDatePicker } from "components/ui";
|
||||||
import { CustomMenu, Loader, ProgressBar } from "@plane/ui";
|
import { CustomMenu, Loader, ProgressBar } from "@plane/ui";
|
||||||
@ -285,9 +285,8 @@ export const CycleDetailsSidebar: React.FC<Props> = observer((props) => {
|
|||||||
{cycleDetails && workspaceSlug && projectId && (
|
{cycleDetails && workspaceSlug && projectId && (
|
||||||
<CycleDeleteModal
|
<CycleDeleteModal
|
||||||
cycle={cycleDetails}
|
cycle={cycleDetails}
|
||||||
modal={cycleDeleteModal}
|
isOpen={cycleDeleteModal}
|
||||||
modalClose={() => setCycleDeleteModal(false)}
|
handleClose={() => setCycleDeleteModal(false)}
|
||||||
onSubmit={() => {}}
|
|
||||||
workspaceSlug={workspaceSlug.toString()}
|
workspaceSlug={workspaceSlug.toString()}
|
||||||
projectId={projectId.toString()}
|
projectId={projectId.toString()}
|
||||||
/>
|
/>
|
||||||
|
@ -10,8 +10,7 @@ import { useMobxStore } from "lib/mobx/store-provider";
|
|||||||
import { AppLayout } from "layouts/app-layout";
|
import { AppLayout } from "layouts/app-layout";
|
||||||
// components
|
// components
|
||||||
import { CyclesHeader } from "components/headers";
|
import { CyclesHeader } from "components/headers";
|
||||||
import { CyclesView, ActiveCycleDetails } from "components/cycles";
|
import { CyclesView, ActiveCycleDetails, CycleCreateUpdateModal } from "components/cycles";
|
||||||
import { CycleCreateEditModal } from "components/cycles/cycle-create-edit-modal";
|
|
||||||
// ui
|
// ui
|
||||||
import { EmptyState } from "components/common";
|
import { EmptyState } from "components/common";
|
||||||
// images
|
// images
|
||||||
@ -26,15 +25,12 @@ import { setLocalStorage, getLocalStorage } from "lib/local-storage";
|
|||||||
|
|
||||||
const ProjectCyclesPage: NextPage = observer(() => {
|
const ProjectCyclesPage: NextPage = observer(() => {
|
||||||
const [createModal, setCreateModal] = useState(false);
|
const [createModal, setCreateModal] = useState(false);
|
||||||
const createOnSubmit = () => {};
|
|
||||||
|
|
||||||
// store
|
// store
|
||||||
const { project: projectStore, cycle: cycleStore } = useMobxStore();
|
const { project: projectStore, cycle: cycleStore } = useMobxStore();
|
||||||
|
|
||||||
// router
|
// router
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { workspaceSlug, projectId } = router.query as { workspaceSlug: string; projectId: string };
|
const { workspaceSlug, projectId } = router.query as { workspaceSlug: string; projectId: string };
|
||||||
|
// fetching project details
|
||||||
useSWR(
|
useSWR(
|
||||||
workspaceSlug && projectId ? `PROJECT_DETAILS_${projectId}` : null,
|
workspaceSlug && projectId ? `PROJECT_DETAILS_${projectId}` : null,
|
||||||
workspaceSlug && projectId ? () => projectStore.fetchProjectDetails(workspaceSlug, projectId) : null
|
workspaceSlug && projectId ? () => projectStore.fetchProjectDetails(workspaceSlug, projectId) : null
|
||||||
@ -84,14 +80,12 @@ const ProjectCyclesPage: NextPage = observer(() => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<AppLayout header={<CyclesHeader name={projectDetails?.name} />} withProjectWrapper>
|
<AppLayout header={<CyclesHeader name={projectDetails?.name} />} withProjectWrapper>
|
||||||
<CycleCreateEditModal
|
<CycleCreateUpdateModal
|
||||||
workspaceSlug={workspaceSlug}
|
workspaceSlug={workspaceSlug}
|
||||||
projectId={projectId}
|
projectId={projectId}
|
||||||
modal={createModal}
|
isOpen={createModal}
|
||||||
modalClose={() => setCreateModal(false)}
|
handleClose={() => setCreateModal(false)}
|
||||||
onSubmit={createOnSubmit}
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{projectDetails?.total_cycles === 0 ? (
|
{projectDetails?.total_cycles === 0 ? (
|
||||||
<div className="h-full grid place-items-center">
|
<div className="h-full grid place-items-center">
|
||||||
<EmptyState
|
<EmptyState
|
||||||
|
@ -211,12 +211,14 @@ export class CycleStore implements ICycleStore {
|
|||||||
|
|
||||||
createCycle = async (workspaceSlug: string, projectId: string, data: any) => {
|
createCycle = async (workspaceSlug: string, projectId: string, data: any) => {
|
||||||
try {
|
try {
|
||||||
|
console.log("Cycle Creating");
|
||||||
const response = await this.cycleService.createCycle(
|
const response = await this.cycleService.createCycle(
|
||||||
workspaceSlug,
|
workspaceSlug,
|
||||||
projectId,
|
projectId,
|
||||||
data,
|
data,
|
||||||
this.rootStore.user.currentUser
|
this.rootStore.user.currentUser
|
||||||
);
|
);
|
||||||
|
console.log("Cycle created");
|
||||||
|
|
||||||
runInAction(() => {
|
runInAction(() => {
|
||||||
this.cycle_details = {
|
this.cycle_details = {
|
||||||
|
Loading…
Reference in New Issue
Block a user