From 48a2255bfc45ffcfb0b39ffefdd57cbacf8b36df Mon Sep 17 00:00:00 2001 From: dujinkim Date: Fri, 18 Jul 2025 07:52:02 +0000 Subject: (대표님) 파일관리변경, 클라IP추적, 실시간알림, 미들웨어변경, 알림API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components/layout/NotificationDropdown.tsx | 146 +++++++++++++++++++++++++++++ 1 file changed, 146 insertions(+) create mode 100644 components/layout/NotificationDropdown.tsx (limited to 'components/layout/NotificationDropdown.tsx') diff --git a/components/layout/NotificationDropdown.tsx b/components/layout/NotificationDropdown.tsx new file mode 100644 index 00000000..1030bbc0 --- /dev/null +++ b/components/layout/NotificationDropdown.tsx @@ -0,0 +1,146 @@ +"use client"; + +import * as React from "react"; +import { BellIcon, CheckIcon, MoreHorizontal } from "lucide-react"; +import { Button } from "@/components/ui/button"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuLabel, + DropdownMenuSeparator, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu"; +import { Badge } from "@/components/ui/badge"; +import { ScrollArea } from "@/components/ui/scroll-area"; +import { formatDistanceToNow } from "date-fns"; +import { ko } from "date-fns/locale"; +import { useNotifications } from "@/lib/notification/NotificationContext"; + +export function NotificationDropdown() { + const { notifications, unreadCount, markAsRead, markAllAsRead } = useNotifications(); + + const handleNotificationClick = (notification: any) => { + if (!notification.isRead) { + markAsRead(notification.id); + } + + // 관련 레코드로 이동 + if (notification.relatedRecordId && notification.relatedRecordType) { + const url = getNotificationUrl(notification.relatedRecordType, notification.relatedRecordId); + window.location.href = url; + } + }; + + const getNotificationUrl = (type: string, id: string) => { + const baseUrl = window.location.pathname.split('/').slice(0, 3).join('/'); + + switch (type) { + case 'project': + return `${baseUrl}/projects/${id}`; + case 'task': + return `${baseUrl}/tasks/${id}`; + case 'order': + return `${baseUrl}/orders/${id}`; + default: + return baseUrl; + } + }; + + const getNotificationIcon = (type: string) => { + switch (type) { + case 'assignment': + return '📝'; + case 'update': + return '🔄'; + case 'reminder': + return '⏰'; + case 'approval': + return '✅'; + default: + return '🔔'; + } + }; + + return ( + + + + + +
+ 알림 + {unreadCount > 0 && ( + + )} +
+ + + {notifications.length === 0 ? ( +
+ 새로운 알림이 없습니다 +
+ ) : ( + + {notifications.slice(0, 10).map((notification) => ( + handleNotificationClick(notification)} + > +
+
+
+ {getNotificationIcon(notification.type)} +
+
+
+

+ {notification.title} +

+ {!notification.isRead && ( +
+ )} +
+

+ {notification.message} +

+

+ {formatDistanceToNow(new Date(notification.createdAt), { + addSuffix: true, + locale: ko + })} +

+
+
+
+ + ))} + {notifications.length > 10 && ( + + 더 많은 알림 보기... + + )} + + )} + + + ); +} \ No newline at end of file -- cgit v1.2.3