Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 59 additions & 48 deletions apps/web/core/components/inbox/content/inbox-issue-header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,22 @@

import { useCallback, useEffect, useState } from "react";
import { observer } from "mobx-react";
import { CircleCheck, CircleX, Clock, FileStack, MoveRight } from "lucide-react";
import { Clock, FileStack, MoreHorizontal, MoveRight } from "lucide-react";
// plane imports
import { EUserPermissions, EUserPermissionsLevel } from "@plane/constants";
import { useTranslation } from "@plane/i18n";
import { Button } from "@plane/propel/button";
import { LinkIcon, CopyIcon, NewTabIcon, TrashIcon, ChevronDownIcon, ChevronUpIcon } from "@plane/propel/icons";
import { IconButton, getIconButtonStyling } from "@plane/propel/icon-button";
import {
LinkIcon,
CopyIcon,
NewTabIcon,
TrashIcon,
ChevronDownIcon,
ChevronUpIcon,
CheckCircleFilledIcon,
CloseCircleFilledIcon,
} from "@plane/propel/icons";
import { TOAST_TYPE, setToast } from "@plane/propel/toast";
import type { TNameDescriptionLoader } from "@plane/types";
import { EInboxIssueStatus } from "@plane/types";
Expand Down Expand Up @@ -296,81 +306,82 @@ export const InboxIssueActionsHeader = observer(function InboxIssueActionsHeader
<div className="flex items-center gap-2">
{!isNotificationEmbed && (
<div className="flex items-center gap-x-2">
<button
type="button"
className="rounded-sm border border-subtle p-1.5"
<IconButton
variant="secondary"
size="lg"
icon={ChevronUpIcon}
aria-label="Previous work item"
onClick={() => handleInboxIssueNavigation("prev")}
>
<ChevronUpIcon height={14} width={14} strokeWidth={2} />
</button>
<button
type="button"
className="rounded-sm border border-subtle p-1.5"
/>
<IconButton
variant="secondary"
size="lg"
icon={ChevronDownIcon}
aria-label="Next work item"
onClick={() => handleInboxIssueNavigation("next")}
>
<ChevronDownIcon height={14} width={14} strokeWidth={2} />
</button>
/>
</div>
)}

<div className="flex flex-wrap items-center gap-2">
{canMarkAsAccepted && (
<div className="shrink-0">
<Button
variant="secondary"
prependIcon={<CircleCheck className="h-3 w-3" />}
className="border border-success-strong bg-success-primary text-on-color hover:bg-success-primary focus:bg-success-primary focus:text-success-primary"
onClick={() =>
handleActionWithPermission(
isProjectAdmin,
() => setAcceptIssueModal(true),
t("inbox_issue.errors.accept_permission")
)
}
>
{t("inbox_issue.actions.accept")}
</Button>
</div>
<Button
variant="secondary"
size="lg"
onClick={() =>
handleActionWithPermission(
isProjectAdmin,
() => setAcceptIssueModal(true),
t("inbox_issue.errors.accept_permission")
)
}
>
<CheckCircleFilledIcon className="size-4 shrink-0 text-success-secondary" />
{t("inbox_issue.actions.accept")}
</Button>
)}

{canMarkAsDeclined && (
<div className="shrink-0">
<Button
variant="secondary"
prependIcon={<CircleX className="h-3 w-3" />}
className="border border-danger-strong bg-danger-primary text-on-color hover:bg-danger-primary-hover focus:bg-danger-primary focus:text-danger-primary"
onClick={() =>
handleActionWithPermission(
isProjectAdmin,
() => setDeclineIssueModal(true),
t("inbox_issue.errors.decline_permission")
)
}
>
{t("inbox_issue.actions.decline")}
</Button>
</div>
<Button
variant="secondary"
size="lg"
onClick={() =>
handleActionWithPermission(
isProjectAdmin,
() => setDeclineIssueModal(true),
t("inbox_issue.errors.decline_permission")
)
}
>
<CloseCircleFilledIcon className="size-4 shrink-0 text-danger-secondary" />
{t("inbox_issue.actions.decline")}
</Button>
)}

{isAcceptedOrDeclined ? (
<div className="flex items-center gap-2">
<Button
variant="secondary"
size="lg"
prependIcon={<LinkIcon className="h-2.5 w-2.5" />}
onClick={() => handleCopyIssueLink(workItemLink)}
>
{t("inbox_issue.actions.copy")}
</Button>
<ControlLink href={workItemLink} onClick={() => router.push(workItemLink)} target="_self">
<Button variant="secondary" prependIcon={<NewTabIcon className="h-2.5 w-2.5" />}>
<Button variant="secondary" size="lg" prependIcon={<NewTabIcon className="h-2.5 w-2.5" />}>
{t("inbox_issue.actions.open")}
</Button>
</ControlLink>
</div>
) : (
<>
{isAllowed && (
<CustomMenu verticalEllipsis placement="bottom-start">
<CustomMenu
customButton={<MoreHorizontal className="size-4" />}
customButtonClassName={getIconButtonStyling("secondary", "lg")}
placement="bottom-start"
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CustomMenu is being rendered with an icon-only customButton but without an ariaLabel. Since CustomMenu forwards ariaLabel to the underlying <button aria-label=...>, this control currently has no accessible name for screen readers. Please provide an appropriate ariaLabel (e.g., "More actions").

Suggested change
placement="bottom-start"
placement="bottom-start"
ariaLabel="More actions"

Copilot uses AI. Check for mistakes.
>
{canMarkAsAccepted && (
<CustomMenu.MenuItem
onClick={() =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,17 @@

import React from "react";
import { observer } from "mobx-react";
import { CircleCheck, CircleX, Clock, FileStack, PanelLeft, MoveRight } from "lucide-react";
import { LinkIcon, NewTabIcon, TrashIcon, ChevronDownIcon, ChevronUpIcon } from "@plane/propel/icons";
import { Clock, FileStack, MoreHorizontal, PanelLeft, MoveRight } from "lucide-react";
import { IconButton, getIconButtonStyling } from "@plane/propel/icon-button";
import {
LinkIcon,
NewTabIcon,
TrashIcon,
ChevronDownIcon,
ChevronUpIcon,
CheckCircleFilledIcon,
CloseCircleFilledIcon,
} from "@plane/propel/icons";
import type { TNameDescriptionLoader } from "@plane/types";
import { Header, CustomMenu, EHeaderVariant } from "@plane/ui";
import { cn, findHowManyDaysLeft, generateWorkItemLink } from "@plane/utils";
Expand Down Expand Up @@ -102,20 +111,20 @@ export const InboxIssueActionsMobileHeader = observer(function InboxIssueActions
/>
<div className="z-[15] flex w-full items-center gap-2 bg-surface-1">
<div className="flex items-center gap-x-2">
<button
type="button"
className="rounded-sm border border-subtle p-1.5"
<IconButton
variant="secondary"
size="lg"
icon={ChevronUpIcon}
aria-label="Previous work item"
onClick={() => handleInboxIssueNavigation("prev")}
>
<ChevronUpIcon height={14} width={14} strokeWidth={2} />
</button>
<button
type="button"
className="rounded-sm border border-subtle p-1.5"
/>
<IconButton
variant="secondary"
size="lg"
icon={ChevronDownIcon}
aria-label="Next work item"
onClick={() => handleInboxIssueNavigation("next")}
>
<ChevronDownIcon height={14} width={14} strokeWidth={2} />
</button>
/>
</div>
<div className="flex items-center gap-4">
<InboxIssueStatus inboxIssue={inboxIssue} iconSize={12} />
Expand All @@ -124,7 +133,11 @@ export const InboxIssueActionsMobileHeader = observer(function InboxIssueActions
</div>
</div>
<div className="ml-auto">
<CustomMenu verticalEllipsis placement="bottom-start">
<CustomMenu
customButton={<MoreHorizontal className="size-4" />}
customButtonClassName={getIconButtonStyling("secondary", "lg")}
placement="bottom-start"
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CustomMenu is using an icon-only customButton but no ariaLabel is provided. CustomMenu applies ariaLabel to the trigger button, so without it the menu trigger has no accessible name. Please add an ariaLabel such as "More actions".

Suggested change
placement="bottom-start"
placement="bottom-start"
ariaLabel="More actions"

Copilot uses AI. Check for mistakes.
>
{isAcceptedOrDeclined && (
<CustomMenu.MenuItem onClick={handleCopyIssueLink}>
<div className="flex items-center gap-2">
Expand Down Expand Up @@ -183,8 +196,8 @@ export const InboxIssueActionsMobileHeader = observer(function InboxIssueActions
)
}
>
<div className="flex items-center gap-2 text-success-primary">
<CircleCheck size={14} strokeWidth={2} />
<div className="flex items-center gap-2 text-success-secondary">
<CheckCircleFilledIcon width={14} height={14} />
Accept
</div>
</CustomMenu.MenuItem>
Expand All @@ -199,8 +212,8 @@ export const InboxIssueActionsMobileHeader = observer(function InboxIssueActions
)
}
>
<div className="flex items-center gap-2 text-danger-primary">
<CircleX size={14} strokeWidth={2} />
<div className="flex items-center gap-2 text-danger-secondary">
<CloseCircleFilledIcon width={14} height={14} />
Decline
</div>
</CustomMenu.MenuItem>
Expand Down
16 changes: 8 additions & 8 deletions apps/web/core/components/inbox/inbox-status-icon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,28 +13,28 @@ import { cn } from "@plane/utils";
export const ICON_PROPERTIES = {
[EInboxIssueStatus.PENDING]: {
icon: AlertTriangle,
textColor: (snoozeDatePassed: boolean = false) => (snoozeDatePassed ? "" : "text-[#AB6400]"),
bgColor: (snoozeDatePassed: boolean = false) => (snoozeDatePassed ? "" : "bg-[#FFF7C2]"),
textColor: (snoozeDatePassed: boolean = false) => (snoozeDatePassed ? "" : "text-warning-primary"),
bgColor: (snoozeDatePassed: boolean = false) => (snoozeDatePassed ? "" : "bg-warning-subtle"),
},
[EInboxIssueStatus.DECLINED]: {
icon: XCircle,
textColor: (snoozeDatePassed: boolean = false) => (snoozeDatePassed ? "" : "text-[#CE2C31]"),
bgColor: (snoozeDatePassed: boolean = false) => (snoozeDatePassed ? "" : "bg-[#FEEBEC]"),
textColor: (snoozeDatePassed: boolean = false) => (snoozeDatePassed ? "" : "text-danger-primary"),
bgColor: (snoozeDatePassed: boolean = false) => (snoozeDatePassed ? "" : "bg-danger-subtle"),
},
[EInboxIssueStatus.SNOOZED]: {
icon: Clock,
textColor: (snoozeDatePassed: boolean = false) => (snoozeDatePassed ? "text-danger-primary" : "text-placeholder"),
bgColor: (snoozeDatePassed: boolean = false) => (snoozeDatePassed ? "bg-danger-subtle" : "bg-[#E0E1E6]"),
bgColor: (snoozeDatePassed: boolean = false) => (snoozeDatePassed ? "bg-danger-subtle" : "bg-layer-3"),
},
[EInboxIssueStatus.ACCEPTED]: {
icon: CheckCircle2,
textColor: (snoozeDatePassed: boolean = false) => (snoozeDatePassed ? "" : "text-[#3E9B4F]"),
bgColor: (snoozeDatePassed: boolean = false) => (snoozeDatePassed ? "" : "bg-[#E9F6E9]"),
textColor: (snoozeDatePassed: boolean = false) => (snoozeDatePassed ? "" : "text-success-primary"),
bgColor: (snoozeDatePassed: boolean = false) => (snoozeDatePassed ? "" : "bg-success-subtle"),
},
[EInboxIssueStatus.DUPLICATE]: {
icon: CopyIcon,
textColor: (snoozeDatePassed: boolean = false) => (snoozeDatePassed ? "" : "text-secondary"),
bgColor: (snoozeDatePassed: boolean = false) => (snoozeDatePassed ? "" : "bg-gray-500/10"),
bgColor: (snoozeDatePassed: boolean = false) => (snoozeDatePassed ? "" : "bg-layer-3"),
},
};
export function InboxStatusIcon({
Expand Down
25 changes: 25 additions & 0 deletions packages/propel/src/icons/misc/check-circle-filled-icon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/**
* Copyright (c) 2023-present Plane Software, Inc. and contributors
* SPDX-License-Identifier: AGPL-3.0-only
* See the LICENSE file for details.
*/

import * as React from "react";

import { IconWrapper } from "../icon-wrapper";
import type { ISvgIcons } from "../type";

export const CheckCircleFilledIcon: React.FC<ISvgIcons> = ({ color = "currentColor", ...rest }) => {
const clipPathId = React.useId();

return (
<IconWrapper color={color} clipPathId={clipPathId} {...rest}>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M7.99984 0.666992C3.94975 0.666992 0.666504 3.95024 0.666504 8.00033C0.666504 12.0504 3.94975 15.3337 7.99984 15.3337C12.0499 15.3337 15.3332 12.0504 15.3332 8.00033C15.3332 3.95024 12.0499 0.666992 7.99984 0.666992ZM11.4712 6.47173C11.7316 6.21138 11.7316 5.78927 11.4712 5.52892C11.2109 5.26857 10.7888 5.26857 10.5284 5.52892L6.99984 9.05752L5.47124 7.52892C5.21089 7.26857 4.78878 7.26857 4.52843 7.52892C4.26808 7.78927 4.26808 8.21138 4.52843 8.47173L6.52843 10.4717C6.78878 10.7321 7.21089 10.7321 7.47124 10.4717L11.4712 6.47173Z"
fill={color}
/>
</IconWrapper>
);
};
25 changes: 25 additions & 0 deletions packages/propel/src/icons/misc/close-circle-filled-icon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/**
* Copyright (c) 2023-present Plane Software, Inc. and contributors
* SPDX-License-Identifier: AGPL-3.0-only
* See the LICENSE file for details.
*/

import * as React from "react";

import { IconWrapper } from "../icon-wrapper";
import type { ISvgIcons } from "../type";

export const CloseCircleFilledIcon: React.FC<ISvgIcons> = ({ color = "currentColor", ...rest }) => {
const clipPathId = React.useId();

return (
<IconWrapper color={color} clipPathId={clipPathId} {...rest}>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M7.99984 0.666992C3.94975 0.666992 0.666504 3.95024 0.666504 8.00033C0.666504 12.0504 3.94975 15.3337 7.99984 15.3337C12.0499 15.3337 15.3332 12.0504 15.3332 8.00033C15.3332 3.95024 12.0499 0.666992 7.99984 0.666992ZM10.4712 5.52892C10.7316 5.78927 10.7316 6.21138 10.4712 6.47173L8.94265 8.00033L10.4712 9.52892C10.7316 9.78927 10.7316 10.2114 10.4712 10.4717C10.2109 10.7321 9.78878 10.7321 9.52843 10.4717L7.99984 8.94313L6.47124 10.4717C6.21089 10.7321 5.78878 10.7321 5.52843 10.4717C5.26808 10.2114 5.26808 9.78927 5.52843 9.52892L7.05703 8.00033L5.52843 6.47173C5.26808 6.21138 5.26808 5.78927 5.52843 5.52892C5.78878 5.26857 6.21089 5.26857 6.47124 5.52892L7.99984 7.05752L9.52843 5.52892C9.78878 5.26857 10.2109 5.26857 10.4712 5.52892Z"
fill={color}
/>
</IconWrapper>
);
};
2 changes: 2 additions & 0 deletions packages/propel/src/icons/misc/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,5 @@
*/

export * from "./info-icon";
export * from "./check-circle-filled-icon";
export * from "./close-circle-filled-icon";
Loading