add user invitation

This commit is contained in:
Nitwel
2020-10-20 12:46:37 +02:00
parent 24da502089
commit 5167b33430
5 changed files with 150 additions and 3 deletions

View File

@@ -31,6 +31,11 @@
"create_user": "Create User",
"create_webhook": "Create Webhook",
"invite_users": "Invite Users",
"add_to_invite_user": "admin@example.com, user@example.com...",
"invite": "Invite",
"emails": "Emails",
"connection_excellent": "Excellent Connection",
"connection_good": "Good Connection",
"connection_fair": "Fair Connection",

View File

@@ -39,6 +39,10 @@
</v-card>
</v-dialog>
<v-button rounded icon @click="userInviteModalActive = true" v-tooltip.bottom="$t('invite_users')">
<v-icon name="person_add" />
</v-button>
<v-button
rounded
icon
@@ -55,6 +59,8 @@
<settings-navigation />
</template>
<users-invite v-model="userInviteModalActive" :role="primaryKey" />
<div class="roles">
<v-notice v-if="adminEnabled" type="info">
{{ $t('admins_have_all_permissions') }}
@@ -88,6 +94,7 @@ import useItem from '@/composables/use-item';
import { useUserStore } from '@/stores/';
import RoleInfoSidebarDetail from './components/role-info-sidebar-detail.vue';
import PermissionsOverview from './components/permissions-overview.vue';
import UsersInvite from '@/views/private/components/users-invite';
type Values = {
[field: string]: any;
@@ -95,7 +102,7 @@ type Values = {
export default defineComponent({
name: 'roles-item',
components: { SettingsNavigation, RevisionsDrawerDetail, RoleInfoSidebarDetail, PermissionsOverview },
components: { SettingsNavigation, RevisionsDrawerDetail, RoleInfoSidebarDetail, PermissionsOverview, UsersInvite },
props: {
primaryKey: {
type: String,
@@ -108,7 +115,7 @@ export default defineComponent({
},
setup(props) {
const userStore = useUserStore();
const userInviteModalActive = ref(false);
const { primaryKey } = toRefs(props);
const { edits, item, saving, loading, error, save, remove, deleting, isBatch } = useItem(
@@ -142,6 +149,7 @@ export default defineComponent({
deleting,
isBatch,
adminEnabled,
userInviteModalActive,
};
/**

View File

@@ -49,6 +49,10 @@
<v-icon name="edit" outline />
</v-button>
<v-button rounded icon @click="userInviteModalActive = true" v-tooltip.bottom="$t('invite_users')">
<v-icon name="person_add" />
</v-button>
<v-button rounded icon :to="addNewLink" v-tooltip.bottom="$t('create_user')">
<v-icon name="add" />
</v-button>
@@ -58,6 +62,8 @@
<users-navigation :current-role="queryFilters && queryFilters.role" />
</template>
<users-invite v-model="userInviteModalActive" />
<component
class="layout"
ref="layoutRef"
@@ -104,6 +110,7 @@
<script lang="ts">
import { defineComponent, computed, ref, PropType } from '@vue/composition-api';
import UsersNavigation from '../components/navigation.vue';
import UsersInvite from '@/views/private/components/users-invite';
import { i18n } from '@/lang';
import api from '@/api';
@@ -120,7 +127,7 @@ type Item = {
export default defineComponent({
name: 'users-collection',
components: { UsersNavigation, LayoutSidebarDetail, SearchInput },
components: { UsersNavigation, LayoutSidebarDetail, SearchInput, UsersInvite },
props: {
queryFilters: {
type: Object as PropType<Record<string, string>>,
@@ -130,6 +137,7 @@ export default defineComponent({
setup(props) {
const { roles } = useNavigation();
const layoutRef = ref<LayoutComponent | null>(null);
const userInviteModalActive = ref(false);
const selection = ref<Item[]>([]);
@@ -175,6 +183,7 @@ export default defineComponent({
searchQuery,
marked,
clearFilters,
userInviteModalActive,
};
function useBatchDelete() {

View File

@@ -0,0 +1,4 @@
import UsersInvite from './users-invite.vue';
export { UsersInvite };
export default UsersInvite;

View File

@@ -0,0 +1,121 @@
<template>
<v-dialog :active="active" @toggle="$emit('toggle', $event)" @esc="$emit('toggle', false)">
<v-card>
<v-card-title>{{ $t('invite_users') }}</v-card-title>
<v-card-text class="grid">
<div class="field">
<span class="type-label">{{ $t('emails') }}</span>
<interface-tags
v-model="emails"
:placeholder="$t('add_to_invite_user')"
icon-right="email"
whitespace=""
></interface-tags>
</div>
<div class="field" v-if="role === null">
<span class="type-label">{{ $t('role') }}</span>
<v-select v-model="roleSelected" :items="roles"></v-select>
</div>
</v-card-text>
<v-card-actions>
<v-button secondary @click="$emit('toggle', false)">{{ $t('cancel') }}</v-button>
<v-button @click="inviteUsers" :disabled="emails === null || emails.length === 0" :loading="loading">
{{ $t('invite') }}
</v-button>
</v-card-actions>
</v-card>
</v-dialog>
</template>
<script lang="ts">
import { defineComponent, computed, ref, PropType, watch } from '@vue/composition-api';
import api from '@/api';
import { useNotificationsStore } from '@/stores';
import i18n from '@/lang';
export default defineComponent({
model: {
prop: 'active',
event: 'toggle',
},
props: {
active: {
type: Boolean,
default: false,
},
role: {
type: String,
default: null,
},
},
setup(props, { emit }) {
const notifications = useNotificationsStore();
const emails = ref<string[]>([]);
const roles = ref<Record<string, any>[]>([]);
const roleSelected = ref<string | null>(props.role);
const loading = ref(false);
watch(
() => props.active,
() => {
loadRoles();
}
);
return { emails, inviteUsers, roles, roleSelected, loading };
async function inviteUsers() {
loading.value = true;
try {
await Promise.all(
emails.value.map((email) => {
return api.post('/users/invite', {
email,
role: roleSelected.value,
});
})
);
emit('toggle', false);
} catch (err) {
notifications.add({
title: i18n.t('server_error'),
text: err.message,
persist: true,
type: 'error',
});
} finally {
loading.value = false;
}
}
async function loadRoles() {
const response = await api.get('/roles');
roles.value = response.data.data.map((role: Record<string, any>) => ({
text: role.name,
value: role.id,
}));
if (roles.value.length > 0 && roleSelected.value === null) {
roleSelected.value = roles.value[0].value;
}
}
},
});
</script>
<style lang="scss">
@import '@/styles/mixins/form-grid';
.v-card-text {
--v-form-vertical-gap: 20px;
@include form-grid;
}
.v-card-title {
font-size: 20px;
}
</style>