import React, { useCallback, useEffect, useState } from "react"; import Link from "next/link"; import { useRouter } from "next/router"; import useSWR, { mutate } from "swr"; // react-hook-form import { useForm } from "react-hook-form"; // services import issuesService from "services/issues.service"; // lib import { requiredAdmin, requiredAuth } from "lib/auth"; // layouts import AppLayout from "layouts/app-layout"; // components import { IssueDescriptionForm, SubIssuesList, CreateUpdateIssueModal, IssueDetailsSidebar, IssueActivitySection, AddComment, SubIssuesListModal, } from "components/issues"; // ui import { Loader, CustomMenu } from "components/ui"; import { Breadcrumbs } from "components/breadcrumbs"; // icons import { PlusIcon } from "@heroicons/react/24/outline"; // types import { IIssue, IssueResponse, UserAuth } from "types"; import type { NextPage, NextPageContext } from "next"; // fetch-keys import { PROJECT_ISSUES_ACTIVITY, ISSUE_DETAILS, SUB_ISSUES, PROJECT_ISSUES_LIST, } 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, issue_module: null, labels_list: [], }; const IssueDetailsPage: NextPage = (props) => { // states const [isOpen, setIsOpen] = useState(false); const [subIssuesListModal, setSubIssuesListModal] = useState(false); const [preloadedData, setPreloadedData] = useState< (Partial & { actionType: "createIssue" | "edit" | "delete" }) | undefined >(undefined); const router = useRouter(); const { workspaceSlug, projectId, issueId } = router.query; const { data: issueDetails, mutate: mutateIssueDetails } = useSWR( workspaceSlug && projectId && issueId ? ISSUE_DETAILS(issueId as string) : null, workspaceSlug && projectId && issueId ? () => issuesService.retrieve(workspaceSlug as string, projectId as string, issueId as string) : 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: issueActivities, mutate: mutateIssueActivities } = useSWR( workspaceSlug && projectId && issueId ? PROJECT_ISSUES_ACTIVITY(issueId as string) : null, workspaceSlug && projectId && issueId ? () => issuesService.getIssueActivities( workspaceSlug as string, projectId as string, issueId as string ) : null ); const { data: siblingIssues } = useSWR( workspaceSlug && projectId && issueDetails?.parent ? SUB_ISSUES(issueDetails.parent) : null, workspaceSlug && projectId && issueDetails?.parent ? () => issuesService.subIssues( workspaceSlug as string, projectId as string, issueDetails.parent ?? "" ) : null ); const { reset, control, watch } = useForm({ defaultValues, }); const submitChanges = useCallback( (formData: Partial) => { if (!workspaceSlug || !projectId || !issueId) return; mutate( ISSUE_DETAILS(issueId as string), (prevData: IIssue) => ({ ...prevData, ...formData, }), false ); const payload = { ...formData }; issuesService .patchIssue(workspaceSlug as string, projectId as string, issueId as string, payload) .then((res) => { mutateIssueDetails(); mutateIssueActivities(); }) .catch((e) => { console.error(e); }); }, [workspaceSlug, issueId, projectId, mutateIssueDetails, mutateIssueActivities] ); const handleSubIssueRemove = (issueId: string) => { if (!workspaceSlug || !projectId) return; mutate( SUB_ISSUES(issueDetails?.id ?? ""), (prevData) => prevData?.filter((i) => i.id !== issueId), false ); issuesService .patchIssue(workspaceSlug as string, projectId as string, issueId, { parent: null }) .then((res) => { mutate(SUB_ISSUES(issueDetails?.id ?? "")); mutate( PROJECT_ISSUES_LIST(workspaceSlug as string, projectId as string), (prevData) => ({ ...(prevData as IssueResponse), results: (prevData?.results ?? []).map((p) => { if (p.id === res.id) return { ...p, ...res, }; return p; }), }), false ); }) .catch((e) => { console.error(e); }); }; useEffect(() => { if (!issueDetails) return; mutateIssueActivities(); reset({ ...issueDetails, blockers_list: issueDetails.blockers_list ?? issueDetails.blocker_issues?.map((issue) => issue.blocker_issue_detail?.id), blocked_list: issueDetails.blocks_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 isNotAllowed = props.isGuest || props.isViewer; return ( } > {isOpen && ( setIsOpen(false)} prePopulateData={{ ...preloadedData, }} /> )} {subIssuesListModal && ( setSubIssuesListModal(false)} parent={issueDetails} /> )} {issueDetails && projectId ? (
{issueDetails?.parent && issueDetails.parent !== "" ? ( ) : null}
{issueId && workspaceSlug && projectId && subIssues && subIssues.length > 0 ? ( ) : ( !isNotAllowed && ( Add sub-issue } optionsPosition="left" noBorder > { setIsOpen(true); setPreloadedData({ parent: issueDetails.id, actionType: "createIssue", }); }} > Create new { setSubIssuesListModal(true); setPreloadedData({ parent: issueDetails.id, actionType: "createIssue", }); }} > Add an existing issue ) )}

Comments/Activity

) : (
)}
); }; 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, }, }; } const projectId = ctx.query.projectId as string; const workspaceSlug = ctx.query.workspaceSlug as string; const memberDetail = await requiredAdmin(workspaceSlug, projectId, ctx.req?.headers.cookie); return { props: { isOwner: memberDetail?.role === 20, isMember: memberDetail?.role === 15, isViewer: memberDetail?.role === 10, isGuest: memberDetail?.role === 5, }, }; }; export default IssueDetailsPage;