diff --git a/netbox/extras/models/notifications.py b/netbox/extras/models/notifications.py index 428be3b2a..6ad91f0ae 100644 --- a/netbox/extras/models/notifications.py +++ b/netbox/extras/models/notifications.py @@ -78,7 +78,13 @@ class Notification(models.Model): return super().__str__() def get_absolute_url(self): - return self.object.get_absolute_url() + return reverse('extras:notifications') + + def get_read_url(self): + return reverse('extras:notification_read', kwargs={'pk': self.pk}) + + def get_dismiss_url(self): + return reverse('extras:notification_dismiss', kwargs={'pk': self.pk}) def clean(self): super().clean() diff --git a/netbox/extras/tables/tables.py b/netbox/extras/tables/tables.py index f4804874e..39967e253 100644 --- a/netbox/extras/tables/tables.py +++ b/netbox/extras/tables/tables.py @@ -266,14 +266,35 @@ class BookmarkTable(NetBoxTable): default_columns = ('object', 'object_type', 'created') -class NotificationTable(NetBoxTable): +class SubscriptionTable(NetBoxTable): object_type = columns.ContentTypeColumn( - verbose_name=_('Object Types'), + verbose_name=_('Object Type'), ) object = tables.Column( verbose_name=_('Object'), linkify=True ) + actions = columns.ActionsColumn( + actions=('delete',) + ) + + class Meta(NetBoxTable.Meta): + model = Subscription + fields = ('pk', 'object', 'object_type', 'created') + default_columns = ('object', 'object_type', 'created') + + +class NotificationTable(NetBoxTable): + object_type = columns.ContentTypeColumn( + verbose_name=_('Object Type'), + ) + object = tables.Column( + verbose_name=_('Object'), + linkify={ + 'viewname': 'extras:notification_read', + 'args': [tables.A('pk')], + } + ) created = columns.DateTimeColumn( timespec='minutes', verbose_name=_('Created'), @@ -290,6 +311,9 @@ class NotificationTable(NetBoxTable): model = Notification fields = ('pk', 'object', 'object_type', 'created', 'read') default_columns = ('object', 'object_type', 'created', 'read') + row_attrs = { + 'data-unread': lambda record: not record.read, + } class NotificationGroupTable(NetBoxTable): @@ -304,24 +328,6 @@ class NotificationGroupTable(NetBoxTable): default_columns = ('name', 'description') -class SubscriptionTable(NetBoxTable): - object_type = columns.ContentTypeColumn( - verbose_name=_('Object Types'), - ) - object = tables.Column( - verbose_name=_('Object'), - linkify=True - ) - actions = columns.ActionsColumn( - actions=('delete',) - ) - - class Meta(NetBoxTable.Meta): - model = Subscription - fields = ('pk', 'object', 'object_type', 'created') - default_columns = ('object', 'object_type', 'created') - - class WebhookTable(NetBoxTable): name = tables.Column( verbose_name=_('Name'), diff --git a/netbox/extras/views.py b/netbox/extras/views.py index fd836ed55..2fb274de4 100644 --- a/netbox/extras/views.py +++ b/netbox/extras/views.py @@ -428,9 +428,24 @@ class NotificationsView(LoginRequiredMixin, View): @register_model_view(Notification, 'read') class NotificationReadView(LoginRequiredMixin, View): - + """ + Mark the Notification read and redirect the user to its attached object. + """ def get(self, request, pk): - request.user.notifications.filter(pk=pk).update(read=timezone.now()) + notification = get_object_or_404(Notification, pk=pk) + notification.read = timezone.now() + notification.save() + + return redirect(notification.object.get_absolute_url()) + + +@register_model_view(Notification, 'dismiss') +class NotificationDismissView(LoginRequiredMixin, View): + """ + A convenience view which allows deleting notifications with one click. + """ + def get(self, request, pk): + request.user.notifications.filter(pk=pk).delete() notifications = request.user.notifications.unread()[:10] return render(request, 'htmx/notifications.html', { diff --git a/netbox/project-static/dist/netbox.css b/netbox/project-static/dist/netbox.css index 36ed4defc..62c4b9af2 100644 Binary files a/netbox/project-static/dist/netbox.css and b/netbox/project-static/dist/netbox.css differ diff --git a/netbox/project-static/styles/custom/_notifications.scss b/netbox/project-static/styles/custom/_notifications.scss new file mode 100644 index 000000000..41feb0b8c --- /dev/null +++ b/netbox/project-static/styles/custom/_notifications.scss @@ -0,0 +1,8 @@ +@use 'sass:map'; + +// Highlight unread notifications +tr[data-unread=True] { + td { + background-color: rgba(map.get($theme-colors, "green"), 0.15); + } +} diff --git a/netbox/project-static/styles/netbox.scss b/netbox/project-static/styles/netbox.scss index af2905312..0e1b44d59 100644 --- a/netbox/project-static/styles/netbox.scss +++ b/netbox/project-static/styles/netbox.scss @@ -24,3 +24,4 @@ @import 'custom/interfaces'; @import 'custom/markdown'; @import 'custom/misc'; +@import 'custom/notifications'; diff --git a/netbox/templates/htmx/notifications.html b/netbox/templates/htmx/notifications.html index bd9141327..14d2f7130 100644 --- a/netbox/templates/htmx/notifications.html +++ b/netbox/templates/htmx/notifications.html @@ -4,11 +4,11 @@