From 82cf92fb36df9060f834c9beae6856217f3f2d3e Mon Sep 17 00:00:00 2001 From: Brent Silkyn <160223373+bsilkyn@users.noreply.github.com> Date: Mon, 20 May 2024 09:36:26 +0200 Subject: [PATCH 1/4] fix: Admin panel fix (#427) * chore: make /admin/ redirect to /admin/users/ #356 * chore: make change size depending on screen size #356 * chore: replace "public" ToggleButton by a ToggleSwitch #356 * chore: add "remove" button #356 * chore: visual layout remove button for selected docker images #356 * chore: make multiRemove button disabled when nothing's selected #356 * chore: add selectedItems variable, so I can reach that when the objects are actually being removed #356 * chore: error message for case saving when no item is selected (shouldn't actually happen ever) #356 * chore: not able to upload when there is no name for docker image #356 * chore: make selection an option instead of mandatory for LazyDataTable #356 * chore: documentation UsersView #356 * chore: documentation DockerImagesView #356 * chore: translations + try at allowing and file upload after 1st upload #356 * chore: change to list when uploading #356 * chore: after dockerImage is removed separately, remove it from list of selectedItems #356 * chore: implement multiRemove of docker images #356 * fix: lint fix #356 * fix: correct "or" filter for functions of user to an "and" filter #356 * docs: extra documentation of DockerImagesView.vue #356 * fix: replace Dialog by ConfirmDialog #356 * style: linting fix #356 * docs: extra doc for DockerImagesView.vue #356 * chore: translation corrections #356 * chore: joining of functions (fillCreators and fillDestroyers) for a code execution speedup (hopefully) #356 * style: lint fix #356 * chore: change word in header in admin panel from "ypovoli" to "admin" #356 * chore: translation fixes #356 * chore: forgot to remove stuff in Dutch translations #356 --- backend/api/views/docker_view.py | 8 + backend/api/views/user_view.py | 2 +- frontend/src/assets/lang/app/en.json | 14 +- frontend/src/assets/lang/app/nl.json | 13 +- .../src/components/admin/LazyDataTable.vue | 35 ++- .../components/layout/admin/AdminHeader.vue | 2 +- .../composables/services/docker.service.ts | 24 ++- frontend/src/config/endpoints.ts | 2 + frontend/src/router/router.ts | 3 +- frontend/src/views/admin/AdminView.vue | 9 - frontend/src/views/admin/DockerImagesView.vue | 199 +++++++++++++----- frontend/src/views/admin/UsersView.vue | 53 +++-- 12 files changed, 274 insertions(+), 90 deletions(-) delete mode 100644 frontend/src/views/admin/AdminView.vue diff --git a/backend/api/views/docker_view.py b/backend/api/views/docker_view.py index 1e2da9d7..6fad8423 100644 --- a/backend/api/views/docker_view.py +++ b/backend/api/views/docker_view.py @@ -61,6 +61,14 @@ def patch_public(self, request: Request, **_) -> Response: return Response(serializer.data) + @action(detail=False, methods=['DELETE'], permission_classes=[IsAdminUser]) + def delete(self, request: Request, **_) -> Response: + response = self.queryset.filter(id__in=request.data['ids']).delete() + + return Response(response) + + # TODO: Maybe not necessary + # https://www.django-rest-framework.org/api-guide/permissions/#overview-of-access-restriction-methods def list(self, request: Request) -> Response: images: BaseManager[DockerImage] = DockerImage.objects.all() if not request.user.is_staff: diff --git a/backend/api/views/user_view.py b/backend/api/views/user_view.py index 3a65fa37..9d8b33a2 100644 --- a/backend/api/views/user_view.py +++ b/backend/api/views/user_view.py @@ -57,7 +57,7 @@ def search(self, request: Request) -> Response: role_filters = Q() for role in roles: - role_filters |= Q(**{f"{role}__isnull": False, f"{role}__is_active": True}) + role_filters &= Q(**{f"{role}__isnull": False, f"{role}__is_active": True}) queryset = queryset.filter( role_filters, diff --git a/frontend/src/assets/lang/app/en.json b/frontend/src/assets/lang/app/en.json index bafc4d00..9e86d683 100644 --- a/frontend/src/assets/lang/app/en.json +++ b/frontend/src/assets/lang/app/en.json @@ -245,6 +245,14 @@ "login": { "success": "You have successfully logged in.", "error": "An error occurred while logging in." + }, + "admin": { + "save": { + "error": { + "title": "Invalid save operation", + "detail": "You are trying to save an item without selecting it" + } + } } } }, @@ -287,6 +295,7 @@ "edit": "Edit", "cancel": "Cancel", "save": "Save", + "delete": "Delete", "users": { "title": "Users", "username": "Username", @@ -316,7 +325,9 @@ "private": "Private" }, "none_found": "No matching data.", - "loading": "Loading data. Please wait." + "loading": "Loading data. Please wait.", + "safeGuard": "Are you sure?", + "no_file": "No file found." }, "primevue": { "startsWith": "Starts with", @@ -416,6 +427,7 @@ "emptySelectionMessage": "No selected item", "emptySearchMessage": "No results found", "emptyMessage": "No available options", + "emptyFileSelect": "No file selected", "aria": { "trueLabel": "True", "falseLabel": "False", diff --git a/frontend/src/assets/lang/app/nl.json b/frontend/src/assets/lang/app/nl.json index 7864c171..884cb246 100644 --- a/frontend/src/assets/lang/app/nl.json +++ b/frontend/src/assets/lang/app/nl.json @@ -242,6 +242,14 @@ "login": { "success": "Je bent succesvol ingelogd.", "error": "Er is een fout opgetreden tijdens het inloggen." + }, + "admin": { + "save": { + "error": { + "title": "Ongeldige opsla operatie", + "detail": "U probeert een item op te slaan zonder dit te selecteren" + } + } } } }, @@ -283,6 +291,7 @@ "edit": "Bewerken", "cancel": "Annuleer", "save": "Sla op", + "delete": "Verwijder", "users": { "title": "Gebruikers", "username": "Gebruikersnaam", @@ -312,7 +321,8 @@ "private": "Privaat" }, "none_found": "Geen overeenkomende data gevonden.", - "loading": "Aan het laden. Wacht een momentje aub." + "loading": "Aan het laden. Wacht even aub.", + "safeGuard": "Bent u het zeker?" }, "primevue": { "accept": "Ja", @@ -364,6 +374,7 @@ "emptyMessage": "Geen resultaten gevonden", "emptySearchMessage": "Geen resultaten gevonden", "emptySelectionMessage": "Geen optie geselecteerd", + "emptyFileSelect": "Geen bestand geselecteerd", "endsWith": "Eindigt met", "equals": "Is gelijk aan", "fileSizeTypes": [ diff --git a/frontend/src/components/admin/LazyDataTable.vue b/frontend/src/components/admin/LazyDataTable.vue index 9a1e2a32..dcdfe499 100644 --- a/frontend/src/components/admin/LazyDataTable.vue +++ b/frontend/src/components/admin/LazyDataTable.vue @@ -8,14 +8,27 @@ import { type Filter } from '@/types/filter/Filter.ts'; import Column from 'primevue/column'; /* Properties */ -const props = defineProps<{ - pagination: PaginatorResponse | null; - entities: any[] | null; // list containing all the entities displayed by data table after executing get method - get: () => Promise; // get method for backend - search: (filters: Filter, page: number, pageSize: number) => Promise; - filter: Filter; - onFilter: (callback: () => Promise, debounce?: number | undefined, immediate?: boolean | undefined) => void; -}>(); +const props = withDefaults( + defineProps<{ + pagination: PaginatorResponse | null; + entities: any[] | null; // list containing all the entities displayed by data table after executing get method + get: () => Promise; // get method for backend + search: (filters: Filter, page: number, pageSize: number) => Promise; + filter: Filter; + onFilter: ( + callback: () => Promise, + debounce?: number | undefined, + immediate?: boolean | undefined, + ) => void; + select?: boolean; + }>(), + { + select: false, + }, +); + +/* Emits */ +const emit = defineEmits(['select']); /* Injections */ const { t } = useI18n(); @@ -59,17 +72,21 @@ const onSelectAllChange = (event: DataTableSelectAllChangeEvent): void => { props.get().then(() => { selectAll.value = true; selected.value = props.entities; + emit('select', selected.value); }); } else { selectAll.value = false; selected.value = []; + emit('select', selected.value); } }; const onRowSelect = (): void => { selectAll.value = selected.value?.length === (props.pagination?.count ?? 0); + emit('select', selected.value); }; const onRowUnselect = (): void => { selectAll.value = false; + emit('select', selected.value); }; defineExpose({ fetch }); @@ -109,7 +126,7 @@ defineExpose({ fetch }); {{ t('admin.loading') }} - + diff --git a/frontend/src/components/layout/admin/AdminHeader.vue b/frontend/src/components/layout/admin/AdminHeader.vue index 731accb5..06e38cc3 100644 --- a/frontend/src/components/layout/admin/AdminHeader.vue +++ b/frontend/src/components/layout/admin/AdminHeader.vue @@ -53,7 +53,7 @@ const items = computed(() => [