getcert-backend / src /components /NotificationCenter.jsx
ggop213's picture
feat: full UI credibility overhaul — Inter typography, clean headers, remove all AI slop tags
50cd427
Raw History Blame Contribute Delete
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>
);
}