fix: v3 issues for the layouts (#2941)

* fix drag n drop exception error

* fix peek overlay close buttons

* fix project empty state view

* fix cycle and module empty state view

* add ai options to inbox issue creation

* fix inbox filters for viewers

* fix inbox filters for viewers for project

* disable editing permission for members and viewers

* define accurate types for drag and drop
This commit is contained in:
rahulramesha 2023-11-29 19:58:27 +05:30 committed by sriram veeraghanta
parent f7fa4d8b65
commit 90ca459b4a
17 changed files with 290 additions and 147 deletions

View file

@ -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<IIssue> = {
};
// services
const aiService = new AIService();
const fileService = new FileService();
export const CreateInboxIssueModal: React.FC<Props> = observer((props) => {
@ -38,21 +43,35 @@ export const CreateInboxIssueModal: React.FC<Props> = observer((props) => {
// states
const [createMore, setCreateMore] = useState(false);
const [gptAssistantModal, setGptAssistantModal] = useState(false);
const [iAmFeelingLucky, setIAmFeelingLucky] = useState(false);
const editorRef = useRef<any>(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<Props> = observer((props) => {
reset(defaultValues);
};
const issueName = watch("name");
const handleFormSubmit = async (formData: Partial<IIssue>) => {
if (!workspaceSlug || !projectId || !inboxId) return;
@ -70,24 +91,66 @@ export const CreateInboxIssueModal: React.FC<Props> = 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")}<p>${response}</p>`);
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 (
<Transition.Root show={isOpen} as={React.Fragment}>
<Dialog as="div" className="relative z-20" onClose={onClose}>
@ -146,7 +209,35 @@ export const CreateInboxIssueModal: React.FC<Props> = observer((props) => {
)}
/>
</div>
<div>
<div className="relative">
<div className="flex justify-end">
{issueName && issueName !== "" && (
<button
type="button"
className={`flex items-center gap-1 rounded px-1.5 py-1 text-xs hover:bg-custom-background-90 ${
iAmFeelingLucky ? "cursor-wait" : ""
}`}
onClick={handleAutoGenerateDescription}
disabled={iAmFeelingLucky}
>
{iAmFeelingLucky ? (
"Generating response..."
) : (
<>
<Sparkle className="h-4 w-4" />I{"'"}m feeling lucky
</>
)}
</button>
)}
<button
type="button"
className="flex items-center gap-1 rounded px-1.5 py-1 text-xs hover:bg-custom-background-90"
onClick={() => setGptAssistantModal((prevData) => !prevData)}
>
<Sparkle className="h-4 w-4" />
AI
</button>
</div>
<Controller
name="description_html"
control={control}
@ -168,6 +259,23 @@ export const CreateInboxIssueModal: React.FC<Props> = observer((props) => {
/>
)}
/>
{envConfig?.has_openai_configured && (
<GptAssistantModal
isOpen={gptAssistantModal}
handleClose={() => {
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}
/>
)}
</div>
<div className="flex flex-wrap items-center gap-2">
@ -188,7 +296,7 @@ export const CreateInboxIssueModal: React.FC<Props> = observer((props) => {
onClick={() => setCreateMore((prevData) => !prevData)}
>
<span className="text-xs">Create more</span>
<ToggleSwitch value={createMore} onChange={() => { }} size="md" />
<ToggleSwitch value={createMore} onChange={() => {}} size="md" />
</div>
<div className="flex items-center gap-2">
<Button variant="neutral-primary" size="sm" onClick={() => handleClose()}>