diff --git a/src/components/NotificationsDropdown.tsx b/src/components/NotificationsDropdown.tsx index e1d16e3..8fae2ae 100644 --- a/src/components/NotificationsDropdown.tsx +++ b/src/components/NotificationsDropdown.tsx @@ -44,7 +44,6 @@ const NotificationsDropdown: React.FC = () => { const { notifications, unreadNotifications, refreshUserData, friends } = useAuth(); const navigate = useNavigate(); const [localNotifications, setLocalNotifications] = React.useState(notifications); - const [dismissedIds, setDismissedIds] = React.useState>(new Set()); React.useEffect(() => { setLocalNotifications(notifications); @@ -54,7 +53,10 @@ const NotificationsDropdown: React.FC = () => { const result = await acceptFriendRequest(auth.currentUser?.uid || '', senderId); if (result) { toast.success('Friend request accepted'); - setDismissedIds(prev => new Set(prev).add(notificationId)); + await markNotificationAsRead(auth.currentUser?.uid || '', notificationId); + setLocalNotifications(prev => + prev.map(n => n.id === notificationId ? { ...n, read: true } : n) + ); refreshUserData(); } else { toast.error('Failed to accept friend request'); @@ -65,7 +67,10 @@ const NotificationsDropdown: React.FC = () => { const result = await declineFriendRequest(auth.currentUser?.uid || '', senderId); if (result) { toast.success('Friend request declined'); - setDismissedIds(prev => new Set(prev).add(notificationId)); + await markNotificationAsRead(auth.currentUser?.uid || '', notificationId); + setLocalNotifications(prev => + prev.map(n => n.id === notificationId ? { ...n, read: true } : n) + ); refreshUserData(); } else { toast.error('Failed to decline friend request'); @@ -132,7 +137,6 @@ const NotificationsDropdown: React.FC = () => { {localNotifications && localNotifications.length > 0 ? ( {localNotifications - .filter(n => !dismissedIds.has(n.id)) .sort((a, b) => { let aTime: number; let bTime: number; @@ -159,7 +163,7 @@ const NotificationsDropdown: React.FC = () => { .map((notification, index) => ( {notification.type === 'friendRequest' && notification.senderInfo ? ( -
+
{notification.senderInfo && ( @@ -175,24 +179,26 @@ const NotificationsDropdown: React.FC = () => {

-
- - -
+ {!notification.read && ( +
+ + +
+ )}
) : ( = ({ userId, showStats = false }) => { +const RelapseCalendar: React.FC = ({ userId, showStats = false, editable = false }) => { const [calendarData, setCalendarData] = useState([]); const [isLoading, setIsLoading] = useState(true); const [month, setMonth] = useState(new Date()); const [stats, setStats] = useState({ cleanDays: 0, relapseDays: 0, netGrowth: 0 }); - useEffect(() => { - const fetchData = async () => { - if (!userId) { - setIsLoading(false); - return; - } + // Edit dialog state + const [editDialogOpen, setEditDialogOpen] = useState(false); + const [selectedDay, setSelectedDay] = useState(null); + const [selectedDayData, setSelectedDayData] = useState(null); + const [editMarkAsRelapse, setEditMarkAsRelapse] = useState(false); + const [editTriggers, setEditTriggers] = useState(''); + const [editNotes, setEditNotes] = useState(''); + const [isSaving, setIsSaving] = useState(false); - try { - setIsLoading(true); - // Get calendar visualization data - const data = await getRelapseCalendarData(userId); - setCalendarData(data); - - // Get analytics data for accurate stats - const relapseData = await getRelapseData(userId, 'all'); - setStats({ - cleanDays: relapseData.cleanDays, - relapseDays: relapseData.relapseDays, - netGrowth: relapseData.netGrowth - }); - } catch (error) { - console.error('Error fetching calendar data:', error); - } finally { - setIsLoading(false); - } - }; + const fetchData = async () => { + if (!userId) { + setIsLoading(false); + return; + } + + try { + setIsLoading(true); + // Get calendar visualization data + const data = await getRelapseCalendarData(userId); + setCalendarData(data); + + // Get analytics data for accurate stats + const relapseData = await getRelapseData(userId, 'all'); + setStats({ + cleanDays: relapseData.cleanDays, + relapseDays: relapseData.relapseDays, + netGrowth: relapseData.netGrowth + }); + } catch (error) { + console.error('Error fetching calendar data:', error); + } finally { + setIsLoading(false); + } + }; + useEffect(() => { fetchData(); }, [userId]); + const handleDayClick = (day: Date) => { + if (!editable || !userId) return; + const dayData = calendarData.find(d => isSameDay(d.date, day)) || null; + setSelectedDay(day); + setSelectedDayData(dayData); + setEditMarkAsRelapse(dayData?.hadRelapse ?? false); + setEditTriggers(dayData?.relapseInfo?.triggers ?? ''); + setEditNotes(dayData?.relapseInfo?.notes ?? ''); + setEditDialogOpen(true); + }; + + const handleSaveEdit = async () => { + if (!userId || !selectedDay) return; + setIsSaving(true); + try { + const success = await updateCalendarDay( + userId, + selectedDay, + editMarkAsRelapse, + editMarkAsRelapse ? editTriggers : undefined, + editMarkAsRelapse ? editNotes : undefined + ); + if (success) { + toast.success(editMarkAsRelapse ? 'Day marked as relapse' : 'Day marked as clean'); + setEditDialogOpen(false); + await fetchData(); + } else { + toast.error('Failed to update day'); + } + } catch (error) { + toast.error('Failed to update day'); + } finally { + setIsSaving(false); + } + }; + // Custom day rendering with dots for relapse status const renderDay = (props: DayContentProps) => { const day = props.date; @@ -72,7 +130,10 @@ const RelapseCalendar: React.FC = ({ userId, showStats = f return ( -
+
handleDayClick(day) : undefined} + >
{day.getDate()}
@@ -93,6 +154,7 @@ const RelapseCalendar: React.FC = ({ userId, showStats = f ) : (

Clean day

)} + {editable &&

Click to edit

}
@@ -120,6 +182,9 @@ const RelapseCalendar: React.FC = ({ userId, showStats = f
) : ( + {editable && ( +

Click any day to mark it as clean or relapse

+ )} = ({ userId, showStats = f
)} + + {/* Edit day dialog */} + + + + + Edit Day – {selectedDay ? new Intl.DateTimeFormat('en-US', { month: 'long', day: 'numeric', year: 'numeric' }).format(selectedDay) : ''} + + +
+
+ + +
+ + {editMarkAsRelapse && ( + <> +
+ + setEditTriggers(e.target.value)} + /> +
+
+ +