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:
Alex Grozav
2023-01-05 17:10:08 +02:00
committed by GitHub
parent 11a46a4cbc
commit 2327563c44
22 changed files with 419 additions and 247 deletions

View File

@@ -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',

View File

@@ -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);
},
},
});

View File

@@ -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',

View File

@@ -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;
}