mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-16 17:46:45 +00:00
refactor: Add common DTOs and schemas for the redesigned users list (#16097)
This commit is contained in:
@@ -71,3 +71,4 @@ export { ListInsightsWorkflowQueryDto } from './insights/list-workflow-query.dto
|
||||
export { InsightsDateFilterDto } from './insights/date-filter.dto';
|
||||
|
||||
export { PaginationDto } from './pagination/pagination.dto';
|
||||
export { UsersListFilterDto } from './user/users-list-filter.dto';
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
import { UsersListFilterDto } from '../users-list-filter.dto';
|
||||
|
||||
describe('UsersListFilterDto', () => {
|
||||
describe('Valid requests', () => {
|
||||
test.each([
|
||||
{
|
||||
name: 'pagination with default values',
|
||||
request: {},
|
||||
parsedResult: { skip: 0, take: 10 },
|
||||
},
|
||||
{
|
||||
name: 'pagination with custom values',
|
||||
request: { skip: '5', take: '20' },
|
||||
parsedResult: { skip: 5, take: 20 },
|
||||
},
|
||||
{
|
||||
name: 'sort by name ascending',
|
||||
request: { sortBy: 'name:asc' },
|
||||
parsedResult: { skip: 0, take: 10, sortBy: 'name:asc' },
|
||||
},
|
||||
{
|
||||
name: 'sort by last active descending and pagination',
|
||||
request: { skip: '5', take: '20', sortBy: 'lastActive:desc' },
|
||||
parsedResult: { skip: 5, take: 20, sortBy: 'lastActive:desc' },
|
||||
},
|
||||
])('should validate $name', ({ request, parsedResult }) => {
|
||||
const result = UsersListFilterDto.safeParse(request);
|
||||
expect(result.success).toBe(true);
|
||||
if (parsedResult) {
|
||||
expect(result.data).toMatchObject(parsedResult);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('Invalid requests', () => {
|
||||
test.each([
|
||||
{
|
||||
name: 'invalid skip format',
|
||||
request: {
|
||||
skip: 'not-a-number',
|
||||
take: '10',
|
||||
},
|
||||
expectedErrorPath: ['skip'],
|
||||
},
|
||||
{
|
||||
name: 'invalid take format',
|
||||
request: {
|
||||
skip: '0',
|
||||
take: 'not-a-number',
|
||||
},
|
||||
expectedErrorPath: ['take'],
|
||||
},
|
||||
{
|
||||
name: 'invalid sortBy value',
|
||||
request: {
|
||||
sortBy: 'invalid-value',
|
||||
},
|
||||
expectedErrorPath: ['sortBy'],
|
||||
},
|
||||
])('should invalidate $name', ({ request, expectedErrorPath }) => {
|
||||
const result = UsersListFilterDto.safeParse(request);
|
||||
|
||||
expect(result.success).toBe(false);
|
||||
|
||||
if (expectedErrorPath && !result.success) {
|
||||
if (Array.isArray(expectedErrorPath)) {
|
||||
const errorPaths = result.error.issues[0].path;
|
||||
expect(errorPaths).toContain(expectedErrorPath[0]);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,25 @@
|
||||
import { z } from 'zod';
|
||||
import { Z } from 'zod-class';
|
||||
|
||||
import { paginationSchema } from '../pagination/pagination.dto';
|
||||
|
||||
const USERS_LIST_SORT_OPTIONS = [
|
||||
'name:asc',
|
||||
'name:desc',
|
||||
'role:asc', // ascending order by role is Owner, Admin, Member
|
||||
'role:desc',
|
||||
'lastActive:asc',
|
||||
'lastActive:desc',
|
||||
] as const;
|
||||
|
||||
const usersListSortByValidator = z
|
||||
.enum(USERS_LIST_SORT_OPTIONS, {
|
||||
message: `sortBy must be one of: ${USERS_LIST_SORT_OPTIONS.join(', ')}`,
|
||||
})
|
||||
.optional();
|
||||
|
||||
export class UsersListFilterDto extends Z.class({
|
||||
...paginationSchema,
|
||||
// Default sort order is role:asc, secondary sort criteria is name:asc
|
||||
sortBy: usersListSortByValidator,
|
||||
}) {}
|
||||
@@ -38,3 +38,9 @@ export {
|
||||
type InsightsDateRange,
|
||||
INSIGHTS_DATE_RANGE_KEYS,
|
||||
} from './schemas/insights.schema';
|
||||
|
||||
export {
|
||||
ROLE,
|
||||
type Role,
|
||||
type UsersList,
|
||||
} from './schemas/user.schema';
|
||||
|
||||
@@ -0,0 +1,170 @@
|
||||
import { roleSchema, userListItemSchema, usersListSchema } from '../user.schema';
|
||||
|
||||
describe('user.schema', () => {
|
||||
describe('roleSchema', () => {
|
||||
test.each([
|
||||
['global:owner', true],
|
||||
['global:member', true],
|
||||
['global:admin', true],
|
||||
['default', true],
|
||||
['invalid-role', false],
|
||||
])('should validate role %s', (value, expected) => {
|
||||
const result = roleSchema.safeParse(value);
|
||||
expect(result.success).toBe(expected);
|
||||
});
|
||||
});
|
||||
|
||||
describe('userListItemSchema', () => {
|
||||
test.each([
|
||||
{
|
||||
name: 'valid user',
|
||||
data: {
|
||||
id: '123',
|
||||
firstName: 'John',
|
||||
lastName: 'Doe',
|
||||
email: 'johndoe@example.com',
|
||||
role: 'global:member',
|
||||
isPending: false,
|
||||
lastActive: '2023-10-01T12:00:00Z',
|
||||
projects: ['project1', 'project2'],
|
||||
},
|
||||
isValid: true,
|
||||
},
|
||||
{
|
||||
name: 'user with null fields',
|
||||
data: {
|
||||
id: '123',
|
||||
firstName: null,
|
||||
lastName: null,
|
||||
email: null,
|
||||
role: 'global:member',
|
||||
isPending: false,
|
||||
lastActive: null,
|
||||
projects: null,
|
||||
},
|
||||
isValid: true,
|
||||
},
|
||||
{
|
||||
name: 'invalid email',
|
||||
data: {
|
||||
id: '123',
|
||||
firstName: 'John',
|
||||
lastName: 'Doe',
|
||||
email: 'not-an-email',
|
||||
role: 'global:member',
|
||||
isPending: false,
|
||||
lastActive: '2023-10-01T12:00:00Z',
|
||||
projects: ['project1', 'project2'],
|
||||
},
|
||||
isValid: false,
|
||||
},
|
||||
{
|
||||
name: 'missing required fields',
|
||||
data: {
|
||||
firstName: 'John',
|
||||
lastName: 'Doe',
|
||||
email: null,
|
||||
role: 'global:member',
|
||||
isPending: false,
|
||||
lastActive: '2023-10-01T12:00:00Z',
|
||||
},
|
||||
isValid: false,
|
||||
},
|
||||
{
|
||||
name: 'invalid role',
|
||||
data: {
|
||||
id: '123',
|
||||
firstName: 'John',
|
||||
lastName: 'Doe',
|
||||
email: 'johndoe@example.com',
|
||||
role: 'invalid-role',
|
||||
isPending: false,
|
||||
lastActive: '2023-10-01T12:00:00Z',
|
||||
projects: ['project1', 'project2'],
|
||||
},
|
||||
isValid: false,
|
||||
},
|
||||
])('should validate $name', ({ data, isValid }) => {
|
||||
const result = userListItemSchema.safeParse(data);
|
||||
expect(result.success).toBe(isValid);
|
||||
});
|
||||
});
|
||||
|
||||
describe('usersListSchema', () => {
|
||||
test.each([
|
||||
{
|
||||
name: 'valid users list',
|
||||
data: {
|
||||
count: 2,
|
||||
data: [
|
||||
{
|
||||
id: '123',
|
||||
firstName: 'John',
|
||||
lastName: 'Doe',
|
||||
email: 'johndoe@example.com',
|
||||
role: 'global:member',
|
||||
isPending: false,
|
||||
lastActive: '2023-10-01T12:00:00Z',
|
||||
projects: ['project1', 'project2'],
|
||||
},
|
||||
{
|
||||
id: '456',
|
||||
firstName: 'Jane',
|
||||
lastName: 'Doe',
|
||||
email: 'janedoe@example.com',
|
||||
role: 'global:admin',
|
||||
isPending: true,
|
||||
lastActive: '2023-10-02T12:00:00Z',
|
||||
projects: null,
|
||||
},
|
||||
],
|
||||
},
|
||||
isValid: true,
|
||||
},
|
||||
{
|
||||
name: 'empty users list',
|
||||
data: {
|
||||
count: 0,
|
||||
data: [],
|
||||
},
|
||||
isValid: true,
|
||||
},
|
||||
{
|
||||
name: 'missing count',
|
||||
data: {
|
||||
data: [],
|
||||
},
|
||||
isValid: false,
|
||||
},
|
||||
{
|
||||
name: 'missing data',
|
||||
data: {
|
||||
count: 5,
|
||||
},
|
||||
isValid: false,
|
||||
},
|
||||
{
|
||||
name: 'invalid user in list',
|
||||
data: {
|
||||
count: 1,
|
||||
data: [
|
||||
{
|
||||
id: '123',
|
||||
firstName: 'John',
|
||||
lastName: 'Doe',
|
||||
email: 'email',
|
||||
role: 'global:member',
|
||||
isPending: false,
|
||||
lastActive: '2023-10-01T12:00:00Z',
|
||||
projects: ['project1', 'project2'],
|
||||
},
|
||||
],
|
||||
},
|
||||
isValid: false,
|
||||
},
|
||||
])('should validate $name', ({ data, isValid }) => {
|
||||
const result = usersListSchema.safeParse(data);
|
||||
expect(result.success).toBe(isValid);
|
||||
});
|
||||
});
|
||||
});
|
||||
32
packages/@n8n/api-types/src/schemas/user.schema.ts
Normal file
32
packages/@n8n/api-types/src/schemas/user.schema.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
export const ROLE = {
|
||||
Owner: 'global:owner',
|
||||
Member: 'global:member',
|
||||
Admin: 'global:admin',
|
||||
Default: 'default', // default user with no email when setting up instance
|
||||
} as const;
|
||||
|
||||
export type Role = (typeof ROLE)[keyof typeof ROLE];
|
||||
|
||||
// Ensuring the array passed to z.enum is correctly typed as non-empty.
|
||||
const roleValuesForSchema = Object.values(ROLE) as [Role, ...Role[]];
|
||||
export const roleSchema = z.enum(roleValuesForSchema);
|
||||
|
||||
export const userListItemSchema = z.object({
|
||||
id: z.string(),
|
||||
firstName: z.string().nullable(),
|
||||
lastName: z.string().nullable(),
|
||||
email: z.string().email().nullable(),
|
||||
role: roleSchema,
|
||||
isPending: z.boolean(),
|
||||
lastActive: z.string().nullable(),
|
||||
projects: z.array(z.string()).nullable(), // Can be null if the user is the owner or is an admin
|
||||
});
|
||||
|
||||
export const usersListSchema = z.object({
|
||||
count: z.number(),
|
||||
data: z.array(userListItemSchema),
|
||||
});
|
||||
|
||||
export type UsersList = z.infer<typeof usersListSchema>;
|
||||
@@ -6,6 +6,8 @@ import type {
|
||||
Iso8601DateTimeString,
|
||||
IUserManagementSettings,
|
||||
IVersionNotificationSettings,
|
||||
ROLE,
|
||||
Role,
|
||||
} from '@n8n/api-types';
|
||||
import type { Scope } from '@n8n/permissions';
|
||||
import type { NodeCreatorTag } from '@n8n/design-system';
|
||||
@@ -55,7 +57,6 @@ import type {
|
||||
TRIGGER_NODE_CREATOR_VIEW,
|
||||
REGULAR_NODE_CREATOR_VIEW,
|
||||
AI_OTHERS_NODE_CREATOR_VIEW,
|
||||
ROLE,
|
||||
AI_UNCATEGORIZED_CATEGORY,
|
||||
AI_EVALUATION,
|
||||
} from '@/constants';
|
||||
@@ -568,9 +569,7 @@ export type IPersonalizationSurveyVersions =
|
||||
| IPersonalizationSurveyAnswersV3
|
||||
| IPersonalizationSurveyAnswersV4;
|
||||
|
||||
export type Roles = typeof ROLE;
|
||||
export type IRole = Roles[keyof Roles];
|
||||
export type InvitableRoleName = Roles['Member' | 'Admin'];
|
||||
export type InvitableRoleName = (typeof ROLE)['Member' | 'Admin'];
|
||||
|
||||
export interface IUserResponse {
|
||||
id: string;
|
||||
@@ -578,7 +577,7 @@ export interface IUserResponse {
|
||||
lastName?: string;
|
||||
email?: string;
|
||||
createdAt?: string;
|
||||
role?: IRole;
|
||||
role?: Role;
|
||||
globalScopes?: Scope[];
|
||||
personalizationAnswers?: IPersonalizationSurveyVersions | null;
|
||||
isPending: boolean;
|
||||
@@ -613,7 +612,7 @@ export const enum UserManagementAuthenticationMethod {
|
||||
|
||||
export interface IPermissionGroup {
|
||||
loginStatus?: ILogInStatus[];
|
||||
role?: IRole[];
|
||||
role?: Role[];
|
||||
}
|
||||
|
||||
export interface IPermissionAllowGroup extends IPermissionGroup {
|
||||
@@ -1168,7 +1167,7 @@ export interface IInviteResponse {
|
||||
email: string;
|
||||
emailSent: boolean;
|
||||
inviteAcceptUrl: string;
|
||||
role: IRole;
|
||||
role: Role;
|
||||
};
|
||||
error?: string;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import merge from 'lodash/merge';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
|
||||
import { SETTINGS_STORE_DEFAULT_STATE } from '@/__tests__/utils';
|
||||
import { ROLE } from '@/constants';
|
||||
import { ROLE } from '@n8n/api-types';
|
||||
import { STORES } from '@n8n/stores';
|
||||
|
||||
import { createTestingPinia } from '@pinia/testing';
|
||||
|
||||
@@ -3,12 +3,8 @@ import { computed, onMounted, ref } from 'vue';
|
||||
import { useToast } from '@/composables/useToast';
|
||||
import Modal from './Modal.vue';
|
||||
import type { IFormInputs, IInviteResponse, IUser, InvitableRoleName } from '@/Interface';
|
||||
import {
|
||||
EnterpriseEditionFeature,
|
||||
VALID_EMAIL_REGEX,
|
||||
INVITE_USER_MODAL_KEY,
|
||||
ROLE,
|
||||
} from '@/constants';
|
||||
import { EnterpriseEditionFeature, VALID_EMAIL_REGEX, INVITE_USER_MODAL_KEY } from '@/constants';
|
||||
import { ROLE } from '@n8n/api-types';
|
||||
import { useUsersStore } from '@/stores/users.store';
|
||||
import { useSettingsStore } from '@/stores/settings.store';
|
||||
import { createFormEventBus } from '@n8n/design-system/utils';
|
||||
|
||||
@@ -2,7 +2,7 @@ import { createComponentRenderer } from '@/__tests__/render';
|
||||
import V1Banner from './V1Banner.vue';
|
||||
import { createPinia, setActivePinia } from 'pinia';
|
||||
import { useUsersStore } from '@/stores/users.store';
|
||||
import { ROLE } from '@/constants';
|
||||
import { ROLE } from '@n8n/api-types';
|
||||
import type { IUser } from '@/Interface';
|
||||
|
||||
const renderComponent = createComponentRenderer(V1Banner);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ROLE } from '@/constants';
|
||||
import { ROLE } from '@n8n/api-types';
|
||||
import { useSettingsStore } from '@/stores/settings.store';
|
||||
import merge from 'lodash/merge';
|
||||
import { usePageRedirectionHelper } from './usePageRedirectionHelper';
|
||||
|
||||
@@ -879,13 +879,6 @@ export const TEMPLATES_URLS = {
|
||||
},
|
||||
};
|
||||
|
||||
export const ROLE = {
|
||||
Owner: 'global:owner',
|
||||
Member: 'global:member',
|
||||
Admin: 'global:admin',
|
||||
Default: 'default', // default user with no email when setting up instance
|
||||
} as const;
|
||||
|
||||
export const INSECURE_CONNECTION_WARNING = `
|
||||
<body style="margin-top: 20px; font-family: 'Open Sans', sans-serif; text-align: center;">
|
||||
<h1 style="font-size: 40px">🚫</h1>
|
||||
|
||||
@@ -3,10 +3,10 @@ import { hasScope as genericHasScope } from '@n8n/permissions';
|
||||
import type { ScopeOptions, Scope, Resource } from '@n8n/permissions';
|
||||
import { ref } from 'vue';
|
||||
import { STORES } from '@n8n/stores';
|
||||
import type { IRole } from '@/Interface';
|
||||
import type { Role } from '@n8n/api-types';
|
||||
|
||||
export const useRBACStore = defineStore(STORES.RBAC, () => {
|
||||
const globalRoles = ref<IRole[]>([]);
|
||||
const globalRoles = ref<Role[]>([]);
|
||||
const rolesByProjectId = ref<Record<string, string[]>>({});
|
||||
|
||||
const globalScopes = ref<Scope[]>([]);
|
||||
@@ -38,13 +38,13 @@ export const useRBACStore = defineStore(STORES.RBAC, () => {
|
||||
insights: {},
|
||||
});
|
||||
|
||||
function addGlobalRole(role: IRole) {
|
||||
function addGlobalRole(role: Role) {
|
||||
if (!globalRoles.value.includes(role)) {
|
||||
globalRoles.value.push(role);
|
||||
}
|
||||
}
|
||||
|
||||
function hasRole(role: IRole) {
|
||||
function hasRole(role: Role) {
|
||||
return globalRoles.value.includes(role);
|
||||
}
|
||||
|
||||
|
||||
@@ -12,14 +12,13 @@ import {
|
||||
getUserCloudInfo,
|
||||
getNotTrialingUserResponse,
|
||||
} from './__tests__/utils/cloudStoreUtils';
|
||||
import type { IRole } from '@/Interface';
|
||||
import { ROLE } from '@/constants';
|
||||
import { ROLE, type Role } from '@n8n/api-types';
|
||||
|
||||
let uiStore: ReturnType<typeof useUIStore>;
|
||||
let settingsStore: ReturnType<typeof useSettingsStore>;
|
||||
let cloudPlanStore: ReturnType<typeof useCloudPlanStore>;
|
||||
|
||||
function setUser(role: IRole) {
|
||||
function setUser(role: Role) {
|
||||
useUsersStore().addUsers([
|
||||
{
|
||||
id: '1',
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import type {
|
||||
LoginRequestDto,
|
||||
PasswordUpdateRequestDto,
|
||||
SettingsUpdateRequestDto,
|
||||
UserUpdateRequestDto,
|
||||
import {
|
||||
type LoginRequestDto,
|
||||
type PasswordUpdateRequestDto,
|
||||
type SettingsUpdateRequestDto,
|
||||
type UserUpdateRequestDto,
|
||||
ROLE,
|
||||
} from '@n8n/api-types';
|
||||
import type { UpdateGlobalRolePayload } from '@/api/users';
|
||||
import * as usersApi from '@/api/users';
|
||||
import { BROWSER_ID_STORAGE_KEY } from '@n8n/constants';
|
||||
import { PERSONALIZATION_MODAL_KEY, ROLE } from '@/constants';
|
||||
import { PERSONALIZATION_MODAL_KEY } from '@/constants';
|
||||
import { STORES } from '@n8n/stores';
|
||||
import type {
|
||||
IPersonalizationLatestVersion,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { Resource, ScopeOptions, Scope } from '@n8n/permissions';
|
||||
import type { EnterpriseEditionFeatureValue, IRole } from '@/Interface';
|
||||
import type { EnterpriseEditionFeatureValue } from '@/Interface';
|
||||
import type { Role } from '@n8n/api-types';
|
||||
|
||||
export type AuthenticatedPermissionOptions = {
|
||||
bypass?: () => boolean;
|
||||
@@ -19,7 +20,7 @@ export type RBACPermissionOptions = {
|
||||
resourceId?: string;
|
||||
options?: ScopeOptions;
|
||||
};
|
||||
export type RolePermissionOptions = IRole[];
|
||||
export type RolePermissionOptions = Role[];
|
||||
|
||||
export type PermissionType =
|
||||
| 'authenticated'
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useUsersStore } from '@/stores/users.store';
|
||||
import { hasRole } from '@/utils/rbac/checks';
|
||||
import { ROLE } from '@/constants';
|
||||
import { ROLE } from '@n8n/api-types';
|
||||
|
||||
vi.mock('@/stores/users.store', () => ({
|
||||
useUsersStore: vi.fn(),
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { useUsersStore } from '@/stores/users.store';
|
||||
import type { RBACPermissionCheck, RolePermissionOptions } from '@/types/rbac';
|
||||
import { ROLE } from '@/constants';
|
||||
import type { IRole } from '@/Interface';
|
||||
import { ROLE, type Role } from '@n8n/api-types';
|
||||
|
||||
export const hasRole: RBACPermissionCheck<RolePermissionOptions> = (checkRoles) => {
|
||||
const usersStore = useUsersStore();
|
||||
@@ -9,7 +8,7 @@ export const hasRole: RBACPermissionCheck<RolePermissionOptions> = (checkRoles)
|
||||
|
||||
if (currentUser && checkRoles) {
|
||||
const userRole = currentUser.isDefaultUser ? ROLE.Default : currentUser.role;
|
||||
return checkRoles.includes(userRole as IRole);
|
||||
return checkRoles.includes(userRole as Role);
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
@@ -2,7 +2,8 @@ import { roleMiddleware } from '@/utils/rbac/middleware/role';
|
||||
import { useUsersStore } from '@/stores/users.store';
|
||||
import type { IUser } from '@/Interface';
|
||||
import type { RouteLocationNormalized } from 'vue-router';
|
||||
import { VIEWS, ROLE } from '@/constants';
|
||||
import { ROLE } from '@n8n/api-types';
|
||||
import { VIEWS } from '@/constants';
|
||||
|
||||
vi.mock('@/stores/users.store', () => ({
|
||||
useUsersStore: vi.fn(),
|
||||
|
||||
@@ -59,8 +59,8 @@ import {
|
||||
BAMBOO_HR_NODE_TYPE,
|
||||
GOOGLE_SHEETS_NODE_TYPE,
|
||||
CODE_NODE_TYPE,
|
||||
ROLE,
|
||||
} from '@/constants';
|
||||
import { ROLE } from '@n8n/api-types';
|
||||
import type {
|
||||
IPersonalizationSurveyAnswersV1,
|
||||
IPersonalizationSurveyAnswersV2,
|
||||
|
||||
@@ -6,7 +6,7 @@ import { useSettingsStore } from '@/stores/settings.store';
|
||||
import { useUsersStore } from '@/stores/users.store';
|
||||
import { createComponentRenderer } from '@/__tests__/render';
|
||||
import { setupServer } from '@/__tests__/server';
|
||||
import { ROLE } from '@/constants';
|
||||
import { ROLE } from '@n8n/api-types';
|
||||
import { useUIStore } from '@/stores/ui.store';
|
||||
import { useCloudPlanStore } from '@/stores/cloudPlan.store';
|
||||
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed, onMounted, onBeforeUnmount } from 'vue';
|
||||
import { ROLE, type Role } from '@n8n/api-types';
|
||||
import { useI18n } from '@n8n/i18n';
|
||||
import { useToast } from '@/composables/useToast';
|
||||
import { useDocumentTitle } from '@/composables/useDocumentTitle';
|
||||
import type { IFormInputs, IRole, IUser, ThemeOption } from '@/Interface';
|
||||
import type { IFormInputs, IUser, ThemeOption } from '@/Interface';
|
||||
import {
|
||||
CHANGE_PASSWORD_MODAL_KEY,
|
||||
MFA_DOCS_URL,
|
||||
MFA_SETUP_MODAL_KEY,
|
||||
PROMPT_MFA_CODE_MODAL_KEY,
|
||||
ROLE,
|
||||
} from '@/constants';
|
||||
import { useUIStore } from '@/stores/ui.store';
|
||||
import { useUsersStore } from '@/stores/users.store';
|
||||
@@ -90,7 +90,7 @@ const hasAnyChanges = computed(() => {
|
||||
return hasAnyBasicInfoChanges.value || hasAnyPersonalisationChanges.value;
|
||||
});
|
||||
|
||||
const roles = computed<Record<IRole, RoleContent>>(() => ({
|
||||
const roles = computed<Record<Role, RoleContent>>(() => ({
|
||||
[ROLE.Default]: {
|
||||
name: i18n.baseText('auth.roles.default'),
|
||||
description: i18n.baseText('settings.personal.role.tooltip.default'),
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script lang="ts" setup>
|
||||
import { EnterpriseEditionFeature, INVITE_USER_MODAL_KEY, ROLE } from '@/constants';
|
||||
|
||||
import type { IRole, IUser, IUserListAction, InvitableRoleName } from '@/Interface';
|
||||
import { ROLE, type Role } from '@n8n/api-types';
|
||||
import { EnterpriseEditionFeature, INVITE_USER_MODAL_KEY } from '@/constants';
|
||||
import type { IUser, IUserListAction, InvitableRoleName } from '@/Interface';
|
||||
import { useToast } from '@/composables/useToast';
|
||||
import { useUIStore } from '@/stores/ui.store';
|
||||
import { useSettingsStore } from '@/stores/settings.store';
|
||||
@@ -85,7 +85,7 @@ const isAdvancedPermissionsEnabled = computed((): boolean => {
|
||||
return settingsStore.isEnterpriseFeatureEnabled[EnterpriseEditionFeature.AdvancedPermissions];
|
||||
});
|
||||
|
||||
const userRoles = computed((): Array<{ value: IRole; label: string; disabled?: boolean }> => {
|
||||
const userRoles = computed((): Array<{ value: Role; label: string; disabled?: boolean }> => {
|
||||
return [
|
||||
{
|
||||
value: ROLE.Member,
|
||||
|
||||
Reference in New Issue
Block a user