<template> <div id="page-content-wrapper" class="flex flex-wrap justify-center" > <base-toaster :open="toasterIsOpen" @close="hideDialog" > <div class="flex justify-between space-x-4 items-center"> <font-awesome-icon class="ml-3" icon="fa-solid fa-thumbs-up " size="lg" /> <h4>{{toasterMessage}}</h4> <button class="btn btn-outline" @click="hideDialog" > <font-awesome-icon icon="fa-regular fa-circle-xmark" size="2x" /> </button> </div> </base-toaster> <div class="w-5/6"> <fetch-profile v-if="userData !== null" :key="updatedKey" > <template slot-scope="{ response, loading, error: profile_error }"> <slot :response="response" :loading="loading" :error="profile_error" > <div v-if="loading"> <base-spinner /> </div> <div v-else-if="!loading && profile_error"> <p class="text-red-500 text-2xl"> {{ response }} </p> </div> <div v-else> <!-- {{response}}--> <h2 class="round font-bold"> User Profile </h2> <div class="panel panel-default"> <div class="panel-body"> <div class="container-fluid col-md-12"> <div class="row"> <div class="text col-sm-3"> User ID </div> <div class="col-sm-9"> <div class="flex justify-between"> <b>{{ response.username }}</b> <div class="relative"> <button class=" text-white bg-blue-500 hover:bg-blue-700 focus:ring-2 focus:ring-blue-300 rounded-lg items-center px-5 py-3 text-center font-bold mr-2 font-bold " @click="isOpen = !isOpen" > Edit <font-awesome-icon class="ml-2" icon="fa-solid fa-user-pen" /> </button> <button v-if="isOpen" tabindex="-1" class="fixed inset-0 cursor-default h-full w-full" @click="isOpen = false" /> <div v-if="isOpen" class=" absolute mt-2 z-50 grid grid-cols-1 right-0 bg-black min-w-max rounded-lg shadow-lg border border-gray-200 divide-y divide-white-base " > <!-- Active: "bg-gray-100 text-gray-900", Not Active: "text-gray-700" --> <a class=" px-4 py-4 rounded-lg text-2xl text-white-base hover:text-pink-base hover:no-underline " @click="openScientificDisciplineEdit" >Scientific Discipline</a> <a class=" px-4 py-4 rounded-lg text-2xl text-white-base hover:text-pink-base hover:no-underline " @click="openProfileLinkEdit" >Profile Link</a> <a class=" px-4 py-4 rounded-lg text-2xl text-white-base hover:text-pink-base hover:no-underline " @click="openChangePassword" >Password</a> </div> </div> </div> </div> </div> <div class="row"> <div class="text col-sm-3"> Full Name </div> <div class="col-sm-9"> <b>{{ response.full_name }}</b> </div> </div> <div class="row"> <div class="text col-sm-3"> Email </div> <div class="col-sm-9"> {{ response.email }} </div> </div> <div class="row"> <div class="text col-sm-3"> Role </div> <div class="col-sm-9"> {{ setRole(response.role.name) }} </div> </div> <div class="row"> <div class="text col-sm-3"> Current role </div> <div class="col-sm-9"> <div> {{ response.current_role.replace(/_/g, " ") }} </div> </div> </div> <div class="row"> <div class="text col-sm-3"> Scientific Discipline </div> <div class="col-sm-9"> <div> {{ response.scientific_discipline.join(", ") }} </div> </div> </div> <div class="row"> <div class="text col-sm-3"> Motivation </div> <div class="col-sm-9"> <p> {{ response.motivation_text }} </p> </div> </div> <div class="row"> <div class="text col-sm-3"> Affiliation </div> <div class="col-sm-9"> <div> {{ response.current_affiliation }} </div> </div> </div> <div class="row"> <div class="text col-sm-3"> Profile Link </div> <div class="col-sm-9"> <a :href="response.profile_link" target="_blank" > {{ response.profile_link }} </a> </div> </div> <div class="row"> <div class="text col-sm-3"> Joined </div> <div class="col-sm-9"> {{ response.createdAt }} </div> </div> <div class="row"> <div class="text col-sm-3"> Updated </div> <div class="col-sm-9"> {{ response.updatedAt }} </div> </div> </div> </div> </div> <div v-if="editMode" class="panel panel-default" > <div class="panel-body"> <div class="container-fluid col-md-12"> <div v-if="showEditScientificDis"> <h3 class="mb-6 border-b"> Update Scientific Discipline </h3> <div class="row"> <div class="text col-sm-3 p-4"> Scientific Discipline </div> <div class="col-sm-9 p-4"> <div v-for="options in scientificDisciplineOptions" :key="options.id" > <input :id="options.id" v-model="selected" type="checkbox" class="h-6 w-6" :name="options" :value="options.discipline" > <label class="mx-3" :for="options.id" >{{ options.discipline }}</label> </div> <p v-if="profile_error && scientificErrMsg" class="text-red-500 font-bold mt-2" > {{ scientificErrMsg }} </p> </div> </div> </div> <div v-if="showEditProfileLink"> <h3 class="mb-6 border-b"> Change Profile Link </h3> <div class="row"> <div class="text col-sm-3 p-4"> Profile Link </div> <div class="col-sm-9 p-4"> <input id="inline-profile-link" v-model.trim="profile_link" class=" bg-white w-1/3 py-4 px-4 rounded-lg text-gray-700 bg-transparent border border-gray-500 hover:border-gray-700 " type="text" placeholder="Enter profile link." > <p v-if="profile_error && profileLinkErrMsg" class="text-red-500 font-bold mt-2" > {{ profileLinkErrMsg }} </p> </div> </div> </div> <div v-if="showChangePassword"> <h3 class="mb-6 border-b"> Change Password </h3> <div class="row"> <div class="text col-sm-3 p-4"> Current password </div> <div class="col-sm-9 p-4"> <input id="current-password" v-model.trim="currentPassword" class=" bg-white w-1/3 py-4 px-4 rounded-lg text-gray-700 bg-transparent border border-gray-500 hover:border-gray-700 " type="password" > </div> </div> <div class="row"> <div class="text col-sm-3 p-4"> New Password </div> <div class="col-sm-9 p-4"> <input id="inline-password" v-model.trim="newPassword" class=" bg-white w-1/3 py-4 px-4 rounded-lg text-gray-700 bg-transparent border border-gray-500 hover:border-gray-700 " type="password" > </div> </div> <div class="row"> <div class="text col-sm-3 p-4"> Confirm New Password </div> <div class="col-sm-9 p-4"> <input id="confirm-password" v-model.trim="confirmPassword" class=" bg-white w-1/3 py-4 px-4 rounded-lg text-gray-700 bg-transparent border border-gray-500 hover:border-gray-700 " type="password" > <p v-if="profile_error && passwordErrMsg" class="text-red-500 font-bold mt-2" > {{ passwordErrMsg }} </p> </div> </div> </div> <div class="flex space-x-4 mt-2"> <button class=" bg-blue-500 text-xl font-bold rounded-lg px-5 py-4 text-white hover:bg-blue-700 " @click="update(response)" > Update Profile </button> <button class=" bg-gray-500 text-xl font-bold rounded-lg px-5 py-4 text-white hover:bg-gray-700 " @click="close" > Cancel </button> </div> </div> </div> <p v-if="profile_error && errorMsg" class="text-red-500 font-bold mt-2" > {{ errorMsg }} </p> </div> <!-- <button v-if="userData !== null && (getRole === 'Contributor' || getRole === 'Maintainer')" class="btn btn-primary" @click="$router.push('/updateitem/new')"> Create new Update Item </button> --> </div> </slot> </template> </fetch-profile> <fetch-users v-if="getRole === 'Administrator'" :key="'users-' + updatedUsersKey"> <template slot-scope="{ response, loading }"> <slot :response="response" :loading="loading" > <div v-if="loading || response === null" /> <div v-else> <h4 class="round"> User List </h4> <user-table id="usersTable" :data="response" @approve="approve" /> </div> </slot> </template> </fetch-users> </div> </div> </template> <script> import FetchProfile from '@/components/CMS/fetchProfile'; import FetchUpdateItems from '@/components/CMS/fetchUpdateItems'; import FetchUsers from '@/components/CMS/fetchUsers'; import UserTable from '../components/UserTable.vue'; import BaseSpinner from '../components/UI/BaseSpinner.vue'; import BaseToaster from '../components/UI/BaseToaster.vue'; const _ = require('lodash'); let host = require('@/components/js/const').apiHost; export default { name: 'ProfilePage', components: { FetchProfile, UserTable, FetchUsers, BaseSpinner, BaseToaster, }, props: { msg: String, }, data() { return { user: '', motivation_text: '', current_role: '', scientific_discipline: [], current_affiliation: '', profile_link: '', currentPassword: '', newPassword: '', confirmPassword: '', isDev: process.env.NODE_ENV === 'development', editMode: false, toasterIsOpen: false, toasterMessage: 'Profile update successful!', error: false, scientificDisciplineOptions: [ { id: '1', discipline: 'Physics' }, { id: '2', discipline: 'Chemistry' }, { id: '3', discipline: 'Biology' }, { id: '4', discipline: 'Computer Science' }, { id: '5', discipline: 'Mathematics' }, ], selected: [], isOpen: false, showEditScientificDis: false, showEditProfileLink: false, showChangePassword: false, scientificErrMsg: '', profileLinkErrMsg: '', passwordErrMsg: '', errorMsg: '', updatedKey: 0, updatedUsersKey: 0, }; }, computed: { jwt: function () { return this.$store.getters['User/jwt']; }, userData: function () { return this.$store.getters['User/userData']; }, getRole: function () { return this.$store.getters['User/userRole']; }, }, mounted: function () { const vm = this; if (vm.userData === null) { this.$router.push('/login'); } else { vm.user = vm.userData.id; vm.current_role = vm.userData.current_role; vm.scientific_discipline = vm.userData.scientific_discipline; if (vm.scientific_discipline === null) { vm.scientific_discipline = []; } } }, methods: { close() { this.editMode = false; this.showEditScientificDis = false; this.showEditProfileLink = false; this.showChangePassword = false; this.error = false; this.scientificErrMsg = ''; this.profileLinkErrMsg = ''; this.passwordErrMsg = ''; this.selected = []; this.profile_link = ''; this.password = ''; }, openScientificDisciplineEdit() { this.editMode = true; this.showEditScientificDis = true; this.showEditProfileLink = false; this.showChangePassword = false; }, openProfileLinkEdit() { this.editMode = true; this.showEditScientificDis = false; this.showEditProfileLink = true; this.showChangePassword = false; }, openChangePassword() { this.editMode = true; this.showEditScientificDis = false; this.showEditProfileLink = false; this.showChangePassword = true; }, showDialog() { this.toasterMessage = 'Profile update successful!'; this.toasterIsOpen = true; }, hideDialog() { this.toasterIsOpen = false; }, showEditMode() { this.editMode = true; }, setRole(roleName) { //console.log("role name is,", roleName); const vm = this; window.localStorage.setItem('roleName', roleName); vm.$store.dispatch('User/setUserRole', roleName); return roleName; }, async update(e) { // console.log("user data", e); const vm = this; if (vm.isDev) { host = require('@/components/js/const').devApiHost; } const jwt = vm.jwt; if (jwt === null) { vm.loaded = false; return; } vm.user = e.id; let dat; if (this.showEditScientificDis) { if (this.selected.length === 0) { this.error = true; this.scientificErrMsg = 'Please select at least one discipline.'; return; } dat = { id: vm.user, scientific_discipline: vm.selected, }; } else if (this.showEditProfileLink) { if (!this.profile_link) { this.error = true; this.profileLinkErrMsg = 'Please enter your profile link.'; return; } dat = { id: vm.user, profile_link: vm.profile_link, }; } else if (this.showChangePassword) { if (!this.currentPassword) { this.error = true; this.passwordErrMsg = 'Please enter the current password.'; return; } if (!this.newPassword) { this.error = true; this.passwordErrMsg = 'Please enter new password.'; return; } if (!this.confirmPassword) { this.error = true; this.passwordErrMsg = 'Please enter new password again.'; return; } dat = { identifier: vm.user, password: vm.currentPassword, newPassword: vm.newPassword, confirmPassword: vm.confirmPassword, }; } this.error = false; this.scientificErrMsg = ''; this.profileLinkErrMsg = ''; this.passwordErrMsg = ''; // let dat = { // id: vm.user, // profile_link: vm.profile_link, // scientific_discipline: vm.selected, // password: vm.password, // }; // console.log(dat); try { let res; if (this.showChangePassword) { res = await this.axios.post(`${host}/api/password`, dat, { headers: { Authorization: `Bearer ${jwt}`, }, }); // console.log(res.data); const newJwt = res.data.jwt; window.localStorage.setItem('jwt', newJwt); this.$store.dispatch('User/setJwt', newJwt); } else { res = await this.axios.put(`${host}/api/users/${vm.user}`, dat, { headers: { Authorization: `Bearer ${jwt}`, }, }); } // console.log(res.data); this.error = false; this.toasterMessage = 'Profile update successful!'; this.toasterIsOpen = true; this.editMode = false; this.isOpen = false; setTimeout(() => { this.toasterIsOpen = false; }, 2000); this.selected = []; this.profile_link = ''; this.password = ''; this.updatedKey += 1; } catch (error) { // console.log(error.response.data.error.message) // console.log(error.response) this.error = true; this.errorMsg = error; } }, async approve(e) { const vm = this try { vm.user = e.id; if (vm.isDev) { host = require('@/components/js/const').devApiHost; } let jwt = vm.jwt; if (jwt === null) { vm.loaded = false; return; } const dat = { id: vm.user, role: 3, // Make the user contributor }; let res = await this.axios.put(`${host}/api/users/${vm.user}`, dat, { headers: { Authorization: `Bearer ${jwt}`, }, }); jwt = require('@/components/js/const').wikiApikey; const query = `mutation User { users { create (email: "${e.email}", name: "${e.full_name}", passwordRaw: "welcome2cdcode", providerKey: "local", groups: 3, mustChangePassword: true, sendWelcomeEmail: true) { responseResult { succeeded, errorCode, slug, message }, user { email, name } } } }` res = await this.axios.post(`https://wiki.cd-code.org/graphql`, { query }, { headers: { Authorization: `Bearer ${jwt}`, }, }); vm.error = false; vm.toasterMessage = 'Approved and a welcome email sent!'; vm.toasterIsOpen = true; setTimeout(() => { vm.toasterIsOpen = false; }, 2000); vm.updatedUsersKey += 1; } catch (error) { this.error = true; this.errorMsg = error; } } }, }; </script> <!-- Add "scoped" attribute to limit CSS to this component only --> <style scoped> @import url("~@/assets/bootstrap.css"); .main { margin: 1.5rem; } h3 { margin: 40px 0 0; } /* a { color: #ef0087 !important; } */ .panel { font-size: 1.2rem; } ul.a { list-style-type: disc; list-style-position: inside; } </style>