| "use client"; |
|
|
| import { Bell, X, CheckCheck } from "lucide-react"; |
| import { Button } from "@/components/ui/button"; |
| import { |
| Popover, |
| PopoverContent, |
| PopoverTrigger, |
| } from "@/components/ui/popover"; |
| import { cn } from "@/lib/utils"; |
| import { useNotificationStore } from "@/store/notifications"; |
|
|
| export function NotificationCenter() { |
| const { |
| notifications, |
| unreadCount, |
| markAsRead, |
| markAllAsRead, |
| removeNotification, |
| clearAll, |
| } = useNotificationStore(); |
|
|
| const typeColors = { |
| success: "text-green-600 dark:text-green-400", |
| error: "text-red-600 dark:text-red-400", |
| warning: "text-yellow-600 dark:text-yellow-400", |
| info: "text-blue-600 dark:text-blue-400", |
| }; |
|
|
| return ( |
| <Popover> |
| <PopoverTrigger asChild> |
| <Button variant="ghost" size="icon" className="relative cursor-pointer"> |
| <Bell className="h-5 w-5" /> |
| {unreadCount > 0 && ( |
| <span className="absolute -top-1 -right-1 h-5 w-5 rounded-full bg-red-500 text-xs text-white flex items-center justify-center"> |
| {unreadCount > 9 ? "9+" : unreadCount} |
| </span> |
| )} |
| </Button> |
| </PopoverTrigger> |
| <PopoverContent className="w-80 p-0" align="end"> |
| <div className="flex items-center justify-between p-4 border-b"> |
| <h3 className="font-semibold">Notifications</h3> |
| {unreadCount > 0 && ( |
| <Button |
| variant="ghost" |
| size="sm" |
| onClick={markAllAsRead} |
| className="h-8 text-xs cursor-pointer" |
| > |
| <CheckCheck className="h-3 w-3 mr-1" /> |
| Mark all read |
| </Button> |
| )} |
| </div> |
| <div className="max-h-[400px] overflow-y-auto"> |
| {notifications.length === 0 ? ( |
| <div className="p-8 text-center text-sm text-muted-foreground"> |
| No notifications |
| </div> |
| ) : ( |
| notifications.map((notification) => ( |
| <div |
| key={notification.id} |
| className={cn( |
| "p-4 border-b hover:bg-accent cursor-pointer transition-colors", |
| !notification.read && "bg-blue-50/50 dark:bg-blue-950/20" |
| )} |
| onClick={() => markAsRead(notification.id)} |
| > |
| <div className="flex items-start gap-3"> |
| <div className="flex-1 min-w-0"> |
| <div className="flex items-center gap-2"> |
| <p className={cn("font-medium text-sm", typeColors[notification.type])}> |
| {notification.title} |
| </p> |
| {!notification.read && ( |
| <span className="h-2 w-2 rounded-full bg-blue-500" /> |
| )} |
| </div> |
| {notification.message && ( |
| <p className="text-sm text-muted-foreground mt-1"> |
| {notification.message} |
| </p> |
| )} |
| <p className="text-xs text-muted-foreground mt-1"> |
| {new Date(notification.timestamp).toLocaleString()} |
| </p> |
| </div> |
| <button |
| onClick={(e) => { |
| e.stopPropagation(); |
| removeNotification(notification.id); |
| }} |
| className="flex-shrink-0 rounded-full p-1 hover:bg-black/10 dark:hover:bg-white/10 transition-colors cursor-pointer" |
| > |
| <X className="h-3 w-3" /> |
| </button> |
| </div> |
| </div> |
| )) |
| )} |
| </div> |
| {notifications.length > 0 && ( |
| <div className="p-2 border-t"> |
| <Button |
| variant="ghost" |
| size="sm" |
| onClick={clearAll} |
| className="w-full cursor-pointer text-xs" |
| > |
| Clear all notifications |
| </Button> |
| </div> |
| )} |
| </PopoverContent> |
| </Popover> |
| ); |
| } |
|
|