import React, { useEffect, useState } from "react"; // next import Link from "next/link"; import { useRouter } from "next/router"; // swr import { mutate } from "swr"; // react hook form import { useForm } from "react-hook-form"; // fetching keys import { PROJECT_ISSUES_DETAILS, PROJECT_ISSUES_LIST, CYCLE_ISSUES, USER_ISSUE, } from "constants/fetch-keys"; // headless import { Dialog, Transition } from "@headlessui/react"; // services import issuesServices from "lib/services/issues.services"; // hooks import useUser from "lib/hooks/useUser"; import useToast from "lib/hooks/useToast"; // ui import { Button, Input, TextArea } from "ui"; // commons import { renderDateFormat, cosineSimilarity } from "constants/common"; // components import SelectState from "./SelectState"; import SelectCycles from "./SelectCycles"; import SelectLabels from "./SelectLabels"; import SelectProject from "./SelectProject"; import SelectPriority from "./SelectPriority"; import SelectAssignee from "./SelectAssignee"; import SelectParent from "./SelectParentIssues"; import CreateUpdateStateModal from "components/project/issues/BoardView/state/CreateUpdateStateModal"; import CreateUpdateCycleModal from "components/project/cycles/CreateUpdateCyclesModal"; // types import type { IIssue, IssueResponse, SprintIssueResponse } from "types"; type Props = { isOpen: boolean; setIsOpen: React.Dispatch>; projectId?: string; data?: IIssue; prePopulateData?: Partial; isUpdatingSingleIssue?: boolean; }; const defaultValues: Partial = { name: "", description: "", }; const CreateUpdateIssuesModal: React.FC = ({ isOpen, setIsOpen, data, projectId, prePopulateData, isUpdatingSingleIssue = false, }) => { const [isCycleModalOpen, setIsCycleModalOpen] = useState(false); const [isStateModalOpen, setIsStateModalOpen] = useState(false); const [mostSimilarIssue, setMostSimilarIssue] = useState(); const router = useRouter(); const handleClose = () => { setIsOpen(false); if (data) { resetForm(); } }; const resetForm = () => { const timeout = setTimeout(() => { reset(defaultValues); clearTimeout(timeout); }, 500); }; const { activeWorkspace, activeProject, user, issues } = useUser(); const { setToastAlert } = useToast(); const { register, formState: { errors, isSubmitting }, handleSubmit, reset, setError, control, watch, } = useForm({ defaultValues, }); const addIssueToSprint = async (issueId: string, sprintId: string, issueDetail: IIssue) => { if (!activeWorkspace || !activeProject) return; await issuesServices .addIssueToSprint(activeWorkspace.slug, activeProject.id, sprintId, { issue: issueId, }) .then((res) => { console.log("add to sprint", res); mutate( CYCLE_ISSUES(sprintId), (prevData) => { const targetResponse = prevData?.find((t) => t.cycle === sprintId); if (targetResponse) { targetResponse.issue_details = issueDetail; return prevData; } else { return [ ...(prevData ?? []), { cycle: sprintId, issue_details: issueDetail, } as SprintIssueResponse, ]; } }, false ); if (isUpdatingSingleIssue) { mutate( PROJECT_ISSUES_DETAILS, (prevData) => ({ ...(prevData as IIssue), sprints: sprintId }), false ); } else mutate( PROJECT_ISSUES_LIST(activeWorkspace.slug, activeProject.id), (prevData) => { return { ...(prevData as IssueResponse), results: (prevData?.results ?? []).map((issue) => { if (issue.id === res.id) return { ...issue, sprints: sprintId }; return issue; }), }; }, false ); setToastAlert({ title: "Success", type: "success", message: "Issue added to sprint successfully", }); }) .catch((err) => { console.log(err); }); }; const onSubmit = async (formData: IIssue) => { if (!activeWorkspace || !activeProject) return; const payload: Partial = { ...formData, target_date: formData.target_date ? renderDateFormat(formData.target_date ?? "") : null, }; if (!data) { await issuesServices .createIssues(activeWorkspace.slug, activeProject.id, payload) .then(async (res) => { console.log(res); mutate( PROJECT_ISSUES_LIST(activeWorkspace.slug, activeProject.id), (prevData) => { return { ...(prevData as IssueResponse), results: [res, ...(prevData?.results ?? [])], count: (prevData?.count ?? 0) + 1, }; }, false ); if (formData.sprints && formData.sprints !== null) { await addIssueToSprint(res.id, formData.sprints, formData); } handleClose(); resetForm(); setToastAlert({ title: "Success", type: "success", message: `Issue ${data ? "updated" : "created"} successfully`, }); if (formData.assignees_list.some((assignee) => assignee === user?.id)) { mutate( USER_ISSUE, (prevData) => { return [res, ...(prevData ?? [])]; }, false ); } }) .catch((err) => { Object.keys(err).map((key) => { setError(key as keyof IIssue, { message: err[key].join(", ") }); }); }); } else { await issuesServices .updateIssue(activeWorkspace.slug, activeProject.id, data.id, payload) .then(async (res) => { console.log(res); if (isUpdatingSingleIssue) { mutate(PROJECT_ISSUES_DETAILS, (prevData) => ({ ...prevData, ...res }), false); } else mutate( PROJECT_ISSUES_LIST(activeWorkspace.slug, activeProject.id), (prevData) => { return { ...(prevData as IssueResponse), results: (prevData?.results ?? []).map((issue) => { if (issue.id === res.id) return { ...issue, ...res }; return issue; }), }; }, false ); if (formData.sprints && formData.sprints !== null) { await addIssueToSprint(res.id, formData.sprints, formData); } handleClose(); resetForm(); setToastAlert({ title: "Success", type: "success", message: "Issue updated successfully", }); }) .catch((err) => { Object.keys(err).map((key) => { setError(key as keyof IIssue, { message: err[key].join(", ") }); }); }); } }; useEffect(() => { if (data) setIsOpen(true); }, [data, setIsOpen]); useEffect(() => { reset({ ...defaultValues, ...watch(), ...data, project: activeProject?.id ?? projectId, ...prePopulateData, }); }, [data, prePopulateData, reset, projectId, activeProject, isOpen, watch]); useEffect(() => { return () => setMostSimilarIssue(undefined); }, []); return ( <> {activeProject && ( <> )}

{data ? "Update" : "Create"} Issue