<template> <div id="project-members"> <h1 class="ui header"> Gérer les membres </h1> <h4>Ajouter un membre</h4> <div id="form-feature-edit" class="ui form" name="add-member" > <div class="two fields"> <div class="field"> <Dropdown :options="userOptions" :selected="newMember.user.name" :selection.sync="newMember.user" :search="true" :clearable="true" /> <ul id="errorlist" class="errorlist" > <li v-for="error in newMember.errors" :key="error" > {{ error }} </li> </ul> </div> <div class="field"> <Dropdown :options="levelOptions" :selected="newMember.role.name" :selection.sync="newMember.role" /> </div> </div> <button type="button" class="ui green icon button" :disabled="!newMember.user.name" @click="addMember" > <i class="white add icon" aria-hidden="true" /> <span class="padding-1">Ajouter</span> </button> </div> <h4>Modifier le rôle d'un membre</h4> <div id="form-members" class="ui form" > <table class="ui red table" aria-describedby="Table des membres du projet" > <thead> <tr> <th scope="col"> Membre <i :class="{ down: isSortedAsc('member'), up: isSortedDesc('member'), }" class="icon sort" aria-hidden="true" @click="changeSort('member')" /> </th> <th scope="col"> Niveau d'autorisation <i :class="{ down: isSortedAsc('role'), up: isSortedDesc('role'), }" class="icon sort" aria-hidden="true" @click="changeSort('role')" /> </th> </tr> </thead> <tbody> <tr v-for="member in projectMembers" :key="member.username" > <td> {{ member.user.last_name }} {{ member.user.first_name }}<br><em>{{ member.user.username }}</em> </td> <td> <div class="required field online"> <Dropdown :options="levelOptions" :selected="member.userLevel.name" :selection.sync="member.userLevel" :search="true" /> <button type="button" class="ui icon button button-hover-red" data-tooltip="Retirer ce membre" @click="removeMember(member)" > <i class="times icon" aria-hidden="true" /> </button> </div> </td> </tr> </tbody> </table> <div class="ui divider" /> <button type="button" class="ui teal icon button" @click="saveMembers" > <i class="white save icon" aria-hidden="true" /> Enregistrer les changements </button> </div> </div> </template> <script> import axios from '@/axios-client.js'; import { mapMutations, mapState } from 'vuex'; import Dropdown from '@/components/Dropdown.vue'; export default { name: 'ProjectMembers', components: { Dropdown, }, data() { return { projectUsers: [], options: [ { name: 'Utilisateur connecté', value: 'logged_user' }, { name: 'Contributeur', value: 'contributor' }, { name: 'Super Contributeur', value: 'super_contributor' }, { name: 'Modérateur', value: 'moderator' }, { name: 'Administrateur projet', value: 'admin' }, ], newMember: { errors: [], user: { name: '', value: '', }, role: { name: 'Contributeur', value: 'contributor', }, }, sort: { column: '', ascending: true, }, }; }, computed: { ...mapState('projects', ['project']), userOptions: function () { return this.projectUsers .filter((el) => el.userLevel.value === 'logged_user') .map((el) => { let name = el.user.first_name || ''; if (el.user.last_name) { name = name + ' ' + el.user.last_name; } return { name: [name, el.user.username], value: el.user.id, }; }); }, levelOptions: function () { return this.options.filter( (el) => (this.project && this.project.moderation ? el : el.value !== 'moderator') && el.value !== 'logged_user' ); }, projectMembers() { return this.projectUsers .filter((el) => el.userLevel.value !== 'logged_user') .sort((a, b) => { if (this.sort.column !== '') { if (this.sort.column === 'member') { const textA = a.user.username.toUpperCase(); const textB = b.user.username.toUpperCase(); if (this.sort.ascending) { return textA < textB ? -1 : textA > textB ? 1 : 0; } else { return textA > textB ? -1 : textA < textB ? 1 : 0; } } else if (this.sort.column === 'role') { const textA = a.userLevel.name.toUpperCase(); const textB = b.userLevel.name.toUpperCase(); if (this.sort.ascending) { return textA < textB ? -1 : textA > textB ? 1 : 0; } else { return textA > textB ? -1 : textA < textB ? 1 : 0; } } } else { return 0; } }); }, }, created() { if (!this.project) { this.$store.dispatch('projects/GET_PROJECT', this.$route.params.slug); this.$store.dispatch('projects/GET_PROJECT_INFO', this.$route.params.slug); } this.populateMembers(); }, destroyed() { //* allow user to change page if ever stuck on loader this.DISCARD_LOADER(); }, methods: { ...mapMutations([ 'DISPLAY_MESSAGE', 'DISPLAY_LOADER', 'DISCARD_LOADER' ]), validateNewMember() { this.newMember.errors = []; if (!this.newMember.user.value) { this.newMember.errors.push('Veuillez compléter ce champ.'); return false; } return true; }, changeUserRole(id, role) { const indexOfUser = this.projectUsers.findIndex( (el) => el.user.id === id ); //* modify its userLever this.projectUsers[indexOfUser].userLevel = role; }, addMember() { if (this.validateNewMember()) { this.changeUserRole(this.newMember.user.value, this.newMember.role); //* empty add form this.newMember.user = { value: '', name: '' }; } }, isSortedAsc(column) { return this.sort.column === column && this.sort.ascending; }, isSortedDesc(column) { return this.sort.column === column && !this.sort.ascending; }, changeSort(column) { if (this.sort.column === column) { //changer order this.sort.ascending = !this.sort.ascending; } else { this.sort.column = column; this.sort.ascending = true; } }, removeMember(member) { this.changeUserRole(member.user.id, { name: 'Utilisateur connecté', value: 'logged_user', }); }, /** * Saves the updated members and their roles for a project. * Displays a loader while the update is in progress and provides feedback upon completion or error. */ saveMembers() { // Display a loader to indicate that the update process is ongoing this.DISPLAY_LOADER('Updating project members...'); // Prepare the data to be sent in the API request const data = this.projectUsers.map((member) => { return { user: member.user, level: { display: member.userLevel.name, // Display name of the user level codename: member.userLevel.value, // Codename of the user level }, }; }); // Make an API request to update the project members axios .put( `${this.$store.state.configuration.VUE_APP_DJANGO_API_BASE}projects/${this.project.slug}/utilisateurs/`, data ) .then((response) => { // Check if the response status is 200 (OK) if (response.status === 200) { // Dispatch an action to update the user status in the top right menu this.$store.dispatch('GET_USER_LEVEL_PROJECTS'); // Display a positive message indicating success this.DISPLAY_MESSAGE({ comment: 'Permissions updated successfully', level: 'positive' }); } else { // Display a generic error message if the response status is not 200 this.DISPLAY_MESSAGE({ comment: "An error occurred while updating permissions", level: 'negative' }); } // Hide the loader regardless of the request result this.DISCARD_LOADER(); }) .catch((error) => { // Hide the loader if an error occurs this.DISCARD_LOADER(); // Determine the error message to display const errorMessage = error.response && error.response.data && error.response.data.error ? error.response.data.error : "An error occurred while updating permissions"; // Display the error message this.DISPLAY_MESSAGE({ comment: errorMessage, level: 'negative' }); // Log the error to the console for debugging console.error(error); }); }, fetchMembers() { // todo: move function to a service return axios .get( `${this.$store.state.configuration.VUE_APP_DJANGO_API_BASE}projects/${this.$route.params.slug}/utilisateurs/` ) .then((response) => response.data) .catch((error) => { throw error; }); }, populateMembers() { this.DISPLAY_LOADER('Récupération des membres en cours...'); this.fetchMembers().then((members) => { this.DISCARD_LOADER(); this.projectUsers = members.map((el) => { return { userLevel: { name: el.level.display, value: el.level.codename }, ...el, }; }); }); }, }, }; </script> <style> .padding-1 { padding: 0 1em; } i.icon.sort:not(.down):not(.up) { color: rgb(220, 220, 220); } .online { display: flex; } </style>