diff --git a/web/components/inbox/modals/create-issue-modal.tsx b/web/components/inbox/modals/create-issue-modal.tsx index 3a0746da6..0ab6b3d85 100644 --- a/web/components/inbox/modals/create-issue-modal.tsx +++ b/web/components/inbox/modals/create-issue-modal.tsx @@ -16,6 +16,10 @@ import { Button, Input, ToggleSwitch } from "@plane/ui"; // types import { IIssue } from "types"; import useEditorSuggestions from "hooks/use-editor-suggestions"; +import { GptAssistantModal } from "components/core"; +import { Sparkle } from "lucide-react"; +import useToast from "hooks/use-toast"; +import { AIService } from "services/ai.service"; type Props = { isOpen: boolean; @@ -31,6 +35,7 @@ const defaultValues: Partial = { }; // services +const aiService = new AIService(); const fileService = new FileService(); export const CreateInboxIssueModal: React.FC = observer((props) => { @@ -38,21 +43,35 @@ export const CreateInboxIssueModal: React.FC = observer((props) => { // states const [createMore, setCreateMore] = useState(false); + const [gptAssistantModal, setGptAssistantModal] = useState(false); + const [iAmFeelingLucky, setIAmFeelingLucky] = useState(false); const editorRef = useRef(null); + const { setToastAlert } = useToast(); const editorSuggestion = useEditorSuggestions(); const router = useRouter(); - const { workspaceSlug, projectId, inboxId } = router.query; + const { workspaceSlug, projectId, inboxId } = router.query as { + workspaceSlug: string; + projectId: string; + inboxId: string; + }; - const { inboxIssueDetails: inboxIssueDetailsStore, trackEvent: { postHogEventTracker } } = useMobxStore(); + const { + inboxIssueDetails: inboxIssueDetailsStore, + trackEvent: { postHogEventTracker }, + appConfig: { envConfig }, + } = useMobxStore(); const { control, formState: { errors, isSubmitting }, handleSubmit, reset, + watch, + getValues, + setValue, } = useForm({ defaultValues }); const handleClose = () => { @@ -60,6 +79,8 @@ export const CreateInboxIssueModal: React.FC = observer((props) => { reset(defaultValues); }; + const issueName = watch("name"); + const handleFormSubmit = async (formData: Partial) => { if (!workspaceSlug || !projectId || !inboxId) return; @@ -70,24 +91,66 @@ export const CreateInboxIssueModal: React.FC = observer((props) => { router.push(`/${workspaceSlug}/projects/${projectId}/inbox/${inboxId}?inboxIssueId=${res.issue_inbox[0].id}`); handleClose(); } else reset(defaultValues); - postHogEventTracker( - "ISSUE_CREATE", - { - ...res, - state: "SUCCESS" - } - ); - }).catch((error) => { + postHogEventTracker("ISSUE_CREATE", { + ...res, + state: "SUCCESS", + }); + }) + .catch((error) => { console.log(error); - postHogEventTracker( - "ISSUE_CREATE", - { - state: "FAILED" - } - ); + postHogEventTracker("ISSUE_CREATE", { + state: "FAILED", + }); }); }; + const handleAiAssistance = async (response: string) => { + if (!workspaceSlug || !projectId) return; + + setValue("description", {}); + setValue("description_html", `${watch("description_html")}

${response}

`); + editorRef.current?.setEditorValue(`${watch("description_html")}`); + }; + + const handleAutoGenerateDescription = async () => { + if (!workspaceSlug || !projectId || !issueName) return; + + setIAmFeelingLucky(true); + + aiService + .createGptTask(workspaceSlug as string, projectId as string, { + prompt: issueName, + task: "Generate a proper description for this issue.", + }) + .then((res) => { + if (res.response === "") + setToastAlert({ + type: "error", + title: "Error!", + message: + "Issue title isn't informative enough to generate the description. Please try with a different title.", + }); + else handleAiAssistance(res.response_html); + }) + .catch((err) => { + const error = err?.data?.error; + + if (err.status === 429) + setToastAlert({ + type: "error", + title: "Error!", + message: error || "You have reached the maximum number of requests of 50 requests per month per user.", + }); + else + setToastAlert({ + type: "error", + title: "Error!", + message: error || "Some error occurred. Please try again.", + }); + }) + .finally(() => setIAmFeelingLucky(false)); + }; + return ( @@ -146,7 +209,35 @@ export const CreateInboxIssueModal: React.FC = observer((props) => { )} /> -
+
+
+ {issueName && issueName !== "" && ( + + )} + +
= observer((props) => { /> )} /> + {envConfig?.has_openai_configured && ( + { + setGptAssistantModal(false); + // this is done so that the title do not reset after gpt popover closed + reset(getValues()); + }} + inset="top-2 left-0" + content="" + htmlContent={watch("description_html")} + onResponse={(response) => { + handleAiAssistance(response); + }} + projectId={projectId} + /> + )}
@@ -188,7 +296,7 @@ export const CreateInboxIssueModal: React.FC = observer((props) => { onClick={() => setCreateMore((prevData) => !prevData)} > Create more - { }} size="md" /> + {}} size="md" />