diff --git a/backend/api/permissions/group_permissions.py b/backend/api/permissions/group_permissions.py index 29280cdf..08ab0641 100644 --- a/backend/api/permissions/group_permissions.py +++ b/backend/api/permissions/group_permissions.py @@ -65,14 +65,7 @@ def has_object_permission(self, request: Request, view: ViewSet, group) -> bool: class GroupSubmissionPermission(BasePermission): """Permission class for submission related group endpoints""" - def has_permission(self, request: Request, view: APIView) -> bool: - """Check if user has permission to view a general group submission endpoint.""" - user = request.user - - # Get the individual permission clauses. - return request.method in SAFE_METHODS or is_teacher(user) or is_assistant(user) - - def had_object_permission(self, request: Request, view: ViewSet, group: Group) -> bool: + def had_object_permission(self, request: Request, _: ViewSet, group: Group) -> bool: """Check if user has permission to view a detailed group submission endpoint""" user = request.user course = group.project.course diff --git a/backend/api/views/group_view.py b/backend/api/views/group_view.py index b4c67383..480164a4 100644 --- a/backend/api/views/group_view.py +++ b/backend/api/views/group_view.py @@ -70,6 +70,26 @@ def submissions(self, request, **_): ) return Response(serializer.data) + @submissions.mapping.post + @submissions.mapping.put + @swagger_auto_schema(request_body=SubmissionSerializer) + def _add_submission(self, request: Request, **_): + """Add a submission to the group""" + group: Group = self.get_object() + + # Add submission to course + serializer = SubmissionSerializer( + data=request.data, context={"group": group, "request": request} + ) + + if serializer.is_valid(raise_exception=True): + serializer.save(group=group) + + return Response({ + "message": gettext("group.success.submissions.add"), + "submission": serializer.data + }) + @students.mapping.post @students.mapping.put @swagger_auto_schema(request_body=StudentJoinGroupSerializer) @@ -109,24 +129,4 @@ def _remove_student(self, request, **_): return Response({ "message": gettext("group.success.students.remove"), - }) - - @submissions.mapping.post - @submissions.mapping.put - @swagger_auto_schema(request_body=SubmissionSerializer) - def _add_submission(self, request: Request, **_): - """Add a submission to the group""" - group: Group = self.get_object() - - # Add submission to course - serializer = SubmissionSerializer( - data=request.data, context={"group": group, "request": request} - ) - - if serializer.is_valid(raise_exception=True): - serializer.save(group=group) - - return Response({ - "message": gettext("group.success.submissions.add"), - "submission": serializer.data - }) + }) \ No newline at end of file diff --git a/backend/api/views/user_view.py b/backend/api/views/user_view.py index 1208f697..413a975c 100644 --- a/backend/api/views/user_view.py +++ b/backend/api/views/user_view.py @@ -84,7 +84,12 @@ def notifications(self, request: Request, pk: str): ) # Get the notifications for the user - notifications = Notification.objects.filter(user=pk).order_by("-created_at")[:count] + notifications = Notification.objects.filter(user=pk, is_read=False).order_by("-created_at") + + if 0 < notifications.count() < count: + notifications = list(notifications) + list( + Notification.objects.filter(user=pk, is_read=True).order_by("-created_at")[:count - notifications.count()] + ) # Serialize the notifications serializer = NotificationSerializer( diff --git a/frontend/src/assets/lang/app/en.json b/frontend/src/assets/lang/app/en.json index e562c163..04e4191e 100644 --- a/frontend/src/assets/lang/app/en.json +++ b/frontend/src/assets/lang/app/en.json @@ -19,7 +19,8 @@ "new": "New", "title": "Notifications", "markAsRead": "Mark as read", - "loadMore": "Load more" + "loadMore": "Load more", + "noNotifications": "No notifications available" } }, "footer": { diff --git a/frontend/src/assets/lang/app/nl.json b/frontend/src/assets/lang/app/nl.json index d51bbb47..2f2ae12e 100644 --- a/frontend/src/assets/lang/app/nl.json +++ b/frontend/src/assets/lang/app/nl.json @@ -19,7 +19,8 @@ "new": "Nieuw", "title": "Notificaties", "markAsRead": "Markeer alles als gelezen", - "loadMore": "Laad meer notificaties" + "loadMore": "Laad meer notificaties", + "noNotifications": "Geen notificaties" } }, "footer": { diff --git a/frontend/src/components/courses/CourseForm.vue b/frontend/src/components/courses/CourseForm.vue index 47114855..f8bf714e 100644 --- a/frontend/src/components/courses/CourseForm.vue +++ b/frontend/src/components/courses/CourseForm.vue @@ -31,7 +31,7 @@ const form = ref(new Course()); const rules = computed(() => { return { name: { required: helpers.withMessage(t('validations.required'), required) }, - faculty: { required: helpers.withMessage(t('validations.required'), required) }, + faculty: { required: helpers.withMessage(t('validations.required'), (faculty: Faculty) => faculty.id !== '') }, excerpt: { required: helpers.withMessage(t('validations.required'), required) }, }; }); diff --git a/frontend/src/components/notifications/NotificationsOverlay.vue b/frontend/src/components/notifications/NotificationsOverlay.vue index d86ca414..0f2d5c98 100644 --- a/frontend/src/components/notifications/NotificationsOverlay.vue +++ b/frontend/src/components/notifications/NotificationsOverlay.vue @@ -47,23 +47,21 @@ function toggleNotificationOverlay(event: Event): void {

{{ t('layout.header.notifications.title') }}

- + +
+
diff --git a/frontend/src/components/notifications/NotificationsScrollPanel.vue b/frontend/src/components/notifications/NotificationsScrollPanel.vue index 20c4bee5..6f8aeec5 100644 --- a/frontend/src/components/notifications/NotificationsScrollPanel.vue +++ b/frontend/src/components/notifications/NotificationsScrollPanel.vue @@ -2,11 +2,15 @@ import ScrollPanel from 'primevue/scrollpanel'; import { type Notification } from '@/types/Notification.ts'; import NotificationCard from '@/components/notifications/NotificationCard.vue'; +import { useI18n } from 'vue-i18n'; /* Props */ defineProps<{ notifications: Notification[]; }>(); + +/* Composable injections */ +const { t } = useI18n();