Add default role to workspace (#10444)
## Context Adding a defaultRole to each workspace, this role will be automatically added when a member joins a workspace via invite link or public link (seeds work differently though). Took the occasion to refactor a bit the frontend components, splitting them in smaller components for more readability. ## Test <img width="948" alt="Screenshot 2025-02-24 at 14 54 02" src="https://github.com/user-attachments/assets/13ef1452-d3c9-4385-940c-2ced0f0b05ef" />
This commit is contained in:
@ -2045,6 +2045,7 @@ export type UpdateWorkflowVersionStepInput = {
|
|||||||
export type UpdateWorkspaceInput = {
|
export type UpdateWorkspaceInput = {
|
||||||
allowImpersonation?: InputMaybe<Scalars['Boolean']['input']>;
|
allowImpersonation?: InputMaybe<Scalars['Boolean']['input']>;
|
||||||
customDomain?: InputMaybe<Scalars['String']['input']>;
|
customDomain?: InputMaybe<Scalars['String']['input']>;
|
||||||
|
defaultRoleId?: InputMaybe<Scalars['String']['input']>;
|
||||||
displayName?: InputMaybe<Scalars['String']['input']>;
|
displayName?: InputMaybe<Scalars['String']['input']>;
|
||||||
inviteHash?: InputMaybe<Scalars['String']['input']>;
|
inviteHash?: InputMaybe<Scalars['String']['input']>;
|
||||||
isGoogleAuthEnabled?: InputMaybe<Scalars['Boolean']['input']>;
|
isGoogleAuthEnabled?: InputMaybe<Scalars['Boolean']['input']>;
|
||||||
@ -2197,6 +2198,7 @@ export type Workspace = {
|
|||||||
customDomain?: Maybe<Scalars['String']['output']>;
|
customDomain?: Maybe<Scalars['String']['output']>;
|
||||||
databaseSchema: Scalars['String']['output'];
|
databaseSchema: Scalars['String']['output'];
|
||||||
databaseUrl: Scalars['String']['output'];
|
databaseUrl: Scalars['String']['output'];
|
||||||
|
defaultRole?: Maybe<Role>;
|
||||||
deletedAt?: Maybe<Scalars['DateTime']['output']>;
|
deletedAt?: Maybe<Scalars['DateTime']['output']>;
|
||||||
displayName?: Maybe<Scalars['String']['output']>;
|
displayName?: Maybe<Scalars['String']['output']>;
|
||||||
featureFlags?: Maybe<Array<FeatureFlag>>;
|
featureFlags?: Maybe<Array<FeatureFlag>>;
|
||||||
|
|||||||
@ -1833,6 +1833,7 @@ export type UpdateWorkflowVersionStepInput = {
|
|||||||
export type UpdateWorkspaceInput = {
|
export type UpdateWorkspaceInput = {
|
||||||
allowImpersonation?: InputMaybe<Scalars['Boolean']>;
|
allowImpersonation?: InputMaybe<Scalars['Boolean']>;
|
||||||
customDomain?: InputMaybe<Scalars['String']>;
|
customDomain?: InputMaybe<Scalars['String']>;
|
||||||
|
defaultRoleId?: InputMaybe<Scalars['String']>;
|
||||||
displayName?: InputMaybe<Scalars['String']>;
|
displayName?: InputMaybe<Scalars['String']>;
|
||||||
inviteHash?: InputMaybe<Scalars['String']>;
|
inviteHash?: InputMaybe<Scalars['String']>;
|
||||||
isGoogleAuthEnabled?: InputMaybe<Scalars['Boolean']>;
|
isGoogleAuthEnabled?: InputMaybe<Scalars['Boolean']>;
|
||||||
@ -1975,6 +1976,7 @@ export type Workspace = {
|
|||||||
customDomain?: Maybe<Scalars['String']>;
|
customDomain?: Maybe<Scalars['String']>;
|
||||||
databaseSchema: Scalars['String'];
|
databaseSchema: Scalars['String'];
|
||||||
databaseUrl: Scalars['String'];
|
databaseUrl: Scalars['String'];
|
||||||
|
defaultRole?: Maybe<Role>;
|
||||||
deletedAt?: Maybe<Scalars['DateTime']>;
|
deletedAt?: Maybe<Scalars['DateTime']>;
|
||||||
displayName?: Maybe<Scalars['String']>;
|
displayName?: Maybe<Scalars['String']>;
|
||||||
featureFlags?: Maybe<Array<FeatureFlag>>;
|
featureFlags?: Maybe<Array<FeatureFlag>>;
|
||||||
@ -2436,7 +2438,7 @@ export type GetSsoIdentityProvidersQueryVariables = Exact<{ [key: string]: never
|
|||||||
|
|
||||||
export type GetSsoIdentityProvidersQuery = { __typename?: 'Query', getSSOIdentityProviders: Array<{ __typename?: 'FindAvailableSSOIDPOutput', type: IdentityProviderType, id: string, name: string, issuer: string, status: SsoIdentityProviderStatus }> };
|
export type GetSsoIdentityProvidersQuery = { __typename?: 'Query', getSSOIdentityProviders: Array<{ __typename?: 'FindAvailableSSOIDPOutput', type: IdentityProviderType, id: string, name: string, issuer: string, status: SsoIdentityProviderStatus }> };
|
||||||
|
|
||||||
export type UserQueryFragmentFragment = { __typename?: 'User', id: any, firstName: string, lastName: string, email: string, canAccessFullAdminPanel: boolean, canImpersonate: boolean, supportUserHash?: string | null, onboardingStatus?: OnboardingStatus | null, userVars: any, analyticsTinybirdJwts?: { __typename?: 'AnalyticsTinybirdJwtMap', getWebhookAnalytics: string, getPageviewsAnalytics: string, getUsersAnalytics: string, getServerlessFunctionDuration: string, getServerlessFunctionSuccessRate: string, getServerlessFunctionErrorCount: string } | null, workspaceMember?: { __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale?: string | null, userEmail: string, timeZone?: string | null, dateFormat?: WorkspaceMemberDateFormatEnum | null, timeFormat?: WorkspaceMemberTimeFormatEnum | null, name: { __typename?: 'FullName', firstName: string, lastName: string } } | null, workspaceMembers?: Array<{ __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale?: string | null, userEmail: string, timeZone?: string | null, dateFormat?: WorkspaceMemberDateFormatEnum | null, timeFormat?: WorkspaceMemberTimeFormatEnum | null, name: { __typename?: 'FullName', firstName: string, lastName: string } }> | null, currentUserWorkspace?: { __typename?: 'UserWorkspace', settingsPermissions?: Array<SettingsPermissions> | null, objectRecordsPermissions?: Array<PermissionsOnAllObjectRecords> | null } | null, currentWorkspace?: { __typename?: 'Workspace', id: any, displayName?: string | null, logo?: string | null, inviteHash?: string | null, allowImpersonation: boolean, activationStatus: WorkspaceActivationStatus, isPublicInviteLinkEnabled: boolean, isGoogleAuthEnabled: boolean, isMicrosoftAuthEnabled: boolean, isPasswordAuthEnabled: boolean, subdomain: string, hasValidEnterpriseKey: boolean, customDomain?: string | null, metadataVersion: number, workspaceMembersCount?: number | null, workspaceUrls: { __typename?: 'workspaceUrls', subdomainUrl: string, customUrl?: string | null }, featureFlags?: Array<{ __typename?: 'FeatureFlag', id: any, key: FeatureFlagKey, value: boolean, workspaceId: string }> | null, currentBillingSubscription?: { __typename?: 'BillingSubscription', id: any, status: SubscriptionStatus, interval?: SubscriptionInterval | null } | null, billingSubscriptions: Array<{ __typename?: 'BillingSubscription', id: any, status: SubscriptionStatus }> } | null, workspaces: Array<{ __typename?: 'UserWorkspace', workspace?: { __typename?: 'Workspace', id: any, logo?: string | null, displayName?: string | null, subdomain: string, customDomain?: string | null, workspaceUrls: { __typename?: 'workspaceUrls', subdomainUrl: string, customUrl?: string | null } } | null }> };
|
export type UserQueryFragmentFragment = { __typename?: 'User', id: any, firstName: string, lastName: string, email: string, canAccessFullAdminPanel: boolean, canImpersonate: boolean, supportUserHash?: string | null, onboardingStatus?: OnboardingStatus | null, userVars: any, analyticsTinybirdJwts?: { __typename?: 'AnalyticsTinybirdJwtMap', getWebhookAnalytics: string, getPageviewsAnalytics: string, getUsersAnalytics: string, getServerlessFunctionDuration: string, getServerlessFunctionSuccessRate: string, getServerlessFunctionErrorCount: string } | null, workspaceMember?: { __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale?: string | null, userEmail: string, timeZone?: string | null, dateFormat?: WorkspaceMemberDateFormatEnum | null, timeFormat?: WorkspaceMemberTimeFormatEnum | null, name: { __typename?: 'FullName', firstName: string, lastName: string } } | null, workspaceMembers?: Array<{ __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale?: string | null, userEmail: string, timeZone?: string | null, dateFormat?: WorkspaceMemberDateFormatEnum | null, timeFormat?: WorkspaceMemberTimeFormatEnum | null, name: { __typename?: 'FullName', firstName: string, lastName: string } }> | null, currentUserWorkspace?: { __typename?: 'UserWorkspace', settingsPermissions?: Array<SettingsPermissions> | null, objectRecordsPermissions?: Array<PermissionsOnAllObjectRecords> | null } | null, currentWorkspace?: { __typename?: 'Workspace', id: any, displayName?: string | null, logo?: string | null, inviteHash?: string | null, allowImpersonation: boolean, activationStatus: WorkspaceActivationStatus, isPublicInviteLinkEnabled: boolean, isGoogleAuthEnabled: boolean, isMicrosoftAuthEnabled: boolean, isPasswordAuthEnabled: boolean, subdomain: string, hasValidEnterpriseKey: boolean, customDomain?: string | null, metadataVersion: number, workspaceMembersCount?: number | null, workspaceUrls: { __typename?: 'workspaceUrls', subdomainUrl: string, customUrl?: string | null }, featureFlags?: Array<{ __typename?: 'FeatureFlag', id: any, key: FeatureFlagKey, value: boolean, workspaceId: string }> | null, currentBillingSubscription?: { __typename?: 'BillingSubscription', id: any, status: SubscriptionStatus, interval?: SubscriptionInterval | null } | null, billingSubscriptions: Array<{ __typename?: 'BillingSubscription', id: any, status: SubscriptionStatus }>, defaultRole?: { __typename?: 'Role', id: string, label: string, description?: string | null, canUpdateAllSettings: boolean, isEditable: boolean, canReadAllObjectRecords: boolean, canUpdateAllObjectRecords: boolean, canSoftDeleteAllObjectRecords: boolean, canDestroyAllObjectRecords: boolean } | null } | null, workspaces: Array<{ __typename?: 'UserWorkspace', workspace?: { __typename?: 'Workspace', id: any, logo?: string | null, displayName?: string | null, subdomain: string, customDomain?: string | null, workspaceUrls: { __typename?: 'workspaceUrls', subdomainUrl: string, customUrl?: string | null } } | null }> };
|
||||||
|
|
||||||
export type DeleteUserAccountMutationVariables = Exact<{ [key: string]: never; }>;
|
export type DeleteUserAccountMutationVariables = Exact<{ [key: string]: never; }>;
|
||||||
|
|
||||||
@ -2453,7 +2455,7 @@ export type UploadProfilePictureMutation = { __typename?: 'Mutation', uploadProf
|
|||||||
export type GetCurrentUserQueryVariables = Exact<{ [key: string]: never; }>;
|
export type GetCurrentUserQueryVariables = Exact<{ [key: string]: never; }>;
|
||||||
|
|
||||||
|
|
||||||
export type GetCurrentUserQuery = { __typename?: 'Query', currentUser: { __typename?: 'User', id: any, firstName: string, lastName: string, email: string, canAccessFullAdminPanel: boolean, canImpersonate: boolean, supportUserHash?: string | null, onboardingStatus?: OnboardingStatus | null, userVars: any, analyticsTinybirdJwts?: { __typename?: 'AnalyticsTinybirdJwtMap', getWebhookAnalytics: string, getPageviewsAnalytics: string, getUsersAnalytics: string, getServerlessFunctionDuration: string, getServerlessFunctionSuccessRate: string, getServerlessFunctionErrorCount: string } | null, workspaceMember?: { __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale?: string | null, userEmail: string, timeZone?: string | null, dateFormat?: WorkspaceMemberDateFormatEnum | null, timeFormat?: WorkspaceMemberTimeFormatEnum | null, name: { __typename?: 'FullName', firstName: string, lastName: string } } | null, workspaceMembers?: Array<{ __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale?: string | null, userEmail: string, timeZone?: string | null, dateFormat?: WorkspaceMemberDateFormatEnum | null, timeFormat?: WorkspaceMemberTimeFormatEnum | null, name: { __typename?: 'FullName', firstName: string, lastName: string } }> | null, currentUserWorkspace?: { __typename?: 'UserWorkspace', settingsPermissions?: Array<SettingsPermissions> | null, objectRecordsPermissions?: Array<PermissionsOnAllObjectRecords> | null } | null, currentWorkspace?: { __typename?: 'Workspace', id: any, displayName?: string | null, logo?: string | null, inviteHash?: string | null, allowImpersonation: boolean, activationStatus: WorkspaceActivationStatus, isPublicInviteLinkEnabled: boolean, isGoogleAuthEnabled: boolean, isMicrosoftAuthEnabled: boolean, isPasswordAuthEnabled: boolean, subdomain: string, hasValidEnterpriseKey: boolean, customDomain?: string | null, metadataVersion: number, workspaceMembersCount?: number | null, workspaceUrls: { __typename?: 'workspaceUrls', subdomainUrl: string, customUrl?: string | null }, featureFlags?: Array<{ __typename?: 'FeatureFlag', id: any, key: FeatureFlagKey, value: boolean, workspaceId: string }> | null, currentBillingSubscription?: { __typename?: 'BillingSubscription', id: any, status: SubscriptionStatus, interval?: SubscriptionInterval | null } | null, billingSubscriptions: Array<{ __typename?: 'BillingSubscription', id: any, status: SubscriptionStatus }> } | null, workspaces: Array<{ __typename?: 'UserWorkspace', workspace?: { __typename?: 'Workspace', id: any, logo?: string | null, displayName?: string | null, subdomain: string, customDomain?: string | null, workspaceUrls: { __typename?: 'workspaceUrls', subdomainUrl: string, customUrl?: string | null } } | null }> } };
|
export type GetCurrentUserQuery = { __typename?: 'Query', currentUser: { __typename?: 'User', id: any, firstName: string, lastName: string, email: string, canAccessFullAdminPanel: boolean, canImpersonate: boolean, supportUserHash?: string | null, onboardingStatus?: OnboardingStatus | null, userVars: any, analyticsTinybirdJwts?: { __typename?: 'AnalyticsTinybirdJwtMap', getWebhookAnalytics: string, getPageviewsAnalytics: string, getUsersAnalytics: string, getServerlessFunctionDuration: string, getServerlessFunctionSuccessRate: string, getServerlessFunctionErrorCount: string } | null, workspaceMember?: { __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale?: string | null, userEmail: string, timeZone?: string | null, dateFormat?: WorkspaceMemberDateFormatEnum | null, timeFormat?: WorkspaceMemberTimeFormatEnum | null, name: { __typename?: 'FullName', firstName: string, lastName: string } } | null, workspaceMembers?: Array<{ __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale?: string | null, userEmail: string, timeZone?: string | null, dateFormat?: WorkspaceMemberDateFormatEnum | null, timeFormat?: WorkspaceMemberTimeFormatEnum | null, name: { __typename?: 'FullName', firstName: string, lastName: string } }> | null, currentUserWorkspace?: { __typename?: 'UserWorkspace', settingsPermissions?: Array<SettingsPermissions> | null, objectRecordsPermissions?: Array<PermissionsOnAllObjectRecords> | null } | null, currentWorkspace?: { __typename?: 'Workspace', id: any, displayName?: string | null, logo?: string | null, inviteHash?: string | null, allowImpersonation: boolean, activationStatus: WorkspaceActivationStatus, isPublicInviteLinkEnabled: boolean, isGoogleAuthEnabled: boolean, isMicrosoftAuthEnabled: boolean, isPasswordAuthEnabled: boolean, subdomain: string, hasValidEnterpriseKey: boolean, customDomain?: string | null, metadataVersion: number, workspaceMembersCount?: number | null, workspaceUrls: { __typename?: 'workspaceUrls', subdomainUrl: string, customUrl?: string | null }, featureFlags?: Array<{ __typename?: 'FeatureFlag', id: any, key: FeatureFlagKey, value: boolean, workspaceId: string }> | null, currentBillingSubscription?: { __typename?: 'BillingSubscription', id: any, status: SubscriptionStatus, interval?: SubscriptionInterval | null } | null, billingSubscriptions: Array<{ __typename?: 'BillingSubscription', id: any, status: SubscriptionStatus }>, defaultRole?: { __typename?: 'Role', id: string, label: string, description?: string | null, canUpdateAllSettings: boolean, isEditable: boolean, canReadAllObjectRecords: boolean, canUpdateAllObjectRecords: boolean, canSoftDeleteAllObjectRecords: boolean, canDestroyAllObjectRecords: boolean } | null } | null, workspaces: Array<{ __typename?: 'UserWorkspace', workspace?: { __typename?: 'Workspace', id: any, logo?: string | null, displayName?: string | null, subdomain: string, customDomain?: string | null, workspaceUrls: { __typename?: 'workspaceUrls', subdomainUrl: string, customUrl?: string | null } } | null }> } };
|
||||||
|
|
||||||
export type ActivateWorkflowVersionMutationVariables = Exact<{
|
export type ActivateWorkflowVersionMutationVariables = Exact<{
|
||||||
workflowVersionId: Scalars['String'];
|
workflowVersionId: Scalars['String'];
|
||||||
@ -2556,7 +2558,7 @@ export type UpdateWorkspaceMutationVariables = Exact<{
|
|||||||
}>;
|
}>;
|
||||||
|
|
||||||
|
|
||||||
export type UpdateWorkspaceMutation = { __typename?: 'Mutation', updateWorkspace: { __typename?: 'Workspace', id: any, customDomain?: string | null, subdomain: string, displayName?: string | null, logo?: string | null, allowImpersonation: boolean, isPublicInviteLinkEnabled: boolean, isGoogleAuthEnabled: boolean, isMicrosoftAuthEnabled: boolean, isPasswordAuthEnabled: boolean } };
|
export type UpdateWorkspaceMutation = { __typename?: 'Mutation', updateWorkspace: { __typename?: 'Workspace', id: any, customDomain?: string | null, subdomain: string, displayName?: string | null, logo?: string | null, allowImpersonation: boolean, isPublicInviteLinkEnabled: boolean, isGoogleAuthEnabled: boolean, isMicrosoftAuthEnabled: boolean, isPasswordAuthEnabled: boolean, defaultRole?: { __typename?: 'Role', id: string, label: string, description?: string | null, canUpdateAllSettings: boolean, isEditable: boolean, canReadAllObjectRecords: boolean, canUpdateAllObjectRecords: boolean, canSoftDeleteAllObjectRecords: boolean, canDestroyAllObjectRecords: boolean } | null } };
|
||||||
|
|
||||||
export type UploadWorkspaceLogoMutationVariables = Exact<{
|
export type UploadWorkspaceLogoMutationVariables = Exact<{
|
||||||
file: Scalars['Upload'];
|
file: Scalars['Upload'];
|
||||||
@ -2676,19 +2678,6 @@ export const AvailableSsoIdentityProvidersFragmentFragmentDoc = gql`
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
export const RoleFragmentFragmentDoc = gql`
|
|
||||||
fragment RoleFragment on Role {
|
|
||||||
id
|
|
||||||
label
|
|
||||||
description
|
|
||||||
canUpdateAllSettings
|
|
||||||
isEditable
|
|
||||||
canReadAllObjectRecords
|
|
||||||
canUpdateAllObjectRecords
|
|
||||||
canSoftDeleteAllObjectRecords
|
|
||||||
canDestroyAllObjectRecords
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
export const WorkspaceMemberQueryFragmentFragmentDoc = gql`
|
export const WorkspaceMemberQueryFragmentFragmentDoc = gql`
|
||||||
fragment WorkspaceMemberQueryFragment on WorkspaceMember {
|
fragment WorkspaceMemberQueryFragment on WorkspaceMember {
|
||||||
id
|
id
|
||||||
@ -2705,6 +2694,19 @@ export const WorkspaceMemberQueryFragmentFragmentDoc = gql`
|
|||||||
timeFormat
|
timeFormat
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
export const RoleFragmentFragmentDoc = gql`
|
||||||
|
fragment RoleFragment on Role {
|
||||||
|
id
|
||||||
|
label
|
||||||
|
description
|
||||||
|
canUpdateAllSettings
|
||||||
|
isEditable
|
||||||
|
canReadAllObjectRecords
|
||||||
|
canUpdateAllObjectRecords
|
||||||
|
canSoftDeleteAllObjectRecords
|
||||||
|
canDestroyAllObjectRecords
|
||||||
|
}
|
||||||
|
`;
|
||||||
export const UserQueryFragmentFragmentDoc = gql`
|
export const UserQueryFragmentFragmentDoc = gql`
|
||||||
fragment UserQueryFragment on User {
|
fragment UserQueryFragment on User {
|
||||||
id
|
id
|
||||||
@ -2768,6 +2770,9 @@ export const UserQueryFragmentFragmentDoc = gql`
|
|||||||
status
|
status
|
||||||
}
|
}
|
||||||
workspaceMembersCount
|
workspaceMembersCount
|
||||||
|
defaultRole {
|
||||||
|
...RoleFragment
|
||||||
|
}
|
||||||
}
|
}
|
||||||
workspaces {
|
workspaces {
|
||||||
workspace {
|
workspace {
|
||||||
@ -2784,7 +2789,8 @@ export const UserQueryFragmentFragmentDoc = gql`
|
|||||||
}
|
}
|
||||||
userVars
|
userVars
|
||||||
}
|
}
|
||||||
${WorkspaceMemberQueryFragmentFragmentDoc}`;
|
${WorkspaceMemberQueryFragmentFragmentDoc}
|
||||||
|
${RoleFragmentFragmentDoc}`;
|
||||||
export const GetTimelineCalendarEventsFromCompanyIdDocument = gql`
|
export const GetTimelineCalendarEventsFromCompanyIdDocument = gql`
|
||||||
query GetTimelineCalendarEventsFromCompanyId($companyId: UUID!, $page: Int!, $pageSize: Int!) {
|
query GetTimelineCalendarEventsFromCompanyId($companyId: UUID!, $page: Int!, $pageSize: Int!) {
|
||||||
getTimelineCalendarEventsFromCompanyId(
|
getTimelineCalendarEventsFromCompanyId(
|
||||||
@ -5217,9 +5223,12 @@ export const UpdateWorkspaceDocument = gql`
|
|||||||
isGoogleAuthEnabled
|
isGoogleAuthEnabled
|
||||||
isMicrosoftAuthEnabled
|
isMicrosoftAuthEnabled
|
||||||
isPasswordAuthEnabled
|
isPasswordAuthEnabled
|
||||||
|
defaultRole {
|
||||||
|
...RoleFragment
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
${RoleFragmentFragmentDoc}`;
|
||||||
export type UpdateWorkspaceMutationFn = Apollo.MutationFunction<UpdateWorkspaceMutation, UpdateWorkspaceMutationVariables>;
|
export type UpdateWorkspaceMutationFn = Apollo.MutationFunction<UpdateWorkspaceMutation, UpdateWorkspaceMutationVariables>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { createState } from '@ui/utilities/state/utils/createState';
|
import { createState } from '@ui/utilities/state/utils/createState';
|
||||||
|
|
||||||
import { Workspace } from '~/generated/graphql';
|
import { Role, Workspace } from '~/generated/graphql';
|
||||||
|
|
||||||
export type CurrentWorkspace = Pick<
|
export type CurrentWorkspace = Pick<
|
||||||
Workspace,
|
Workspace,
|
||||||
@ -23,7 +23,9 @@ export type CurrentWorkspace = Pick<
|
|||||||
| 'customDomain'
|
| 'customDomain'
|
||||||
| 'workspaceUrls'
|
| 'workspaceUrls'
|
||||||
| 'metadataVersion'
|
| 'metadataVersion'
|
||||||
>;
|
> & {
|
||||||
|
defaultRole?: Omit<Role, 'workspaceMembers'> | null;
|
||||||
|
};
|
||||||
|
|
||||||
export const currentWorkspaceState = createState<CurrentWorkspace | null>({
|
export const currentWorkspaceState = createState<CurrentWorkspace | null>({
|
||||||
key: 'currentWorkspaceState',
|
key: 'currentWorkspaceState',
|
||||||
|
|||||||
@ -342,6 +342,17 @@ export const responseData = {
|
|||||||
metadataVersion: 1,
|
metadataVersion: 1,
|
||||||
currentBillingSubscription: null,
|
currentBillingSubscription: null,
|
||||||
workspaceMembersCount: 1,
|
workspaceMembersCount: 1,
|
||||||
|
defaultRole: {
|
||||||
|
id: 'default-role-id',
|
||||||
|
label: 'Default Role',
|
||||||
|
description: 'Default Role Description',
|
||||||
|
canUpdateAllSettings: true,
|
||||||
|
isEditable: true,
|
||||||
|
canReadAllObjectRecords: true,
|
||||||
|
canUpdateAllObjectRecords: true,
|
||||||
|
canSoftDeleteAllObjectRecords: true,
|
||||||
|
canDestroyAllObjectRecords: true,
|
||||||
|
}
|
||||||
},
|
},
|
||||||
currentBillingSubscription: null,
|
currentBillingSubscription: null,
|
||||||
billingSubscriptions: [],
|
billingSubscriptions: [],
|
||||||
|
|||||||
@ -21,6 +21,16 @@ import {
|
|||||||
responseData as findManyObjectMetadataItemsResponseData,
|
responseData as findManyObjectMetadataItemsResponseData,
|
||||||
} from '../__mocks__/useFindManyObjectMetadataItems';
|
} from '../__mocks__/useFindManyObjectMetadataItems';
|
||||||
|
|
||||||
|
jest.mock('@/object-metadata/hooks/useUpdateOneFieldMetadataItem', () => ({
|
||||||
|
useUpdateOneFieldMetadataItem: () => ({
|
||||||
|
updateOneFieldMetadataItem: jest.fn().mockResolvedValue({
|
||||||
|
data: {
|
||||||
|
updateOneField: responseData.default,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
|
||||||
const fieldMetadataItem: FieldMetadataItem = {
|
const fieldMetadataItem: FieldMetadataItem = {
|
||||||
id: FIELD_METADATA_ID,
|
id: FIELD_METADATA_ID,
|
||||||
createdAt: '',
|
createdAt: '',
|
||||||
@ -111,17 +121,6 @@ const mocks = [
|
|||||||
},
|
},
|
||||||
})),
|
})),
|
||||||
},
|
},
|
||||||
{
|
|
||||||
request: {
|
|
||||||
query: queries.activateMetadataField,
|
|
||||||
variables: variables.activateMetadataField,
|
|
||||||
},
|
|
||||||
result: jest.fn(() => ({
|
|
||||||
data: {
|
|
||||||
updateOneField: responseData.default,
|
|
||||||
},
|
|
||||||
})),
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
request: {
|
request: {
|
||||||
query: queries.createMetadataField,
|
query: queries.createMetadataField,
|
||||||
@ -133,26 +132,6 @@ const mocks = [
|
|||||||
},
|
},
|
||||||
})),
|
})),
|
||||||
},
|
},
|
||||||
{
|
|
||||||
request: {
|
|
||||||
query: queries.activateMetadataField,
|
|
||||||
variables: variables.deactivateMetadataField,
|
|
||||||
},
|
|
||||||
result: jest.fn(() => ({
|
|
||||||
data: {
|
|
||||||
updateOneField: responseData.default,
|
|
||||||
},
|
|
||||||
})),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
request: {
|
|
||||||
query: queries.getCurrentUser,
|
|
||||||
variables: {},
|
|
||||||
},
|
|
||||||
result: jest.fn(() => ({
|
|
||||||
data: responseData.getCurrentUser,
|
|
||||||
})),
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
request: {
|
request: {
|
||||||
query: queries.getCurrentUser,
|
query: queries.getCurrentUser,
|
||||||
|
|||||||
@ -57,7 +57,10 @@ export const UserProviderEffect = () => {
|
|||||||
setCurrentUser(queryData.currentUser);
|
setCurrentUser(queryData.currentUser);
|
||||||
|
|
||||||
if (isDefined(queryData.currentUser.currentWorkspace)) {
|
if (isDefined(queryData.currentUser.currentWorkspace)) {
|
||||||
setCurrentWorkspace(queryData.currentUser.currentWorkspace);
|
setCurrentWorkspace({
|
||||||
|
...queryData.currentUser.currentWorkspace,
|
||||||
|
defaultRole: queryData.currentUser.currentWorkspace.defaultRole ?? null,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isDefined(queryData.currentUser.currentUserWorkspace)) {
|
if (isDefined(queryData.currentUser.currentUserWorkspace)) {
|
||||||
|
|||||||
@ -1,7 +1,9 @@
|
|||||||
|
import { ROLE_FRAGMENT } from '@/settings/roles/graphql/fragments/roleFragment';
|
||||||
import { WORKSPACE_MEMBER_QUERY_FRAGMENT } from '@/workspace-member/graphql/fragments/workspaceMemberQueryFragment';
|
import { WORKSPACE_MEMBER_QUERY_FRAGMENT } from '@/workspace-member/graphql/fragments/workspaceMemberQueryFragment';
|
||||||
import { gql } from '@apollo/client';
|
import { gql } from '@apollo/client';
|
||||||
|
|
||||||
export const USER_QUERY_FRAGMENT = gql`
|
export const USER_QUERY_FRAGMENT = gql`
|
||||||
|
${ROLE_FRAGMENT}
|
||||||
fragment UserQueryFragment on User {
|
fragment UserQueryFragment on User {
|
||||||
id
|
id
|
||||||
firstName
|
firstName
|
||||||
@ -64,6 +66,9 @@ export const USER_QUERY_FRAGMENT = gql`
|
|||||||
status
|
status
|
||||||
}
|
}
|
||||||
workspaceMembersCount
|
workspaceMembersCount
|
||||||
|
defaultRole {
|
||||||
|
...RoleFragment
|
||||||
|
}
|
||||||
}
|
}
|
||||||
workspaces {
|
workspaces {
|
||||||
workspace {
|
workspace {
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
|
import { ROLE_FRAGMENT } from '@/settings/roles/graphql/fragments/roleFragment';
|
||||||
import { gql } from '@apollo/client';
|
import { gql } from '@apollo/client';
|
||||||
|
|
||||||
export const UPDATE_WORKSPACE = gql`
|
export const UPDATE_WORKSPACE = gql`
|
||||||
|
${ROLE_FRAGMENT}
|
||||||
mutation UpdateWorkspace($input: UpdateWorkspaceInput!) {
|
mutation UpdateWorkspace($input: UpdateWorkspaceInput!) {
|
||||||
updateWorkspace(data: $input) {
|
updateWorkspace(data: $input) {
|
||||||
id
|
id
|
||||||
@ -13,6 +15,9 @@ export const UPDATE_WORKSPACE = gql`
|
|||||||
isGoogleAuthEnabled
|
isGoogleAuthEnabled
|
||||||
isMicrosoftAuthEnabled
|
isMicrosoftAuthEnabled
|
||||||
isPasswordAuthEnabled
|
isPasswordAuthEnabled
|
||||||
|
defaultRole {
|
||||||
|
...RoleFragment
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|||||||
@ -1,106 +1,19 @@
|
|||||||
import styled from '@emotion/styled';
|
|
||||||
import { Trans, useLingui } from '@lingui/react/macro';
|
import { Trans, useLingui } from '@lingui/react/macro';
|
||||||
import {
|
|
||||||
AppTooltip,
|
|
||||||
Avatar,
|
|
||||||
Button,
|
|
||||||
H2Title,
|
|
||||||
IconChevronRight,
|
|
||||||
IconLock,
|
|
||||||
IconPlus,
|
|
||||||
IconUser,
|
|
||||||
Section,
|
|
||||||
TooltipDelay,
|
|
||||||
} from 'twenty-ui';
|
|
||||||
|
|
||||||
import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer';
|
import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer';
|
||||||
import { SettingsPath } from '@/types/SettingsPath';
|
import { SettingsPath } from '@/types/SettingsPath';
|
||||||
import { SubMenuTopBarContainer } from '@/ui/layout/page/components/SubMenuTopBarContainer';
|
import { SubMenuTopBarContainer } from '@/ui/layout/page/components/SubMenuTopBarContainer';
|
||||||
import { Table } from '@/ui/layout/table/components/Table';
|
|
||||||
import { TableCell } from '@/ui/layout/table/components/TableCell';
|
|
||||||
import { TableHeader } from '@/ui/layout/table/components/TableHeader';
|
|
||||||
import { TableRow } from '@/ui/layout/table/components/TableRow';
|
|
||||||
import { useTheme } from '@emotion/react';
|
|
||||||
import React from 'react';
|
|
||||||
import { useGetRolesQuery } from '~/generated/graphql';
|
import { useGetRolesQuery } from '~/generated/graphql';
|
||||||
import { useNavigateSettings } from '~/hooks/useNavigateSettings';
|
import { Roles } from '~/pages/settings/roles/components/Roles';
|
||||||
|
import { RolesDefaultRole } from '~/pages/settings/roles/components/RolesDefaultRole';
|
||||||
import { getSettingsPath } from '~/utils/navigation/getSettingsPath';
|
import { getSettingsPath } from '~/utils/navigation/getSettingsPath';
|
||||||
|
|
||||||
const StyledTable = styled(Table)`
|
|
||||||
margin-top: ${({ theme }) => theme.spacing(0.5)};
|
|
||||||
`;
|
|
||||||
|
|
||||||
const StyledTableRow = styled(TableRow)`
|
|
||||||
&:hover {
|
|
||||||
background: ${({ theme }) => theme.background.transparent.light};
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
const StyledNameCell = styled.div`
|
|
||||||
align-items: center;
|
|
||||||
display: flex;
|
|
||||||
gap: ${({ theme }) => theme.spacing(2)};
|
|
||||||
`;
|
|
||||||
|
|
||||||
const StyledAssignedCell = styled.div`
|
|
||||||
align-items: center;
|
|
||||||
display: flex;
|
|
||||||
gap: ${({ theme }) => theme.spacing(1)};
|
|
||||||
`;
|
|
||||||
|
|
||||||
const StyledAvatarGroup = styled.div`
|
|
||||||
align-items: center;
|
|
||||||
display: flex;
|
|
||||||
margin-right: ${({ theme }) => theme.spacing(1)};
|
|
||||||
|
|
||||||
> * {
|
|
||||||
margin-left: -5px;
|
|
||||||
|
|
||||||
&:first-of-type {
|
|
||||||
margin-left: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
const StyledTableHeaderRow = styled(Table)`
|
|
||||||
margin-bottom: ${({ theme }) => theme.spacing(2)};
|
|
||||||
`;
|
|
||||||
|
|
||||||
const StyledBottomSection = styled(Section)`
|
|
||||||
border-top: 1px solid ${({ theme }) => theme.border.color.light};
|
|
||||||
margin-top: ${({ theme }) => theme.spacing(2)};
|
|
||||||
padding-top: ${({ theme }) => theme.spacing(4)};
|
|
||||||
display: flex;
|
|
||||||
justify-content: flex-end;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const StyledIconChevronRight = styled(IconChevronRight)`
|
|
||||||
color: ${({ theme }) => theme.font.color.tertiary};
|
|
||||||
`;
|
|
||||||
|
|
||||||
const StyledAvatarContainer = styled.div`
|
|
||||||
border: 0px;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const StyledAssignedText = styled.div`
|
|
||||||
color: ${({ theme }) => theme.font.color.primary};
|
|
||||||
font-size: ${({ theme }) => theme.font.size.md};
|
|
||||||
`;
|
|
||||||
|
|
||||||
export const SettingsRoles = () => {
|
export const SettingsRoles = () => {
|
||||||
const { t } = useLingui();
|
const { t } = useLingui();
|
||||||
|
|
||||||
const theme = useTheme();
|
|
||||||
const navigateSettings = useNavigateSettings();
|
|
||||||
const { data: rolesData, loading: rolesLoading } = useGetRolesQuery({
|
const { data: rolesData, loading: rolesLoading } = useGetRolesQuery({
|
||||||
fetchPolicy: 'network-only',
|
fetchPolicy: 'network-only',
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleRoleClick = (roleId: string) => {
|
|
||||||
navigateSettings(SettingsPath.RoleDetail, { roleId });
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SubMenuTopBarContainer
|
<SubMenuTopBarContainer
|
||||||
title={t`Roles`}
|
title={t`Roles`}
|
||||||
@ -113,90 +26,12 @@ export const SettingsRoles = () => {
|
|||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<SettingsPageContainer>
|
<SettingsPageContainer>
|
||||||
<Section>
|
{!rolesLoading && (
|
||||||
<H2Title
|
<>
|
||||||
title={t`All roles`}
|
<Roles roles={rolesData?.getRoles ?? []} />
|
||||||
description={t`Assign roles to specify each member's access permissions`}
|
<RolesDefaultRole roles={rolesData?.getRoles ?? []} />
|
||||||
/>
|
</>
|
||||||
<StyledTable>
|
)}
|
||||||
<StyledTableHeaderRow>
|
|
||||||
<TableRow>
|
|
||||||
<TableHeader>
|
|
||||||
<Trans>Name</Trans>
|
|
||||||
</TableHeader>
|
|
||||||
<TableHeader align={'right'}>
|
|
||||||
<Trans>Assigned to</Trans>
|
|
||||||
</TableHeader>
|
|
||||||
<TableHeader align={'right'}></TableHeader>
|
|
||||||
</TableRow>
|
|
||||||
</StyledTableHeaderRow>
|
|
||||||
{!rolesLoading &&
|
|
||||||
rolesData?.getRoles.map((role) => (
|
|
||||||
<StyledTableRow
|
|
||||||
key={role.id}
|
|
||||||
onClick={() => handleRoleClick(role.id)}
|
|
||||||
>
|
|
||||||
<TableCell>
|
|
||||||
<StyledNameCell>
|
|
||||||
<IconUser size={theme.icon.size.md} />
|
|
||||||
{role.label}
|
|
||||||
{!role.isEditable && (
|
|
||||||
<IconLock size={theme.icon.size.sm} />
|
|
||||||
)}
|
|
||||||
</StyledNameCell>
|
|
||||||
</TableCell>
|
|
||||||
<TableCell align={'right'}>
|
|
||||||
<StyledAssignedCell>
|
|
||||||
<StyledAvatarGroup>
|
|
||||||
{role.workspaceMembers
|
|
||||||
.slice(0, 5)
|
|
||||||
.map((workspaceMember) => (
|
|
||||||
<React.Fragment key={workspaceMember.id}>
|
|
||||||
<StyledAvatarContainer
|
|
||||||
id={`avatar-${workspaceMember.id}`}
|
|
||||||
>
|
|
||||||
<Avatar
|
|
||||||
avatarUrl={workspaceMember.avatarUrl}
|
|
||||||
placeholderColorSeed={workspaceMember.id}
|
|
||||||
placeholder={
|
|
||||||
workspaceMember.name.firstName ?? ''
|
|
||||||
}
|
|
||||||
type="rounded"
|
|
||||||
size="md"
|
|
||||||
/>
|
|
||||||
</StyledAvatarContainer>
|
|
||||||
<AppTooltip
|
|
||||||
anchorSelect={`#avatar-${workspaceMember.id}`}
|
|
||||||
content={`${workspaceMember.name.firstName} ${workspaceMember.name.lastName}`}
|
|
||||||
noArrow
|
|
||||||
place="top"
|
|
||||||
positionStrategy="fixed"
|
|
||||||
delay={TooltipDelay.shortDelay}
|
|
||||||
/>
|
|
||||||
</React.Fragment>
|
|
||||||
))}
|
|
||||||
</StyledAvatarGroup>
|
|
||||||
<StyledAssignedText>
|
|
||||||
{role.workspaceMembers.length}
|
|
||||||
</StyledAssignedText>
|
|
||||||
</StyledAssignedCell>
|
|
||||||
</TableCell>
|
|
||||||
<TableCell align={'right'}>
|
|
||||||
<StyledIconChevronRight size={theme.icon.size.md} />
|
|
||||||
</TableCell>
|
|
||||||
</StyledTableRow>
|
|
||||||
))}
|
|
||||||
</StyledTable>
|
|
||||||
<StyledBottomSection>
|
|
||||||
<Button
|
|
||||||
Icon={IconPlus}
|
|
||||||
title={t`Create Role`}
|
|
||||||
variant="secondary"
|
|
||||||
size="small"
|
|
||||||
soon
|
|
||||||
/>
|
|
||||||
</StyledBottomSection>
|
|
||||||
</Section>
|
|
||||||
</SettingsPageContainer>
|
</SettingsPageContainer>
|
||||||
</SubMenuTopBarContainer>
|
</SubMenuTopBarContainer>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -6,8 +6,8 @@ import { RolePermissionsObjectPermission } from '~/pages/settings/roles/types/Ro
|
|||||||
|
|
||||||
const StyledIconWrapper = styled.div`
|
const StyledIconWrapper = styled.div`
|
||||||
align-items: center;
|
align-items: center;
|
||||||
background: ${({ theme }) => theme.color.blue10};
|
background: ${({ theme }) => theme.adaptiveColors.blue1};
|
||||||
border: 1px solid ${({ theme }) => theme.color.blue30};
|
border: 1px solid ${({ theme }) => theme.adaptiveColors.blue3};
|
||||||
border-radius: ${({ theme }) => theme.border.radius.sm};
|
border-radius: ${({ theme }) => theme.border.radius.sm};
|
||||||
display: flex;
|
display: flex;
|
||||||
height: ${({ theme }) => theme.spacing(4)};
|
height: ${({ theme }) => theme.spacing(4)};
|
||||||
|
|||||||
@ -0,0 +1,46 @@
|
|||||||
|
import { Table } from '@/ui/layout/table/components/Table';
|
||||||
|
import styled from '@emotion/styled';
|
||||||
|
import { t } from '@lingui/core/macro';
|
||||||
|
|
||||||
|
import { Button, H2Title, IconPlus, Section } from 'twenty-ui';
|
||||||
|
import { Role } from '~/generated-metadata/graphql';
|
||||||
|
import { RolesTableHeader } from '~/pages/settings/roles/components/RolesTableHeader';
|
||||||
|
import { RolesTableRow } from '~/pages/settings/roles/components/RolesTableRow';
|
||||||
|
|
||||||
|
const StyledTable = styled(Table)`
|
||||||
|
margin-top: ${({ theme }) => theme.spacing(0.5)};
|
||||||
|
`;
|
||||||
|
|
||||||
|
const StyledBottomSection = styled(Section)`
|
||||||
|
border-top: 1px solid ${({ theme }) => theme.border.color.light};
|
||||||
|
margin-top: ${({ theme }) => theme.spacing(2)};
|
||||||
|
padding-top: ${({ theme }) => theme.spacing(4)};
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const Roles = ({ roles }: { roles: Role[] }) => {
|
||||||
|
return (
|
||||||
|
<Section>
|
||||||
|
<H2Title
|
||||||
|
title={t`All roles`}
|
||||||
|
description={t`Assign roles to specify each member's access permissions`}
|
||||||
|
/>
|
||||||
|
<StyledTable>
|
||||||
|
<RolesTableHeader />
|
||||||
|
{roles.map((role) => (
|
||||||
|
<RolesTableRow key={role.id} role={role} />
|
||||||
|
))}
|
||||||
|
</StyledTable>
|
||||||
|
<StyledBottomSection>
|
||||||
|
<Button
|
||||||
|
Icon={IconPlus}
|
||||||
|
title={t`Create Role`}
|
||||||
|
variant="secondary"
|
||||||
|
size="small"
|
||||||
|
soon
|
||||||
|
/>
|
||||||
|
</StyledBottomSection>
|
||||||
|
</Section>
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -0,0 +1,80 @@
|
|||||||
|
import {
|
||||||
|
CurrentWorkspace,
|
||||||
|
currentWorkspaceState,
|
||||||
|
} from '@/auth/states/currentWorkspaceState';
|
||||||
|
import { SettingsOptionCardContentSelect } from '@/settings/components/SettingsOptions/SettingsOptionCardContentSelect';
|
||||||
|
import { Select } from '@/ui/input/components/Select';
|
||||||
|
import { t } from '@lingui/core/macro';
|
||||||
|
import { useRecoilState } from 'recoil';
|
||||||
|
import { isDefined } from 'twenty-shared';
|
||||||
|
import { Card, H2Title, IconUserPin, Section } from 'twenty-ui';
|
||||||
|
import {
|
||||||
|
Role,
|
||||||
|
UpdateWorkspaceMutation,
|
||||||
|
useUpdateWorkspaceMutation,
|
||||||
|
} from '~/generated/graphql';
|
||||||
|
|
||||||
|
export const RolesDefaultRole = ({ roles }: { roles: Role[] }) => {
|
||||||
|
const [updateWorkspace] = useUpdateWorkspaceMutation();
|
||||||
|
|
||||||
|
const [currentWorkspace, setCurrentWorkspace] = useRecoilState(
|
||||||
|
currentWorkspaceState,
|
||||||
|
);
|
||||||
|
|
||||||
|
const defaultRole = currentWorkspace?.defaultRole;
|
||||||
|
|
||||||
|
const updateDefaultRole = (
|
||||||
|
defaultRoleId: string | null,
|
||||||
|
currentWorkspace: CurrentWorkspace,
|
||||||
|
) => {
|
||||||
|
updateWorkspace({
|
||||||
|
variables: {
|
||||||
|
input: {
|
||||||
|
defaultRoleId: isDefined(defaultRoleId) ? defaultRoleId : null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
onCompleted: (data: UpdateWorkspaceMutation) => {
|
||||||
|
const defaultRole = data.updateWorkspace.defaultRole;
|
||||||
|
|
||||||
|
setCurrentWorkspace({
|
||||||
|
...currentWorkspace,
|
||||||
|
defaultRole: isDefined(defaultRole) ? defaultRole : null,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!currentWorkspace) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Section>
|
||||||
|
<H2Title
|
||||||
|
title={t`Options`}
|
||||||
|
description={t`Adjust the role-related settings`}
|
||||||
|
/>
|
||||||
|
<Card rounded>
|
||||||
|
<SettingsOptionCardContentSelect
|
||||||
|
Icon={IconUserPin}
|
||||||
|
title="Default Role"
|
||||||
|
description={t`Set a default role for this workspace`}
|
||||||
|
>
|
||||||
|
<Select
|
||||||
|
selectSizeVariant="small"
|
||||||
|
withSearchInput
|
||||||
|
dropdownId="default-role-select"
|
||||||
|
options={roles.map((role) => ({
|
||||||
|
label: role.label,
|
||||||
|
value: role.id,
|
||||||
|
}))}
|
||||||
|
value={defaultRole?.id ?? ''}
|
||||||
|
onChange={(value) =>
|
||||||
|
updateDefaultRole(value as string, currentWorkspace)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</SettingsOptionCardContentSelect>
|
||||||
|
</Card>
|
||||||
|
</Section>
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -0,0 +1,25 @@
|
|||||||
|
import { Table } from '@/ui/layout/table/components/Table';
|
||||||
|
import { TableHeader } from '@/ui/layout/table/components/TableHeader';
|
||||||
|
import { TableRow } from '@/ui/layout/table/components/TableRow';
|
||||||
|
import styled from '@emotion/styled';
|
||||||
|
import { Trans } from '@lingui/react/macro';
|
||||||
|
|
||||||
|
const StyledTableHeaderRow = styled(Table)`
|
||||||
|
margin-bottom: ${({ theme }) => theme.spacing(2)};
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const RolesTableHeader = () => {
|
||||||
|
return (
|
||||||
|
<StyledTableHeaderRow>
|
||||||
|
<TableRow>
|
||||||
|
<TableHeader>
|
||||||
|
<Trans>Name</Trans>
|
||||||
|
</TableHeader>
|
||||||
|
<TableHeader align={'right'}>
|
||||||
|
<Trans>Assigned to</Trans>
|
||||||
|
</TableHeader>
|
||||||
|
<TableHeader align={'right'}></TableHeader>
|
||||||
|
</TableRow>
|
||||||
|
</StyledTableHeaderRow>
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -0,0 +1,117 @@
|
|||||||
|
import { SettingsPath } from '@/types/SettingsPath';
|
||||||
|
import { TableCell } from '@/ui/layout/table/components/TableCell';
|
||||||
|
import { TableRow } from '@/ui/layout/table/components/TableRow';
|
||||||
|
import { useTheme } from '@emotion/react';
|
||||||
|
import styled from '@emotion/styled';
|
||||||
|
import React from 'react';
|
||||||
|
import {
|
||||||
|
AppTooltip,
|
||||||
|
Avatar,
|
||||||
|
IconChevronRight,
|
||||||
|
IconLock,
|
||||||
|
IconUser,
|
||||||
|
TooltipDelay,
|
||||||
|
} from 'twenty-ui';
|
||||||
|
import { Role } from '~/generated-metadata/graphql';
|
||||||
|
import { useNavigateSettings } from '~/hooks/useNavigateSettings';
|
||||||
|
|
||||||
|
const StyledIconChevronRight = styled(IconChevronRight)`
|
||||||
|
color: ${({ theme }) => theme.font.color.tertiary};
|
||||||
|
`;
|
||||||
|
|
||||||
|
const StyledAvatarContainer = styled.div`
|
||||||
|
border: 0px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const StyledAssignedText = styled.div`
|
||||||
|
color: ${({ theme }) => theme.font.color.primary};
|
||||||
|
font-size: ${({ theme }) => theme.font.size.md};
|
||||||
|
`;
|
||||||
|
|
||||||
|
const StyledNameCell = styled.div`
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
gap: ${({ theme }) => theme.spacing(2)};
|
||||||
|
`;
|
||||||
|
|
||||||
|
const StyledAssignedCell = styled.div`
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
gap: ${({ theme }) => theme.spacing(1)};
|
||||||
|
`;
|
||||||
|
|
||||||
|
const StyledAvatarGroup = styled.div`
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
margin-right: ${({ theme }) => theme.spacing(1)};
|
||||||
|
|
||||||
|
> * {
|
||||||
|
margin-left: -5px;
|
||||||
|
|
||||||
|
&:first-of-type {
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const StyledTableRow = styled(TableRow)`
|
||||||
|
&:hover {
|
||||||
|
background: ${({ theme }) => theme.background.transparent.light};
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const RolesTableRow = ({ role }: { role: Role }) => {
|
||||||
|
const theme = useTheme();
|
||||||
|
|
||||||
|
const navigateSettings = useNavigateSettings();
|
||||||
|
|
||||||
|
const handleRoleClick = (roleId: string) => {
|
||||||
|
navigateSettings(SettingsPath.RoleDetail, { roleId });
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<StyledTableRow key={role.id} onClick={() => handleRoleClick(role.id)}>
|
||||||
|
<TableCell>
|
||||||
|
<StyledNameCell>
|
||||||
|
<IconUser size={theme.icon.size.md} />
|
||||||
|
{role.label}
|
||||||
|
{!role.isEditable && <IconLock size={theme.icon.size.sm} />}
|
||||||
|
</StyledNameCell>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell align={'right'}>
|
||||||
|
<StyledAssignedCell>
|
||||||
|
<StyledAvatarGroup>
|
||||||
|
{role.workspaceMembers.slice(0, 5).map((workspaceMember) => (
|
||||||
|
<React.Fragment key={workspaceMember.id}>
|
||||||
|
<StyledAvatarContainer id={`avatar-${workspaceMember.id}`}>
|
||||||
|
<Avatar
|
||||||
|
avatarUrl={workspaceMember.avatarUrl}
|
||||||
|
placeholderColorSeed={workspaceMember.id}
|
||||||
|
placeholder={workspaceMember.name.firstName ?? ''}
|
||||||
|
type="rounded"
|
||||||
|
size="md"
|
||||||
|
/>
|
||||||
|
</StyledAvatarContainer>
|
||||||
|
<AppTooltip
|
||||||
|
anchorSelect={`#avatar-${workspaceMember.id}`}
|
||||||
|
content={`${workspaceMember.name.firstName} ${workspaceMember.name.lastName}`}
|
||||||
|
noArrow
|
||||||
|
place="top"
|
||||||
|
positionStrategy="fixed"
|
||||||
|
delay={TooltipDelay.shortDelay}
|
||||||
|
/>
|
||||||
|
</React.Fragment>
|
||||||
|
))}
|
||||||
|
</StyledAvatarGroup>
|
||||||
|
<StyledAssignedText>
|
||||||
|
{role.workspaceMembers.length}
|
||||||
|
</StyledAssignedText>
|
||||||
|
</StyledAssignedCell>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell align={'right'}>
|
||||||
|
<StyledIconChevronRight size={theme.icon.size.md} />
|
||||||
|
</TableCell>
|
||||||
|
</StyledTableRow>
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -0,0 +1,19 @@
|
|||||||
|
import { MigrationInterface, QueryRunner } from 'typeorm';
|
||||||
|
|
||||||
|
export class AddDefaultRoleToWorkspace1740390801418
|
||||||
|
implements MigrationInterface
|
||||||
|
{
|
||||||
|
name = 'AddDefaultRoleToWorkspace1740390801418';
|
||||||
|
|
||||||
|
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(
|
||||||
|
`ALTER TABLE "core"."workspace" ADD "defaultRoleId" uuid`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(
|
||||||
|
`ALTER TABLE "core"."workspace" DROP COLUMN "defaultRoleId"`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -15,9 +15,9 @@ import { ApiKeyService } from 'src/engine/core-modules/auth/services/api-key.ser
|
|||||||
import { GoogleAPIsService } from 'src/engine/core-modules/auth/services/google-apis.service';
|
import { GoogleAPIsService } from 'src/engine/core-modules/auth/services/google-apis.service';
|
||||||
import { MicrosoftAPIsService } from 'src/engine/core-modules/auth/services/microsoft-apis.service';
|
import { MicrosoftAPIsService } from 'src/engine/core-modules/auth/services/microsoft-apis.service';
|
||||||
// import { OAuthService } from 'src/engine/core-modules/auth/services/oauth.service';
|
// import { OAuthService } from 'src/engine/core-modules/auth/services/oauth.service';
|
||||||
|
import { AuthSsoService } from 'src/engine/core-modules/auth/services/auth-sso.service';
|
||||||
import { ResetPasswordService } from 'src/engine/core-modules/auth/services/reset-password.service';
|
import { ResetPasswordService } from 'src/engine/core-modules/auth/services/reset-password.service';
|
||||||
import { SignInUpService } from 'src/engine/core-modules/auth/services/sign-in-up.service';
|
import { SignInUpService } from 'src/engine/core-modules/auth/services/sign-in-up.service';
|
||||||
import { AuthSsoService } from 'src/engine/core-modules/auth/services/auth-sso.service';
|
|
||||||
import { SamlAuthStrategy } from 'src/engine/core-modules/auth/strategies/saml.auth.strategy';
|
import { SamlAuthStrategy } from 'src/engine/core-modules/auth/strategies/saml.auth.strategy';
|
||||||
import { AccessTokenService } from 'src/engine/core-modules/auth/token/services/access-token.service';
|
import { AccessTokenService } from 'src/engine/core-modules/auth/token/services/access-token.service';
|
||||||
import { LoginTokenService } from 'src/engine/core-modules/auth/token/services/login-token.service';
|
import { LoginTokenService } from 'src/engine/core-modules/auth/token/services/login-token.service';
|
||||||
@ -46,6 +46,7 @@ import { WorkspaceModule } from 'src/engine/core-modules/workspace/workspace.mod
|
|||||||
import { DataSourceModule } from 'src/engine/metadata-modules/data-source/data-source.module';
|
import { DataSourceModule } from 'src/engine/metadata-modules/data-source/data-source.module';
|
||||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||||
import { PermissionsModule } from 'src/engine/metadata-modules/permissions/permissions.module';
|
import { PermissionsModule } from 'src/engine/metadata-modules/permissions/permissions.module';
|
||||||
|
import { UserRoleModule } from 'src/engine/metadata-modules/user-role/user-role.module';
|
||||||
import { WorkspaceDataSourceModule } from 'src/engine/workspace-datasource/workspace-datasource.module';
|
import { WorkspaceDataSourceModule } from 'src/engine/workspace-datasource/workspace-datasource.module';
|
||||||
import { WorkspaceManagerModule } from 'src/engine/workspace-manager/workspace-manager.module';
|
import { WorkspaceManagerModule } from 'src/engine/workspace-manager/workspace-manager.module';
|
||||||
import { ConnectedAccountModule } from 'src/modules/connected-account/connected-account.module';
|
import { ConnectedAccountModule } from 'src/modules/connected-account/connected-account.module';
|
||||||
@ -91,6 +92,7 @@ import { JwtAuthStrategy } from './strategies/jwt.auth.strategy';
|
|||||||
GuardRedirectModule,
|
GuardRedirectModule,
|
||||||
HealthModule,
|
HealthModule,
|
||||||
PermissionsModule,
|
PermissionsModule,
|
||||||
|
UserRoleModule,
|
||||||
],
|
],
|
||||||
controllers: [
|
controllers: [
|
||||||
GoogleAuthController,
|
GoogleAuthController,
|
||||||
|
|||||||
@ -6,6 +6,10 @@ import { WorkspaceActivationStatus } from 'twenty-shared';
|
|||||||
import { Repository } from 'typeorm';
|
import { Repository } from 'typeorm';
|
||||||
|
|
||||||
import { AppToken } from 'src/engine/core-modules/app-token/app-token.entity';
|
import { AppToken } from 'src/engine/core-modules/app-token/app-token.entity';
|
||||||
|
import {
|
||||||
|
AuthException,
|
||||||
|
AuthExceptionCode,
|
||||||
|
} from 'src/engine/core-modules/auth/auth.exception';
|
||||||
import { SignInUpService } from 'src/engine/core-modules/auth/services/sign-in-up.service';
|
import { SignInUpService } from 'src/engine/core-modules/auth/services/sign-in-up.service';
|
||||||
import {
|
import {
|
||||||
AuthProviderWithPasswordType,
|
AuthProviderWithPasswordType,
|
||||||
@ -14,18 +18,16 @@ import {
|
|||||||
} from 'src/engine/core-modules/auth/types/signInUp.type';
|
} from 'src/engine/core-modules/auth/types/signInUp.type';
|
||||||
import { DomainManagerService } from 'src/engine/core-modules/domain-manager/services/domain-manager.service';
|
import { DomainManagerService } from 'src/engine/core-modules/domain-manager/services/domain-manager.service';
|
||||||
import { EnvironmentService } from 'src/engine/core-modules/environment/environment.service';
|
import { EnvironmentService } from 'src/engine/core-modules/environment/environment.service';
|
||||||
|
import { FeatureFlagService } from 'src/engine/core-modules/feature-flag/services/feature-flag.service';
|
||||||
import { FileUploadService } from 'src/engine/core-modules/file/file-upload/services/file-upload.service';
|
import { FileUploadService } from 'src/engine/core-modules/file/file-upload/services/file-upload.service';
|
||||||
import { OnboardingService } from 'src/engine/core-modules/onboarding/onboarding.service';
|
import { OnboardingService } from 'src/engine/core-modules/onboarding/onboarding.service';
|
||||||
|
import { UserWorkspace } from 'src/engine/core-modules/user-workspace/user-workspace.entity';
|
||||||
import { UserWorkspaceService } from 'src/engine/core-modules/user-workspace/user-workspace.service';
|
import { UserWorkspaceService } from 'src/engine/core-modules/user-workspace/user-workspace.service';
|
||||||
import { UserService } from 'src/engine/core-modules/user/services/user.service';
|
import { UserService } from 'src/engine/core-modules/user/services/user.service';
|
||||||
import { User } from 'src/engine/core-modules/user/user.entity';
|
import { User } from 'src/engine/core-modules/user/user.entity';
|
||||||
import { WorkspaceInvitationService } from 'src/engine/core-modules/workspace-invitation/services/workspace-invitation.service';
|
import { WorkspaceInvitationService } from 'src/engine/core-modules/workspace-invitation/services/workspace-invitation.service';
|
||||||
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||||
import { UserWorkspace } from 'src/engine/core-modules/user-workspace/user-workspace.entity';
|
import { UserRoleService } from 'src/engine/metadata-modules/user-role/user-role.service';
|
||||||
import {
|
|
||||||
AuthException,
|
|
||||||
AuthExceptionCode,
|
|
||||||
} from 'src/engine/core-modules/auth/auth.exception';
|
|
||||||
|
|
||||||
jest.mock('src/utils/image', () => {
|
jest.mock('src/utils/image', () => {
|
||||||
return {
|
return {
|
||||||
@ -42,6 +44,8 @@ describe('SignInUpService', () => {
|
|||||||
let userWorkspaceService: UserWorkspaceService;
|
let userWorkspaceService: UserWorkspaceService;
|
||||||
let environmentService: EnvironmentService;
|
let environmentService: EnvironmentService;
|
||||||
let domainManagerService: DomainManagerService;
|
let domainManagerService: DomainManagerService;
|
||||||
|
let userRoleService: UserRoleService;
|
||||||
|
let featureFlagService: FeatureFlagService;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
const module: TestingModule = await Test.createTestingModule({
|
const module: TestingModule = await Test.createTestingModule({
|
||||||
@ -117,6 +121,18 @@ describe('SignInUpService', () => {
|
|||||||
generateSubdomain: jest.fn(),
|
generateSubdomain: jest.fn(),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
provide: UserRoleService,
|
||||||
|
useValue: {
|
||||||
|
assignRoleToUserWorkspace: jest.fn(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: FeatureFlagService,
|
||||||
|
useValue: {
|
||||||
|
isFeatureEnabled: jest.fn(),
|
||||||
|
},
|
||||||
|
},
|
||||||
],
|
],
|
||||||
}).compile();
|
}).compile();
|
||||||
|
|
||||||
@ -132,6 +148,8 @@ describe('SignInUpService', () => {
|
|||||||
environmentService = module.get<EnvironmentService>(EnvironmentService);
|
environmentService = module.get<EnvironmentService>(EnvironmentService);
|
||||||
domainManagerService =
|
domainManagerService =
|
||||||
module.get<DomainManagerService>(DomainManagerService);
|
module.get<DomainManagerService>(DomainManagerService);
|
||||||
|
userRoleService = module.get<UserRoleService>(UserRoleService);
|
||||||
|
featureFlagService = module.get<FeatureFlagService>(FeatureFlagService);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle signInUp with valid personal invitation', async () => {
|
it('should handle signInUp with valid personal invitation', async () => {
|
||||||
@ -161,9 +179,10 @@ describe('SignInUpService', () => {
|
|||||||
.spyOn(workspaceInvitationService, 'invalidateWorkspaceInvitation')
|
.spyOn(workspaceInvitationService, 'invalidateWorkspaceInvitation')
|
||||||
.mockResolvedValue(undefined);
|
.mockResolvedValue(undefined);
|
||||||
|
|
||||||
jest
|
jest.spyOn(userWorkspaceService, 'addUserToWorkspace').mockResolvedValue({
|
||||||
.spyOn(userWorkspaceService, 'addUserToWorkspace')
|
user: {} as User,
|
||||||
.mockResolvedValue({} as User);
|
userWorkspace: {} as UserWorkspace,
|
||||||
|
});
|
||||||
|
|
||||||
const result = await service.signInUp(params);
|
const result = await service.signInUp(params);
|
||||||
|
|
||||||
@ -198,9 +217,10 @@ describe('SignInUpService', () => {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
jest
|
jest.spyOn(userWorkspaceService, 'addUserToWorkspace').mockResolvedValue({
|
||||||
.spyOn(userWorkspaceService, 'addUserToWorkspace')
|
user: {} as User,
|
||||||
.mockResolvedValue({} as User);
|
userWorkspace: {} as UserWorkspace,
|
||||||
|
});
|
||||||
|
|
||||||
const result = await service.signInUp(params);
|
const result = await service.signInUp(params);
|
||||||
|
|
||||||
@ -271,9 +291,10 @@ describe('SignInUpService', () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
jest.spyOn(environmentService, 'get').mockReturnValue(false);
|
jest.spyOn(environmentService, 'get').mockReturnValue(false);
|
||||||
jest
|
jest.spyOn(userWorkspaceService, 'addUserToWorkspace').mockResolvedValue({
|
||||||
.spyOn(userWorkspaceService, 'addUserToWorkspace')
|
user: {} as User,
|
||||||
.mockResolvedValue({} as User);
|
userWorkspace: {} as UserWorkspace,
|
||||||
|
});
|
||||||
jest
|
jest
|
||||||
.spyOn(userWorkspaceService, 'checkUserWorkspaceExists')
|
.spyOn(userWorkspaceService, 'checkUserWorkspaceExists')
|
||||||
.mockResolvedValue({} as UserWorkspace);
|
.mockResolvedValue({} as UserWorkspace);
|
||||||
@ -312,4 +333,38 @@ describe('SignInUpService', () => {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should assign default role when permissions are enabled', async () => {
|
||||||
|
const params: SignInUpBaseParams &
|
||||||
|
ExistingUserOrPartialUserWithPicture &
|
||||||
|
AuthProviderWithPasswordType = {
|
||||||
|
workspace: {
|
||||||
|
id: 'workspaceId',
|
||||||
|
defaultRoleId: 'defaultRoleId',
|
||||||
|
activationStatus: WorkspaceActivationStatus.ACTIVE,
|
||||||
|
} as Workspace,
|
||||||
|
authParams: { provider: 'password', password: 'validPassword' },
|
||||||
|
userData: {
|
||||||
|
type: 'existingUser',
|
||||||
|
existingUser: { email: 'test@example.com' } as User,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const mockUserWorkspace = { id: 'userWorkspaceId' };
|
||||||
|
|
||||||
|
jest.spyOn(featureFlagService, 'isFeatureEnabled').mockResolvedValue(true);
|
||||||
|
jest.spyOn(userWorkspaceService, 'addUserToWorkspace').mockResolvedValue({
|
||||||
|
user: {} as User,
|
||||||
|
userWorkspace: mockUserWorkspace as UserWorkspace,
|
||||||
|
});
|
||||||
|
|
||||||
|
await service.signInUp(params);
|
||||||
|
|
||||||
|
expect(params.workspace).toBeDefined();
|
||||||
|
expect(userRoleService.assignRoleToUserWorkspace).toHaveBeenCalledWith({
|
||||||
|
workspaceId: params.workspace!.id,
|
||||||
|
userWorkspaceId: mockUserWorkspace.id,
|
||||||
|
roleId: params.workspace!.defaultRoleId,
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -31,6 +31,8 @@ import {
|
|||||||
} from 'src/engine/core-modules/auth/types/signInUp.type';
|
} from 'src/engine/core-modules/auth/types/signInUp.type';
|
||||||
import { DomainManagerService } from 'src/engine/core-modules/domain-manager/services/domain-manager.service';
|
import { DomainManagerService } from 'src/engine/core-modules/domain-manager/services/domain-manager.service';
|
||||||
import { EnvironmentService } from 'src/engine/core-modules/environment/environment.service';
|
import { EnvironmentService } from 'src/engine/core-modules/environment/environment.service';
|
||||||
|
import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum';
|
||||||
|
import { FeatureFlagService } from 'src/engine/core-modules/feature-flag/services/feature-flag.service';
|
||||||
import { FileUploadService } from 'src/engine/core-modules/file/file-upload/services/file-upload.service';
|
import { FileUploadService } from 'src/engine/core-modules/file/file-upload/services/file-upload.service';
|
||||||
import { OnboardingService } from 'src/engine/core-modules/onboarding/onboarding.service';
|
import { OnboardingService } from 'src/engine/core-modules/onboarding/onboarding.service';
|
||||||
import { UserWorkspaceService } from 'src/engine/core-modules/user-workspace/user-workspace.service';
|
import { UserWorkspaceService } from 'src/engine/core-modules/user-workspace/user-workspace.service';
|
||||||
@ -38,6 +40,7 @@ import { UserService } from 'src/engine/core-modules/user/services/user.service'
|
|||||||
import { User } from 'src/engine/core-modules/user/user.entity';
|
import { User } from 'src/engine/core-modules/user/user.entity';
|
||||||
import { WorkspaceInvitationService } from 'src/engine/core-modules/workspace-invitation/services/workspace-invitation.service';
|
import { WorkspaceInvitationService } from 'src/engine/core-modules/workspace-invitation/services/workspace-invitation.service';
|
||||||
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||||
|
import { UserRoleService } from 'src/engine/metadata-modules/user-role/user-role.service';
|
||||||
import { getDomainNameByEmail } from 'src/utils/get-domain-name-by-email';
|
import { getDomainNameByEmail } from 'src/utils/get-domain-name-by-email';
|
||||||
import { getImageBufferFromUrl } from 'src/utils/image';
|
import { getImageBufferFromUrl } from 'src/utils/image';
|
||||||
import { isWorkEmail } from 'src/utils/is-work-email';
|
import { isWorkEmail } from 'src/utils/is-work-email';
|
||||||
@ -58,6 +61,8 @@ export class SignInUpService {
|
|||||||
private readonly environmentService: EnvironmentService,
|
private readonly environmentService: EnvironmentService,
|
||||||
private readonly domainManagerService: DomainManagerService,
|
private readonly domainManagerService: DomainManagerService,
|
||||||
private readonly userService: UserService,
|
private readonly userService: UserService,
|
||||||
|
private readonly userRoleService: UserRoleService,
|
||||||
|
private readonly featureFlagService: FeatureFlagService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async computeParamsForNewUser(
|
async computeParamsForNewUser(
|
||||||
@ -256,10 +261,11 @@ export class SignInUpService {
|
|||||||
)
|
)
|
||||||
: params.userData.existingUser;
|
: params.userData.existingUser;
|
||||||
|
|
||||||
const updatedUser = await this.userWorkspaceService.addUserToWorkspace(
|
const { user: updatedUser, userWorkspace } =
|
||||||
currentUser,
|
await this.userWorkspaceService.addUserToWorkspace(
|
||||||
params.workspace,
|
currentUser,
|
||||||
);
|
params.workspace,
|
||||||
|
);
|
||||||
|
|
||||||
const user = Object.assign(currentUser, updatedUser);
|
const user = Object.assign(currentUser, updatedUser);
|
||||||
|
|
||||||
@ -267,6 +273,19 @@ export class SignInUpService {
|
|||||||
await this.activateOnboardingForUser(user, params.workspace);
|
await this.activateOnboardingForUser(user, params.workspace);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const isPermissionsEnabled = await this.featureFlagService.isFeatureEnabled(
|
||||||
|
FeatureFlagKey.IsPermissionsEnabled,
|
||||||
|
params.workspace.id,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (isPermissionsEnabled && params.workspace.defaultRoleId) {
|
||||||
|
await this.userRoleService.assignRoleToUserWorkspace({
|
||||||
|
workspaceId: params.workspace.id,
|
||||||
|
userWorkspaceId: userWorkspace.id,
|
||||||
|
roleId: params.workspace.defaultRoleId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -113,13 +113,13 @@ export class UserWorkspaceService extends TypeOrmQueryService<UserWorkspace> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async addUserToWorkspace(user: User, workspace: Workspace) {
|
async addUserToWorkspace(user: User, workspace: Workspace) {
|
||||||
const userWorkspaceExists = await this.checkUserWorkspaceExists(
|
let userWorkspace = await this.checkUserWorkspaceExists(
|
||||||
user.id,
|
user.id,
|
||||||
workspace.id,
|
workspace.id,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!userWorkspaceExists) {
|
if (!userWorkspace) {
|
||||||
await this.create(user.id, workspace.id);
|
userWorkspace = await this.create(user.id, workspace.id);
|
||||||
|
|
||||||
await this.createWorkspaceMember(workspace.id, user);
|
await this.createWorkspaceMember(workspace.id, user);
|
||||||
}
|
}
|
||||||
@ -129,7 +129,10 @@ export class UserWorkspaceService extends TypeOrmQueryService<UserWorkspace> {
|
|||||||
user.email,
|
user.email,
|
||||||
);
|
);
|
||||||
|
|
||||||
return user;
|
return {
|
||||||
|
user,
|
||||||
|
userWorkspace,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public async getUserCount(workspaceId: string): Promise<number | undefined> {
|
public async getUserCount(workspaceId: string): Promise<number | undefined> {
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import {
|
|||||||
IsNotIn,
|
IsNotIn,
|
||||||
IsOptional,
|
IsOptional,
|
||||||
IsString,
|
IsString,
|
||||||
|
IsUUID,
|
||||||
Matches,
|
Matches,
|
||||||
} from 'class-validator';
|
} from 'class-validator';
|
||||||
|
|
||||||
@ -184,4 +185,9 @@ export class UpdateWorkspaceInput {
|
|||||||
@IsBoolean()
|
@IsBoolean()
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
isPasswordAuthEnabled?: boolean;
|
isPasswordAuthEnabled?: boolean;
|
||||||
|
|
||||||
|
@Field({ nullable: true })
|
||||||
|
@IsUUID()
|
||||||
|
@IsOptional()
|
||||||
|
defaultRoleId?: string;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -21,6 +21,7 @@ import { KeyValuePair } from 'src/engine/core-modules/key-value-pair/key-value-p
|
|||||||
import { PostgresCredentials } from 'src/engine/core-modules/postgres-credentials/postgres-credentials.entity';
|
import { PostgresCredentials } from 'src/engine/core-modules/postgres-credentials/postgres-credentials.entity';
|
||||||
import { WorkspaceSSOIdentityProvider } from 'src/engine/core-modules/sso/workspace-sso-identity-provider.entity';
|
import { WorkspaceSSOIdentityProvider } from 'src/engine/core-modules/sso/workspace-sso-identity-provider.entity';
|
||||||
import { UserWorkspace } from 'src/engine/core-modules/user-workspace/user-workspace.entity';
|
import { UserWorkspace } from 'src/engine/core-modules/user-workspace/user-workspace.entity';
|
||||||
|
import { RoleDTO } from 'src/engine/metadata-modules/role/dtos/role.dto';
|
||||||
|
|
||||||
registerEnumType(WorkspaceActivationStatus, {
|
registerEnumType(WorkspaceActivationStatus, {
|
||||||
name: 'WorkspaceActivationStatus',
|
name: 'WorkspaceActivationStatus',
|
||||||
@ -151,6 +152,12 @@ export class Workspace {
|
|||||||
@Column({ default: false })
|
@Column({ default: false })
|
||||||
isCustomDomainEnabled: boolean;
|
isCustomDomainEnabled: boolean;
|
||||||
|
|
||||||
|
@Column({ nullable: true, type: 'uuid' })
|
||||||
|
defaultRoleId: string | null;
|
||||||
|
|
||||||
|
@Field(() => RoleDTO, { nullable: true })
|
||||||
|
defaultRole: RoleDTO | null;
|
||||||
|
|
||||||
@Field(() => String, { nullable: true })
|
@Field(() => String, { nullable: true })
|
||||||
@Column({ type: 'varchar', nullable: true })
|
@Column({ type: 'varchar', nullable: true })
|
||||||
version: string | null;
|
version: string | null;
|
||||||
|
|||||||
@ -20,6 +20,7 @@ import { WorkspaceWorkspaceMemberListener } from 'src/engine/core-modules/worksp
|
|||||||
import { WorkspaceResolver } from 'src/engine/core-modules/workspace/workspace.resolver';
|
import { WorkspaceResolver } from 'src/engine/core-modules/workspace/workspace.resolver';
|
||||||
import { DataSourceModule } from 'src/engine/metadata-modules/data-source/data-source.module';
|
import { DataSourceModule } from 'src/engine/metadata-modules/data-source/data-source.module';
|
||||||
import { PermissionsModule } from 'src/engine/metadata-modules/permissions/permissions.module';
|
import { PermissionsModule } from 'src/engine/metadata-modules/permissions/permissions.module';
|
||||||
|
import { RoleModule } from 'src/engine/metadata-modules/role/role.module';
|
||||||
import { WorkspaceMetadataCacheModule } from 'src/engine/metadata-modules/workspace-metadata-cache/workspace-metadata-cache.module';
|
import { WorkspaceMetadataCacheModule } from 'src/engine/metadata-modules/workspace-metadata-cache/workspace-metadata-cache.module';
|
||||||
import { WorkspaceCacheStorageModule } from 'src/engine/workspace-cache-storage/workspace-cache-storage.module';
|
import { WorkspaceCacheStorageModule } from 'src/engine/workspace-cache-storage/workspace-cache-storage.module';
|
||||||
import { WorkspaceManagerModule } from 'src/engine/workspace-manager/workspace-manager.module';
|
import { WorkspaceManagerModule } from 'src/engine/workspace-manager/workspace-manager.module';
|
||||||
@ -53,6 +54,7 @@ import { WorkspaceService } from './services/workspace.service';
|
|||||||
TypeORMModule,
|
TypeORMModule,
|
||||||
PermissionsModule,
|
PermissionsModule,
|
||||||
WorkspaceCacheStorageModule,
|
WorkspaceCacheStorageModule,
|
||||||
|
RoleModule,
|
||||||
],
|
],
|
||||||
services: [WorkspaceService],
|
services: [WorkspaceService],
|
||||||
resolvers: workspaceAutoResolverOpts,
|
resolvers: workspaceAutoResolverOpts,
|
||||||
|
|||||||
@ -48,6 +48,8 @@ import { UserAuthGuard } from 'src/engine/guards/user-auth.guard';
|
|||||||
import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard';
|
import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard';
|
||||||
import { SettingsPermissions } from 'src/engine/metadata-modules/permissions/constants/settings-permissions.constants';
|
import { SettingsPermissions } from 'src/engine/metadata-modules/permissions/constants/settings-permissions.constants';
|
||||||
import { PermissionsGraphqlApiExceptionFilter } from 'src/engine/metadata-modules/permissions/utils/permissions-graphql-api-exception.filter';
|
import { PermissionsGraphqlApiExceptionFilter } from 'src/engine/metadata-modules/permissions/utils/permissions-graphql-api-exception.filter';
|
||||||
|
import { RoleDTO } from 'src/engine/metadata-modules/role/dtos/role.dto';
|
||||||
|
import { RoleService } from 'src/engine/metadata-modules/role/role.service';
|
||||||
import { GraphqlValidationExceptionFilter } from 'src/filters/graphql-validation-exception.filter';
|
import { GraphqlValidationExceptionFilter } from 'src/filters/graphql-validation-exception.filter';
|
||||||
import { streamToBuffer } from 'src/utils/stream-to-buffer';
|
import { streamToBuffer } from 'src/utils/stream-to-buffer';
|
||||||
|
|
||||||
@ -70,6 +72,7 @@ export class WorkspaceResolver {
|
|||||||
private readonly fileService: FileService,
|
private readonly fileService: FileService,
|
||||||
private readonly billingSubscriptionService: BillingSubscriptionService,
|
private readonly billingSubscriptionService: BillingSubscriptionService,
|
||||||
private readonly featureFlagService: FeatureFlagService,
|
private readonly featureFlagService: FeatureFlagService,
|
||||||
|
private readonly roleService: RoleService,
|
||||||
@InjectRepository(BillingSubscription, 'core')
|
@InjectRepository(BillingSubscription, 'core')
|
||||||
private readonly billingSubscriptionRepository: Repository<BillingSubscription>,
|
private readonly billingSubscriptionRepository: Repository<BillingSubscription>,
|
||||||
) {}
|
) {}
|
||||||
@ -191,6 +194,20 @@ export class WorkspaceResolver {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ResolveField(() => RoleDTO, { nullable: true })
|
||||||
|
async defaultRole(@Parent() workspace: Workspace): Promise<RoleDTO | null> {
|
||||||
|
if (!workspace.defaultRoleId) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const role = await this.roleService.getRoleById(
|
||||||
|
workspace.defaultRoleId,
|
||||||
|
workspace.id,
|
||||||
|
);
|
||||||
|
|
||||||
|
return role;
|
||||||
|
}
|
||||||
|
|
||||||
@ResolveField(() => BillingSubscription, { nullable: true })
|
@ResolveField(() => BillingSubscription, { nullable: true })
|
||||||
async currentBillingSubscription(
|
async currentBillingSubscription(
|
||||||
@Parent() workspace: Workspace,
|
@Parent() workspace: Workspace,
|
||||||
|
|||||||
@ -34,23 +34,7 @@ export class RoleResolver {
|
|||||||
|
|
||||||
@Query(() => [RoleDTO])
|
@Query(() => [RoleDTO])
|
||||||
async getRoles(@AuthWorkspace() workspace: Workspace): Promise<RoleDTO[]> {
|
async getRoles(@AuthWorkspace() workspace: Workspace): Promise<RoleDTO[]> {
|
||||||
const roles = await this.roleService.getWorkspaceRoles(workspace.id);
|
return this.roleService.getWorkspaceRoles(workspace.id);
|
||||||
|
|
||||||
return roles.map((role) => ({
|
|
||||||
id: role.id,
|
|
||||||
label: role.label,
|
|
||||||
description: role.description,
|
|
||||||
workspaceId: role.workspaceId,
|
|
||||||
createdAt: role.createdAt,
|
|
||||||
updatedAt: role.updatedAt,
|
|
||||||
isEditable: role.isEditable,
|
|
||||||
userWorkspaceRoles: role.userWorkspaceRoles,
|
|
||||||
canUpdateAllSettings: role.canUpdateAllSettings,
|
|
||||||
canReadAllObjectRecords: role.canReadAllObjectRecords,
|
|
||||||
canUpdateAllObjectRecords: role.canUpdateAllObjectRecords,
|
|
||||||
canSoftDeleteAllObjectRecords: role.canSoftDeleteAllObjectRecords,
|
|
||||||
canDestroyAllObjectRecords: role.canDestroyAllObjectRecords,
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Mutation(() => WorkspaceMember)
|
@Mutation(() => WorkspaceMember)
|
||||||
|
|||||||
@ -21,6 +21,19 @@ export class RoleService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async getRoleById(
|
||||||
|
id: string,
|
||||||
|
workspaceId: string,
|
||||||
|
): Promise<RoleEntity | null> {
|
||||||
|
return this.roleRepository.findOne({
|
||||||
|
where: {
|
||||||
|
id,
|
||||||
|
workspaceId,
|
||||||
|
},
|
||||||
|
relations: ['userWorkspaceRoles'],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public async createAdminRole({
|
public async createAdminRole({
|
||||||
workspaceId,
|
workspaceId,
|
||||||
}: {
|
}: {
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import { TypeOrmModule } from '@nestjs/typeorm';
|
|||||||
|
|
||||||
import { FeatureFlagModule } from 'src/engine/core-modules/feature-flag/feature-flag.module';
|
import { FeatureFlagModule } from 'src/engine/core-modules/feature-flag/feature-flag.module';
|
||||||
import { UserWorkspace } from 'src/engine/core-modules/user-workspace/user-workspace.entity';
|
import { UserWorkspace } from 'src/engine/core-modules/user-workspace/user-workspace.entity';
|
||||||
|
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||||
import { DataSourceModule } from 'src/engine/metadata-modules/data-source/data-source.module';
|
import { DataSourceModule } from 'src/engine/metadata-modules/data-source/data-source.module';
|
||||||
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||||
import { ObjectMetadataModule } from 'src/engine/metadata-modules/object-metadata/object-metadata.module';
|
import { ObjectMetadataModule } from 'src/engine/metadata-modules/object-metadata/object-metadata.module';
|
||||||
@ -29,7 +30,7 @@ import { WorkspaceManagerService } from './workspace-manager.service';
|
|||||||
WorkspaceHealthModule,
|
WorkspaceHealthModule,
|
||||||
FeatureFlagModule,
|
FeatureFlagModule,
|
||||||
PermissionsModule,
|
PermissionsModule,
|
||||||
TypeOrmModule.forFeature([UserWorkspace], 'core'),
|
TypeOrmModule.forFeature([UserWorkspace, Workspace], 'core'),
|
||||||
RoleModule,
|
RoleModule,
|
||||||
UserRoleModule,
|
UserRoleModule,
|
||||||
TypeOrmModule.forFeature(
|
TypeOrmModule.forFeature(
|
||||||
|
|||||||
@ -11,6 +11,7 @@ import {
|
|||||||
import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum';
|
import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum';
|
||||||
import { FeatureFlagService } from 'src/engine/core-modules/feature-flag/services/feature-flag.service';
|
import { FeatureFlagService } from 'src/engine/core-modules/feature-flag/services/feature-flag.service';
|
||||||
import { UserWorkspace } from 'src/engine/core-modules/user-workspace/user-workspace.entity';
|
import { UserWorkspace } from 'src/engine/core-modules/user-workspace/user-workspace.entity';
|
||||||
|
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||||
import { DataSourceEntity } from 'src/engine/metadata-modules/data-source/data-source.entity';
|
import { DataSourceEntity } from 'src/engine/metadata-modules/data-source/data-source.entity';
|
||||||
import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service';
|
import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service';
|
||||||
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||||
@ -51,6 +52,8 @@ export class WorkspaceManagerService {
|
|||||||
private readonly roleService: RoleService,
|
private readonly roleService: RoleService,
|
||||||
private readonly userRoleService: UserRoleService,
|
private readonly userRoleService: UserRoleService,
|
||||||
private readonly featureFlagService: FeatureFlagService,
|
private readonly featureFlagService: FeatureFlagService,
|
||||||
|
@InjectRepository(Workspace, 'core')
|
||||||
|
private readonly workspaceRepository: Repository<Workspace>,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -292,6 +295,14 @@ export class WorkspaceManagerService {
|
|||||||
userWorkspaceId: userWorkspace.id,
|
userWorkspaceId: userWorkspace.id,
|
||||||
roleId: adminRole.id,
|
roleId: adminRole.id,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const memberRole = await this.roleService.createMemberRole({
|
||||||
|
workspaceId,
|
||||||
|
});
|
||||||
|
|
||||||
|
await this.workspaceRepository.update(workspaceId, {
|
||||||
|
defaultRoleId: memberRole.id,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private async initPermissionsDev(workspaceId: string) {
|
private async initPermissionsDev(workspaceId: string) {
|
||||||
@ -336,6 +347,10 @@ export class WorkspaceManagerService {
|
|||||||
workspaceId,
|
workspaceId,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
await this.workspaceRepository.update(workspaceId, {
|
||||||
|
defaultRoleId: memberRole.id,
|
||||||
|
});
|
||||||
|
|
||||||
if (memberUserWorkspaceId) {
|
if (memberUserWorkspaceId) {
|
||||||
await this.userRoleService.assignRoleToUserWorkspace({
|
await this.userRoleService.assignRoleToUserWorkspace({
|
||||||
workspaceId,
|
workspaceId,
|
||||||
|
|||||||
@ -12,9 +12,9 @@ export {
|
|||||||
IconArrowDown,
|
IconArrowDown,
|
||||||
IconArrowLeft,
|
IconArrowLeft,
|
||||||
IconArrowRight,
|
IconArrowRight,
|
||||||
|
IconArrowsVertical,
|
||||||
IconArrowUp,
|
IconArrowUp,
|
||||||
IconArrowUpRight,
|
IconArrowUpRight,
|
||||||
IconArrowsVertical,
|
|
||||||
IconAt,
|
IconAt,
|
||||||
IconBaselineDensitySmall,
|
IconBaselineDensitySmall,
|
||||||
IconBell,
|
IconBell,
|
||||||
@ -41,8 +41,8 @@ export {
|
|||||||
IconChevronDown,
|
IconChevronDown,
|
||||||
IconChevronLeft,
|
IconChevronLeft,
|
||||||
IconChevronRight,
|
IconChevronRight,
|
||||||
IconChevronUp,
|
|
||||||
IconChevronsRight,
|
IconChevronsRight,
|
||||||
|
IconChevronUp,
|
||||||
IconCircleDot,
|
IconCircleDot,
|
||||||
IconCircleOff,
|
IconCircleOff,
|
||||||
IconCirclePlus,
|
IconCirclePlus,
|
||||||
@ -261,6 +261,7 @@ export {
|
|||||||
IconUpload,
|
IconUpload,
|
||||||
IconUser,
|
IconUser,
|
||||||
IconUserCircle,
|
IconUserCircle,
|
||||||
|
IconUserPin,
|
||||||
IconUserPlus,
|
IconUserPlus,
|
||||||
IconUsers,
|
IconUsers,
|
||||||
IconVariable,
|
IconVariable,
|
||||||
|
|||||||
@ -102,7 +102,7 @@ const StyledInput = styled.input<InputProps>`
|
|||||||
checkboxSize === CheckboxSize.Large ? '18px' : '12px'};
|
checkboxSize === CheckboxSize.Large ? '18px' : '12px'};
|
||||||
background: ${({ theme, indeterminate, isChecked, disabled }) =>
|
background: ${({ theme, indeterminate, isChecked, disabled }) =>
|
||||||
disabled && isChecked
|
disabled && isChecked
|
||||||
? theme.color.blue30
|
? theme.color.blue
|
||||||
: indeterminate || isChecked
|
: indeterminate || isChecked
|
||||||
? theme.color.blue
|
? theme.color.blue
|
||||||
: 'transparent'};
|
: 'transparent'};
|
||||||
@ -114,8 +114,10 @@ const StyledInput = styled.input<InputProps>`
|
|||||||
disabled,
|
disabled,
|
||||||
}) => {
|
}) => {
|
||||||
switch (true) {
|
switch (true) {
|
||||||
|
case isChecked:
|
||||||
|
return theme.color.blue;
|
||||||
case disabled:
|
case disabled:
|
||||||
return isChecked ? theme.color.blue30 : theme.font.color.extraLight;
|
return theme.background.transparent.medium;
|
||||||
case indeterminate || isChecked:
|
case indeterminate || isChecked:
|
||||||
return theme.color.blue;
|
return theme.color.blue;
|
||||||
case variant === CheckboxVariant.Primary:
|
case variant === CheckboxVariant.Primary:
|
||||||
@ -150,7 +152,7 @@ const StyledInput = styled.input<InputProps>`
|
|||||||
height: var(--size);
|
height: var(--size);
|
||||||
left: var(--padding);
|
left: var(--padding);
|
||||||
position: absolute;
|
position: absolute;
|
||||||
stroke: ${({ theme }) => theme.grayScale.gray0};
|
stroke: ${({ theme }) => theme.font.color.inverted};
|
||||||
top: var(--padding);
|
top: var(--padding);
|
||||||
width: var(--size);
|
width: var(--size);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user