Skip to content

Commit cbdf1de

Browse files
committed
feat: add clickable notification items
1 parent ef66632 commit cbdf1de

3 files changed

Lines changed: 85 additions & 51 deletions

File tree

apps/dashboard/src/app/(internal)/varslinger/page.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@
33
import { Box, Button, Skeleton, Stack } from "@mantine/core"
44
import { AllNotificationsTable } from "./all-notification-table"
55
import { useCreateNotificationModal } from "./modals/create-notification"
6-
import { useNotificationAllQuery } from "./queries"
6+
import { useNotificationAllInfiniteQuery } from "./queries"
77

88
export default function NotificationPage() {
9-
const { notifications, isLoading: isNotificationsLoading } = useNotificationAllQuery()
9+
const { notifications, isLoading: isNotificationsLoading } = useNotificationAllInfiniteQuery()
1010
const open = useCreateNotificationModal()
1111

1212
return (

apps/dashboard/src/app/(internal)/varslinger/queries.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import type { Pageable } from "@dotkomonline/utils"
33
import { useInfiniteQuery } from "@tanstack/react-query"
44
import { useMemo } from "react"
55

6-
export const useNotificationAllQuery = (page?: Pageable) => {
6+
export const useNotificationAllInfiniteQuery = (page?: Pageable) => {
77
const trpc = useTRPC()
88

99
const { data, ...query } = useInfiniteQuery({

apps/web/src/components/Navbar/Notifications/NotificationItem.tsx

Lines changed: 82 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,10 @@ import {
1414
import type { ComponentProps, ForwardRefExoticComponent, RefAttributes } from "react"
1515
import * as DropdownMenu from "@radix-ui/react-dropdown-menu"
1616
import { Badge, cn } from "@dotkomonline/ui"
17-
import type { NotificationType, UserNotificationDTO } from "@dotkomonline/rpc"
17+
import type { NotificationType, NotificationPayloadType, UserNotificationDTO } from "@dotkomonline/rpc"
1818
import { formatDistanceToNow, format, differenceInDays } from "date-fns"
1919
import { nb } from "date-fns/locale"
20+
import Link from "next/link"
2021

2122
const formatNotificationTime = (date: Date): string => {
2223
const now = new Date()
@@ -47,6 +48,27 @@ export const NotificationIconMap: Record<
4748
NEW_FEEDBACK_FORM: IconSpeakerphone,
4849
}
4950

51+
const getNotificationUrl = (payloadType: NotificationPayloadType, payload: string | null): string | null => {
52+
switch (payloadType) {
53+
case "URL":
54+
return payload
55+
case "EVENT":
56+
return payload ? `/arrangementer/${payload}` : null
57+
case "ARTICLE":
58+
return payload ? `/artikler/${payload}` : null
59+
case "GROUP":
60+
return payload ? `/grupper/${payload}` : null
61+
case "USER":
62+
return payload ? `/profil/${payload}` : null
63+
case "OFFLINE":
64+
return `/offline` // TODO: Fix in the future
65+
case "JOB_LISTING":
66+
return payload ? `/karriere/${payload}` : null
67+
case "NONE":
68+
return null
69+
}
70+
}
71+
5072
interface NotificationItem extends ComponentProps<typeof DropdownMenu.Item> {
5173
userNotification: UserNotificationDTO
5274
onItemClick?: () => void
@@ -67,61 +89,73 @@ export const NotificationItem = ({ userNotification, onItemClick, className, ...
6789
const unreadIconBgColor = isImportant ? "bg-red-500/20" : "bg-blue-500/20"
6890

6991
const handleSelect = (event: Event) => {
70-
// Prevent dropdown from closing if payloadType is NONE
7192
if (notification.payloadType === "NONE") {
7293
event.preventDefault()
7394
}
7495
}
7596

76-
return (
77-
<DropdownMenu.Item
78-
{...props}
79-
className={cn(
80-
"flex gap-4 px-5 py-4 cursor-pointer border-l-4 focus-visible:outline-none not-last:border-b not-last:border-b-white/10",
81-
{ [unreadItemColor]: !isRead },
82-
{
83-
"opacity-80 border-transparent hover:bg-blue-100 focus-visible:bg-blue-100 dark:hover:bg-white/5 dark:focus-visible:bg-white/10":
84-
isRead,
85-
},
86-
className
87-
)}
88-
onSelect={handleSelect}
89-
onClick={onItemClick}
90-
>
91-
<div className="flex flex-row gap-3 w-full">
92-
<div>
93-
<div
94-
className={cn(
95-
"rounded-lg flex items-center justify-center h-10 w-10",
96-
{ [unreadIconBgColor]: !isRead },
97-
{ "bg-black/10": isRead }
98-
)}
99-
>
100-
<Icon
101-
className={cn("w-6 h-6", { [unreadTextColor]: !isRead }, { "text-stone-800 dark:text-white": isRead })}
102-
/>
103-
</div>
104-
{!isRead && <div className={cn("h-2 w-2 rounded-full mx-auto mt-2", unreadDotColor)} />}
105-
</div>
106-
<div className="flex flex-col p-0.5 w-full relative">
107-
<p className="text-black/70 dark:text-white/70 text-xs ml-auto absolute top-0 right-0">
108-
{formatNotificationTime(new Date(notification.createdAt))}
109-
</p>
110-
111-
{isImportant && (
112-
<Badge
113-
variant="solid"
114-
color="red"
115-
className={cn("bg-red-500 text-white text-xs py-1 px-2", { "mb-1": isImportant })}
116-
>
117-
Viktig melding
118-
</Badge>
119-
)}
97+
const notificationUrl = getNotificationUrl(notification.payloadType, notification.payload)
12098

121-
<p className="font-semibold text-black dark:text-white text-sm">{notification.title}</p>
122-
<p className="text-black dark:text-white/80 text-sm">{notification.shortDescription}</p>
99+
const itemContent = (
100+
<div className="flex flex-row gap-3 w-full">
101+
<div>
102+
<div
103+
className={cn(
104+
"rounded-lg flex items-center justify-center h-10 w-10",
105+
{ [unreadIconBgColor]: !isRead },
106+
{ "bg-black/10": isRead }
107+
)}
108+
>
109+
<Icon
110+
className={cn("w-6 h-6", { [unreadTextColor]: !isRead }, { "text-stone-800 dark:text-white": isRead })}
111+
/>
123112
</div>
113+
{!isRead && <div className={cn("h-2 w-2 rounded-full mx-auto mt-2", unreadDotColor)} />}
124114
</div>
115+
<div className="flex flex-col p-0.5 w-full relative">
116+
<p className="text-black/70 dark:text-white/70 text-xs ml-auto absolute top-0 right-0">
117+
{formatNotificationTime(new Date(notification.createdAt))}
118+
</p>
119+
120+
{isImportant && (
121+
<Badge
122+
variant="solid"
123+
color="red"
124+
className={cn("bg-red-500 text-white text-xs py-1 px-2", { "mb-1": isImportant })}
125+
>
126+
Viktig melding
127+
</Badge>
128+
)}
129+
130+
<p className="font-semibold text-black dark:text-white text-sm">{notification.title}</p>
131+
<p className="text-black dark:text-white/80 text-sm">{notification.shortDescription}</p>
132+
</div>
133+
</div>
134+
)
135+
136+
const itemClassName = cn(
137+
"flex gap-4 px-5 py-4 cursor-pointer border-l-4 focus-visible:outline-none not-last:border-b not-last:border-b-white/10",
138+
{ [unreadItemColor]: !isRead },
139+
{
140+
"opacity-80 border-transparent hover:bg-blue-100 focus-visible:bg-blue-100 dark:hover:bg-white/5 dark:focus-visible:bg-white/10":
141+
isRead,
142+
},
143+
className
144+
)
145+
146+
if (notificationUrl !== null) {
147+
return (
148+
<DropdownMenu.Item {...props} asChild onSelect={handleSelect} onClick={onItemClick}>
149+
<Link href={notificationUrl} className={itemClassName}>
150+
{itemContent}
151+
</Link>
152+
</DropdownMenu.Item>
153+
)
154+
}
155+
156+
return (
157+
<DropdownMenu.Item {...props} className={itemClassName} onSelect={handleSelect} onClick={onItemClick}>
158+
{itemContent}
125159
</DropdownMenu.Item>
126160
)
127161
}

0 commit comments

Comments
 (0)