refactor: modules and states folder structure

This commit is contained in:
Aaryan Khandelwal 2023-02-02 18:04:13 +05:30
parent 8b1bf53831
commit b2eab805e9
25 changed files with 371 additions and 346 deletions

View File

@ -20,9 +20,9 @@ import userService from "services/user.service";
// components
import ShortcutsModal from "components/command-palette/shortcuts";
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 CreateUpdateModuleModal from "components/project/modules/create-update-module-modal";
import BulkDeleteIssuesModal from "components/common/bulk-delete-issues-modal";
// headless ui
// helpers
@ -179,7 +179,6 @@ const CommandPalette: React.FC = () => {
<CreateUpdateModuleModal
isOpen={isCreateModuleModalOpen}
setIsOpen={setIsCreateModuleModalOpen}
projectId={projectId as string}
/>
</>
)}

View File

@ -1,5 +1,6 @@
export * from "./board-view";
export * from "./comment";
export * from "./sidebar-select";
export * from "./activity";
export * from "./delete-issue-modal";
export * from "./description-form";

View File

@ -16,8 +16,6 @@ import issuesService from "services/issues.service";
import useUser from "hooks/use-user";
import useToast from "hooks/use-toast";
// components
import { CreateUpdateStateModal } from "components/states";
import CreateUpdateCycleModal from "components/project/cycles/create-update-cycle-modal";
import { IssueForm } from "components/issues";
// common
import { renderDateFormat } from "helpers/date-time.helper";
@ -206,15 +204,15 @@ export const CreateUpdateIssueModal: React.FC<IssuesModalProps> = ({
};
const handleFormSubmit = async (formData: Partial<IIssue>) => {
if (workspaceSlug && activeProject) {
const payload: Partial<IIssue> = {
...formData,
target_date: formData.target_date ? renderDateFormat(formData.target_date ?? "") : null,
};
if (!workspaceSlug || !activeProject) return;
if (!data) await createIssue(payload);
else await updateIssue(payload);
}
const payload: Partial<IIssue> = {
...formData,
target_date: formData.target_date ? renderDateFormat(formData.target_date ?? "") : null,
};
if (!data) await createIssue(payload);
else await updateIssue(payload);
};
return (

View File

@ -16,6 +16,7 @@ import useToast from "hooks/use-toast";
import issuesServices from "services/issues.service";
// components
import {
DeleteIssueModal,
SidebarAssigneeSelect,
SidebarBlockedSelect,
SidebarBlockerSelect,
@ -23,8 +24,7 @@ import {
SidebarParentSelect,
SidebarPrioritySelect,
SidebarStateSelect,
} from "components/issues/sidebar-select";
import { DeleteIssueModal } from "components/issues";
} from "components/issues";
// ui
import { Input, Button, Spinner, CustomDatePicker } from "components/ui";
// icons

View File

@ -13,7 +13,7 @@ import issuesService from "services/issues.service";
import useIssuesProperties from "hooks/use-issue-properties";
import useIssueView from "hooks/use-issue-view";
// components
import SingleBoard from "components/project/modules/board-view/single-board";
import SingleBoard from "components/modules/board-view/single-board";
// ui
import { Spinner } from "components/ui";
// types

View File

@ -25,7 +25,7 @@ type Props = {
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 router = useRouter();
@ -152,5 +152,3 @@ const ConfirmModuleDeletion: React.FC<Props> = ({ isOpen, setIsOpen, data }) =>
</Transition.Root>
);
};
export default ConfirmModuleDeletion;

View 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>
);
};

View 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";

View File

