Spaces:
Running
Running
Download src/components/NotificationCenter.jsx from Sarvesh213/getcert-backend: direct link, hf CLI and curl.
- Browser
- Download file 2.75 kB
-
https://huggingface.co/spaces/Sarvesh213/getcert-backend/resolve/main/src/components/NotificationCenter.jsx
- Command line
-
hf download hf://spaces/Sarvesh213/getcert-backend/src/components/NotificationCenter.jsx
-
curl -L -o NotificationCenter.jsx https://huggingface.co/spaces/Sarvesh213/getcert-backend/resolve/main/src/components/NotificationCenter.jsx
2.75 kB
| import React, { useEffect, useState } from 'react'; | |
| import { Check, Bell } from 'lucide-react'; | |
| import { supabase } from '../lib/supabase'; | |
| export default function NotificationCenter({ userId }) { | |
| const [notifications, setNotifications] = useState([]); | |
| const [loading, setLoading] = useState(true); | |
| const [updating, setUpdating] = useState({}); | |
| useEffect(() => { | |
| if (!userId) return; | |
| const fetchNotifications = async () => { | |
| setLoading(true); | |
| const { data, error } = await supabase | |
| .from('notifications') | |
| .select('*') | |
| .eq('user_id', userId) | |
| .eq('seen', false) | |
| .order('created_at', { ascending: false }); | |
| if (!error) setNotifications(data || []); | |
| setLoading(false); | |
| }; | |
| fetchNotifications(); | |
| }, [userId]); | |
| const markAsSeen = async (id) => { | |
| setUpdating(u => ({ ...u, [id]: true })); | |
| await supabase | |
| .from('notifications') | |
| .update({ seen: true }) | |
| .eq('id', id); | |
| setNotifications(n => n.filter(notif => notif.id !== id)); | |
| setUpdating(u => ({ ...u, [id]: false })); | |
| }; | |
| if (loading) { | |
| return ( | |
| <div className="space-y-2.5 font-sans"> | |
| {[1, 2].map((i) => ( | |
| <div key={i} className="h-12 w-full bg-slate-100 dark:bg-slate-800 animate-pulse rounded-xl" /> | |
| ))} | |
| </div> | |
| ); | |
| } | |
| if (!notifications.length) return null; | |
| return ( | |
| <div className="space-y-2.5 font-sans"> | |
| {notifications.map((notif) => ( | |
| <div | |
| key={notif.id} | |
| className="bg-white dark:bg-slate-900 border border-slate-200 dark:border-slate-800 rounded-xl p-3.5 shadow-2xs flex items-center justify-between gap-3 text-sm" | |
| > | |
| <div className="flex items-center gap-2.5 min-w-0"> | |
| <div className="w-7 h-7 rounded-lg bg-blue-50 dark:bg-blue-950/40 text-blue-600 dark:text-blue-400 flex items-center justify-center shrink-0"> | |
| <Bell className="w-3.5 h-3.5" /> | |
| </div> | |
| <p className="text-slate-800 dark:text-slate-200 font-medium text-xs sm:text-sm leading-snug truncate"> | |
| {notif.message} | |
| </p> | |
| </div> | |
| <button | |
| onClick={() => markAsSeen(notif.id)} | |
| disabled={updating[notif.id]} | |
| className="shrink-0 px-2.5 py-1 text-xs font-semibold text-slate-600 hover:text-slate-900 dark:text-slate-400 dark:hover:text-white bg-slate-100 hover:bg-slate-200 dark:bg-slate-800 dark:hover:bg-slate-700 rounded-lg transition-colors inline-flex items-center gap-1 cursor-pointer disabled:opacity-50" | |
| > | |
| <Check className="w-3 h-3 text-emerald-600" /> | |
| <span>Done</span> | |
| </button> | |
| </div> | |
| ))} | |
| </div> | |
| ); | |
| } |