[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:
parent
d94a269451
commit
942d2b98ef
6 changed files with 156 additions and 78 deletions
|
|
@ -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={() =>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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({
|
||||
|
|
|
|||
25
packages/propel/src/icons/misc/check-circle-filled-icon.tsx
Normal file
25
packages/propel/src/icons/misc/check-circle-filled-icon.tsx
Normal file
|
|
@ -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
packages/propel/src/icons/misc/close-circle-filled-icon.tsx
Normal file
25
packages/propel/src/icons/misc/close-circle-filled-icon.tsx
Normal file
|
|
@ -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>
|
||||
);
|
||||
};
|
||||
|
|
@ -4,4 +4,6 @@
|
|||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
export * from "./check-circle-filled-icon";
|
||||
export * from "./close-circle-filled-icon";
|
||||
export * from "./info-icon";
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue