plane/web/pages/m/[workspaceSlug]/projects/[projectId]/issues/[issueId].tsx

198 lines
5.2 KiB
TypeScript
Raw Normal View History

2023-09-05 09:12:34 +00:00
// react
import React, { useCallback, useEffect } from "react";
// next
import { useRouter } from "next/router";
// swr
import useSWR, { mutate } from "swr";
// react hook forms
import { useFormContext, useForm, FormProvider } from "react-hook-form";
2023-09-05 09:12:34 +00:00
// services
import issuesService from "services/issues.service";
// fetch key
import { ISSUE_DETAILS, PROJECT_ISSUES_ACTIVITY } from "constants/fetch-keys";
2023-09-05 09:12:34 +00:00
// hooks
import useUser from "hooks/use-user";
import useProjectMembers from "hooks/use-project-members";
2023-09-05 09:12:34 +00:00
// layouts
import WebViewLayout from "layouts/web-view-layout";
2023-09-05 09:12:34 +00:00
// components
import {
IssueWebViewForm,
SubIssueList,
IssueAttachments,
IssuePropertiesDetail,
IssueLinks,
IssueActivity,
Spinner,
2023-09-05 09:12:34 +00:00
} from "components/web-view";
// types
import type { IIssue } from "types";
const MobileWebViewIssueDetail_ = () => {
2023-09-05 09:12:34 +00:00
const router = useRouter();
const { workspaceSlug, projectId, issueId } = router.query;
const isArchive = Boolean(router.query.archive);
const memberRole = useProjectMembers(
workspaceSlug as string,
projectId as string,
!!workspaceSlug && !!projectId
);
2023-09-05 09:12:34 +00:00
const isAllowed = Boolean((memberRole.isMember || memberRole.isOwner) && !isArchive);
2023-09-05 09:12:34 +00:00
const { user } = useUser();
const formContext = useFormContext<IIssue>();
const { register, handleSubmit, control, watch, reset } = formContext;
2023-09-05 09:12:34 +00:00
const {
data: issue,
mutate: mutateIssue,
2023-09-05 09:12:34 +00:00
error,
} = useSWR(
workspaceSlug && projectId && issueId && !isArchive ? ISSUE_DETAILS(issueId.toString()) : null,
workspaceSlug && projectId && issueId && !isArchive
2023-09-05 09:12:34 +00:00
? () =>
issuesService.retrieve(workspaceSlug.toString(), projectId.toString(), issueId.toString())
: null
);
const { data: archiveIssueDetails, mutate: mutateaArchiveIssue } = useSWR<IIssue | undefined>(
workspaceSlug && projectId && issueId && isArchive ? ISSUE_DETAILS(issueId as string) : null,
workspaceSlug && projectId && issueId && isArchive
? () =>
issuesService.retrieveArchivedIssue(
workspaceSlug.toString(),
projectId.toString(),
issueId.toString()
)
: null
);
const issueDetails = isArchive ? archiveIssueDetails : issue;
const mutateIssueDetails = isArchive ? mutateaArchiveIssue : mutateIssue;
2023-09-05 09:12:34 +00:00
useEffect(() => {
if (!issueDetails) return;
reset({
...issueDetails,
name: issueDetails.name,
description: issueDetails.description,
description_html: issueDetails.description_html,
state: issueDetails.state,
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,
2023-09-05 09:12:34 +00:00
});
}, [issueDetails, reset]);
const submitChanges = useCallback(
async (formData: Partial<IIssue>) => {
if (!workspaceSlug || !projectId || !issueId) return;
mutate<IIssue>(
ISSUE_DETAILS(issueId.toString()),
2023-09-05 09:12:34 +00:00
(prevData) => {
if (!prevData) return prevData;
return {
...prevData,
...formData,
};
},
false
);
const payload: Partial<IIssue> = {
...formData,
};
delete payload.issue_relations;
delete payload.related_issues;
2023-09-05 09:12:34 +00:00
await issuesService
.patchIssue(workspaceSlug as string, projectId as string, issueId as string, payload, user)
.then(() => {
mutateIssueDetails();
mutate(PROJECT_ISSUES_ACTIVITY(issueId as string));
})
.catch((e) => {
console.error(e);
});
},
[workspaceSlug, issueId, projectId, mutateIssueDetails, user]
);
if (!error && !issueDetails)
return (
<WebViewLayout>
2023-09-05 09:12:34 +00:00
<div className="px-4 py-2 h-full">
<div className="h-full flex justify-center items-center">
<Spinner />
</div>
</div>
</WebViewLayout>
2023-09-05 09:12:34 +00:00
);
if (error)
return (
<WebViewLayout>
2023-09-05 09:12:34 +00:00
<div className="px-4 py-2">{error?.response?.data || "Something went wrong"}</div>
</WebViewLayout>
2023-09-05 09:12:34 +00:00
);
return (
<WebViewLayout>
{isArchive && (
<div className="w-full h-screen top-0 left-0 fixed z-50 bg-white/20 pointer-events-none" />
)}
2023-09-05 09:12:34 +00:00
<div className="px-6 py-2 h-full overflow-auto space-y-3">
<IssueWebViewForm
isAllowed={isAllowed}
issueDetails={issueDetails!}
submitChanges={submitChanges}
register={register}
control={control}
watch={watch}
handleSubmit={handleSubmit}
/>
<SubIssueList issueDetails={issueDetails!} />
<IssuePropertiesDetail submitChanges={submitChanges} />
<IssueAttachments allowed={isAllowed} />
2023-09-05 09:12:34 +00:00
<IssueLinks allowed={isAllowed} issueDetails={issueDetails!} />
<IssueActivity allowed={isAllowed} issueDetails={issueDetails!} />
2023-09-05 09:12:34 +00:00
</div>
</WebViewLayout>
2023-09-05 09:12:34 +00:00
);
};
const MobileWebViewIssueDetail = () => {
const methods = useForm();
return (
<FormProvider {...methods}>
<MobileWebViewIssueDetail_ />
</FormProvider>
);
};
2023-09-05 09:12:34 +00:00
export default MobileWebViewIssueDetail;