mirror of
https://github.com/makeplane/plane
synced 2024-06-14 14:31:34 +00:00
refactor: modules and states folder structure
This commit is contained in:
parent
8b1bf53831
commit
b2eab805e9
@ -20,9 +20,9 @@ import userService from "services/user.service";
|
|||||||
// components
|
// components
|
||||||
import ShortcutsModal from "components/command-palette/shortcuts";
|
import ShortcutsModal from "components/command-palette/shortcuts";
|
||||||
import { CreateProjectModal } from "components/project";
|
import { CreateProjectModal } from "components/project";
|
||||||
import { CreateUpdateIssueModal } from "components/issues/modal";
|
import { CreateUpdateIssueModal } from "components/issues";
|
||||||
|
import { CreateUpdateModuleModal } from "components/modules";
|
||||||
import CreateUpdateCycleModal from "components/project/cycles/create-update-cycle-modal";
|
import CreateUpdateCycleModal from "components/project/cycles/create-update-cycle-modal";
|
||||||
import CreateUpdateModuleModal from "components/project/modules/create-update-module-modal";
|
|
||||||
import BulkDeleteIssuesModal from "components/common/bulk-delete-issues-modal";
|
import BulkDeleteIssuesModal from "components/common/bulk-delete-issues-modal";
|
||||||
// headless ui
|
// headless ui
|
||||||
// helpers
|
// helpers
|
||||||
@ -179,7 +179,6 @@ const CommandPalette: React.FC = () => {
|
|||||||
<CreateUpdateModuleModal
|
<CreateUpdateModuleModal
|
||||||
isOpen={isCreateModuleModalOpen}
|
isOpen={isCreateModuleModalOpen}
|
||||||
setIsOpen={setIsCreateModuleModalOpen}
|
setIsOpen={setIsCreateModuleModalOpen}
|
||||||
projectId={projectId as string}
|
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
export * from "./board-view";
|
export * from "./board-view";
|
||||||
export * from "./comment";
|
export * from "./comment";
|
||||||
|
export * from "./sidebar-select";
|
||||||
export * from "./activity";
|
export * from "./activity";
|
||||||
export * from "./delete-issue-modal";
|
export * from "./delete-issue-modal";
|
||||||
export * from "./description-form";
|
export * from "./description-form";
|
||||||
|
@ -16,8 +16,6 @@ import issuesService from "services/issues.service";
|
|||||||
import useUser from "hooks/use-user";
|
import useUser from "hooks/use-user";
|
||||||
import useToast from "hooks/use-toast";
|
import useToast from "hooks/use-toast";
|
||||||
// components
|
// components
|
||||||
import { CreateUpdateStateModal } from "components/states";
|
|
||||||
import CreateUpdateCycleModal from "components/project/cycles/create-update-cycle-modal";
|
|
||||||
import { IssueForm } from "components/issues";
|
import { IssueForm } from "components/issues";
|
||||||
// common
|
// common
|
||||||
import { renderDateFormat } from "helpers/date-time.helper";
|
import { renderDateFormat } from "helpers/date-time.helper";
|
||||||
@ -206,15 +204,15 @@ export const CreateUpdateIssueModal: React.FC<IssuesModalProps> = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleFormSubmit = async (formData: Partial<IIssue>) => {
|
const handleFormSubmit = async (formData: Partial<IIssue>) => {
|
||||||
if (workspaceSlug && activeProject) {
|
if (!workspaceSlug || !activeProject) return;
|
||||||
const payload: Partial<IIssue> = {
|
|
||||||
...formData,
|
|
||||||
target_date: formData.target_date ? renderDateFormat(formData.target_date ?? "") : null,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!data) await createIssue(payload);
|
const payload: Partial<IIssue> = {
|
||||||
else await updateIssue(payload);
|
...formData,
|
||||||
}
|
target_date: formData.target_date ? renderDateFormat(formData.target_date ?? "") : null,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!data) await createIssue(payload);
|
||||||
|
else await updateIssue(payload);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -16,6 +16,7 @@ import useToast from "hooks/use-toast";
|
|||||||
import issuesServices from "services/issues.service";
|
import issuesServices from "services/issues.service";
|
||||||
// components
|
// components
|
||||||
import {
|
import {
|
||||||
|
DeleteIssueModal,
|
||||||
SidebarAssigneeSelect,
|
SidebarAssigneeSelect,
|
||||||
SidebarBlockedSelect,
|
SidebarBlockedSelect,
|
||||||
SidebarBlockerSelect,
|
SidebarBlockerSelect,
|
||||||
@ -23,8 +24,7 @@ import {
|
|||||||
SidebarParentSelect,
|
SidebarParentSelect,
|
||||||
SidebarPrioritySelect,
|
SidebarPrioritySelect,
|
||||||
SidebarStateSelect,
|
SidebarStateSelect,
|
||||||
} from "components/issues/sidebar-select";
|
} from "components/issues";
|
||||||
import { DeleteIssueModal } from "components/issues";
|
|
||||||
// ui
|
// ui
|
||||||
import { Input, Button, Spinner, CustomDatePicker } from "components/ui";
|
import { Input, Button, Spinner, CustomDatePicker } from "components/ui";
|
||||||
// icons
|
// icons
|
||||||
|
@ -13,7 +13,7 @@ import issuesService from "services/issues.service";
|
|||||||
import useIssuesProperties from "hooks/use-issue-properties";
|
import useIssuesProperties from "hooks/use-issue-properties";
|
||||||
import useIssueView from "hooks/use-issue-view";
|
import useIssueView from "hooks/use-issue-view";
|
||||||
// components
|
// components
|
||||||
import SingleBoard from "components/project/modules/board-view/single-board";
|
import SingleBoard from "components/modules/board-view/single-board";
|
||||||
// ui
|
// ui
|
||||||
import { Spinner } from "components/ui";
|
import { Spinner } from "components/ui";
|
||||||
// types
|
// types
|
@ -25,7 +25,7 @@ type Props = {
|
|||||||
data?: IModule;
|
data?: IModule;
|
||||||
};
|
};
|
||||||
|
|
||||||
const ConfirmModuleDeletion: React.FC<Props> = ({ isOpen, setIsOpen, data }) => {
|
export const DeleteModuleModal: React.FC<Props> = ({ isOpen, setIsOpen, data }) => {
|
||||||
const [isDeleteLoading, setIsDeleteLoading] = useState(false);
|
const [isDeleteLoading, setIsDeleteLoading] = useState(false);
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
@ -152,5 +152,3 @@ const ConfirmModuleDeletion: React.FC<Props> = ({ isOpen, setIsOpen, data }) =>
|
|||||||
</Transition.Root>
|
</Transition.Root>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default ConfirmModuleDeletion;
|
|
128
apps/app/components/modules/form.tsx
Normal file
128
apps/app/components/modules/form.tsx
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
// react-hook-form
|
||||||
|
import { Controller, useForm } from "react-hook-form";
|
||||||
|
// components
|
||||||
|
import { ModuleLeadSelect, ModuleMembersSelect, ModuleStatusSelect } from "components/modules";
|
||||||
|
// ui
|
||||||
|
import { Button, CustomDatePicker, Input, TextArea } from "components/ui";
|
||||||
|
// types
|
||||||
|
import { IModule } from "types";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
handleFormSubmit: (values: Partial<IModule>) => void;
|
||||||
|
handleClose: () => void;
|
||||||
|
status: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
const defaultValues: Partial<IModule> = {
|
||||||
|
name: "",
|
||||||
|
description: "",
|
||||||
|
status: null,
|
||||||
|
lead: null,
|
||||||
|
members_list: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ModuleForm: React.FC<Props> = ({ handleFormSubmit, handleClose, status }) => {
|
||||||
|
const {
|
||||||
|
register,
|
||||||
|
formState: { errors, isSubmitting },
|
||||||
|
handleSubmit,
|
||||||
|
control,
|
||||||
|
reset,
|
||||||
|
} = useForm<IModule>({
|
||||||
|
defaultValues,
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleCreateUpdateModule = async (formData: Partial<IModule>) => {
|
||||||
|
await handleFormSubmit(formData);
|
||||||
|
|
||||||
|
reset({
|
||||||
|
...defaultValues,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<form onSubmit={handleSubmit(handleCreateUpdateModule)}>
|
||||||
|
<div className="space-y-5">
|
||||||
|
<h3 className="text-lg font-medium leading-6 text-gray-900">
|
||||||
|
{status ? "Update" : "Create"} Module
|
||||||
|
</h3>
|
||||||
|
<div className="space-y-3">
|
||||||
|
<div>
|
||||||
|
<Input
|
||||||
|
id="name"
|
||||||
|
label="Name"
|
||||||
|
name="name"
|
||||||
|
type="name"
|
||||||
|
placeholder="Enter name"
|
||||||
|
autoComplete="off"
|
||||||
|
error={errors.name}
|
||||||
|
register={register}
|
||||||
|
validations={{
|
||||||
|
required: "Name is required",
|
||||||
|
maxLength: {
|
||||||
|
value: 255,
|
||||||
|
message: "Name should be less than 255 characters",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<TextArea
|
||||||
|
id="description"
|
||||||
|
name="description"
|
||||||
|
label="Description"
|
||||||
|
placeholder="Enter description"
|
||||||
|
error={errors.description}
|
||||||
|
register={register}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="flex gap-x-2">
|
||||||
|
<div className="w-full">
|
||||||
|
<h6 className="text-gray-500">Start Date</h6>
|
||||||
|
<div className="w-full">
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="start_date"
|
||||||
|
render={({ field: { value, onChange } }) => (
|
||||||
|
<CustomDatePicker renderAs="input" value={value} onChange={onChange} />
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="w-full">
|
||||||
|
<h6 className="text-gray-500">Target Date</h6>
|
||||||
|
<div className="w-full">
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="target_date"
|
||||||
|
render={({ field: { value, onChange } }) => (
|
||||||
|
<CustomDatePicker renderAs="input" value={value} onChange={onChange} />
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-wrap items-center gap-2">
|
||||||
|
<ModuleStatusSelect control={control} error={errors.status} />
|
||||||
|
<ModuleLeadSelect control={control} />
|
||||||
|
<ModuleMembersSelect control={control} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="mt-5 flex justify-end gap-2">
|
||||||
|
<Button theme="secondary" onClick={handleClose}>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
<Button type="submit" disabled={isSubmitting}>
|
||||||
|
{status
|
||||||
|
? isSubmitting
|
||||||
|
? "Updating Module..."
|
||||||
|
: "Update Module"
|
||||||
|
: isSubmitting
|
||||||
|
? "Creating Module..."
|
||||||
|
: "Create Module"}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
);
|
||||||
|
};
|
10
apps/app/components/modules/index.ts
Normal file
10
apps/app/components/modules/index.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
export * from "./board-view";
|
||||||
|
export * from "./select";
|
||||||
|
export * from "./sidebar-select";
|
||||||
|
export * from "./delete-module-modal";
|
||||||
|
export * from "./form";
|
||||||
|
export * from "./list-view";
|
||||||
|
export * from "./modal";
|
||||||
|
export * from "./module-link-modal";
|
||||||
|
export * from "./sidebar";
|
||||||
|
export * from "./single-module-card";
|
@ -38,7 +38,7 @@ type Props = {
|
|||||||
userAuth: UserAuth;
|
userAuth: UserAuth;
|
||||||
};
|
};
|
||||||
|
|
||||||
const ModulesListView: React.FC<Props> = ({
|
export const ModulesListView: React.FC<Props> = ({
|
||||||
issues,
|
issues,
|
||||||
openCreateIssueModal,
|
openCreateIssueModal,
|
||||||
openIssuesListModal,
|
openIssuesListModal,
|
||||||
@ -192,5 +192,3 @@ const ModulesListView: React.FC<Props> = ({
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default ModulesListView;
|
|
170
apps/app/components/modules/modal.tsx
Normal file
170
apps/app/components/modules/modal.tsx
Normal file
@ -0,0 +1,170 @@
|
|||||||
|
import React, { useEffect } from "react";
|
||||||
|
|
||||||
|
import { useRouter } from "next/router";
|
||||||
|
|
||||||
|
import { mutate } from "swr";
|
||||||
|
|
||||||
|
// react-hook-form
|
||||||
|
import { useForm } from "react-hook-form";
|
||||||
|
// headless ui
|
||||||
|
import { Dialog, Transition } from "@headlessui/react";
|
||||||
|
// components
|
||||||
|
import { ModuleForm } from "components/modules";
|
||||||
|
// services
|
||||||
|
import modulesService from "services/modules.service";
|
||||||
|
// hooks
|
||||||
|
import useToast from "hooks/use-toast";
|
||||||
|
// helpers
|
||||||
|
import { renderDateFormat } from "helpers/date-time.helper";
|
||||||
|
// types
|
||||||
|
import type { IModule } from "types";
|
||||||
|
// fetch-keys
|
||||||
|
import { MODULE_LIST } from "constants/fetch-keys";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
isOpen: boolean;
|
||||||
|
setIsOpen: React.Dispatch<React.SetStateAction<boolean>>;
|
||||||
|
data?: IModule;
|
||||||
|
};
|
||||||
|
|
||||||
|
const defaultValues: Partial<IModule> = {
|
||||||
|
name: "",
|
||||||
|
description: "",
|
||||||
|
status: null,
|
||||||
|
lead: null,
|
||||||
|
members_list: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
export const CreateUpdateModuleModal: React.FC<Props> = ({ isOpen, setIsOpen, data }) => {
|
||||||
|
const router = useRouter();
|
||||||
|
const { workspaceSlug, projectId } = router.query;
|
||||||
|
|
||||||
|
const { setToastAlert } = useToast();
|
||||||
|
|
||||||
|
const handleClose = () => {
|
||||||
|
setIsOpen(false);
|
||||||
|
reset(defaultValues);
|
||||||
|
};
|
||||||
|
|
||||||
|
const { reset, setError } = useForm<IModule>({
|
||||||
|
defaultValues,
|
||||||
|
});
|
||||||
|
|
||||||
|
const createModule = async (payload: Partial<IModule>) => {
|
||||||
|
await modulesService
|
||||||
|
.createModule(workspaceSlug as string, projectId as string, payload)
|
||||||
|
.then(() => {
|
||||||
|
mutate(MODULE_LIST(projectId as string));
|
||||||
|
handleClose();
|
||||||
|
|
||||||
|
setToastAlert({
|
||||||
|
title: "Success",
|
||||||
|
type: "success",
|
||||||
|
message: "Module created successfully",
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
Object.keys(err).map((key) => {
|
||||||
|
setError(key as keyof typeof defaultValues, {
|
||||||
|
message: err[key].join(", "),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateModule = async (payload: Partial<IModule>) => {
|
||||||
|
await modulesService
|
||||||
|
.updateModule(workspaceSlug as string, projectId as string, data?.id ?? "", payload)
|
||||||
|
.then((res) => {
|
||||||
|
mutate<IModule[]>(
|
||||||
|
MODULE_LIST(projectId as string),
|
||||||
|
(prevData) => {
|
||||||
|
const newData = prevData?.map((item) => {
|
||||||
|
if (item.id === res.id) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
return item;
|
||||||
|
});
|
||||||
|
return newData;
|
||||||
|
},
|
||||||
|
false
|
||||||
|
);
|
||||||
|
handleClose();
|
||||||
|
|
||||||
|
setToastAlert({
|
||||||
|
title: "Success",
|
||||||
|
type: "success",
|
||||||
|
message: "Module updated successfully",
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
Object.keys(err).map((key) => {
|
||||||
|
setError(key as keyof typeof defaultValues, {
|
||||||
|
message: err[key].join(", "),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleFormSubmit = async (formData: Partial<IModule>) => {
|
||||||
|
if (!workspaceSlug || !projectId) return;
|
||||||
|
|
||||||
|
const payload: Partial<IModule> = {
|
||||||
|
...formData,
|
||||||
|
start_date: formData.start_date ? renderDateFormat(formData.start_date ?? "") : null,
|
||||||
|
target_date: formData.target_date ? renderDateFormat(formData.target_date ?? "") : null,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!data) await createModule(payload);
|
||||||
|
else await updateModule(payload);
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (data) {
|
||||||
|
setIsOpen(true);
|
||||||
|
reset(data);
|
||||||
|
} else {
|
||||||
|
reset(defaultValues);
|
||||||
|
}
|
||||||
|
}, [data, setIsOpen, reset]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Transition.Root show={isOpen} as={React.Fragment}>
|
||||||
|
<Dialog as="div" className="relative z-20" onClose={handleClose}>
|
||||||
|
<Transition.Child
|
||||||
|
as={React.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-gray-500 bg-opacity-75 transition-opacity" />
|
||||||
|
</Transition.Child>
|
||||||
|
|
||||||
|
<div className="fixed inset-0 z-20 overflow-y-auto">
|
||||||
|
<div className="flex min-h-full items-center justify-center p-4 text-center sm:p-0">
|
||||||
|
<Transition.Child
|
||||||
|
as={React.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 bg-white px-5 py-8 text-left shadow-xl transition-all sm:my-8 sm:w-full sm:max-w-2xl sm:p-6">
|
||||||
|
<ModuleForm
|
||||||
|
handleFormSubmit={handleFormSubmit}
|
||||||
|
handleClose={handleClose}
|
||||||
|
status={data ? true : false}
|
||||||
|
/>
|
||||||
|
</Dialog.Panel>
|
||||||
|
</Transition.Child>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Dialog>
|
||||||
|
</Transition.Root>
|
||||||
|
);
|
||||||
|
};
|
@ -28,7 +28,7 @@ const defaultValues: ModuleLink = {
|
|||||||
url: "",
|
url: "",
|
||||||
};
|
};
|
||||||
|
|
||||||
const ModuleLinkModal: React.FC<Props> = ({ isOpen, module, handleClose }) => {
|
export const ModuleLinkModal: React.FC<Props> = ({ isOpen, module, handleClose }) => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { workspaceSlug, projectId, moduleId } = router.query;
|
const { workspaceSlug, projectId, moduleId } = router.query;
|
||||||
|
|
||||||
@ -158,5 +158,3 @@ const ModuleLinkModal: React.FC<Props> = ({ isOpen, module, handleClose }) => {
|
|||||||
</Transition.Root>
|
</Transition.Root>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default ModuleLinkModal;
|
|
3
apps/app/components/modules/select/index.ts
Normal file
3
apps/app/components/modules/select/index.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export * from "./select-lead";
|
||||||
|
export * from "./select-members";
|
||||||
|
export * from "./select-status";
|
@ -4,16 +4,16 @@ import { useRouter } from "next/router";
|
|||||||
|
|
||||||
import useSWR from "swr";
|
import useSWR from "swr";
|
||||||
|
|
||||||
|
// react-hook-form
|
||||||
import { Controller, Control } from "react-hook-form";
|
import { Controller, Control } from "react-hook-form";
|
||||||
import { UserIcon } from "@heroicons/react/24/outline";
|
// services
|
||||||
// import type { Control } from "react-hook-form";
|
|
||||||
// service
|
|
||||||
import type { IModule } from "types";
|
|
||||||
import projectServices from "services/project.service";
|
import projectServices from "services/project.service";
|
||||||
// ui
|
// ui
|
||||||
import SearchListbox from "components/search-listbox";
|
import SearchListbox from "components/search-listbox";
|
||||||
// icons
|
// icons
|
||||||
|
import { UserIcon } from "@heroicons/react/24/outline";
|
||||||
// types
|
// types
|
||||||
|
import type { IModule } from "types";
|
||||||
// fetch-keys
|
// fetch-keys
|
||||||
import { PROJECT_MEMBERS } from "constants/fetch-keys";
|
import { PROJECT_MEMBERS } from "constants/fetch-keys";
|
||||||
|
|
||||||
@ -21,7 +21,7 @@ type Props = {
|
|||||||
control: Control<IModule, any>;
|
control: Control<IModule, any>;
|
||||||
};
|
};
|
||||||
|
|
||||||
const SelectLead: React.FC<Props> = ({ control }) => {
|
export const ModuleLeadSelect: React.FC<Props> = ({ control }) => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { workspaceSlug, projectId } = router.query;
|
const { workspaceSlug, projectId } = router.query;
|
||||||
|
|
||||||
@ -55,5 +55,3 @@ const SelectLead: React.FC<Props> = ({ control }) => {
|
|||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default SelectLead;
|
|
@ -4,16 +4,16 @@ import { useRouter } from "next/router";
|
|||||||
|
|
||||||
import useSWR from "swr";
|
import useSWR from "swr";
|
||||||
|
|
||||||
|
// react-hook-form
|
||||||
import { Controller, Control } from "react-hook-form";
|
import { Controller, Control } from "react-hook-form";
|
||||||
import { UserIcon } from "@heroicons/react/24/outline";
|
// services
|
||||||
// import type { Control } from "react-hook-form";
|
|
||||||
// service
|
|
||||||
import type { IModule } from "types";
|
|
||||||
import projectServices from "services/project.service";
|
import projectServices from "services/project.service";
|
||||||
// ui
|
// ui
|
||||||
import SearchListbox from "components/search-listbox";
|
import SearchListbox from "components/search-listbox";
|
||||||
// icons
|
// icons
|
||||||
|
import { UserIcon } from "@heroicons/react/24/outline";
|
||||||
// types
|
// types
|
||||||
|
import type { IModule } from "types";
|
||||||
// fetch-keys
|
// fetch-keys
|
||||||
import { PROJECT_MEMBERS } from "constants/fetch-keys";
|
import { PROJECT_MEMBERS } from "constants/fetch-keys";
|
||||||
|
|
||||||
@ -21,7 +21,7 @@ type Props = {
|
|||||||
control: Control<IModule, any>;
|
control: Control<IModule, any>;
|
||||||
};
|
};
|
||||||
|
|
||||||
const SelectMembers: React.FC<Props> = ({ control }) => {
|
export const ModuleMembersSelect: React.FC<Props> = ({ control }) => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { workspaceSlug, projectId } = router.query;
|
const { workspaceSlug, projectId } = router.query;
|
||||||
|
|
||||||
@ -56,5 +56,3 @@ const SelectMembers: React.FC<Props> = ({ control }) => {
|
|||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default SelectMembers;
|
|
@ -16,7 +16,7 @@ type Props = {
|
|||||||
error?: FieldError;
|
error?: FieldError;
|
||||||
};
|
};
|
||||||
|
|
||||||
const SelectStatus: React.FC<Props> = ({ control, error }) => (
|
export const ModuleStatusSelect: React.FC<Props> = ({ control, error }) => (
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
rules={{ required: true }}
|
rules={{ required: true }}
|
||||||
@ -45,4 +45,3 @@ const SelectStatus: React.FC<Props> = ({ control, error }) => (
|
|||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
export default SelectStatus;
|
|
3
apps/app/components/modules/sidebar-select/index.ts
Normal file
3
apps/app/components/modules/sidebar-select/index.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export * from "./select-lead";
|
||||||
|
export * from "./select-members";
|
||||||
|
export * from "./select-status";
|
@ -25,7 +25,7 @@ type Props = {
|
|||||||
lead: IUserLite | null;
|
lead: IUserLite | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
const SelectLead: React.FC<Props> = ({ control, submitChanges, lead }) => {
|
export const SidebarLeadSelect: React.FC<Props> = ({ control, submitChanges, lead }) => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { workspaceSlug } = router.query;
|
const { workspaceSlug } = router.query;
|
||||||
|
|
||||||
@ -164,5 +164,3 @@ const SelectLead: React.FC<Props> = ({ control, submitChanges, lead }) => {
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default SelectLead;
|
|
@ -23,7 +23,7 @@ type Props = {
|
|||||||
submitChanges: (formData: Partial<IModule>) => void;
|
submitChanges: (formData: Partial<IModule>) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const SelectMembers: React.FC<Props> = ({ control, submitChanges }) => {
|
export const SidebarMembersSelect: React.FC<Props> = ({ control, submitChanges }) => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { workspaceSlug } = router.query;
|
const { workspaceSlug } = router.query;
|
||||||
|
|
||||||
@ -132,5 +132,3 @@ const SelectMembers: React.FC<Props> = ({ control, submitChanges }) => {
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default SelectMembers;
|
|
@ -18,7 +18,7 @@ type Props = {
|
|||||||
watch: UseFormWatch<Partial<IModule>>;
|
watch: UseFormWatch<Partial<IModule>>;
|
||||||
};
|
};
|
||||||
|
|
||||||
const SelectStatus: React.FC<Props> = ({ control, submitChanges, watch }) => (
|
export const SidebarStatusSelect: React.FC<Props> = ({ control, submitChanges, watch }) => (
|
||||||
<div className="flex flex-wrap items-center py-2">
|
<div className="flex flex-wrap items-center py-2">
|
||||||
<div className="flex items-center gap-x-2 text-sm sm:basis-1/2">
|
<div className="flex items-center gap-x-2 text-sm sm:basis-1/2">
|
||||||
<Squares2X2Icon className="h-4 w-4 flex-shrink-0" />
|
<Squares2X2Icon className="h-4 w-4 flex-shrink-0" />
|
||||||
@ -67,5 +67,3 @@ const SelectStatus: React.FC<Props> = ({ control, submitChanges, watch }) => (
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
export default SelectStatus;
|
|
@ -12,10 +12,12 @@ import modulesService from "services/modules.service";
|
|||||||
// hooks
|
// hooks
|
||||||
import useToast from "hooks/use-toast";
|
import useToast from "hooks/use-toast";
|
||||||
// components
|
// components
|
||||||
import SelectLead from "components/project/modules/module-detail-sidebar/select-lead";
|
import {
|
||||||
import SelectMembers from "components/project/modules/module-detail-sidebar/select-members";
|
ModuleLinkModal,
|
||||||
import SelectStatus from "components/project/modules/module-detail-sidebar/select-status";
|
SidebarLeadSelect,
|
||||||
import ModuleLinkModal from "components/project/modules/module-link-modal";
|
SidebarMembersSelect,
|
||||||
|
SidebarStatusSelect,
|
||||||
|
} from "components/modules";
|
||||||
// progress-bar
|
// progress-bar
|
||||||
import { CircularProgressbar } from "react-circular-progressbar";
|
import { CircularProgressbar } from "react-circular-progressbar";
|
||||||
import "react-circular-progressbar/dist/styles.css";
|
import "react-circular-progressbar/dist/styles.css";
|
||||||
@ -53,7 +55,7 @@ type Props = {
|
|||||||
handleDeleteModule: () => void;
|
handleDeleteModule: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const ModuleDetailSidebar: React.FC<Props> = ({
|
export const ModuleDetailsSidebar: React.FC<Props> = ({
|
||||||
module,
|
module,
|
||||||
isOpen,
|
isOpen,
|
||||||
moduleIssues,
|
moduleIssues,
|
||||||
@ -161,12 +163,12 @@ const ModuleDetailSidebar: React.FC<Props> = ({
|
|||||||
</div>
|
</div>
|
||||||
<div className="divide-y-2 divide-gray-100 text-xs">
|
<div className="divide-y-2 divide-gray-100 text-xs">
|
||||||
<div className="py-1">
|
<div className="py-1">
|
||||||
<SelectLead
|
<SidebarLeadSelect
|
||||||
control={control}
|
control={control}
|
||||||
submitChanges={submitChanges}
|
submitChanges={submitChanges}
|
||||||
lead={module.lead_detail}
|
lead={module.lead_detail}
|
||||||
/>
|
/>
|
||||||
<SelectMembers control={control} submitChanges={submitChanges} />
|
<SidebarMembersSelect control={control} submitChanges={submitChanges} />
|
||||||
<div className="flex flex-wrap items-center py-2">
|
<div className="flex flex-wrap items-center py-2">
|
||||||
<div className="flex items-center gap-x-2 text-sm sm:basis-1/2">
|
<div className="flex items-center gap-x-2 text-sm sm:basis-1/2">
|
||||||
<ChartPieIcon className="h-4 w-4 flex-shrink-0" />
|
<ChartPieIcon className="h-4 w-4 flex-shrink-0" />
|
||||||
@ -233,7 +235,11 @@ const ModuleDetailSidebar: React.FC<Props> = ({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="py-1">
|
<div className="py-1">
|
||||||
<SelectStatus control={control} submitChanges={submitChanges} watch={watch} />
|
<SidebarStatusSelect
|
||||||
|
control={control}
|
||||||
|
submitChanges={submitChanges}
|
||||||
|
watch={watch}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="py-1">
|
<div className="py-1">
|
||||||
<div className="flex items-center justify-between gap-2">
|
<div className="flex items-center justify-between gap-2">
|
||||||
@ -302,5 +308,3 @@ const ModuleDetailSidebar: React.FC<Props> = ({
|
|||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default ModuleDetailSidebar;
|
|
@ -1,10 +1,13 @@
|
|||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
|
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import { CalendarDaysIcon, TrashIcon } from "@heroicons/react/24/outline";
|
|
||||||
import ConfirmModuleDeletion from "./confirm-module-deletion";
|
// components
|
||||||
|
import { DeleteModuleModal } from "components/modules";
|
||||||
// icons
|
// icons
|
||||||
|
import { CalendarDaysIcon, TrashIcon } from "@heroicons/react/24/outline";
|
||||||
import User from "public/user.png";
|
import User from "public/user.png";
|
||||||
// helpers
|
// helpers
|
||||||
import { renderShortNumericDateFormat } from "helpers/date-time.helper";
|
import { renderShortNumericDateFormat } from "helpers/date-time.helper";
|
||||||
@ -17,7 +20,7 @@ type Props = {
|
|||||||
module: IModule;
|
module: IModule;
|
||||||
};
|
};
|
||||||
|
|
||||||
const SingleModuleCard: React.FC<Props> = ({ module }) => {
|
export const SingleModuleCard: React.FC<Props> = ({ module }) => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { workspaceSlug } = router.query;
|
const { workspaceSlug } = router.query;
|
||||||
const [moduleDeleteModal, setModuleDeleteModal] = useState(false);
|
const [moduleDeleteModal, setModuleDeleteModal] = useState(false);
|
||||||
@ -40,7 +43,7 @@ const SingleModuleCard: React.FC<Props> = ({ module }) => {
|
|||||||
<TrashIcon className="h-4 w-4" />
|
<TrashIcon className="h-4 w-4" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<ConfirmModuleDeletion
|
<DeleteModuleModal
|
||||||
isOpen={
|
isOpen={
|
||||||
moduleDeleteModal &&
|
moduleDeleteModal &&
|
||||||
!!selectedModuleForDelete &&
|
!!selectedModuleForDelete &&
|
||||||
@ -147,5 +150,3 @@ const SingleModuleCard: React.FC<Props> = ({ module }) => {
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default SingleModuleCard;
|
|
@ -1,273 +0,0 @@
|
|||||||
import React, { useEffect } from "react";
|
|
||||||
|
|
||||||
import { useRouter } from "next/router";
|
|
||||||
|
|
||||||
import { mutate } from "swr";
|
|
||||||
|
|
||||||
// react-hook-form
|
|
||||||
import { Controller, useForm } from "react-hook-form";
|
|
||||||
// headless ui
|
|
||||||
import { Dialog, Transition } from "@headlessui/react";
|
|
||||||
// components
|
|
||||||
import SelectLead from "components/project/modules/create-update-module-modal/select-lead";
|
|
||||||
import SelectMembers from "components/project/modules/create-update-module-modal/select-members";
|
|
||||||
import SelectStatus from "components/project/modules/create-update-module-modal/select-status";
|
|
||||||
// ui
|
|
||||||
import { Button, CustomDatePicker, Input, TextArea } from "components/ui";
|
|
||||||
// services
|
|
||||||
import modulesService from "services/modules.service";
|
|
||||||
// hooks
|
|
||||||
import useToast from "hooks/use-toast";
|
|
||||||
// helpers
|
|
||||||
import { renderDateFormat } from "helpers/date-time.helper";
|
|
||||||
// types
|
|
||||||
import type { IModule } from "types";
|
|
||||||
// fetch-keys
|
|
||||||
import { MODULE_LIST } from "constants/fetch-keys";
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
isOpen: boolean;
|
|
||||||
setIsOpen: React.Dispatch<React.SetStateAction<boolean>>;
|
|
||||||
projectId: string;
|
|
||||||
data?: IModule;
|
|
||||||
};
|
|
||||||
|
|
||||||
const defaultValues: Partial<IModule> = {
|
|
||||||
name: "",
|
|
||||||
description: "",
|
|
||||||
status: null,
|
|
||||||
lead: null,
|
|
||||||
members_list: [],
|
|
||||||
};
|
|
||||||
|
|
||||||
const CreateUpdateModuleModal: React.FC<Props> = ({ isOpen, setIsOpen, data, projectId }) => {
|
|
||||||
const router = useRouter();
|
|
||||||
const { workspaceSlug } = router.query;
|
|
||||||
|
|
||||||
const { setToastAlert } = useToast();
|
|
||||||
|
|
||||||
const {
|
|
||||||
register,
|
|
||||||
formState: { errors, isSubmitting },
|
|
||||||
handleSubmit,
|
|
||||||
control,
|
|
||||||
reset,
|
|
||||||
setError,
|
|
||||||
} = useForm<IModule>({
|
|
||||||
defaultValues,
|
|
||||||
});
|
|
||||||
|
|
||||||
const onSubmit = async (formData: IModule) => {
|
|
||||||
if (!workspaceSlug) return;
|
|
||||||
const payload = {
|
|
||||||
...formData,
|
|
||||||
start_date: formData.start_date ? renderDateFormat(formData.start_date) : null,
|
|
||||||
target_date: formData.target_date ? renderDateFormat(formData.target_date) : null,
|
|
||||||
};
|
|
||||||
if (!data) {
|
|
||||||
await modulesService
|
|
||||||
.createModule(workspaceSlug as string, projectId, payload)
|
|
||||||
.then(() => {
|
|
||||||
mutate(MODULE_LIST(projectId));
|
|
||||||
handleClose();
|
|
||||||
|
|
||||||
setToastAlert({
|
|
||||||
title: "Success",
|
|
||||||
type: "success",
|
|
||||||
message: "Module created successfully",
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
Object.keys(err).map((key) => {
|
|
||||||
setError(key as keyof typeof defaultValues, {
|
|
||||||
message: err[key].join(", "),
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
await modulesService
|
|
||||||
.updateModule(workspaceSlug as string, projectId, data.id, payload)
|
|
||||||
.then((res) => {
|
|
||||||
mutate<IModule[]>(
|
|
||||||
MODULE_LIST(projectId),
|
|
||||||
(prevData) => {
|
|
||||||
const newData = prevData?.map((item) => {
|
|
||||||
if (item.id === res.id) {
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
return item;
|
|
||||||
});
|
|
||||||
return newData;
|
|
||||||
},
|
|
||||||
false
|
|
||||||
);
|
|
||||||
handleClose();
|
|
||||||
|
|
||||||
setToastAlert({
|
|
||||||
title: "Success",
|
|
||||||
type: "success",
|
|
||||||
message: "Module updated successfully",
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
Object.keys(err).map((key) => {
|
|
||||||
setError(key as keyof typeof defaultValues, {
|
|
||||||
message: err[key].join(", "),
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleClose = () => {
|
|
||||||
setIsOpen(false);
|
|
||||||
reset(defaultValues);
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (data) {
|
|
||||||
setIsOpen(true);
|
|
||||||
reset(data);
|
|
||||||
} else {
|
|
||||||
reset(defaultValues);
|
|
||||||
}
|
|
||||||
}, [data, setIsOpen, reset]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Transition.Root show={isOpen} as={React.Fragment}>
|
|
||||||
<Dialog as="div" className="relative z-20" onClose={handleClose}>
|
|
||||||
<Transition.Child
|
|
||||||
as={React.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-gray-500 bg-opacity-75 transition-opacity" />
|
|
||||||
</Transition.Child>
|
|
||||||
|
|
||||||
<div className="fixed inset-0 z-20 overflow-y-auto">
|
|
||||||
<div className="flex min-h-full items-center justify-center p-4 text-center sm:p-0">
|
|
||||||
<Transition.Child
|
|
||||||
as={React.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 bg-white px-5 py-8 text-left shadow-xl transition-all sm:my-8 sm:w-full sm:max-w-2xl sm:p-6">
|
|
||||||
<form onSubmit={handleSubmit(onSubmit)}>
|
|
||||||
<div className="space-y-5">
|
|
||||||
<Dialog.Title as="h3" className="text-lg font-medium leading-6 text-gray-900">
|
|
||||||
{data ? "Update" : "Create"} Module
|
|
||||||
</Dialog.Title>
|
|
||||||
<div className="space-y-3">
|
|
||||||
<div>
|
|
||||||
<Input
|
|
||||||
id="name"
|
|
||||||
label="Name"
|
|
||||||
name="name"
|
|
||||||
type="name"
|
|
||||||
placeholder="Enter name"
|
|
||||||
autoComplete="off"
|
|
||||||
error={errors.name}
|
|
||||||
register={register}
|
|
||||||
validations={{
|
|
||||||
required: "Name is required",
|
|
||||||
maxLength: {
|
|
||||||
value: 255,
|
|
||||||
message: "Name should be less than 255 characters",
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<TextArea
|
|
||||||
id="description"
|
|
||||||
name="description"
|
|
||||||
label="Description"
|
|
||||||
placeholder="Enter description"
|
|
||||||
error={errors.description}
|
|
||||||
register={register}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="flex gap-x-2">
|
|
||||||
<div className="w-full">
|
|
||||||
<h6 className="text-gray-500">Start Date</h6>
|
|
||||||
<div className="w-full">
|
|
||||||
<Controller
|
|
||||||
control={control}
|
|
||||||
name="start_date"
|
|
||||||
rules={{ required: "Start date is required" }}
|
|
||||||
render={({ field: { value, onChange } }) => (
|
|
||||||
<CustomDatePicker
|
|
||||||
renderAs="input"
|
|
||||||
value={value}
|
|
||||||
onChange={onChange}
|
|
||||||
error={errors.start_date ? true : false}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
{errors.start_date && (
|
|
||||||
<h6 className="text-sm text-red-500">{errors.start_date.message}</h6>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="w-full">
|
|
||||||
<h6 className="text-gray-500">Target Date</h6>
|
|
||||||
<div className="w-full">
|
|
||||||
<Controller
|
|
||||||
control={control}
|
|
||||||
name="target_date"
|
|
||||||
rules={{ required: "Target date is required" }}
|
|
||||||
render={({ field: { value, onChange } }) => (
|
|
||||||
<CustomDatePicker
|
|
||||||
renderAs="input"
|
|
||||||
value={value}
|
|
||||||
onChange={onChange}
|
|
||||||
error={errors.target_date ? true : false}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
{errors.target_date && (
|
|
||||||
<h6 className="text-sm text-red-500">{errors.target_date.message}</h6>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="flex flex-wrap items-center gap-2">
|
|
||||||
<SelectStatus control={control} error={errors.status} />
|
|
||||||
<SelectLead control={control} />
|
|
||||||
<SelectMembers control={control} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="mt-5 flex justify-end gap-2">
|
|
||||||
<Button theme="secondary" onClick={handleClose}>
|
|
||||||
Cancel
|
|
||||||
</Button>
|
|
||||||
<Button type="submit" disabled={isSubmitting}>
|
|
||||||
{data
|
|
||||||
? isSubmitting
|
|
||||||
? "Updating Module..."
|
|
||||||
: "Update Module"
|
|
||||||
: isSubmitting
|
|
||||||
? "Creating Module..."
|
|
||||||
: "Create Module"}
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</Dialog.Panel>
|
|
||||||
</Transition.Child>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Dialog>
|
|
||||||
</Transition.Root>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default CreateUpdateModuleModal;
|
|
@ -16,11 +16,9 @@ import AppLayout from "layouts/app-layout";
|
|||||||
import { IssueViewContextProvider } from "contexts/issue-view.context";
|
import { IssueViewContextProvider } from "contexts/issue-view.context";
|
||||||
// components
|
// components
|
||||||
import ExistingIssuesListModal from "components/common/existing-issues-list-modal";
|
import ExistingIssuesListModal from "components/common/existing-issues-list-modal";
|
||||||
import ModulesBoardView from "components/project/modules/board-view";
|
import ModulesBoardView from "components/modules/board-view";
|
||||||
import ModulesListView from "components/project/modules/list-view";
|
|
||||||
import ModuleDetailSidebar from "components/project/modules/module-detail-sidebar";
|
|
||||||
import ConfirmModuleDeletion from "components/project/modules/confirm-module-deletion";
|
|
||||||
import { CreateUpdateIssueModal, DeleteIssueModal } from "components/issues";
|
import { CreateUpdateIssueModal, DeleteIssueModal } from "components/issues";
|
||||||
|
import { DeleteModuleModal, ModuleDetailsSidebar, ModulesListView } from "components/modules";
|
||||||
import View from "components/core/view";
|
import View from "components/core/view";
|
||||||
// ui
|
// ui
|
||||||
import { CustomMenu, EmptySpace, EmptySpaceItem, Spinner } from "components/ui";
|
import { CustomMenu, EmptySpace, EmptySpaceItem, Spinner } from "components/ui";
|
||||||
@ -212,7 +210,7 @@ const SingleModule: React.FC<UserAuth> = (props) => {
|
|||||||
isOpen={!!deleteIssue}
|
isOpen={!!deleteIssue}
|
||||||
data={moduleIssuesArray?.find((issue) => issue.id === deleteIssue)}
|
data={moduleIssuesArray?.find((issue) => issue.id === deleteIssue)}
|
||||||
/>
|
/>
|
||||||
<ConfirmModuleDeletion
|
<DeleteModuleModal
|
||||||
isOpen={
|
isOpen={
|
||||||
moduleDeleteModal &&
|
moduleDeleteModal &&
|
||||||
!!selectedModuleForDelete &&
|
!!selectedModuleForDelete &&
|
||||||
@ -321,7 +319,7 @@ const SingleModule: React.FC<UserAuth> = (props) => {
|
|||||||
<Spinner />
|
<Spinner />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<ModuleDetailSidebar
|
<ModuleDetailsSidebar
|
||||||
module={moduleDetails}
|
module={moduleDetails}
|
||||||
isOpen={moduleSidebar}
|
isOpen={moduleSidebar}
|
||||||
moduleIssues={moduleIssues}
|
moduleIssues={moduleIssues}
|
||||||
|
@ -12,7 +12,7 @@ import { requiredAuth } from "lib/auth";
|
|||||||
import projectService from "services/project.service";
|
import projectService from "services/project.service";
|
||||||
import modulesService from "services/modules.service";
|
import modulesService from "services/modules.service";
|
||||||
// components
|
// components
|
||||||
import SingleModuleCard from "components/project/modules/single-module-card";
|
import { SingleModuleCard } from "components/modules";
|
||||||
// ui
|
// ui
|
||||||
import { EmptySpace, EmptySpaceItem, HeaderButton, Loader } from "components/ui";
|
import { EmptySpace, EmptySpaceItem, HeaderButton, Loader } from "components/ui";
|
||||||
import { BreadcrumbItem, Breadcrumbs } from "components/breadcrumbs";
|
import { BreadcrumbItem, Breadcrumbs } from "components/breadcrumbs";
|
||||||
|
Loading…
Reference in New Issue
Block a user