<template> <MainTemplate> <template #headerInsert> <RouterLink to="../" class="text-primary">zurück zur Liste (abbrechen)</RouterLink> </template> <template #topBar> <div class="flex flex-row items-center justify-between pt-5 pb-3 px-7"> <h1 class="font-bold text-xl h-8">Nutzer {{ origin?.username }} - Rollen bearbeiten</h1> </div> </template> <template #main> <Spinner v-if="loading == 'loading'" class="mx-auto" /> <p v-else-if="loading == 'failed'">laden fehlgeschlagen</p> <div v-else class="flex flex-col grow gap-4"> <div class="flex flex-col gap-2 grow"> <p class="text-xl font-semibold">zugewiesene Rollen</p> <div class="flex flex-row flex-wrap gap-2 h-1/2 overflow-y-auto"> <div v-for="role in assignedRoles" :key="role.id" class="flex flex-row gap-2 items-center px-2 p-1 border border-gray-300 rounded-md h-fit cursor-pointer" @click="removeAssigned(role.id)" > <p>{{ role.role }}</p> <XMarkIcon class="w-5 h-5" /> </div> </div> <p class="text-xl font-semibold">verfügbare Rollen</p> <div class="flex flex-row flex-wrap gap-2 h-1/2 overflow-y-auto"> <div v-for="role in availableRoles" :key="role.id" class="flex flex-row gap-2 items-center px-2 p-1 border border-gray-300 rounded-md h-fit cursor-pointer" @click="addAvailable(role.id)" > <p>{{ role.role }}</p> <PlusIcon class="w-5 h-5" /> </div> </div> </div> <div class="flex flex-row justify-end gap-2"> <button primary-outline class="!w-fit" :disabled="canSaveOrReset" @click="resetForm">verwerfen</button> <button primary class="!w-fit" :disabled="status == 'loading' || canSaveOrReset" @click="triggerRolesUpdate"> speichern </button> <Spinner v-if="status == 'loading'" class="my-auto" /> <SuccessCheckmark v-else-if="status?.status == 'success'" /> <FailureXMark v-else-if="status?.status == 'failed'" /> </div> </div> </template> </MainTemplate> </template> <script setup lang="ts"> import { defineComponent } from "vue"; import { RouterLink } from "vue-router"; import { mapState, mapActions } from "pinia"; import { useUserStore } from "@/stores/admin/user"; import { useRoleStore } from "@/stores/admin/role"; import type { PermissionObject } from "@/types/permissionTypes"; import MainTemplate from "@/templates/Main.vue"; import Spinner from "@/components/Spinner.vue"; import SuccessCheckmark from "@/components/SuccessCheckmark.vue"; import FailureXMark from "@/components/FailureXMark.vue"; import { XMarkIcon, PlusIcon } from "@heroicons/vue/24/outline"; import type { UserViewModel } from "@/viewmodels/admin/user.models"; import cloneDeep from "lodash.clonedeep"; import isEqual from "lodash.isEqual"; </script> <script lang="ts"> export default defineComponent({ props: { id: String, }, data() { return { loading: "loading" as "loading" | "fetched" | "failed", status: null as null | "loading" | { status: "success" | "failed"; reason?: string }, origin: null as null | UserViewModel, assigned: [] as Array<number>, timeout: null as any, }; }, computed: { ...mapState(useRoleStore, ["roles"]), assignedRoles() { return this.roles.filter((r) => this.assigned.includes(r.id)); }, availableRoles() { return this.roles.filter((r) => !this.assigned.includes(r.id)); }, canSaveOrReset(): boolean { return isEqual(this.origin?.roles.map((r) => r.id) ?? [], this.assigned); }, }, mounted() { this.fetchRoles(); this.fetchItem(); }, beforeUnmount() { try { clearTimeout(this.timeout); } catch (error) {} }, methods: { ...mapActions(useUserStore, ["fetchUserById", "updateActiveUserRoles"]), ...mapActions(useRoleStore, ["fetchRoles"]), resetForm() { this.assigned = this.origin?.roles.map((r) => r.id) ?? []; }, fetchItem() { this.fetchUserById(parseInt(this.id ?? "")) .then((result) => { this.origin = cloneDeep(result.data); this.assigned = this.origin?.roles.map((r) => r.id) ?? []; this.loading = "fetched"; }) .catch((err) => { this.loading = "failed"; }); }, addAvailable(id: number) { this.assigned.push(id); }, removeAssigned(id: number) { this.assigned = this.assigned.filter((roleId) => roleId !== id); }, triggerRolesUpdate() { if (this.origin == null) return; this.status = "loading"; this.updateActiveUserRoles(this.origin.id, this.assigned) .then(() => { this.fetchItem(); this.status = { status: "success" }; }) .catch((err) => { this.status = { status: "failed" }; }) .finally(() => { this.timeout = setTimeout(() => { this.status = null; }, 2000); }); }, }, }); </script>