From 879b8cf508eb603cc4c362ddc5081d748bb8dc3b Mon Sep 17 00:00:00 2001 From: sandeepsajan0 Date: Wed, 15 Jun 2022 17:32:06 +0530 Subject: [PATCH 01/13] [Draft]Add notifications page and list activities in tabs --- .../templates/activity/notifications.html | 48 +++++++++++++++++++ .../activity/templatetags/activity_tags.py | 7 +++ hypha/apply/activity/urls.py | 3 ++ hypha/apply/activity/views.py | 16 ++++++- 4 files changed, 73 insertions(+), 1 deletion(-) create mode 100644 hypha/apply/activity/templates/activity/notifications.html diff --git a/hypha/apply/activity/templates/activity/notifications.html b/hypha/apply/activity/templates/activity/notifications.html new file mode 100644 index 0000000000..4e36a728cc --- /dev/null +++ b/hypha/apply/activity/templates/activity/notifications.html @@ -0,0 +1,48 @@ +{% extends "base-apply.html" %} +{% load i18n static activity_tags %} + +{% block content %} +
+ +
+ +
+ {# Tab 1 #} +
+ {% for comment in object_list %} + {% if comment.type == 'comment' %} + +

{{ comment.source_content_type.name|source_type }}({{ comment.source.title|truncatechars:15 }}) + : {{ comment.user }} made a comment

+ {% endif %} + {% endfor %} +
+ {# Tab 2 #} + +
+ +{% endblock %} + +{% block extra_js %} + +{% endblock %} diff --git a/hypha/apply/activity/templatetags/activity_tags.py b/hypha/apply/activity/templatetags/activity_tags.py index 2df2a15d1c..406010ede2 100644 --- a/hypha/apply/activity/templatetags/activity_tags.py +++ b/hypha/apply/activity/templatetags/activity_tags.py @@ -55,3 +55,10 @@ def display_for(activity, user): def visibility_options(activity, user): choices = activity.visibility_choices_for(user) return json.dumps(choices) + + +@register.filter +def source_type(value): + if value and "submission" in value: + return "Submission" + return value.capitalize() diff --git a/hypha/apply/activity/urls.py b/hypha/apply/activity/urls.py index 19c0855275..4ba4a55f80 100644 --- a/hypha/apply/activity/urls.py +++ b/hypha/apply/activity/urls.py @@ -1,8 +1,11 @@ from django.urls import include, path +from .views import NotificationsView + app_name = 'activity' urlpatterns = [ path('anymail/', include('anymail.urls')), + path('notifications/', NotificationsView.as_view(), name='notifications') ] diff --git a/hypha/apply/activity/views.py b/hypha/apply/activity/views.py index 7b75221f08..3d6315ca6a 100644 --- a/hypha/apply/activity/views.py +++ b/hypha/apply/activity/views.py @@ -1,6 +1,8 @@ from django.utils import timezone -from django.views.generic import CreateView +from django.utils.decorators import method_decorator +from django.views.generic import CreateView, ListView +from hypha.apply.users.decorators import staff_required from hypha.apply.utils.views import DelegatedViewMixin from .forms import CommentForm @@ -57,3 +59,15 @@ def get_form_kwargs(self): kwargs = super().get_form_kwargs() kwargs.pop('instance') return kwargs + + +@method_decorator(staff_required, name='dispatch') +class NotificationsView(ListView): + model = Activity + template_name = 'activity/notifications.html' + + def get_context_data(self, *, object_list=None, **kwargs): + context = super(NotificationsView, self).get_context_data() + context['comments'] = Activity.comments.all().order_by('-timestamp') + context['actions'] = Activity.actions.all().order_by('-timestamp') + return context From 08b22b2ade17b2add64756fd73b13012c62f4875 Mon Sep 17 00:00:00 2001 From: sandeepsajan0 Date: Thu, 16 Jun 2022 12:22:23 +0530 Subject: [PATCH 02/13] Add support for different type of users, and update template and template tags --- .../templates/activity/notifications.html | 5 +++-- .../activity/templatetags/activity_tags.py | 2 +- hypha/apply/activity/views.py | 21 +++++++++++++++---- 3 files changed, 21 insertions(+), 7 deletions(-) diff --git a/hypha/apply/activity/templates/activity/notifications.html b/hypha/apply/activity/templates/activity/notifications.html index 4e36a728cc..0f20cb70c5 100644 --- a/hypha/apply/activity/templates/activity/notifications.html +++ b/hypha/apply/activity/templates/activity/notifications.html @@ -4,6 +4,7 @@ {% block content %}
+

{% trans "Notifications" %}

@@ -24,7 +25,7 @@ {% for comment in object_list %} {% if comment.type == 'comment' %} -

{{ comment.source_content_type.name|source_type }}({{ comment.source.title|truncatechars:15 }}) +

{{ comment.source_content_type.name|source_type }} ({{ comment.source.title|truncatechars:15 }}) : {{ comment.user }} made a comment

{% endif %} {% endfor %} @@ -33,7 +34,7 @@
{% for action in actions %} -

{{ action.source_content_type.name|source_type }}({{ action.source.title|truncatechars:15 }}) +

{{ action.source_content_type.name|source_type }} ({{ action.source.title|truncatechars:15 }}) : {{ action.message }} {{ action.related_object.title }}

{% endfor %} diff --git a/hypha/apply/activity/templatetags/activity_tags.py b/hypha/apply/activity/templatetags/activity_tags.py index 406010ede2..5b0dc4b115 100644 --- a/hypha/apply/activity/templatetags/activity_tags.py +++ b/hypha/apply/activity/templatetags/activity_tags.py @@ -61,4 +61,4 @@ def visibility_options(activity, user): def source_type(value): if value and "submission" in value: return "Submission" - return value.capitalize() + return str(value).capitalize() diff --git a/hypha/apply/activity/views.py b/hypha/apply/activity/views.py index 3d6315ca6a..197e042f53 100644 --- a/hypha/apply/activity/views.py +++ b/hypha/apply/activity/views.py @@ -2,7 +2,8 @@ from django.utils.decorators import method_decorator from django.views.generic import CreateView, ListView -from hypha.apply.users.decorators import staff_required +from hypha.apply.funds.models.submissions import ApplicationSubmission +from hypha.apply.users.decorators import login_required from hypha.apply.utils.views import DelegatedViewMixin from .forms import CommentForm @@ -61,13 +62,25 @@ def get_form_kwargs(self): return kwargs -@method_decorator(staff_required, name='dispatch') +@method_decorator(login_required, name='dispatch') class NotificationsView(ListView): model = Activity template_name = 'activity/notifications.html' def get_context_data(self, *, object_list=None, **kwargs): context = super(NotificationsView, self).get_context_data() - context['comments'] = Activity.comments.all().order_by('-timestamp') - context['actions'] = Activity.actions.all().order_by('-timestamp') + user = self.request.user + if user.is_applicant or user.is_partner: + context['comments'] = Activity.comments.filter(source__user=user).order_by('-timestamp') + context['actions'] = Activity.actions.filter(source__user=user).order_by('-timestamp') + elif user.is_reviewer: + reviewer_submissions = ApplicationSubmission.objects.filter(reviewers=user).values_list("id", flat=True) + context['comments'] = Activity.comments.filter(source_object_id__in=reviewer_submissions).order_by('-timestamp') + context['actions'] = Activity.actions.filter(source_object_id__in=reviewer_submissions).order_by('-timestamp') + elif user.is_apply_staff or user.is_apply_staff_admin: + context['comments'] = Activity.comments.all().order_by('-timestamp') + context['actions'] = Activity.actions.all().order_by('-timestamp') + else: + context['comments'] = Activity.comments.filter(user=user).order_by('-timestamp') + context['actions'] = Activity.actions.filter(user=user).order_by('-timestamp') return context From 64975243ccfd4684e95206ca83ee7ac3e0b5aa0d Mon Sep 17 00:00:00 2001 From: sandeepsajan0 Date: Thu, 16 Jun 2022 18:36:18 +0530 Subject: [PATCH 03/13] Update notifications list UI,add related object link and date --- hypha/apply/activity/filters.py | 13 ++++++ .../templates/activity/notifications.html | 41 +++++++++++++------ hypha/apply/activity/views.py | 1 + 3 files changed, 43 insertions(+), 12 deletions(-) create mode 100644 hypha/apply/activity/filters.py diff --git a/hypha/apply/activity/filters.py b/hypha/apply/activity/filters.py new file mode 100644 index 0000000000..73391af450 --- /dev/null +++ b/hypha/apply/activity/filters.py @@ -0,0 +1,13 @@ +import django_filters + +from .models import Activity + + +class NotificationFilter(django_filters.FilterSet): + + class Meta: + model = Activity + fields = { + 'timestamp', + 'source_content_type', + } diff --git a/hypha/apply/activity/templates/activity/notifications.html b/hypha/apply/activity/templates/activity/notifications.html index 0f20cb70c5..c6bc4e573a 100644 --- a/hypha/apply/activity/templates/activity/notifications.html +++ b/hypha/apply/activity/templates/activity/notifications.html @@ -1,5 +1,5 @@ {% extends "base-apply.html" %} -{% load i18n static activity_tags %} +{% load i18n static activity_tags apply_tags %} {% block content %}
@@ -22,23 +22,40 @@

{% trans "Notificatio
{# Tab 1 #}
- {% for comment in object_list %} - {% if comment.type == 'comment' %} - -

{{ comment.source_content_type.name|source_type }} ({{ comment.source.title|truncatechars:15 }}) - : {{ comment.user }} made a comment

- {% endif %} + + + + + {% for comment in comments %} +
+
+

{{ comment.source_content_type.name|source_type }}

+
+
+
+

{{ comment.source.title|capfirst|truncatechars:15 }} + : {{ comment.user }} made a comment – {{ comment.timestamp|date:"SHORT_DATETIME_FORMAT" }}

+
+
+
{% endfor %}
{# Tab 2 #}
-
{% for action in actions %} -

{{ action.source_content_type.name|source_type }} ({{ action.source.title|truncatechars:15 }}) - : {{ action.message }} {{ action.related_object.title }} -

+
+
+

{{ action.source_content_type.name|source_type }}

+
+
+
+

{{ action.source.title|capfirst|truncatechars:15 }} + : {{ action.message }} {% if action.related_object %}{{ action.related_object|model_verbose_name }}{% endif %} + – {{ action.timestamp|date:"SHORT_DATETIME_FORMAT" }}

+
+
+
{% endfor %} -
diff --git a/hypha/apply/activity/views.py b/hypha/apply/activity/views.py index 197e042f53..407d0a67af 100644 --- a/hypha/apply/activity/views.py +++ b/hypha/apply/activity/views.py @@ -83,4 +83,5 @@ def get_context_data(self, *, object_list=None, **kwargs): else: context['comments'] = Activity.comments.filter(user=user).order_by('-timestamp') context['actions'] = Activity.actions.filter(user=user).order_by('-timestamp') + # context['filter'] = NotificationFilter(self.request.GET, queryset=self.get_queryset()) return context From a872279d1e0341409dbfda02320783f1ee98b165 Mon Sep 17 00:00:00 2001 From: sandeepsajan0 Date: Tue, 21 Jun 2022 08:50:54 +0530 Subject: [PATCH 04/13] Add notification bell to show latest notification and link it with all notifications page --- hypha/apply/activity/context_processors.py | 8 ++++ .../templates/activity/notifications.html | 22 ++++++++-- hypha/apply/activity/views.py | 31 +++++++------ hypha/settings/base.py | 1 + .../components/_activity-notifications.scss | 44 +++++++++++++++++++ hypha/static_src/src/sass/apply/main.scss | 1 + hypha/templates/base-apply.html | 19 +++++++- hypha/templates/includes/sprites.html | 4 ++ 8 files changed, 112 insertions(+), 18 deletions(-) create mode 100644 hypha/apply/activity/context_processors.py create mode 100644 hypha/static_src/src/sass/apply/components/_activity-notifications.scss diff --git a/hypha/apply/activity/context_processors.py b/hypha/apply/activity/context_processors.py new file mode 100644 index 0000000000..0ce63073fc --- /dev/null +++ b/hypha/apply/activity/context_processors.py @@ -0,0 +1,8 @@ +from .models import Activity + + +def notification_context(request): + context_data = dict() + if request.user.is_authenticated: + context_data['latest_notifications'] = Activity.objects.filter(user=request.user).order_by('-timestamp')[:5] + return context_data diff --git a/hypha/apply/activity/templates/activity/notifications.html b/hypha/apply/activity/templates/activity/notifications.html index c6bc4e573a..f216ae4205 100644 --- a/hypha/apply/activity/templates/activity/notifications.html +++ b/hypha/apply/activity/templates/activity/notifications.html @@ -22,10 +22,24 @@

{% trans "Notificatio
{# Tab 1 #}
- - - - + + + + + + + + + + + + + + + + + + {% for comment in comments %}
diff --git a/hypha/apply/activity/views.py b/hypha/apply/activity/views.py index 407d0a67af..67ab9604e7 100644 --- a/hypha/apply/activity/views.py +++ b/hypha/apply/activity/views.py @@ -2,7 +2,6 @@ from django.utils.decorators import method_decorator from django.views.generic import CreateView, ListView -from hypha.apply.funds.models.submissions import ApplicationSubmission from hypha.apply.users.decorators import login_required from hypha.apply.utils.views import DelegatedViewMixin @@ -70,18 +69,24 @@ class NotificationsView(ListView): def get_context_data(self, *, object_list=None, **kwargs): context = super(NotificationsView, self).get_context_data() user = self.request.user - if user.is_applicant or user.is_partner: - context['comments'] = Activity.comments.filter(source__user=user).order_by('-timestamp') - context['actions'] = Activity.actions.filter(source__user=user).order_by('-timestamp') - elif user.is_reviewer: - reviewer_submissions = ApplicationSubmission.objects.filter(reviewers=user).values_list("id", flat=True) - context['comments'] = Activity.comments.filter(source_object_id__in=reviewer_submissions).order_by('-timestamp') - context['actions'] = Activity.actions.filter(source_object_id__in=reviewer_submissions).order_by('-timestamp') - elif user.is_apply_staff or user.is_apply_staff_admin: - context['comments'] = Activity.comments.all().order_by('-timestamp') - context['actions'] = Activity.actions.all().order_by('-timestamp') + comments = Activity.comments.all() + actions = Activity.actions.all() + + # Getting issues with source's reverse relationship + # if user.is_applicant or user.is_partner: + # context['comments'] = comments.filter(source__user=user).order_by('-timestamp') + # context['actions'] = actions.filter(source__user=user).order_by('-timestamp') + # elif user.is_reviewer: + # reviewer_submissions = ApplicationSubmission.objects.filter(reviewers=user).values_list("id", flat=True) + # context['comments'] = comments.filter(source_object_id__in=reviewer_submissions).order_by('-timestamp') + # context['actions'] = actions.filter(source_object_id__in=reviewer_submissions).order_by('-timestamp') + + if user.is_apply_staff or user.is_apply_staff_admin: + context['comments'] = comments.all().order_by('-timestamp') + context['actions'] = actions.all().order_by('-timestamp') else: - context['comments'] = Activity.comments.filter(user=user).order_by('-timestamp') - context['actions'] = Activity.actions.filter(user=user).order_by('-timestamp') + context['comments'] = comments.filter(user=user).order_by('-timestamp') + context['actions'] = actions.filter(user=user).order_by('-timestamp') + # WIP Filters # context['filter'] = NotificationFilter(self.request.GET, queryset=self.get_queryset()) return context diff --git a/hypha/settings/base.py b/hypha/settings/base.py index 07e5af2b9d..7524adc3ed 100644 --- a/hypha/settings/base.py +++ b/hypha/settings/base.py @@ -179,6 +179,7 @@ 'social_django.context_processors.login_redirect', 'hypha.apply.projects.context_processors.projects_enabled', 'hypha.cookieconsent.context_processors.cookies_accepted', + 'hypha.apply.activity.context_processors.notification_context', ], }, }, diff --git a/hypha/static_src/src/sass/apply/components/_activity-notifications.scss b/hypha/static_src/src/sass/apply/components/_activity-notifications.scss new file mode 100644 index 0000000000..86d2dfdd3d --- /dev/null +++ b/hypha/static_src/src/sass/apply/components/_activity-notifications.scss @@ -0,0 +1,44 @@ +.dropbtn { + padding: 5px 12px; + cursor: pointer; +} + +.dropdown { + position: relative; + display: inline-block; + + &:hover { + .dropdown-content { + display: block; + } + } +} + +.dropdown-content { + display: none; + position: absolute; + right: 0; + font-size: 15px; + background-color: #F0F0F0; + min-width: 400px; + box-shadow: 8px 8px 16px 8px rgba(0,0,0,0.2); + z-index: 1; + + p { + color: black; + padding: 0px 10px; + display: block; + } +} + +.dropdown-item { + border-bottom: .1px solid grey; +} + +.show-all { + text-align: center; +} + +.source-label { + background-color: #7dc588; +} diff --git a/hypha/static_src/src/sass/apply/main.scss b/hypha/static_src/src/sass/apply/main.scss index 150d659960..c0a9ee4695 100644 --- a/hypha/static_src/src/sass/apply/main.scss +++ b/hypha/static_src/src/sass/apply/main.scss @@ -68,6 +68,7 @@ @import 'components/reminder-sidebar'; @import 'components/two-factor'; @import 'components/determination'; +@import 'components/activity-notifications'; // Layout @import 'layout/header'; diff --git a/hypha/templates/base-apply.html b/hypha/templates/base-apply.html index 33f22e7256..a9d4b8e1fc 100644 --- a/hypha/templates/base-apply.html +++ b/hypha/templates/base-apply.html @@ -1,4 +1,4 @@ -{% load i18n static wagtailuserbar wagtailcore_tags wagtailimages_tags navigation_tags util_tags hijack cookieconsent_tags %} +{% load i18n static wagtailuserbar wagtailcore_tags wagtailimages_tags navigation_tags util_tags hijack cookieconsent_tags activity_tags %} {% wagtail_site as current_site %} @@ -104,6 +104,23 @@
+ {% if latest_notifications %} + + {% endif %} {{ request.user }} diff --git a/hypha/templates/includes/sprites.html b/hypha/templates/includes/sprites.html index dbd3555ac3..ed6584bc88 100644 --- a/hypha/templates/includes/sprites.html +++ b/hypha/templates/includes/sprites.html @@ -119,6 +119,10 @@ + + + + From 02271a7362dc2c63610975af10b00374b63f971f Mon Sep 17 00:00:00 2001 From: sandeepsajan0 Date: Thu, 23 Jun 2022 09:53:24 +0530 Subject: [PATCH 05/13] Add notification heading, make bellicon clickable, and align nav icons --- .../src/javascript/apply/notifications.js | 17 +++++++++++++++++ .../components/_activity-notifications.scss | 12 +++++------- hypha/templates/base-apply.html | 8 +++++--- 3 files changed, 27 insertions(+), 10 deletions(-) create mode 100644 hypha/static_src/src/javascript/apply/notifications.js diff --git a/hypha/static_src/src/javascript/apply/notifications.js b/hypha/static_src/src/javascript/apply/notifications.js new file mode 100644 index 0000000000..43e3de5143 --- /dev/null +++ b/hypha/static_src/src/javascript/apply/notifications.js @@ -0,0 +1,17 @@ +function notificationToggle() { + document.getElementById("notificationDropdown").classList.toggle("show"); +} + +// Close the dropdown menu if the user clicks outside of it +window.onclick = function(event) { + if (!event.target.matches('.dropbtn, .dropbtn *')) { + var dropdowns = document.getElementsByClassName("dropdown-content"); + var i; + for (i = 0; i < dropdowns.length; i++) { + var openDropdown = dropdowns[i]; + if (openDropdown.classList.contains('show')) { + openDropdown.classList.remove('show'); + } + } + } +} diff --git a/hypha/static_src/src/sass/apply/components/_activity-notifications.scss b/hypha/static_src/src/sass/apply/components/_activity-notifications.scss index 86d2dfdd3d..9cdc26e2de 100644 --- a/hypha/static_src/src/sass/apply/components/_activity-notifications.scss +++ b/hypha/static_src/src/sass/apply/components/_activity-notifications.scss @@ -1,17 +1,11 @@ .dropbtn { - padding: 5px 12px; + padding: 7px 12px; cursor: pointer; } .dropdown { position: relative; display: inline-block; - - &:hover { - .dropdown-content { - display: block; - } - } } .dropdown-content { @@ -42,3 +36,7 @@ .source-label { background-color: #7dc588; } + +.show { + display:block; +} diff --git a/hypha/templates/base-apply.html b/hypha/templates/base-apply.html index a9d4b8e1fc..c67b94e852 100644 --- a/hypha/templates/base-apply.html +++ b/hypha/templates/base-apply.html @@ -30,6 +30,7 @@ + {% if COOKIES_ACCEPTED and MATOMO_URL and MATOMO_SITEID %} {# we are only expecting strings, so make sure we escape the values #} - + {% if COOKIES_ACCEPTED and MATOMO_URL and MATOMO_SITEID %} {# we are only expecting strings, so make sure we escape the values #} + {% if latest_notifications %} + {% endif %} {% if COOKIES_ACCEPTED and MATOMO_URL and MATOMO_SITEID %} {# we are only expecting strings, so make sure we escape the values #}