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 @@
Incidents
Profile
Admin
+
+
+
+
Loading...
-
@@ -124,13 +392,24 @@
Welcome!
@@ -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();
});