mirror of
https://github.com/makeplane/plane
synced 2024-06-14 14:31:34 +00:00
fix: issue details mutation (#389)
This commit is contained in:
parent
f725ea5b15
commit
30a91a6b91
@ -1,11 +1,9 @@
|
|||||||
import { FC, useCallback, useEffect, useMemo, useState } from "react";
|
import { FC, useCallback, useEffect, useState } from "react";
|
||||||
|
|
||||||
import dynamic from "next/dynamic";
|
import dynamic from "next/dynamic";
|
||||||
|
|
||||||
// react-hook-form
|
// react-hook-form
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
// lodash
|
|
||||||
import debounce from "lodash.debounce";
|
|
||||||
// components
|
// components
|
||||||
import { Loader, TextArea } from "components/ui";
|
import { Loader, TextArea } from "components/ui";
|
||||||
const RemirrorRichTextEditor = dynamic(() => import("components/rich-text-editor"), {
|
const RemirrorRichTextEditor = dynamic(() => import("components/rich-text-editor"), {
|
||||||
@ -27,7 +25,7 @@ export interface IssueDescriptionFormValues {
|
|||||||
|
|
||||||
export interface IssueDetailsProps {
|
export interface IssueDetailsProps {
|
||||||
issue: IIssue;
|
issue: IIssue;
|
||||||
handleFormSubmit: (value: IssueDescriptionFormValues) => void;
|
handleFormSubmit: (value: IssueDescriptionFormValues) => Promise<void>;
|
||||||
userAuth: UserAuth;
|
userAuth: UserAuth;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -36,6 +34,7 @@ export const IssueDescriptionForm: FC<IssueDetailsProps> = ({
|
|||||||
handleFormSubmit,
|
handleFormSubmit,
|
||||||
userAuth,
|
userAuth,
|
||||||
}) => {
|
}) => {
|
||||||
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||||
const [characterLimit, setCharacterLimit] = useState(false);
|
const [characterLimit, setCharacterLimit] = useState(false);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@ -53,10 +52,10 @@ export const IssueDescriptionForm: FC<IssueDetailsProps> = ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const handleDescriptionFormSubmit = useCallback(
|
const handleDescriptionFormSubmit = useCallback(
|
||||||
(formData: Partial<IIssue>) => {
|
async (formData: Partial<IIssue>) => {
|
||||||
if (!formData.name || formData.name.length === 0 || formData.name.length > 255) return;
|
if (!formData.name || formData.name.length === 0 || formData.name.length > 255) return;
|
||||||
|
|
||||||
handleFormSubmit({
|
await handleFormSubmit({
|
||||||
name: formData.name ?? "",
|
name: formData.name ?? "",
|
||||||
description: formData.description ?? "",
|
description: formData.description ?? "",
|
||||||
description_html: formData.description_html ?? "<p></p>",
|
description_html: formData.description_html ?? "<p></p>",
|
||||||
@ -65,17 +64,19 @@ export const IssueDescriptionForm: FC<IssueDetailsProps> = ({
|
|||||||
[handleFormSubmit]
|
[handleFormSubmit]
|
||||||
);
|
);
|
||||||
|
|
||||||
const debounceHandler = useMemo(
|
useEffect(() => {
|
||||||
() => debounce(handleSubmit(handleDescriptionFormSubmit), 2000),
|
const alertUser = (e: BeforeUnloadEvent) => {
|
||||||
[handleSubmit, handleDescriptionFormSubmit]
|
console.log("beforeunload");
|
||||||
);
|
e.preventDefault();
|
||||||
|
e.returnValue = "";
|
||||||
|
return "Are you sure you want to leave?";
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(
|
window.addEventListener("beforeunload", alertUser);
|
||||||
() => () => {
|
return () => {
|
||||||
debounceHandler.cancel();
|
window.removeEventListener("beforeunload", alertUser);
|
||||||
},
|
};
|
||||||
[debounceHandler]
|
}, [isSubmitting]);
|
||||||
);
|
|
||||||
|
|
||||||
// reset form values
|
// reset form values
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -95,19 +96,29 @@ export const IssueDescriptionForm: FC<IssueDetailsProps> = ({
|
|||||||
placeholder="Enter issue name"
|
placeholder="Enter issue name"
|
||||||
value={watch("name")}
|
value={watch("name")}
|
||||||
onFocus={() => setCharacterLimit(true)}
|
onFocus={() => setCharacterLimit(true)}
|
||||||
onBlur={() => setCharacterLimit(false)}
|
onBlur={() => {
|
||||||
|
setCharacterLimit(false);
|
||||||
|
|
||||||
|
setIsSubmitting(true);
|
||||||
|
handleSubmit(handleDescriptionFormSubmit)()
|
||||||
|
.then(() => {
|
||||||
|
setIsSubmitting(false);
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
setIsSubmitting(false);
|
||||||
|
});
|
||||||
|
}}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
setValue("name", e.target.value);
|
setValue("name", e.target.value);
|
||||||
debounceHandler();
|
|
||||||
}}
|
}}
|
||||||
required={true}
|
required={true}
|
||||||
className="block px-3 py-2 text-xl
|
className="min-h-10 block w-full resize-none
|
||||||
w-full overflow-hidden resize-none min-h-10
|
overflow-hidden rounded border-none bg-transparent
|
||||||
rounded border-none bg-transparent ring-0 focus:ring-1 focus:ring-theme outline-none"
|
px-3 py-2 text-xl outline-none ring-0 focus:ring-1 focus:ring-theme"
|
||||||
role="textbox"
|
role="textbox"
|
||||||
/>
|
/>
|
||||||
{characterLimit && (
|
{characterLimit && (
|
||||||
<div className="absolute bottom-0 right-0 text-xs bg-white p-1 rounded pointer-events-none z-[2]">
|
<div className="pointer-events-none absolute bottom-0 right-0 z-[2] rounded bg-white p-1 text-xs">
|
||||||
<span
|
<span
|
||||||
className={`${
|
className={`${
|
||||||
watch("name").length === 0 || watch("name").length > 255 ? "text-red-500" : ""
|
watch("name").length === 0 || watch("name").length > 255 ? "text-red-500" : ""
|
||||||
@ -123,13 +134,21 @@ export const IssueDescriptionForm: FC<IssueDetailsProps> = ({
|
|||||||
<RemirrorRichTextEditor
|
<RemirrorRichTextEditor
|
||||||
value={watch("description")}
|
value={watch("description")}
|
||||||
placeholder="Describe the issue..."
|
placeholder="Describe the issue..."
|
||||||
onJSONChange={(json) => {
|
onBlur={() => {
|
||||||
setValue("description", json);
|
setIsSubmitting(true);
|
||||||
debounceHandler();
|
handleSubmit(handleDescriptionFormSubmit)()
|
||||||
|
.then(() => {
|
||||||
|
setIsSubmitting(false);
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
setIsSubmitting(false);
|
||||||
|
});
|
||||||
}}
|
}}
|
||||||
|
onJSONChange={(json) => setValue("description", json)}
|
||||||
onHTMLChange={(html) => setValue("description_html", html)}
|
onHTMLChange={(html) => setValue("description_html", html)}
|
||||||
editable={!isNotAllowed}
|
editable={!isNotAllowed}
|
||||||
/>
|
/>
|
||||||
|
<div className="text-right text-sm text-gray-500">{isSubmitting && "Saving..."}</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -74,7 +74,7 @@ const IssueDetailsPage: NextPage<UserAuth> = (props) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const submitChanges = useCallback(
|
const submitChanges = useCallback(
|
||||||
(formData: Partial<IIssue>) => {
|
async (formData: Partial<IIssue>) => {
|
||||||
if (!workspaceSlug || !projectId || !issueId) return;
|
if (!workspaceSlug || !projectId || !issueId) return;
|
||||||
|
|
||||||
mutate(
|
mutate(
|
||||||
@ -87,7 +87,7 @@ const IssueDetailsPage: NextPage<UserAuth> = (props) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const payload = { ...formData };
|
const payload = { ...formData };
|
||||||
issuesService
|
await issuesService
|
||||||
.patchIssue(workspaceSlug as string, projectId as string, issueId as string, payload)
|
.patchIssue(workspaceSlug as string, projectId as string, issueId as string, payload)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
mutateIssueDetails();
|
mutateIssueDetails();
|
||||||
|
Loading…
Reference in New Issue
Block a user