feat(sprint-6): Phase 6 — Notifications (WebSocket) + PWA
Deploy to Production / test (push) Has been cancelled
Deploy to Production / deploy (push) Has been cancelled

- WebSocket: Spring STOMP + SockJS, NotificationService, persistent notifications table
- NotificationController: GET/PUT endpoints for notification management
- Frontend: notification bell with unread badge, dropdown panel, real-time via STOMP
- PWA: manifest.json, service worker (manual sw.js), offline page, install prompt
- PWA icons (192+512), dark theme colors, standalone display
- Full i18n (de/en) for notifications and PWA
- Flyway V10 migration for notifications table
- spring-boot-starter-websocket dependency added
This commit is contained in:
Patrick Plate
2026-06-12 23:02:44 +02:00
parent 076fd6f9b3
commit 599514c0db
39 changed files with 6684 additions and 3217 deletions
@@ -0,0 +1,64 @@
package de.cannamanage.domain.entity;
import de.cannamanage.domain.enums.NotificationType;
import jakarta.persistence.*;
import java.util.UUID;
/**
* Persistent notification entity — survives page refresh.
* Delivered in real-time via WebSocket, but also queryable via REST.
*/
@Entity
@Table(name = "notifications", indexes = {
@Index(name = "idx_notifications_user", columnList = "user_id, read, created_at DESC")
})
public class Notification extends AbstractTenantEntity {
@Column(name = "user_id", nullable = false)
private UUID userId;
@Enumerated(EnumType.STRING)
@Column(name = "type", nullable = false, length = 50)
private NotificationType type;
@Column(name = "title", nullable = false)
private String title;
@Column(name = "message", nullable = false, columnDefinition = "TEXT")
private String message;
@Column(name = "link", length = 500)
private String link;
@Column(name = "read", nullable = false)
private boolean read = false;
public Notification() {}
public Notification(UUID userId, NotificationType type, String title, String message, String link) {
this.userId = userId;
this.type = type;
this.title = title;
this.message = message;
this.link = link;
}
public UUID getUserId() { return userId; }
public void setUserId(UUID userId) { this.userId = userId; }
public NotificationType getType() { return type; }
public void setType(NotificationType type) { this.type = type; }
public String getTitle() { return title; }
public void setTitle(String title) { this.title = title; }
public String getMessage() { return message; }
public void setMessage(String message) { this.message = message; }
public String getLink() { return link; }
public void setLink(String link) { this.link = link; }
public boolean isRead() { return read; }
public void setRead(boolean read) { this.read = read; }
}
@@ -0,0 +1,11 @@
package de.cannamanage.domain.enums;
/**
* Types of notifications sent to users.
*/
public enum NotificationType {
QUOTA_WARNING,
BATCH_RECALLED,
DISTRIBUTION_RECORDED,
SUBSCRIPTION_EXPIRING
}