From 3a8fa8cdcf77a4df9f4a43cd117c155b323e6c02 Mon Sep 17 00:00:00 2001 From: jedi Date: Tue, 5 Nov 2024 23:25:44 +0100 Subject: [PATCH 01/18] show an animation to signify that the page is still loading --- web/src/components/AsyncLoader.vue | 133 +++++++++++++++++++++++++++++ web/src/views/Items.vue | 128 ++++++++++++++------------- web/src/views/Ticket.vue | 111 ++++++++++++------------ web/src/views/Tickets.vue | 95 +++++++++++---------- 4 files changed, 306 insertions(+), 161 deletions(-) create mode 100644 web/src/components/AsyncLoader.vue diff --git a/web/src/components/AsyncLoader.vue b/web/src/components/AsyncLoader.vue new file mode 100644 index 0000000..00bf841 --- /dev/null +++ b/web/src/components/AsyncLoader.vue @@ -0,0 +1,133 @@ + + + + + \ No newline at end of file diff --git a/web/src/views/Items.vue b/web/src/views/Items.vue index 9d127f0..5abcf5d 100644 --- a/web/src/views/Items.vue +++ b/web/src/views/Items.vue @@ -1,77 +1,82 @@ diff --git a/web/src/components/inputs/AsyncButton.vue b/web/src/components/inputs/AsyncButton.vue new file mode 100644 index 0000000..a3c1712 --- /dev/null +++ b/web/src/components/inputs/AsyncButton.vue @@ -0,0 +1,47 @@ + + + + + \ No newline at end of file From 55cef1128eaa59c79122fccc3f3ce63fed42f3e4 Mon Sep 17 00:00:00 2001 From: jedi Date: Tue, 5 Nov 2024 23:36:05 +0100 Subject: [PATCH 03/18] extract the search box into its own component --- web/src/components/Navbar.vue | 17 ++++------ web/src/components/inputs/SearchBox.vue | 43 +++++++++++++++++++++++++ web/src/store.js | 13 ++++++-- 3 files changed, 59 insertions(+), 14 deletions(-) create mode 100644 web/src/components/inputs/SearchBox.vue diff --git a/web/src/components/Navbar.vue b/web/src/components/Navbar.vue index fb0736b..686f324 100644 --- a/web/src/components/Navbar.vue +++ b/web/src/components/Navbar.vue @@ -29,16 +29,7 @@ -
- -
+
Dashboard + + diff --git a/web/src/views/admin/Debug.vue b/web/src/views/admin/Debug.vue new file mode 100644 index 0000000..0c83e76 --- /dev/null +++ b/web/src/views/admin/Debug.vue @@ -0,0 +1,87 @@ + + + + + \ No newline at end of file diff --git a/web/src/views/admin/Notifications.vue b/web/src/views/admin/Notifications.vue new file mode 100644 index 0000000..cc2b615 --- /dev/null +++ b/web/src/views/admin/Notifications.vue @@ -0,0 +1,36 @@ + + + + + \ No newline at end of file diff --git a/web/src/views/admin/Settings.vue b/web/src/views/admin/Settings.vue new file mode 100644 index 0000000..2f15a83 --- /dev/null +++ b/web/src/views/admin/Settings.vue @@ -0,0 +1,109 @@ + + + + + \ No newline at end of file From 51f0a90bc70d12239d95dc0d4d4541dd97c3bc2d Mon Sep 17 00:00:00 2001 From: jedi Date: Wed, 26 Jun 2024 21:22:34 +0200 Subject: [PATCH 08/18] stash --- web/src/components/Timeline.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/src/components/Timeline.vue b/web/src/components/Timeline.vue index 88bfa94..f3ac1d8 100644 --- a/web/src/components/Timeline.vue +++ b/web/src/components/Timeline.vue @@ -73,7 +73,7 @@ import TimelineMail from "@/components/TimelineMail.vue"; import TimelineComment from "@/components/TimelineComment.vue"; import TimelineStateChange from "@/components/TimelineStateChange.vue"; -import {mapActions, mapGetters} from "vuex"; +import {mapGetters} from "vuex"; import TimelineAssignment from "@/components/TimelineAssignment.vue"; import TimelineRelatedItem from "@/components/TimelineRelatedItem.vue"; import TimelineShippingVoucher from "@/components/TimelineShippingVoucher.vue"; From 0cb45c73dbd1e842b2acd45db3dfb5524fcb4343 Mon Sep 17 00:00:00 2001 From: jedi Date: Fri, 28 Jun 2024 00:49:09 +0200 Subject: [PATCH 09/18] stash --- core/notifications/api_v2.py | 15 ++++++++++++-- web/src/store.js | 10 ++++++++- web/src/views/admin/Notifications.vue | 30 +++++++++------------------ 3 files changed, 32 insertions(+), 23 deletions(-) diff --git a/core/notifications/api_v2.py b/core/notifications/api_v2.py index f50a453..e459d01 100644 --- a/core/notifications/api_v2.py +++ b/core/notifications/api_v2.py @@ -5,7 +5,7 @@ from rest_framework.decorators import api_view, permission_classes from rest_framework.permissions import IsAuthenticated from rest_framework.response import Response -from notifications.models import MessageTemplate +from notifications.models import MessageTemplate, UserNotificationChannel from rest_framework import serializers from notifications.templates import TEMPLATE_VARS @@ -17,11 +17,22 @@ class MessageTemplateSerializer(serializers.ModelSerializer): fields = '__all__' +class UserNotificationChannelSerializer(serializers.ModelSerializer): + class Meta: + model = UserNotificationChannel + fields = '__all__' + + class MessageTemplateViewSet(viewsets.ModelViewSet): serializer_class = MessageTemplateSerializer queryset = MessageTemplate.objects.all() +class UserNotificationChannelViewSet(viewsets.ModelViewSet): + serializer_class = UserNotificationChannelSerializer + queryset = UserNotificationChannel.objects.all() + + @api_view(['GET']) @permission_classes([IsAuthenticated]) @permission_required('tickets.add_issuethread_manual', raise_exception=True) # TDOO: change this permission @@ -31,7 +42,7 @@ def get_template_vars(self): router = routers.SimpleRouter() router.register(r'message_templates', MessageTemplateViewSet) - +router.register(r'user_notification_channels', UserNotificationChannelViewSet) urlpatterns = ([ re_path('message_template_variables', get_template_vars), ] + router.urls) diff --git a/web/src/store.js b/web/src/store.js index 6f650d5..b5fba1c 100644 --- a/web/src/store.js +++ b/web/src/store.js @@ -22,6 +22,7 @@ const store = createStore({ messageTemplates: [], messageTemplateVariables: [], shippingVouchers: [], + userNotificationChannels: [], lastEvent: '37C3', lastUsed: {}, @@ -532,7 +533,14 @@ const store = createStore({ state.fetchedData.tickets = 0; await Promise.all([dispatch('loadTickets'), dispatch('fetchShippingVouchers')]); } - } + }, + async fetchUserNotificationChannels({commit, state}) { + if (!state.user.token) return; + const {data, success} = await http.get('/2/user_notification_channels/', state.user.token); + if (data && success) { + state.userNotificationChannels = data; + } + }, }, plugins: [ persistentStatePlugin({ // TODO change remember to some kind of enable field diff --git a/web/src/views/admin/Notifications.vue b/web/src/views/admin/Notifications.vue index cc2b615..5a451fc 100644 --- a/web/src/views/admin/Notifications.vue +++ b/web/src/views/admin/Notifications.vue @@ -1,22 +1,9 @@ From 8e04cea448ead2964b74b5e5e315a5e6fd9e61b0 Mon Sep 17 00:00:00 2001 From: jedi Date: Sat, 29 Jun 2024 16:48:08 +0200 Subject: [PATCH 10/18] stash --- core/authentication/api_v2.py | 41 +++++---------------------- core/authentication/serializers.py | 32 +++++++++++++++++++++ core/notifications/api_v2.py | 3 ++ web/src/views/admin/Notifications.vue | 2 +- 4 files changed, 43 insertions(+), 35 deletions(-) create mode 100644 core/authentication/serializers.py diff --git a/core/authentication/api_v2.py b/core/authentication/api_v2.py index 514a697..2547b6d 100644 --- a/core/authentication/api_v2.py +++ b/core/authentication/api_v2.py @@ -12,25 +12,7 @@ from knox.models import AuthToken from knox.views import LoginView as KnoxLoginView from authentication.models import ExtendedUser - - -class UserSerializer(serializers.ModelSerializer): - permissions = serializers.SerializerMethodField() - groups = serializers.SlugRelatedField(many=True, read_only=True, slug_field='name') - - class Meta: - model = ExtendedUser - fields = ('id', 'username', 'email', 'first_name', 'last_name', 'permissions', 'groups') - read_only_fields = ('id', 'username', 'email', 'first_name', 'last_name', 'permissions', 'groups') - - def get_permissions(self, obj): - return list(set(obj.get_permissions())) - - -@receiver(post_save, sender=ExtendedUser) -def create_auth_token(sender, instance=None, created=False, **kwargs): - if created: - AuthToken.objects.create(user=instance) +from authentication.serializers import UserSerializer, GroupSerializer class UserViewSet(viewsets.ModelViewSet): @@ -38,26 +20,17 @@ class UserViewSet(viewsets.ModelViewSet): serializer_class = UserSerializer -class GroupSerializer(serializers.ModelSerializer): - permissions = serializers.SerializerMethodField() - members = serializers.SerializerMethodField() - - class Meta: - model = Group - fields = ('id', 'name', 'permissions', 'members') - - def get_permissions(self, obj): - return ["*:" + p.codename for p in obj.permissions.all()] - - def get_members(self, obj): - return [u.username for u in obj.user_set.all()] - - class GroupViewSet(viewsets.ModelViewSet): queryset = Group.objects.all() serializer_class = GroupSerializer +@receiver(post_save, sender=ExtendedUser) +def create_auth_token(sender, instance=None, created=False, **kwargs): + if created: + AuthToken.objects.create(user=instance) + + @api_view(['GET']) @permission_classes([IsAuthenticated]) def selfUser(request): diff --git a/core/authentication/serializers.py b/core/authentication/serializers.py new file mode 100644 index 0000000..0581865 --- /dev/null +++ b/core/authentication/serializers.py @@ -0,0 +1,32 @@ +from rest_framework import serializers +from django.contrib.auth.models import Group + +from authentication.models import ExtendedUser + + +class UserSerializer(serializers.ModelSerializer): + permissions = serializers.SerializerMethodField() + groups = serializers.SlugRelatedField(many=True, read_only=True, slug_field='name') + + class Meta: + model = ExtendedUser + fields = ('id', 'username', 'email', 'first_name', 'last_name', 'permissions', 'groups') + read_only_fields = ('id', 'username', 'email', 'first_name', 'last_name', 'permissions', 'groups') + + def get_permissions(self, obj): + return list(set(obj.get_permissions())) + + +class GroupSerializer(serializers.ModelSerializer): + permissions = serializers.SerializerMethodField() + members = serializers.SerializerMethodField() + + class Meta: + model = Group + fields = ('id', 'name', 'permissions', 'members') + + def get_permissions(self, obj): + return ["*:" + p.codename for p in obj.permissions.all()] + + def get_members(self, obj): + return [u.username for u in obj.user_set.all()] diff --git a/core/notifications/api_v2.py b/core/notifications/api_v2.py index e459d01..a9492f5 100644 --- a/core/notifications/api_v2.py +++ b/core/notifications/api_v2.py @@ -9,6 +9,7 @@ from notifications.models import MessageTemplate, UserNotificationChannel from rest_framework import serializers from notifications.templates import TEMPLATE_VARS +from authentication.serializers import UserSerializer class MessageTemplateSerializer(serializers.ModelSerializer): @@ -18,6 +19,8 @@ class MessageTemplateSerializer(serializers.ModelSerializer): class UserNotificationChannelSerializer(serializers.ModelSerializer): + user = UserSerializer() + class Meta: model = UserNotificationChannel fields = '__all__' diff --git a/web/src/views/admin/Notifications.vue b/web/src/views/admin/Notifications.vue index 5a451fc..312a902 100644 --- a/web/src/views/admin/Notifications.vue +++ b/web/src/views/admin/Notifications.vue @@ -1,7 +1,7 @@ From 7df9a3f8ccf842136f12f62ce7dab1ba319202ce Mon Sep 17 00:00:00 2001 From: jedi Date: Sat, 13 Jul 2024 17:28:38 +0200 Subject: [PATCH 11/18] stash --- web/src/components/Timeline.vue | 2 +- web/src/store.js | 8 +++++- web/src/views/Tickets.vue | 2 +- web/src/views/admin/Notifications.vue | 37 +++++++++++++++++++++++---- 4 files changed, 41 insertions(+), 8 deletions(-) diff --git a/web/src/components/Timeline.vue b/web/src/components/Timeline.vue index f3ac1d8..88bfa94 100644 --- a/web/src/components/Timeline.vue +++ b/web/src/components/Timeline.vue @@ -73,7 +73,7 @@ import TimelineMail from "@/components/TimelineMail.vue"; import TimelineComment from "@/components/TimelineComment.vue"; import TimelineStateChange from "@/components/TimelineStateChange.vue"; -import {mapGetters} from "vuex"; +import {mapActions, mapGetters} from "vuex"; import TimelineAssignment from "@/components/TimelineAssignment.vue"; import TimelineRelatedItem from "@/components/TimelineRelatedItem.vue"; import TimelineShippingVoucher from "@/components/TimelineShippingVoucher.vue"; diff --git a/web/src/store.js b/web/src/store.js index b5fba1c..0724070 100644 --- a/web/src/store.js +++ b/web/src/store.js @@ -47,6 +47,7 @@ const store = createStore({ states: 0, messageTemplates: 0, shippingVouchers: 0, + userNotificationChannels: 0, }, persistent_loaded: false, shared_loaded: false, @@ -241,6 +242,10 @@ const store = createStore({ state.shippingVouchers = codes; state.fetchedData = {...state.fetchedData, shippingVouchers: Date.now()}; }, + setUserNotificationChannels(state, channels) { + state.userNotificationChannels = channels; + state.fetchedData = {...state.fetchedData, userNotificationChannels: Date.now()}; + }, }, actions: { async login({commit}, {username, password, remember}) { @@ -536,9 +541,10 @@ const store = createStore({ }, async fetchUserNotificationChannels({commit, state}) { if (!state.user.token) return; + if (state.fetchedData.userNotificationChannels > Date.now() - 1000 * 60 * 60 * 24) return; const {data, success} = await http.get('/2/user_notification_channels/', state.user.token); if (data && success) { - state.userNotificationChannels = data; + commit('setUserNotificationChannels', data); } }, }, diff --git a/web/src/views/Tickets.vue b/web/src/views/Tickets.vue index 55574c8..f1497bb 100644 --- a/web/src/views/Tickets.vue +++ b/web/src/views/Tickets.vue @@ -3,7 +3,7 @@
- -
    -
  • - {{ channel.id }} - {{ channel.channel_type }} - {{ channel.channel_target }} - {{ channel.event_filter }} - {{ channel.active }} - {{ channel.created }} - {{ channel.user }} -
  • -
+
+ + +
+
+
+
+ + + + +
+ +
+
+
+
+