diff --git a/apps/app/components/command-palette/command-pallette.tsx b/apps/app/components/command-palette/command-pallette.tsx index 0ec31ca84..4110d0a27 100644 --- a/apps/app/components/command-palette/command-pallette.tsx +++ b/apps/app/components/command-palette/command-pallette.tsx @@ -17,8 +17,8 @@ import { ShortcutsModal } from "components/command-palette"; import { BulkDeleteIssuesModal } from "components/core"; import { CreateProjectModal } from "components/project"; import { CreateUpdateIssueModal } from "components/issues"; +import { CreateUpdateCycleModal } from "components/cycles"; import { CreateUpdateModuleModal } from "components/modules"; -import CreateUpdateCycleModal from "components/project/cycles/create-update-cycle-modal"; // ui import { Button } from "components/ui"; // icons @@ -169,8 +169,7 @@ export const CommandPalette: React.FC = () => { <> setIsCreateCycleModalOpen(false)} /> = ({ +export const CyclesListView: React.FC = ({ cycles, setCreateUpdateCycleModal, setSelectedCycle, @@ -35,7 +34,7 @@ const CycleStatsView: React.FC = ({ return ( <> - = ({ /> {cycles.length > 0 ? ( cycles.map((cycle) => ( - handleDeleteCycle(cycle)} @@ -71,5 +70,3 @@ const CycleStatsView: React.FC = ({ ); }; - -export default CycleStatsView; diff --git a/apps/app/components/project/cycles/confirm-cycle-deletion.tsx b/apps/app/components/cycles/delete-cycle-modal.tsx similarity index 97% rename from apps/app/components/project/cycles/confirm-cycle-deletion.tsx rename to apps/app/components/cycles/delete-cycle-modal.tsx index c1a5f38ed..a962533a4 100644 --- a/apps/app/components/project/cycles/confirm-cycle-deletion.tsx +++ b/apps/app/components/cycles/delete-cycle-modal.tsx @@ -23,7 +23,7 @@ type TConfirmCycleDeletionProps = { // fetch-keys import { CYCLE_LIST } from "constants/fetch-keys"; -const ConfirmCycleDeletion: React.FC = ({ +export const DeleteCycleModal: React.FC = ({ isOpen, setIsOpen, data, @@ -149,5 +149,3 @@ const ConfirmCycleDeletion: React.FC = ({ ); }; - -export default ConfirmCycleDeletion; diff --git a/apps/app/components/cycles/form.tsx b/apps/app/components/cycles/form.tsx index 262db1abc..58f57ba14 100644 --- a/apps/app/components/cycles/form.tsx +++ b/apps/app/components/cycles/form.tsx @@ -1,39 +1,59 @@ -import { FC } from "react"; +import { useEffect } from "react"; + +// react-hook-form import { Controller, useForm } from "react-hook-form"; -// components -import { Button, Input, TextArea, CustomSelect } from "components/ui"; +// ui +import { Button, CustomDatePicker, CustomSelect, Input, TextArea } from "components/ui"; // types -import type { ICycle } from "types"; +import { ICycle } from "types"; + +type Props = { + handleFormSubmit: (values: Partial) => Promise; + handleClose: () => void; + status: boolean; + data?: ICycle; +}; const defaultValues: Partial = { name: "", description: "", status: "draft", - start_date: new Date().toString(), - end_date: new Date().toString(), + start_date: "", + end_date: "", }; -export interface CycleFormProps { - handleFormSubmit: (values: Partial) => void; - handleFormCancel?: () => void; - initialData?: Partial; -} - -export const CycleForm: FC = (props) => { - const { handleFormSubmit, handleFormCancel = () => {}, initialData = null } = props; - // form handler +export const CycleForm: React.FC = ({ handleFormSubmit, handleClose, status, data }) => { const { register, formState: { errors, isSubmitting }, handleSubmit, control, + reset, } = useForm({ - defaultValues: initialData || defaultValues, + defaultValues, }); + const handleCreateUpdateCycle = async (formData: Partial) => { + await handleFormSubmit(formData); + + reset({ + ...defaultValues, + }); + }; + + useEffect(() => { + reset({ + ...defaultValues, + ...data, + }); + }, [data, reset]); + return ( -
+
+

