shoc-frontend-new/src/components/notifications/notification-row.tsx

108 lines
3.1 KiB
TypeScript
Raw Normal View History

import { createElement } from "react";
import type { LucideIcon } from "lucide-react";
import {
AlertTriangle,
Bell,
CircleDollarSign,
Clock,
Inbox,
MessageSquare,
ShieldAlert,
UserPlus,
Wrench,
X,
} from "lucide-react";
import { Text } from "@/components/ui/text";
import type { NotificationItem } from "@/domain/notifications/types/notification-feed";
import { isDismissable } from "@/domain/notifications/utils/visible-notification-sections";
const REASON_ICONS: Record<string, LucideIcon> = {
"severity-critical": AlertTriangle,
sla: AlertTriangle,
mentions: MessageSquare,
unassigned: Inbox,
"due-soon": Clock,
"no-vendor": Wrench,
"aveta-missing": ShieldAlert,
activity: MessageSquare,
"vendor-conflict": Wrench,
};
const ICON_PROPS = {
size: 15,
"aria-hidden": true,
className: "shrink-0",
style: { color: "var(--color-text-muted)", marginTop: 2 },
} as const;
// Recent activity mixes comments, assignments and uplift decisions; where the row leads tells them apart.
function ItemIcon({ item }: { item: NotificationItem }) {
if (item.reason === "activity" && item.target.tab === "uplifts") {
return <CircleDollarSign {...ICON_PROPS} />;
}
if (item.reason === "activity" && item.target.tab !== "comments") {
return <UserPlus {...ICON_PROPS} />;
}
return createElement(REASON_ICONS[item.reason] ?? Bell, ICON_PROPS);
}
type NotificationRowProps = {
item: NotificationItem;
unread: boolean;
onOpen: (item: NotificationItem) => void;
onDismiss: (item: NotificationItem) => void;
};
export function NotificationRow({ item, unread, onOpen, onDismiss }: NotificationRowProps) {
return (
<li
className="group flex items-start gap-2.5 transition-colors hover:bg-[var(--color-bg-muted)]"
style={{
padding: "10px 8px",
marginInline: -8,
borderRadius: "var(--radius-md)",
borderBottom: "1px solid var(--color-bg-muted)",
}}
data-unread={unread}
>
<button
type="button"
onClick={() => onOpen(item)}
className="flex min-w-0 flex-1 cursor-pointer items-start gap-2.5 text-left"
>
<span
aria-hidden
className="shrink-0 rounded-full"
style={{
width: 6,
height: 6,
marginTop: 7,
background: unread ? "var(--color-primary)" : "transparent",
}}
/>
<ItemIcon item={item} />
<Text
as="span"
variant="description"
sx={{ fontSize: 13, lineHeight: 1.45, fontWeight: unread ? 500 : 400, margin: 0 }}
>
{unread && <span className="sr-only">Unread: </span>}
{item.title}
</Text>
</button>
{isDismissable(item) && (
<button
type="button"
onClick={() => onDismiss(item)}
title="Dismiss"
aria-label="Dismiss notification"
className="shrink-0 cursor-pointer opacity-0 transition-opacity group-hover:opacity-100 focus-visible:opacity-100"
style={{ color: "var(--color-text-muted)", marginTop: 2 }}
>
<X size={13} aria-hidden />
</button>
)}
</li>
);
}