From 96910e18973a09100b14dc865940afc241d6ba09 Mon Sep 17 00:00:00 2001 From: Aaryan Khandelwal <65252264+aaryan610@users.noreply.github.com> Date: Wed, 29 Mar 2023 16:30:40 +0530 Subject: [PATCH] chore: ai for issue description (#575) * feat: block sync * chore: ai assistant for issue description --- .../components/core/gpt-assistant-modal.tsx | 138 +++++++++--------- .../components/issues/description-form.tsx | 80 +++++----- apps/app/components/issues/form.tsx | 35 ++++- .../components/pages/single-page-block.tsx | 1 + .../projects/[projectId]/views/index.tsx | 8 +- 5 files changed, 148 insertions(+), 114 deletions(-) diff --git a/apps/app/components/core/gpt-assistant-modal.tsx b/apps/app/components/core/gpt-assistant-modal.tsx index e9c7241c1..f5c372798 100644 --- a/apps/app/components/core/gpt-assistant-modal.tsx +++ b/apps/app/components/core/gpt-assistant-modal.tsx @@ -19,6 +19,7 @@ type Props = { content: string; htmlContent?: string; onResponse: (response: string) => void; + projectId: string; }; type FormData = { @@ -37,12 +38,13 @@ export const GptAssistantModal: React.FC = ({ content, htmlContent, onResponse, + projectId, }) => { const [response, setResponse] = useState(""); const [invalidResponse, setInvalidResponse] = useState(false); const router = useRouter(); - const { workspaceSlug, projectId } = router.query; + const { workspaceSlug } = router.query; const { setToastAlert } = useToast(); @@ -80,7 +82,7 @@ export const GptAssistantModal: React.FC = ({ await aiService .createGptTask(workspaceSlug as string, projectId as string, { - prompt: content && content !== "" ? content : "", + prompt: content && content !== "" ? content : htmlContent ?? "", task: formData.task, }) .then((res) => { @@ -98,75 +100,77 @@ export const GptAssistantModal: React.FC = ({ return (
-
- {content && content !== "" && ( -
- Content: - {content}

} - customClassName="-mx-3 -my-3" - noBorder - borderOnFocus={false} - editable={false} - /> -
- )} - {response !== "" && ( -
- Response: - ${response}

`} - customClassName="-mx-3 -my-3" - noBorder - borderOnFocus={false} - editable={false} - /> -
- )} - {invalidResponse && ( -
- No response could be generated. This may be due to insufficient content or task - information. Please try again. -
- )} - -
- {response !== "" && ( - { - onResponse(response); - onClose(); - }} - > - Use this response - - )} -
- Close - - {isSubmitting - ? "Generating response..." - : response === "" - ? "Generate response" - : "Generate again"} - -
+ {((content && content !== "") || htmlContent) && ( +
+ Content: + {content}

} + customClassName="-mx-3 -my-3" + noBorder + borderOnFocus={false} + editable={false} + />
- + )} + {response !== "" && ( +
+ Response: + ${response}

`} + customClassName="-mx-3 -my-3" + noBorder + borderOnFocus={false} + editable={false} + /> +
+ )} + {invalidResponse && ( +
+ No response could be generated. This may be due to insufficient content or task + information. Please try again. +
+ )} + +
+ {response !== "" && ( + { + onResponse(response); + onClose(); + }} + > + Use this response + + )} +
+ Close + + {isSubmitting + ? "Generating response..." + : response === "" + ? "Generate response" + : "Generate again"} + +
+
); }; diff --git a/apps/app/components/issues/description-form.tsx b/apps/app/components/issues/description-form.tsx index a4ba631e8..666b30964 100644 --- a/apps/app/components/issues/description-form.tsx +++ b/apps/app/components/issues/description-form.tsx @@ -3,7 +3,7 @@ import { FC, useCallback, useEffect, useState } from "react"; import dynamic from "next/dynamic"; // react-hook-form -import { useForm } from "react-hook-form"; +import { Controller, useForm } from "react-hook-form"; // components import { Loader, TextArea } from "components/ui"; const RemirrorRichTextEditor = dynamic(() => import("components/rich-text-editor"), { @@ -42,6 +42,8 @@ export const IssueDescriptionForm: FC = ({ watch, setValue, reset, + register, + control, formState: { errors }, } = useForm({ defaultValues: { @@ -64,19 +66,18 @@ export const IssueDescriptionForm: FC = ({ [handleFormSubmit] ); - useEffect(() => { - const alertUser = (e: BeforeUnloadEvent) => { - console.log("beforeunload"); - e.preventDefault(); - e.returnValue = ""; - return "Are you sure you want to leave?"; - }; + // useEffect(() => { + // const alertUser = (e: BeforeUnloadEvent) => { + // e.preventDefault(); + // e.returnValue = ""; + // return "Are you sure you want to leave?"; + // }; - window.addEventListener("beforeunload", alertUser); - return () => { - window.removeEventListener("beforeunload", alertUser); - }; - }, [isSubmitting]); + // window.addEventListener("beforeunload", alertUser); + // return () => { + // window.removeEventListener("beforeunload", alertUser); + // }; + // }, [isSubmitting]); // reset form values useEffect(() => { @@ -94,7 +95,7 @@ export const IssueDescriptionForm: FC = ({ id="name" name="name" placeholder="Enter issue name" - value={watch("name")} + register={register} onFocus={() => setCharacterLimit(true)} onBlur={() => { setCharacterLimit(false); @@ -108,9 +109,6 @@ export const IssueDescriptionForm: FC = ({ setIsSubmitting(false); }); }} - onChange={(e) => { - setValue("name", e.target.value); - }} required={true} className="min-h-10 block w-full resize-none overflow-hidden rounded border-none bg-transparent @@ -131,26 +129,34 @@ export const IssueDescriptionForm: FC = ({ )}
{errors.name ? errors.name.message : null} - { - setIsSubmitting(true); - handleSubmit(handleDescriptionFormSubmit)() - .then(() => { - setIsSubmitting(false); - }) - .catch(() => { - setIsSubmitting(false); - }); - }} - onJSONChange={(json) => setValue("description", json)} - onHTMLChange={(html) => setValue("description_html", html)} - editable={!isNotAllowed} + ( + setValue("description", jsonValue)} + onHTMLChange={(htmlValue) => setValue("description_html", htmlValue)} + onBlur={() => { + setIsSubmitting(true); + handleSubmit(handleDescriptionFormSubmit)() + .then(() => { + setIsSubmitting(false); + }) + .catch(() => { + setIsSubmitting(false); + }); + }} + placeholder="Describe the issue..." + editable={!isNotAllowed} + /> + )} />
import("components/rich-text-editor"), { ssr: false, loading: () => ( - + ), @@ -81,6 +82,8 @@ export const IssueForm: FC = ({ const [labelModal, setLabelModal] = useState(false); const [parentIssueListModalOpen, setParentIssueListModalOpen] = useState(false); + const [gptAssistantModal, setGptAssistantModal] = useState(false); + const router = useRouter(); const { workspaceSlug } = router.query; @@ -115,6 +118,13 @@ export const IssueForm: FC = ({ }); }; + const handleAiAssistance = async (response: string) => { + if (!workspaceSlug || !projectId) return; + + setValue("description", {}); + setValue("description_html", `${watch("description_html")}

${response}

`); + }; + useEffect(() => { setFocus("name"); @@ -233,7 +243,17 @@ export const IssueForm: FC = ({
)} -
+
+
+ +
= ({ /> )} /> + setGptAssistantModal(false)} + inset="top-2 left-0" + content="" + htmlContent={watch("description_html")} + onResponse={handleAiAssistance} + projectId={projectId} + />
= ({ block, projectDetails }) => { content={block.description_stripped} htmlContent={block.description_html} onResponse={handleAiAssistance} + projectId={projectId as string} />
diff --git a/apps/app/pages/[workspaceSlug]/projects/[projectId]/views/index.tsx b/apps/app/pages/[workspaceSlug]/projects/[projectId]/views/index.tsx index 848073251..084a149ce 100644 --- a/apps/app/pages/[workspaceSlug]/projects/[projectId]/views/index.tsx +++ b/apps/app/pages/[workspaceSlug]/projects/[projectId]/views/index.tsx @@ -50,8 +50,6 @@ const ProjectViews: NextPage = (props) => { : null ); - console.log(views) - return ( = (props) => {

Views

{views.map((view) => ( - + ))}