mirror of
https://github.com/makeplane/plane
synced 2024-06-14 14:31:34 +00:00
fix : module and cycle invalid date fix (#605)
* fix: module and cycle modal invalid date validation * fix: cycle and module sidebar invalid date
This commit is contained in:
parent
a94e38c093
commit
f5f90dab69
@ -11,7 +11,7 @@ import useToast from "hooks/use-toast";
|
|||||||
// ui
|
// ui
|
||||||
import { CustomDatePicker, Input, PrimaryButton, SecondaryButton, TextArea } from "components/ui";
|
import { CustomDatePicker, Input, PrimaryButton, SecondaryButton, TextArea } from "components/ui";
|
||||||
// helpers
|
// helpers
|
||||||
import { getDateRangeStatus } from "helpers/date-time.helper";
|
import { getDateRangeStatus, isDateRangeValid } from "helpers/date-time.helper";
|
||||||
// types
|
// types
|
||||||
import { ICycle } from "types";
|
import { ICycle } from "types";
|
||||||
|
|
||||||
@ -141,12 +141,22 @@ export const CycleForm: React.FC<Props> = ({ handleFormSubmit, handleClose, stat
|
|||||||
value={value}
|
value={value}
|
||||||
onChange={(val) => {
|
onChange={(val) => {
|
||||||
onChange(val);
|
onChange(val);
|
||||||
val && watch("end_date") && cycleStatus != "current"
|
if (val && watch("end_date")) {
|
||||||
? dateChecker({
|
if (isDateRangeValid(val, `${watch("end_date")}`)) {
|
||||||
start_date: val,
|
cycleStatus != "current" &&
|
||||||
end_date: watch("end_date"),
|
dateChecker({
|
||||||
})
|
start_date: val,
|
||||||
: "";
|
end_date: watch("end_date"),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
setIsDateValid(false);
|
||||||
|
setToastAlert({
|
||||||
|
type: "error",
|
||||||
|
title: "Error!",
|
||||||
|
message: "You have enter invalid date.",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
error={errors.start_date ? true : false}
|
error={errors.start_date ? true : false}
|
||||||
/>
|
/>
|
||||||
@ -169,12 +179,22 @@ export const CycleForm: React.FC<Props> = ({ handleFormSubmit, handleClose, stat
|
|||||||
value={value}
|
value={value}
|
||||||
onChange={(val) => {
|
onChange={(val) => {
|
||||||
onChange(val);
|
onChange(val);
|
||||||
val && watch("start_date") && cycleStatus != "current"
|
if (watch("start_date") && val) {
|
||||||
? dateChecker({
|
if (isDateRangeValid(`${watch("start_date")}`, val)) {
|
||||||
start_date: watch("start_date"),
|
cycleStatus != "current" &&
|
||||||
end_date: val,
|
dateChecker({
|
||||||
})
|
start_date: watch("start_date"),
|
||||||
: "";
|
end_date: val,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
setIsDateValid(false);
|
||||||
|
setToastAlert({
|
||||||
|
type: "error",
|
||||||
|
title: "Error!",
|
||||||
|
message: "You have enter invalid date.",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
error={errors.end_date ? true : false}
|
error={errors.end_date ? true : false}
|
||||||
/>
|
/>
|
||||||
|
@ -35,7 +35,7 @@ import { ExclamationIcon } from "components/icons";
|
|||||||
// helpers
|
// helpers
|
||||||
import { capitalizeFirstLetter, copyTextToClipboard } from "helpers/string.helper";
|
import { capitalizeFirstLetter, copyTextToClipboard } from "helpers/string.helper";
|
||||||
import { groupBy } from "helpers/array.helper";
|
import { groupBy } from "helpers/array.helper";
|
||||||
import { renderDateFormat, renderShortDate } from "helpers/date-time.helper";
|
import { isDateRangeValid, renderDateFormat, renderShortDate } from "helpers/date-time.helper";
|
||||||
// types
|
// types
|
||||||
import { ICycle, IIssue } from "types";
|
import { ICycle, IIssue } from "types";
|
||||||
// fetch-keys
|
// fetch-keys
|
||||||
@ -55,8 +55,6 @@ export const CycleDetailsSidebar: React.FC<Props> = ({
|
|||||||
isCompleted,
|
isCompleted,
|
||||||
}) => {
|
}) => {
|
||||||
const [cycleDeleteModal, setCycleDeleteModal] = useState(false);
|
const [cycleDeleteModal, setCycleDeleteModal] = useState(false);
|
||||||
const [startDateRange, setStartDateRange] = useState<Date | null>(new Date());
|
|
||||||
const [endDateRange, setEndDateRange] = useState<Date | null>(null);
|
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { workspaceSlug, projectId, cycleId } = router.query;
|
const { workspaceSlug, projectId, cycleId } = router.query;
|
||||||
@ -89,7 +87,7 @@ export const CycleDetailsSidebar: React.FC<Props> = ({
|
|||||||
...groupBy(issues ?? [], "state_detail.group"),
|
...groupBy(issues ?? [], "state_detail.group"),
|
||||||
};
|
};
|
||||||
|
|
||||||
const { reset } = useForm({
|
const { reset, watch } = useForm({
|
||||||
defaultValues,
|
defaultValues,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -190,17 +188,32 @@ export const CycleDetailsSidebar: React.FC<Props> = ({
|
|||||||
>
|
>
|
||||||
<Popover.Panel className="absolute top-10 -right-5 z-20 transform overflow-hidden">
|
<Popover.Panel className="absolute top-10 -right-5 z-20 transform overflow-hidden">
|
||||||
<DatePicker
|
<DatePicker
|
||||||
selected={startDateRange}
|
selected={
|
||||||
|
watch("start_date")
|
||||||
|
? new Date(`${watch("start_date")}`)
|
||||||
|
: new Date()
|
||||||
|
}
|
||||||
onChange={(date) => {
|
onChange={(date) => {
|
||||||
submitChanges({
|
if (date && watch("end_date")) {
|
||||||
start_date: renderDateFormat(date),
|
if (
|
||||||
});
|
isDateRangeValid(renderDateFormat(date), `${watch("end_date")}`)
|
||||||
setStartDateRange(date);
|
) {
|
||||||
|
submitChanges({
|
||||||
|
start_date: renderDateFormat(date),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
setToastAlert({
|
||||||
|
type: "error",
|
||||||
|
title: "Error!",
|
||||||
|
message: "You have enter invalid date.",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
selectsStart
|
selectsStart
|
||||||
startDate={startDateRange}
|
startDate={new Date(`${watch("start_date")}`)}
|
||||||
endDate={endDateRange}
|
endDate={new Date(`${watch("end_date")}`)}
|
||||||
maxDate={endDateRange}
|
maxDate={new Date(`${watch("end_date")}`)}
|
||||||
shouldCloseOnSelect
|
shouldCloseOnSelect
|
||||||
inline
|
inline
|
||||||
/>
|
/>
|
||||||
@ -237,18 +250,34 @@ export const CycleDetailsSidebar: React.FC<Props> = ({
|
|||||||
>
|
>
|
||||||
<Popover.Panel className="absolute top-10 -right-5 z-20 transform overflow-hidden">
|
<Popover.Panel className="absolute top-10 -right-5 z-20 transform overflow-hidden">
|
||||||
<DatePicker
|
<DatePicker
|
||||||
selected={endDateRange}
|
selected={
|
||||||
|
watch("end_date") ? new Date(`${watch("end_date")}`) : new Date()
|
||||||
|
}
|
||||||
onChange={(date) => {
|
onChange={(date) => {
|
||||||
submitChanges({
|
if (watch("start_date") && date) {
|
||||||
end_date: renderDateFormat(date),
|
if (
|
||||||
});
|
isDateRangeValid(
|
||||||
setEndDateRange(date);
|
`${watch("start_date")}`,
|
||||||
|
renderDateFormat(date)
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
submitChanges({
|
||||||
|
end_date: renderDateFormat(date),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
setToastAlert({
|
||||||
|
type: "error",
|
||||||
|
title: "Error!",
|
||||||
|
message: "You have enter invalid date.",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
selectsEnd
|
selectsEnd
|
||||||
startDate={startDateRange}
|
startDate={new Date(`${watch("start_date")}`)}
|
||||||
endDate={endDateRange}
|
endDate={new Date(`${watch("end_date")}`)}
|
||||||
// minDate={startDateRange}
|
minDate={new Date(`${watch("start_date")}`)}
|
||||||
|
shouldCloseOnSelect
|
||||||
inline
|
inline
|
||||||
/>
|
/>
|
||||||
</Popover.Panel>
|
</Popover.Panel>
|
||||||
|
@ -1,11 +1,15 @@
|
|||||||
import { useEffect } from "react";
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
// react-hook-form
|
// react-hook-form
|
||||||
import { Controller, useForm } from "react-hook-form";
|
import { Controller, useForm } from "react-hook-form";
|
||||||
|
// hooks
|
||||||
|
import useToast from "hooks/use-toast";
|
||||||
// components
|
// components
|
||||||
import { ModuleLeadSelect, ModuleMembersSelect, ModuleStatusSelect } from "components/modules";
|
import { ModuleLeadSelect, ModuleMembersSelect, ModuleStatusSelect } from "components/modules";
|
||||||
// ui
|
// ui
|
||||||
import { CustomDatePicker, Input, PrimaryButton, SecondaryButton, TextArea } from "components/ui";
|
import { CustomDatePicker, Input, PrimaryButton, SecondaryButton, TextArea } from "components/ui";
|
||||||
|
// helper
|
||||||
|
import { isDateRangeValid } from "helpers/date-time.helper";
|
||||||
// types
|
// types
|
||||||
import { IModule } from "types";
|
import { IModule } from "types";
|
||||||
|
|
||||||
@ -25,10 +29,13 @@ const defaultValues: Partial<IModule> = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const ModuleForm: React.FC<Props> = ({ handleFormSubmit, handleClose, status, data }) => {
|
export const ModuleForm: React.FC<Props> = ({ handleFormSubmit, handleClose, status, data }) => {
|
||||||
|
const [isDateValid, setIsDateValid] = useState(true);
|
||||||
|
const { setToastAlert } = useToast();
|
||||||
const {
|
const {
|
||||||
register,
|
register,
|
||||||
formState: { errors, isSubmitting },
|
formState: { errors, isSubmitting },
|
||||||
handleSubmit,
|
handleSubmit,
|
||||||
|
watch,
|
||||||
control,
|
control,
|
||||||
reset,
|
reset,
|
||||||
} = useForm<IModule>({
|
} = useForm<IModule>({
|
||||||
@ -94,7 +101,25 @@ export const ModuleForm: React.FC<Props> = ({ handleFormSubmit, handleClose, sta
|
|||||||
control={control}
|
control={control}
|
||||||
name="start_date"
|
name="start_date"
|
||||||
render={({ field: { value, onChange } }) => (
|
render={({ field: { value, onChange } }) => (
|
||||||
<CustomDatePicker renderAs="input" value={value} onChange={onChange} />
|
<CustomDatePicker
|
||||||
|
renderAs="input"
|
||||||
|
value={value}
|
||||||
|
onChange={(val) => {
|
||||||
|
onChange(val);
|
||||||
|
if (val && watch("target_date")) {
|
||||||
|
if (isDateRangeValid(val, `${watch("target_date")}`)) {
|
||||||
|
setIsDateValid(true);
|
||||||
|
} else {
|
||||||
|
setIsDateValid(false);
|
||||||
|
setToastAlert({
|
||||||
|
type: "error",
|
||||||
|
title: "Error!",
|
||||||
|
message: "You have enter invalid date.",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -106,7 +131,25 @@ export const ModuleForm: React.FC<Props> = ({ handleFormSubmit, handleClose, sta
|
|||||||
control={control}
|
control={control}
|
||||||
name="target_date"
|
name="target_date"
|
||||||
render={({ field: { value, onChange } }) => (
|
render={({ field: { value, onChange } }) => (
|
||||||
<CustomDatePicker renderAs="input" value={value} onChange={onChange} />
|
<CustomDatePicker
|
||||||
|
renderAs="input"
|
||||||
|
value={value}
|
||||||
|
onChange={(val) => {
|
||||||
|
onChange(val);
|
||||||
|
if (watch("start_date") && val) {
|
||||||
|
if (isDateRangeValid(`${watch("start_date")}`, val)) {
|
||||||
|
setIsDateValid(true);
|
||||||
|
} else {
|
||||||
|
setIsDateValid(false);
|
||||||
|
setToastAlert({
|
||||||
|
type: "error",
|
||||||
|
title: "Error!",
|
||||||
|
message: "You have enter invalid date.",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -133,7 +176,7 @@ export const ModuleForm: React.FC<Props> = ({ handleFormSubmit, handleClose, sta
|
|||||||
</div>
|
</div>
|
||||||
<div className="mt-5 flex justify-end gap-2">
|
<div className="mt-5 flex justify-end gap-2">
|
||||||
<SecondaryButton onClick={handleClose}>Cancel</SecondaryButton>
|
<SecondaryButton onClick={handleClose}>Cancel</SecondaryButton>
|
||||||
<PrimaryButton type="submit" loading={isSubmitting}>
|
<PrimaryButton type="submit" loading={isSubmitting || isDateValid ? false : true}>
|
||||||
{status
|
{status
|
||||||
? isSubmitting
|
? isSubmitting
|
||||||
? "Updating Module..."
|
? "Updating Module..."
|
||||||
|
@ -32,7 +32,7 @@ import { CustomMenu, CustomSelect, Loader, ProgressBar } from "components/ui";
|
|||||||
// icon
|
// icon
|
||||||
import { ExclamationIcon } from "components/icons";
|
import { ExclamationIcon } from "components/icons";
|
||||||
// helpers
|
// helpers
|
||||||
import { renderDateFormat, renderShortDate } from "helpers/date-time.helper";
|
import { isDateRangeValid, renderDateFormat, renderShortDate } from "helpers/date-time.helper";
|
||||||
import { capitalizeFirstLetter, copyTextToClipboard } from "helpers/string.helper";
|
import { capitalizeFirstLetter, copyTextToClipboard } from "helpers/string.helper";
|
||||||
import { groupBy } from "helpers/array.helper";
|
import { groupBy } from "helpers/array.helper";
|
||||||
// types
|
// types
|
||||||
@ -67,8 +67,6 @@ export const ModuleDetailsSidebar: React.FC<Props> = ({
|
|||||||
}) => {
|
}) => {
|
||||||
const [moduleDeleteModal, setModuleDeleteModal] = useState(false);
|
const [moduleDeleteModal, setModuleDeleteModal] = useState(false);
|
||||||
const [moduleLinkModal, setModuleLinkModal] = useState(false);
|
const [moduleLinkModal, setModuleLinkModal] = useState(false);
|
||||||
const [startDateRange, setStartDateRange] = useState<Date | null>(new Date());
|
|
||||||
const [endDateRange, setEndDateRange] = useState<Date | null>(null);
|
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { workspaceSlug, projectId, moduleId } = router.query;
|
const { workspaceSlug, projectId, moduleId } = router.query;
|
||||||
@ -257,17 +255,20 @@ export const ModuleDetailsSidebar: React.FC<Props> = ({
|
|||||||
>
|
>
|
||||||
<Popover.Panel className="absolute top-10 -right-5 z-20 transform overflow-hidden">
|
<Popover.Panel className="absolute top-10 -right-5 z-20 transform overflow-hidden">
|
||||||
<DatePicker
|
<DatePicker
|
||||||
selected={startDateRange}
|
selected={
|
||||||
|
watch("start_date")
|
||||||
|
? new Date(`${watch("start_date")}`)
|
||||||
|
: new Date()
|
||||||
|
}
|
||||||
onChange={(date) => {
|
onChange={(date) => {
|
||||||
submitChanges({
|
submitChanges({
|
||||||
start_date: renderDateFormat(date),
|
start_date: renderDateFormat(date),
|
||||||
});
|
});
|
||||||
setStartDateRange(date);
|
|
||||||
}}
|
}}
|
||||||
selectsStart
|
selectsStart
|
||||||
startDate={startDateRange}
|
startDate={new Date(`${watch("start_date")}`)}
|
||||||
endDate={endDateRange}
|
endDate={new Date(`${watch("target_date")}`)}
|
||||||
maxDate={endDateRange}
|
maxDate={new Date(`${watch("target_date")}`)}
|
||||||
shouldCloseOnSelect
|
shouldCloseOnSelect
|
||||||
inline
|
inline
|
||||||
/>
|
/>
|
||||||
@ -303,18 +304,19 @@ export const ModuleDetailsSidebar: React.FC<Props> = ({
|
|||||||
>
|
>
|
||||||
<Popover.Panel className="absolute top-10 -right-5 z-20 transform overflow-hidden">
|
<Popover.Panel className="absolute top-10 -right-5 z-20 transform overflow-hidden">
|
||||||
<DatePicker
|
<DatePicker
|
||||||
selected={endDateRange}
|
selected={
|
||||||
|
watch("target_date") ? new Date(`${watch("target_date")}`) : new Date()
|
||||||
|
}
|
||||||
onChange={(date) => {
|
onChange={(date) => {
|
||||||
submitChanges({
|
submitChanges({
|
||||||
target_date: renderDateFormat(date),
|
target_date: renderDateFormat(date),
|
||||||
});
|
});
|
||||||
setEndDateRange(date);
|
|
||||||
}}
|
}}
|
||||||
selectsEnd
|
selectsEnd
|
||||||
startDate={startDateRange}
|
startDate={new Date(`${watch("start_date")}`)}
|
||||||
endDate={endDateRange}
|
endDate={new Date(`${watch("target_date")}`)}
|
||||||
// minDate={startDateRange}
|
minDate={new Date(`${watch("start_date")}`)}
|
||||||
|
shouldCloseOnSelect
|
||||||
inline
|
inline
|
||||||
/>
|
/>
|
||||||
</Popover.Panel>
|
</Popover.Panel>
|
||||||
|
@ -169,3 +169,5 @@ export const renderShortTime = (date: string | Date) => {
|
|||||||
|
|
||||||
return hours + ":" + minutes;
|
return hours + ":" + minutes;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const isDateRangeValid = (startDate: string, endDate: string)=> new Date(startDate) < new Date(endDate);
|
Loading…
Reference in New Issue
Block a user