+ {status ? "Update" : "Create"} Cycle +

= (props) => { register={register} validations={{ required: "Name is required", + maxLength: { + value: 255, + message: "Name should be less than 255 characters", + }, }} />
@@ -86,42 +110,56 @@ export const CycleForm: FC = (props) => {
- +
Start Date
+
+ ( + + )} + /> + {errors.start_date && ( +
{errors.start_date.message}
+ )} +
- +
End Date
+
+ ( + + )} + /> + {errors.end_date && ( +
{errors.end_date.message}
+ )} +
-
diff --git a/apps/app/components/cycles/select.tsx b/apps/app/components/cycles/select.tsx index 26de56bb8..c48219276 100644 --- a/apps/app/components/cycles/select.tsx +++ b/apps/app/components/cycles/select.tsx @@ -12,7 +12,7 @@ import { CyclesIcon } from "components/icons"; // services import cycleServices from "services/cycles.service"; // components -import { CycleModal } from "components/cycles"; +import { CreateUpdateCycleModal } from "components/cycles"; // fetch-keys import { CYCLE_LIST } from "constants/fetch-keys"; @@ -54,12 +54,7 @@ export const CycleSelect: React.FC = ({ return ( <> - + {({ open }) => ( <> diff --git a/apps/app/components/project/cycles/cycle-detail-sidebar/index.tsx b/apps/app/components/cycles/sidebar.tsx similarity index 97% rename from apps/app/components/project/cycles/cycle-detail-sidebar/index.tsx rename to apps/app/components/cycles/sidebar.tsx index f552bef88..a4f794f64 100644 --- a/apps/app/components/project/cycles/cycle-detail-sidebar/index.tsx +++ b/apps/app/components/cycles/sidebar.tsx @@ -30,7 +30,7 @@ import cyclesService from "services/cycles.service"; // components import { SidebarProgressStats } from "components/core"; import ProgressChart from "components/core/sidebar/progress-chart"; -import ConfirmCycleDeletion from "components/project/cycles/confirm-cycle-deletion"; +import { DeleteCycleModal } from "components/cycles"; // helpers import { copyTextToClipboard } from "helpers/string.helper"; import { groupBy } from "helpers/array.helper"; @@ -49,7 +49,7 @@ type Props = { cycleIssues: CycleIssueResponse[]; }; -const CycleDetailSidebar: React.FC = ({ issues, cycle, isOpen, cycleIssues }) => { +export const CycleDetailsSidebar: React.FC = ({ issues, cycle, isOpen, cycleIssues }) => { const [cycleDeleteModal, setCycleDeleteModal] = useState(false); const router = useRouter(); @@ -111,11 +111,7 @@ const CycleDetailSidebar: React.FC = ({ issues, cycle, isOpen, cycleIssue return ( <> - +
= ({ issues, cycle, isOpen, cycleIssue ); }; - -export default CycleDetailSidebar; diff --git a/apps/app/components/project/cycles/stats-view/single-stat.tsx b/apps/app/components/cycles/single-cycle-card.tsx similarity index 98% rename from apps/app/components/project/cycles/stats-view/single-stat.tsx rename to apps/app/components/cycles/single-cycle-card.tsx index 7673c9615..86589995f 100644 --- a/apps/app/components/project/cycles/stats-view/single-stat.tsx +++ b/apps/app/components/cycles/single-cycle-card.tsx @@ -41,7 +41,7 @@ const stateGroupColours: { completed: "#096e8d", }; -const SingleStat: React.FC = (props) => { +export const SingleCycleCard: React.FC = (props) => { const { cycle, handleEditCycle, handleDeleteCycle } = props; const router = useRouter(); @@ -184,5 +184,3 @@ const SingleStat: React.FC = (props) => { ); }; - -export default SingleStat; diff --git a/apps/app/components/issues/description-form.tsx b/apps/app/components/issues/description-form.tsx index 9b6a50cc6..2caf5b635 100644 --- a/apps/app/components/issues/description-form.tsx +++ b/apps/app/components/issues/description-form.tsx @@ -45,7 +45,6 @@ export const IssueDescriptionForm: FC = ({ setValue, reset, formState: { errors }, - setError, } = useForm({ defaultValues: { name: "", @@ -76,8 +75,8 @@ export const IssueDescriptionForm: FC = ({ handleFormSubmit({ name: formData.name ?? "", - description: formData.description, - description_html: formData.description_html, + description: formData.description ?? "", + description_html: formData.description_html ?? "

", }); }, [handleFormSubmit, setToastAlert] diff --git a/apps/app/components/issues/form.tsx b/apps/app/components/issues/form.tsx index 6b9841a73..416fa8b19 100644 --- a/apps/app/components/issues/form.tsx +++ b/apps/app/components/issues/form.tsx @@ -17,7 +17,7 @@ import { } from "components/issues/select"; import { CycleSelect as IssueCycleSelect } from "components/cycles/select"; import { CreateStateModal } from "components/states"; -import CreateUpdateCycleModal from "components/project/cycles/create-update-cycle-modal"; +import { CreateUpdateCycleModal } from "components/cycles"; import { CreateLabelModal } from "components/labels"; // ui import { Button, CustomDatePicker, CustomMenu, Input, Loader } from "components/ui"; @@ -131,11 +131,7 @@ export const IssueForm: FC = ({ handleClose={() => setStateModal(false)} projectId={projectId} /> - + setCycleModal(false)} /> setLabelModal(false)} diff --git a/apps/app/components/modules/form.tsx b/apps/app/components/modules/form.tsx index 60fd93059..35a141451 100644 --- a/apps/app/components/modules/form.tsx +++ b/apps/app/components/modules/form.tsx @@ -1,3 +1,5 @@ +import { useEffect } from "react"; + // react-hook-form import { Controller, useForm } from "react-hook-form"; // components @@ -8,9 +10,10 @@ import { Button, CustomDatePicker, Input, TextArea } from "components/ui"; import { IModule } from "types"; type Props = { - handleFormSubmit: (values: Partial) => void; + handleFormSubmit: (values: Partial) => Promise; handleClose: () => void; status: boolean; + data?: IModule; }; const defaultValues: Partial = { @@ -21,7 +24,7 @@ const defaultValues: Partial = { members_list: [], }; -export const ModuleForm: React.FC = ({ handleFormSubmit, handleClose, status }) => { +export const ModuleForm: React.FC = ({ handleFormSubmit, handleClose, status, data }) => { const { register, formState: { errors, isSubmitting }, @@ -40,6 +43,13 @@ export const ModuleForm: React.FC = ({ handleFormSubmit, handleClose, sta }); }; + useEffect(() => { + reset({ + ...defaultValues, + ...data, + }); + }, [data, reset]); + return (
diff --git a/apps/app/components/modules/modal.tsx b/apps/app/components/modules/modal.tsx index 22f9a7e49..7acf81339 100644 --- a/apps/app/components/modules/modal.tsx +++ b/apps/app/components/modules/modal.tsx @@ -14,8 +14,6 @@ import { ModuleForm } from "components/modules"; 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 @@ -46,7 +44,7 @@ export const CreateUpdateModuleModal: React.FC = ({ isOpen, setIsOpen, da reset(defaultValues); }; - const { reset, setError } = useForm({ + const { reset } = useForm({ defaultValues, }); @@ -58,16 +56,16 @@ export const CreateUpdateModuleModal: React.FC = ({ isOpen, setIsOpen, da handleClose(); setToastAlert({ - title: "Success", type: "success", - message: "Module created successfully", + title: "Success!", + message: "Module created successfully.", }); }) - .catch((err) => { - Object.keys(err).map((key) => { - setError(key as keyof typeof defaultValues, { - message: err[key].join(", "), - }); + .catch(() => { + setToastAlert({ + type: "error", + title: "Error!", + message: "Module could not be created. Please try again.", }); }); }; @@ -92,16 +90,16 @@ export const CreateUpdateModuleModal: React.FC = ({ isOpen, setIsOpen, da handleClose(); setToastAlert({ - title: "Success", type: "success", - message: "Module updated successfully", + title: "Success!", + message: "Module updated successfully.", }); }) - .catch((err) => { - Object.keys(err).map((key) => { - setError(key as keyof typeof defaultValues, { - message: err[key].join(", "), - }); + .catch(() => { + setToastAlert({ + type: "error", + title: "Error!", + message: "Module could not be updated. Please try again.", }); }); }; @@ -117,15 +115,6 @@ export const CreateUpdateModuleModal: React.FC = ({ isOpen, setIsOpen, da else await updateModule(payload); }; - useEffect(() => { - if (data) { - setIsOpen(true); - reset(data); - } else { - reset(defaultValues); - } - }, [data, setIsOpen, reset]); - return ( @@ -157,6 +146,7 @@ export const CreateUpdateModuleModal: React.FC = ({ isOpen, setIsOpen, da handleFormSubmit={handleFormSubmit} handleClose={handleClose} status={data ? true : false} + data={data} /> diff --git a/apps/app/components/project/cycles/create-update-cycle-modal.tsx b/apps/app/components/project/cycles/create-update-cycle-modal.tsx deleted file mode 100644 index 30348e587..000000000 --- a/apps/app/components/project/cycles/create-update-cycle-modal.tsx +++ /dev/null @@ -1,278 +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"; -// services -import cycleService from "services/cycles.service"; -// hooks -import useToast from "hooks/use-toast"; -// ui -import { Button, Input, TextArea, CustomSelect, CustomDatePicker } from "components/ui"; -// common -import { renderDateFormat } from "helpers/date-time.helper"; -// types -import type { ICycle } from "types"; -// fetch keys -import { CYCLE_LIST } from "constants/fetch-keys"; - -type Props = { - isOpen: boolean; - setIsOpen: React.Dispatch>; - projectId: string; - data?: ICycle; -}; - -const defaultValues: Partial = { - name: "", - description: "", - status: "draft", - start_date: null, - end_date: null, -}; - -const CreateUpdateCycleModal: React.FC = ({ isOpen, setIsOpen, data, projectId }) => { - const router = useRouter(); - const { workspaceSlug } = router.query; - - const { setToastAlert } = useToast(); - - const { - register, - formState: { errors, isSubmitting }, - handleSubmit, - control, - reset, - setError, - } = useForm({ - defaultValues, - }); - - useEffect(() => { - if (data) { - setIsOpen(true); - reset(data); - } else { - reset(defaultValues); - } - }, [data, setIsOpen, reset]); - - const onSubmit = async (formData: ICycle) => { - if (!workspaceSlug) return; - const payload = { - ...formData, - start_date: formData.start_date ? renderDateFormat(formData.start_date) : null, - end_date: formData.end_date ? renderDateFormat(formData.end_date) : null, - }; - if (!data) { - await cycleService - .createCycle(workspaceSlug as string, projectId, payload) - .then((res) => { - mutate(CYCLE_LIST(projectId), (prevData) => [res, ...(prevData ?? [])], false); - - handleClose(); - setToastAlert({ - title: "Success", - type: "success", - message: "Cycle created successfully", - }); - }) - .catch((err) => { - Object.keys(err).map((key) => { - setError(key as keyof typeof defaultValues, { - message: err[key].join(", "), - }); - }); - }); - } else { - await cycleService - .updateCycle(workspaceSlug as string, projectId, data.id, payload) - .then((res) => { - mutate(CYCLE_LIST(projectId)); - handleClose(); - - setToastAlert({ - title: "Success", - type: "success", - message: "Cycle 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); - }; - - return ( - - - -
- - -
-
- - - -
- - {data ? "Update" : "Create"} Cycle - -
-
- -
-
-