import React, { useCallback, useEffect, useState } from "react"; import Link from "next/link"; import { useRouter } from "next/router"; import useSWR, { mutate } from "swr"; import { useForm } from "react-hook-form"; import { ChevronLeftIcon, ChevronRightIcon, PlusIcon } from "@heroicons/react/24/outline"; // services import issuesService from "services/issues.service"; import projectService from "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 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"; import { IssueDescriptionForm, IssueDescriptionFormValues, SubIssueList, CreateUpdateIssueModal, } from "components/issues"; // ui import { Loader, HeaderButton, CustomMenu } from "components/ui"; import { Breadcrumbs } from "components/breadcrumbs"; // types import { IIssue } from "types"; import type { NextPage, NextPageContext } from "next"; // fetch-keys import { PROJECT_DETAILS, PROJECT_ISSUES_LIST, PROJECT_ISSUES_ACTIVITY, ISSUE_DETAILS, SUB_ISSUES, } from "constants/fetch-keys"; 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 IssueDetailsPage: NextPage = () => { const router = useRouter(); const { workspaceSlug, projectId, issueId } = router.query; // states const [isOpen, setIsOpen] = useState(false); const [isAddAsSubIssueOpen, setIsAddAsSubIssueOpen] = useState(false); const [preloadedData, setPreloadedData] = useState< (Partial & { actionType: "createIssue" | "edit" | "delete" }) | undefined >(undefined); const { data: issueDetails, mutate: mutateIssueDetails } = useSWR( issueId && workspaceSlug && projectId ? ISSUE_DETAILS(issueId as string) : null, issueId && workspaceSlug && projectId ? () => issuesService.retrieve( workspaceSlug?.toString(), projectId?.toString(), issueId?.toString() ) : null ); const { data: subIssues } = useSWR( issueId && workspaceSlug && projectId ? SUB_ISSUES(issueId as string) : null, issueId && workspaceSlug && projectId ? () => issuesService.subIssues(workspaceSlug as string, projectId as string, issueId as string) : null ); 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 } = useSWR( workspaceSlug && projectId ? PROJECT_ISSUES_LIST(workspaceSlug as string, projectId as string) : null, workspaceSlug && projectId ? () => issuesService.getIssues(workspaceSlug as string, projectId as string) : null ); const { data: issueActivities, mutate: mutateIssueActivities } = useSWR( workspaceSlug && projectId && issueId ? PROJECT_ISSUES_ACTIVITY : null, workspaceSlug && projectId && issueId ? () => issuesService.getIssueActivities( workspaceSlug as string, projectId as string, issueId as string ) : null ); const { reset, control, watch } = useForm({ defaultValues, }); 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 siblingIssues = issueDetails && issues?.results.filter((i) => i.parent === issueDetails.parent && i.id !== issueId); useEffect(() => { if (issueDetails) { mutateIssueActivities(); reset({ ...issueDetails, blockers_list: issueDetails.blockers_list ?? issueDetails.blocker_issues?.map((issue) => issue.blocker_issue_detail?.id), blocked_list: issueDetails.blocked_list ?? issueDetails.blocked_issues?.map((issue) => issue.blocked_issue_detail?.id), assignees_list: issueDetails.assignees_list ?? issueDetails.assignee_details?.map((user) => user.id), labels_list: issueDetails.labels_list ?? issueDetails.labels, labels: issueDetails.labels_list ?? issueDetails.labels, }); } }, [issueDetails, reset, mutateIssueActivities]); const submitChanges = useCallback( (formData: Partial) => { if (!workspaceSlug || !activeProject || !issueId) return; const payload = { ...formData }; issuesService .patchIssue(workspaceSlug as string, projectId as string, issueId as string, payload) .then((res) => { mutateIssueDetails(); mutateIssueActivities(); }) .catch((e) => { console.error(e); }); }, [activeProject, workspaceSlug, issueId, projectId, mutateIssueDetails, mutateIssueActivities] ); const handleSubIssueRemove = (issueId: string) => { if (workspaceSlug && activeProject) { issuesService .patchIssue(workspaceSlug as string, activeProject.id, issueId, { parent: null }) .then((res) => { mutate(SUB_ISSUES(issueDetails?.id ?? "")); mutateIssueActivities(); }) .catch((e) => { console.error(e); }); } }; /** * Handling the debounce submit by updating the issue with name, description and description_html * @param values IssueDescriptionFormValues */ const handleDescriptionFormSubmit = useCallback( (values: IssueDescriptionFormValues) => { if (workspaceSlug && projectId && issueId) { issuesService .updateIssue(workspaceSlug?.toString(), projectId.toString(), issueId.toString(), values) .then((res) => { console.log(res); mutateIssueActivities(); }); } }, [workspaceSlug, projectId, issueId, mutateIssueActivities] ); 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 && ( setIsOpen(false)} prePopulateData={{ ...preloadedData, }} /> )} {isAddAsSubIssueOpen && ( )} {issueDetails && activeProject ? (
{issueDetails?.parent && issueDetails.parent !== "" ? ( ) : null}
{issueId && workspaceSlug && projectId && subIssues?.length > 0 ? ( ) : ( Add sub-issue } optionsPosition="left" noBorder > { setIsOpen(true); setPreloadedData({ parent: issueDetails.id, actionType: "createIssue", }); }} > Create new { setIsAddAsSubIssueOpen(true); setPreloadedData({ parent: issueDetails.id, actionType: "createIssue", }); }} > Add an existing issue )}

Comments/Activity

{/* TODO add flex-grow, if needed */}
) : (
)}
); }; export const getServerSideProps = async (ctx: NextPageContext) => { const user = await requiredAuth(ctx.req?.headers.cookie); const redirectAfterSignIn = ctx.req?.url; if (!user) { return { redirect: { destination: `/signin?next=${redirectAfterSignIn}`, permanent: false, }, }; } return { props: { user, }, }; }; export default IssueDetailsPage;