| 1 | import React from 'react'
|
|---|
| 2 | import { useNavigate } from 'react-router-dom'
|
|---|
| 3 | import { Heart, MessageCircle, Users, Bell, Settings, Check } from 'lucide-react'
|
|---|
| 4 | import { useNotificationStore } from '../../store/notificationStore'
|
|---|
| 5 | import { NotificationType } from '../../types'
|
|---|
| 6 |
|
|---|
| 7 | const typeIcon = (type: NotificationType) => {
|
|---|
| 8 | const props = { size: 14 }
|
|---|
| 9 | switch (type) {
|
|---|
| 10 | case 'like': return <Heart {...props} className="text-rose-400" />
|
|---|
| 11 | case 'comment': return <MessageCircle {...props} className="text-blue-400" />
|
|---|
| 12 | case 'collaboration': return <Users {...props} className="text-violet-400" />
|
|---|
| 13 | case 'follow': return <Bell {...props} className="text-amber-400" />
|
|---|
| 14 | case 'system': return <Settings {...props} className="text-slate-400" />
|
|---|
| 15 | default: return <Bell {...props} className="text-slate-400" />
|
|---|
| 16 | }
|
|---|
| 17 | }
|
|---|
| 18 |
|
|---|
| 19 | function timeAgo(dateStr: string): string {
|
|---|
| 20 | const diff = Date.now() - new Date(dateStr).getTime()
|
|---|
| 21 | const mins = Math.floor(diff / 60000)
|
|---|
| 22 | if (mins < 1) return 'just now'
|
|---|
| 23 | if (mins < 60) return `${mins}m ago`
|
|---|
| 24 | const hrs = Math.floor(mins / 60)
|
|---|
| 25 | if (hrs < 24) return `${hrs}h ago`
|
|---|
| 26 | return `${Math.floor(hrs / 24)}d ago`
|
|---|
| 27 | }
|
|---|
| 28 |
|
|---|
| 29 | interface Props {
|
|---|
| 30 | onClose: () => void
|
|---|
| 31 | }
|
|---|
| 32 |
|
|---|
| 33 | export const NotificationDropdown: React.FC<Props> = ({ onClose }) => {
|
|---|
| 34 | const navigate = useNavigate()
|
|---|
| 35 | const { notifications, markAllRead, markRead } = useNotificationStore()
|
|---|
| 36 | const recent = notifications.slice(0, 5)
|
|---|
| 37 |
|
|---|
| 38 | return (
|
|---|
| 39 | <div className="absolute right-0 top-full mt-2 w-80 bg-slate-900 border border-slate-700 rounded-2xl shadow-2xl z-50 overflow-hidden">
|
|---|
| 40 | <div className="flex items-center justify-between px-4 py-3 border-b border-slate-700">
|
|---|
| 41 | <span className="text-sm font-semibold text-white">Notifications</span>
|
|---|
| 42 | <button
|
|---|
| 43 | onClick={markAllRead}
|
|---|
| 44 | className="text-xs text-indigo-400 hover:text-indigo-300 flex items-center gap-1 transition-colors"
|
|---|
| 45 | >
|
|---|
| 46 | <Check size={12} />
|
|---|
| 47 | Mark all read
|
|---|
| 48 | </button>
|
|---|
| 49 | </div>
|
|---|
| 50 | <div className="divide-y divide-slate-800">
|
|---|
| 51 | {recent.length === 0 ? (
|
|---|
| 52 | <div className="px-4 py-8 text-center text-slate-500 text-sm">
|
|---|
| 53 | No notifications yet
|
|---|
| 54 | </div>
|
|---|
| 55 | ) : (
|
|---|
| 56 | recent.map(n => (
|
|---|
| 57 | <div
|
|---|
| 58 | key={n.notification_id}
|
|---|
| 59 | className={`flex gap-3 px-4 py-3 cursor-pointer transition-colors ${
|
|---|
| 60 | !n.is_read ? 'bg-indigo-500/5 hover:bg-indigo-500/10' : 'hover:bg-slate-800'
|
|---|
| 61 | }`}
|
|---|
| 62 | onClick={() => {
|
|---|
| 63 | markRead(n.notification_id)
|
|---|
| 64 | if (n.link) { navigate(n.link); onClose() }
|
|---|
| 65 | }}
|
|---|
| 66 | >
|
|---|
| 67 | <div className="mt-0.5 w-6 h-6 rounded-full bg-slate-800 flex items-center justify-center flex-shrink-0">
|
|---|
| 68 | {typeIcon(n.type)}
|
|---|
| 69 | </div>
|
|---|
| 70 | <div className="flex-1 min-w-0">
|
|---|
| 71 | <p className="text-xs font-medium text-white">{n.title}</p>
|
|---|
| 72 | <p className="text-xs text-slate-400 line-clamp-2">{n.message}</p>
|
|---|
| 73 | <p className="text-xs text-slate-600 mt-0.5">{timeAgo(n.created_at)}</p>
|
|---|
| 74 | </div>
|
|---|
| 75 | {!n.is_read && (
|
|---|
| 76 | <div className="w-2 h-2 rounded-full bg-indigo-400 flex-shrink-0 mt-1.5" />
|
|---|
| 77 | )}
|
|---|
| 78 | </div>
|
|---|
| 79 | ))
|
|---|
| 80 | )}
|
|---|
| 81 | </div>
|
|---|
| 82 | <div className="border-t border-slate-700 p-2">
|
|---|
| 83 | <button
|
|---|
| 84 | onClick={() => { navigate('/notifications'); onClose() }}
|
|---|
| 85 | className="w-full text-center text-xs text-indigo-400 hover:text-indigo-300 py-1.5 rounded-lg hover:bg-slate-800 transition-colors"
|
|---|
| 86 | >
|
|---|
| 87 | View all notifications
|
|---|
| 88 | </button>
|
|---|
| 89 | </div>
|
|---|
| 90 | </div>
|
|---|
| 91 | )
|
|---|
| 92 | }
|
|---|