gohorsejobs/frontend/src/contexts/notification-context.tsx
2026-03-07 11:06:47 -03:00

222 lines
5.7 KiB
TypeScript

"use client";
import React, { createContext, useContext, useState, useCallback, useEffect } from "react";
import { toast } from "sonner";
import type { Notification } from "@/lib/types";
import { notificationsApi } from "@/lib/api";
import { useAuth } from "@/contexts/AuthContext";
interface NotificationContextType {
notifications: Notification[];
addNotification: (
notification: Omit<Notification, "id" | "createdAt" | "read">
) => void;
markAsRead: (id: string) => void;
markAllAsRead: () => void;
removeNotification: (id: string) => void;
clearAllNotifications: () => void;
unreadCount: number;
}
const NotificationContext = createContext<NotificationContextType | undefined>(
undefined
);
export function NotificationProvider({
children,
}: {
children: React.ReactNode;
}) {
const [notifications, setNotifications] = useState<Notification[]>([]);
const { user, loading } = useAuth();
useEffect(() => {
const loadNotifications = async () => {
const hasLocalSession =
typeof window !== "undefined" &&
Boolean(
localStorage.getItem("job-portal-auth") ||
localStorage.getItem("auth_token") ||
localStorage.getItem("token")
);
if (loading) {
return;
}
if (!user || !hasLocalSession) {
setNotifications([]);
return;
}
try {
const data = await notificationsApi.list();
setNotifications(data || []);
} catch (error: any) {
// Silently handle 401 errors - user is not authenticated
if (error?.status === 401 || error?.silent) {
return;
}
// Only log other errors in development
if (process.env.NODE_ENV === 'development') {
console.debug("Could not load notifications:", error?.message || 'Unknown error');
}
setNotifications([]);
}
};
loadNotifications();
}, [loading, user]);
const addNotification = useCallback(
(notification: Omit<Notification, "id" | "createdAt" | "read">) => {
const newNotification: Notification = {
...notification,
id: Date.now().toString(),
createdAt: new Date().toISOString(),
read: false,
};
setNotifications((prev) => [newNotification, ...prev]);
// Show toast notification
switch (notification.type) {
case "success":
toast.success(notification.title, {
description: notification.message,
});
break;
case "error":
toast.error(notification.title, {
description: notification.message,
});
break;
case "warning":
toast.warning(notification.title, {
description: notification.message,
});
break;
default:
toast.info(notification.title, {
description: notification.message,
});
break;
}
},
[]
);
const markAsRead = useCallback((id: string) => {
setNotifications((prev) =>
prev.map((notification) =>
notification.id === id ? { ...notification, read: true } : notification
)
);
}, []);
const markAllAsRead = useCallback(() => {
setNotifications((prev) =>
prev.map((notification) => ({ ...notification, read: true }))
);
}, []);
const removeNotification = useCallback((id: string) => {
setNotifications((prev) =>
prev.filter((notification) => notification.id !== id)
);
}, []);
const clearAllNotifications = useCallback(() => {
setNotifications([]);
}, []);
const unreadCount = notifications.filter((n) => !n.read).length;
return (
<NotificationContext.Provider
value={{
notifications,
addNotification,
markAsRead,
markAllAsRead,
removeNotification,
clearAllNotifications,
unreadCount,
}}
>
{children}
</NotificationContext.Provider>
);
}
export function useNotifications() {
const context = useContext(NotificationContext);
if (context === undefined) {
throw new Error(
"useNotifications must be used within a NotificationProvider"
);
}
return context;
}
// Hook personalizado para facilitar o uso
export function useNotify() {
const { addNotification } = useNotifications();
return React.useMemo(() => ({
success: (
title: string,
message: string,
options?: { actionUrl?: string; actionLabel?: string; userId?: string }
) =>
addNotification({
title,
message,
type: "success",
userId: options?.userId || "current",
actionUrl: options?.actionUrl,
actionLabel: options?.actionLabel,
}),
error: (
title: string,
message: string,
options?: { actionUrl?: string; actionLabel?: string; userId?: string }
) =>
addNotification({
title,
message,
type: "error",
userId: options?.userId || "current",
actionUrl: options?.actionUrl,
actionLabel: options?.actionLabel,
}),
warning: (
title: string,
message: string,
options?: { actionUrl?: string; actionLabel?: string; userId?: string }
) =>
addNotification({
title,
message,
type: "warning",
userId: options?.userId || "current",
actionUrl: options?.actionUrl,
actionLabel: options?.actionLabel,
}),
info: (
title: string,
message: string,
options?: { actionUrl?: string; actionLabel?: string; userId?: string }
) =>
addNotification({
title,
message,
type: "info",
userId: options?.userId || "current",
actionUrl: options?.actionUrl,
actionLabel: options?.actionLabel,
}),
}), [addNotification]);
}