138 lines
3.7 KiB
TypeScript
138 lines
3.7 KiB
TypeScript
"use client"
|
|
|
|
import { createContext, useContext, useState, useCallback, useMemo, ReactNode } from "react"
|
|
import { Notification, NotificationType } from "@/types"
|
|
import { conversations } from "@/data/chats"
|
|
import { leads } from "@/data/leads"
|
|
|
|
interface NotificationContextValue {
|
|
notifications: Notification[]
|
|
unreadCount: number
|
|
unreadChatCount: number
|
|
addNotification: (type: NotificationType, title: string, description: string, link?: string) => void
|
|
markAsRead: (id: string) => void
|
|
markAllAsRead: () => void
|
|
dismiss: (id: string) => void
|
|
}
|
|
|
|
const NotificationContext = createContext<NotificationContextValue | null>(null)
|
|
|
|
function generateId(): string {
|
|
return `notif-${Date.now()}-${Math.random().toString(36).slice(2, 7)}`
|
|
}
|
|
|
|
function seedInitialNotifications(): Notification[] {
|
|
const result: Notification[] = []
|
|
|
|
const recentLeads = leads.slice(0, 5)
|
|
recentLeads.forEach((lead) => {
|
|
result.push({
|
|
id: generateId(),
|
|
type: "lead_created",
|
|
title: "New Lead Created",
|
|
description: `${lead.companyName} — ${lead.contactName}`,
|
|
timestamp: lead.createdAt,
|
|
read: false,
|
|
link: `/leads/${lead.id}`,
|
|
})
|
|
})
|
|
|
|
const statusChanged = leads.filter((l) => l.status !== "open").slice(0, 3)
|
|
statusChanged.forEach((lead) => {
|
|
result.push({
|
|
id: generateId(),
|
|
type: "lead_status_changed",
|
|
title: "Lead Status Updated",
|
|
description: `${lead.companyName} moved to ${lead.status}`,
|
|
timestamp: lead.updatedAt,
|
|
read: false,
|
|
link: `/leads/${lead.id}`,
|
|
})
|
|
})
|
|
|
|
const assignedLeads = leads.filter((l) => l.assignedUser).slice(0, 3)
|
|
assignedLeads.forEach((lead) => {
|
|
result.push({
|
|
id: generateId(),
|
|
type: "lead_assigned",
|
|
title: "Lead Assigned",
|
|
description: `${lead.companyName} assigned to ${lead.assignedUser!.name}`,
|
|
timestamp: lead.updatedAt,
|
|
read: false,
|
|
link: `/leads/${lead.id}`,
|
|
})
|
|
})
|
|
|
|
result.sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime())
|
|
|
|
return result
|
|
}
|
|
|
|
export function NotificationProvider({ children }: { children: ReactNode }) {
|
|
const [notifications, setNotifications] = useState<Notification[]>(seedInitialNotifications)
|
|
|
|
const unreadChatCount = useMemo(
|
|
() => conversations.reduce((sum, c) => sum + c.unread, 0),
|
|
[]
|
|
)
|
|
|
|
const unreadCount = useMemo(
|
|
() => notifications.filter((n) => !n.read).length,
|
|
[notifications]
|
|
)
|
|
|
|
const addNotification = useCallback(
|
|
(type: NotificationType, title: string, description: string, link?: string) => {
|
|
const notif: Notification = {
|
|
id: generateId(),
|
|
type,
|
|
title,
|
|
description,
|
|
timestamp: new Date().toISOString(),
|
|
read: false,
|
|
link,
|
|
}
|
|
setNotifications((prev) => [notif, ...prev])
|
|
},
|
|
[]
|
|
)
|
|
|
|
const markAsRead = useCallback((id: string) => {
|
|
setNotifications((prev) =>
|
|
prev.map((n) => (n.id === id ? { ...n, read: true } : n))
|
|
)
|
|
}, [])
|
|
|
|
const markAllAsRead = useCallback(() => {
|
|
setNotifications((prev) => prev.map((n) => ({ ...n, read: true })))
|
|
}, [])
|
|
|
|
const dismiss = useCallback((id: string) => {
|
|
setNotifications((prev) => prev.filter((n) => n.id !== id))
|
|
}, [])
|
|
|
|
return (
|
|
<NotificationContext.Provider
|
|
value={{
|
|
notifications,
|
|
unreadCount,
|
|
unreadChatCount,
|
|
addNotification,
|
|
markAsRead,
|
|
markAllAsRead,
|
|
dismiss,
|
|
}}
|
|
>
|
|
{children}
|
|
</NotificationContext.Provider>
|
|
)
|
|
}
|
|
|
|
export function useNotifications(): NotificationContextValue {
|
|
const ctx = useContext(NotificationContext)
|
|
if (!ctx) {
|
|
throw new Error("useNotifications must be used within a NotificationProvider")
|
|
}
|
|
return ctx
|
|
}
|