Skip to content

Commit

Permalink
[feat] Notification Settings Page
Browse files Browse the repository at this point in the history
  • Loading branch information
Dhanus3133 committed Aug 1, 2024
1 parent 99bf8c7 commit f79ad0f
Show file tree
Hide file tree
Showing 8 changed files with 407 additions and 7 deletions.
20 changes: 15 additions & 5 deletions openwisp_notifications/api/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,15 @@ def get_api_urls(api_views=None):
views.notification_read_redirect,
name='notification_read_redirect',
),
# WIP
path(
'user/user-setting/',
'user/<uuid:user_id>/user-setting/',
views.notification_setting_list,
name='notification_setting_list',
name='user_notification_setting_list',
),
path(
'user/user-setting/<uuid:pk>/',
'user/<uuid:user_id>/user-setting/<uuid:pk>/',
views.notification_setting,
name='notification_setting',
name='user_notification_setting',
),
path(
'notification/ignore/',
Expand All @@ -56,4 +55,15 @@ def get_api_urls(api_views=None):
views.notification_preference,
name='notification_preference',
),
# DEPRECATED
path(
'user/user-setting/',
views.notification_setting_list,
name='notification_setting_list',
),
path(
'user/user-setting/<uuid:pk>/',
views.notification_setting,
name='notification_setting',
),
]
9 changes: 9 additions & 0 deletions openwisp_notifications/api/views.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import PermissionDenied
from django.http import Http404, HttpResponseRedirect
from django.urls import reverse
from django_filters.rest_framework import DjangoFilterBackend
Expand Down Expand Up @@ -123,6 +124,14 @@ class BaseNotificationSettingView(GenericAPIView):
def get_queryset(self):
if getattr(self, 'swagger_fake_view', False):
return NotificationSetting.objects.none() # pragma: no cover

user_id = self.kwargs.get('user_id')

if user_id:
if not (self.request.user.id == user_id or self.request.user.is_staff):
raise PermissionDenied()
return NotificationSetting.objects.filter(user_id=user_id)

return NotificationSetting.objects.filter(user=self.request.user)


Expand Down
10 changes: 10 additions & 0 deletions openwisp_notifications/base/views.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from django.contrib.auth.mixins import LoginRequiredMixin
from django.views.generic import TemplateView


class NotificationSettingPage(LoginRequiredMixin, TemplateView):
template_name = 'openwisp_notifications/settings.html'
login_url = '/admin/login/'


