UI - МЕЖПРОЕКТНАЯ КОММУНИКАЦИЯ: перевод внешних контуров на shell предложений

This commit is contained in:
DCCONSTRUCTIONS
2026-04-18 21:04:29 +03:00
parent c76f519488
commit 390bcdbf38
20 changed files with 1586 additions and 96 deletions
@@ -217,7 +217,7 @@ export const InboxIssueActionsHeader = observer(function InboxIssueActionsHeader
else {
setToast({
type: TOAST_TYPE.ERROR,
title: "Permission denied",
title: t("permission_denied"),
message: errorMessage,
});
}
@@ -407,7 +407,7 @@ export const InboxIssueActionsHeader = observer(function InboxIssueActionsHeader
handleActionWithPermission(
isProjectAdmin,
() => setSelectDuplicateIssue(true),
"Only project admins can mark work item as duplicate"
t("inbox_issue.errors.duplicate_permission")
)
}
>
@@ -6,6 +6,7 @@
import { observer } from "mobx-react";
import { Clock, FileStack, MoreHorizontal, PanelLeft, MoveRight } from "lucide-react";
import { useTranslation } from "@plane/i18n";
import { IconButton, getIconButtonStyling } from "@plane/propel/icon-button";
import {
LinkIcon,
@@ -79,6 +80,7 @@ export const InboxIssueActionsMobileHeader = observer(function InboxIssueActions
isProjectAdmin,
handleActionWithPermission,
} = props;
const { t } = useTranslation();
const router = useAppRouter();
const { getProjectIdentifierById } = useProject();
@@ -143,7 +145,7 @@ export const InboxIssueActionsMobileHeader = observer(function InboxIssueActions
<CustomMenu.MenuItem onClick={handleCopyIssueLink}>
<div className="flex items-center gap-2">
<LinkIcon width={14} height={14} strokeWidth={2} />
Copy work item link
{t("inbox_issue.actions.copy")}
</div>
</CustomMenu.MenuItem>
)}
@@ -151,7 +153,7 @@ export const InboxIssueActionsMobileHeader = observer(function InboxIssueActions
<CustomMenu.MenuItem onClick={() => router.push(workItemLink)}>
<div className="flex items-center gap-2">
<NewTabIcon width={14} height={14} strokeWidth={2} />
Open work item
{t("inbox_issue.actions.open")}
</div>
</CustomMenu.MenuItem>
)}
@@ -161,13 +163,15 @@ export const InboxIssueActionsMobileHeader = observer(function InboxIssueActions
handleActionWithPermission(
isProjectAdmin,
handleIssueSnoozeAction,
"Only project admins can snooze/Un-snooze work items"
t("inbox_issue.errors.snooze_permission")
)
}
>
<div className="flex items-center gap-2">
<Clock size={14} strokeWidth={2} />
{inboxIssue?.snoozed_till && numberOfDaysLeft && numberOfDaysLeft > 0 ? "Un-snooze" : "Snooze"}
{inboxIssue?.snoozed_till && numberOfDaysLeft && numberOfDaysLeft > 0
? t("inbox_issue.actions.unsnooze")
: t("inbox_issue.actions.snooze")}
</div>
</CustomMenu.MenuItem>
)}
@@ -177,13 +181,13 @@ export const InboxIssueActionsMobileHeader = observer(function InboxIssueActions
handleActionWithPermission(
isProjectAdmin,
() => setSelectDuplicateIssue(true),
"Only project admins can mark work items as duplicate"
t("inbox_issue.errors.duplicate_permission")
)
}
>
<div className="flex items-center gap-2">
<FileStack size={14} strokeWidth={2} />
Mark as duplicate
{t("inbox_issue.actions.mark_as_duplicate")}
</div>
</CustomMenu.MenuItem>
)}
@@ -193,13 +197,13 @@ export const InboxIssueActionsMobileHeader = observer(function InboxIssueActions
handleActionWithPermission(
isProjectAdmin,
() => setAcceptIssueModal(true),
"Only project admins can accept work items"
t("inbox_issue.errors.accept_permission")
)
}
>
<div className="flex items-center gap-2 text-success-secondary">
<CheckCircleFilledIcon width={14} height={14} />
Accept
{t("inbox_issue.actions.accept")}
</div>
</CustomMenu.MenuItem>
)}
@@ -209,13 +213,13 @@ export const InboxIssueActionsMobileHeader = observer(function InboxIssueActions
handleActionWithPermission(
isProjectAdmin,
() => setDeclineIssueModal(true),
"Only project admins can deny work items"
t("inbox_issue.errors.decline_permission")
)
}
>
<div className="flex items-center gap-2 text-danger-secondary">
<CloseCircleFilledIcon width={14} height={14} />
Decline
{t("inbox_issue.actions.decline")}
</div>
</CustomMenu.MenuItem>
)}
@@ -223,7 +227,7 @@ export const InboxIssueActionsMobileHeader = observer(function InboxIssueActions
<CustomMenu.MenuItem onClick={() => setDeleteIssueModal(true)}>
<div className="flex items-center gap-2 text-danger-primary">
<TrashIcon height={14} width={14} strokeWidth={2} />
Delete
{t("inbox_issue.actions.delete")}
</div>
</CustomMenu.MenuItem>
)}
@@ -5,6 +5,7 @@
*/
import { observer } from "mobx-react";
import { useTranslation } from "@plane/i18n";
import {
StatePropertyIcon,
MembersPropertyIcon,
@@ -44,6 +45,7 @@ export const InboxIssueContentProperties = observer(function InboxIssueContentPr
props;
const router = useAppRouter();
const { t } = useTranslation();
// store hooks
const { currentProjectDetails } = useProject();
@@ -63,14 +65,14 @@ export const InboxIssueContentProperties = observer(function InboxIssueContentPr
return (
<div className="flex w-full flex-col divide-y-2 divide-subtle-1">
<div className="w-full overflow-y-auto">
<h5 className="mb-2 text-body-sm-medium">Properties</h5>
<h5 className="mb-2 text-body-sm-medium">{t("properties")}</h5>
<div className={`divide-y-2 divide-subtle-1 ${!isEditable ? "opacity-60" : ""}`}>
<div className="flex flex-col gap-3">
{/* Intake State */}
<div className="flex h-8 items-center gap-2">
<div className="flex w-2/5 flex-shrink-0 items-center gap-1 text-13 text-tertiary">
<StatePropertyIcon className="h-4 w-4 flex-shrink-0" />
<span>State</span>
<span>{t("state")}</span>
</div>
{issue?.state_id && (
<DropdownComponent
@@ -91,7 +93,7 @@ export const InboxIssueContentProperties = observer(function InboxIssueContentPr
<div className="flex h-8 items-center gap-2">
<div className="flex w-2/5 flex-shrink-0 items-center gap-1 text-13 text-tertiary">
<MembersPropertyIcon className="h-4 w-4 flex-shrink-0" />
<span>Assignees</span>
<span>{t("assignees")}</span>
</div>
<MemberDropdown
value={issue?.assignee_ids ?? []}
@@ -100,7 +102,7 @@ export const InboxIssueContentProperties = observer(function InboxIssueContentPr
}
disabled={!isEditable}
projectId={projectId?.toString() ?? ""}
placeholder="Add assignees"
placeholder={t("assignee")}
multiple
buttonVariant={
(issue?.assignee_ids || [])?.length > 0 ? "transparent-without-text" : "transparent-with-text"
@@ -119,7 +121,7 @@ export const InboxIssueContentProperties = observer(function InboxIssueContentPr
<div className="flex h-8 items-center gap-2">
<div className="flex w-2/5 flex-shrink-0 items-center gap-1 text-13 text-tertiary">
<PriorityPropertyIcon className="h-4 w-4 flex-shrink-0" />
<span>Priority</span>
<span>{t("priority")}</span>
</div>
<PriorityDropdown
value={issue?.priority}
@@ -141,10 +143,10 @@ export const InboxIssueContentProperties = observer(function InboxIssueContentPr
<div className="flex h-8 items-center gap-2">
<div className="flex w-2/5 flex-shrink-0 items-center gap-1 text-13 text-tertiary">
<DueDatePropertyIcon className="h-4 w-4 flex-shrink-0" />
<span>Due date</span>
<span>{t("due_date")}</span>
</div>
<DateDropdown
placeholder="Add due date"
placeholder={t("issues.properties.add_due_date")}
value={issue.target_date || null}
onChange={(val) =>
issue?.id &&
@@ -166,7 +168,7 @@ export const InboxIssueContentProperties = observer(function InboxIssueContentPr
<div className="flex min-h-8 items-center gap-2">
<div className="flex w-2/5 flex-shrink-0 items-center gap-1 text-13 text-tertiary">
<LabelPropertyIcon className="h-4 w-4 flex-shrink-0" />
<span>Labels</span>
<span>{t("labels")}</span>
</div>
<div className="h-full min-h-8 w-3/5 flex-grow pt-1">
{issue?.id && (
@@ -187,10 +189,10 @@ export const InboxIssueContentProperties = observer(function InboxIssueContentPr
{/* duplicate to*/}
{duplicateIssueDetails && (
<div className="flex min-h-8 gap-2">
<div className="flex w-2/5 flex-shrink-0 gap-1 pt-2 text-13 text-tertiary">
<DuplicatePropertyIcon className="h-4 w-4 flex-shrink-0" />
<span>Duplicate of</span>
</div>
<div className="flex w-2/5 flex-shrink-0 gap-1 pt-2 text-13 text-tertiary">
<DuplicatePropertyIcon className="h-4 w-4 flex-shrink-0" />
<span>{t("issues.properties.duplicate_of")}</span>
</div>
<ControlLink
href={duplicateWorkItemLink}
@@ -7,6 +7,7 @@
import type { Dispatch, SetStateAction } from "react";
import { useEffect, useMemo, useRef } from "react";
import { observer } from "mobx-react";
import { useTranslation } from "@plane/i18n";
// plane imports
import type { EditorRefApi } from "@plane/editor";
import { TOAST_TYPE, setToast } from "@plane/propel/toast";
@@ -52,6 +53,7 @@ type Props = {
export const InboxIssueMainContent = observer(function InboxIssueMainContent(props: Props) {
const { workspaceSlug, projectId, inboxIssue, isEditable, isSubmitting, setIsSubmitting } = props;
const { t } = useTranslation();
// refs
const editorRef = useRef<EditorRefApi>(null);
// store hooks
@@ -101,16 +103,16 @@ export const InboxIssueMainContent = observer(function InboxIssueMainContent(pro
try {
await removeIssue(workspaceSlug, projectId, _issueId);
setToast({
title: "Success!",
title: t("success"),
type: TOAST_TYPE.SUCCESS,
message: "Work item deleted successfully",
message: t("inbox_issue.modals.delete.success"),
});
} catch (error) {
console.log("Error in deleting work item:", error);
setToast({
title: "Error!",
title: t("error"),
type: TOAST_TYPE.ERROR,
message: "Work item delete failed",
message: t("something_went_wrong_please_try_again"),
});
}
},
@@ -119,9 +121,9 @@ export const InboxIssueMainContent = observer(function InboxIssueMainContent(pro
await inboxIssue.updateIssue(data);
} catch (_error) {
setToast({
title: "Work item update failed",
title: t("error"),
type: TOAST_TYPE.ERROR,
message: "Work item update failed",
message: t("issue_could_not_be_updated"),
});
}
},
@@ -206,7 +208,7 @@ export const InboxIssueMainContent = observer(function InboxIssueMainContent(pro
createdAt: issue.created_at ? new Date(issue.created_at) : new Date(),
createdByDisplayName:
inboxIssue.source === EInboxIssueSource.FORMS
? "Intake Form user"
? t("inbox_issue.source.form_user")
: (getUserDetails(issue.created_by ?? "")?.display_name ?? ""),
id: issue.id,
isRestoreDisabled: !isEditable,
@@ -161,11 +161,10 @@ export const InboxSidebar = observer(function InboxSidebar(props: IInboxSidebarP
rootClassName="px-page-x"
/>
) : (
// TODO: Add translation
<EmptyStateDetailed
assetKey="inbox"
title="No request closed yet"
description="All the work items whether accepted or declined can be found here."
title={t("inbox_issue.empty_state.sidebar_closed_tab.title")}
description={t("inbox_issue.empty_state.sidebar_closed_tab.description")}
assetClassName="size-20"
className="px-10"
/>