Skip to content

Commit

Permalink
fix: Admin panel fix (#427)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
bsilkyn authored May 20, 2024
1 parent 21a7532 commit 82cf92f
Show file tree
Hide file tree
Showing 12 changed files with 274 additions and 90 deletions.
8 changes: 8 additions & 0 deletions backend/api/views/docker_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
2 changes: 1 addition & 1 deletion backend/api/views/user_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
14 changes: 13 additions & 1 deletion frontend/src/assets/lang/app/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
}
}
}
},
Expand Down Expand Up @@ -287,6 +295,7 @@
"edit": "Edit",
"cancel": "Cancel",
"save": "Save",
"delete": "Delete",
"users": {
"title": "Users",
"username": "Username",
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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",
Expand Down
13 changes: 12 additions & 1 deletion frontend/src/assets/lang/app/nl.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
}
}
}
},
Expand Down Expand Up @@ -283,6 +291,7 @@
"edit": "Bewerken",
"cancel": "Annuleer",
"save": "Sla op",
"delete": "Verwijder",
"users": {
"title": "Gebruikers",
"username": "Gebruikersnaam",
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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": [
Expand Down
35 changes: 26 additions & 9 deletions frontend/src/components/admin/LazyDataTable.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,27 @@ import { type Filter } from '@/types/filter/Filter.ts';
import Column from 'primevue/column';
/* Properties */
const props = defineProps<{
pagination: PaginatorResponse<any> | null;
entities: any[] | null; // list containing all the entities displayed by data table after executing get method
get: () => Promise<void>; // get method for backend
search: (filters: Filter, page: number, pageSize: number) => Promise<void>;
filter: Filter;
onFilter: (callback: () => Promise<void>, debounce?: number | undefined, immediate?: boolean | undefined) => void;
}>();
const props = withDefaults(
defineProps<{
pagination: PaginatorResponse<any> | null;
entities: any[] | null; // list containing all the entities displayed by data table after executing get method
get: () => Promise<void>; // get method for backend
search: (filters: Filter, page: number, pageSize: number) => Promise<void>;
filter: Filter;
onFilter: (
callback: () => Promise<void>,
debounce?: number | undefined,
immediate?: boolean | undefined,
) => void;
select?: boolean;
}>(),
{
select: false,
},
);
/* Emits */
const emit = defineEmits(['select']);
/* Injections */
const { t } = useI18n();
Expand Down Expand Up @@ -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 });
Expand Down Expand Up @@ -109,7 +126,7 @@ defineExpose({ fetch });
{{ t('admin.loading') }}
</slot>
</template>
<Column selectionMode="multiple" headerStyle="width: 3rem" class="justify-content-center"></Column>
<Column v-if="select" selectionMode="multiple" headerStyle="width: 3rem" class="justify-content-center" />
<slot />
</DataTable>
</div>
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/layout/admin/AdminHeader.vue
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ const items = computed(() => [
<div id="header" class="w-full flex text-white p-4">
<!-- Title -->
<div class="hidden md:flex align-items-end">
<h1 class="text-white m-0">Ypovoli</h1>
<h1 class="text-white m-0">Admin</h1>
</div>
<!-- User information -->
<div class="text-right ml-auto text-sm flex flex-column align-items-end gap-3">
Expand Down
24 changes: 23 additions & 1 deletion frontend/src/composables/services/docker.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,14 @@ import { Response } from '@/types/Response.ts';
import { endpoints } from '@/config/endpoints.ts';
import { type Ref, ref } from 'vue';
import { type Filter } from '@/types/filter/Filter.ts';
import { create, getList, getPaginatedList, patch } from '@/composables/services/helpers.ts';
import {
create,
getList,
getPaginatedList,
patch,
deleteId,
deleteIdWithData,
} from '@/composables/services/helpers.ts';
import { type PaginatorResponse } from '@/types/filter/Paginator.ts';

interface DockerImagesState {
Expand All @@ -14,6 +21,8 @@ interface DockerImagesState {
searchDockerImages: (filters: Filter, page: number, pageSize: number) => Promise<void>;
patchDockerImage: (dockerData: DockerImage) => Promise<void>;
createDockerImage: (dockerData: DockerImage, file: File) => Promise<void>;
deleteDockerImage: (id: string) => Promise<void>;
deleteDockerImages: (ids: string[]) => Promise<void>;
}

export function useDockerImages(): DockerImagesState {
Expand Down Expand Up @@ -51,6 +60,17 @@ export function useDockerImages(): DockerImagesState {
);
}

async function deleteDockerImage(id: string): Promise<void> {
const endpoint = endpoints.dockerImages.retrieve.replace('{id}', id);
await deleteId<Response>(endpoint, response, Response.fromJSON);
}

async function deleteDockerImages(ids: string[]): Promise<void> {
const endpoint = endpoints.dockerImages.deleteMany;
const data = { ids };
await deleteIdWithData<Response>(endpoint, data, response, Response.fromJSON);
}

return {
pagination,
dockerImages,
Expand All @@ -60,5 +80,7 @@ export function useDockerImages(): DockerImagesState {
searchDockerImages,
patchDockerImage,
createDockerImage,
deleteDockerImage,
deleteDockerImages,
};
}
2 changes: 2 additions & 0 deletions frontend/src/config/endpoints.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,10 @@ export const endpoints = {
},
dockerImages: {
index: '/api/docker-images/',
retrieve: '/api/docker-images/{id}/',
search: '/api/docker-images/search/',
patch: '/api/docker-images/{id}/public/',
deleteMany: '/api/docker-images/delete/',
},
students: {
index: '/api/students/',
Expand Down
3 changes: 1 addition & 2 deletions frontend/src/router/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import CreateProjectView from '@/views/projects/CreateProjectView.vue';
import UpdateProjectView from '@/views/projects/UpdateProjectView.vue';
import SearchCourseView from '@/views/courses/SearchCourseView.vue';
import SubmissionView from '@/views/submissions/SubmissionView.vue';
import AdminView from '@/views/admin/AdminView.vue';
import UsersView from '@/views/admin/UsersView.vue';
import ProjectsView from '@/views/projects/ProjectsView.vue';
import DockerImagesView from '@/views/admin/DockerImagesView.vue';
Expand Down Expand Up @@ -156,7 +155,7 @@ const routes: RouteRecordRaw[] = [
path: '/admin/',
beforeEnter: AdminGuard,
children: [
{ path: '', component: AdminView, name: 'admin' },
{ path: '', redirect: { name: 'admin-users' } },
{ path: 'users', component: UsersView, name: 'admin-users' },
{ path: 'docker-images', component: DockerImagesView, name: 'admin-dockerImages' },
],
Expand Down
9 changes: 0 additions & 9 deletions frontend/src/views/admin/AdminView.vue

This file was deleted.

Loading

0 comments on commit 82cf92f

Please sign in to comment.