notifiation_setting_page = NotificationSettingPage.as_view()
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
.container {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
h2 {
margin-bottom: 10px;
}
.global-settings {
margin-bottom: 20px;
}
.org-panel {
background-color: #fff;
border: 1px solid #ddd;
}
.org-header {
background-color: #e0e0e0;
padding: 10px;
font-weight: bold;
display: flex;
justify-content: space-between;
cursor: pointer;
}
.org-content {
padding: 10px;
display: none;
}
.org-content.active {
display: block;
}
table {
width: 100%;
border-collapse: separate;
border-spacing: 0;
}
th,
td {
padding: 8px;
text-align: left;
border-bottom: 1px solid #ddd;
}
th {
background-color: #f2f2f2;
}
th:not(:last-child),
td:not(:last-child) {
border-right: 1px solid #ddd;
}
th:not(:first-child),
td:not(:first-child) {
text-align: center;
}
.checkbox {
width: 15px;
height: 15px;
text-align: center;
}
.no-settings {
padding: 10px;
text-align: center;
color: #666;
}
149 changes: 149 additions & 0 deletions openwisp_notifications/static/openwisp-notifications/js/settings.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
'use strict';

(function ($) {
$(document).ready(function () {
fetchNotificationSettings();
});

function fetchNotificationSettings() {
$.ajax({
url: "/api/v1/notifications/user/user-setting",
method: "GET",
success: function (data) {
const groupedData = groupByOrganization(data.results);
renderNotificationSettings(groupedData);
initializeEventListeners();
},
});
}

function groupByOrganization(settings) {
const grouped = {};
settings.forEach((setting) => {
if (!grouped[setting.organization_name]) {
grouped[setting.organization_name] = [];
}
grouped[setting.organization_name].push(setting);
});
return grouped;
}

function renderNotificationSettings(data) {
const orgPanelsContainer = $("#org-panels");
orgPanelsContainer.empty(); // Clear existing content

Object.keys(data)
.sort()
.forEach((orgName) => {
const orgSettings = data[orgName].sort((a, b) =>
a.type.localeCompare(b.type)
);
const orgPanel = $(`
<div class="org-panel">
<div class="org-header">
<span>${orgName}</span><span class="toggle">▼</span>
</div>
<div class="org-content"></div>
</div>
`);
const orgContent = orgPanel.find(".org-content");

if (orgSettings.length > 0) {
const table = $(`
<table>
<tr>
<th>Settings</th>
<th>
<input type="checkbox" class="checkbox main-checkbox" data-column="email" /> Email
</th>
<th>
<input type="checkbox" class="checkbox main-checkbox" data-column="web" /> Web
</th>
</tr>
</table>
`);
orgSettings.forEach((setting) => {
const row = $(`
<tr>
<td>${setting.type}</td>
<td><input type="checkbox" class="checkbox email-checkbox" ${setting.email ? "checked" : ""} /></td>
<td><input type="checkbox" class="checkbox web-checkbox" ${setting.web ? "checked" : ""} /></td>
</tr>
`);
table.append(row);
});
orgContent.append(table);
updateMainCheckboxes(table);
} else {
orgContent.append(`<div class="no-settings">No settings available for this organization</div>`);
}

orgPanelsContainer.append(orgPanel);
});
}

function updateMainCheckboxes(table) {
const emailCheckboxes = table.find('.email-checkbox');
const webCheckboxes = table.find('.web-checkbox');
const emailMainCheckbox = table.find('.main-checkbox[data-column="email"]');
const webMainCheckbox = table.find('.main-checkbox[data-column="web"]');

emailMainCheckbox.prop('checked', emailCheckboxes.length === emailCheckboxes.filter(':checked').length);
webMainCheckbox.prop('checked', webCheckboxes.length === webCheckboxes.filter(':checked').length);
}

function initializeEventListeners() {
$(document).on('click', '.org-header', function () {
const toggle = $(this).find(".toggle");
toggle.text(toggle.text() === "▼" ? "▲" : "▼");
$(this).next(".org-content").toggleClass("active");
});

$(document).on('change', '.main-checkbox', function () {
const column = $(this).data("column");
$(this).closest("table").find(`.${column}-checkbox`).prop("checked", $(this).prop("checked"));
showToast('success', 'Settings updated successfully.');
});

$(document).on('change', '.email-checkbox, .web-checkbox', function () {
const column = $(this).hasClass("email-checkbox") ? "email" : "web";
const mainCheckbox = $(this).closest("table").find(`.main-checkbox[data-column="${column}"]`);
const checkboxes = $(this).closest("table").find(`.${column}-checkbox`);
mainCheckbox.prop("checked", checkboxes.length === checkboxes.filter(":checked").length);
showToast('success', 'Settings updated successfully.');
});

$("#global-email, #global-web").change(function () {
const isEmail = $(this).attr("id") === "global-email";
const columnClass = isEmail ? "email-checkbox" : "web-checkbox";
$(`.${columnClass}`).prop("checked", $(this).prop("checked"));
$(`.main-checkbox[data-column="${isEmail ? "email" : "web"}"]`).prop("checked", $(this).prop("checked"));
showToast('success', 'Global settings updated successfully.');
});
}

function showToast(level, message) {
const toast = $(`
<div class="ow-notification-toast ${level}">
<div class="icon ow-notify-close btn" role="button" tabindex="1"></div>
<div style="display:flex">
<div class="icon ow-notify-${level}"></div>
${message}
</div>
</div>
`);
$('.ow-notification-toast-wrapper').prepend(toast);
// toast.slideDown('slow', function () {
// setTimeout(function () {
// toast.slideUp('slow', function () {
// toast.remove();
// });
// }, 3000);
// });

$(document).on('click', '.ow-notification-toast .ow-notify-close.btn', function () {
toast.remove();
});
}

})(django.jQuery);
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
{% extends "admin/base_site.html" %}

{% load i18n %}
{% load static %}

{% block title %}{% trans "Notification Settings" %}{% endblock %}

{% block extrastyle %}
{{ block.super }}
<link rel="stylesheet" type="text/css" href="{% static 'openwisp-notifications/css/settings.css' %}" />
{% endblock extrastyle %}

{% block breadcrumbs %}
<div class='breadcrumbs'>
<a href='{% url 'admin:index' %}'>{% trans 'Home' %}</a>
&rsaquo; {% trans 'Notification Preferences' %}
</div>
{% endblock %}

{% block content %}
<div class="container">
<h2>Notification Preferences</h2>

<div class="global-settings">
<strong>Global Settings:</strong>
<label><input type="checkbox" id="global-email" checked /> Email</label>
<label><input type="checkbox" id="global-web" checked /> Web</label>
</div>

<div id="org-panels">
<!-- Organization panels will be dynamically inserted here -->
</div>
</div>
{% endblock content %}

{% block footer %}
{{ block.super }}
{% if request.user.is_authenticated %}
<script type="text/javascript" src="{% static 'openwisp-notifications/js/settings.js' %}"></script>
{% endif %}
{% endblock footer %}
Loading

0 comments on commit f79ad0f

Please sign in to comment.