feat: Add user management invite links without SMTP set up (#5084)
* feat: update n8n-users-list to no longer use preset list of actions * feat: prepared users settings for invite links feature * refactor: Return invite link URLs when inviting users (#5079) * refactor: Return invite link URLs when inviting users * test: Refactor and add tests to mailer * feat: Add FE inviteAcceptUrl integration (#5085) * feat: update n8n-users-list to no longer use preset list of actions * feat: prepared users settings for invite links feature * feat: add integration with new inviteAcceptUrl changes * feat: Add inviteAcceptUrl to user list for pending users Co-authored-by: Alex Grozav <alex@grozav.com> * fix conflicts * fix lint issue * test: Make sure inviteAcceptUrl is defined * feat: update smtp setup suggestion * feat: add invite link summary when inviting multiple users * refactor: Add telemetry flag for when email is sent * fix: add email_sent correctly to telemetry event * feat: move SMTP info-tip to invite modal Co-authored-by: Omar Ajoue <krynble@gmail.com>
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import N8nUsersList from './UsersList.vue';
|
||||
import { action } from '@storybook/addon-actions';
|
||||
import type { StoryFn } from '@storybook/vue';
|
||||
import { IUser } from '@/types';
|
||||
|
||||
export default {
|
||||
title: 'Modules/UsersList',
|
||||
@@ -21,12 +22,24 @@ const Template: StoryFn = (args, { argTypes }) => ({
|
||||
components: {
|
||||
N8nUsersList,
|
||||
},
|
||||
template: '<n8n-users-list v-bind="$props" @reinvite="onReinvite" @delete="onDelete" />',
|
||||
template:
|
||||
'<n8n-users-list v-bind="$props" :actions="actions" @reinvite="onReinvite" @delete="onDelete" />',
|
||||
methods,
|
||||
});
|
||||
|
||||
export const UsersList = Template.bind({});
|
||||
UsersList.args = {
|
||||
actions: [
|
||||
{
|
||||
label: 'Resend Invite',
|
||||
value: 'reinvite',
|
||||
guard: (user: IUser) => !user.firstName,
|
||||
},
|
||||
{
|
||||
label: 'Delete User',
|
||||
value: 'delete',
|
||||
},
|
||||
],
|
||||
users: [
|
||||
{
|
||||
id: '1',
|
||||
|
||||
@@ -25,20 +25,14 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { IUser } from '../../types';
|
||||
import { IUser, IUserListAction } from '../../types';
|
||||
import N8nActionToggle from '../N8nActionToggle';
|
||||
import N8nBadge from '../N8nBadge';
|
||||
import N8nUserInfo from '../N8nUserInfo';
|
||||
import Locale from '../../mixins/locale';
|
||||
import mixins from 'vue-typed-mixins';
|
||||
import { t } from '../../locale';
|
||||
import { PropType } from 'vue';
|
||||
|
||||
export interface IUserListAction {
|
||||
label: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
export default mixins(Locale).extend({
|
||||
name: 'n8n-users-list',
|
||||
components: {
|
||||
@@ -61,17 +55,9 @@ export default mixins(Locale).extend({
|
||||
currentUserId: {
|
||||
type: String,
|
||||
},
|
||||
deleteLabel: {
|
||||
type: String,
|
||||
default: () => t('nds.usersList.deleteUser'),
|
||||
},
|
||||
reinviteLabel: {
|
||||
type: String,
|
||||
default: () => t('nds.usersList.reinviteUser'),
|
||||
},
|
||||
actions: {
|
||||
type: Array as PropType<string[]>,
|
||||
default: () => ['delete', 'reinvite'],
|
||||
type: Array as PropType<IUserListAction[]>,
|
||||
default: () => [],
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
@@ -115,37 +101,16 @@ export default mixins(Locale).extend({
|
||||
},
|
||||
methods: {
|
||||
getActions(user: IUser): IUserListAction[] {
|
||||
const actions = [];
|
||||
const DELETE: IUserListAction = {
|
||||
label: this.deleteLabel,
|
||||
value: 'delete',
|
||||
};
|
||||
|
||||
const REINVITE: IUserListAction = {
|
||||
label: this.reinviteLabel,
|
||||
value: 'reinvite',
|
||||
};
|
||||
|
||||
if (user.isOwner) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (!user.firstName) {
|
||||
if (this.actions.includes('reinvite')) {
|
||||
actions.push(REINVITE);
|
||||
}
|
||||
}
|
||||
const defaultGuard = () => true;
|
||||
|
||||
if (this.actions.includes('delete')) {
|
||||
actions.push(DELETE);
|
||||
}
|
||||
|
||||
return actions;
|
||||
return this.actions.filter((action) => (action.guard || defaultGuard)(user));
|
||||
},
|
||||
onUserAction(user: IUser, action: string): void {
|
||||
if (action === 'delete' || action === 'reinvite') {
|
||||
this.$emit(action, user.id);
|
||||
}
|
||||
this.$emit(action, user.id);
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -3,8 +3,6 @@ export default {
|
||||
'nds.userInfo.you': '(you)',
|
||||
'nds.userSelect.selectUser': 'Select User',
|
||||
'nds.userSelect.noMatchingUsers': 'No matching users',
|
||||
'nds.usersList.deleteUser': 'Delete User',
|
||||
'nds.usersList.reinviteUser': 'Resend invite',
|
||||
'notice.showMore': 'Show more',
|
||||
'notice.showLess': 'Show less',
|
||||
'formInput.validator.fieldRequired': 'This field is required',
|
||||
|
||||
@@ -6,4 +6,17 @@ export interface IUser {
|
||||
email?: string;
|
||||
isOwner: boolean;
|
||||
isPendingUser: boolean;
|
||||
inviteAcceptUrl?: string;
|
||||
}
|
||||
|
||||
export interface IUserListAction {
|
||||
label: string;
|
||||
value: string;
|
||||
guard?: (user: IUser) => boolean;
|
||||
}
|
||||
|
||||
export interface IUserListAction {
|
||||
label: string;
|
||||
value: string;
|
||||
guard?: (user: IUser) => boolean;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user