forked from github/plane
fix: create cycle modal (#1631)
* fix: cycle date select in the modal * chore: update remove assignee issue activity
This commit is contained in:
parent
464c13fcd0
commit
fe60771943
@ -29,16 +29,13 @@ export const CycleForm: React.FC<Props> = ({ handleFormSubmit, handleClose, stat
|
|||||||
handleSubmit,
|
handleSubmit,
|
||||||
control,
|
control,
|
||||||
reset,
|
reset,
|
||||||
|
watch,
|
||||||
} = useForm<ICycle>({
|
} = useForm<ICycle>({
|
||||||
defaultValues,
|
defaultValues,
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleCreateUpdateCycle = async (formData: Partial<ICycle>) => {
|
const handleCreateUpdateCycle = async (formData: Partial<ICycle>) => {
|
||||||
await handleFormSubmit(formData);
|
await handleFormSubmit(formData);
|
||||||
|
|
||||||
reset({
|
|
||||||
...defaultValues,
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -48,6 +45,15 @@ export const CycleForm: React.FC<Props> = ({ handleFormSubmit, handleClose, stat
|
|||||||
});
|
});
|
||||||
}, [data, reset]);
|
}, [data, reset]);
|
||||||
|
|
||||||
|
const startDate = watch("start_date");
|
||||||
|
const endDate = watch("end_date");
|
||||||
|
|
||||||
|
const minDate = startDate ? new Date(startDate) : new Date();
|
||||||
|
minDate.setDate(minDate.getDate() + 1);
|
||||||
|
|
||||||
|
const maxDate = endDate ? new Date(endDate) : null;
|
||||||
|
maxDate?.setDate(maxDate.getDate() - 1);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form onSubmit={handleSubmit(handleCreateUpdateCycle)}>
|
<form onSubmit={handleSubmit(handleCreateUpdateCycle)}>
|
||||||
<div className="space-y-5">
|
<div className="space-y-5">
|
||||||
@ -91,7 +97,13 @@ export const CycleForm: React.FC<Props> = ({ handleFormSubmit, handleClose, stat
|
|||||||
control={control}
|
control={control}
|
||||||
name="start_date"
|
name="start_date"
|
||||||
render={({ field: { value, onChange } }) => (
|
render={({ field: { value, onChange } }) => (
|
||||||
<DateSelect label="Start date" value={value} onChange={(val) => onChange(val)} />
|
<DateSelect
|
||||||
|
label="Start date"
|
||||||
|
value={value}
|
||||||
|
onChange={(val) => onChange(val)}
|
||||||
|
minDate={new Date()}
|
||||||
|
maxDate={maxDate ?? undefined}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -100,7 +112,12 @@ export const CycleForm: React.FC<Props> = ({ handleFormSubmit, handleClose, stat
|
|||||||
control={control}
|
control={control}
|
||||||
name="end_date"
|
name="end_date"
|
||||||
render={({ field: { value, onChange } }) => (
|
render={({ field: { value, onChange } }) => (
|
||||||
<DateSelect label="End date" value={value} onChange={(val) => onChange(val)} />
|
<DateSelect
|
||||||
|
label="End date"
|
||||||
|
value={value}
|
||||||
|
onChange={(val) => onChange(val)}
|
||||||
|
minDate={minDate}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -13,9 +13,9 @@ import useToast from "hooks/use-toast";
|
|||||||
// components
|
// components
|
||||||
import { CycleForm } from "components/cycles";
|
import { CycleForm } from "components/cycles";
|
||||||
// helper
|
// helper
|
||||||
import { getDateRangeStatus, isDateGreaterThanToday } from "helpers/date-time.helper";
|
import { getDateRangeStatus } from "helpers/date-time.helper";
|
||||||
// types
|
// types
|
||||||
import type { ICurrentUserResponse, ICycle } from "types";
|
import type { CycleDateCheckData, ICurrentUserResponse, ICycle } from "types";
|
||||||
// fetch keys
|
// fetch keys
|
||||||
import {
|
import {
|
||||||
COMPLETED_CYCLES_LIST,
|
COMPLETED_CYCLES_LIST,
|
||||||
@ -65,7 +65,6 @@ export const CreateUpdateCycleModal: React.FC<CycleModalProps> = ({
|
|||||||
}
|
}
|
||||||
mutate(INCOMPLETE_CYCLES_LIST(projectId.toString()));
|
mutate(INCOMPLETE_CYCLES_LIST(projectId.toString()));
|
||||||
mutate(CYCLES_LIST(projectId.toString()));
|
mutate(CYCLES_LIST(projectId.toString()));
|
||||||
handleClose();
|
|
||||||
|
|
||||||
setToastAlert({
|
setToastAlert({
|
||||||
type: "success",
|
type: "success",
|
||||||
@ -121,8 +120,6 @@ export const CreateUpdateCycleModal: React.FC<CycleModalProps> = ({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleClose();
|
|
||||||
|
|
||||||
setToastAlert({
|
setToastAlert({
|
||||||
type: "success",
|
type: "success",
|
||||||
title: "Success!",
|
title: "Success!",
|
||||||
@ -138,19 +135,16 @@ export const CreateUpdateCycleModal: React.FC<CycleModalProps> = ({
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const dateChecker = async (payload: any) => {
|
const dateChecker = async (payload: CycleDateCheckData) => {
|
||||||
try {
|
let status = false;
|
||||||
const res = await cycleService.cycleDateCheck(
|
|
||||||
workspaceSlug as string,
|
await cycleService
|
||||||
projectId as string,
|
.cycleDateCheck(workspaceSlug as string, projectId as string, payload)
|
||||||
payload
|
.then((res) => {
|
||||||
);
|
status = res.status;
|
||||||
console.log(res);
|
});
|
||||||
return res.status;
|
|
||||||
} catch (err) {
|
return status;
|
||||||
console.log(err);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleFormSubmit = async (formData: Partial<ICycle>) => {
|
const handleFormSubmit = async (formData: Partial<ICycle>) => {
|
||||||
@ -160,66 +154,34 @@ export const CreateUpdateCycleModal: React.FC<CycleModalProps> = ({
|
|||||||
...formData,
|
...formData,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (payload.start_date && payload.end_date) {
|
let isDateValid: boolean = true;
|
||||||
if (!isDateGreaterThanToday(payload.end_date)) {
|
|
||||||
setToastAlert({
|
|
||||||
type: "error",
|
|
||||||
title: "Error!",
|
|
||||||
message: "Unable to create cycle in past date. Please enter a valid date.",
|
|
||||||
});
|
|
||||||
handleClose();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data?.start_date && data?.end_date) {
|
if (payload.start_date && payload.end_date) {
|
||||||
const isDateValidForExistingCycle = await dateChecker({
|
if (data?.start_date && data?.end_date)
|
||||||
|
isDateValid = await dateChecker({
|
||||||
start_date: payload.start_date,
|
start_date: payload.start_date,
|
||||||
end_date: payload.end_date,
|
end_date: payload.end_date,
|
||||||
cycle_id: data.id,
|
cycle_id: data.id,
|
||||||
});
|
});
|
||||||
|
else
|
||||||
if (isDateValidForExistingCycle) {
|
isDateValid = await dateChecker({
|
||||||
await updateCycle(data.id, payload);
|
start_date: payload.start_date,
|
||||||
return;
|
end_date: payload.end_date,
|
||||||
} else {
|
|
||||||
setToastAlert({
|
|
||||||
type: "error",
|
|
||||||
title: "Error!",
|
|
||||||
message:
|
|
||||||
"You have a cycle already on the given dates, if you want to create your draft cycle you can do that by removing dates",
|
|
||||||
});
|
|
||||||
handleClose();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const isDateValid = await dateChecker({
|
|
||||||
start_date: payload.start_date,
|
|
||||||
end_date: payload.end_date,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (isDateValid) {
|
|
||||||
if (data) {
|
|
||||||
await updateCycle(data.id, payload);
|
|
||||||
} else {
|
|
||||||
await createCycle(payload);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
setToastAlert({
|
|
||||||
type: "error",
|
|
||||||
title: "Error!",
|
|
||||||
message:
|
|
||||||
"You have a cycle already on the given dates, if you want to create your draft cycle you can do that by removing dates",
|
|
||||||
});
|
});
|
||||||
handleClose();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (data) {
|
|
||||||
await updateCycle(data.id, payload);
|
|
||||||
} else {
|
|
||||||
await createCycle(payload);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isDateValid) {
|
||||||
|
if (data) await updateCycle(data.id, payload);
|
||||||
|
else await createCycle(payload);
|
||||||
|
|
||||||
|
handleClose();
|
||||||
|
} else
|
||||||
|
setToastAlert({
|
||||||
|
type: "error",
|
||||||
|
title: "Error!",
|
||||||
|
message:
|
||||||
|
"You already have a cycle on the given dates, if you want to create a draft cycle, remove the dates.",
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -27,22 +27,23 @@ const activityDetails: {
|
|||||||
icon: React.ReactNode;
|
icon: React.ReactNode;
|
||||||
};
|
};
|
||||||
} = {
|
} = {
|
||||||
assignee: {
|
|
||||||
message: (activity) => (
|
|
||||||
<>
|
|
||||||
removed the assignee{" "}
|
|
||||||
<span className="font-medium text-custom-text-100">{activity.old_value}</span>.
|
|
||||||
</>
|
|
||||||
),
|
|
||||||
icon: <Icon iconName="group" className="!text-sm" aria-hidden="true" />,
|
|
||||||
},
|
|
||||||
assignees: {
|
assignees: {
|
||||||
message: (activity) => (
|
message: (activity) => {
|
||||||
<>
|
if (activity.old_value === "")
|
||||||
added a new assignee{" "}
|
return (
|
||||||
<span className="font-medium text-custom-text-100">{activity.new_value}</span>.
|
<>
|
||||||
</>
|
added a new assignee{" "}
|
||||||
),
|
<span className="font-medium text-custom-text-100">{activity.new_value}</span>.
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
else
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
removed the assignee{" "}
|
||||||
|
<span className="font-medium text-custom-text-100">{activity.old_value}</span>.
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
},
|
||||||
icon: <Icon iconName="group" className="!text-sm" aria-hidden="true" />,
|
icon: <Icon iconName="group" className="!text-sm" aria-hidden="true" />,
|
||||||
},
|
},
|
||||||
archived_at: {
|
archived_at: {
|
||||||
|
@ -11,11 +11,21 @@ type Props = {
|
|||||||
value: string | null;
|
value: string | null;
|
||||||
onChange: (val: string | null) => void;
|
onChange: (val: string | null) => void;
|
||||||
label: string;
|
label: string;
|
||||||
|
minDate?: Date;
|
||||||
|
maxDate?: Date;
|
||||||
|
closeOnSelect?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const DateSelect: React.FC<Props> = ({ value, onChange, label }) => (
|
export const DateSelect: React.FC<Props> = ({
|
||||||
|
value,
|
||||||
|
onChange,
|
||||||
|
label,
|
||||||
|
minDate,
|
||||||
|
maxDate,
|
||||||
|
closeOnSelect = true,
|
||||||
|
}) => (
|
||||||
<Popover className="relative flex items-center justify-center rounded-lg">
|
<Popover className="relative flex items-center justify-center rounded-lg">
|
||||||
{({ open }) => (
|
{({ close }) => (
|
||||||
<>
|
<>
|
||||||
<Popover.Button className="flex cursor-pointer items-center rounded-md border border-custom-border-200 text-xs shadow-sm duration-300 hover:bg-custom-background-80">
|
<Popover.Button className="flex cursor-pointer items-center rounded-md border border-custom-border-200 text-xs shadow-sm duration-300 hover:bg-custom-background-80">
|
||||||
<span className="flex items-center justify-center gap-2 px-2 py-1 text-xs text-custom-text-200">
|
<span className="flex items-center justify-center gap-2 px-2 py-1 text-xs text-custom-text-200">
|
||||||
@ -50,8 +60,12 @@ export const DateSelect: React.FC<Props> = ({ value, onChange, label }) => (
|
|||||||
onChange={(val) => {
|
onChange={(val) => {
|
||||||
if (!val) onChange("");
|
if (!val) onChange("");
|
||||||
else onChange(renderDateFormat(val));
|
else onChange(renderDateFormat(val));
|
||||||
|
|
||||||
|
if (closeOnSelect) close();
|
||||||
}}
|
}}
|
||||||
dateFormat="dd-MM-yyyy"
|
dateFormat="dd-MM-yyyy"
|
||||||
|
minDate={minDate}
|
||||||
|
maxDate={maxDate}
|
||||||
inline
|
inline
|
||||||
/>
|
/>
|
||||||
</Popover.Panel>
|
</Popover.Panel>
|
||||||
|
@ -3,7 +3,7 @@ import APIService from "services/api.service";
|
|||||||
import trackEventServices from "services/track-event.service";
|
import trackEventServices from "services/track-event.service";
|
||||||
|
|
||||||
// types
|
// types
|
||||||
import type { ICurrentUserResponse, ICycle, IIssue, IIssueViewOptions } from "types";
|
import type { CycleDateCheckData, ICurrentUserResponse, ICycle, IIssue } from "types";
|
||||||
|
|
||||||
const { NEXT_PUBLIC_API_BASE_URL } = process.env;
|
const { NEXT_PUBLIC_API_BASE_URL } = process.env;
|
||||||
|
|
||||||
@ -148,10 +148,7 @@ class ProjectCycleServices extends APIService {
|
|||||||
async cycleDateCheck(
|
async cycleDateCheck(
|
||||||
workspaceSlug: string,
|
workspaceSlug: string,
|
||||||
projectId: string,
|
projectId: string,
|
||||||
data: {
|
data: CycleDateCheckData
|
||||||
start_date: string;
|
|
||||||
end_date: string;
|
|
||||||
}
|
|
||||||
): Promise<any> {
|
): Promise<any> {
|
||||||
return this.post(
|
return this.post(
|
||||||
`/api/workspaces/${workspaceSlug}/projects/${projectId}/cycles/date-check/`,
|
`/api/workspaces/${workspaceSlug}/projects/${projectId}/cycles/date-check/`,
|
||||||
|
6
apps/app/types/cycles.d.ts
vendored
6
apps/app/types/cycles.d.ts
vendored
@ -85,3 +85,9 @@ export type SelectCycleType =
|
|||||||
| undefined;
|
| undefined;
|
||||||
|
|
||||||
export type SelectIssue = (IIssue & { actionType: "edit" | "delete" | "create" }) | null;
|
export type SelectIssue = (IIssue & { actionType: "edit" | "delete" | "create" }) | null;
|
||||||
|
|
||||||
|
export type CycleDateCheckData = {
|
||||||
|
start_date: string;
|
||||||
|
end_date: string;
|
||||||
|
cycle_id?: string;
|
||||||
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user