feat: set icons for each type of notification (#17115)

Each notification type will have an icon to represent the context:

<img width="503" alt="Screenshot 2025-03-26 at 13 44 35"
src="https://github.com/user-attachments/assets/1187c1c0-1043-4a32-b105-a7f91b52f8ca"
/>

This depends on https://github.com/coder/coder/pull/17013
This commit is contained in:
Bruno Quaresma
2025-03-31 09:40:24 -03:00
committed by GitHub
parent 9bc727e977
commit 489641d0be
11 changed files with 154 additions and 54 deletions
+12 -12
View File
@@ -839,18 +839,6 @@ export interface ExternalAuthUser {
readonly name: string;
}
// From codersdk/inboxnotification.go
export const FallbackIconAccount = "DEFAULT_ICON_ACCOUNT";
// From codersdk/inboxnotification.go
export const FallbackIconOther = "DEFAULT_ICON_OTHER";
// From codersdk/inboxnotification.go
export const FallbackIconTemplate = "DEFAULT_ICON_TEMPLATE";
// From codersdk/inboxnotification.go
export const FallbackIconWorkspace = "DEFAULT_ICON_WORKSPACE";
// From codersdk/deployment.go
export interface Feature {
readonly entitlement: Entitlement;
@@ -1124,6 +1112,18 @@ export interface InboxNotificationAction {
readonly url: string;
}
// From codersdk/inboxnotification.go
export const InboxNotificationFallbackIconAccount = "DEFAULT_ICON_ACCOUNT";
// From codersdk/inboxnotification.go
export const InboxNotificationFallbackIconOther = "DEFAULT_ICON_OTHER";
// From codersdk/inboxnotification.go
export const InboxNotificationFallbackIconTemplate = "DEFAULT_ICON_TEMPLATE";
// From codersdk/inboxnotification.go
export const InboxNotificationFallbackIconWorkspace = "DEFAULT_ICON_WORKSPACE";
// From codersdk/insights.go
export type InsightsReportInterval = "day" | "week";
+1 -1
View File
@@ -28,7 +28,7 @@ const avatarVariants = cva(
},
variant: {
default: null,
icon: null,
icon: "[&_svg]:size-full",
},
},
defaultVariants: {
@@ -0,0 +1,46 @@
import type { Meta, StoryObj } from "@storybook/react";
import { InboxAvatar } from "./InboxAvatar";
const meta: Meta<typeof InboxAvatar> = {
title: "modules/notifications/NotificationsInbox/InboxAvatar",
component: InboxAvatar,
};
export default meta;
type Story = StoryObj<typeof InboxAvatar>;
export const Custom: Story = {
args: {
icon: "/icon/git.svg",
},
};
export const EmptyIcon: Story = {
args: {
icon: "",
},
};
export const FallbackWorkspace: Story = {
args: {
icon: "DEFAULT_ICON_WORKSPACE",
},
};
export const FallbackAccount: Story = {
args: {
icon: "DEFAULT_ICON_ACCOUNT",
},
};
export const FallbackTemplate: Story = {
args: {
icon: "DEFAULT_ICON_TEMPLATE",
},
};
export const FallbackOther: Story = {
args: {
icon: "DEFAULT_ICON_OTHER",
},
};
@@ -0,0 +1,54 @@
import {
InboxNotificationFallbackIconAccount,
InboxNotificationFallbackIconOther,
InboxNotificationFallbackIconTemplate,
InboxNotificationFallbackIconWorkspace,
} from "api/typesGenerated";
import { Avatar } from "components/Avatar/Avatar";
import {
InfoIcon,
LaptopIcon,
LayoutTemplateIcon,
UserIcon,
} from "lucide-react";
import type { FC } from "react";
import type React from "react";
const InboxNotificationFallbackIcons = [
InboxNotificationFallbackIconAccount,
InboxNotificationFallbackIconWorkspace,
InboxNotificationFallbackIconTemplate,
InboxNotificationFallbackIconOther,
] as const;
type InboxNotificationFallbackIcon =
(typeof InboxNotificationFallbackIcons)[number];
const fallbackIcons: Record<InboxNotificationFallbackIcon, React.ReactNode> = {
DEFAULT_ICON_WORKSPACE: <LaptopIcon />,
DEFAULT_ICON_ACCOUNT: <UserIcon />,
DEFAULT_ICON_TEMPLATE: <LayoutTemplateIcon />,
DEFAULT_ICON_OTHER: <InfoIcon />,
};
type InboxAvatarProps = {
icon: string;
};
export const InboxAvatar: FC<InboxAvatarProps> = ({ icon }) => {
if (icon === "") {
return <Avatar variant="icon">{fallbackIcons.DEFAULT_ICON_OTHER}</Avatar>;
}
if (isInboxNotificationFallbackIcon(icon)) {
return <Avatar variant="icon">{fallbackIcons[icon]}</Avatar>;
}
return <Avatar variant="icon" src={icon} />;
};
function isInboxNotificationFallbackIcon(
icon: string,
): icon is InboxNotificationFallbackIcon {
return (InboxNotificationFallbackIcons as readonly string[]).includes(icon);
}
@@ -61,6 +61,7 @@ export const Markdown: Story = {
url: "https://dev.coder.com/workspaces?filter=template%3Acoder-with-ai",
},
],
icon: "DEFAULT_ICON_TEMPLATE",
},
},
};
@@ -1,13 +1,12 @@
import type { InboxNotification } from "api/typesGenerated";
import { Avatar } from "components/Avatar/Avatar";
import { Button } from "components/Button/Button";
import { Link } from "components/Link/Link";
import { SquareCheckBig } from "lucide-react";
import type { FC } from "react";
import Markdown from "react-markdown";
import { Link as RouterLink } from "react-router-dom";
import { cn } from "utils/cn";
import { relativeTime } from "utils/time";
import { InboxAvatar } from "./InboxAvatar";
type InboxItemProps = {
notification: InboxNotification;
@@ -25,7 +24,7 @@ export const InboxItem: FC<InboxItemProps> = ({
tabIndex={-1}
>
<div className="flex-shrink-0">
<Avatar fallback="AR" />
<InboxAvatar icon={notification.icon} />
</div>
<div className="flex flex-col gap-3 flex-1">
+1 -1
View File
@@ -4261,7 +4261,7 @@ export const MockNotification: TypesGen.InboxNotification = {
template_id: MockTemplate.id,
targets: [],
title: "User account created",
icon: "user",
icon: "DEFAULT_ICON_ACCOUNT",
};
export const MockNotifications: TypesGen.InboxNotification[] = [