UI - МЕЖПРОЕКТНАЯ КОММУНИКАЦИЯ: миграция mobile header, intake actions и member filters на общий канон
This commit is contained in:
@@ -25,7 +25,7 @@ import {
|
||||
import { TOAST_TYPE, setToast } from "@plane/propel/toast";
|
||||
import type { TNameDescriptionLoader } from "@plane/types";
|
||||
import { EInboxIssueStatus } from "@plane/types";
|
||||
import { ControlLink, CustomMenu } from "@plane/ui";
|
||||
import { ActionDropdown, ControlLink } from "@plane/ui";
|
||||
import { copyUrlToClipboard, findHowManyDaysLeft, generateWorkItemLink } from "@plane/utils";
|
||||
// components
|
||||
import { CreateUpdateIssueModal } from "@/components/issues/issue-modal/modal";
|
||||
@@ -348,61 +348,68 @@ export const InboxIssueActionsHeader = observer(function InboxIssueActionsHeader
|
||||
) : (
|
||||
<>
|
||||
{isAllowed && (
|
||||
<CustomMenu
|
||||
customButton={<MoreHorizontal className="size-4" />}
|
||||
customButtonClassName="nodedc-external-icon-button"
|
||||
<ActionDropdown
|
||||
button={<MoreHorizontal className="size-4" />}
|
||||
buttonClassName="nodedc-external-icon-button"
|
||||
placement="bottom-start"
|
||||
menuItemsClassName="z-[760]"
|
||||
>
|
||||
{canMarkAsAccepted && (
|
||||
<CustomMenu.MenuItem
|
||||
onClick={() =>
|
||||
items={[
|
||||
{
|
||||
key: "snooze",
|
||||
shouldRender: canMarkAsAccepted,
|
||||
action: () =>
|
||||
handleActionWithPermission(
|
||||
isProjectAdmin,
|
||||
handleIssueSnoozeAction,
|
||||
t("inbox_issue.errors.snooze_permission")
|
||||
)
|
||||
}
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<Clock size={14} strokeWidth={2} />
|
||||
{inboxIssue?.snoozed_till && numberOfDaysLeft && numberOfDaysLeft > 0
|
||||
? t("inbox_issue.actions.unsnooze")
|
||||
: t("inbox_issue.actions.snooze")}
|
||||
</div>
|
||||
</CustomMenu.MenuItem>
|
||||
)}
|
||||
{canMarkAsDuplicate && (
|
||||
<CustomMenu.MenuItem
|
||||
onClick={() =>
|
||||
),
|
||||
customContent: (
|
||||
<div className="flex items-center gap-2">
|
||||
<Clock size={14} strokeWidth={2} />
|
||||
{inboxIssue?.snoozed_till && numberOfDaysLeft && numberOfDaysLeft > 0
|
||||
? t("inbox_issue.actions.unsnooze")
|
||||
: t("inbox_issue.actions.snooze")}
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: "duplicate",
|
||||
shouldRender: canMarkAsDuplicate,
|
||||
action: () =>
|
||||
handleActionWithPermission(
|
||||
isProjectAdmin,
|
||||
() => setSelectDuplicateIssue(true),
|
||||
t("inbox_issue.errors.duplicate_permission")
|
||||
)
|
||||
}
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<FileStack size={14} strokeWidth={2} />
|
||||
{t("inbox_issue.actions.mark_as_duplicate")}
|
||||
</div>
|
||||
</CustomMenu.MenuItem>
|
||||
)}
|
||||
<CustomMenu.MenuItem onClick={() => handleCopyIssueLink(workItemLink)}>
|
||||
<div className="flex items-center gap-2">
|
||||
<CopyIcon width={14} height={14} strokeWidth={2} />
|
||||
{t("inbox_issue.actions.copy")}
|
||||
</div>
|
||||
</CustomMenu.MenuItem>
|
||||
{canDelete && (
|
||||
<CustomMenu.MenuItem onClick={() => setDeleteIssueModal(true)}>
|
||||
<div className="flex items-center gap-2">
|
||||
<TrashIcon width={14} height={14} strokeWidth={2} />
|
||||
{t("inbox_issue.actions.delete")}
|
||||
</div>
|
||||
</CustomMenu.MenuItem>
|
||||
)}
|
||||
</CustomMenu>
|
||||
),
|
||||
customContent: (
|
||||
<div className="flex items-center gap-2">
|
||||
<FileStack size={14} strokeWidth={2} />
|
||||
{t("inbox_issue.actions.mark_as_duplicate")}
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: "copy",
|
||||
action: () => handleCopyIssueLink(workItemLink),
|
||||
customContent: (
|
||||
<div className="flex items-center gap-2">
|
||||
<CopyIcon width={14} height={14} strokeWidth={2} />
|
||||
{t("inbox_issue.actions.copy")}
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: "delete",
|
||||
shouldRender: canDelete,
|
||||
action: () => setDeleteIssueModal(true),
|
||||
customContent: (
|
||||
<div className="flex items-center gap-2">
|
||||
<TrashIcon width={14} height={14} strokeWidth={2} />
|
||||
{t("inbox_issue.actions.delete")}
|
||||
</div>
|
||||
),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
||||
@@ -19,7 +19,7 @@ import {
|
||||
} from "@plane/propel/icons";
|
||||
import type { TNameDescriptionLoader } from "@plane/types";
|
||||
|
||||
import { Header, CustomMenu, EHeaderVariant } from "@plane/ui";
|
||||
import { ActionDropdown, Header, EHeaderVariant } from "@plane/ui";
|
||||
import { cn, findHowManyDaysLeft, generateWorkItemLink } from "@plane/utils";
|
||||
// components
|
||||
import { NameDescriptionUpdateStatus } from "@/components/issues/issue-update-status";
|
||||
@@ -136,102 +136,112 @@ export const InboxIssueActionsMobileHeader = observer(function InboxIssueActions
|
||||
</div>
|
||||
</div>
|
||||
<div className="ml-auto">
|
||||
<CustomMenu
|
||||
customButton={<MoreHorizontal className="size-4" />}
|
||||
customButtonClassName={getIconButtonStyling("secondary", "lg")}
|
||||
<ActionDropdown
|
||||
button={<MoreHorizontal className="size-4" />}
|
||||
buttonClassName={getIconButtonStyling("secondary", "lg")}
|
||||
placement="bottom-start"
|
||||
>
|
||||
{isAcceptedOrDeclined && (
|
||||
<CustomMenu.MenuItem onClick={handleCopyIssueLink}>
|
||||
<div className="flex items-center gap-2">
|
||||
<LinkIcon width={14} height={14} strokeWidth={2} />
|
||||
{t("inbox_issue.actions.copy")}
|
||||
</div>
|
||||
</CustomMenu.MenuItem>
|
||||
)}
|
||||
{isAcceptedOrDeclined && (
|
||||
<CustomMenu.MenuItem onClick={() => router.push(workItemLink)}>
|
||||
<div className="flex items-center gap-2">
|
||||
<NewTabIcon width={14} height={14} strokeWidth={2} />
|
||||
{t("inbox_issue.actions.open")}
|
||||
</div>
|
||||
</CustomMenu.MenuItem>
|
||||
)}
|
||||
{canMarkAsAccepted && !isAcceptedOrDeclined && (
|
||||
<CustomMenu.MenuItem
|
||||
onClick={() =>
|
||||
items={[
|
||||
{
|
||||
key: "copy",
|
||||
shouldRender: !!isAcceptedOrDeclined,
|
||||
action: handleCopyIssueLink,
|
||||
customContent: (
|
||||
<div className="flex items-center gap-2">
|
||||
<LinkIcon width={14} height={14} strokeWidth={2} />
|
||||
{t("inbox_issue.actions.copy")}
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: "open",
|
||||
shouldRender: !!isAcceptedOrDeclined,
|
||||
action: () => router.push(workItemLink),
|
||||
customContent: (
|
||||
<div className="flex items-center gap-2">
|
||||
<NewTabIcon width={14} height={14} strokeWidth={2} />
|
||||
{t("inbox_issue.actions.open")}
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: "snooze",
|
||||
shouldRender: canMarkAsAccepted && !isAcceptedOrDeclined,
|
||||
action: () =>
|
||||
handleActionWithPermission(
|
||||
isProjectAdmin,
|
||||
handleIssueSnoozeAction,
|
||||
t("inbox_issue.errors.snooze_permission")
|
||||
)
|
||||
}
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<Clock size={14} strokeWidth={2} />
|
||||
{inboxIssue?.snoozed_till && numberOfDaysLeft && numberOfDaysLeft > 0
|
||||
? t("inbox_issue.actions.unsnooze")
|
||||
: t("inbox_issue.actions.snooze")}
|
||||
</div>
|
||||
</CustomMenu.MenuItem>
|
||||
)}
|
||||
{canMarkAsDuplicate && !isAcceptedOrDeclined && (
|
||||
<CustomMenu.MenuItem
|
||||
onClick={() =>
|
||||
),
|
||||
customContent: (
|
||||
<div className="flex items-center gap-2">
|
||||
<Clock size={14} strokeWidth={2} />
|
||||
{inboxIssue?.snoozed_till && numberOfDaysLeft && numberOfDaysLeft > 0
|
||||
? t("inbox_issue.actions.unsnooze")
|
||||
: t("inbox_issue.actions.snooze")}
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: "duplicate",
|
||||
shouldRender: canMarkAsDuplicate && !isAcceptedOrDeclined,
|
||||
action: () =>
|
||||
handleActionWithPermission(
|
||||
isProjectAdmin,
|
||||
() => setSelectDuplicateIssue(true),
|
||||
t("inbox_issue.errors.duplicate_permission")
|
||||
)
|
||||
}
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<FileStack size={14} strokeWidth={2} />
|
||||
{t("inbox_issue.actions.mark_as_duplicate")}
|
||||
</div>
|
||||
</CustomMenu.MenuItem>
|
||||
)}
|
||||
{canMarkAsAccepted && (
|
||||
<CustomMenu.MenuItem
|
||||
onClick={() =>
|
||||
),
|
||||
customContent: (
|
||||
<div className="flex items-center gap-2">
|
||||
<FileStack size={14} strokeWidth={2} />
|
||||
{t("inbox_issue.actions.mark_as_duplicate")}
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: "accept",
|
||||
shouldRender: canMarkAsAccepted,
|
||||
action: () =>
|
||||
handleActionWithPermission(
|
||||
isProjectAdmin,
|
||||
() => setAcceptIssueModal(true),
|
||||
t("inbox_issue.errors.accept_permission")
|
||||
)
|
||||
}
|
||||
>
|
||||
<div className="flex items-center gap-2 text-success-secondary">
|
||||
<CheckCircleFilledIcon width={14} height={14} />
|
||||
{t("inbox_issue.actions.accept")}
|
||||
</div>
|
||||
</CustomMenu.MenuItem>
|
||||
)}
|
||||
{canMarkAsDeclined && (
|
||||
<CustomMenu.MenuItem
|
||||
onClick={() =>
|
||||
),
|
||||
customContent: (
|
||||
<div className="flex items-center gap-2 text-success-secondary">
|
||||
<CheckCircleFilledIcon width={14} height={14} />
|
||||
{t("inbox_issue.actions.accept")}
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: "decline",
|
||||
shouldRender: canMarkAsDeclined,
|
||||
action: () =>
|
||||
handleActionWithPermission(
|
||||
isProjectAdmin,
|
||||
() => setDeclineIssueModal(true),
|
||||
t("inbox_issue.errors.decline_permission")
|
||||
)
|
||||
}
|
||||
>
|
||||
<div className="flex items-center gap-2 text-danger-secondary">
|
||||
<CloseCircleFilledIcon width={14} height={14} />
|
||||
{t("inbox_issue.actions.decline")}
|
||||
</div>
|
||||
</CustomMenu.MenuItem>
|
||||
)}
|
||||
{canDelete && !isAcceptedOrDeclined && (
|
||||
<CustomMenu.MenuItem onClick={() => setDeleteIssueModal(true)}>
|
||||
<div className="flex items-center gap-2 text-danger-primary">
|
||||
<TrashIcon height={14} width={14} strokeWidth={2} />
|
||||
{t("inbox_issue.actions.delete")}
|
||||
</div>
|
||||
</CustomMenu.MenuItem>
|
||||
)}
|
||||
</CustomMenu>
|
||||
),
|
||||
customContent: (
|
||||
<div className="flex items-center gap-2 text-danger-secondary">
|
||||
<CloseCircleFilledIcon width={14} height={14} />
|
||||
{t("inbox_issue.actions.decline")}
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: "delete",
|
||||
shouldRender: canDelete && !isAcceptedOrDeclined,
|
||||
action: () => setDeleteIssueModal(true),
|
||||
customContent: (
|
||||
<div className="flex items-center gap-2 text-danger-primary">
|
||||
<TrashIcon height={14} width={14} strokeWidth={2} />
|
||||
{t("inbox_issue.actions.delete")}
|
||||
</div>
|
||||
),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Header>
|
||||
|
||||
@@ -8,13 +8,11 @@ import { useState } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
// plane imports
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { Button } from "@plane/propel/button";
|
||||
import { ChevronDownIcon } from "@plane/propel/icons";
|
||||
import { EUserProjectRoles, EUserWorkspaceRoles } from "@plane/types";
|
||||
// plane ui
|
||||
import { CustomMenu } from "@plane/ui";
|
||||
// components
|
||||
import { FilterHeader, FilterOption } from "@/components/issues/issue-layouts/filters";
|
||||
import { FilterHeader, FilterOption, FiltersDropdown } from "@/components/issues/issue-layouts/filters";
|
||||
|
||||
interface IRoleOption {
|
||||
value: string;
|
||||
@@ -101,13 +99,11 @@ export const MemberListFiltersDropdown = observer(function MemberListFiltersDrop
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<CustomMenu
|
||||
customButton={
|
||||
<div className="relative">
|
||||
<Button variant="ghost" size="lg" className="nodedc-settings-secondary-button flex items-center gap-2">
|
||||
<FiltersDropdown
|
||||
menuButton={
|
||||
<div className="nodedc-settings-secondary-button relative flex items-center gap-2">
|
||||
<span>{t("filters")}</span>
|
||||
<ChevronDownIcon className="h-3 w-3" />
|
||||
</Button>
|
||||
{appliedFiltersCount > 0 && (
|
||||
<div className="absolute -top-1 -right-1 h-2 w-2 rounded-full bg-accent-primary" />
|
||||
)}
|
||||
@@ -115,7 +111,9 @@ export const MemberListFiltersDropdown = observer(function MemberListFiltersDrop
|
||||
}
|
||||
placement="bottom-start"
|
||||
>
|
||||
<MemberListFilters appliedFilters={appliedFilters} handleUpdate={handleUpdate} memberType={memberType} />
|
||||
</CustomMenu>
|
||||
<div className="vertical-scrollbar relative h-full w-full overflow-y-auto px-2.5 py-2">
|
||||
<MemberListFilters appliedFilters={appliedFilters} handleUpdate={handleUpdate} memberType={memberType} />
|
||||
</div>
|
||||
</FiltersDropdown>
|
||||
);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user