ФУНКЦИИ - МЕЖПРОЕКТНАЯ КОММУНИКАЦИЯ: причина отклонения, reply и in-app уведомления

This commit is contained in:
DCCONSTRUCTIONS
2026-04-19 09:22:15 +03:00
parent 61a8625a5c
commit 0a584abf26
25 changed files with 623 additions and 32 deletions
@@ -20,6 +20,7 @@ import { useUserPermissions } from "@/hooks/store/user";
import { useWorkspaceIssueProperties } from "@/hooks/use-workspace-issue-properties";
// plane web imports
import { useNotificationPreview } from "@/plane-web/hooks/use-notification-preview";
import { ExternalContoursContentRoot } from "@/plane-web/components/projects/external-contours/content-root";
// local imports
import { InboxContentRoot } from "../inbox/content";
@@ -40,7 +41,7 @@ export const NotificationsRoot = observer(function NotificationsRoot({ workspace
const { fetchUserProjectInfo } = useUserPermissions();
const { isWorkItem, PeekOverviewComponent, setPeekWorkItem } = useNotificationPreview();
// derived values
const { workspace_slug, project_id, issue_id, is_inbox_issue } =
const { workspace_slug, project_id, issue_id, is_inbox_issue, is_external_contour } =
notificationLiteByNotificationId(currentSelectedNotificationId);
// fetching workspace work item properties
@@ -64,10 +65,12 @@ export const NotificationsRoot = observer(function NotificationsRoot({ workspace
// fetching user project member info
const { isLoading: projectMemberInfoLoader } = useSWR(
workspace_slug && project_id && is_inbox_issue
workspace_slug && project_id && (is_inbox_issue || is_external_contour)
? `PROJECT_MEMBER_PERMISSION_INFO_${workspace_slug}_${project_id}`
: null,
workspace_slug && project_id && is_inbox_issue ? () => fetchUserProjectInfo(workspace_slug, project_id) : null
workspace_slug && project_id && (is_inbox_issue || is_external_contour)
? () => fetchUserProjectInfo(workspace_slug, project_id)
: null
);
const embedRemoveCurrentNotification = useCallback(
@@ -91,7 +94,23 @@ export const NotificationsRoot = observer(function NotificationsRoot({ workspace
</div>
) : (
<>
{is_inbox_issue === true && workspace_slug && project_id && issue_id ? (
{is_external_contour === true && workspace_slug && project_id && issue_id ? (
<>
{projectMemberInfoLoader ? (
<div className="flex h-full w-full items-center justify-center">
<LogoSpinner />
</div>
) : (
<ExternalContoursContentRoot
setIsMobileSidebar={() => {}}
isMobileSidebar={false}
workspaceSlug={workspace_slug}
projectId={project_id}
inboxIssueId={issue_id}
/>
)}
</>
) : is_inbox_issue === true && workspace_slug && project_id && issue_id ? (
<>
{projectMemberInfoLoader ? (
<div className="flex h-full w-full items-center justify-center">
@@ -81,10 +81,27 @@ export class ExternalContourService extends APIService {
workspaceSlug: string,
projectId: string,
requestId: string,
action: "accept" | "decline"
action: "accept" | "decline",
comment?: string
): Promise<TExternalContourRequest> {
return this.post(`/api/workspaces/${workspaceSlug}/projects/${projectId}/external-contours/${requestId}/decision/`, {
action,
comment,
})
.then((response) => response?.data)
.catch((error) => {
throw error?.response?.data;
});
}
async reply(
workspaceSlug: string,
projectId: string,
requestId: string,
comment: string
): Promise<TExternalContourRequest> {
return this.post(`/api/workspaces/${workspaceSlug}/projects/${projectId}/external-contours/${requestId}/reply/`, {
comment,
})
.then((response) => response?.data)
.catch((error) => {
@@ -38,7 +38,14 @@ export interface IProjectExternalContoursStore {
workspaceSlug: string,
projectId: string,
requestId: string,
action: "accept" | "decline"
action: "accept" | "decline",
comment?: string
) => Promise<TExternalContourRequest | undefined>;
replyToRequest: (
workspaceSlug: string,
projectId: string,
requestId: string,
comment: string
) => Promise<TExternalContourRequest | undefined>;
fetchTargetProjects: (workspaceSlug: string, projectId: string) => Promise<void>;
fetchTargetOptions: (workspaceSlug: string, projectId: string, targetProjectId: string) => Promise<TExternalContourTargetOptions | undefined>;
@@ -89,6 +96,7 @@ export class ProjectExternalContoursStore implements IProjectExternalContoursSto
fetchRequestById: action,
createRequest: action,
decideRequest: action,
replyToRequest: action,
handleCurrentTab: action,
upsertRequests: action,
updateRequestIssue: action,
@@ -231,11 +239,29 @@ export class ProjectExternalContoursStore implements IProjectExternalContoursSto
workspaceSlug: string,
projectId: string,
requestId: string,
action: "accept" | "decline"
action: "accept" | "decline",
comment?: string
) => {
this.loader = "mutation-loading";
try {
const request = await this.externalContourService.decide(workspaceSlug, projectId, requestId, action);
const request = await this.externalContourService.decide(workspaceSlug, projectId, requestId, action, comment);
runInAction(() => {
this.upsertRequests([request]);
this.loader = undefined;
});
return request;
} catch (error) {
runInAction(() => {
this.loader = undefined;
});
throw error;
}
};
replyToRequest = async (workspaceSlug: string, projectId: string, requestId: string, comment: string) => {
this.loader = "mutation-loading";
try {
const request = await this.externalContourService.reply(workspaceSlug, projectId, requestId, comment);
runInAction(() => {
this.upsertRequests([request]);
this.loader = undefined;
@@ -48,6 +48,7 @@ export class Notification implements INotification {
archived_at: string | undefined = undefined;
snoozed_till: string | undefined = undefined;
is_inbox_issue: boolean | undefined = undefined;
is_external_contour: boolean | undefined = undefined;
is_mentioned_notification: boolean | undefined = undefined;
workspace: string | undefined = undefined;
project: string | undefined = undefined;
@@ -79,6 +80,7 @@ export class Notification implements INotification {
archived_at: observable.ref,
snoozed_till: observable.ref,
is_inbox_issue: observable.ref,
is_external_contour: observable.ref,
is_mentioned_notification: observable.ref,
workspace: observable.ref,
project: observable.ref,
@@ -112,6 +114,7 @@ export class Notification implements INotification {
this.archived_at = this.notification.archived_at;
this.snoozed_till = this.notification.snoozed_till;
this.is_inbox_issue = this.notification.is_inbox_issue;
this.is_external_contour = this.notification.is_external_contour;
this.is_mentioned_notification = this.notification.is_mentioned_notification;
this.workspace = this.notification.workspace;
this.project = this.notification.project;
@@ -143,6 +146,7 @@ export class Notification implements INotification {
archived_at: this.archived_at,
snoozed_till: this.snoozed_till,
is_inbox_issue: this.is_inbox_issue,
is_external_contour: this.is_external_contour,
is_mentioned_notification: this.is_mentioned_notification,
workspace: this.workspace,
project: this.project,
@@ -169,6 +169,7 @@ export class WorkspaceNotificationStore implements IWorkspaceNotificationStore {
notification_id: notification.id,
issue_id: notification.data?.issue?.id,
is_inbox_issue: notification.is_inbox_issue || false,
is_external_contour: notification.is_external_contour || false,
};
});