[WEB-6702] feat: redesign intake action buttons and use design tokens (#8801)

* feat: intake action buttons redesign

* chore: code refactoring
This commit is contained in:
Anmol Singh Bhatia 2026-03-26 18:12:24 +05:30 committed by GitHub
parent d94a269451
commit 942d2b98ef
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 156 additions and 78 deletions

View file

@ -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";
@ -67,7 +77,8 @@ export const InboxIssueActionsHeader = observer(function InboxIssueActionsHeader
const { currentTab, deleteInboxIssue, filteredInboxIssueIds } = useProjectInbox();
const { data: currentUser } = useUser();
const { allowPermissions } = useUserPermissions();
const { currentProjectDetails } = useProject();
const { getPartialProjectById } = useProject();
const currentProjectDetails = getPartialProjectById(projectId);
const { t } = useTranslation();
const router = useAppRouter();
@ -296,73 +307,70 @@ 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>
@ -370,7 +378,11 @@ export const InboxIssueActionsHeader = observer(function InboxIssueActionsHeader
) : (
<>
{isAllowed && (
<CustomMenu verticalEllipsis placement="bottom-start">
<CustomMenu
customButton={<MoreHorizontal className="size-4" />}
customButtonClassName={getIconButtonStyling("secondary", "lg")}
placement="bottom-start"
>
{canMarkAsAccepted && (
<CustomMenu.MenuItem
onClick={() =>

View file

@ -4,11 +4,20 @@
* See the LICENSE file for details.
*/
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";
// components
@ -18,6 +27,7 @@ import { useProject } from "@/hooks/store/use-project";
import { useAppRouter } from "@/hooks/use-app-router";
// store types
import type { IInboxIssueStore } from "@/store/inbox/inbox-issue.store";
// local imports
import { InboxIssueStatus } from "../inbox-issue-status";
@ -102,20 +112,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} />
@ -124,7 +134,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"
>
{isAcceptedOrDeclined && (
<CustomMenu.MenuItem onClick={handleCopyIssueLink}>
<div className="flex items-center gap-2">
@ -183,8 +197,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>
@ -199,8 +213,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>
@ -208,7 +222,7 @@ export const InboxIssueActionsMobileHeader = observer(function InboxIssueActions
{canDelete && !isAcceptedOrDeclined && (
<CustomMenu.MenuItem onClick={() => setDeleteIssueModal(true)}>
<div className="flex items-center gap-2 text-danger-primary">
<TrashIcon width={14} height={14} strokeWidth={2} />
<TrashIcon height={14} width={14} strokeWidth={2} />
Delete
</div>
</CustomMenu.MenuItem>

View file

@ -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({