// next import type { NextPage } from "next"; import { useRouter } from "next/router"; import dynamic from "next/dynamic"; // react import React, { useCallback, useEffect, useState } from "react"; // swr import useSWR, { mutate } from "swr"; // react hook form import { Controller, useForm } from "react-hook-form"; // headless ui import { Disclosure, Menu, Tab, Transition } from "@headlessui/react"; // services import issuesServices from "lib/services/issues.services"; import stateServices from "lib/services/state.services"; // fetch keys import { PROJECT_ISSUES_ACTIVITY, PROJECT_ISSUES_COMMENTS, PROJECT_ISSUES_LIST, STATE_LIST, } from "constants/fetch-keys"; // hooks import useUser from "lib/hooks/useUser"; // layouts import AdminLayout from "layouts/AdminLayout"; // components import CreateUpdateIssuesModal from "components/project/issues/CreateUpdateIssueModal"; import IssueCommentSection from "components/project/issues/issue-detail/comment/IssueCommentSection"; // common import { debounce } from "constants/common"; // components import IssueDetailSidebar from "components/project/issues/issue-detail/IssueDetailSidebar"; // activites import IssueActivitySection from "components/project/issues/issue-detail/activity"; // ui import { Spinner, TextArea } from "ui"; import HeaderButton from "ui/HeaderButton"; import { BreadcrumbItem, Breadcrumbs } from "ui/Breadcrumbs"; // types import { IIssue, IIssueComment, IssueResponse, IState } from "types"; // icons import { ChevronLeftIcon, ChevronRightIcon, EllipsisHorizontalIcon, PlusIcon, } from "@heroicons/react/24/outline"; import Link from "next/link"; import AddAsSubIssue from "components/command-palette/addAsSubIssue"; const IssueDetail: NextPage = () => { const router = useRouter(); const { issueId, projectId } = router.query; const { activeWorkspace, activeProject, issues, mutateIssues, states } = useUser(); const [isOpen, setIsOpen] = useState(false); const [isAddAsSubIssueOpen, setIsAddAsSubIssueOpen] = useState(false); const [issueDetail, setIssueDetail] = useState(undefined); const [preloadedData, setPreloadedData] = useState< (Partial & { actionType: "createIssue" | "edit" | "delete" }) | undefined >(undefined); const [issueDescriptionValue, setIssueDescriptionValue] = useState(""); const handleDescriptionChange: any = (value: any) => { console.log(value); setIssueDescriptionValue(value); }; const RichTextEditor = dynamic(() => import("components/lexical/editor"), { ssr: false, }); const LexicalViewer = dynamic(() => import("components/lexical/viewer"), { ssr: false, }); const { register, formState: { errors }, handleSubmit, reset, control, watch, } = useForm({ defaultValues: { name: "", description: "", state: "", assignees_list: [], priority: "low", blockers_list: [], blocked_list: [], target_date: new Date().toString(), cycle: "", labels_list: [], }, }); const { data: issueActivities } = useSWR( activeWorkspace && projectId && issueId ? PROJECT_ISSUES_ACTIVITY : null, activeWorkspace && projectId && issueId ? () => issuesServices.getIssueActivities( activeWorkspace.slug, projectId as string, issueId as string ) : null ); const { data: issueComments } = useSWR( activeWorkspace && projectId && issueId ? PROJECT_ISSUES_COMMENTS : null, activeWorkspace && projectId && issueId ? () => issuesServices.getIssueComments( activeWorkspace.slug, projectId as string, issueId as string ) : null ); const submitChanges = useCallback( (formData: Partial) => { if (!activeWorkspace || !activeProject || !issueId) return; mutateIssues( (prevData) => ({ ...(prevData as IssueResponse), results: (prevData?.results ?? []).map((issue) => { if (issue.id === issueId) { return { ...issue, ...formData }; } return issue; }), }), false ); issuesServices .patchIssue(activeWorkspace.slug, projectId as string, issueId as string, formData) .then((response) => { console.log(response); }) .catch((error) => { console.log(error); }); }, [activeProject, activeWorkspace, issueId, projectId, mutateIssues] ); useEffect(() => { if (issueDetail) reset({ ...issueDetail, blockers_list: issueDetail.blockers_list ?? issueDetail.blocker_issues?.map((issue) => issue.blocker_issue_detail?.id), blocked_list: issueDetail.blocked_list ?? issueDetail.blocked_issues?.map((issue) => issue.blocked_issue_detail?.id), assignees_list: issueDetail.assignees_list ?? issueDetail.assignee_details?.map((user) => user.id), labels_list: issueDetail.labels_list ?? issueDetail.labels?.map((label) => label), }); }, [issueDetail, reset]); useEffect(() => { const issueIndex = issues?.results.findIndex((issue) => issue.id === issueId); if (issueIndex === undefined) return; const issueDetail = issues?.results[issueIndex]; setIssueDetail(issueDetail); }, [issues, issueId]); const prevIssue = issues?.results[issues?.results.findIndex((issue) => issue.id === issueId) - 1]; const nextIssue = issues?.results[issues?.results.findIndex((issue) => issue.id === issueId) + 1]; const subIssues = (issues && issues.results.filter((i) => i.parent === issueDetail?.id)) ?? []; const siblingIssues = issueDetail && issues?.results.filter((i) => i.parent === issueDetail.parent && i.id !== issueDetail.id); const handleSubIssueRemove = (issueId: string) => { if (activeWorkspace && activeProject) { issuesServices .patchIssue(activeWorkspace.slug, activeProject.id, issueId, { parent: null }) .then((res) => { mutate( PROJECT_ISSUES_LIST(activeWorkspace.slug, activeProject.id), (prevData) => ({ ...(prevData as IssueResponse), results: (prevData?.results ?? []).map((p) => p.id === issueId ? { ...p, ...res } : p ), }), false ); }) .catch((e) => { console.log(e); }); } }; return (
{ if (!prevIssue) return; router.push(`/projects/${prevIssue.project}/issues/${prevIssue.id}`); }} /> { if (!nextIssue) return; router.push(`/projects/${nextIssue.project}/issues/${nextIssue?.id}`); }} position="reverse" />
{issueDetail && activeProject ? (
{issueDetail.parent !== null && issueDetail.parent !== "" ? ( ) : null}