import React, { useCallback, useEffect, useMemo, useState } from "react"; import Link from "next/link"; import dynamic from "next/dynamic"; import type { NextPage, NextPageContext } from "next"; import { useRouter } from "next/router"; import useSWR, { mutate } from "swr"; import { Controller, useForm } from "react-hook-form"; import { Disclosure, Transition } from "@headlessui/react"; // services import issuesServices from "lib/services/issues.service"; import projectService from "lib/services/project.service"; // lib import { requiredAuth } from "lib/auth"; // layouts import AppLayout from "layouts/app-layout"; // components import AddAsSubIssue from "components/project/issues/issue-detail/add-as-sub-issue"; import CreateUpdateIssuesModal from "components/project/issues/create-update-issue-modal"; import IssueDetailSidebar from "components/project/issues/issue-detail/issue-detail-sidebar"; import AddIssueComment from "components/project/issues/issue-detail/comment/issue-comment-section"; import IssueActivitySection from "components/project/issues/issue-detail/activity"; // ui import { Loader, TextArea, HeaderButton, Breadcrumbs, CustomMenu } from "ui"; // icons import { ChevronLeftIcon, ChevronRightIcon, PlusIcon } from "@heroicons/react/24/outline"; // types import { IIssue, IssueResponse } from "types"; // fetch-keys import { PROJECT_DETAILS, PROJECT_ISSUES_LIST, PROJECT_ISSUES_ACTIVITY, } from "constants/fetch-keys"; // common import { debounce } from "constants/common"; const RemirrorRichTextEditor = dynamic(() => import("components/rich-text-editor"), { ssr: false, loading: () => ( ), }); const defaultValues = { name: "", description: "", description_html: "", state: "", assignees_list: [], priority: "low", blockers_list: [], blocked_list: [], target_date: new Date().toString(), issue_cycle: null, labels_list: [], }; const IssueDetail: NextPage = () => { const [isOpen, setIsOpen] = useState(false); const [isAddAsSubIssueOpen, setIsAddAsSubIssueOpen] = useState(false); const [preloadedData, setPreloadedData] = useState< (Partial & { actionType: "createIssue" | "edit" | "delete" }) | undefined >(undefined); const router = useRouter(); const { workspaceSlug, projectId, issueId } = router.query; const { data: activeProject } = useSWR( workspaceSlug && projectId ? PROJECT_DETAILS(projectId as string) : null, workspaceSlug && projectId ? () => projectService.getProject(workspaceSlug as string, projectId as string) : null ); const { data: issues, mutate: mutateIssues } = useSWR( workspaceSlug && projectId ? PROJECT_ISSUES_LIST(workspaceSlug as string, projectId as string) : null, workspaceSlug && projectId ? () => issuesServices.getIssues(workspaceSlug as string, projectId as string) : null ); const { data: issueActivities, mutate: mutateIssueActivities } = useSWR( workspaceSlug && projectId && issueId ? PROJECT_ISSUES_ACTIVITY : null, workspaceSlug && projectId && issueId ? () => issuesServices.getIssueActivities( workspaceSlug as string, projectId as string, issueId as string ) : null ); const { register, handleSubmit, reset, control, watch, setValue } = useForm({ defaultValues, }); const issueDetail = issues?.results?.find((issue) => issue.id === 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 === issueId)) ?? []; const siblingIssues = issueDetail && issues?.results.filter((i) => i.parent === issueDetail.parent && i.id !== issueId); useEffect(() => { if (issueDetail) { mutateIssueActivities(); 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, labels: issueDetail.labels_list ?? issueDetail.labels, }); } }, [issueDetail, reset, mutateIssueActivities]); const submitChanges = useCallback( (formData: Partial) => { if (!workspaceSlug || !activeProject || !issueId) return; mutateIssues( (prevData) => ({ ...(prevData as IssueResponse), results: (prevData?.results ?? []).map((issue) => { if (issue.id === issueId) return { ...issue, ...formData }; return issue; }), }), false ); const payload = { ...formData, }; issuesServices .patchIssue(workspaceSlug as string, projectId as string, issueId as string, payload) .then((response) => { mutateIssues((prevData) => ({ ...(prevData as IssueResponse), results: (prevData?.results ?? []).map((issue) => { if (issue.id === issueId) { return { ...issue, ...response }; } return issue; }), })); mutateIssueActivities(); }) .catch((error) => { console.error(error); }); }, [activeProject, workspaceSlug, issueId, projectId, mutateIssues, mutateIssueActivities] ); const handleSubIssueRemove = (issueId: string) => { if (workspaceSlug && activeProject) { issuesServices .patchIssue(workspaceSlug as string, activeProject.id, issueId, { parent: null }) .then((res) => { mutate( PROJECT_ISSUES_LIST(workspaceSlug as string, activeProject.id), (prevData) => ({ ...(prevData as IssueResponse), results: (prevData?.results ?? []).map((p) => p.id === issueId ? { ...p, ...res } : p ), }), false ); mutateIssueActivities(); }) .catch((e) => { console.error(e); }); } }; return ( } right={
{ if (!prevIssue) return; router.push(`/${workspaceSlug}/projects/${prevIssue.project}/issues/${prevIssue.id}`); }} /> { if (!nextIssue) return; router.push( `/${workspaceSlug}/projects/${nextIssue.project}/issues/${nextIssue?.id}` ); }} position="reverse" />
} > {isOpen && ( )} {isAddAsSubIssueOpen && ( )} {issueDetail && activeProject ? (
{issueDetail.parent !== null && issueDetail.parent !== "" ? ( ) : null}