mirror of
https://github.com/makeplane/plane
synced 2024-06-14 14:31:34 +00:00
feat: editor for issue description (#2038)
This commit is contained in:
parent
099bce87b5
commit
eab1d9329b
192
apps/app/pages/[workspaceSlug]/editor.tsx
Normal file
192
apps/app/pages/[workspaceSlug]/editor.tsx
Normal file
@ -0,0 +1,192 @@
|
||||
import { TipTapEditor } from "components/tiptap";
|
||||
import type { NextPage } from "next";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import { Controller, useForm } from "react-hook-form";
|
||||
import issuesService from "services/issues.service";
|
||||
import { ICurrentUserResponse, IIssue } from "types";
|
||||
import useReloadConfirmations from "hooks/use-reload-confirmation";
|
||||
import { Spinner } from "components/ui";
|
||||
import Image404 from "public/404.svg";
|
||||
import DefaultLayout from "layouts/default-layout";
|
||||
import Image from "next/image";
|
||||
import userService from "services/user.service";
|
||||
import { useRouter } from "next/router";
|
||||
|
||||
const Editor: NextPage = () => {
|
||||
const [user, setUser] = useState<ICurrentUserResponse | undefined>();
|
||||
const [isSubmitting, setIsSubmitting] = useState<"submitting" | "submitted" | "saved">("saved");
|
||||
const [isLoading, setIsLoading] = useState("false");
|
||||
const { setShowAlert } = useReloadConfirmations();
|
||||
const [cookies, setCookies] = useState<any>({});
|
||||
const [issueDetail, setIssueDetail] = useState<IIssue | null>(null);
|
||||
const router = useRouter();
|
||||
const { editable } = router.query;
|
||||
const {
|
||||
handleSubmit,
|
||||
watch,
|
||||
setValue,
|
||||
control,
|
||||
formState: { errors },
|
||||
} = useForm<IIssue>({
|
||||
defaultValues: {
|
||||
name: "",
|
||||
description: "",
|
||||
description_html: "",
|
||||
},
|
||||
});
|
||||
|
||||
const getCookies = () => {
|
||||
const cookies = document.cookie.split(";");
|
||||
const cookieObj: any = {};
|
||||
cookies.forEach((cookie) => {
|
||||
const cookieArr = cookie.split("=");
|
||||
cookieObj[cookieArr[0].trim()] = cookieArr[1];
|
||||
});
|
||||
|
||||
setCookies(cookieObj);
|
||||
return cookieObj;
|
||||
};
|
||||
|
||||
const getIssueDetail = async (cookiesData: any) => {
|
||||
try {
|
||||
setIsLoading("true");
|
||||
const userData = await userService.currentUser();
|
||||
setUser(userData);
|
||||
const issueDetail = await issuesService.retrieve(
|
||||
cookiesData.MOBILE_slug,
|
||||
cookiesData.MOBILE_project_id,
|
||||
cookiesData.MOBILE_issue_id
|
||||
);
|
||||
setIssueDetail(issueDetail);
|
||||
setIsLoading("false");
|
||||
setValue("description_html", issueDetail.description_html);
|
||||
setValue("description", issueDetail.description);
|
||||
} catch (e) {
|
||||
setIsLoading("error");
|
||||
console.log(e);
|
||||
}
|
||||
};
|
||||
useEffect(() => {
|
||||
const cookiesData = getCookies();
|
||||
|
||||
getIssueDetail(cookiesData);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (isSubmitting === "submitted") {
|
||||
setShowAlert(false);
|
||||
setTimeout(async () => {
|
||||
setIsSubmitting("saved");
|
||||
}, 2000);
|
||||
} else if (isSubmitting === "submitting") {
|
||||
setShowAlert(true);
|
||||
}
|
||||
}, [isSubmitting, setShowAlert]);
|
||||
|
||||
const submitChanges = async (
|
||||
formData: Partial<IIssue>,
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
issueId: string
|
||||
) => {
|
||||
if (!workspaceSlug || !projectId || !issueId) return;
|
||||
|
||||
const payload: Partial<IIssue> = {
|
||||
...formData,
|
||||
};
|
||||
|
||||
delete payload.blocker_issues;
|
||||
delete payload.blocked_issues;
|
||||
await issuesService
|
||||
.patchIssue(workspaceSlug as string, projectId as string, issueId as string, payload, user)
|
||||
.catch((e) => {
|
||||
console.log(e);
|
||||
});
|
||||
};
|
||||
|
||||
const handleDescriptionFormSubmit = useCallback(
|
||||
async (formData: Partial<IIssue>) => {
|
||||
if (!formData) return;
|
||||
|
||||
await submitChanges(
|
||||
{
|
||||
name: issueDetail?.name ?? "",
|
||||
description: formData.description ?? "",
|
||||
description_html: formData.description_html ?? "<p></p>",
|
||||
},
|
||||
cookies.MOBILE_slug,
|
||||
cookies.MOBILE_project_id,
|
||||
cookies.MOBILE_issue_id
|
||||
);
|
||||
},
|
||||
[submitChanges]
|
||||
);
|
||||
|
||||
return isLoading === "error" ? (
|
||||
<ErrorEncountered />
|
||||
) : isLoading === "true" ? (
|
||||
<div className="grid place-items-center h-screen w-full">
|
||||
<Spinner />
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex blur-none shadow-none backdrop:backdrop-blur-none justify-center items-center">
|
||||
<Controller
|
||||
name="description_html"
|
||||
control={control}
|
||||
render={({ field: { value, onChange } }) => (
|
||||
<TipTapEditor
|
||||
borderOnFocus={false}
|
||||
value={
|
||||
!value ||
|
||||
value === "" ||
|
||||
(typeof value === "object" && Object.keys(value).length === 0)
|
||||
? watch("description_html")
|
||||
: value
|
||||
}
|
||||
editable={editable === "true"}
|
||||
noBorder={true}
|
||||
workspaceSlug={cookies.MOBILE_slug ?? ""}
|
||||
debouncedUpdatesEnabled={true}
|
||||
setShouldShowAlert={setShowAlert}
|
||||
setIsSubmitting={setIsSubmitting}
|
||||
customClassName="min-h-[150px] shadow-sm"
|
||||
editorContentCustomClassNames="pb-9"
|
||||
onChange={(description: Object, description_html: string) => {
|
||||
setShowAlert(true);
|
||||
setIsSubmitting("submitting");
|
||||
onChange(description_html);
|
||||
setValue("description", description);
|
||||
handleSubmit(handleDescriptionFormSubmit)().finally(() => {
|
||||
setIsSubmitting("submitted");
|
||||
});
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<div
|
||||
className={`absolute right-5 bottom-5 text-xs text-custom-text-200 border border-custom-border-400 rounded-xl w-[6.5rem] py-1 z-10 flex items-center justify-center ${
|
||||
isSubmitting === "saved" ? "fadeOut" : "fadeIn"
|
||||
}`}
|
||||
>
|
||||
{isSubmitting === "submitting" ? "Saving..." : "Saved"}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const ErrorEncountered: NextPage = () => (
|
||||
<DefaultLayout>
|
||||
<div className="grid max-h-fit place-items-center p-4">
|
||||
<div className="space-y-8 text-center">
|
||||
<div className="relative mx-auto h-40 w-40 lg:h-40 lg:w-40">
|
||||
<Image src={Image404} layout="fill" alt="404- Page not found" />
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<h3 className="text-lg font-semibold">Oops! Something went wrong.</h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</DefaultLayout>
|
||||
);
|
||||
|
||||
export default Editor;
|
Loading…
Reference in New Issue
Block a user