@ -38,7 +38,7 @@ type Props = {
userAuth: UserAuth;
};
const ModulesListView: React.FC<Props> = ({
export const ModulesListView: React.FC<Props> = ({
issues,
openCreateIssueModal,
openIssuesListModal,
@ -192,5 +192,3 @@ const ModulesListView: React.FC<Props> = ({
</div>
);
};
export default ModulesListView;

View 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>
);
};

View File

@ -28,7 +28,7 @@ const defaultValues: ModuleLink = {
url: "",
};
const ModuleLinkModal: React.FC<Props> = ({ isOpen, module, handleClose }) => {
export const ModuleLinkModal: React.FC<Props> = ({ isOpen, module, handleClose }) => {
const router = useRouter();
const { workspaceSlug, projectId, moduleId } = router.query;
@ -158,5 +158,3 @@ const ModuleLinkModal: React.FC<Props> = ({ isOpen, module, handleClose }) => {
</Transition.Root>
);
};
export default ModuleLinkModal;

View File

@ -0,0 +1,3 @@
export * from "./select-lead";
export * from "./select-members";
export * from "./select-status";

View File

@ -4,16 +4,16 @@ import { useRouter } from "next/router";
import useSWR from "swr";
// react-hook-form
import { Controller, Control } from "react-hook-form";
import { UserIcon } from "@heroicons/react/24/outline";
// import type { Control } from "react-hook-form";
// service
import type { IModule } from "types";
// services
import projectServices from "services/project.service";
// ui
import SearchListbox from "components/search-listbox";
// icons
import { UserIcon } from "@heroicons/react/24/outline";
// types
import type { IModule } from "types";
// fetch-keys
import { PROJECT_MEMBERS } from "constants/fetch-keys";
@ -21,7 +21,7 @@ type Props = {
control: Control<IModule, any>;
};
const SelectLead: React.FC<Props> = ({ control }) => {
export const ModuleLeadSelect: React.FC<Props> = ({ control }) => {
const router = useRouter();
const { workspaceSlug, projectId } = router.query;
@ -55,5 +55,3 @@ const SelectLead: React.FC<Props> = ({ control }) => {
/>
);
};
export default SelectLead;

View File

@ -4,16 +4,16 @@ import { useRouter } from "next/router";
import useSWR from "swr";
// react-hook-form
import { Controller, Control } from "react-hook-form";
import { UserIcon } from "@heroicons/react/24/outline";
// import type { Control } from "react-hook-form";
// service
import type { IModule } from "types";
// services
import projectServices from "services/project.service";
// ui
import SearchListbox from "components/search-listbox";
// icons
import { UserIcon } from "@heroicons/react/24/outline";
// types
import type { IModule } from "types";
// fetch-keys
import { PROJECT_MEMBERS } from "constants/fetch-keys";
@ -21,7 +21,7 @@ type Props = {
control: Control<IModule, any>;
};
const SelectMembers: React.FC<Props> = ({ control }) => {
export const ModuleMembersSelect: React.FC<Props> = ({ control }) => {
const router = useRouter();
const { workspaceSlug, projectId } = router.query;
@ -56,5 +56,3 @@ const SelectMembers: React.FC<Props> = ({ control }) => {
/>
);
};
export default SelectMembers;

View File

@ -16,7 +16,7 @@ type Props = {
error?: FieldError;
};
const SelectStatus: React.FC<Props> = ({ control, error }) => (
export const ModuleStatusSelect: React.FC<Props> = ({ control, error }) => (
<Controller
control={control}
rules={{ required: true }}
@ -45,4 +45,3 @@ const SelectStatus: React.FC<Props> = ({ control, error }) => (
)}
/>
);
export default SelectStatus;

View File

@ -0,0 +1,3 @@
export * from "./select-lead";
export * from "./select-members";
export * from "./select-status";

View File

@ -25,7 +25,7 @@ type Props = {
lead: IUserLite | null;
};
const SelectLead: React.FC<Props> = ({ control, submitChanges, lead }) => {
export const SidebarLeadSelect: React.FC<Props> = ({ control, submitChanges, lead }) => {
const router = useRouter();
const { workspaceSlug } = router.query;
@ -164,5 +164,3 @@ const SelectLead: React.FC<Props> = ({ control, submitChanges, lead }) => {
</div>
);
};
export default SelectLead;

View File

@ -23,7 +23,7 @@ type Props = {
submitChanges: (formData: Partial<IModule>) => void;
};
const SelectMembers: React.FC<Props> = ({ control, submitChanges }) => {
export const SidebarMembersSelect: React.FC<Props> = ({ control, submitChanges }) => {
const router = useRouter();
const { workspaceSlug } = router.query;
@ -132,5 +132,3 @@ const SelectMembers: React.FC<Props> = ({ control, submitChanges }) => {
</div>
);
};
export default SelectMembers;

View File

@ -18,7 +18,7 @@ type Props = {
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 items-center gap-x-2 text-sm sm:basis-1/2">
<Squares2X2Icon className="h-4 w-4 flex-shrink-0" />
@ -67,5 +67,3 @@ const SelectStatus: React.FC<Props> = ({ control, submitChanges, watch }) => (
</div>
</div>
);
export default SelectStatus;

View File

@ -12,10 +12,12 @@ import modulesService from "services/modules.service";
// hooks
import useToast from "hooks/use-toast";
// components
import SelectLead from "components/project/modules/module-detail-sidebar/select-lead";
import SelectMembers from "components/project/modules/module-detail-sidebar/select-members";
import SelectStatus from "components/project/modules/module-detail-sidebar/select-status";
import ModuleLinkModal from "components/project/modules/module-link-modal";
import {
ModuleLinkModal,
SidebarLeadSelect,
SidebarMembersSelect,
SidebarStatusSelect,
} from "components/modules";
// progress-bar
import { CircularProgressbar } from "react-circular-progressbar";
import "react-circular-progressbar/dist/styles.css";
@ -53,7 +55,7 @@ type Props = {
handleDeleteModule: () => void;
};
const ModuleDetailSidebar: React.FC<Props> = ({
export const ModuleDetailsSidebar: React.FC<Props> = ({
module,
isOpen,
moduleIssues,
@ -161,12 +163,12 @@ const ModuleDetailSidebar: React.FC<Props> = ({
</div>
<div className="divide-y-2 divide-gray-100 text-xs">
<div className="py-1">
<SelectLead
<SidebarLeadSelect
control={control}
submitChanges={submitChanges}
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 items-center gap-x-2 text-sm sm:basis-1/2">
<ChartPieIcon className="h-4 w-4 flex-shrink-0" />
@ -233,7 +235,11 @@ const ModuleDetailSidebar: React.FC<Props> = ({
</div>
</div>
<div className="py-1">
<SelectStatus control={control} submitChanges={submitChanges} watch={watch} />
<SidebarStatusSelect
control={control}
submitChanges={submitChanges}
watch={watch}
/>
</div>
<div className="py-1">
<div className="flex items-center justify-between gap-2">
@ -302,5 +308,3 @@ const ModuleDetailSidebar: React.FC<Props> = ({
</>
);
};
export default ModuleDetailSidebar;

View File

@ -1,10 +1,13 @@
import React, { useState } from "react";
import Link from "next/link";
import Image from "next/image";
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
import { CalendarDaysIcon, TrashIcon } from "@heroicons/react/24/outline";
import User from "public/user.png";
// helpers
import { renderShortNumericDateFormat } from "helpers/date-time.helper";
@ -17,7 +20,7 @@ type Props = {
module: IModule;
};
const SingleModuleCard: React.FC<Props> = ({ module }) => {
export const SingleModuleCard: React.FC<Props> = ({ module }) => {
const router = useRouter();
const { workspaceSlug } = router.query;
const [moduleDeleteModal, setModuleDeleteModal] = useState(false);
@ -40,7 +43,7 @@ const SingleModuleCard: React.FC<Props> = ({ module }) => {
<TrashIcon className="h-4 w-4" />
</button>
</div>
<ConfirmModuleDeletion
<DeleteModuleModal
isOpen={
moduleDeleteModal &&
!!selectedModuleForDelete &&
@ -147,5 +150,3 @@ const SingleModuleCard: React.FC<Props> = ({ module }) => {
</div>
);
};
export default SingleModuleCard;

View File

@ -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;

View File

@ -16,11 +16,9 @@ import AppLayout from "layouts/app-layout";
import { IssueViewContextProvider } from "contexts/issue-view.context";
// components
import ExistingIssuesListModal from "components/common/existing-issues-list-modal";
import ModulesBoardView from "components/project/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 ModulesBoardView from "components/modules/board-view";
import { CreateUpdateIssueModal, DeleteIssueModal } from "components/issues";
import { DeleteModuleModal, ModuleDetailsSidebar, ModulesListView } from "components/modules";
import View from "components/core/view";
// ui
import { CustomMenu, EmptySpace, EmptySpaceItem, Spinner } from "components/ui";
@ -212,7 +210,7 @@ const SingleModule: React.FC<UserAuth> = (props) => {
isOpen={!!deleteIssue}
data={moduleIssuesArray?.find((issue) => issue.id === deleteIssue)}
/>
<ConfirmModuleDeletion
<DeleteModuleModal
isOpen={
moduleDeleteModal &&
!!selectedModuleForDelete &&
@ -321,7 +319,7 @@ const SingleModule: React.FC<UserAuth> = (props) => {
<Spinner />
</div>
)}
<ModuleDetailSidebar
<ModuleDetailsSidebar
module={moduleDetails}
isOpen={moduleSidebar}
moduleIssues={moduleIssues}

View File

@ -12,7 +12,7 @@ import { requiredAuth } from "lib/auth";
import projectService from "services/project.service";
import modulesService from "services/modules.service";
// components
import SingleModuleCard from "components/project/modules/single-module-card";
import { SingleModuleCard } from "components/modules";
// ui
import { EmptySpace, EmptySpaceItem, HeaderButton, Loader } from "components/ui";
import { BreadcrumbItem, Breadcrumbs } from "components/breadcrumbs";