diff --git a/src/main/java/org/example/team6backend/config/SecurityConfig.java b/src/main/java/org/example/team6backend/config/SecurityConfig.java index 3fbfb02..30de704 100644 --- a/src/main/java/org/example/team6backend/config/SecurityConfig.java +++ b/src/main/java/org/example/team6backend/config/SecurityConfig.java @@ -27,7 +27,7 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti .requestMatchers("/api/incidents/**").hasAnyRole("RESIDENT", "HANDLER", "ADMIN") .requestMatchers("/api/admin/**").hasRole("ADMIN").requestMatchers("/api/users/me").authenticated() - .requestMatchers("/comments/**", "/activity/**", "/documents/**").authenticated() + .requestMatchers("/comments/**", "/activity/**", "/documents/**", "/notifications/**").authenticated() .requestMatchers("/swagger-ui.html", "/swagger-ui/**", "/v3/api-docs/**").hasRole("ADMIN").anyRequest() .authenticated()) .oauth2Login( @@ -35,8 +35,8 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti .defaultSuccessUrl("/dashboard.html", true)) .logout(logout -> logout.logoutSuccessUrl("/").invalidateHttpSession(true).clearAuthentication(true) .deleteCookies("JSESSIONID")) - .csrf(csrf -> csrf.ignoringRequestMatchers("/api/admin/**", "/api/incidents/**", "/comments", - "/dev/**")); + .csrf(csrf -> csrf.ignoringRequestMatchers("/api/admin/**", "/api/incidents/**", "/comments", "/dev/**", + "/notifications/**")); return http.build(); } diff --git a/src/main/resources/static/dashboard.html b/src/main/resources/static/dashboard.html index 47ed4c8..c86ee7e 100644 --- a/src/main/resources/static/dashboard.html +++ b/src/main/resources/static/dashboard.html @@ -5,13 +5,19 @@ Dashboard - Incident Manager @@ -109,6 +361,22 @@ Profile + + + +
Loading... - @@ -124,13 +392,24 @@

Welcome!

-
Total Incidents
0
-
Open
0
-
Resolved
0
+
+
Total Incidents
+
0
+
+
+
Open
+
0
+
+
+
Resolved
+
0
+
-

Recent incidents

View all →
-

Loading incidents...

+

Recent incidents

View all + →
+

Loading + incidents...

@@ -156,10 +435,16 @@

Welcome!

if (currentUser.role === 'ADMIN') { document.getElementById('adminLink').style.display = 'inline'; } + loadNotifications(); + setupNotificationDropdown(); loadIncidents(); } - } else { window.location.href = '/'; } - } catch (error) { window.location.href = '/'; } + } else { + window.location.href = '/'; + } + } catch (error) { + window.location.href = '/'; + } } async function loadIncidents() { @@ -192,7 +477,9 @@

Welcome!

if (currentUser.role === 'RESIDENT') actionButtons.innerHTML = 'Create new incident'; else if (currentUser.role === 'HANDLER') actionButtons.innerHTML = 'Manage incidents'; else if (currentUser.role === 'ADMIN') actionButtons.innerHTML = 'Go to Admin Panel'; - } catch (error) { console.error('Error loading incidents:', error); } + } catch (error) { + console.error('Error loading incidents:', error); + } } function viewIncident(id) { @@ -200,7 +487,7 @@

Welcome!

} async function logout() { - await fetch('/logout', { method: 'POST' }); + await fetch('/logout', {method: 'POST'}); window.location.href = '/'; } @@ -222,6 +509,86 @@

Welcome!

}); } + let notifications = []; + + async function loadNotifications() { + try { + const response = await fetch('/notifications/user'); + + if (!response.ok) { + throw new Error('Failed to load notifications') + } + + notifications = await response.json(); + + const list = document.getElementById('notificationList'); + const badge = document.getElementById('notificationBadge'); + const wrapper = document.getElementById('notificationWrapper'); + + wrapper.style.display = 'inline-flex'; + + const unreadCount = notifications.filter(notification => !notification.read).length; + + if (unreadCount > 0) { + badge.textContent = unreadCount; + badge.style.display = 'inline-flex' + } else { + badge.style.display = 'none'; + } + + if (!notifications.length) { + list.innerHTML = `
No notifications yet.
`; + return; + } + + list.innerHTML = notifications.map(notification => ` +
+
${escapeHtml(notification.message)}
+
${formatDate(notification.createdAt)}
+
+ `).join(''); + } catch (error) { + console.error('Error loading notifications:', error); + document.getElementById('notificationList').innerHTML = + `
Failed to load notifications.
`; + } + } + + function setupNotificationDropdown() { + const btn = document.getElementById('notificationBtn'); + const dropdown = document.getElementById('notificationDropdown'); + const wrapper = document.getElementById('notificationWrapper'); + + btn.addEventListener('click', (event) => { + event.stopPropagation(); + dropdown.style.display = dropdown.style.display === 'block' ? 'none' : 'block'; + }); + + document.addEventListener('click', (event) => { + if (!wrapper.contains(event.target)) { + dropdown.style.display = 'none'; + } + }); + } + + async function openNotification(notificationId, incidentId) { + try { + const response = await fetch(`/notifications/${notificationId}/read`, { + method: 'PATCH' + }); + + if (!response.ok) { + throw new Error('Failed to mark notification as read'); + } + + window.location.href = `/viewincident.html?id=${incidentId}`; + } catch (error) { + console.error('Error opening notification:', error); + alert('Failed to open notification'); + } + } + document.addEventListener('DOMContentLoaded', () => { loadCurrentUser(); });