notifications
This commit is contained in:
parent
4efcd22107
commit
9b62063962
@ -1,9 +1,10 @@
|
||||
import React, { memo, useState, useEffect } from 'react';
|
||||
import Link from 'next/link';
|
||||
import { Bell, Check, ExternalLink, AlertCircle } from 'lucide-react';
|
||||
import { Bell, Check, ExternalLink, AlertCircle, LogIn } from 'lucide-react';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { useNotifications } from '@/hooks/use-notifications';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { useSession, signIn } from 'next-auth/react';
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
@ -19,22 +20,53 @@ interface NotificationBadgeProps {
|
||||
|
||||
// Use React.memo to prevent unnecessary re-renders
|
||||
export const NotificationBadge = memo(function NotificationBadge({ className }: NotificationBadgeProps) {
|
||||
const { data: session, status } = useSession();
|
||||
const { notifications, notificationCount, markAsRead, markAllAsRead, fetchNotifications, loading, error } = useNotifications();
|
||||
const hasUnread = notificationCount.unread > 0;
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [manualFetchAttempted, setManualFetchAttempted] = useState(false);
|
||||
|
||||
console.log('[NOTIFICATION_BADGE] Auth status:', status);
|
||||
console.log('[NOTIFICATION_BADGE] Session:', session ? 'exists' : 'null');
|
||||
console.log('[NOTIFICATION_BADGE] Current notification count:', notificationCount);
|
||||
console.log('[NOTIFICATION_BADGE] Current notifications:', notifications.length > 0 ? `${notifications.length} loaded` : 'none loaded');
|
||||
console.log('[NOTIFICATION_BADGE] Loading state:', loading);
|
||||
console.log('[NOTIFICATION_BADGE] Error state:', error);
|
||||
|
||||
// Manual fetch function with error handling
|
||||
const manualFetch = async () => {
|
||||
console.log('[NOTIFICATION_BADGE] Manual fetch initiated');
|
||||
setManualFetchAttempted(true);
|
||||
|
||||
try {
|
||||
// Direct fetch to debug
|
||||
const response = await fetch('/api/notifications', {
|
||||
credentials: 'include'
|
||||
});
|
||||
|
||||
console.log('[NOTIFICATION_BADGE] Manual fetch response:', response.status);
|
||||
|
||||
if (!response.ok) {
|
||||
console.error('[NOTIFICATION_BADGE] Manual fetch failed:', response.status, await response.text());
|
||||
} else {
|
||||
const data = await response.json();
|
||||
console.log('[NOTIFICATION_BADGE] Manual fetch success:', data);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('[NOTIFICATION_BADGE] Manual fetch error:', err);
|
||||
}
|
||||
|
||||
// Then try the normal way
|
||||
fetchNotifications(1, 10);
|
||||
};
|
||||
|
||||
// Fetch notifications when dropdown is opened
|
||||
useEffect(() => {
|
||||
if (isOpen) {
|
||||
if (isOpen && status === 'authenticated') {
|
||||
console.log('[NOTIFICATION_BADGE] Dropdown opened, fetching notifications');
|
||||
fetchNotifications(1, 10);
|
||||
manualFetch();
|
||||
}
|
||||
}, [isOpen, fetchNotifications]);
|
||||
}, [isOpen, status]);
|
||||
|
||||
const handleMarkAsRead = async (notificationId: string) => {
|
||||
await markAsRead(notificationId);
|
||||
@ -47,22 +79,27 @@ export const NotificationBadge = memo(function NotificationBadge({ className }:
|
||||
|
||||
// Force fetch when component mounts
|
||||
useEffect(() => {
|
||||
console.log('[NOTIFICATION_BADGE] Component mounted, fetching initial notifications');
|
||||
fetchNotifications(1, 10);
|
||||
}, [fetchNotifications]);
|
||||
if (status === 'authenticated') {
|
||||
console.log('[NOTIFICATION_BADGE] Component mounted and authenticated, fetching initial notifications');
|
||||
manualFetch();
|
||||
}
|
||||
}, [status]);
|
||||
|
||||
// Take the latest 10 notifications for the dropdown
|
||||
const recentNotifications = notifications.slice(0, 10);
|
||||
|
||||
const handleOpenChange = (open: boolean) => {
|
||||
setIsOpen(open);
|
||||
if (open) {
|
||||
if (open && status === 'authenticated') {
|
||||
// Fetch fresh notifications when dropdown opens
|
||||
console.log('[NOTIFICATION_BADGE] Dropdown opened via handleOpenChange, fetching notifications');
|
||||
fetchNotifications(1, 10);
|
||||
manualFetch();
|
||||
}
|
||||
};
|
||||
|
||||
// Special case for auth error
|
||||
const isAuthError = error?.includes('Not authenticated') || error?.includes('401');
|
||||
|
||||
return (
|
||||
<div className={`relative ${className || ''}`}>
|
||||
<DropdownMenu open={isOpen} onOpenChange={handleOpenChange}>
|
||||
@ -98,14 +135,29 @@ export const NotificationBadge = memo(function NotificationBadge({ className }:
|
||||
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-gray-900 mx-auto mb-2"></div>
|
||||
<p className="text-sm text-muted-foreground">Loading notifications...</p>
|
||||
</div>
|
||||
) : isAuthError ? (
|
||||
<div className="py-8 px-4 text-center">
|
||||
<LogIn className="h-8 w-8 text-orange-500 mx-auto mb-2" />
|
||||
<p className="text-sm text-muted-foreground mb-2">Authentication required</p>
|
||||
<Button variant="outline" size="sm" onClick={() => signIn()}>
|
||||
Sign in
|
||||
</Button>
|
||||
</div>
|
||||
) : error ? (
|
||||
<div className="py-8 px-4 text-center">
|
||||
<AlertCircle className="h-8 w-8 text-red-500 mx-auto mb-2" />
|
||||
<p className="text-sm text-red-500 mb-2">{error}</p>
|
||||
<Button variant="outline" size="sm" onClick={() => fetchNotifications(1, 10)}>
|
||||
<Button variant="outline" size="sm" onClick={manualFetch}>
|
||||
Retry
|
||||
</Button>
|
||||
</div>
|
||||
) : notifications.length === 0 && manualFetchAttempted ? (
|
||||
<div className="py-8 px-4 text-center">
|
||||
<p className="text-sm text-muted-foreground">No notifications found</p>
|
||||
<Button variant="outline" size="sm" className="mt-2" onClick={manualFetch}>
|
||||
Refresh
|
||||
</Button>
|
||||
</div>
|
||||
) : notifications.length === 0 ? (
|
||||
<div className="py-8 px-4 text-center">
|
||||
<p className="text-sm text-muted-foreground">No notifications</p>
|
||||
|
||||
@ -51,12 +51,18 @@ export function useNotifications() {
|
||||
setError(null);
|
||||
lastFetchTimeRef.current = now;
|
||||
|
||||
const response = await fetch('/api/notifications/count');
|
||||
console.log('[useNotifications] Fetching notification count');
|
||||
const response = await fetch('/api/notifications/count', {
|
||||
credentials: 'include' // Ensure cookies are sent with the request
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json();
|
||||
console.error('Failed to fetch notification count:', errorData);
|
||||
setError(errorData.error || 'Failed to fetch notification count');
|
||||
const errorText = await response.text();
|
||||
console.error('Failed to fetch notification count:', {
|
||||
status: response.status,
|
||||
body: errorText
|
||||
});
|
||||
setError(errorText || 'Failed to fetch notification count');
|
||||
return;
|
||||
}
|
||||
|
||||
@ -91,12 +97,18 @@ export function useNotifications() {
|
||||
lastFetchTimeRef.current = now;
|
||||
|
||||
try {
|
||||
const response = await fetch(`/api/notifications?page=${page}&limit=${limit}`);
|
||||
console.log('[useNotifications] Fetching notifications', { page, limit });
|
||||
const response = await fetch(`/api/notifications?page=${page}&limit=${limit}`, {
|
||||
credentials: 'include' // Ensure cookies are sent with the request
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json();
|
||||
console.error('Failed to fetch notifications:', errorData);
|
||||
setError(errorData.error || 'Failed to fetch notifications');
|
||||
const errorText = await response.text();
|
||||
console.error('Failed to fetch notifications:', {
|
||||
status: response.status,
|
||||
body: errorText
|
||||
});
|
||||
setError(errorText || 'Failed to fetch notifications');
|
||||
return;
|
||||
}
|
||||
|
||||
@ -119,16 +131,21 @@ export function useNotifications() {
|
||||
if (!session?.user) return false;
|
||||
|
||||
try {
|
||||
console.log('[useNotifications] Marking notification as read:', notificationId);
|
||||
const response = await fetch(`/api/notifications/${notificationId}/read`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
},
|
||||
credentials: 'include' // Ensure cookies are sent with the request
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json();
|
||||
console.error('Failed to mark notification as read:', errorData);
|
||||
const errorText = await response.text();
|
||||
console.error('Failed to mark notification as read:', {
|
||||
status: response.status,
|
||||
body: errorText
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -156,16 +173,21 @@ export function useNotifications() {
|
||||
if (!session?.user) return false;
|
||||
|
||||
try {
|
||||
console.log('[useNotifications] Marking all notifications as read');
|
||||
const response = await fetch('/api/notifications/read-all', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
},
|
||||
credentials: 'include' // Ensure cookies are sent with the request
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json();
|
||||
console.error('Failed to mark all notifications as read:', errorData);
|
||||
const errorText = await response.text();
|
||||
console.error('Failed to mark all notifications as read:', {
|
||||
status: response.status,
|
||||
body: errorText
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user