/** * Copyright (c) 2023-present Plane Software, Inc. and contributors * SPDX-License-Identifier: AGPL-3.0-only * See the LICENSE file for details. */ import type { Dispatch, SetStateAction } from "react"; import { useEffect, useMemo, useRef } from "react"; import { observer } from "mobx-react"; import type { EditorRefApi } from "@plane/editor"; import { ISSUE_PRIORITIES } from "@plane/constants"; import { useTranslation } from "@plane/i18n"; import { LabelPropertyIcon, PriorityIcon } from "@plane/propel/icons"; import { TOAST_TYPE, setToast } from "@plane/propel/toast"; import type { TExternalContourRequest, TIssue, TNameDescriptionLoader } from "@plane/types"; import { EFileAssetType } from "@plane/types"; import { getTextContent } from "@plane/utils"; import { DescriptionVersionsRoot } from "@/components/core/description-versions"; import { DescriptionInput } from "@/components/editor/rich-text/description-input"; import { DescriptionInputLoader } from "@/components/editor/rich-text/description-input/loader"; import { IssueAttachmentRoot } from "@/components/issues/attachment"; import type { TIssueOperations } from "@/components/issues/issue-detail"; import { IssueActivity } from "@/components/issues/issue-detail/issue-activity"; import { IssueReaction } from "@/components/issues/issue-detail/reactions"; import { IssueTitleInput } from "@/components/issues/title-input"; import { useProjectExternalContours } from "@/hooks/store/use-project-external-contours"; import { useUser } from "@/hooks/store/user"; import useReloadConfirmations from "@/hooks/use-reload-confirmation"; import { DeDupeIssuePopoverRoot } from "@/plane-web/components/de-dupe/duplicate-popover"; import { useDebouncedDuplicateIssues } from "@/plane-web/hooks/use-debounced-duplicate-issues"; import { IssueService } from "@/services/issue/issue.service"; import { WorkItemVersionService } from "@/services/issue/work_item_version.service"; import { ExternalContoursMirroredActivity } from "./mirrored-activity"; import { ExternalContoursMirroredAttachments } from "./mirrored-attachments"; import { ExternalContoursMirroredComments } from "./mirrored-comments"; import { ExternalContoursIssueContentProperties } from "./issue-properties"; import { ExternalContoursRequestTraceability } from "./request-traceability"; import { ExternalContoursSourceReplyBox } from "./source-reply-box"; const workItemVersionService = new WorkItemVersionService(); const issueService = new IssueService(); type Props = { workspaceSlug: string; sourceProjectId: string; contourRequest: TExternalContourRequest; hasDirectTargetAccess: boolean; isEditable: boolean; isSourceEditable: boolean; isSubmitting: TNameDescriptionLoader; setIsSubmitting: Dispatch>; }; export const ExternalContoursIssueMainContent = observer(function ExternalContoursIssueMainContent(props: Props) { const { workspaceSlug, sourceProjectId, contourRequest, hasDirectTargetAccess, isEditable, isSourceEditable, isSubmitting, setIsSubmitting, } = props; const { t } = useTranslation(); const editorRef = useRef(null); const { data: currentUser } = useUser(); const { loader, updateRequest, updateRequestIssue } = useProjectExternalContours(); const { setShowAlert } = useReloadConfirmations(isSubmitting === "submitting"); useEffect(() => { if (isSubmitting === "submitted") { setShowAlert(false); setTimeout(async () => setIsSubmitting("saved"), 3000); } else if (isSubmitting === "submitting") { setShowAlert(true); } }, [isSubmitting, setIsSubmitting, setShowAlert]); const issue = contourRequest.issue; const mirroredActivity = contourRequest.mirrored_activity ?? []; const mirroredAttachments = contourRequest.mirrored_attachments ?? []; const mirroredComments = contourRequest.mirrored_comments ?? []; const targetProjectId = issue.project_id || sourceProjectId; const priorityDetails = ISSUE_PRIORITIES.find((priority) => priority.key === issue.priority); const { duplicateIssues } = useDebouncedDuplicateIssues( workspaceSlug, targetProjectId, targetProjectId, { name: issue?.name, description_html: getTextContent(issue?.description_html), issueId: issue?.id, } ); const issueOperations: TIssueOperations = useMemo( () => ({ fetch: async () => undefined, remove: async (_workspaceSlug: string, _projectId: string, issueId: string) => { try { await issueService.deleteIssue(workspaceSlug, targetProjectId, issueId); setToast({ title: t("success"), type: TOAST_TYPE.SUCCESS, message: t("inbox_issue.modals.delete.success") }); } catch { setToast({ title: t("error"), type: TOAST_TYPE.ERROR, message: t("something_went_wrong_please_try_again") }); } }, update: async (_workspaceSlug: string, _projectId: string, issueId: string, data: Partial) => { try { const updatedIssue = await issueService.patchIssue(workspaceSlug, targetProjectId, issueId, data); updateRequestIssue(contourRequest.id, { ...data, ...updatedIssue }); } catch { setToast({ title: t("error"), type: TOAST_TYPE.ERROR, message: t("issue_could_not_be_updated") }); } }, archive: async () => undefined, }), [contourRequest.id, targetProjectId, t, updateRequestIssue, workspaceSlug] ); const sourceIssueOperations: TIssueOperations = useMemo( () => ({ fetch: async () => undefined, remove: async () => undefined, update: async (_workspaceSlug: string, _projectId: string, requestId: string, data: Partial) => { try { await updateRequest(workspaceSlug, sourceProjectId, requestId, { name: data.name, description_html: data.description_html, }); } catch { setToast({ title: t("error"), type: TOAST_TYPE.ERROR, message: t("issue_could_not_be_updated") }); } }, }), [sourceProjectId, t, updateRequest, workspaceSlug] ); if (!issue || !issue.project_id || !issue.id) return <>; if (!hasDirectTargetAccess) { return (
{isSourceEditable ? ( <> setIsSubmitting(value)} issueOperations={sourceIssueOperations} disabled={false} value={issue.name} containerClassName="-ml-3" /> {loader === "issue-loading" || issue.description_html === undefined ? ( ) : (

" : issue.description_html} key={`${issue.id}-source`} onSubmit={async (value) => { await sourceIssueOperations.update(workspaceSlug, sourceProjectId, contourRequest.id, { description_html: value.description_html, }); }} projectId={sourceProjectId} setIsSubmitting={(value) => setIsSubmitting(value)} workspaceSlug={workspaceSlug} /> )} ) : ( <>

{issue.name}

" }} /> )}
{t("external_contours_page.properties.section_title")}
{t("priority")}
{issue.priority && issue.priority !== "none" ? priorityDetails?.title ?? t(issue.priority) : t("external_contours_page.form.priority")}
{t("labels")}
{issue.label_details?.length ? (
{issue.label_details.map((label) => label.name).join(", ")}
) : (
{t("labels")}
)}
{!isSourceEditable && (
{t("external_contours_page.readonly_source_view")}
)}
); } return (
{duplicateIssues.length > 0 && ( )} setIsSubmitting(value)} issueOperations={issueOperations} disabled={!isEditable} value={issue.name} containerClassName="-ml-3" /> {loader === "issue-loading" || issue.description_html === undefined ? ( ) : (

" : issue.description_html} key={issue.id} onSubmit={async (value, isMigrationUpdate) => { await issueOperations.update(workspaceSlug, targetProjectId, issue.id, { description_html: value.description_html, ...(isMigrationUpdate ? { skip_activity: "true" } : {}), }); }} projectId={targetProjectId} setIsSubmitting={(value) => setIsSubmitting(value)} workspaceSlug={workspaceSlug} /> )}
{currentUser && ( )} {isEditable && ( workItemVersionService.listDescriptionVersions(workspaceSlug, targetProjectId, issueId), retrieveDescriptionVersion: (issueId, versionId) => workItemVersionService.retrieveDescriptionVersion(workspaceSlug, targetProjectId, issueId, versionId), }} handleRestore={(descriptionHTML) => editorRef.current?.setEditorValue(descriptionHTML, true)} projectId={targetProjectId} workspaceSlug={workspaceSlug} /> )}
); });