"use client"; import { useEffect, useRef, useState } from "react"; import { differenceInCalendarDays } from "date-fns"; import { observer } from "mobx-react"; import { useParams, usePathname } from "next/navigation"; import { useForm } from "react-hook-form"; import { PlusIcon } from "lucide-react"; // types import { ISearchIssueResponse, TIssue } from "@plane/types"; // ui import { TOAST_TYPE, setPromiseToast, setToast, CustomMenu } from "@plane/ui"; // components import { ExistingIssuesListModal } from "@/components/core"; // constants import { ISSUE_CREATED } from "@/constants/event-tracker"; // helpers import { cn } from "@/helpers/common.helper"; import { createIssuePayload } from "@/helpers/issue.helper"; // hooks import { useEventTracker, useIssueDetail, useProject } from "@/hooks/store"; import useKeypress from "@/hooks/use-keypress"; import useOutsideClickDetector from "@/hooks/use-outside-click-detector"; type Props = { formKey: keyof TIssue; groupId?: string; subGroupId?: string | null; prePopulatedData?: Partial; quickAddCallback?: ( workspaceSlug: string, projectId: string, data: TIssue, viewId?: string ) => Promise; addIssuesToView?: (issueIds: string[]) => Promise; viewId?: string; onOpen?: () => void; }; const defaultValues: Partial = { name: "", }; const Inputs = (props: any) => { const { register, setFocus, projectDetails } = props; useEffect(() => { setFocus("name"); }, [setFocus]); return ( <>

{projectDetails?.identifier ?? "..."}

); }; export const CalendarQuickAddIssueForm: React.FC = observer((props) => { const { formKey, prePopulatedData, quickAddCallback, addIssuesToView, viewId, onOpen } = props; // router const { workspaceSlug, projectId, moduleId } = useParams(); const pathname = usePathname(); // store hooks const { getProjectById } = useProject(); const { captureIssueEvent } = useEventTracker(); const { updateIssue } = useIssueDetail(); // refs const ref = useRef(null); // states const [isOpen, setIsOpen] = useState(false); const [isMenuOpen, setIsMenuOpen] = useState(false); const [isExistingIssueModalOpen, setIsExistingIssueModalOpen] = useState(false); // derived values const projectDetail = projectId ? getProjectById(projectId.toString()) : null; const ExistingIssuesListModalPayload = addIssuesToView ? moduleId ? { module: moduleId.toString(), target_date: "none" } : { cycle: true, target_date: "none" } : { target_date: "none" }; const { reset, handleSubmit, register, setFocus, formState: { errors, isSubmitting }, } = useForm({ defaultValues }); const handleClose = () => { setIsOpen(false); }; useKeypress("Escape", handleClose); useOutsideClickDetector(ref, handleClose); useEffect(() => { if (!isOpen) reset({ ...defaultValues }); }, [isOpen, reset]); useEffect(() => { if (!errors) return; Object.keys(errors).forEach((key) => { const error = errors[key as keyof TIssue]; setToast({ type: TOAST_TYPE.ERROR, title: "Error!", message: error?.message?.toString() || "Some error occurred. Please try again.", }); }); }, [errors]); const onSubmitHandler = async (formData: TIssue) => { if (isSubmitting || !workspaceSlug || !projectId) return; reset({ ...defaultValues }); const payload = createIssuePayload(projectId.toString(), { ...(prePopulatedData ?? {}), ...formData, }); if (quickAddCallback) { const quickAddPromise = quickAddCallback( workspaceSlug.toString(), projectId.toString(), { ...payload, }, viewId ); setPromiseToast(quickAddPromise, { loading: "Adding issue...", success: { title: "Success!", message: () => "Issue created successfully.", }, error: { title: "Error!", message: (err) => err?.message || "Some error occurred. Please try again.", }, }); await quickAddPromise .then((res) => { captureIssueEvent({ eventName: ISSUE_CREATED, payload: { ...res, state: "SUCCESS", element: "Calendar quick add" }, path: pathname, }); }) .catch(() => { captureIssueEvent({ eventName: ISSUE_CREATED, payload: { ...payload, state: "FAILED", element: "Calendar quick add" }, path: pathname, }); }); } }; const handleAddIssuesToView = async (data: ISearchIssueResponse[]) => { if (!workspaceSlug || !projectId) return; const issueIds = data.map((i) => i.id); try { // To handle all updates in parallel await Promise.all( data.map((issue) => updateIssue(workspaceSlug.toString(), projectId.toString(), issue.id, prePopulatedData ?? {}) ) ); await addIssuesToView?.(issueIds); } catch (error) { setToast({ type: TOAST_TYPE.ERROR, title: "Error!", message: "Something went wrong. Please try again.", }); } }; const handleNewIssue = () => { setIsOpen(true); if (onOpen) onOpen(); }; const handleExistingIssue = () => { setIsExistingIssueModalOpen(true); }; return ( <> {workspaceSlug && projectId && ( setIsExistingIssueModalOpen(false)} searchParams={ExistingIssuesListModalPayload} handleOnSubmit={handleAddIssuesToView} shouldHideIssue={(issue) => { if (issue.start_date && prePopulatedData?.target_date) { const issueStartDate = new Date(issue.start_date); const targetDate = new Date(prePopulatedData.target_date); const diffInDays = differenceInCalendarDays(targetDate, issueStartDate); if (diffInDays < 0) return true; } return false; }} /> )} {isOpen && (
)} {!isOpen && (
setIsMenuOpen(true)} onMenuClose={() => setIsMenuOpen(false)} className="w-full" customButtonClassName="w-full" customButton={
New Issue
} > New Issue Add existing issue
)} ); });