ФУНКЦИИ - МЕЖПРОЕКТНАЯ КОММУНИКАЦИЯ: frontend read-layer и первый экран двусторонней доски внешних контуров

This commit is contained in:
DCCONSTRUCTIONS
2026-04-20 20:49:09 +03:00
parent 0184ff9a32
commit 8bf6f2a510
15 changed files with 603 additions and 69 deletions
@@ -6,7 +6,6 @@
import { useCallback, useEffect, useState } from "react";
import { observer } from "mobx-react";
import { PanelLeft } from "lucide-react";
import { useTranslation } from "@plane/i18n";
import { Button } from "@plane/propel/button";
import { CheckCircleFilledIcon, ChevronDownIcon, ChevronUpIcon, CloseCircleFilledIcon, LinkIcon, NewTabIcon } from "@plane/propel/icons";
@@ -28,8 +27,6 @@ type Props = {
contourRequest: TExternalContourRequest;
hasDirectTargetAccess: boolean;
isSubmitting: TNameDescriptionLoader;
isMobileSidebar: boolean;
setIsMobileSidebar: (value: boolean) => void;
};
export const ExternalContoursIssueActionsHeader = observer(function ExternalContoursIssueActionsHeader(props: Props) {
@@ -39,8 +36,6 @@ export const ExternalContoursIssueActionsHeader = observer(function ExternalCont
contourRequest,
hasDirectTargetAccess,
isSubmitting,
isMobileSidebar,
setIsMobileSidebar,
} = props;
const { t } = useTranslation();
const router = useAppRouter();
@@ -50,13 +45,17 @@ export const ExternalContoursIssueActionsHeader = observer(function ExternalCont
const issue = contourRequest.issue;
const currentRequestId = contourRequest.id;
const canReviewClosedRequest = contourRequest.status === "closed" && contourRequest.source_decision !== "accepted";
const hasRelativeNavigation = !!currentRequestId && filteredRequestIds.includes(currentRequestId);
const canReviewClosedRequest =
contourRequest.capabilities?.can_source_decide ??
(contourRequest.status === "closed" && contourRequest.source_decision !== "accepted");
const isSourceAccepted = contourRequest.source_decision === "accepted";
const redirectToRelativeIssue = useCallback(
(direction: "next" | "prev") => {
if (!filteredRequestIds || !currentRequestId) return;
if (!filteredRequestIds || !currentRequestId || !hasRelativeNavigation || filteredRequestIds.length <= 1) return;
const currentIssueIndex = filteredRequestIds.findIndex((requestId) => requestId === currentRequestId);
if (currentIssueIndex === -1) return;
const nextIssueIndex =
direction === "next"
? (currentIssueIndex + 1) % filteredRequestIds.length
@@ -65,7 +64,7 @@ export const ExternalContoursIssueActionsHeader = observer(function ExternalCont
if (!nextIssueId) return;
router.push(`/${workspaceSlug}/projects/${sourceProjectId}/external-contours?currentTab=${currentTab}&inboxIssueId=${nextIssueId}`);
},
[currentRequestId, currentTab, filteredRequestIds, router, sourceProjectId, workspaceSlug]
[currentRequestId, currentTab, filteredRequestIds, hasRelativeNavigation, router, sourceProjectId, workspaceSlug]
);
useEffect(() => {
@@ -152,6 +151,7 @@ export const ExternalContoursIssueActionsHeader = observer(function ExternalCont
type="button"
aria-label="Previous request"
onClick={() => redirectToRelativeIssue("prev")}
disabled={!hasRelativeNavigation || filteredRequestIds.length <= 1}
className="nodedc-external-icon-button"
>
<ChevronUpIcon className="size-3.5" />
@@ -160,6 +160,7 @@ export const ExternalContoursIssueActionsHeader = observer(function ExternalCont
type="button"
aria-label="Next request"
onClick={() => redirectToRelativeIssue("next")}
disabled={!hasRelativeNavigation || filteredRequestIds.length <= 1}
className="nodedc-external-icon-button"
>
<ChevronDownIcon className="size-3.5" />
@@ -207,10 +208,6 @@ export const ExternalContoursIssueActionsHeader = observer(function ExternalCont
</Row>
<Header className="justify-start lg:hidden">
<PanelLeft
onClick={() => setIsMobileSidebar(!isMobileSidebar)}
className={`my-auto mr-2 h-4 w-4 flex-shrink-0 ${isMobileSidebar ? "text-accent-primary" : "text-secondary"}`}
/>
<div className="flex w-full items-center gap-2">
<ExternalContourStatePill request={contourRequest} />
<div className="ml-auto flex items-center gap-2">