mirror of
https://github.com/nocodb/nocodb.git
synced 2026-05-05 21:06:54 +00:00
Xmysql is now NocoDB (An Open Source Airtable alternative)
This commit is contained in:
788
packages/nc-gui/components/auth/userManagement.vue
Normal file
788
packages/nc-gui/components/auth/userManagement.vue
Normal file
@@ -0,0 +1,788 @@
|
||||
<template>
|
||||
<div class="h-100">
|
||||
|
||||
<v-toolbar flat height="38" class="toolbar-border-bottom">
|
||||
<v-text-field
|
||||
style="max-width: 300px"
|
||||
dense
|
||||
solo
|
||||
flat
|
||||
class="search-field caption"
|
||||
v-model="query" hide-details
|
||||
@keypress.enter="loadUsers"
|
||||
placeholder="Filter by email">
|
||||
<template v-slot:prepend-inner>
|
||||
<v-icon class="mt-1" small>search</v-icon>
|
||||
</template>
|
||||
</v-text-field>
|
||||
<v-spacer></v-spacer>
|
||||
|
||||
<x-btn outlined tooltip="Reload roles"
|
||||
color="primary"
|
||||
small
|
||||
:disabled="loading"
|
||||
@click="loadUsers"
|
||||
v-ge="['roles','reload']"
|
||||
@click.prevent>
|
||||
<v-icon small left>refresh</v-icon>
|
||||
Reload
|
||||
</x-btn>
|
||||
<x-btn
|
||||
v-if="_isUIAllowed('newUser')"
|
||||
outlined tooltip="Add new role"
|
||||
color="primary"
|
||||
small
|
||||
:disabled="loading"
|
||||
@click="addUser"
|
||||
v-ge="['roles','add new']">
|
||||
<v-icon small left>mdi-plus</v-icon>
|
||||
New User
|
||||
</x-btn>
|
||||
</v-toolbar>
|
||||
|
||||
<v-card style="height:calc(100% - 38px)">
|
||||
<v-container style="height: 100%" fluid>
|
||||
<!-- <div class="d-flex d-100 justify-center">-->
|
||||
|
||||
|
||||
<v-row style="height:100%">
|
||||
<v-col cols="12" class="h-100">
|
||||
|
||||
<v-card class="h-100 elevation-0">
|
||||
<v-row style="height:100%">
|
||||
<v-col offset="2" :cols="8" class="h-100">
|
||||
|
||||
<!-- <v-card class="h-100 px-4 py-2">-->
|
||||
|
||||
|
||||
<!-- <v-row>
|
||||
<v-col>
|
||||
|
||||
</v-col>
|
||||
<v-col class="flex-shrink-1 flex-grow-0"><h4 class="text-center text-capitalize mt-2 d-100"
|
||||
style="min-width:100px">User List</h4></v-col>
|
||||
<v-col></v-col>
|
||||
|
||||
</v-row>-->
|
||||
<v-data-table dense v-if="users"
|
||||
:headers="[{},{},{},{}]"
|
||||
hide-default-header
|
||||
:hide-default-footer="count < limit"
|
||||
:options.sync="options"
|
||||
:items="users"
|
||||
:items-per-page.sync="limit"
|
||||
:server-items-length="count">
|
||||
<template v-slot:header="{props:{headers}}">
|
||||
<thead>
|
||||
<tr class="text-left">
|
||||
<!-- <th>#</th>-->
|
||||
<th class="font-weight-regular caption">
|
||||
<v-icon small>mdi-email-outline</v-icon>
|
||||
Email
|
||||
</th>
|
||||
<th class="font-weight-regular caption">
|
||||
<v-icon small>mdi-drama-masks</v-icon>
|
||||
Roles
|
||||
</th>
|
||||
<th class="font-weight-regular caption">
|
||||
<!-- <v-icon small class="mt-n1">mdi-cursor-default-outline</v-icon>-->
|
||||
Actions
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
</template>
|
||||
|
||||
|
||||
<template v-slot:item="{item,index}">
|
||||
<tr @click="selectedUser = item">
|
||||
<!-- <td>
|
||||
<v-radio-group dense hide-details v-model="selectedUserIndex" class="mt-n2">
|
||||
<v-radio :value="index"
|
||||
></v-radio>
|
||||
</v-radio-group>
|
||||
</td>-->
|
||||
<td>{{ item.email }}</td>
|
||||
<td>
|
||||
<!-- {{ item.roles }}-->
|
||||
|
||||
<v-chip class="mr-1" v-for="role in (item.roles ? item.roles.split(',') : [])" :key="role"
|
||||
:color="rolesColors[role]">
|
||||
{{ role }}
|
||||
</v-chip>
|
||||
|
||||
|
||||
<!-- <v-edit-dialog-->
|
||||
<!-- >-->
|
||||
<!-- <div-->
|
||||
<!-- :title="item.roles"-->
|
||||
<!-- style="width:180px;overflow:hidden;white-space: nowrap;text-overflow:ellipsis"> {{-->
|
||||
<!-- item.roles-->
|
||||
<!-- }}-->
|
||||
<!-- </div>-->
|
||||
<!-- <template v-slot:input>-->
|
||||
<!-- <v-text-field-->
|
||||
<!-- v-model="item.roles"-->
|
||||
<!-- label="Edit"-->
|
||||
<!-- single-line-->
|
||||
<!-- ></v-text-field>-->
|
||||
<!-- </template>-->
|
||||
<!-- </v-edit-dialog>-->
|
||||
</td>
|
||||
<td>
|
||||
|
||||
|
||||
<x-icon tooltip="Edit User" iconClass=""
|
||||
color="primary"
|
||||
@click.prevent.stop="invite_token = null; selectedUser = item; userEditDialog = true"
|
||||
small>mdi-pencil-outline
|
||||
</x-icon>
|
||||
|
||||
<x-icon v-if="!item.project_id" tooltip="Add user to project"
|
||||
color="primary"
|
||||
@click="inviteUser(item.email)"
|
||||
small>mdi-plus
|
||||
</x-icon>
|
||||
<x-icon
|
||||
v-else
|
||||
tooltip="Remove user from project" class="ml-2" color="error"
|
||||
@click.prevent.stop="deleteId = item.id; deleteItem = item.id;showConfirmDlg = true"
|
||||
small>mdi-delete-outline
|
||||
</x-icon>
|
||||
|
||||
<x-icon v-if="item.invite_token" tooltip="Resend invite email" iconClass="mt-n1"
|
||||
color="primary"
|
||||
@click.prevent.stop="rensendInvite(item.id)"
|
||||
small>mdi-email-send-outline
|
||||
</x-icon>
|
||||
|
||||
<x-icon v-if="item.invite_token" tooltip="Copy invite url" iconClass=""
|
||||
color="primary"
|
||||
@click.prevent.stop="clipboard(getInviteUrl(item.invite_token)); $toast.success('Invite url copied to clipboard').goAway(3000)"
|
||||
small>mdi-content-copy
|
||||
</x-icon>
|
||||
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</template>
|
||||
</v-data-table>
|
||||
<div class="mt-10 text-center">
|
||||
|
||||
<x-btn
|
||||
v-if="_isUIAllowed('newUser')"
|
||||
outlined tooltip="Add new user"
|
||||
color="primary"
|
||||
small
|
||||
:disabled="loading"
|
||||
@click="addUser"
|
||||
v-ge="['roles','add new']">
|
||||
<v-icon small left>mdi-plus</v-icon>
|
||||
New User
|
||||
</x-btn>
|
||||
</div>
|
||||
<!-- </v-card>-->
|
||||
</v-col>
|
||||
|
||||
<!-- <v-col cols="5" v-if="selectedUser && _isUIAllowed('editUser')" style="height:100%; overflow: auto">
|
||||
<v-card class="px-4 py-2" style="min-height: 100%">
|
||||
<h4 class="text-center text-capitalize mt-2 d-100 grey--text text-darken-1">User Info</h4>
|
||||
|
||||
<v-toolbar flat height="38" class="toolbar-border-bottom">
|
||||
<v-spacer></v-spacer>
|
||||
|
||||
<x-btn outlined tooltip="Save Changes"
|
||||
color="primary"
|
||||
small
|
||||
@click="saveUser"
|
||||
:disabled="loading || !edited || !valid"
|
||||
btn.class="text-capitalize"
|
||||
v-ge="['rows','save']"
|
||||
>
|
||||
<v-icon small left>save</v-icon>
|
||||
Save<span v-if="!selectedUser.id"> & Invite</span>
|
||||
</x-btn>
|
||||
|
||||
|
||||
</v-toolbar>
|
||||
<v-form v-model="valid">
|
||||
|
||||
<v-row class="mt-3">
|
||||
<!– <v-col cols="12" class="edit-header">–>
|
||||
<!– </v-col>–>
|
||||
<v-col cols="12">
|
||||
<v-text-field
|
||||
:disabled="!!selectedUser.id"
|
||||
auto
|
||||
dense
|
||||
v-model="selectedUser.email"
|
||||
:rules="emailRules"
|
||||
@input="edited=true"
|
||||
outlined label="Email"></v-text-field>
|
||||
</v-col>
|
||||
<v-col cols="12">
|
||||
|
||||
<v-combobox
|
||||
class="role-select"
|
||||
outlined
|
||||
hide-details
|
||||
v-model="selectedRoles"
|
||||
:items="roles"
|
||||
@change="edited = true"
|
||||
label="Select User roles"
|
||||
multiple
|
||||
chips
|
||||
dense
|
||||
deletable-chips
|
||||
color="warning"
|
||||
></v-combobox>
|
||||
|
||||
</v-col>
|
||||
|
||||
</v-row>
|
||||
</v-form>
|
||||
</v-card>
|
||||
</v-col>-->
|
||||
|
||||
|
||||
</v-row>
|
||||
</v-card>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<table v-if="false" class="mx-auto users-table" style="min-width:700px">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>Email</th>
|
||||
<th>Roles</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="user in users">
|
||||
<td>
|
||||
<v-icon x-large>mdi-account-outline</v-icon>
|
||||
|
||||
</td>
|
||||
<td class="px-1 py-1" align="top">
|
||||
<v-text-field
|
||||
solo
|
||||
class="elevation-0"
|
||||
disabled
|
||||
:value="user.email"
|
||||
hide-details
|
||||
dense></v-text-field>
|
||||
</td>
|
||||
<td class="px-1 py-1">
|
||||
|
||||
<set-list-checkbox-cell v-model="user.roles" :values="roles"></set-list-checkbox-cell>
|
||||
<!-- <v-text-field outlined hide-details dense></v-text-field>-->
|
||||
<!-- <v-combobox
|
||||
solo
|
||||
:value="user.roles.split(',')"
|
||||
class="elevation-0"
|
||||
:items="roles"
|
||||
hide-selected
|
||||
multiple
|
||||
small-chips
|
||||
hide-details
|
||||
dense
|
||||
>
|
||||
</v-combobox>-->
|
||||
</td>
|
||||
<td align="middle">
|
||||
<v-icon large>mdi-close</v-icon>
|
||||
<v-icon large>mdi-save</v-icon>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<v-icon x-large>mdi-account-outline</v-icon>
|
||||
|
||||
</td>
|
||||
<td class="px-1 py-1" align="top">
|
||||
<v-text-field
|
||||
solo
|
||||
class="elevation-0"
|
||||
|
||||
hide-details
|
||||
dense></v-text-field>
|
||||
</td>
|
||||
<td class="px-1 py-1">
|
||||
|
||||
<set-list-checkbox-cell :values="roles"></set-list-checkbox-cell>
|
||||
<!-- <v-text-field outlined hide-details dense></v-text-field>-->
|
||||
<!-- <v-combobox
|
||||
solo
|
||||
class="elevation-0"
|
||||
:items="roles"
|
||||
|
||||
hide-selected
|
||||
multiple
|
||||
small-chips
|
||||
|
||||
hide-details
|
||||
dense
|
||||
>
|
||||
</v-combobox>-->
|
||||
</td>
|
||||
<td align="middle">
|
||||
<v-icon large>mdi-account-plus</v-icon>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</v-container>
|
||||
</v-card>
|
||||
|
||||
<dlg-label-submit-cancel
|
||||
type="primary"
|
||||
:actions-mtd="confirmDelete"
|
||||
heading="Do you want to remove the user from project?"
|
||||
:dialog-show="showConfirmDlg"
|
||||
></dlg-label-submit-cancel>
|
||||
|
||||
|
||||
<v-dialog v-model="userEditDialog" :width="invite_token ? 700 :480" @close="invite_token = null">
|
||||
<v-card v-if="selectedUser" class="px-15 py-5 " style="min-height: 100%">
|
||||
<h4 class="text-center text-capitalize mt-2 d-100 display-1">
|
||||
<template v-if="invite_token">Copy Invite Token</template>
|
||||
<template v-else-if="selectedUser.id">Edit User</template>
|
||||
<template v-else>Invite</template>
|
||||
</h4>
|
||||
<div v-if="invite_token" class="mt-6 align-center">
|
||||
<!-- <div class="d-flex">
|
||||
<v-alert
|
||||
type="success"
|
||||
outlined
|
||||
|
||||
>
|
||||
<div class="ellipsis d-100">
|
||||
{{ inviteUrl }}
|
||||
</div>
|
||||
</v-alert>
|
||||
|
||||
<x-icon icon.class="ml-3 mt-n3"
|
||||
@click="clipboard(inviteUrl); $toast.success('Copied invite url to clipboard').goAway(3000)">
|
||||
mdi-content-copy
|
||||
</x-icon>
|
||||
</div>-->
|
||||
<v-alert
|
||||
type="success"
|
||||
outlined
|
||||
v-ripple
|
||||
|
||||
@click="clipboard(inviteUrl); $toast.success('Copied invite url to clipboard').goAway(3000)"
|
||||
class="pointer"
|
||||
>
|
||||
<template v-slot:append>
|
||||
<v-icon color="green" class="ml-2">mdi-content-copy</v-icon>
|
||||
</template>
|
||||
<div class="ellipsis d-100">
|
||||
{{ inviteUrl }}
|
||||
</div>
|
||||
</v-alert>
|
||||
|
||||
<p class="caption grey--text mt-3"> Looks like you have not configured mailer yet! <br>Please copy above
|
||||
invite
|
||||
link and send it to {{ invite_token && invite_token.email }}.</p>
|
||||
</div>
|
||||
<template v-else>
|
||||
<v-form v-model="valid" @submit.prevent="saveUser">
|
||||
|
||||
<v-row class="mt-4">
|
||||
<v-col cols="12">
|
||||
<v-text-field
|
||||
:disabled="!!selectedUser.id"
|
||||
filled
|
||||
dense
|
||||
ref="email"
|
||||
v-model="selectedUser.email"
|
||||
:rules="emailRules"
|
||||
@input="edited=true"
|
||||
label="Email"></v-text-field>
|
||||
</v-col>
|
||||
<v-col cols="12">
|
||||
|
||||
<v-combobox
|
||||
filled
|
||||
class="role-select"
|
||||
hide-details
|
||||
v-model="selectedRoles"
|
||||
:items="roles"
|
||||
@change="edited = true"
|
||||
label="Select User roles"
|
||||
multiple
|
||||
dense
|
||||
deletable-chips
|
||||
>
|
||||
<template v-slot:selection="{item}">
|
||||
<v-chip :color="rolesColors[item]">{{ item }}</v-chip>
|
||||
</template>
|
||||
</v-combobox>
|
||||
|
||||
</v-col>
|
||||
|
||||
</v-row>
|
||||
</v-form>
|
||||
|
||||
<v-card-actions class="justify-center">
|
||||
<x-btn tooltip="Save Changes"
|
||||
color="primary"
|
||||
@click="saveUser"
|
||||
btn.class="mt-5 mb-3 pr-5"
|
||||
v-ge="['rows','save']"
|
||||
>
|
||||
<v-icon small left>{{ selectedUser.id ? 'save' : 'mdi-send' }}</v-icon>
|
||||
{{ selectedUser.id ? 'Save' : 'Invite' }}
|
||||
</x-btn>
|
||||
</v-card-actions>
|
||||
</template>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import SetListCheckboxCell from "@/components/project/spreadsheet/editableCell/setListCheckboxCell";
|
||||
import {enumColor as colors, enumColor} from "@/components/project/spreadsheet/helpers/colors";
|
||||
import DlgLabelSubmitCancel from "@/components/utils/dlgLabelSubmitCancel";
|
||||
|
||||
export default {
|
||||
name: "user-management",
|
||||
components: {DlgLabelSubmitCancel, SetListCheckboxCell},
|
||||
data: () => ({
|
||||
deleteItem: null,
|
||||
invite_token: null,
|
||||
userEditDialog: false,
|
||||
limit: 10,
|
||||
showConfirmDlg: false,
|
||||
users: null,
|
||||
count: 0,
|
||||
options: {},
|
||||
loading: false,
|
||||
selectedUser: null,
|
||||
roles: [],
|
||||
query: '',
|
||||
deleteId: null,
|
||||
edited: false,
|
||||
valid: null,
|
||||
emailRules: [
|
||||
v => !!v || 'E-mail is required',
|
||||
v => /^\w+([.-]?\w+)*@\w+([.-]?\w+)*(\.\w{2,})+$/.test(v) || 'E-mail must be valid'
|
||||
],
|
||||
userList: []
|
||||
}),
|
||||
async created() {
|
||||
await this.loadUsers();
|
||||
await this.loadRoles();
|
||||
},
|
||||
methods: {
|
||||
simpleAnim() {
|
||||
var count = 30;
|
||||
var defaults = {
|
||||
origin: {y: 0.7},
|
||||
zIndex: 9999999
|
||||
};
|
||||
|
||||
function fire(particleRatio, opts) {
|
||||
window.confetti(Object.assign({}, defaults, opts, {
|
||||
particleCount: Math.floor(count * particleRatio),
|
||||
}));
|
||||
}
|
||||
|
||||
fire(0.25, {
|
||||
spread: 26,
|
||||
startVelocity: 55,
|
||||
});
|
||||
fire(0.2, {
|
||||
spread: 60,
|
||||
});
|
||||
fire(0.35, {
|
||||
spread: 100,
|
||||
decay: 0.91,
|
||||
scalar: 0.8
|
||||
});
|
||||
fire(0.1, {
|
||||
spread: 120,
|
||||
startVelocity: 25,
|
||||
decay: 0.92,
|
||||
scalar: 1.2
|
||||
});
|
||||
fire(0.1, {
|
||||
spread: 120,
|
||||
startVelocity: 45,
|
||||
});
|
||||
},
|
||||
getInviteUrl(token) {
|
||||
return token ? `${location.origin}${location.pathname}#/user/authentication/signup/${token}` : null;
|
||||
},
|
||||
|
||||
clipboard(str) {
|
||||
const el = document.createElement('textarea');
|
||||
el.addEventListener('focusin', e => e.stopPropagation());
|
||||
el.value = str;
|
||||
document.body.appendChild(el);
|
||||
el.select();
|
||||
document.execCommand('copy');
|
||||
document.body.removeChild(el);
|
||||
},
|
||||
async rensendInvite(id) {
|
||||
try {
|
||||
await this.$axios.post('/admin/resendInvite/' + id, null, {
|
||||
headers: {
|
||||
'xc-auth': this.$store.state.users.token
|
||||
}
|
||||
});
|
||||
this.$toast.success('Invite email sent successfully').goAway(3000);
|
||||
await this.loadUsers();
|
||||
} catch (e) {
|
||||
this.$toast.error(e.response.data.msg).goAway(3000);
|
||||
}
|
||||
},
|
||||
async loadUsers() {
|
||||
try {
|
||||
const {sortBy, sortDesc, page = 1, itemsPerPage = 20} = this.options;
|
||||
const data = (await this.$axios.get('/admin', {
|
||||
headers: {
|
||||
'xc-auth': this.$store.state.users.token
|
||||
},
|
||||
params: {
|
||||
limit: itemsPerPage,
|
||||
offset: (page - 1) * itemsPerPage,
|
||||
query: this.query,
|
||||
project_id: this.$route.params.project_id
|
||||
}
|
||||
})).data;
|
||||
this.count = data.count;
|
||||
this.users = data.list;
|
||||
if (!this.selectedUser && this.users && this.users[0]) {
|
||||
this.selectedUserIndex = 0;
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
}
|
||||
},
|
||||
async loadRoles() {
|
||||
try {
|
||||
this.roles = (await this.$axios.get('/admin/roles', {
|
||||
headers: {
|
||||
'xc-auth': this.$store.state.users.token
|
||||
},
|
||||
params: {
|
||||
project_id: this.$route.params.project_id
|
||||
}
|
||||
})).data.map(role => role.title).filter(role => role !== 'guest');
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
}
|
||||
},
|
||||
async deleteUser(id) {
|
||||
try {
|
||||
await this.$axios.delete('/admin/' + id, {
|
||||
params: {
|
||||
project_id: this.$route.params.project_id,
|
||||
email: this.deleteItem.email
|
||||
},
|
||||
headers: {
|
||||
'xc-auth': this.$store.state.users.token
|
||||
}
|
||||
});
|
||||
this.$toast.success('Successfully removed the user from project').goAway(3000);
|
||||
await this.loadUsers();
|
||||
} catch (e) {
|
||||
this.$toast.error(e.response.data.msg).goAway(3000);
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
async confirmDelete(hideDialog) {
|
||||
if (hideDialog) {
|
||||
this.showConfirmDlg = false;
|
||||
return
|
||||
}
|
||||
await this.deleteUser(this.deleteId);
|
||||
this.showConfirmDlg = false;
|
||||
},
|
||||
addUser() {
|
||||
this.invite_token = null;
|
||||
this.selectedUser = {
|
||||
roles: 'creator,editor'
|
||||
}
|
||||
this.userEditDialog = true
|
||||
},
|
||||
async inviteUser(email) {
|
||||
try {
|
||||
await this.$axios.post('/admin', {email, project_id: this.$route.params.project_id}, {
|
||||
headers: {
|
||||
'xc-auth': this.$store.state.users.token
|
||||
}
|
||||
});
|
||||
this.$toast.success('Successfully added user to project').goAway(3000);
|
||||
await this.loadUsers();
|
||||
} catch (e) {
|
||||
this.$toast.error(e.response.data.msg).goAway(3000);
|
||||
}
|
||||
},
|
||||
async saveUser() {
|
||||
if (this.loading || !this.valid || !this.selectedUser) {
|
||||
return
|
||||
}
|
||||
|
||||
if (!this.edited) {
|
||||
this.userEditDialog = false;
|
||||
}
|
||||
|
||||
try {
|
||||
let data;
|
||||
if (this.selectedUser.id) {
|
||||
await this.$axios.put('/admin/' + this.selectedUser.id, {
|
||||
roles: this.selectedUser.roles,
|
||||
email: this.selectedUser.email,
|
||||
project_id: this.$route.params.project_id
|
||||
}, {
|
||||
headers: {
|
||||
'xc-auth': this.$store.state.users.token
|
||||
}
|
||||
});
|
||||
} else {
|
||||
data = await this.$axios.post('/admin', {
|
||||
...this.selectedUser,
|
||||
project_id: this.$route.params.project_id
|
||||
}, {
|
||||
headers: {
|
||||
'xc-auth': this.$store.state.users.token
|
||||
}
|
||||
});
|
||||
}
|
||||
this.$toast.success('Successfully updated the user details').goAway(3000);
|
||||
await this.loadUsers();
|
||||
if (data && data.data && data.data.invite_token) {
|
||||
this.invite_token = data.data;
|
||||
this.simpleAnim();
|
||||
return;
|
||||
}
|
||||
} catch (e) {
|
||||
this.$toast.error(e.response.data.msg).goAway(3000);
|
||||
}
|
||||
|
||||
this.userEditDialog = false;
|
||||
await this.loadUsers();
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
options: {
|
||||
async handler() {
|
||||
await this.loadUsers();
|
||||
},
|
||||
deep: true,
|
||||
},
|
||||
userEditDialog(v) {
|
||||
if (v && (this.selectedUser && !this.selectedUser.id)) {
|
||||
this.$nextTick(() => {
|
||||
setTimeout(() => {
|
||||
this.$refs.email.$el.querySelector('input').focus();
|
||||
}, 100)
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
||||
inviteUrl() {
|
||||
return this.invite_token ? `${location.origin}${location.pathname}#/user/authentication/signup/${this.invite_token.invite_token}` : null;
|
||||
},
|
||||
rolesColors() {
|
||||
const colors = this.$store.state.windows.darkTheme ? enumColor.dark : enumColor.light;
|
||||
return this.roles.reduce((o, r, i) => {
|
||||
o[r] = colors[i % colors.length];
|
||||
return o;
|
||||
}, {})
|
||||
},
|
||||
selectedRoles: {
|
||||
get() {
|
||||
return this.selectedUser && this.selectedUser.roles ? this.selectedUser.roles.split(',') : [];
|
||||
}, set(roles) {
|
||||
if (this.selectedUser) {
|
||||
this.selectedUser.roles = roles.filter(Boolean).join(',');
|
||||
}
|
||||
}
|
||||
},
|
||||
selectedUserIndex: {
|
||||
get() {
|
||||
return this.users ? this.users.findIndex(u => u.email === this.selectedUser.email) : -1;
|
||||
}, set(i) {
|
||||
this.selectedUser = this.users[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
::v-deep {
|
||||
.v-alert__wrapper > .v-icon {
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
tbody tr:nth-of-type(odd) {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.search-field.v-text-field > .v-input__control, .search-field.v-text-field > .v-input__control > .v-input__slot {
|
||||
min-height: auto;
|
||||
}
|
||||
|
||||
.role-select {
|
||||
.v-select__selections {
|
||||
//padding-bottom: 0 !important;
|
||||
min-height: auto !important;
|
||||
|
||||
.v-chip {
|
||||
margin-top: 6px;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.users-table {
|
||||
td:first-child {
|
||||
width: 50px
|
||||
}
|
||||
|
||||
td:last-child {
|
||||
width: 50px
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<!--
|
||||
/**
|
||||
* @copyright Copyright (c) 2021, Xgene Cloud Ltd
|
||||
*
|
||||
* @author Naveen MR <oof1lab@gmail.com>
|
||||
* @author Pranav C Balan <pranavxc@gmail.com>
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
-->
|
||||
Reference in New Issue
Block a user