1import { useEffect, useState } from 'react';
2import MainLayout from '../components/Layout/MainLayout';
3import { apiFetch } from '../lib/api';
5function Notifications() {
6 const [notes, setNotes] = useState([]);
7 const [loading, setLoading] = useState(false);
8 const [error, setError] = useState('');
10 async function fetchNotes() {
14 const token = localStorage.getItem('token');
15 const { apiUrl } = await import('../lib/api');
16 const url = apiUrl('/notifications');
17 const res = await apiFetch(url.toString(), { headers: { Authorization: `Bearer ${token}` } });
18 if (!res.ok) throw new Error('Failed to load notifications');
19 const data = await res.json();
23 setError(err.message);
29 useEffect(() => { fetchNotes(); }, []);
31 const markRead = async (id) => {
33 const token = localStorage.getItem('token');
34 const { apiUrl } = await import('../lib/api');
35 const url = apiUrl(`/notifications/${id}/mark-read`);
36 const res = await apiFetch(url.toString(), { method: 'PUT', headers: { Authorization: `Bearer ${token}` } });
37 if (!res.ok) throw new Error('Failed');
38 setNotes(n => n.map(x => x.notification_id === id ? { ...x, is_read: true } : x));
44 const dismissNote = async (id) => {
45 if (!window.confirm('Dismiss this notification?')) return;
47 const token = localStorage.getItem('token');
48 const { apiUrl } = await import('../lib/api');
49 const url = apiUrl(`/notifications/${id}`);
50 const res = await apiFetch(url.toString(), { method: 'DELETE', headers: { Authorization: `Bearer ${token}` } });
51 if (!res.ok) throw new Error('Failed to dismiss notification');
52 setNotes(n => n.filter(x => x.notification_id !== id));
55 alert('Failed to dismiss notification');
61 <div className="page-content">
62 <div className="page-header"><h2>Notifications</h2></div>
63 {loading && <div className="loading">Loading...</div>}
64 {error && <div className="error-message">{error}</div>}
65 <div className="notifications-list" style={{ maxWidth: 600, margin: '0 auto' }}>
66 {notes.length === 0 ? (
67 <div style={{ color: '#888', textAlign: 'center', marginTop: 32 }}>
68 <span className="material-symbols-outlined" style={{ fontSize: 48, opacity: 0.3 }}>notifications_off</span>
69 <div>No notifications</div>
73 <div key={n.notification_id} style={{
75 borderBottom: '1px solid #eee',
76 background: n.is_read ? '#fafafa' : '#fff',
78 flexDirection: 'column',
82 <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
83 <div style={{ fontWeight: 'bold', fontSize: '1.1em' }}>{n.title}</div>
84 <div style={{ fontSize: '0.95em', color: '#888' }}>{new Date(n.created_at).toLocaleString()}</div>
86 <div style={{ marginTop: 2 }}>{n.message}</div>
87 <div style={{ display: 'flex', gap: 8, marginTop: 8 }}>
89 <button className="btn btn-sm" onClick={() => markRead(n.notification_id)}>
93 <button className="btn btn-sm btn-danger" onClick={() => dismissNote(n.notification_id)}>
106export default Notifications;