[WIP] Whole FE migrated (#2517)
* Wip * WIP * Removed concole log * Add relations to workspace init (#2511) * Add relations to workspace init * remove logs * update prefill * add missing isSystem * comment relation fields * Migrate v2 core models to graphql schema (#2509) * migrate v2 core models to graphql schema * Migrate to new workspace member schema * Continue work * migrated-main * Finished accountOwner nested field integration on companies * Introduce bug * Fix --------- Co-authored-by: Lucas Bordeau <bordeau.lucas@gmail.com> Co-authored-by: Weiko <corentin@twenty.com>
This commit is contained in:
@ -19,7 +19,7 @@ const documents = {
|
||||
"\n mutation UpdateOneObjectMetadataItem(\n $idToUpdate: ID!\n $updatePayload: UpdateObjectInput!\n ) {\n updateOneObject(input: { id: $idToUpdate, update: $updatePayload }) {\n id\n dataSourceId\n nameSingular\n namePlural\n labelSingular\n labelPlural\n description\n icon\n isCustom\n isActive\n createdAt\n updatedAt\n }\n }\n": types.UpdateOneObjectMetadataItemDocument,
|
||||
"\n mutation DeleteOneObjectMetadataItem($idToDelete: ID!) {\n deleteOneObject(input: { id: $idToDelete }) {\n id\n dataSourceId\n nameSingular\n namePlural\n labelSingular\n labelPlural\n description\n icon\n isCustom\n isActive\n createdAt\n updatedAt\n }\n }\n": types.DeleteOneObjectMetadataItemDocument,
|
||||
"\n mutation DeleteOneFieldMetadataItem($idToDelete: ID!) {\n deleteOneField(input: { id: $idToDelete }) {\n id\n type\n name\n label\n description\n icon\n placeholder\n isCustom\n isActive\n isNullable\n createdAt\n updatedAt\n }\n }\n": types.DeleteOneFieldMetadataItemDocument,
|
||||
"\n query ObjectMetadataItems($filter: objectFilter) {\n objects(paging: { first: 1000 }, filter: $filter) {\n edges {\n node {\n id\n dataSourceId\n nameSingular\n namePlural\n labelSingular\n labelPlural\n description\n icon\n isCustom\n isActive\n isSystem\n createdAt\n updatedAt\n fields(paging: { first: 1000 }) {\n edges {\n node {\n id\n type\n name\n label\n description\n icon\n placeholder\n isCustom\n isActive\n isNullable\n createdAt\n updatedAt\n fromRelationMetadata {\n id\n relationType\n }\n toRelationMetadata {\n id\n relationType\n }\n }\n }\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n totalCount\n }\n }\n }\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n totalCount\n }\n }\n": types.ObjectMetadataItemsDocument,
|
||||
"\n query ObjectMetadataItems($filter: objectFilter) {\n objects(paging: { first: 1000 }, filter: $filter) {\n edges {\n node {\n id\n dataSourceId\n nameSingular\n namePlural\n labelSingular\n labelPlural\n description\n icon\n isCustom\n isActive\n isSystem\n createdAt\n updatedAt\n fields(paging: { first: 1000 }) {\n edges {\n node {\n id\n type\n name\n label\n description\n icon\n isCustom\n isActive\n isNullable\n createdAt\n updatedAt\n fromRelationMetadata {\n id\n relationType\n toObjectMetadata {\n id\n dataSourceId\n nameSingular\n namePlural\n }\n fromObjectMetadata {\n id\n dataSourceId\n nameSingular\n namePlural\n }\n }\n toRelationMetadata {\n id\n relationType\n toObjectMetadata {\n id\n dataSourceId\n nameSingular\n namePlural\n }\n fromObjectMetadata {\n id\n dataSourceId\n nameSingular\n namePlural\n }\n }\n }\n }\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n totalCount\n }\n }\n }\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n totalCount\n }\n }\n": types.ObjectMetadataItemsDocument,
|
||||
};
|
||||
|
||||
/**
|
||||
@ -63,7 +63,7 @@ export function graphql(source: "\n mutation DeleteOneFieldMetadataItem($idToDe
|
||||
/**
|
||||
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||
*/
|
||||
export function graphql(source: "\n query ObjectMetadataItems($filter: objectFilter) {\n objects(paging: { first: 1000 }, filter: $filter) {\n edges {\n node {\n id\n dataSourceId\n nameSingular\n namePlural\n labelSingular\n labelPlural\n description\n icon\n isCustom\n isActive\n isSystem\n createdAt\n updatedAt\n fields(paging: { first: 1000 }) {\n edges {\n node {\n id\n type\n name\n label\n description\n icon\n placeholder\n isCustom\n isActive\n isNullable\n createdAt\n updatedAt\n fromRelationMetadata {\n id\n relationType\n }\n toRelationMetadata {\n id\n relationType\n }\n }\n }\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n totalCount\n }\n }\n }\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n totalCount\n }\n }\n"): (typeof documents)["\n query ObjectMetadataItems($filter: objectFilter) {\n objects(paging: { first: 1000 }, filter: $filter) {\n edges {\n node {\n id\n dataSourceId\n nameSingular\n namePlural\n labelSingular\n labelPlural\n description\n icon\n isCustom\n isActive\n isSystem\n createdAt\n updatedAt\n fields(paging: { first: 1000 }) {\n edges {\n node {\n id\n type\n name\n label\n description\n icon\n placeholder\n isCustom\n isActive\n isNullable\n createdAt\n updatedAt\n fromRelationMetadata {\n id\n relationType\n }\n toRelationMetadata {\n id\n relationType\n }\n }\n }\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n totalCount\n }\n }\n }\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n totalCount\n }\n }\n"];
|
||||
export function graphql(source: "\n query ObjectMetadataItems($filter: objectFilter) {\n objects(paging: { first: 1000 }, filter: $filter) {\n edges {\n node {\n id\n dataSourceId\n nameSingular\n namePlural\n labelSingular\n labelPlural\n description\n icon\n isCustom\n isActive\n isSystem\n createdAt\n updatedAt\n fields(paging: { first: 1000 }) {\n edges {\n node {\n id\n type\n name\n label\n description\n icon\n isCustom\n isActive\n isNullable\n createdAt\n updatedAt\n fromRelationMetadata {\n id\n relationType\n toObjectMetadata {\n id\n dataSourceId\n nameSingular\n namePlural\n }\n fromObjectMetadata {\n id\n dataSourceId\n nameSingular\n namePlural\n }\n }\n toRelationMetadata {\n id\n relationType\n toObjectMetadata {\n id\n dataSourceId\n nameSingular\n namePlural\n }\n fromObjectMetadata {\n id\n dataSourceId\n nameSingular\n namePlural\n }\n }\n }\n }\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n totalCount\n }\n }\n }\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n totalCount\n }\n }\n"): (typeof documents)["\n query ObjectMetadataItems($filter: objectFilter) {\n objects(paging: { first: 1000 }, filter: $filter) {\n edges {\n node {\n id\n dataSourceId\n nameSingular\n namePlural\n labelSingular\n labelPlural\n description\n icon\n isCustom\n isActive\n isSystem\n createdAt\n updatedAt\n fields(paging: { first: 1000 }) {\n edges {\n node {\n id\n type\n name\n label\n description\n icon\n isCustom\n isActive\n isNullable\n createdAt\n updatedAt\n fromRelationMetadata {\n id\n relationType\n toObjectMetadata {\n id\n dataSourceId\n nameSingular\n namePlural\n }\n fromObjectMetadata {\n id\n dataSourceId\n nameSingular\n namePlural\n }\n }\n toRelationMetadata {\n id\n relationType\n toObjectMetadata {\n id\n dataSourceId\n nameSingular\n namePlural\n }\n fromObjectMetadata {\n id\n dataSourceId\n nameSingular\n namePlural\n }\n }\n }\n }\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n totalCount\n }\n }\n }\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n totalCount\n }\n }\n"];
|
||||
|
||||
export function graphql(source: string) {
|
||||
return (documents as any)[source] ?? {};
|
||||
|
||||
File diff suppressed because one or more lines are too long
@ -16,6 +16,7 @@ export type Scalars = {
|
||||
ConnectionCursor: any;
|
||||
DateTime: string;
|
||||
JSON: any;
|
||||
JSONObject: any;
|
||||
Upload: any;
|
||||
};
|
||||
|
||||
@ -966,6 +967,15 @@ export type CompanyWhereUniqueInput = {
|
||||
id?: InputMaybe<Scalars['String']>;
|
||||
};
|
||||
|
||||
export type CreateOneRefreshTokenV2Input = {
|
||||
/** The record to create */
|
||||
refreshTokenV2: CreateRefreshTokenInput;
|
||||
};
|
||||
|
||||
export type CreateRefreshTokenInput = {
|
||||
expiresAt: Scalars['DateTime'];
|
||||
};
|
||||
|
||||
export enum Currency {
|
||||
Aed = 'AED',
|
||||
Afn = 'AFN',
|
||||
@ -1398,6 +1408,8 @@ export type Mutation = {
|
||||
createOnePerson: Person;
|
||||
createOnePipelineProgress: PipelineProgress;
|
||||
createOnePipelineStage: PipelineStage;
|
||||
createOneRefreshTokenV2: RefreshTokenV2;
|
||||
createOneRelation: Relation;
|
||||
createOneWebHook: WebHook;
|
||||
deleteCurrentWorkspace: Workspace;
|
||||
deleteFavorite: Favorite;
|
||||
@ -1410,6 +1422,7 @@ export type Mutation = {
|
||||
deleteOnePipelineStage: PipelineStage;
|
||||
deleteOneWebHook: WebHook;
|
||||
deleteUserAccount: User;
|
||||
deleteUserV2: UserV2;
|
||||
deleteWorkspaceMember: WorkspaceMember;
|
||||
impersonate: Verify;
|
||||
renewToken: AuthTokens;
|
||||
@ -1430,6 +1443,7 @@ export type Mutation = {
|
||||
uploadImage: Scalars['String'];
|
||||
uploadPersonPicture: Scalars['String'];
|
||||
uploadProfilePicture: Scalars['String'];
|
||||
uploadProfilePictureV2: Scalars['String'];
|
||||
uploadWorkspaceLogo: Scalars['String'];
|
||||
verify: Verify;
|
||||
};
|
||||
@ -1515,6 +1529,11 @@ export type MutationCreateOnePipelineStageArgs = {
|
||||
};
|
||||
|
||||
|
||||
export type MutationCreateOneRefreshTokenV2Args = {
|
||||
input: CreateOneRefreshTokenV2Input;
|
||||
};
|
||||
|
||||
|
||||
export type MutationCreateOneWebHookArgs = {
|
||||
data: WebHookCreateInput;
|
||||
};
|
||||
@ -1660,6 +1679,11 @@ export type MutationUploadProfilePictureArgs = {
|
||||
};
|
||||
|
||||
|
||||
export type MutationUploadProfilePictureV2Args = {
|
||||
file: Scalars['Upload'];
|
||||
};
|
||||
|
||||
|
||||
export type MutationUploadWorkspaceLogoArgs = {
|
||||
file: Scalars['Upload'];
|
||||
};
|
||||
@ -2409,6 +2433,7 @@ export type Query = {
|
||||
checkWorkspaceInviteHashIsValid: WorkspaceInviteHashValid;
|
||||
clientConfig: ClientConfig;
|
||||
currentUser: User;
|
||||
currentUserV2: UserV2;
|
||||
currentWorkspace: Workspace;
|
||||
field: Field;
|
||||
fields: FieldConnection;
|
||||
@ -2428,6 +2453,8 @@ export type Query = {
|
||||
findWorkspaceFromInviteHash: Workspace;
|
||||
object: Object;
|
||||
objects: ObjectConnection;
|
||||
relation: Relation;
|
||||
relations: RelationConnection;
|
||||
};
|
||||
|
||||
|
||||
@ -2560,32 +2587,6 @@ export enum QueryMode {
|
||||
Insensitive = 'insensitive'
|
||||
}
|
||||
|
||||
export type RefreshToken = {
|
||||
__typename?: 'RefreshToken';
|
||||
createdAt: Scalars['DateTime'];
|
||||
expiresAt: Scalars['DateTime'];
|
||||
id: Scalars['ID'];
|
||||
updatedAt: Scalars['DateTime'];
|
||||
};
|
||||
|
||||
export type RefreshTokenConnection = {
|
||||
__typename?: 'RefreshTokenConnection';
|
||||
/** Array of edges. */
|
||||
edges: Array<RefreshTokenEdge>;
|
||||
/** Paging information */
|
||||
pageInfo: PageInfo;
|
||||
/** Fetch total count of records */
|
||||
totalCount: Scalars['Int'];
|
||||
};
|
||||
|
||||
export type RefreshTokenEdge = {
|
||||
__typename?: 'RefreshTokenEdge';
|
||||
/** Cursor for this node. */
|
||||
cursor: Scalars['ConnectionCursor'];
|
||||
/** The node containing the RefreshToken */
|
||||
node: RefreshToken;
|
||||
};
|
||||
|
||||
export type RelationConnection = {
|
||||
__typename?: 'RelationConnection';
|
||||
/** Array of edges. */
|
||||
@ -2644,15 +2645,6 @@ export type Support = {
|
||||
supportFrontChatId?: Maybe<Scalars['String']>;
|
||||
};
|
||||
|
||||
export type TUser = {
|
||||
__typename?: 'TUser';
|
||||
email: Scalars['String'];
|
||||
emailVerified: Scalars['Boolean'];
|
||||
firstName?: Maybe<Scalars['String']>;
|
||||
id: Scalars['ID'];
|
||||
lastName?: Maybe<Scalars['String']>;
|
||||
};
|
||||
|
||||
export type Telemetry = {
|
||||
__typename?: 'Telemetry';
|
||||
anonymizationEnabled: Scalars['Boolean'];
|
||||
@ -2669,6 +2661,7 @@ export type User = {
|
||||
comments?: Maybe<Array<Comment>>;
|
||||
companies?: Maybe<Array<Company>>;
|
||||
createdAt: Scalars['DateTime'];
|
||||
defaultWorkspaceId?: Maybe<Scalars['String']>;
|
||||
disabled: Scalars['Boolean'];
|
||||
displayName: Scalars['String'];
|
||||
email: Scalars['String'];
|
||||
@ -2680,11 +2673,8 @@ export type User = {
|
||||
locale: Scalars['String'];
|
||||
metadata?: Maybe<Scalars['JSON']>;
|
||||
phoneNumber?: Maybe<Scalars['String']>;
|
||||
settings: UserSettings;
|
||||
settingsId: Scalars['String'];
|
||||
supportUserHash?: Maybe<Scalars['String']>;
|
||||
updatedAt: Scalars['DateTime'];
|
||||
workspaceMember?: Maybe<WorkspaceMember>;
|
||||
};
|
||||
|
||||
export type UserCreateNestedOneWithoutAssignedActivitiesInput = {
|
||||
@ -2717,6 +2707,7 @@ export type UserOrderByWithRelationInput = {
|
||||
comments?: InputMaybe<CommentOrderByRelationAggregateInput>;
|
||||
companies?: InputMaybe<CompanyOrderByRelationAggregateInput>;
|
||||
createdAt?: InputMaybe<SortOrder>;
|
||||
defaultWorkspaceId?: InputMaybe<SortOrder>;
|
||||
disabled?: InputMaybe<SortOrder>;
|
||||
email?: InputMaybe<SortOrder>;
|
||||
emailVerified?: InputMaybe<SortOrder>;
|
||||
@ -2727,8 +2718,6 @@ export type UserOrderByWithRelationInput = {
|
||||
locale?: InputMaybe<SortOrder>;
|
||||
metadata?: InputMaybe<SortOrder>;
|
||||
phoneNumber?: InputMaybe<SortOrder>;
|
||||
settings?: InputMaybe<UserSettingsOrderByWithRelationInput>;
|
||||
settingsId?: InputMaybe<SortOrder>;
|
||||
updatedAt?: InputMaybe<SortOrder>;
|
||||
};
|
||||
|
||||
@ -2741,6 +2730,7 @@ export enum UserScalarFieldEnum {
|
||||
AvatarUrl = 'avatarUrl',
|
||||
CanImpersonate = 'canImpersonate',
|
||||
CreatedAt = 'createdAt',
|
||||
DefaultWorkspaceId = 'defaultWorkspaceId',
|
||||
DeletedAt = 'deletedAt',
|
||||
Disabled = 'disabled',
|
||||
Email = 'email',
|
||||
@ -2753,7 +2743,6 @@ export enum UserScalarFieldEnum {
|
||||
Metadata = 'metadata',
|
||||
PasswordHash = 'passwordHash',
|
||||
PhoneNumber = 'phoneNumber',
|
||||
SettingsId = 'settingsId',
|
||||
UpdatedAt = 'updatedAt'
|
||||
}
|
||||
|
||||
@ -2765,7 +2754,6 @@ export type UserSettings = {
|
||||
id: Scalars['ID'];
|
||||
locale: Scalars['String'];
|
||||
updatedAt: Scalars['DateTime'];
|
||||
user?: Maybe<User>;
|
||||
};
|
||||
|
||||
export type UserSettingsOrderByWithRelationInput = {
|
||||
@ -2775,7 +2763,6 @@ export type UserSettingsOrderByWithRelationInput = {
|
||||
id?: InputMaybe<SortOrder>;
|
||||
locale?: InputMaybe<SortOrder>;
|
||||
updatedAt?: InputMaybe<SortOrder>;
|
||||
user?: InputMaybe<UserOrderByWithRelationInput>;
|
||||
};
|
||||
|
||||
export type UserSettingsRelationFilter = {
|
||||
@ -2783,24 +2770,11 @@ export type UserSettingsRelationFilter = {
|
||||
isNot?: InputMaybe<UserSettingsWhereInput>;
|
||||
};
|
||||
|
||||
export type UserSettingsUpdateOneRequiredWithoutUserNestedInput = {
|
||||
update?: InputMaybe<UserSettingsUpdateWithoutUserInput>;
|
||||
};
|
||||
|
||||
export type UserSettingsUpdateOneWithoutWorkspaceMemberNestedInput = {
|
||||
connect?: InputMaybe<UserSettingsWhereUniqueInput>;
|
||||
disconnect?: InputMaybe<Scalars['Boolean']>;
|
||||
};
|
||||
|
||||
export type UserSettingsUpdateWithoutUserInput = {
|
||||
WorkspaceMember?: InputMaybe<WorkspaceMemberUpdateManyWithoutSettingsNestedInput>;
|
||||
colorScheme?: InputMaybe<ColorScheme>;
|
||||
createdAt?: InputMaybe<Scalars['DateTime']>;
|
||||
id?: InputMaybe<Scalars['String']>;
|
||||
locale?: InputMaybe<Scalars['String']>;
|
||||
updatedAt?: InputMaybe<Scalars['DateTime']>;
|
||||
};
|
||||
|
||||
export type UserSettingsWhereInput = {
|
||||
AND?: InputMaybe<Array<UserSettingsWhereInput>>;
|
||||
NOT?: InputMaybe<Array<UserSettingsWhereInput>>;
|
||||
@ -2811,7 +2785,6 @@ export type UserSettingsWhereInput = {
|
||||
id?: InputMaybe<StringFilter>;
|
||||
locale?: InputMaybe<StringFilter>;
|
||||
updatedAt?: InputMaybe<DateTimeFilter>;
|
||||
user?: InputMaybe<UserRelationFilter>;
|
||||
};
|
||||
|
||||
export type UserSettingsWhereUniqueInput = {
|
||||
@ -2827,6 +2800,7 @@ export type UserUpdateInput = {
|
||||
comments?: InputMaybe<CommentUpdateManyWithoutAuthorNestedInput>;
|
||||
companies?: InputMaybe<CompanyUpdateManyWithoutAccountOwnerNestedInput>;
|
||||
createdAt?: InputMaybe<Scalars['DateTime']>;
|
||||
defaultWorkspaceId?: InputMaybe<Scalars['String']>;
|
||||
disabled?: InputMaybe<Scalars['Boolean']>;
|
||||
email?: InputMaybe<Scalars['String']>;
|
||||
emailVerified?: InputMaybe<Scalars['Boolean']>;
|
||||
@ -2837,7 +2811,6 @@ export type UserUpdateInput = {
|
||||
locale?: InputMaybe<Scalars['String']>;
|
||||
metadata?: InputMaybe<Scalars['JSON']>;
|
||||
phoneNumber?: InputMaybe<Scalars['String']>;
|
||||
settings?: InputMaybe<UserSettingsUpdateOneRequiredWithoutUserNestedInput>;
|
||||
updatedAt?: InputMaybe<Scalars['DateTime']>;
|
||||
};
|
||||
|
||||
@ -2845,10 +2818,6 @@ export type UserUpdateOneRequiredWithoutAuthoredActivitiesNestedInput = {
|
||||
connect?: InputMaybe<UserWhereUniqueInput>;
|
||||
};
|
||||
|
||||
export type UserUpdateOneRequiredWithoutWorkspaceMemberNestedInput = {
|
||||
connect?: InputMaybe<UserWhereUniqueInput>;
|
||||
};
|
||||
|
||||
export type UserUpdateOneWithoutAssignedActivitiesNestedInput = {
|
||||
connect?: InputMaybe<UserWhereUniqueInput>;
|
||||
disconnect?: InputMaybe<Scalars['Boolean']>;
|
||||
@ -2871,6 +2840,7 @@ export type UserWhereInput = {
|
||||
comments?: InputMaybe<CommentListRelationFilter>;
|
||||
companies?: InputMaybe<CompanyListRelationFilter>;
|
||||
createdAt?: InputMaybe<DateTimeFilter>;
|
||||
defaultWorkspaceId?: InputMaybe<StringNullableFilter>;
|
||||
disabled?: InputMaybe<BoolFilter>;
|
||||
email?: InputMaybe<StringFilter>;
|
||||
emailVerified?: InputMaybe<BoolFilter>;
|
||||
@ -2881,15 +2851,12 @@ export type UserWhereInput = {
|
||||
locale?: InputMaybe<StringFilter>;
|
||||
metadata?: InputMaybe<JsonNullableFilter>;
|
||||
phoneNumber?: InputMaybe<StringNullableFilter>;
|
||||
settings?: InputMaybe<UserSettingsRelationFilter>;
|
||||
settingsId?: InputMaybe<StringFilter>;
|
||||
updatedAt?: InputMaybe<DateTimeFilter>;
|
||||
};
|
||||
|
||||
export type UserWhereUniqueInput = {
|
||||
email?: InputMaybe<Scalars['String']>;
|
||||
id?: InputMaybe<Scalars['String']>;
|
||||
settingsId?: InputMaybe<Scalars['String']>;
|
||||
};
|
||||
|
||||
export type Verify = {
|
||||
@ -2996,7 +2963,6 @@ export type WorkspaceMember = {
|
||||
settings?: Maybe<UserSettings>;
|
||||
settingsId?: Maybe<Scalars['String']>;
|
||||
updatedAt: Scalars['DateTime'];
|
||||
user: User;
|
||||
userId: Scalars['String'];
|
||||
workspace: Workspace;
|
||||
};
|
||||
@ -3040,7 +3006,6 @@ export type WorkspaceMemberOrderByWithRelationInput = {
|
||||
settings?: InputMaybe<UserSettingsOrderByWithRelationInput>;
|
||||
settingsId?: InputMaybe<SortOrder>;
|
||||
updatedAt?: InputMaybe<SortOrder>;
|
||||
user?: InputMaybe<UserOrderByWithRelationInput>;
|
||||
userId?: InputMaybe<SortOrder>;
|
||||
};
|
||||
|
||||
@ -3072,13 +3037,7 @@ export type WorkspaceMemberUpdateInput = {
|
||||
id?: InputMaybe<Scalars['String']>;
|
||||
settings?: InputMaybe<UserSettingsUpdateOneWithoutWorkspaceMemberNestedInput>;
|
||||
updatedAt?: InputMaybe<Scalars['DateTime']>;
|
||||
user?: InputMaybe<UserUpdateOneRequiredWithoutWorkspaceMemberNestedInput>;
|
||||
};
|
||||
|
||||
export type WorkspaceMemberUpdateManyWithoutSettingsNestedInput = {
|
||||
connect?: InputMaybe<Array<WorkspaceMemberWhereUniqueInput>>;
|
||||
disconnect?: InputMaybe<Array<WorkspaceMemberWhereUniqueInput>>;
|
||||
set?: InputMaybe<Array<WorkspaceMemberWhereUniqueInput>>;
|
||||
userId?: InputMaybe<Scalars['String']>;
|
||||
};
|
||||
|
||||
export type WorkspaceMemberUpdateManyWithoutWorkspaceNestedInput = {
|
||||
@ -3118,7 +3077,6 @@ export type WorkspaceMemberWhereInput = {
|
||||
settings?: InputMaybe<UserSettingsRelationFilter>;
|
||||
settingsId?: InputMaybe<StringNullableFilter>;
|
||||
updatedAt?: InputMaybe<DateTimeFilter>;
|
||||
user?: InputMaybe<UserRelationFilter>;
|
||||
userId?: InputMaybe<StringFilter>;
|
||||
};
|
||||
|
||||
@ -3207,6 +3165,22 @@ export type ObjectEdge = {
|
||||
node: Object;
|
||||
};
|
||||
|
||||
export type RefreshTokenV2 = {
|
||||
__typename?: 'refreshTokenV2';
|
||||
createdAt: Scalars['DateTime'];
|
||||
expiresAt: Scalars['DateTime'];
|
||||
id: Scalars['ID'];
|
||||
updatedAt: Scalars['DateTime'];
|
||||
};
|
||||
|
||||
export type RefreshTokenV2Edge = {
|
||||
__typename?: 'refreshTokenV2Edge';
|
||||
/** Cursor for this node. */
|
||||
cursor: Scalars['ConnectionCursor'];
|
||||
/** The node containing the refreshTokenV2 */
|
||||
node: RefreshTokenV2;
|
||||
};
|
||||
|
||||
export type Relation = {
|
||||
__typename?: 'relation';
|
||||
createdAt: Scalars['DateTime'];
|
||||
@ -3229,6 +3203,36 @@ export type RelationEdge = {
|
||||
node: Relation;
|
||||
};
|
||||
|
||||
export type UserV2 = {
|
||||
__typename?: 'userV2';
|
||||
avatarUrl: Scalars['String'];
|
||||
canImpersonate: Scalars['Boolean'];
|
||||
createdAt: Scalars['DateTime'];
|
||||
deletedAt?: Maybe<Scalars['DateTime']>;
|
||||
disabled?: Maybe<Scalars['Boolean']>;
|
||||
displayName: Scalars['String'];
|
||||
email: Scalars['String'];
|
||||
emailVerified: Scalars['Boolean'];
|
||||
firstName: Scalars['String'];
|
||||
id: Scalars['ID'];
|
||||
lastName: Scalars['String'];
|
||||
lastSeen?: Maybe<Scalars['DateTime']>;
|
||||
locale: Scalars['String'];
|
||||
metadata?: Maybe<Scalars['JSONObject']>;
|
||||
passwordHash?: Maybe<Scalars['String']>;
|
||||
phoneNumber?: Maybe<Scalars['String']>;
|
||||
supportUserHash?: Maybe<Scalars['String']>;
|
||||
updatedAt: Scalars['DateTime'];
|
||||
};
|
||||
|
||||
export type UserV2Edge = {
|
||||
__typename?: 'userV2Edge';
|
||||
/** Cursor for this node. */
|
||||
cursor: Scalars['ConnectionCursor'];
|
||||
/** The node containing the userV2 */
|
||||
node: UserV2;
|
||||
};
|
||||
|
||||
export type ActivityWithTargetsFragment = { __typename?: 'Activity', id: string, createdAt: string, updatedAt: string, activityTargets?: Array<{ __typename?: 'ActivityTarget', id: string, createdAt: string, updatedAt: string, companyId?: string | null, personId?: string | null }> | null };
|
||||
|
||||
export type ActivityQueryFragmentFragment = { __typename?: 'Activity', id: string, createdAt: string, title?: string | null, body?: string | null, type: ActivityType, completedAt?: string | null, dueAt?: string | null, assignee?: { __typename?: 'User', id: string, firstName?: string | null, lastName?: string | null, displayName: string, avatarUrl?: string | null } | null, author: { __typename?: 'User', id: string, firstName?: string | null, lastName?: string | null, displayName: string }, comments?: Array<{ __typename?: 'Comment', id: string, body: string, createdAt: string, updatedAt: string, author: { __typename?: 'User', id: string, displayName: string, firstName?: string | null, lastName?: string | null, avatarUrl?: string | null } }> | null, activityTargets?: Array<{ __typename?: 'ActivityTarget', id: string, companyId?: string | null, personId?: string | null, company?: { __typename?: 'Company', id: string, name: string, domainName: string } | null, person?: { __typename?: 'Person', id: string, displayName: string, avatarUrl?: string | null } | null }> | null };
|
||||
@ -3329,7 +3333,7 @@ export type AuthTokenFragmentFragment = { __typename?: 'AuthToken', token: strin
|
||||
|
||||
export type AuthTokensFragmentFragment = { __typename?: 'AuthTokenPair', accessToken: { __typename?: 'AuthToken', token: string, expiresAt: string }, refreshToken: { __typename?: 'AuthToken', token: string, expiresAt: string } };
|
||||
|
||||
export type UserQueryFragmentFragment = { __typename?: 'User', id: string, email: string, displayName: string, firstName?: string | null, lastName?: string | null, canImpersonate: boolean, supportUserHash?: string | null, avatarUrl?: string | null, workspaceMember?: { __typename?: 'WorkspaceMember', id: string, allowImpersonation: boolean, workspace: { __typename?: 'Workspace', id: string, domainName?: string | null, displayName?: string | null, logo?: string | null, inviteHash?: string | null }, assignedActivities?: Array<{ __typename?: 'Activity', id: string, title?: string | null }> | null, authoredActivities?: Array<{ __typename?: 'Activity', id: string, title?: string | null }> | null, authoredAttachments?: Array<{ __typename?: 'Attachment', id: string, name: string, type: AttachmentType }> | null, settings?: { __typename?: 'UserSettings', id: string, colorScheme: ColorScheme, locale: string } | null, companies?: Array<{ __typename?: 'Company', id: string, name: string, domainName: string }> | null, comments?: Array<{ __typename?: 'Comment', id: string, body: string }> | null } | null, settings: { __typename?: 'UserSettings', id: string, colorScheme: ColorScheme, locale: string } };
|
||||
export type UserQueryFragmentFragment = { __typename?: 'User', id: string, email: string, displayName: string, firstName?: string | null, lastName?: string | null, canImpersonate: boolean, supportUserHash?: string | null };
|
||||
|
||||
export type ChallengeMutationVariables = Exact<{
|
||||
email: Scalars['String'];
|
||||
@ -3344,7 +3348,7 @@ export type ImpersonateMutationVariables = Exact<{
|
||||
}>;
|
||||
|
||||
|
||||
export type ImpersonateMutation = { __typename?: 'Mutation', impersonate: { __typename?: 'Verify', user: { __typename?: 'User', id: string, email: string, displayName: string, firstName?: string | null, lastName?: string | null, canImpersonate: boolean, supportUserHash?: string | null, avatarUrl?: string | null, workspaceMember?: { __typename?: 'WorkspaceMember', id: string, allowImpersonation: boolean, workspace: { __typename?: 'Workspace', id: string, domainName?: string | null, displayName?: string | null, logo?: string | null, inviteHash?: string | null }, assignedActivities?: Array<{ __typename?: 'Activity', id: string, title?: string | null }> | null, authoredActivities?: Array<{ __typename?: 'Activity', id: string, title?: string | null }> | null, authoredAttachments?: Array<{ __typename?: 'Attachment', id: string, name: string, type: AttachmentType }> | null, settings?: { __typename?: 'UserSettings', id: string, colorScheme: ColorScheme, locale: string } | null, companies?: Array<{ __typename?: 'Company', id: string, name: string, domainName: string }> | null, comments?: Array<{ __typename?: 'Comment', id: string, body: string }> | null } | null, settings: { __typename?: 'UserSettings', id: string, colorScheme: ColorScheme, locale: string } }, tokens: { __typename?: 'AuthTokenPair', accessToken: { __typename?: 'AuthToken', token: string, expiresAt: string }, refreshToken: { __typename?: 'AuthToken', token: string, expiresAt: string } } } };
|
||||
export type ImpersonateMutation = { __typename?: 'Mutation', impersonate: { __typename?: 'Verify', user: { __typename?: 'User', id: string, email: string, displayName: string, firstName?: string | null, lastName?: string | null, canImpersonate: boolean, supportUserHash?: string | null }, tokens: { __typename?: 'AuthTokenPair', accessToken: { __typename?: 'AuthToken', token: string, expiresAt: string }, refreshToken: { __typename?: 'AuthToken', token: string, expiresAt: string } } } };
|
||||
|
||||
export type RenewTokenMutationVariables = Exact<{
|
||||
refreshToken: Scalars['String'];
|
||||
@ -3367,7 +3371,7 @@ export type VerifyMutationVariables = Exact<{
|
||||
}>;
|
||||
|
||||
|
||||
export type VerifyMutation = { __typename?: 'Mutation', verify: { __typename?: 'Verify', user: { __typename?: 'User', id: string, email: string, displayName: string, firstName?: string | null, lastName?: string | null, canImpersonate: boolean, supportUserHash?: string | null, avatarUrl?: string | null, workspaceMember?: { __typename?: 'WorkspaceMember', id: string, allowImpersonation: boolean, workspace: { __typename?: 'Workspace', id: string, domainName?: string | null, displayName?: string | null, logo?: string | null, inviteHash?: string | null }, assignedActivities?: Array<{ __typename?: 'Activity', id: string, title?: string | null }> | null, authoredActivities?: Array<{ __typename?: 'Activity', id: string, title?: string | null }> | null, authoredAttachments?: Array<{ __typename?: 'Attachment', id: string, name: string, type: AttachmentType }> | null, settings?: { __typename?: 'UserSettings', id: string, colorScheme: ColorScheme, locale: string } | null, companies?: Array<{ __typename?: 'Company', id: string, name: string, domainName: string }> | null, comments?: Array<{ __typename?: 'Comment', id: string, body: string }> | null } | null, settings: { __typename?: 'UserSettings', id: string, colorScheme: ColorScheme, locale: string } }, tokens: { __typename?: 'AuthTokenPair', accessToken: { __typename?: 'AuthToken', token: string, expiresAt: string }, refreshToken: { __typename?: 'AuthToken', token: string, expiresAt: string } } } };
|
||||
export type VerifyMutation = { __typename?: 'Mutation', verify: { __typename?: 'Verify', user: { __typename?: 'User', id: string, email: string, displayName: string, firstName?: string | null, lastName?: string | null, canImpersonate: boolean, supportUserHash?: string | null }, tokens: { __typename?: 'AuthTokenPair', accessToken: { __typename?: 'AuthToken', token: string, expiresAt: string }, refreshToken: { __typename?: 'AuthToken', token: string, expiresAt: string } } } };
|
||||
|
||||
export type CheckUserExistsQueryVariables = Exact<{
|
||||
email: Scalars['String'];
|
||||
@ -3744,20 +3748,18 @@ export type UpdateUserMutationVariables = Exact<{
|
||||
}>;
|
||||
|
||||
|
||||
export type UpdateUserMutation = { __typename?: 'Mutation', updateUser: { __typename?: 'User', id: string, email: string, displayName: string, firstName?: string | null, lastName?: string | null, avatarUrl?: string | null, workspaceMember?: { __typename?: 'WorkspaceMember', id: string, workspace: { __typename?: 'Workspace', id: string, domainName?: string | null, displayName?: string | null, logo?: string | null, inviteHash?: string | null }, assignedActivities?: Array<{ __typename?: 'Activity', id: string, title?: string | null }> | null, authoredActivities?: Array<{ __typename?: 'Activity', id: string, title?: string | null }> | null, authoredAttachments?: Array<{ __typename?: 'Attachment', id: string, name: string, type: AttachmentType }> | null, settings?: { __typename?: 'UserSettings', id: string, colorScheme: ColorScheme, locale: string } | null, companies?: Array<{ __typename?: 'Company', id: string, name: string, domainName: string }> | null, comments?: Array<{ __typename?: 'Comment', id: string, body: string }> | null } | null, settings: { __typename?: 'UserSettings', id: string, locale: string, colorScheme: ColorScheme } } };
|
||||
export type UpdateUserMutation = { __typename?: 'Mutation', updateUser: { __typename?: 'User', id: string, email: string } };
|
||||
|
||||
export type GetCurrentUserQueryVariables = Exact<{ [key: string]: never; }>;
|
||||
|
||||
|
||||
export type GetCurrentUserQuery = { __typename?: 'Query', currentUser: { __typename?: 'User', avatarUrl?: string | null, canImpersonate: boolean, supportUserHash?: string | null, id: string, email: string, displayName: string, firstName?: string | null, lastName?: string | null, workspaceMember?: { __typename?: 'WorkspaceMember', id: string, allowImpersonation: boolean, workspace: { __typename?: 'Workspace', id: string, domainName?: string | null, displayName?: string | null, logo?: string | null, inviteHash?: string | null }, assignedActivities?: Array<{ __typename?: 'Activity', id: string, title?: string | null }> | null, authoredActivities?: Array<{ __typename?: 'Activity', id: string, title?: string | null }> | null, authoredAttachments?: Array<{ __typename?: 'Attachment', id: string, name: string, type: AttachmentType }> | null, settings?: { __typename?: 'UserSettings', id: string, colorScheme: ColorScheme, locale: string } | null, companies?: Array<{ __typename?: 'Company', id: string, name: string, domainName: string }> | null, comments?: Array<{ __typename?: 'Comment', id: string, body: string }> | null } | null, settings: { __typename?: 'UserSettings', id: string, locale: string, colorScheme: ColorScheme } } };
|
||||
export type GetCurrentUserQuery = { __typename?: 'Query', currentUser: { __typename?: 'User', canImpersonate: boolean, supportUserHash?: string | null, id: string, email: string, displayName: string, firstName?: string | null, lastName?: string | null } };
|
||||
|
||||
export type GetUsersQueryVariables = Exact<{ [key: string]: never; }>;
|
||||
|
||||
|
||||
export type GetUsersQuery = { __typename?: 'Query', findManyUser: Array<{ __typename?: 'User', id: string, email: string, displayName: string, firstName?: string | null, lastName?: string | null }> };
|
||||
|
||||
export type WorkspaceMemberFieldsFragmentFragment = { __typename?: 'WorkspaceMember', id: string, allowImpersonation: boolean, workspace: { __typename?: 'Workspace', id: string, domainName?: string | null, displayName?: string | null, logo?: string | null, inviteHash?: string | null }, assignedActivities?: Array<{ __typename?: 'Activity', id: string, title?: string | null }> | null, authoredActivities?: Array<{ __typename?: 'Activity', id: string, title?: string | null }> | null, authoredAttachments?: Array<{ __typename?: 'Attachment', id: string, name: string, type: AttachmentType }> | null, settings?: { __typename?: 'UserSettings', id: string, colorScheme: ColorScheme, locale: string } | null, companies?: Array<{ __typename?: 'Company', id: string, name: string, domainName: string }> | null, comments?: Array<{ __typename?: 'Comment', id: string, body: string }> | null };
|
||||
|
||||
export type DeleteCurrentWorkspaceMutationVariables = Exact<{ [key: string]: never; }>;
|
||||
|
||||
|
||||
@ -3768,13 +3770,6 @@ export type RemoveWorkspaceLogoMutationVariables = Exact<{ [key: string]: never;
|
||||
|
||||
export type RemoveWorkspaceLogoMutation = { __typename?: 'Mutation', updateWorkspace: { __typename?: 'Workspace', id: string } };
|
||||
|
||||
export type RemoveWorkspaceMemberMutationVariables = Exact<{
|
||||
where: WorkspaceMemberWhereUniqueInput;
|
||||
}>;
|
||||
|
||||
|
||||
export type RemoveWorkspaceMemberMutation = { __typename?: 'Mutation', deleteWorkspaceMember: { __typename?: 'WorkspaceMember', id: string } };
|
||||
|
||||
export type UpdateWorkspaceMutationVariables = Exact<{
|
||||
data: WorkspaceUpdateInput;
|
||||
}>;
|
||||
@ -3789,13 +3784,10 @@ export type UploadWorkspaceLogoMutationVariables = Exact<{
|
||||
|
||||
export type UploadWorkspaceLogoMutation = { __typename?: 'Mutation', uploadWorkspaceLogo: string };
|
||||
|
||||
export type UpdateOneWorkspaceMemberMutationVariables = Exact<{
|
||||
data: WorkspaceMemberUpdateInput;
|
||||
where: WorkspaceMemberWhereUniqueInput;
|
||||
}>;
|
||||
export type GetCurrentWorkspaceQueryVariables = Exact<{ [key: string]: never; }>;
|
||||
|
||||
|
||||
export type UpdateOneWorkspaceMemberMutation = { __typename?: 'Mutation', UpdateOneWorkspaceMember: { __typename?: 'WorkspaceMember', id: string, allowImpersonation: boolean, workspace: { __typename?: 'Workspace', id: string, domainName?: string | null, displayName?: string | null, logo?: string | null, inviteHash?: string | null }, assignedActivities?: Array<{ __typename?: 'Activity', id: string, title?: string | null }> | null, authoredActivities?: Array<{ __typename?: 'Activity', id: string, title?: string | null }> | null, authoredAttachments?: Array<{ __typename?: 'Attachment', id: string, name: string, type: AttachmentType }> | null, settings?: { __typename?: 'UserSettings', id: string, colorScheme: ColorScheme, locale: string } | null, companies?: Array<{ __typename?: 'Company', id: string, name: string, domainName: string }> | null, comments?: Array<{ __typename?: 'Comment', id: string, body: string }> | null } };
|
||||
export type GetCurrentWorkspaceQuery = { __typename?: 'Query', currentWorkspace: { __typename?: 'Workspace', id: string, displayName?: string | null, logo?: string | null } };
|
||||
|
||||
export type GetWorkspaceFromInviteHashQueryVariables = Exact<{
|
||||
inviteHash: Scalars['String'];
|
||||
@ -3809,7 +3801,7 @@ export type GetWorkspaceMembersQueryVariables = Exact<{
|
||||
}>;
|
||||
|
||||
|
||||
export type GetWorkspaceMembersQuery = { __typename?: 'Query', workspaceMembers: Array<{ __typename?: 'WorkspaceMember', id: string, user: { __typename?: 'User', avatarUrl?: string | null, id: string, email: string, displayName: string, firstName?: string | null, lastName?: string | null } }> };
|
||||
export type GetWorkspaceMembersQuery = { __typename?: 'Query', workspaceMembers: Array<{ __typename?: 'WorkspaceMember', id: string }> };
|
||||
|
||||
export const ActivityWithTargetsFragmentDoc = gql`
|
||||
fragment ActivityWithTargets on Activity {
|
||||
@ -3918,50 +3910,6 @@ export const UserQueryFragmentFragmentDoc = gql`
|
||||
lastName
|
||||
canImpersonate
|
||||
supportUserHash
|
||||
avatarUrl
|
||||
workspaceMember {
|
||||
id
|
||||
allowImpersonation
|
||||
workspace {
|
||||
id
|
||||
domainName
|
||||
displayName
|
||||
logo
|
||||
inviteHash
|
||||
}
|
||||
assignedActivities {
|
||||
id
|
||||
title
|
||||
}
|
||||
authoredActivities {
|
||||
id
|
||||
title
|
||||
}
|
||||
authoredAttachments {
|
||||
id
|
||||
name
|
||||
type
|
||||
}
|
||||
settings {
|
||||
id
|
||||
colorScheme
|
||||
locale
|
||||
}
|
||||
companies {
|
||||
id
|
||||
name
|
||||
domainName
|
||||
}
|
||||
comments {
|
||||
id
|
||||
body
|
||||
}
|
||||
}
|
||||
settings {
|
||||
id
|
||||
colorScheme
|
||||
locale
|
||||
}
|
||||
}
|
||||
`;
|
||||
export const BaseAccountOwnerFragmentFragmentDoc = gql`
|
||||
@ -4034,46 +3982,6 @@ export const UserFieldsFragmentFragmentDoc = gql`
|
||||
lastName
|
||||
}
|
||||
`;
|
||||
export const WorkspaceMemberFieldsFragmentFragmentDoc = gql`
|
||||
fragment workspaceMemberFieldsFragment on WorkspaceMember {
|
||||
id
|
||||
allowImpersonation
|
||||
workspace {
|
||||
id
|
||||
domainName
|
||||
displayName
|
||||
logo
|
||||
inviteHash
|
||||
}
|
||||
assignedActivities {
|
||||
id
|
||||
title
|
||||
}
|
||||
authoredActivities {
|
||||
id
|
||||
title
|
||||
}
|
||||
authoredAttachments {
|
||||
id
|
||||
name
|
||||
type
|
||||
}
|
||||
settings {
|
||||
id
|
||||
colorScheme
|
||||
locale
|
||||
}
|
||||
companies {
|
||||
id
|
||||
name
|
||||
domainName
|
||||
}
|
||||
comments {
|
||||
id
|
||||
body
|
||||
}
|
||||
}
|
||||
`;
|
||||
export const AddActivityTargetsOnActivityDocument = gql`
|
||||
mutation AddActivityTargetsOnActivity($activityId: String!, $activityTargetInputs: [ActivityTargetCreateManyActivityInput!]!) {
|
||||
updateOneActivity(
|
||||
@ -6519,52 +6427,6 @@ export const UpdateUserDocument = gql`
|
||||
updateUser(data: $data, where: $where) {
|
||||
id
|
||||
email
|
||||
displayName
|
||||
firstName
|
||||
lastName
|
||||
avatarUrl
|
||||
workspaceMember {
|
||||
id
|
||||
workspace {
|
||||
id
|
||||
domainName
|
||||
displayName
|
||||
logo
|
||||
inviteHash
|
||||
}
|
||||
assignedActivities {
|
||||
id
|
||||
title
|
||||
}
|
||||
authoredActivities {
|
||||
id
|
||||
title
|
||||
}
|
||||
authoredAttachments {
|
||||
id
|
||||
name
|
||||
type
|
||||
}
|
||||
settings {
|
||||
id
|
||||
colorScheme
|
||||
locale
|
||||
}
|
||||
companies {
|
||||
id
|
||||
name
|
||||
domainName
|
||||
}
|
||||
comments {
|
||||
id
|
||||
body
|
||||
}
|
||||
}
|
||||
settings {
|
||||
id
|
||||
locale
|
||||
colorScheme
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
@ -6599,21 +6461,11 @@ export const GetCurrentUserDocument = gql`
|
||||
query GetCurrentUser {
|
||||
currentUser {
|
||||
...userFieldsFragment
|
||||
avatarUrl
|
||||
canImpersonate
|
||||
workspaceMember {
|
||||
...workspaceMemberFieldsFragment
|
||||
}
|
||||
settings {
|
||||
id
|
||||
locale
|
||||
colorScheme
|
||||
}
|
||||
supportUserHash
|
||||
}
|
||||
}
|
||||
${UserFieldsFragmentFragmentDoc}
|
||||
${WorkspaceMemberFieldsFragmentFragmentDoc}`;
|
||||
${UserFieldsFragmentFragmentDoc}`;
|
||||
|
||||
/**
|
||||
* __useGetCurrentUserQuery__
|
||||
@ -6739,39 +6591,6 @@ export function useRemoveWorkspaceLogoMutation(baseOptions?: Apollo.MutationHook
|
||||
export type RemoveWorkspaceLogoMutationHookResult = ReturnType<typeof useRemoveWorkspaceLogoMutation>;
|
||||
export type RemoveWorkspaceLogoMutationResult = Apollo.MutationResult<RemoveWorkspaceLogoMutation>;
|
||||
export type RemoveWorkspaceLogoMutationOptions = Apollo.BaseMutationOptions<RemoveWorkspaceLogoMutation, RemoveWorkspaceLogoMutationVariables>;
|
||||
export const RemoveWorkspaceMemberDocument = gql`
|
||||
mutation RemoveWorkspaceMember($where: WorkspaceMemberWhereUniqueInput!) {
|
||||
deleteWorkspaceMember(where: $where) {
|
||||
id
|
||||
}
|
||||
}
|
||||
`;
|
||||
export type RemoveWorkspaceMemberMutationFn = Apollo.MutationFunction<RemoveWorkspaceMemberMutation, RemoveWorkspaceMemberMutationVariables>;
|
||||
|
||||
/**
|
||||
* __useRemoveWorkspaceMemberMutation__
|
||||
*
|
||||
* To run a mutation, you first call `useRemoveWorkspaceMemberMutation` within a React component and pass it any options that fit your needs.
|
||||
* When your component renders, `useRemoveWorkspaceMemberMutation` returns a tuple that includes:
|
||||
* - A mutate function that you can call at any time to execute the mutation
|
||||
* - An object with fields that represent the current status of the mutation's execution
|
||||
*
|
||||
* @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2;
|
||||
*
|
||||
* @example
|
||||
* const [removeWorkspaceMemberMutation, { data, loading, error }] = useRemoveWorkspaceMemberMutation({
|
||||
* variables: {
|
||||
* where: // value for 'where'
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useRemoveWorkspaceMemberMutation(baseOptions?: Apollo.MutationHookOptions<RemoveWorkspaceMemberMutation, RemoveWorkspaceMemberMutationVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useMutation<RemoveWorkspaceMemberMutation, RemoveWorkspaceMemberMutationVariables>(RemoveWorkspaceMemberDocument, options);
|
||||
}
|
||||
export type RemoveWorkspaceMemberMutationHookResult = ReturnType<typeof useRemoveWorkspaceMemberMutation>;
|
||||
export type RemoveWorkspaceMemberMutationResult = Apollo.MutationResult<RemoveWorkspaceMemberMutation>;
|
||||
export type RemoveWorkspaceMemberMutationOptions = Apollo.BaseMutationOptions<RemoveWorkspaceMemberMutation, RemoveWorkspaceMemberMutationVariables>;
|
||||
export const UpdateWorkspaceDocument = gql`
|
||||
mutation UpdateWorkspace($data: WorkspaceUpdateInput!) {
|
||||
updateWorkspace(data: $data) {
|
||||
@ -6839,40 +6658,42 @@ export function useUploadWorkspaceLogoMutation(baseOptions?: Apollo.MutationHook
|
||||
export type UploadWorkspaceLogoMutationHookResult = ReturnType<typeof useUploadWorkspaceLogoMutation>;
|
||||
export type UploadWorkspaceLogoMutationResult = Apollo.MutationResult<UploadWorkspaceLogoMutation>;
|
||||
export type UploadWorkspaceLogoMutationOptions = Apollo.BaseMutationOptions<UploadWorkspaceLogoMutation, UploadWorkspaceLogoMutationVariables>;
|
||||
export const UpdateOneWorkspaceMemberDocument = gql`
|
||||
mutation UpdateOneWorkspaceMember($data: WorkspaceMemberUpdateInput!, $where: WorkspaceMemberWhereUniqueInput!) {
|
||||
UpdateOneWorkspaceMember(data: $data, where: $where) {
|
||||
...workspaceMemberFieldsFragment
|
||||
export const GetCurrentWorkspaceDocument = gql`
|
||||
query getCurrentWorkspace {
|
||||
currentWorkspace {
|
||||
id
|
||||
displayName
|
||||
logo
|
||||
}
|
||||
}
|
||||
${WorkspaceMemberFieldsFragmentFragmentDoc}`;
|
||||
export type UpdateOneWorkspaceMemberMutationFn = Apollo.MutationFunction<UpdateOneWorkspaceMemberMutation, UpdateOneWorkspaceMemberMutationVariables>;
|
||||
`;
|
||||
|
||||
/**
|
||||
* __useUpdateOneWorkspaceMemberMutation__
|
||||
* __useGetCurrentWorkspaceQuery__
|
||||
*
|
||||
* To run a mutation, you first call `useUpdateOneWorkspaceMemberMutation` within a React component and pass it any options that fit your needs.
|
||||
* When your component renders, `useUpdateOneWorkspaceMemberMutation` returns a tuple that includes:
|
||||
* - A mutate function that you can call at any time to execute the mutation
|
||||
* - An object with fields that represent the current status of the mutation's execution
|
||||
* To run a query within a React component, call `useGetCurrentWorkspaceQuery` and pass it any options that fit your needs.
|
||||
* When your component renders, `useGetCurrentWorkspaceQuery` returns an object from Apollo Client that contains loading, error, and data properties
|
||||
* you can use to render your UI.
|
||||
*
|
||||
* @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2;
|
||||
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
|
||||
*
|
||||
* @example
|
||||
* const [updateOneWorkspaceMemberMutation, { data, loading, error }] = useUpdateOneWorkspaceMemberMutation({
|
||||
* const { data, loading, error } = useGetCurrentWorkspaceQuery({
|
||||
* variables: {
|
||||
* data: // value for 'data'
|
||||
* where: // value for 'where'
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useUpdateOneWorkspaceMemberMutation(baseOptions?: Apollo.MutationHookOptions<UpdateOneWorkspaceMemberMutation, UpdateOneWorkspaceMemberMutationVariables>) {
|
||||
export function useGetCurrentWorkspaceQuery(baseOptions?: Apollo.QueryHookOptions<GetCurrentWorkspaceQuery, GetCurrentWorkspaceQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useMutation<UpdateOneWorkspaceMemberMutation, UpdateOneWorkspaceMemberMutationVariables>(UpdateOneWorkspaceMemberDocument, options);
|
||||
return Apollo.useQuery<GetCurrentWorkspaceQuery, GetCurrentWorkspaceQueryVariables>(GetCurrentWorkspaceDocument, options);
|
||||
}
|
||||
export type UpdateOneWorkspaceMemberMutationHookResult = ReturnType<typeof useUpdateOneWorkspaceMemberMutation>;
|
||||
export type UpdateOneWorkspaceMemberMutationResult = Apollo.MutationResult<UpdateOneWorkspaceMemberMutation>;
|
||||
export type UpdateOneWorkspaceMemberMutationOptions = Apollo.BaseMutationOptions<UpdateOneWorkspaceMemberMutation, UpdateOneWorkspaceMemberMutationVariables>;
|
||||
export function useGetCurrentWorkspaceLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<GetCurrentWorkspaceQuery, GetCurrentWorkspaceQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useLazyQuery<GetCurrentWorkspaceQuery, GetCurrentWorkspaceQueryVariables>(GetCurrentWorkspaceDocument, options);
|
||||
}
|
||||
export type GetCurrentWorkspaceQueryHookResult = ReturnType<typeof useGetCurrentWorkspaceQuery>;
|
||||
export type GetCurrentWorkspaceLazyQueryHookResult = ReturnType<typeof useGetCurrentWorkspaceLazyQuery>;
|
||||
export type GetCurrentWorkspaceQueryResult = Apollo.QueryResult<GetCurrentWorkspaceQuery, GetCurrentWorkspaceQueryVariables>;
|
||||
export const GetWorkspaceFromInviteHashDocument = gql`
|
||||
query GetWorkspaceFromInviteHash($inviteHash: String!) {
|
||||
findWorkspaceFromInviteHash(inviteHash: $inviteHash) {
|
||||
@ -6914,13 +6735,9 @@ export const GetWorkspaceMembersDocument = gql`
|
||||
query GetWorkspaceMembers($where: WorkspaceMemberWhereInput) {
|
||||
workspaceMembers: findManyWorkspaceMember(where: $where) {
|
||||
id
|
||||
user {
|
||||
...userFieldsFragment
|
||||
avatarUrl
|
||||
}
|
||||
}
|
||||
}
|
||||
${UserFieldsFragmentFragmentDoc}`;
|
||||
`;
|
||||
|
||||
/**
|
||||
* __useGetWorkspaceMembersQuery__
|
||||
|
||||
@ -3,6 +3,7 @@ import { useRecoilState, useRecoilValue } from 'recoil';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
import { currentUserState } from '@/auth/states/currentUserState';
|
||||
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
|
||||
import { GET_COMPANIES } from '@/companies/graphql/queries/getCompanies';
|
||||
import { GET_PEOPLE } from '@/people/graphql/queries/getPeople';
|
||||
import { useRightDrawer } from '@/ui/layout/right-drawer/hooks/useRightDrawer';
|
||||
@ -23,6 +24,7 @@ export const useOpenCreateActivityDrawer = () => {
|
||||
const { openRightDrawer } = useRightDrawer();
|
||||
const [createActivityMutation] = useCreateActivityMutation();
|
||||
const currentUser = useRecoilValue(currentUserState);
|
||||
const currentWorkspaceMember = useRecoilValue(currentWorkspaceMemberState);
|
||||
const setHotkeyScope = useSetHotkeyScope();
|
||||
|
||||
const [, setActivityTargetableEntityArray] = useRecoilState(
|
||||
@ -49,11 +51,11 @@ export const useOpenCreateActivityDrawer = () => {
|
||||
updatedAt: now,
|
||||
author: { connect: { id: currentUser?.id ?? '' } },
|
||||
workspaceMemberAuthor: {
|
||||
connect: { id: currentUser?.workspaceMember?.id ?? '' },
|
||||
connect: { id: currentWorkspaceMember?.id ?? '' },
|
||||
},
|
||||
assignee: { connect: { id: assigneeId ?? currentUser?.id ?? '' } },
|
||||
workspaceMemberAssignee: {
|
||||
connect: { id: currentUser?.workspaceMember?.id ?? '' },
|
||||
connect: { id: currentWorkspaceMember?.id ?? '' },
|
||||
},
|
||||
type: type,
|
||||
activityTargets: {
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
import { DateTime } from 'luxon';
|
||||
import { useRecoilState } from 'recoil';
|
||||
import { useRecoilState, useRecoilValue } from 'recoil';
|
||||
|
||||
import { currentUserState } from '@/auth/states/currentUserState';
|
||||
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
|
||||
import { turnFilterIntoWhereClause } from '@/ui/object/object-filter-dropdown/utils/turnFilterIntoWhereClause';
|
||||
import { ViewFilterOperand } from '@/views/types/ViewFilterOperand';
|
||||
import { ActivityType, useGetActivitiesQuery } from '~/generated/graphql';
|
||||
@ -9,6 +10,7 @@ import { parseDate } from '~/utils/date-utils';
|
||||
|
||||
export const useCurrentUserTaskCount = () => {
|
||||
const [currentUser] = useRecoilState(currentUserState);
|
||||
const currentWorkspaceMember = useRecoilValue(currentWorkspaceMemberState);
|
||||
|
||||
const { data } = useGetActivitiesQuery({
|
||||
variables: {
|
||||
@ -20,8 +22,11 @@ export const useCurrentUserTaskCount = () => {
|
||||
fieldMetadataId: 'assigneeId',
|
||||
value: currentUser.id,
|
||||
operand: ViewFilterOperand.Is,
|
||||
displayValue: currentUser.displayName,
|
||||
displayAvatarUrl: currentUser.avatarUrl ?? undefined,
|
||||
displayValue:
|
||||
currentWorkspaceMember?.firstName +
|
||||
' ' +
|
||||
currentWorkspaceMember?.lastName,
|
||||
displayAvatarUrl: currentWorkspaceMember?.avatarUrl ?? undefined,
|
||||
definition: {
|
||||
type: 'ENTITY',
|
||||
},
|
||||
|
||||
@ -9,7 +9,7 @@ import { useRecoilCallback } from 'recoil';
|
||||
|
||||
import { GET_COMPANIES } from '@/companies/graphql/queries/getCompanies';
|
||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { generateFindManyCustomObjectsQuery } from '@/object-record/utils/generateFindManyCustomObjectsQuery';
|
||||
import { useGenerateFindManyCustomObjectsQuery } from '@/object-record/utils/useGenerateFindManyCustomObjectsQuery';
|
||||
import { GET_PEOPLE } from '@/people/graphql/queries/getPeople';
|
||||
import { GET_API_KEYS } from '@/settings/developers/graphql/queries/getApiKeys';
|
||||
import {
|
||||
@ -54,7 +54,7 @@ export const useOptimisticEffect = () => {
|
||||
objectMetadataItem?: ObjectMetadataItem;
|
||||
}) => {
|
||||
if (isUsingFlexibleBackend && objectMetadataItem) {
|
||||
const generatedQuery = generateFindManyCustomObjectsQuery({
|
||||
const generatedQuery = useGenerateFindManyCustomObjectsQuery({
|
||||
objectMetadataItem,
|
||||
});
|
||||
|
||||
|
||||
@ -9,49 +9,5 @@ export const USER_QUERY_FRAGMENT = gql`
|
||||
lastName
|
||||
canImpersonate
|
||||
supportUserHash
|
||||
avatarUrl
|
||||
workspaceMember {
|
||||
id
|
||||
allowImpersonation
|
||||
workspace {
|
||||
id
|
||||
domainName
|
||||
displayName
|
||||
logo
|
||||
inviteHash
|
||||
}
|
||||
assignedActivities {
|
||||
id
|
||||
title
|
||||
}
|
||||
authoredActivities {
|
||||
id
|
||||
title
|
||||
}
|
||||
authoredAttachments {
|
||||
id
|
||||
name
|
||||
type
|
||||
}
|
||||
settings {
|
||||
id
|
||||
colorScheme
|
||||
locale
|
||||
}
|
||||
companies {
|
||||
id
|
||||
name
|
||||
domainName
|
||||
}
|
||||
comments {
|
||||
id
|
||||
body
|
||||
}
|
||||
}
|
||||
settings {
|
||||
id
|
||||
colorScheme
|
||||
locale
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
@ -4,12 +4,19 @@ import {
|
||||
snapshot_UNSTABLE,
|
||||
useGotoRecoilSnapshot,
|
||||
useRecoilState,
|
||||
useSetRecoilState,
|
||||
} from 'recoil';
|
||||
|
||||
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
|
||||
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
||||
import { isVerifyPendingState } from '@/auth/states/isVerifyPendingState';
|
||||
import { CREATE_ONE_WORKSPACE_MEMBER_V2 } from '@/object-record/graphql/mutation/createOneWorkspaceMember';
|
||||
import { FIND_ONE_WORKSPACE_MEMBER_V2 } from '@/object-record/graphql/queries/findOneWorkspaceMember';
|
||||
import { REACT_APP_SERVER_AUTH_URL } from '~/config';
|
||||
import {
|
||||
useChallengeMutation,
|
||||
useCheckUserExistsLazyQuery,
|
||||
useGetCurrentWorkspaceLazyQuery,
|
||||
useSignUpMutation,
|
||||
useVerifyMutation,
|
||||
} from '~/generated/graphql';
|
||||
@ -19,13 +26,20 @@ import { tokenPairState } from '../states/tokenPairState';
|
||||
|
||||
export const useAuth = () => {
|
||||
const [, setTokenPair] = useRecoilState(tokenPairState);
|
||||
const [, setCurrentUser] = useRecoilState(currentUserState);
|
||||
const setCurrentUser = useSetRecoilState(currentUserState);
|
||||
const setCurrentWorkspaceMember = useSetRecoilState(
|
||||
currentWorkspaceMemberState,
|
||||
);
|
||||
const setCurrentWorkspace = useSetRecoilState(currentWorkspaceState);
|
||||
const setIsVerifyPendingState = useSetRecoilState(isVerifyPendingState);
|
||||
|
||||
const [challenge] = useChallengeMutation();
|
||||
const [signUp] = useSignUpMutation();
|
||||
const [verify] = useVerifyMutation();
|
||||
const [checkUserExistsQuery, { data: checkUserExistsData }] =
|
||||
useCheckUserExistsLazyQuery();
|
||||
const [getCurrentWorkspaceQuery, { data: getCurrentWorkspaceData }] =
|
||||
useGetCurrentWorkspaceLazyQuery();
|
||||
|
||||
const client = useApolloClient();
|
||||
|
||||
@ -67,36 +81,56 @@ export const useAuth = () => {
|
||||
throw new Error('No verify result');
|
||||
}
|
||||
|
||||
if (!verifyResult.data?.verify.user.workspaceMember) {
|
||||
throw new Error('No workspace member');
|
||||
}
|
||||
|
||||
if (!verifyResult.data?.verify.user.workspaceMember.settings) {
|
||||
throw new Error('No settings');
|
||||
}
|
||||
|
||||
setCurrentUser({
|
||||
...verifyResult.data?.verify.user,
|
||||
workspaceMember: {
|
||||
...verifyResult.data?.verify.user.workspaceMember,
|
||||
settings: verifyResult.data?.verify.user.workspaceMember.settings,
|
||||
setTokenPair(verifyResult.data?.verify.tokens);
|
||||
const workspaceMember = await client.query({
|
||||
query: FIND_ONE_WORKSPACE_MEMBER_V2,
|
||||
variables: {
|
||||
filter: {
|
||||
userId: { eq: verifyResult.data?.verify.user.id },
|
||||
},
|
||||
},
|
||||
});
|
||||
setTokenPair(verifyResult.data?.verify.tokens);
|
||||
const currentWorkspace = await getCurrentWorkspaceQuery();
|
||||
|
||||
return verifyResult.data?.verify;
|
||||
setCurrentUser(verifyResult.data?.verify.user);
|
||||
setCurrentWorkspaceMember(workspaceMember.data?.findMany);
|
||||
setCurrentWorkspace(currentWorkspace.data?.currentWorkspace ?? null);
|
||||
return {
|
||||
user: verifyResult.data?.verify.user,
|
||||
workspaceMember: workspaceMember.data?.findMany,
|
||||
workspace: currentWorkspace.data?.currentWorkspace,
|
||||
tokens: verifyResult.data?.verify.tokens,
|
||||
};
|
||||
},
|
||||
[setTokenPair, verify, setCurrentUser],
|
||||
[
|
||||
verify,
|
||||
setTokenPair,
|
||||
client,
|
||||
getCurrentWorkspaceQuery,
|
||||
setCurrentUser,
|
||||
setCurrentWorkspaceMember,
|
||||
setCurrentWorkspace,
|
||||
],
|
||||
);
|
||||
|
||||
const handleCrendentialsSignIn = useCallback(
|
||||
async (email: string, password: string) => {
|
||||
const { loginToken } = await handleChallenge(email, password);
|
||||
setIsVerifyPendingState(true);
|
||||
|
||||
const { user } = await handleVerify(loginToken.token);
|
||||
return user;
|
||||
const { user, workspaceMember, workspace } = await handleVerify(
|
||||
loginToken.token,
|
||||
);
|
||||
|
||||
setIsVerifyPendingState(false);
|
||||
|
||||
return {
|
||||
user,
|
||||
workspaceMember,
|
||||
workspace,
|
||||
};
|
||||
},
|
||||
[handleChallenge, handleVerify],
|
||||
[handleChallenge, handleVerify, setIsVerifyPendingState],
|
||||
);
|
||||
|
||||
const handleSignOut = useCallback(() => {
|
||||
@ -110,6 +144,8 @@ export const useAuth = () => {
|
||||
|
||||
const handleCredentialsSignUp = useCallback(
|
||||
async (email: string, password: string, workspaceInviteHash?: string) => {
|
||||
setIsVerifyPendingState(true);
|
||||
|
||||
const signUpResult = await signUp({
|
||||
variables: {
|
||||
email,
|
||||
@ -126,13 +162,30 @@ export const useAuth = () => {
|
||||
throw new Error('No login token');
|
||||
}
|
||||
|
||||
const { user } = await handleVerify(
|
||||
const { user, workspace } = await handleVerify(
|
||||
signUpResult.data?.signUp.loginToken.token,
|
||||
);
|
||||
|
||||
return user;
|
||||
const workspaceMember = await client.mutate({
|
||||
mutation: CREATE_ONE_WORKSPACE_MEMBER_V2,
|
||||
variables: {
|
||||
input: {
|
||||
firstName: user.firstName ?? '',
|
||||
lastName: user.lastName ?? '',
|
||||
colorScheme: 'Light',
|
||||
userId: user.id,
|
||||
allowImpersonation: true,
|
||||
locale: 'en',
|
||||
},
|
||||
},
|
||||
});
|
||||
setCurrentWorkspaceMember(workspaceMember.data?.createWorkspaceMemberV2);
|
||||
|
||||
setIsVerifyPendingState(false);
|
||||
|
||||
return { user, workspaceMember, workspace };
|
||||
},
|
||||
[signUp, handleVerify],
|
||||
[setIsVerifyPendingState, signUp, handleVerify, client],
|
||||
);
|
||||
|
||||
const handleGoogleLogin = useCallback((workspaceInviteHash?: string) => {
|
||||
|
||||
@ -1,9 +1,12 @@
|
||||
import { useRecoilState } from 'recoil';
|
||||
import { useRecoilState, useRecoilValue } from 'recoil';
|
||||
|
||||
import { isVerifyPendingState } from '@/auth/states/isVerifyPendingState';
|
||||
|
||||
import { tokenPairState } from '../states/tokenPairState';
|
||||
|
||||
export const useIsLogged = (): boolean => {
|
||||
const [tokenPair] = useRecoilState(tokenPairState);
|
||||
const isVerifyPending = useRecoilValue(isVerifyPendingState);
|
||||
|
||||
return !!tokenPair;
|
||||
return !!tokenPair && !isVerifyPending;
|
||||
};
|
||||
|
||||
@ -1,15 +1,26 @@
|
||||
import { useRecoilState } from 'recoil';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
|
||||
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
||||
|
||||
import { useIsLogged } from '../hooks/useIsLogged';
|
||||
import { currentUserState } from '../states/currentUserState';
|
||||
import {
|
||||
getOnboardingStatus,
|
||||
OnboardingStatus,
|
||||
} from '../utils/getOnboardingStatus';
|
||||
|
||||
export const useOnboardingStatus = (): OnboardingStatus | undefined => {
|
||||
const [currentUser] = useRecoilState(currentUserState);
|
||||
const currentWorkspaceMember = useRecoilValue(currentWorkspaceMemberState);
|
||||
const currentWorkspace = useRecoilValue(currentWorkspaceState);
|
||||
const isLoggedIn = useIsLogged();
|
||||
|
||||
return getOnboardingStatus(isLoggedIn, currentUser);
|
||||
console.log(
|
||||
getOnboardingStatus(isLoggedIn, currentWorkspaceMember, currentWorkspace),
|
||||
);
|
||||
|
||||
return getOnboardingStatus(
|
||||
isLoggedIn,
|
||||
currentWorkspaceMember,
|
||||
currentWorkspace,
|
||||
);
|
||||
};
|
||||
|
||||
@ -62,7 +62,7 @@ export const useSignInUp = () => {
|
||||
});
|
||||
const [showErrors, setShowErrors] = useState(false);
|
||||
|
||||
const { data: workspace } = useGetWorkspaceFromInviteHashQuery({
|
||||
const { data: workspaceFromInviteHash } = useGetWorkspaceFromInviteHashQuery({
|
||||
variables: { inviteHash: workspaceInviteHash || '' },
|
||||
});
|
||||
|
||||
@ -119,20 +119,23 @@ export const useSignInUp = () => {
|
||||
if (!data.email || !data.password) {
|
||||
throw new Error('Email and password are required');
|
||||
}
|
||||
let user;
|
||||
let currentWorkspace;
|
||||
|
||||
if (signInUpMode === SignInUpMode.SignIn) {
|
||||
user = await signInWithCredentials(
|
||||
const { workspace } = await signInWithCredentials(
|
||||
data.email.toLowerCase(),
|
||||
data.password,
|
||||
);
|
||||
currentWorkspace = workspace;
|
||||
} else {
|
||||
user = await signUpWithCredentials(
|
||||
const { workspace } = await signUpWithCredentials(
|
||||
data.email.toLowerCase(),
|
||||
data.password,
|
||||
workspaceInviteHash,
|
||||
);
|
||||
currentWorkspace = workspace;
|
||||
}
|
||||
if (user?.workspaceMember?.workspace?.displayName) {
|
||||
if (currentWorkspace?.displayName) {
|
||||
navigate('/');
|
||||
} else {
|
||||
navigate('/create/workspace');
|
||||
@ -144,12 +147,12 @@ export const useSignInUp = () => {
|
||||
}
|
||||
},
|
||||
[
|
||||
navigate,
|
||||
signInUpMode,
|
||||
signInWithCredentials,
|
||||
signUpWithCredentials,
|
||||
workspaceInviteHash,
|
||||
navigate,
|
||||
enqueueSnackBar,
|
||||
signInUpMode,
|
||||
],
|
||||
);
|
||||
|
||||
@ -188,6 +191,6 @@ export const useSignInUp = () => {
|
||||
goBackToEmailStep,
|
||||
submitCredentials,
|
||||
form,
|
||||
workspace: workspace?.findWorkspaceFromInviteHash,
|
||||
workspace: workspaceFromInviteHash?.findWorkspaceFromInviteHash,
|
||||
};
|
||||
};
|
||||
|
||||
@ -1,32 +1,11 @@
|
||||
import { atom } from 'recoil';
|
||||
|
||||
import {
|
||||
User,
|
||||
UserSettings,
|
||||
Workspace,
|
||||
WorkspaceMember,
|
||||
} from '~/generated/graphql';
|
||||
import { User } from '~/generated/graphql';
|
||||
|
||||
export type CurrentUser = Pick<
|
||||
User,
|
||||
| 'id'
|
||||
| 'email'
|
||||
| 'displayName'
|
||||
| 'firstName'
|
||||
| 'lastName'
|
||||
| 'avatarUrl'
|
||||
| 'canImpersonate'
|
||||
| 'supportUserHash'
|
||||
> & {
|
||||
workspaceMember: Pick<WorkspaceMember, 'id' | 'allowImpersonation'> & {
|
||||
workspace: Pick<
|
||||
Workspace,
|
||||
'id' | 'displayName' | 'domainName' | 'inviteHash' | 'logo'
|
||||
>;
|
||||
settings: Pick<UserSettings, 'id' | 'colorScheme' | 'locale'>;
|
||||
};
|
||||
settings: Pick<UserSettings, 'id' | 'colorScheme' | 'locale'>;
|
||||
};
|
||||
'id' | 'email' | 'supportUserHash' | 'canImpersonate'
|
||||
>;
|
||||
|
||||
export const currentUserState = atom<CurrentUser | null>({
|
||||
key: 'currentUserState',
|
||||
|
||||
18
front/src/modules/auth/states/currentWorkspaceMemberState.ts
Normal file
18
front/src/modules/auth/states/currentWorkspaceMemberState.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import { atom } from 'recoil';
|
||||
|
||||
import { ColorScheme } from '~/generated-metadata/graphql';
|
||||
|
||||
export type CurrentWorkspaceMember = {
|
||||
id: string;
|
||||
locale: string;
|
||||
colorScheme: ColorScheme;
|
||||
allowImpersonation: boolean;
|
||||
firstName: string;
|
||||
lastName: string;
|
||||
avatarUrl: string;
|
||||
};
|
||||
|
||||
export const currentWorkspaceMemberState = atom<CurrentWorkspaceMember | null>({
|
||||
key: 'currentWorkspaceMemberState',
|
||||
default: null,
|
||||
});
|
||||
13
front/src/modules/auth/states/currentWorkspaceState.ts
Normal file
13
front/src/modules/auth/states/currentWorkspaceState.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import { atom } from 'recoil';
|
||||
|
||||
import { Workspace } from '~/generated-metadata/graphql';
|
||||
|
||||
export type CurrentWorkspace = Pick<
|
||||
Workspace,
|
||||
'id' | 'inviteHash' | 'logo' | 'displayName'
|
||||
>;
|
||||
|
||||
export const currentWorkspaceState = atom<CurrentWorkspace | null>({
|
||||
key: 'currentWorkspaceState',
|
||||
default: null,
|
||||
});
|
||||
6
front/src/modules/auth/states/isVerifyPendingState.ts
Normal file
6
front/src/modules/auth/states/isVerifyPendingState.ts
Normal file
@ -0,0 +1,6 @@
|
||||
import { atom } from 'recoil';
|
||||
|
||||
export const isVerifyPendingState = atom<boolean>({
|
||||
key: 'isVerifyPendingState',
|
||||
default: false,
|
||||
});
|
||||
@ -1,4 +1,5 @@
|
||||
import { CurrentUser } from '../states/currentUserState';
|
||||
import { CurrentWorkspaceMember } from '@/auth/states/currentWorkspaceMemberState';
|
||||
import { CurrentWorkspace } from '@/auth/states/currentWorkspaceState';
|
||||
|
||||
export enum OnboardingStatus {
|
||||
OngoingUserCreation = 'ongoing_user_creation',
|
||||
@ -9,21 +10,22 @@ export enum OnboardingStatus {
|
||||
|
||||
export const getOnboardingStatus = (
|
||||
isLoggedIn: boolean,
|
||||
currentUser: CurrentUser | null,
|
||||
currentWorkspaceMember: CurrentWorkspaceMember | null,
|
||||
currentWorkspace: CurrentWorkspace | null,
|
||||
) => {
|
||||
if (!isLoggedIn) {
|
||||
return OnboardingStatus.OngoingUserCreation;
|
||||
}
|
||||
|
||||
// if the user has not been fetched yet, we can't know the onboarding status
|
||||
if (!currentUser) {
|
||||
if (!currentWorkspaceMember) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (!currentUser.workspaceMember?.workspace.displayName) {
|
||||
if (!currentWorkspace?.displayName) {
|
||||
return OnboardingStatus.OngoingWorkspaceCreation;
|
||||
}
|
||||
if (!currentUser.firstName || !currentUser.lastName) {
|
||||
if (!currentWorkspaceMember.firstName || !currentWorkspaceMember.lastName) {
|
||||
return OnboardingStatus.OngoingProfileCreation;
|
||||
}
|
||||
|
||||
|
||||
@ -27,7 +27,6 @@ export const FIND_MANY_METADATA_OBJECTS = gql`
|
||||
label
|
||||
description
|
||||
icon
|
||||
placeholder
|
||||
isCustom
|
||||
isActive
|
||||
isNullable
|
||||
@ -36,10 +35,34 @@ export const FIND_MANY_METADATA_OBJECTS = gql`
|
||||
fromRelationMetadata {
|
||||
id
|
||||
relationType
|
||||
toObjectMetadata {
|
||||
id
|
||||
dataSourceId
|
||||
nameSingular
|
||||
namePlural
|
||||
}
|
||||
fromObjectMetadata {
|
||||
id
|
||||
dataSourceId
|
||||
nameSingular
|
||||
namePlural
|
||||
}
|
||||
}
|
||||
toRelationMetadata {
|
||||
id
|
||||
relationType
|
||||
toObjectMetadata {
|
||||
id
|
||||
dataSourceId
|
||||
nameSingular
|
||||
namePlural
|
||||
}
|
||||
fromObjectMetadata {
|
||||
id
|
||||
dataSourceId
|
||||
nameSingular
|
||||
namePlural
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -10,7 +10,7 @@ import {
|
||||
import { logError } from '~/utils/logError';
|
||||
|
||||
import { FIND_MANY_METADATA_OBJECTS } from '../graphql/queries';
|
||||
import { formatPagedObjectMetadataItemsToObjectMetadataItems } from '../utils/formatPagedObjectMetadataItemsToObjectMetadataItems';
|
||||
import { mapPaginatedObjectMetadataItemsToObjectMetadataItems } from '../utils/mapPaginatedObjectMetadataItemsToObjectMetadataItems';
|
||||
|
||||
import { useApolloMetadataClient } from './useApolloMetadataClient';
|
||||
|
||||
@ -59,7 +59,7 @@ export const useFindManyObjectMetadataItems = ({
|
||||
});
|
||||
|
||||
const objectMetadataItems = useMemo(() => {
|
||||
return formatPagedObjectMetadataItemsToObjectMetadataItems({
|
||||
return mapPaginatedObjectMetadataItemsToObjectMetadataItems({
|
||||
pagedObjectMetadataItems: data,
|
||||
});
|
||||
}, [data]);
|
||||
|
||||
@ -0,0 +1,74 @@
|
||||
import { useMemo } from 'react';
|
||||
import { useQuery } from '@apollo/client';
|
||||
|
||||
import { useSnackBar } from '@/ui/feedback/snack-bar/hooks/useSnackBar';
|
||||
import {
|
||||
ObjectFilter,
|
||||
ObjectMetadataItemsQuery,
|
||||
ObjectMetadataItemsQueryVariables,
|
||||
} from '~/generated-metadata/graphql';
|
||||
import { logError } from '~/utils/logError';
|
||||
|
||||
import { FIND_MANY_METADATA_OBJECTS } from '../graphql/queries';
|
||||
import { mapPaginatedObjectMetadataItemsToObjectMetadataItems } from '../utils/mapPaginatedObjectMetadataItemsToObjectMetadataItems';
|
||||
|
||||
import { useApolloMetadataClient } from './useApolloMetadataClient';
|
||||
|
||||
// TODO: test fetchMore
|
||||
export const useFindManyObjectMetadataItems = ({
|
||||
skip,
|
||||
filter,
|
||||
}: { skip?: boolean; filter?: ObjectFilter } = {}) => {
|
||||
const apolloMetadataClient = useApolloMetadataClient();
|
||||
|
||||
const { enqueueSnackBar } = useSnackBar();
|
||||
|
||||
const {
|
||||
data,
|
||||
fetchMore: fetchMoreInternal,
|
||||
loading,
|
||||
error,
|
||||
} = useQuery<ObjectMetadataItemsQuery, ObjectMetadataItemsQueryVariables>(
|
||||
FIND_MANY_METADATA_OBJECTS,
|
||||
{
|
||||
variables: {
|
||||
filter,
|
||||
},
|
||||
client: apolloMetadataClient ?? undefined,
|
||||
skip: skip || !apolloMetadataClient,
|
||||
onError: (error) => {
|
||||
logError('useFindManyObjectMetadataItems error : ' + error);
|
||||
enqueueSnackBar(
|
||||
`Error during useFindManyObjectMetadataItems, ${error.message}`,
|
||||
{
|
||||
variant: 'error',
|
||||
},
|
||||
);
|
||||
},
|
||||
onCompleted: () => {},
|
||||
},
|
||||
);
|
||||
|
||||
const hasMore = data?.objects?.pageInfo?.hasNextPage;
|
||||
|
||||
const fetchMore = () =>
|
||||
fetchMoreInternal({
|
||||
variables: {
|
||||
afterCursor: data?.objects?.pageInfo?.endCursor,
|
||||
},
|
||||
});
|
||||
|
||||
const objectMetadataItems = useMemo(() => {
|
||||
return mapPaginatedObjectMetadataItemsToObjectMetadataItems({
|
||||
pagedObjectMetadataItems: data,
|
||||
});
|
||||
}, [data]);
|
||||
|
||||
return {
|
||||
objectMetadataItems,
|
||||
hasMore,
|
||||
fetchMore,
|
||||
loading,
|
||||
error,
|
||||
};
|
||||
};
|
||||
@ -1,10 +1,10 @@
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
import { generateCreateOneObjectMutation } from '@/object-record/utils/generateCreateOneObjectMutation';
|
||||
import { generateDeleteOneObjectMutation } from '@/object-record/utils/generateDeleteOneObjectMutation';
|
||||
import { generateFindManyCustomObjectsQuery } from '@/object-record/utils/generateFindManyCustomObjectsQuery';
|
||||
import { generateFindOneCustomObjectQuery } from '@/object-record/utils/generateFindOneCustomObjectQuery';
|
||||
import { generateUpdateOneObjectMutation } from '@/object-record/utils/generateUpdateOneObjectMutation';
|
||||
import { useGenerateCreateOneObjectMutation } from '@/object-record/utils/generateCreateOneObjectMutation';
|
||||
import { useGenerateDeleteOneObjectMutation } from '@/object-record/utils/useGenerateDeleteOneObjectMutation';
|
||||
import { useGenerateFindManyCustomObjectsQuery } from '@/object-record/utils/useGenerateFindManyCustomObjectsQuery';
|
||||
import { useGenerateFindOneCustomObjectQuery } from '@/object-record/utils/useGenerateFindOneCustomObjectQuery';
|
||||
import { useGenerateUpdateOneObjectMutation } from '@/object-record/utils/useGenerateUpdateOneObjectMutation';
|
||||
import { useLazyLoadIcons } from '@/ui/input/hooks/useLazyLoadIcons';
|
||||
import { FieldMetadata } from '@/ui/object/field/types/FieldMetadata';
|
||||
import { ColumnDefinition } from '@/ui/object/record-table/types/ColumnDefinition';
|
||||
@ -16,13 +16,13 @@ import { formatFieldMetadataItemsAsSortDefinitions } from '../utils/formatFieldM
|
||||
|
||||
import { useFindManyObjectMetadataItems } from './useFindManyObjectMetadataItems';
|
||||
|
||||
const EMPTY_QUERY = gql`
|
||||
export const EMPTY_QUERY = gql`
|
||||
query EmptyQuery {
|
||||
empty
|
||||
}
|
||||
`;
|
||||
|
||||
const EMPTY_MUTATION = gql`
|
||||
export const EMPTY_MUTATION = gql`
|
||||
mutation EmptyMutation {
|
||||
empty
|
||||
}
|
||||
@ -74,35 +74,25 @@ export const useFindOneObjectMetadataItem = ({
|
||||
icons,
|
||||
});
|
||||
|
||||
const findManyQuery = foundObjectMetadataItem
|
||||
? generateFindManyCustomObjectsQuery({
|
||||
objectMetadataItem: foundObjectMetadataItem,
|
||||
})
|
||||
: EMPTY_QUERY;
|
||||
const findManyQuery = useGenerateFindManyCustomObjectsQuery({
|
||||
objectMetadataItem: foundObjectMetadataItem,
|
||||
});
|
||||
|
||||
const findOneQuery = foundObjectMetadataItem
|
||||
? generateFindOneCustomObjectQuery({
|
||||
objectMetadataItem: foundObjectMetadataItem,
|
||||
})
|
||||
: EMPTY_QUERY;
|
||||
const findOneQuery = useGenerateFindOneCustomObjectQuery({
|
||||
objectMetadataItem: foundObjectMetadataItem,
|
||||
});
|
||||
|
||||
const createOneMutation = foundObjectMetadataItem
|
||||
? generateCreateOneObjectMutation({
|
||||
objectMetadataItem: foundObjectMetadataItem,
|
||||
})
|
||||
: EMPTY_MUTATION;
|
||||
const createOneMutation = useGenerateCreateOneObjectMutation({
|
||||
objectMetadataItem: foundObjectMetadataItem,
|
||||
});
|
||||
|
||||
const updateOneMutation = foundObjectMetadataItem
|
||||
? generateUpdateOneObjectMutation({
|
||||
objectMetadataItem: foundObjectMetadataItem,
|
||||
})
|
||||
: EMPTY_MUTATION;
|
||||
const updateOneMutation = useGenerateUpdateOneObjectMutation({
|
||||
objectMetadataItem: foundObjectMetadataItem,
|
||||
});
|
||||
|
||||
const deleteOneMutation = foundObjectMetadataItem
|
||||
? generateDeleteOneObjectMutation({
|
||||
objectMetadataItem: foundObjectMetadataItem,
|
||||
})
|
||||
: EMPTY_MUTATION;
|
||||
const deleteOneMutation = useGenerateDeleteOneObjectMutation({
|
||||
objectMetadataItem: foundObjectMetadataItem,
|
||||
});
|
||||
|
||||
return {
|
||||
foundObjectMetadataItem,
|
||||
|
||||
@ -0,0 +1,86 @@
|
||||
import { useFindManyObjectMetadataItems } from '@/object-metadata/hooks/useFindManyObjectMetadataItems';
|
||||
import { FieldType } from '@/ui/object/field/types/FieldType';
|
||||
|
||||
import { FieldMetadataItem } from '../types/FieldMetadataItem';
|
||||
|
||||
export const useMapFieldMetadataToGraphQLQuery = () => {
|
||||
const { objectMetadataItems } = useFindManyObjectMetadataItems();
|
||||
|
||||
const mapFieldMetadataToGraphQLQuery = (field: FieldMetadataItem): any => {
|
||||
// TODO: parse
|
||||
const fieldType = field.type as FieldType;
|
||||
|
||||
const fieldIsSimpleValue = (
|
||||
[
|
||||
'TEXT',
|
||||
'PHONE',
|
||||
'DATE',
|
||||
'EMAIL',
|
||||
'NUMBER',
|
||||
'BOOLEAN',
|
||||
'DATE',
|
||||
] as FieldType[]
|
||||
).includes(fieldType);
|
||||
|
||||
const fieldIsURL = fieldType === 'URL' || fieldType === 'URL_V2';
|
||||
|
||||
const fieldIsMoneyAmount =
|
||||
fieldType === 'MONEY' || fieldType === 'MONEY_AMOUNT_V2';
|
||||
|
||||
if (fieldIsSimpleValue) {
|
||||
return field.name;
|
||||
} else if (
|
||||
fieldType === 'RELATION' &&
|
||||
field.toRelationMetadata?.relationType === 'ONE_TO_MANY'
|
||||
) {
|
||||
console.log({ objectMetadataItems, field });
|
||||
|
||||
const relationMetadataItem = objectMetadataItems.find(
|
||||
(objectMetadataItem) =>
|
||||
objectMetadataItem.id ===
|
||||
(field.toRelationMetadata as any)?.fromObjectMetadata?.id,
|
||||
);
|
||||
|
||||
console.log({ relationMetadataItem });
|
||||
|
||||
return `${field.name}
|
||||
{
|
||||
id
|
||||
${relationMetadataItem?.fields
|
||||
.filter((field) => field.type !== 'RELATION')
|
||||
.map((field) => field.name)
|
||||
.join('\n')}
|
||||
}`;
|
||||
} else if (
|
||||
fieldType === 'RELATION' &&
|
||||
field.fromRelationMetadata?.relationType === 'ONE_TO_MANY'
|
||||
) {
|
||||
return `${field.name}
|
||||
{
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
}
|
||||
}
|
||||
}`;
|
||||
} else if (fieldIsURL) {
|
||||
return `
|
||||
${field.name}
|
||||
{
|
||||
text
|
||||
link
|
||||
}
|
||||
`;
|
||||
} else if (fieldIsMoneyAmount) {
|
||||
return `
|
||||
${field.name}
|
||||
{
|
||||
amount
|
||||
currency
|
||||
}
|
||||
`;
|
||||
}
|
||||
};
|
||||
|
||||
return mapFieldMetadataToGraphQLQuery;
|
||||
};
|
||||
@ -1,65 +0,0 @@
|
||||
import { FieldType } from '@/ui/object/field/types/FieldType';
|
||||
|
||||
import { FieldMetadataItem } from '../types/FieldMetadataItem';
|
||||
|
||||
export const mapFieldMetadataToGraphQLQuery = (field: FieldMetadataItem) => {
|
||||
// TODO: parse
|
||||
const fieldType = field.type as FieldType;
|
||||
|
||||
const fieldIsSimpleValue = (
|
||||
[
|
||||
'TEXT',
|
||||
'PHONE',
|
||||
'DATE',
|
||||
'EMAIL',
|
||||
'NUMBER',
|
||||
'BOOLEAN',
|
||||
'DATE',
|
||||
] as FieldType[]
|
||||
).includes(fieldType);
|
||||
|
||||
const fieldIsURL = fieldType === 'URL' || fieldType === 'URL_V2';
|
||||
|
||||
const fieldIsMoneyAmount =
|
||||
fieldType === 'MONEY' || fieldType === 'MONEY_AMOUNT_V2';
|
||||
|
||||
if (fieldIsSimpleValue) {
|
||||
return field.name;
|
||||
} else if (
|
||||
fieldType === 'RELATION' &&
|
||||
field.toRelationMetadata?.relationType === 'ONE_TO_MANY'
|
||||
) {
|
||||
return `${field.name}
|
||||
{
|
||||
id
|
||||
}`;
|
||||
} else if (
|
||||
fieldType === 'RELATION' &&
|
||||
field.fromRelationMetadata?.relationType === 'ONE_TO_MANY'
|
||||
) {
|
||||
return `${field.name}
|
||||
{
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
}
|
||||
}
|
||||
}`;
|
||||
} else if (fieldIsURL) {
|
||||
return `
|
||||
${field.name}
|
||||
{
|
||||
text
|
||||
link
|
||||
}
|
||||
`;
|
||||
} else if (fieldIsMoneyAmount) {
|
||||
return `
|
||||
${field.name}
|
||||
{
|
||||
amount
|
||||
currency
|
||||
}
|
||||
`;
|
||||
}
|
||||
};
|
||||
@ -2,8 +2,8 @@ import { ObjectMetadataItemsQuery } from '~/generated-metadata/graphql';
|
||||
|
||||
import { ObjectMetadataItem } from '../types/ObjectMetadataItem';
|
||||
|
||||
export const formatPagedObjectMetadataItemsToObjectMetadataItems = ({
|
||||
pagedObjectMetadataItems: pagedObjectMetadataItems,
|
||||
export const mapPaginatedObjectMetadataItemsToObjectMetadataItems = ({
|
||||
pagedObjectMetadataItems,
|
||||
}: {
|
||||
pagedObjectMetadataItems: ObjectMetadataItemsQuery | undefined;
|
||||
}) => {
|
||||
@ -0,0 +1,11 @@
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
export const CREATE_ONE_WORKSPACE_MEMBER_V2 = gql`
|
||||
mutation CreateOneWorkspaceMemberV2($input: WorkspaceMemberV2CreateInput!) {
|
||||
createWorkspaceMemberV2(data: $input) {
|
||||
id
|
||||
firstName
|
||||
lastName
|
||||
}
|
||||
}
|
||||
`;
|
||||
@ -0,0 +1,15 @@
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
export const FIND_ONE_WORKSPACE_MEMBER_V2 = gql`
|
||||
query FindManyWorkspaceMembersV2($filter: WorkspaceMemberV2FilterInput) {
|
||||
workspaceMembersV2(filter: $filter) {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
firstName
|
||||
lastName
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
@ -20,12 +20,10 @@ export const useDeleteOneObjectRecord = ({
|
||||
const [mutate] = useMutation(deleteOneMutation);
|
||||
|
||||
const deleteOneObject = foundObjectMetadataItem
|
||||
? (input: Record<string, any>) => {
|
||||
? (idToDelete: string) => {
|
||||
return mutate({
|
||||
variables: {
|
||||
input: {
|
||||
...input,
|
||||
},
|
||||
idToDelete,
|
||||
},
|
||||
refetchQueries: [getOperationName(findManyQuery) ?? ''],
|
||||
});
|
||||
|
||||
@ -18,7 +18,7 @@ import {
|
||||
PaginatedObjectTypeEdge,
|
||||
PaginatedObjectTypeResults,
|
||||
} from '../types/PaginatedObjectTypeResults';
|
||||
import { formatPagedObjectsToObjects } from '../utils/formatPagedObjectsToObjects';
|
||||
import { mapPaginatedObjectsToObjects } from '../utils/mapPaginatedObjectsToObjects';
|
||||
|
||||
// TODO: test with a wrong name
|
||||
// TODO: add zod to validate that we have at least id on each object
|
||||
@ -163,7 +163,7 @@ export const useFindManyObjectRecords = <
|
||||
const objects = useMemo(
|
||||
() =>
|
||||
objectNamePlural
|
||||
? formatPagedObjectsToObjects({
|
||||
? mapPaginatedObjectsToObjects({
|
||||
pagedObjects: data,
|
||||
objectNamePlural,
|
||||
})
|
||||
|
||||
@ -16,6 +16,8 @@ export const useUpdateOneObjectRecord = ({
|
||||
objectNameSingular,
|
||||
});
|
||||
|
||||
console.log('update one object');
|
||||
|
||||
// TODO: type this with a minimal type at least with Record<string, any>
|
||||
const [mutate] = useMutation(updateOneMutation);
|
||||
|
||||
|
||||
@ -1,14 +1,21 @@
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
import { EMPTY_MUTATION } from '@/object-metadata/hooks/useFindOneObjectMetadataItem';
|
||||
import { useMapFieldMetadataToGraphQLQuery } from '@/object-metadata/hooks/useMapFieldMetadataToGraphQLQuery';
|
||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { mapFieldMetadataToGraphQLQuery } from '@/object-metadata/utils/mapFieldMetadataToGraphQLQuery';
|
||||
import { capitalize } from '~/utils/string/capitalize';
|
||||
|
||||
export const generateCreateOneObjectMutation = ({
|
||||
export const useGenerateCreateOneObjectMutation = ({
|
||||
objectMetadataItem,
|
||||
}: {
|
||||
objectMetadataItem: ObjectMetadataItem;
|
||||
objectMetadataItem: ObjectMetadataItem | undefined | null;
|
||||
}) => {
|
||||
const mapFieldMetadataToGraphQLQuery = useMapFieldMetadataToGraphQLQuery();
|
||||
|
||||
if (!objectMetadataItem) {
|
||||
return EMPTY_MUTATION;
|
||||
}
|
||||
|
||||
const capitalizedObjectName = capitalize(objectMetadataItem.nameSingular);
|
||||
|
||||
return gql`
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
export const formatPagedObjectsToObjects = <
|
||||
export const mapPaginatedObjectsToObjects = <
|
||||
ObjectType extends { id: string } & Record<string, any>,
|
||||
ObjectTypeQuery extends {
|
||||
[objectNamePlural: string]: {
|
||||
@ -1,14 +1,19 @@
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
import { EMPTY_MUTATION } from '@/object-metadata/hooks/useFindOneObjectMetadataItem';
|
||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { capitalize } from '~/utils/string/capitalize';
|
||||
|
||||
export const generateDeleteOneObjectMutation = ({
|
||||
export const useGenerateDeleteOneObjectMutation = ({
|
||||
objectMetadataItem,
|
||||
}: {
|
||||
objectMetadataItem: ObjectMetadataItem;
|
||||
objectMetadataItem: ObjectMetadataItem | undefined | null;
|
||||
}) => {
|
||||
const capitalizedObjectName = capitalize(objectMetadataItem.nameSingular);
|
||||
if (!objectMetadataItem) {
|
||||
return EMPTY_MUTATION;
|
||||
}
|
||||
|
||||
const capitalizedObjectName = capitalize(objectMetadataItem?.nameSingular);
|
||||
|
||||
return gql`
|
||||
mutation DeleteOne${capitalizedObjectName}($idToDelete: ID!) {
|
||||
@ -1,14 +1,21 @@
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
import { EMPTY_QUERY } from '@/object-metadata/hooks/useFindOneObjectMetadataItem';
|
||||
import { useMapFieldMetadataToGraphQLQuery } from '@/object-metadata/hooks/useMapFieldMetadataToGraphQLQuery';
|
||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { mapFieldMetadataToGraphQLQuery } from '@/object-metadata/utils/mapFieldMetadataToGraphQLQuery';
|
||||
import { capitalize } from '~/utils/string/capitalize';
|
||||
|
||||
export const generateFindManyCustomObjectsQuery = ({
|
||||
export const useGenerateFindManyCustomObjectsQuery = ({
|
||||
objectMetadataItem,
|
||||
}: {
|
||||
objectMetadataItem: ObjectMetadataItem;
|
||||
objectMetadataItem: ObjectMetadataItem | undefined | null;
|
||||
}) => {
|
||||
const mapFieldMetadataToGraphQLQuery = useMapFieldMetadataToGraphQLQuery();
|
||||
|
||||
if (!objectMetadataItem) {
|
||||
return EMPTY_QUERY;
|
||||
}
|
||||
|
||||
return gql`
|
||||
query FindMany${capitalize(
|
||||
objectMetadataItem.namePlural,
|
||||
@ -1,13 +1,20 @@
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
import { EMPTY_QUERY } from '@/object-metadata/hooks/useFindOneObjectMetadataItem';
|
||||
import { useMapFieldMetadataToGraphQLQuery } from '@/object-metadata/hooks/useMapFieldMetadataToGraphQLQuery';
|
||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { mapFieldMetadataToGraphQLQuery } from '@/object-metadata/utils/mapFieldMetadataToGraphQLQuery';
|
||||
|
||||
export const generateFindOneCustomObjectQuery = ({
|
||||
export const useGenerateFindOneCustomObjectQuery = ({
|
||||
objectMetadataItem,
|
||||
}: {
|
||||
objectMetadataItem: ObjectMetadataItem;
|
||||
objectMetadataItem: ObjectMetadataItem | null | undefined;
|
||||
}) => {
|
||||
const mapFieldMetadataToGraphQLQuery = useMapFieldMetadataToGraphQLQuery();
|
||||
|
||||
if (!objectMetadataItem) {
|
||||
return EMPTY_QUERY;
|
||||
}
|
||||
|
||||
return gql`
|
||||
query FindOne${objectMetadataItem.nameSingular}($objectMetadataId: UUID!) {
|
||||
${objectMetadataItem.nameSingular}(filter: {
|
||||
@ -1,7 +1,8 @@
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
import { EMPTY_MUTATION } from '@/object-metadata/hooks/useFindOneObjectMetadataItem';
|
||||
import { useMapFieldMetadataToGraphQLQuery } from '@/object-metadata/hooks/useMapFieldMetadataToGraphQLQuery';
|
||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { mapFieldMetadataToGraphQLQuery } from '@/object-metadata/utils/mapFieldMetadataToGraphQLQuery';
|
||||
import { capitalize } from '~/utils/string/capitalize';
|
||||
|
||||
export const getUpdateOneObjectMutationGraphQLField = ({
|
||||
@ -12,11 +13,17 @@ export const getUpdateOneObjectMutationGraphQLField = ({
|
||||
return `update${capitalize(objectNameSingular)}`;
|
||||
};
|
||||
|
||||
export const generateUpdateOneObjectMutation = ({
|
||||
export const useGenerateUpdateOneObjectMutation = ({
|
||||
objectMetadataItem,
|
||||
}: {
|
||||
objectMetadataItem: ObjectMetadataItem;
|
||||
objectMetadataItem: ObjectMetadataItem | undefined | null;
|
||||
}) => {
|
||||
const mapFieldMetadataToGraphQLQuery = useMapFieldMetadataToGraphQLQuery();
|
||||
|
||||
if (!objectMetadataItem) {
|
||||
return EMPTY_MUTATION;
|
||||
}
|
||||
|
||||
const capitalizedObjectName = capitalize(objectMetadataItem.nameSingular);
|
||||
|
||||
const graphQLFieldForUpdateOneObjectMutation =
|
||||
149
front/src/modules/search/hooks/useFilteredSearchEntityQueryV2.ts
Normal file
149
front/src/modules/search/hooks/useFilteredSearchEntityQueryV2.ts
Normal file
@ -0,0 +1,149 @@
|
||||
import { QueryHookOptions, QueryResult } from '@apollo/client';
|
||||
|
||||
import { mapPaginatedObjectsToObjects } from '@/object-record/utils/mapPaginatedObjectsToObjects';
|
||||
import { EntitiesForMultipleEntitySelect } from '@/ui/input/relation-picker/components/MultipleEntitySelect';
|
||||
import { EntityForSelect } from '@/ui/input/relation-picker/types/EntityForSelect';
|
||||
import { QueryMode, SortOrder } from '~/generated/graphql';
|
||||
|
||||
type SelectStringKeys<T> = NonNullable<
|
||||
{
|
||||
[K in keyof T]: K extends '__typename'
|
||||
? never
|
||||
: T[K] extends string | undefined | null
|
||||
? K
|
||||
: never;
|
||||
}[keyof T]
|
||||
>;
|
||||
|
||||
type ExtractEntityTypeFromQueryResponse<T> = T extends {
|
||||
searchResults: Array<infer U>;
|
||||
}
|
||||
? U
|
||||
: never;
|
||||
|
||||
type SearchFilter = { fieldNames: string[]; filter: string | number };
|
||||
|
||||
const DEFAULT_SEARCH_REQUEST_LIMIT = 10;
|
||||
|
||||
// TODO: use this for all search queries, because we need selectedEntities and entitiesToSelect each time we want to search
|
||||
// Filtered entities to select are
|
||||
export const useFilteredSearchEntityQueryV2 = ({
|
||||
queryHook,
|
||||
orderByField,
|
||||
filters,
|
||||
sortOrder = SortOrder.Asc,
|
||||
selectedIds,
|
||||
mappingFunction,
|
||||
limit,
|
||||
excludeEntityIds = [],
|
||||
objectNamePlural,
|
||||
}: {
|
||||
queryHook: (
|
||||
queryOptions?: QueryHookOptions<any, any>,
|
||||
) => QueryResult<any, any>;
|
||||
orderByField: string;
|
||||
filters: SearchFilter[];
|
||||
sortOrder?: SortOrder;
|
||||
selectedIds: string[];
|
||||
mappingFunction: (entity: any) => EntityForSelect;
|
||||
limit?: number;
|
||||
excludeEntityIds?: string[];
|
||||
objectNamePlural: string;
|
||||
}): EntitiesForMultipleEntitySelect<EntityForSelect> => {
|
||||
const { loading: selectedEntitiesLoading, data: selectedEntitiesData } =
|
||||
queryHook({
|
||||
variables: {
|
||||
where: {
|
||||
id: {
|
||||
in: selectedIds,
|
||||
},
|
||||
},
|
||||
orderBy: {
|
||||
[orderByField]: sortOrder,
|
||||
},
|
||||
} as any,
|
||||
});
|
||||
|
||||
const searchFilter = filters.map(({ fieldNames, filter }) => {
|
||||
return {
|
||||
OR: fieldNames.map((fieldName) => ({
|
||||
[fieldName]: {
|
||||
startsWith: `%${filter}%`,
|
||||
mode: QueryMode.Insensitive,
|
||||
},
|
||||
})),
|
||||
};
|
||||
});
|
||||
|
||||
const {
|
||||
loading: filteredSelectedEntitiesLoading,
|
||||
data: filteredSelectedEntitiesData,
|
||||
} = queryHook({
|
||||
variables: {
|
||||
where: {
|
||||
AND: [
|
||||
{
|
||||
AND: searchFilter,
|
||||
},
|
||||
{
|
||||
id: {
|
||||
in: selectedIds,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
orderBy: {
|
||||
[orderByField]: sortOrder,
|
||||
},
|
||||
} as any,
|
||||
});
|
||||
|
||||
const { loading: entitiesToSelectLoading, data: entitiesToSelectData } =
|
||||
queryHook({
|
||||
variables: {
|
||||
where: {
|
||||
AND: [
|
||||
{
|
||||
AND: searchFilter,
|
||||
},
|
||||
{
|
||||
id: {
|
||||
notIn: [...selectedIds, ...excludeEntityIds],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
limit: limit ?? DEFAULT_SEARCH_REQUEST_LIMIT,
|
||||
orderBy: {
|
||||
[orderByField]: sortOrder,
|
||||
},
|
||||
} as any,
|
||||
});
|
||||
|
||||
console.log({
|
||||
selectedEntitiesData,
|
||||
test: mapPaginatedObjectsToObjects({
|
||||
objectNamePlural: objectNamePlural,
|
||||
pagedObjects: selectedEntitiesData,
|
||||
}),
|
||||
});
|
||||
|
||||
return {
|
||||
selectedEntities: mapPaginatedObjectsToObjects({
|
||||
objectNamePlural: objectNamePlural,
|
||||
pagedObjects: selectedEntitiesData,
|
||||
}).map(mappingFunction),
|
||||
filteredSelectedEntities: mapPaginatedObjectsToObjects({
|
||||
objectNamePlural: objectNamePlural,
|
||||
pagedObjects: filteredSelectedEntitiesData,
|
||||
}).map(mappingFunction),
|
||||
entitiesToSelect: mapPaginatedObjectsToObjects({
|
||||
objectNamePlural: objectNamePlural,
|
||||
pagedObjects: entitiesToSelectData,
|
||||
}).map(mappingFunction),
|
||||
loading:
|
||||
entitiesToSelectLoading ||
|
||||
filteredSelectedEntitiesLoading ||
|
||||
selectedEntitiesLoading,
|
||||
};
|
||||
};
|
||||
@ -5,6 +5,7 @@ import debounce from 'lodash.debounce';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { currentUserState } from '@/auth/states/currentUserState';
|
||||
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
|
||||
import { TextInput } from '@/ui/input/components/TextInput';
|
||||
import { GET_CURRENT_USER } from '@/users/graphql/queries/getCurrentUser';
|
||||
import { useUpdateUserMutation } from '~/generated/graphql';
|
||||
@ -30,9 +31,14 @@ export const NameFields = ({
|
||||
onLastNameUpdate,
|
||||
}: NameFieldsProps) => {
|
||||
const currentUser = useRecoilValue(currentUserState);
|
||||
const currentWorkspaceMember = useRecoilValue(currentWorkspaceMemberState);
|
||||
|
||||
const [firstName, setFirstName] = useState(currentUser?.firstName ?? '');
|
||||
const [lastName, setLastName] = useState(currentUser?.lastName ?? '');
|
||||
const [firstName, setFirstName] = useState(
|
||||
currentWorkspaceMember?.firstName ?? '',
|
||||
);
|
||||
const [lastName, setLastName] = useState(
|
||||
currentWorkspaceMember?.lastName ?? '',
|
||||
);
|
||||
|
||||
const [updateUser] = useUpdateUserMutation();
|
||||
|
||||
@ -69,13 +75,13 @@ export const NameFields = ({
|
||||
}, 500);
|
||||
|
||||
useEffect(() => {
|
||||
if (!currentUser) {
|
||||
if (!currentWorkspaceMember) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
currentUser.firstName !== firstName ||
|
||||
currentUser.lastName !== lastName
|
||||
currentWorkspaceMember.firstName !== firstName ||
|
||||
currentWorkspaceMember.lastName !== lastName
|
||||
) {
|
||||
debouncedUpdate();
|
||||
}
|
||||
@ -83,7 +89,14 @@ export const NameFields = ({
|
||||
return () => {
|
||||
debouncedUpdate.cancel();
|
||||
};
|
||||
}, [firstName, lastName, currentUser, debouncedUpdate, autoSave]);
|
||||
}, [
|
||||
firstName,
|
||||
lastName,
|
||||
currentUser,
|
||||
debouncedUpdate,
|
||||
autoSave,
|
||||
currentWorkspaceMember,
|
||||
]);
|
||||
|
||||
return (
|
||||
<StyledComboInputContainer>
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
import { useState } from 'react';
|
||||
import { getOperationName } from '@apollo/client/utilities';
|
||||
import { useRecoilState } from 'recoil';
|
||||
import { useRecoilState, useRecoilValue } from 'recoil';
|
||||
|
||||
import { currentUserState } from '@/auth/states/currentUserState';
|
||||
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
|
||||
import { ImageInput } from '@/ui/input/components/ImageInput';
|
||||
import { GET_CURRENT_USER } from '@/users/graphql/queries/getCurrentUser';
|
||||
import { getImageAbsoluteURIOrBase64 } from '@/users/utils/getProfilePictureAbsoluteURI';
|
||||
@ -16,6 +17,8 @@ export const ProfilePictureUploader = () => {
|
||||
useUploadProfilePictureMutation();
|
||||
const [removePicture] = useRemoveProfilePictureMutation();
|
||||
const [currentUser] = useRecoilState(currentUserState);
|
||||
const currentWorkspaceMember = useRecoilValue(currentWorkspaceMemberState);
|
||||
|
||||
const [uploadController, setUploadController] =
|
||||
useState<AbortController | null>(null);
|
||||
const [errorMessage, setErrorMessage] = useState<string | null>(null);
|
||||
@ -69,7 +72,7 @@ export const ProfilePictureUploader = () => {
|
||||
|
||||
return (
|
||||
<ImageInput
|
||||
picture={getImageAbsoluteURIOrBase64(currentUser?.avatarUrl)}
|
||||
picture={getImageAbsoluteURIOrBase64(currentWorkspaceMember?.avatarUrl)}
|
||||
onUpload={handleUpload}
|
||||
onRemove={handleRemove}
|
||||
onAbort={handleAbort}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { currentUserState } from '@/auth/states/currentUserState';
|
||||
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
|
||||
import { useSnackBar } from '@/ui/feedback/snack-bar/hooks/useSnackBar';
|
||||
import { Toggle } from '@/ui/input/components/Toggle';
|
||||
import { useUpdateAllowImpersonationMutation } from '~/generated/graphql';
|
||||
@ -8,7 +8,7 @@ import { useUpdateAllowImpersonationMutation } from '~/generated/graphql';
|
||||
export const ToggleField = () => {
|
||||
const { enqueueSnackBar } = useSnackBar();
|
||||
|
||||
const currentUser = useRecoilValue(currentUserState);
|
||||
const currentWorkspaceMember = useRecoilValue(currentWorkspaceMemberState);
|
||||
|
||||
const [updateAllowImpersonation] = useUpdateAllowImpersonationMutation();
|
||||
|
||||
@ -32,7 +32,7 @@ export const ToggleField = () => {
|
||||
|
||||
return (
|
||||
<Toggle
|
||||
value={currentUser?.workspaceMember?.allowImpersonation}
|
||||
value={currentWorkspaceMember?.allowImpersonation}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
);
|
||||
|
||||
@ -2,9 +2,9 @@ import { useCallback, useEffect, useState } from 'react';
|
||||
import { getOperationName } from '@apollo/client/utilities';
|
||||
import styled from '@emotion/styled';
|
||||
import debounce from 'lodash.debounce';
|
||||
import { useRecoilState } from 'recoil';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { currentUserState } from '@/auth/states/currentUserState';
|
||||
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
||||
import { TextInput } from '@/ui/input/components/TextInput';
|
||||
import { GET_CURRENT_USER } from '@/users/graphql/queries/getCurrentUser';
|
||||
import { useUpdateWorkspaceMutation } from '~/generated/graphql';
|
||||
@ -27,10 +27,11 @@ export const NameField = ({
|
||||
autoSave = true,
|
||||
onNameUpdate,
|
||||
}: NameFieldProps) => {
|
||||
const [currentUser] = useRecoilState(currentUserState);
|
||||
const workspace = currentUser?.workspaceMember?.workspace;
|
||||
const currentWorkspace = useRecoilValue(currentWorkspaceState);
|
||||
|
||||
const [displayName, setDisplayName] = useState(workspace?.displayName ?? '');
|
||||
const [displayName, setDisplayName] = useState(
|
||||
currentWorkspace?.displayName ?? '',
|
||||
);
|
||||
|
||||
const [updateWorkspace] = useUpdateWorkspaceMutation();
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { getOperationName } from '@apollo/client/utilities';
|
||||
import { useRecoilState } from 'recoil';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { currentUserState } from '@/auth/states/currentUserState';
|
||||
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
||||
import { ImageInput } from '@/ui/input/components/ImageInput';
|
||||
import { GET_CURRENT_USER } from '@/users/graphql/queries/getCurrentUser';
|
||||
import { getImageAbsoluteURIOrBase64 } from '@/users/utils/getProfilePictureAbsoluteURI';
|
||||
@ -13,7 +13,8 @@ import {
|
||||
export const WorkspaceLogoUploader = () => {
|
||||
const [uploadLogo] = useUploadWorkspaceLogoMutation();
|
||||
const [removeLogo] = useRemoveWorkspaceLogoMutation();
|
||||
const [currentUser] = useRecoilState(currentUserState);
|
||||
const currentWorkspace = useRecoilValue(currentWorkspaceState);
|
||||
|
||||
const onUpload = async (file: File) => {
|
||||
if (!file) {
|
||||
return;
|
||||
@ -34,9 +35,7 @@ export const WorkspaceLogoUploader = () => {
|
||||
|
||||
return (
|
||||
<ImageInput
|
||||
picture={getImageAbsoluteURIOrBase64(
|
||||
currentUser?.workspaceMember?.workspace.logo,
|
||||
)}
|
||||
picture={getImageAbsoluteURIOrBase64(currentWorkspace?.logo)}
|
||||
onUpload={onUpload}
|
||||
onRemove={onRemove}
|
||||
/>
|
||||
|
||||
@ -8,7 +8,7 @@ import { Avatar, AvatarType } from '@/users/components/Avatar';
|
||||
|
||||
import { Chip, ChipVariant } from './Chip';
|
||||
|
||||
type EntityChipProps = {
|
||||
export type EntityChipProps = {
|
||||
linkToEntity?: string;
|
||||
entityId: string;
|
||||
name: string;
|
||||
|
||||
@ -62,9 +62,13 @@ export const SingleEntitySelectBase = <
|
||||
|
||||
const entitiesInDropdown = [selectedEntity, ...entitiesToSelect].filter(
|
||||
(entity): entity is CustomEntityForSelect =>
|
||||
assertNotNull(entity) && isNonEmptyString(entity.name.trim()),
|
||||
assertNotNull(entity) && isNonEmptyString(entity.name),
|
||||
);
|
||||
|
||||
console.log({
|
||||
entitiesInDropdown,
|
||||
});
|
||||
|
||||
const { preselectedOptionId, resetScroll } = useEntitySelectScroll({
|
||||
selectableOptionIds: [
|
||||
EmptyButtonId,
|
||||
|
||||
@ -5,6 +5,7 @@ export enum Entity {
|
||||
Company = 'Company',
|
||||
Person = 'Person',
|
||||
User = 'User',
|
||||
WorkspaceMember = 'WorkspaceMember',
|
||||
}
|
||||
|
||||
export type EntityTypeForSelect =
|
||||
|
||||
@ -2,6 +2,7 @@ import styled from '@emotion/styled';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { currentUserState } from '@/auth/states/currentUserState';
|
||||
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
||||
import { getImageAbsoluteURIOrBase64 } from '@/users/utils/getProfilePictureAbsoluteURI';
|
||||
|
||||
import NavCollapseButton from './NavCollapseButton';
|
||||
@ -53,8 +54,8 @@ const NavWorkspaceButton = ({
|
||||
showCollapseButton,
|
||||
}: NavWorkspaceButtonProps) => {
|
||||
const currentUser = useRecoilValue(currentUserState);
|
||||
const currentWorkspace = useRecoilValue(currentWorkspaceState);
|
||||
|
||||
const currentWorkspace = currentUser?.workspaceMember?.workspace;
|
||||
const DEFAULT_LOGO =
|
||||
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAACXBIWXMAAA7EAAAOxAGVKw4bAAACb0lEQVR4nO2VO4taQRTHr3AblbjxEVlwCwVhg7BoqqCIjy/gAyyFWNlYBOxsfH0KuxgQGwXRUkGuL2S7i1barGAgiwbdW93SnGOc4BonPiKahf3DwXFmuP/fPM4ZlvmlTxAhCBdzHnEQWYiv7Mr4C3NeuVYhQYDPzOUUQgDLBQGcLHNhvQK8DACPx8PTxiqVyvISG43GbyaT6Qfpn06n0m63e/tPAPF4vJ1MJu8kEsnWTCkWi1yr1RKGw+GDRqPBOTfr44vFQvD7/Q/lcpmaaVQAr9fLp1IpO22c47hGOBz+MB6PH+Vy+VYDAL8qlUoGtVotzOfzq4MAgsHgE/6KojiQyWR/bKVSqbSszHFM8Pl8z1YK48JsNltCOBwOnrYLO+8AAIjb+nHbycoTiUQfDJ7tFq4YAHiVSmXBxcD41u8flQU8z7fhzO0r83atVns3Go3u9Xr9x0O/RQXo9/tsIBBg6vX606a52Wz+bZ7P5/WwG29gxSJzhKgA6XTaDoFNF+krFAocmC//4yWEcSf2wTm7mCO19xFgSsKOLI16vV7b7XY7mRNoLwA0JymJ5uQIzgIAuX5PzDElT2m+E8BqtQ4ymcx7Yq7T6a6ZE4sKgOadTucaCwkxp1UzlEKh0GDxIXOwDWHAdi6Xe3swQDQa/Q7mywoolUpvsaptymazDWKxmBHTlWXZm405BFZoNpuGgwEmk4mE2SGtVivii4f1AO7J3ZopkQCQj7Ar1FeRChCJRJzVapX6DKNIfSc1Ax+wtQWQ55h6bH8FWDfYV4fO3wlwDr0C/BcADYiTPCxHqIEA2QsCZAkAKnRGkMbKN/sTX5YHPQ1e7SkAAAAASUVORK5CYII=';
|
||||
|
||||
|
||||
@ -3,6 +3,10 @@ import styled from '@emotion/styled';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { currentUserState } from '@/auth/states/currentUserState';
|
||||
import {
|
||||
CurrentWorkspaceMember,
|
||||
currentWorkspaceMemberState,
|
||||
} from '@/auth/states/currentWorkspaceMemberState';
|
||||
import { supportChatState } from '@/client-config/states/supportChatState';
|
||||
import { IconHelpCircle } from '@/ui/display/icon';
|
||||
import { Button } from '@/ui/input/button/components/Button';
|
||||
@ -30,13 +34,18 @@ const insertScript = ({
|
||||
|
||||
const SupportChat = () => {
|
||||
const currentUser = useRecoilValue(currentUserState);
|
||||
const currentWorkspaceMember = useRecoilValue(currentWorkspaceMemberState);
|
||||
const supportChat = useRecoilValue(supportChatState);
|
||||
const [isFrontChatLoaded, setIsFrontChatLoaded] = useState(false);
|
||||
|
||||
const configureFront = useCallback(
|
||||
(
|
||||
chatId: string,
|
||||
currentUser: Pick<User, 'email' | 'displayName' | 'supportUserHash'>,
|
||||
currentUser: Pick<User, 'email' | 'supportUserHash'>,
|
||||
currentWorkspaceMember: Pick<
|
||||
CurrentWorkspaceMember,
|
||||
'firstName' | 'lastName'
|
||||
>,
|
||||
) => {
|
||||
const url = 'https://chat-assets.frontapp.com/v1/chat.bundle.js';
|
||||
const script = document.querySelector(`script[src="${url}"]`);
|
||||
@ -49,7 +58,10 @@ const SupportChat = () => {
|
||||
chatId,
|
||||
useDefaultLauncher: false,
|
||||
email: currentUser.email,
|
||||
name: currentUser.displayName,
|
||||
name:
|
||||
currentWorkspaceMember.firstName +
|
||||
' ' +
|
||||
currentWorkspaceMember.lastName,
|
||||
userHash: currentUser?.supportUserHash,
|
||||
});
|
||||
setIsFrontChatLoaded(true);
|
||||
@ -65,9 +77,14 @@ const SupportChat = () => {
|
||||
supportChat?.supportDriver === 'front' &&
|
||||
supportChat.supportFrontChatId &&
|
||||
currentUser?.email &&
|
||||
currentWorkspaceMember &&
|
||||
!isFrontChatLoaded
|
||||
) {
|
||||
configureFront(supportChat.supportFrontChatId, currentUser);
|
||||
configureFront(
|
||||
supportChat.supportFrontChatId,
|
||||
currentUser,
|
||||
currentWorkspaceMember,
|
||||
);
|
||||
}
|
||||
}, [
|
||||
configureFront,
|
||||
@ -75,6 +92,7 @@ const SupportChat = () => {
|
||||
isFrontChatLoaded,
|
||||
supportChat?.supportDriver,
|
||||
supportChat.supportFrontChatId,
|
||||
currentWorkspaceMember,
|
||||
]);
|
||||
|
||||
return isFrontChatLoaded ? (
|
||||
|
||||
@ -105,13 +105,16 @@ export const usePersistField = () => {
|
||||
valueToPersist,
|
||||
);
|
||||
|
||||
console.log({
|
||||
fieldName,
|
||||
valueToPersist,
|
||||
});
|
||||
|
||||
updateEntity?.({
|
||||
variables: {
|
||||
where: { id: entityId },
|
||||
data: {
|
||||
[fieldName]: valueToPersist
|
||||
? { connect: { id: valueToPersist.id } }
|
||||
: { disconnect: true },
|
||||
[fieldName]: valueToPersist?.id,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@ -1,24 +1,27 @@
|
||||
import { EntityChip } from '@/ui/display/chip/components/EntityChip';
|
||||
import { getEntityChipFromFieldMetadata } from '@/ui/object/field/meta-types/display/utils/getEntityChipFromFieldMetadata';
|
||||
|
||||
import { useRelationField } from '../../hooks/useRelationField';
|
||||
|
||||
export const RelationFieldDisplay = () => {
|
||||
const { fieldValue, fieldDefinition } = useRelationField();
|
||||
const { entityChipDisplayMapper } = fieldDefinition;
|
||||
if (!entityChipDisplayMapper) {
|
||||
throw new Error(
|
||||
"Missing entityChipDisplayMapper in FieldContext. Please provide it in the FieldContextProvider's value prop.",
|
||||
);
|
||||
}
|
||||
const { name, pictureUrl, avatarType } =
|
||||
entityChipDisplayMapper?.(fieldValue);
|
||||
|
||||
console.log({
|
||||
fieldDefinition,
|
||||
fieldValue,
|
||||
});
|
||||
|
||||
const entityChipProps = getEntityChipFromFieldMetadata(
|
||||
fieldDefinition,
|
||||
fieldValue,
|
||||
);
|
||||
|
||||
return (
|
||||
<EntityChip
|
||||
entityId={fieldValue?.id}
|
||||
name={name}
|
||||
pictureUrl={pictureUrl}
|
||||
avatarType={avatarType}
|
||||
entityId={entityChipProps.entityId}
|
||||
name={entityChipProps.name}
|
||||
pictureUrl={entityChipProps.pictureUrl}
|
||||
avatarType={entityChipProps.avatarType}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@ -0,0 +1,32 @@
|
||||
import { EntityChipProps } from '@/ui/display/chip/components/EntityChip';
|
||||
import { FieldDefinition } from '@/ui/object/field/types/FieldDefinition';
|
||||
import { FieldRelationMetadata } from '@/ui/object/field/types/FieldMetadata';
|
||||
|
||||
export const getEntityChipFromFieldMetadata = (
|
||||
fieldDefinition: FieldDefinition<FieldRelationMetadata>,
|
||||
fieldValue: any,
|
||||
) => {
|
||||
const { fieldName } = fieldDefinition.metadata;
|
||||
|
||||
const chipValue: Pick<
|
||||
EntityChipProps,
|
||||
'name' | 'pictureUrl' | 'avatarType' | 'entityId'
|
||||
> = {
|
||||
name: '',
|
||||
pictureUrl: '',
|
||||
avatarType: 'rounded',
|
||||
entityId: fieldValue?.id,
|
||||
};
|
||||
|
||||
console.log({
|
||||
fieldName,
|
||||
fieldValue,
|
||||
});
|
||||
|
||||
// TODO: use every
|
||||
if (fieldName === 'accountOwner' && fieldValue) {
|
||||
chipValue.name = fieldValue.firstName + ' ' + fieldValue.lastName;
|
||||
}
|
||||
|
||||
return chipValue;
|
||||
};
|
||||
@ -22,6 +22,11 @@ export const useRelationField = () => {
|
||||
}),
|
||||
);
|
||||
|
||||
console.log({
|
||||
fieldDefinition,
|
||||
fieldValue,
|
||||
});
|
||||
|
||||
const fieldInitialValue = useFieldInitialValue();
|
||||
|
||||
const initialSearchValue = fieldInitialValue?.isEmpty
|
||||
|
||||
@ -40,7 +40,7 @@ export const RelationFieldInput = ({
|
||||
|
||||
return (
|
||||
<StyledRelationPickerContainer>
|
||||
{fieldDefinition.metadata.relationType === Entity.Person ? (
|
||||
{fieldDefinition.metadata.fieldName === 'person' ? (
|
||||
<PeoplePicker
|
||||
personId={initialValue?.id ?? ''}
|
||||
companyId={initialValue?.companyId ?? ''}
|
||||
@ -48,7 +48,7 @@ export const RelationFieldInput = ({
|
||||
onCancel={onCancel}
|
||||
initialSearchFilter={initialSearchValue}
|
||||
/>
|
||||
) : fieldDefinition.metadata.relationType === Entity.User ? (
|
||||
) : fieldDefinition.metadata.fieldName === 'accountOwner' ? (
|
||||
<UserPicker
|
||||
userId={initialValue?.id ?? ''}
|
||||
onSubmit={handleSubmit}
|
||||
|
||||
@ -82,6 +82,8 @@ type RecordTableProps = {
|
||||
export const RecordTable = ({ updateEntityMutation }: RecordTableProps) => {
|
||||
const tableBodyRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
console.log('record table');
|
||||
|
||||
const {
|
||||
leaveTableFocus,
|
||||
setRowSelectedState,
|
||||
|
||||
@ -1,105 +1,32 @@
|
||||
import { useCallback } from 'react';
|
||||
import { useRecoilState } from 'recoil';
|
||||
|
||||
import { currentUserState } from '@/auth/states/currentUserState';
|
||||
import {
|
||||
ColorScheme,
|
||||
useUpdateOneWorkspaceMemberMutation,
|
||||
useUpdateUserMutation,
|
||||
} from '~/generated/graphql';
|
||||
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
|
||||
import { useUpdateOneObjectRecord } from '@/object-record/hooks/useUpdateOneObjectRecord';
|
||||
import { ColorScheme } from '~/generated/graphql';
|
||||
|
||||
export const useColorScheme = () => {
|
||||
const [currentUser, setCurrentUser] = useRecoilState(currentUserState);
|
||||
const [currentWorkspaceMember] = useRecoilState(currentWorkspaceMemberState);
|
||||
|
||||
const [updateUser] = useUpdateUserMutation();
|
||||
const [updateWorkspaceMember] = useUpdateOneWorkspaceMemberMutation();
|
||||
|
||||
const colorScheme =
|
||||
!currentUser?.workspaceMember.settings?.colorScheme &&
|
||||
!currentUser?.settings?.colorScheme
|
||||
? ColorScheme.System
|
||||
: currentUser.workspaceMember.settings?.colorScheme ??
|
||||
currentUser.settings.colorScheme;
|
||||
const { updateOneObject: updateOneWorkspaceMember } =
|
||||
useUpdateOneObjectRecord({
|
||||
objectNamePlural: 'workspaceMembersV2',
|
||||
});
|
||||
const colorScheme = currentWorkspaceMember?.colorScheme ?? ColorScheme.System;
|
||||
|
||||
const setColorScheme = useCallback(
|
||||
async (value: ColorScheme) => {
|
||||
try {
|
||||
// connect settings to workspace member if not already connected
|
||||
await updateWorkspaceMember({
|
||||
variables: {
|
||||
where: { id: currentUser?.workspaceMember.id },
|
||||
data: { settings: { connect: { id: currentUser?.settings.id } } },
|
||||
},
|
||||
});
|
||||
|
||||
const result = await updateUser({
|
||||
variables: {
|
||||
where: {
|
||||
id: currentUser?.id,
|
||||
},
|
||||
data: {
|
||||
settings: {
|
||||
update: {
|
||||
colorScheme: value,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
optimisticResponse: currentUser
|
||||
? {
|
||||
__typename: 'Mutation',
|
||||
updateUser: {
|
||||
__typename: 'User',
|
||||
...currentUser,
|
||||
workspaceMember: {
|
||||
...currentUser.workspaceMember,
|
||||
settings: {
|
||||
__typename: 'UserSettings',
|
||||
id: currentUser.settings.id,
|
||||
colorScheme: value,
|
||||
locale: currentUser.settings.locale,
|
||||
},
|
||||
},
|
||||
settings: {
|
||||
__typename: 'UserSettings',
|
||||
id: currentUser.settings.id,
|
||||
colorScheme: value,
|
||||
locale: currentUser.settings.locale,
|
||||
},
|
||||
},
|
||||
}
|
||||
: undefined,
|
||||
update: (_cache, { data }) => {
|
||||
if (
|
||||
data?.updateUser.workspaceMember?.settings?.colorScheme &&
|
||||
currentUser
|
||||
) {
|
||||
setCurrentUser({
|
||||
...currentUser,
|
||||
workspaceMember: {
|
||||
...currentUser.workspaceMember,
|
||||
settings: {
|
||||
...currentUser.workspaceMember.settings,
|
||||
colorScheme:
|
||||
data.updateUser.workspaceMember.settings.colorScheme,
|
||||
},
|
||||
},
|
||||
settings: {
|
||||
...currentUser.settings,
|
||||
colorScheme:
|
||||
data.updateUser.workspaceMember.settings.colorScheme,
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
if (!result.data || result.errors) {
|
||||
throw result.errors;
|
||||
}
|
||||
} catch (err) {}
|
||||
if (!currentWorkspaceMember) {
|
||||
return;
|
||||
}
|
||||
await updateOneWorkspaceMember?.({
|
||||
idToUpdate: currentWorkspaceMember?.id,
|
||||
input: {
|
||||
colorScheme: value,
|
||||
},
|
||||
});
|
||||
},
|
||||
[updateWorkspaceMember, currentUser, updateUser, setCurrentUser],
|
||||
[currentWorkspaceMember, updateOneWorkspaceMember],
|
||||
);
|
||||
|
||||
return {
|
||||
|
||||
@ -1,13 +1,14 @@
|
||||
import { useEffect } from 'react';
|
||||
import { useQuery } from '@apollo/client';
|
||||
|
||||
import { useFilteredSearchEntityQuery } from '@/search/hooks/useFilteredSearchEntityQuery';
|
||||
import { useFindOneObjectMetadataItem } from '@/object-metadata/hooks/useFindOneObjectMetadataItem';
|
||||
import { useFilteredSearchEntityQueryV2 } from '@/search/hooks/useFilteredSearchEntityQueryV2';
|
||||
import { IconUserCircle } from '@/ui/display/icon';
|
||||
import { SingleEntitySelect } from '@/ui/input/relation-picker/components/SingleEntitySelect';
|
||||
import { relationPickerSearchFilterScopedState } from '@/ui/input/relation-picker/states/relationPickerSearchFilterScopedState';
|
||||
import { EntityForSelect } from '@/ui/input/relation-picker/types/EntityForSelect';
|
||||
import { Entity } from '@/ui/input/relation-picker/types/EntityTypeForSelect';
|
||||
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
|
||||
import { useSearchUserQuery } from '~/generated/graphql';
|
||||
|
||||
export type UserPickerProps = {
|
||||
userId: string;
|
||||
@ -18,7 +19,7 @@ export type UserPickerProps = {
|
||||
};
|
||||
|
||||
type UserForSelect = EntityForSelect & {
|
||||
entityType: Entity.User;
|
||||
entityType: Entity.WorkspaceMember;
|
||||
};
|
||||
|
||||
export const UserPicker = ({
|
||||
@ -35,29 +36,39 @@ export const UserPicker = ({
|
||||
setRelationPickerSearchFilter(initialSearchFilter ?? '');
|
||||
}, [initialSearchFilter, setRelationPickerSearchFilter]);
|
||||
|
||||
const users = useFilteredSearchEntityQuery({
|
||||
queryHook: useSearchUserQuery,
|
||||
const { findManyQuery } = useFindOneObjectMetadataItem({
|
||||
objectNamePlural: 'workspaceMembersV2',
|
||||
});
|
||||
|
||||
const useFindManyWorkspaceMembers = () => useQuery(findManyQuery, {});
|
||||
|
||||
// TODO: put workspace member
|
||||
const users = useFilteredSearchEntityQueryV2({
|
||||
queryHook: useFindManyWorkspaceMembers,
|
||||
filters: [
|
||||
{
|
||||
fieldNames: ['firstName', 'lastName'],
|
||||
filter: relationPickerSearchFilter,
|
||||
},
|
||||
],
|
||||
orderByField: 'firstName',
|
||||
mappingFunction: (user) => ({
|
||||
entityType: Entity.User,
|
||||
id: user.id,
|
||||
name: user.displayName,
|
||||
orderByField: '',
|
||||
mappingFunction: (workspaceMember) => ({
|
||||
entityType: Entity.WorkspaceMember,
|
||||
id: workspaceMember.id,
|
||||
name: workspaceMember.firstName,
|
||||
avatarType: 'rounded',
|
||||
avatarUrl: user.avatarUrl ?? '',
|
||||
originalEntity: user,
|
||||
avatarUrl: '',
|
||||
originalEntity: workspaceMember,
|
||||
}),
|
||||
selectedIds: userId ? [userId] : [],
|
||||
objectNamePlural: 'workspaceMembersV2',
|
||||
});
|
||||
|
||||
const handleEntitySelected = async (
|
||||
selectedUser: UserForSelect | null | undefined,
|
||||
) => {
|
||||
console.log({
|
||||
users,
|
||||
});
|
||||
|
||||
const handleEntitySelected = async (selectedUser: any | null | undefined) => {
|
||||
onSubmit(selectedUser ?? null);
|
||||
};
|
||||
|
||||
|
||||
@ -1,29 +1,71 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useRecoilState } from 'recoil';
|
||||
import { useApolloClient } from '@apollo/client';
|
||||
import { useSetRecoilState } from 'recoil';
|
||||
|
||||
import { currentUserState } from '@/auth/states/currentUserState';
|
||||
import { useGetCurrentUserQuery } from '~/generated/graphql';
|
||||
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
|
||||
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
||||
import { FIND_ONE_WORKSPACE_MEMBER_V2 } from '@/object-record/graphql/queries/findOneWorkspaceMember';
|
||||
import {
|
||||
useGetCurrentUserQuery,
|
||||
useGetCurrentWorkspaceQuery,
|
||||
} from '~/generated/graphql';
|
||||
|
||||
export const UserProvider = ({ children }: React.PropsWithChildren) => {
|
||||
const [, setCurrentUser] = useRecoilState(currentUserState);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [isWorkspaceMemberLoading, setIsWorkspaceMemberLoading] =
|
||||
useState(true);
|
||||
const apolloClient = useApolloClient();
|
||||
|
||||
const { data, loading } = useGetCurrentUserQuery();
|
||||
const setCurrentUser = useSetRecoilState(currentUserState);
|
||||
const setCurrentWorkspace = useSetRecoilState(currentWorkspaceState);
|
||||
const setCurrentWorkspaceMember = useSetRecoilState(
|
||||
currentWorkspaceMemberState,
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (!loading) {
|
||||
setIsLoading(false);
|
||||
}
|
||||
if (data?.currentUser?.workspaceMember?.settings) {
|
||||
setCurrentUser({
|
||||
...data.currentUser,
|
||||
workspaceMember: {
|
||||
...data.currentUser.workspaceMember,
|
||||
settings: data.currentUser.workspaceMember.settings,
|
||||
const { data: userData, loading: userLoading } = useGetCurrentUserQuery({
|
||||
onCompleted: async (data) => {
|
||||
const workspaceMember = await apolloClient.query({
|
||||
query: FIND_ONE_WORKSPACE_MEMBER_V2,
|
||||
variables: {
|
||||
filter: {
|
||||
userId: { eq: data.currentUser.id },
|
||||
},
|
||||
},
|
||||
});
|
||||
setCurrentWorkspaceMember(
|
||||
workspaceMember.data.workspaceMembersV2.edges[0].node,
|
||||
);
|
||||
setIsWorkspaceMemberLoading(false);
|
||||
},
|
||||
onError: () => {
|
||||
setIsWorkspaceMemberLoading(false);
|
||||
},
|
||||
});
|
||||
|
||||
const { data: workspaceData, loading: workspaceLoading } =
|
||||
useGetCurrentWorkspaceQuery();
|
||||
|
||||
useEffect(() => {
|
||||
if (!userLoading && !workspaceLoading && !isWorkspaceMemberLoading) {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}, [setCurrentUser, data, isLoading, loading]);
|
||||
if (userData?.currentUser) {
|
||||
setCurrentUser(userData.currentUser);
|
||||
}
|
||||
if (workspaceData?.currentWorkspace) {
|
||||
setCurrentWorkspace(workspaceData.currentWorkspace);
|
||||
}
|
||||
}, [
|
||||
setCurrentUser,
|
||||
isLoading,
|
||||
userLoading,
|
||||
workspaceLoading,
|
||||
userData?.currentUser,
|
||||
workspaceData?.currentWorkspace,
|
||||
setCurrentWorkspace,
|
||||
isWorkspaceMemberLoading,
|
||||
]);
|
||||
|
||||
return isLoading ? <></> : <>{children}</>;
|
||||
};
|
||||
|
||||
@ -5,52 +5,6 @@ export const UPDATE_USER = gql`
|
||||
updateUser(data: $data, where: $where) {
|
||||
id
|
||||
email
|
||||
displayName
|
||||
firstName
|
||||
lastName
|
||||
avatarUrl
|
||||
workspaceMember {
|
||||
id
|
||||
workspace {
|
||||
id
|
||||
domainName
|
||||
displayName
|
||||
logo
|
||||
inviteHash
|
||||
}
|
||||
assignedActivities {
|
||||
id
|
||||
title
|
||||
}
|
||||
authoredActivities {
|
||||
id
|
||||
title
|
||||
}
|
||||
authoredAttachments {
|
||||
id
|
||||
name
|
||||
type
|
||||
}
|
||||
settings {
|
||||
id
|
||||
colorScheme
|
||||
locale
|
||||
}
|
||||
companies {
|
||||
id
|
||||
name
|
||||
domainName
|
||||
}
|
||||
comments {
|
||||
id
|
||||
body
|
||||
}
|
||||
}
|
||||
settings {
|
||||
id
|
||||
locale
|
||||
colorScheme
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
@ -4,16 +4,7 @@ export const GET_CURRENT_USER = gql`
|
||||
query GetCurrentUser {
|
||||
currentUser {
|
||||
...userFieldsFragment
|
||||
avatarUrl
|
||||
canImpersonate
|
||||
workspaceMember {
|
||||
...workspaceMemberFieldsFragment
|
||||
}
|
||||
settings {
|
||||
id
|
||||
locale
|
||||
colorScheme
|
||||
}
|
||||
supportUserHash
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,6 @@
|
||||
export type WorkspaceMember = {
|
||||
id: string;
|
||||
firstName: string;
|
||||
lastName: string;
|
||||
avatarUrl: string;
|
||||
};
|
||||
@ -2,7 +2,7 @@ import styled from '@emotion/styled';
|
||||
|
||||
import { OverflowingTextWithTooltip } from '@/ui/display/tooltip/OverflowingTextWithTooltip';
|
||||
import { Avatar } from '@/users/components/Avatar';
|
||||
import { User } from '~/generated/graphql';
|
||||
import { WorkspaceMember } from '@/workspace-member/types/WorkspaceMember';
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
background: ${({ theme }) => theme.background.secondary};
|
||||
@ -29,12 +29,7 @@ const StyledEmailText = styled.span`
|
||||
`;
|
||||
|
||||
type WorkspaceMemberCardProps = {
|
||||
workspaceMember: {
|
||||
user: Pick<
|
||||
User,
|
||||
'id' | 'firstName' | 'lastName' | 'displayName' | 'avatarUrl' | 'email'
|
||||
>;
|
||||
};
|
||||
workspaceMember: WorkspaceMember;
|
||||
accessory?: React.ReactNode;
|
||||
};
|
||||
|
||||
@ -44,15 +39,19 @@ export const WorkspaceMemberCard = ({
|
||||
}: WorkspaceMemberCardProps) => (
|
||||
<StyledContainer>
|
||||
<Avatar
|
||||
avatarUrl={workspaceMember.user.avatarUrl}
|
||||
colorId={workspaceMember.user.id}
|
||||
placeholder={workspaceMember.user.firstName || ''}
|
||||
avatarUrl={workspaceMember.avatarUrl}
|
||||
colorId={workspaceMember.id}
|
||||
placeholder={workspaceMember.firstName || ''}
|
||||
type="squared"
|
||||
size="xl"
|
||||
/>
|
||||
<StyledContent>
|
||||
<OverflowingTextWithTooltip text={workspaceMember.user.displayName} />
|
||||
<StyledEmailText>{workspaceMember.user.email}</StyledEmailText>
|
||||
<OverflowingTextWithTooltip
|
||||
text={workspaceMember.firstName + ' ' + workspaceMember.lastName}
|
||||
/>
|
||||
<StyledEmailText>
|
||||
{workspaceMember.firstName + ' ' + workspaceMember.lastName}
|
||||
</StyledEmailText>
|
||||
</StyledContent>
|
||||
|
||||
{accessory}
|
||||
|
||||
@ -1,42 +0,0 @@
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
export const WORKSPACE_MEMBER_FIELDS_FRAGMENT = gql`
|
||||
fragment workspaceMemberFieldsFragment on WorkspaceMember {
|
||||
id
|
||||
allowImpersonation
|
||||
workspace {
|
||||
id
|
||||
domainName
|
||||
displayName
|
||||
logo
|
||||
inviteHash
|
||||
}
|
||||
assignedActivities {
|
||||
id
|
||||
title
|
||||
}
|
||||
authoredActivities {
|
||||
id
|
||||
title
|
||||
}
|
||||
authoredAttachments {
|
||||
id
|
||||
name
|
||||
type
|
||||
}
|
||||
settings {
|
||||
id
|
||||
colorScheme
|
||||
locale
|
||||
}
|
||||
companies {
|
||||
id
|
||||
name
|
||||
domainName
|
||||
}
|
||||
comments {
|
||||
id
|
||||
body
|
||||
}
|
||||
}
|
||||
`;
|
||||
@ -1,9 +0,0 @@
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
export const REMOVE_WORKSPACE_MEMBER = gql`
|
||||
mutation RemoveWorkspaceMember($where: WorkspaceMemberWhereUniqueInput!) {
|
||||
deleteWorkspaceMember(where: $where) {
|
||||
id
|
||||
}
|
||||
}
|
||||
`;
|
||||
@ -1,12 +0,0 @@
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
export const UPDATE_WORKSPACE_MEMBER = gql`
|
||||
mutation UpdateOneWorkspaceMember(
|
||||
$data: WorkspaceMemberUpdateInput!
|
||||
$where: WorkspaceMemberWhereUniqueInput!
|
||||
) {
|
||||
UpdateOneWorkspaceMember(data: $data, where: $where) {
|
||||
...workspaceMemberFieldsFragment
|
||||
}
|
||||
}
|
||||
`;
|
||||
@ -0,0 +1,11 @@
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
export const GET_CURRENT_WORKSPACE = gql`
|
||||
query getCurrentWorkspace {
|
||||
currentWorkspace {
|
||||
id
|
||||
displayName
|
||||
logo
|
||||
}
|
||||
}
|
||||
`;
|
||||
@ -4,10 +4,6 @@ export const GET_WORKSPACE_MEMBERS = gql`
|
||||
query GetWorkspaceMembers($where: WorkspaceMemberWhereInput) {
|
||||
workspaceMembers: findManyWorkspaceMember(where: $where) {
|
||||
id
|
||||
user {
|
||||
...userFieldsFragment
|
||||
avatarUrl
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
@ -11,6 +11,7 @@ import { z } from 'zod';
|
||||
import { SubTitle } from '@/auth/components/SubTitle';
|
||||
import { Title } from '@/auth/components/Title';
|
||||
import { currentUserState } from '@/auth/states/currentUserState';
|
||||
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
|
||||
import { ProfilePictureUploader } from '@/settings/profile/components/ProfilePictureUploader';
|
||||
import { PageHotkeyScope } from '@/types/PageHotkeyScope';
|
||||
import { H2Title } from '@/ui/display/typography/components/H2Title';
|
||||
@ -57,6 +58,7 @@ export const CreateProfile = () => {
|
||||
const { enqueueSnackBar } = useSnackBar();
|
||||
|
||||
const [currentUser] = useRecoilState(currentUserState);
|
||||
const [currentWorkspaceMember] = useRecoilState(currentWorkspaceMemberState);
|
||||
|
||||
const [updateUser] = useUpdateUserMutation();
|
||||
|
||||
@ -69,8 +71,8 @@ export const CreateProfile = () => {
|
||||
} = useForm<Form>({
|
||||
mode: 'onChange',
|
||||
defaultValues: {
|
||||
firstName: currentUser?.firstName ?? '',
|
||||
lastName: currentUser?.lastName ?? '',
|
||||
firstName: currentWorkspaceMember?.firstName ?? '',
|
||||
lastName: currentWorkspaceMember?.lastName ?? '',
|
||||
},
|
||||
resolver: zodResolver(validationSchema),
|
||||
});
|
||||
|
||||
@ -1,15 +1,18 @@
|
||||
import { useEffect } from 'react';
|
||||
import { useNavigate, useSearchParams } from 'react-router-dom';
|
||||
import { isNonEmptyString } from '@sniptt/guards';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { useAuth } from '@/auth/hooks/useAuth';
|
||||
import { useIsLogged } from '@/auth/hooks/useIsLogged';
|
||||
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
||||
|
||||
import { AppPath } from '../../modules/types/AppPath';
|
||||
|
||||
export const VerifyEffect = () => {
|
||||
const [searchParams] = useSearchParams();
|
||||
const loginToken = searchParams.get('loginToken');
|
||||
const currentWorkspace = useRecoilValue(currentWorkspaceState);
|
||||
|
||||
const isLogged = useIsLogged();
|
||||
const navigate = useNavigate();
|
||||
@ -21,13 +24,9 @@ export const VerifyEffect = () => {
|
||||
if (!loginToken) {
|
||||
navigate(AppPath.SignIn);
|
||||
} else {
|
||||
const verifyResponse = await verify(loginToken);
|
||||
await verify(loginToken);
|
||||
|
||||
if (
|
||||
isNonEmptyString(
|
||||
verifyResponse.user.workspaceMember?.workspace.displayName,
|
||||
)
|
||||
) {
|
||||
if (isNonEmptyString(currentWorkspace?.displayName)) {
|
||||
navigate(AppPath.Index);
|
||||
} else {
|
||||
navigate(AppPath.CreateWorkspace);
|
||||
|
||||
@ -38,21 +38,9 @@ export const ImpersonateEffect = () => {
|
||||
throw new Error('No impersonate result');
|
||||
}
|
||||
|
||||
if (!impersonateResult.data?.impersonate.user.workspaceMember) {
|
||||
throw new Error('No workspace member');
|
||||
}
|
||||
|
||||
if (!impersonateResult.data?.impersonate.user.workspaceMember.settings) {
|
||||
throw new Error('No workspace member settings');
|
||||
}
|
||||
|
||||
setCurrentUser({
|
||||
...impersonateResult.data.impersonate.user,
|
||||
workspaceMember: {
|
||||
...impersonateResult.data.impersonate.user.workspaceMember,
|
||||
settings:
|
||||
impersonateResult.data.impersonate.user.workspaceMember.settings,
|
||||
},
|
||||
// Todo also set WorkspaceMember
|
||||
});
|
||||
setTokenPair(impersonateResult.data?.impersonate.tokens);
|
||||
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
import { useState } from 'react';
|
||||
import styled from '@emotion/styled';
|
||||
import { useRecoilState } from 'recoil';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { currentUserState } from '@/auth/states/currentUserState';
|
||||
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
||||
import { useDeleteOneObjectRecord } from '@/object-record/hooks/useDeleteOneObjectRecord';
|
||||
import { useFindManyObjectRecords } from '@/object-record/hooks/useFindManyObjectRecords';
|
||||
import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer';
|
||||
import { IconSettings, IconTrash } from '@/ui/display/icon';
|
||||
import { H1Title } from '@/ui/display/typography/components/H1Title';
|
||||
@ -13,10 +15,7 @@ import { SubMenuTopBarContainer } from '@/ui/layout/page/SubMenuTopBarContainer'
|
||||
import { Section } from '@/ui/layout/section/components/Section';
|
||||
import { WorkspaceInviteLink } from '@/workspace/components/WorkspaceInviteLink';
|
||||
import { WorkspaceMemberCard } from '@/workspace/components/WorkspaceMemberCard';
|
||||
import {
|
||||
useGetWorkspaceMembersQuery,
|
||||
useRemoveWorkspaceMemberMutation,
|
||||
} from '~/generated/graphql';
|
||||
import { WorkspaceMember } from '@/workspace-member/types/WorkspaceMember';
|
||||
|
||||
const StyledH1Title = styled(H1Title)`
|
||||
margin-bottom: 0;
|
||||
@ -31,51 +30,22 @@ const StyledButtonContainer = styled.div`
|
||||
|
||||
export const SettingsWorkspaceMembers = () => {
|
||||
const [isConfirmationModalOpen, setIsConfirmationModalOpen] = useState(false);
|
||||
const [userToDelete, setUserToDelete] = useState<string | undefined>();
|
||||
const [workspaceMemberToDelete, setWorkspaceMemberToDelete] = useState<
|
||||
string | undefined
|
||||
>();
|
||||
|
||||
const [currentUser] = useRecoilState(currentUserState);
|
||||
const workspace = currentUser?.workspaceMember?.workspace;
|
||||
|
||||
const { data } = useGetWorkspaceMembersQuery();
|
||||
|
||||
const [removeWorkspaceMember] = useRemoveWorkspaceMemberMutation();
|
||||
|
||||
const handleRemoveWorkspaceMember = async (userId: string) => {
|
||||
await removeWorkspaceMember({
|
||||
variables: {
|
||||
where: {
|
||||
userId,
|
||||
},
|
||||
},
|
||||
optimisticResponse: {
|
||||
__typename: 'Mutation',
|
||||
deleteWorkspaceMember: {
|
||||
__typename: 'WorkspaceMember',
|
||||
id: userId,
|
||||
},
|
||||
},
|
||||
update: (cache, { data: responseData }) => {
|
||||
if (!responseData) {
|
||||
return;
|
||||
}
|
||||
|
||||
cache.evict({
|
||||
id: cache.identify({
|
||||
id: responseData.deleteWorkspaceMember.id,
|
||||
__typename: 'WorkspaceMember',
|
||||
}),
|
||||
});
|
||||
|
||||
cache.evict({
|
||||
id: cache.identify({
|
||||
id: userId,
|
||||
__typename: 'User',
|
||||
}),
|
||||
});
|
||||
|
||||
cache.gc();
|
||||
},
|
||||
const { objects: workspaceMembers } =
|
||||
useFindManyObjectRecords<WorkspaceMember>({
|
||||
objectNamePlural: 'workspaceMembersV2',
|
||||
});
|
||||
const { deleteOneObject: deleteOneWorkspaceMember } =
|
||||
useDeleteOneObjectRecord({
|
||||
objectNamePlural: 'workspaceMembersV2',
|
||||
});
|
||||
const currentWorkspace = useRecoilValue(currentWorkspaceState);
|
||||
|
||||
const handleRemoveWorkspaceMember = async (workspaceMemberId: string) => {
|
||||
await deleteOneWorkspaceMember?.(workspaceMemberId);
|
||||
setIsConfirmationModalOpen(false);
|
||||
};
|
||||
|
||||
@ -83,14 +53,14 @@ export const SettingsWorkspaceMembers = () => {
|
||||
<SubMenuTopBarContainer Icon={IconSettings} title="Settings">
|
||||
<SettingsPageContainer width={350}>
|
||||
<StyledH1Title title="Members" />
|
||||
{workspace?.inviteHash && (
|
||||
{currentWorkspace?.inviteHash && (
|
||||
<Section>
|
||||
<H2Title
|
||||
title="Invite"
|
||||
description="Send an invitation to use Twenty"
|
||||
/>
|
||||
<WorkspaceInviteLink
|
||||
inviteLink={`${window.location.origin}/invite/${workspace?.inviteHash}`}
|
||||
inviteLink={`${window.location.origin}/invite/${currentWorkspace?.inviteHash}`}
|
||||
/>
|
||||
</Section>
|
||||
)}
|
||||
@ -99,17 +69,17 @@ export const SettingsWorkspaceMembers = () => {
|
||||
title="Members"
|
||||
description="Manage the members of your space here"
|
||||
/>
|
||||
{data?.workspaceMembers?.map((member) => (
|
||||
{workspaceMembers?.map((member) => (
|
||||
<WorkspaceMemberCard
|
||||
key={member.user.id}
|
||||
workspaceMember={{ user: member.user }}
|
||||
key={member.id}
|
||||
workspaceMember={member as WorkspaceMember}
|
||||
accessory={
|
||||
currentUser?.id !== member.user.id && (
|
||||
currentWorkspace?.id !== member.id && (
|
||||
<StyledButtonContainer>
|
||||
<IconButton
|
||||
onClick={() => {
|
||||
setIsConfirmationModalOpen(true);
|
||||
setUserToDelete(member.user.id);
|
||||
setWorkspaceMemberToDelete(member.id);
|
||||
}}
|
||||
variant="tertiary"
|
||||
size="medium"
|
||||
@ -133,7 +103,8 @@ export const SettingsWorkspaceMembers = () => {
|
||||
</>
|
||||
}
|
||||
onConfirmClick={() =>
|
||||
userToDelete && handleRemoveWorkspaceMember(userToDelete)
|
||||
workspaceMemberToDelete &&
|
||||
handleRemoveWorkspaceMember(workspaceMemberToDelete)
|
||||
}
|
||||
deleteButtonText="Delete account"
|
||||
/>
|
||||
|
||||
@ -1,14 +1,14 @@
|
||||
import { useEffect } from 'react';
|
||||
import { useRecoilState } from 'recoil';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { currentUserState } from '@/auth/states/currentUserState';
|
||||
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
|
||||
import { useFilter } from '@/ui/object/object-filter-dropdown/hooks/useFilter';
|
||||
import { ViewFilterOperand } from '@/views/types/ViewFilterOperand';
|
||||
|
||||
import { tasksFilterDefinitions } from './tasks-filter-definitions';
|
||||
|
||||
export const TasksEffect = () => {
|
||||
const [currentUser] = useRecoilState(currentUserState);
|
||||
const currentWorkspaceMember = useRecoilValue(currentWorkspaceMemberState);
|
||||
const { setSelectedFilter, setAvailableFilterDefinitions } = useFilter();
|
||||
|
||||
useEffect(() => {
|
||||
@ -16,16 +16,19 @@ export const TasksEffect = () => {
|
||||
}, [setAvailableFilterDefinitions]);
|
||||
|
||||
useEffect(() => {
|
||||
if (currentUser) {
|
||||
if (currentWorkspaceMember) {
|
||||
setSelectedFilter({
|
||||
fieldMetadataId: 'assigneeId',
|
||||
value: currentUser.id,
|
||||
value: currentWorkspaceMember.id,
|
||||
operand: ViewFilterOperand.Is,
|
||||
displayValue: currentUser.displayName,
|
||||
displayAvatarUrl: currentUser.avatarUrl ?? undefined,
|
||||
displayValue:
|
||||
currentWorkspaceMember.firstName +
|
||||
' ' +
|
||||
currentWorkspaceMember.lastName,
|
||||
displayAvatarUrl: currentWorkspaceMember.avatarUrl ?? undefined,
|
||||
definition: tasksFilterDefinitions[0],
|
||||
});
|
||||
}
|
||||
}, [currentUser, setSelectedFilter]);
|
||||
}, [currentWorkspaceMember, setSelectedFilter]);
|
||||
return <></>;
|
||||
};
|
||||
|
||||
@ -58,12 +58,9 @@ export class AbilityFactory {
|
||||
);
|
||||
|
||||
// User
|
||||
can(AbilityAction.Read, 'User', {
|
||||
workspaceMember: {
|
||||
workspaceId: workspace.id,
|
||||
},
|
||||
});
|
||||
|
||||
if (user) {
|
||||
can(AbilityAction.Read, 'User', { id: user.id });
|
||||
can(AbilityAction.Update, 'User', { id: user.id });
|
||||
can(AbilityAction.Delete, 'User', { id: user.id });
|
||||
} else {
|
||||
|
||||
@ -12,7 +12,6 @@ import { TokenExpiredError, JsonWebTokenError, verify } from 'jsonwebtoken';
|
||||
import { AppService } from './app.service';
|
||||
|
||||
import { CoreModule } from './core/core.module';
|
||||
import { CoreV2Module } from './coreV2/core.module';
|
||||
import { IntegrationsModule } from './integrations/integrations.module';
|
||||
import { PrismaModule } from './database/prisma.module';
|
||||
import { HealthModule } from './health/health.module';
|
||||
@ -103,7 +102,6 @@ import { ExceptionFilter } from './filters/exception.filter';
|
||||
AbilityModule,
|
||||
IntegrationsModule,
|
||||
CoreModule,
|
||||
CoreV2Module,
|
||||
TenantModule,
|
||||
],
|
||||
providers: [
|
||||
|
||||
@ -130,7 +130,6 @@ export class AuthResolver {
|
||||
defaultFields: {
|
||||
User: {
|
||||
id: true,
|
||||
workspaceMember: { select: { allowImpersonation: true } },
|
||||
},
|
||||
},
|
||||
})
|
||||
@ -140,7 +139,6 @@ export class AuthResolver {
|
||||
assert(user.canImpersonate, 'User cannot impersonate', ForbiddenException);
|
||||
const select = prismaSelect.valueOf('user') as Prisma.UserSelect & {
|
||||
id: true;
|
||||
workspaceMember: { select: { allowImpersonation: true } };
|
||||
};
|
||||
|
||||
return this.authService.impersonate(impersonateInput.userId, select);
|
||||
|
||||
@ -63,11 +63,6 @@ export class GoogleAuthController {
|
||||
firstName: firstName ?? '',
|
||||
lastName: lastName ?? '',
|
||||
locale: 'en',
|
||||
settings: {
|
||||
create: {
|
||||
locale: 'en',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
workspaceId,
|
||||
|
||||
@ -88,7 +88,6 @@ export class AuthService {
|
||||
data: {
|
||||
email: signUpInput.email,
|
||||
passwordHash,
|
||||
locale: 'en',
|
||||
},
|
||||
} as Prisma.UserCreateArgs,
|
||||
workspace.id,
|
||||
@ -160,11 +159,6 @@ export class AuthService {
|
||||
userId: string,
|
||||
select: Prisma.UserSelect & {
|
||||
id: true;
|
||||
workspaceMember: {
|
||||
select: {
|
||||
allowImpersonation: true;
|
||||
};
|
||||
};
|
||||
},
|
||||
) {
|
||||
const user = await this.userService.findUnique({
|
||||
@ -175,11 +169,8 @@ export class AuthService {
|
||||
});
|
||||
|
||||
assert(user, "This user doesn't exist", NotFoundException);
|
||||
assert(
|
||||
user.workspaceMember?.allowImpersonation,
|
||||
'Impersonation not allowed',
|
||||
ForbiddenException,
|
||||
);
|
||||
|
||||
// Todo: check if workspace member can be impersonated
|
||||
|
||||
const accessToken = await this.tokenService.generateAccessToken(user.id);
|
||||
const refreshToken = await this.tokenService.generateRefreshToken(user.id);
|
||||
|
||||
@ -33,22 +33,19 @@ export class TokenService {
|
||||
|
||||
const user = await this.prismaService.client.user.findUnique({
|
||||
where: { id: userId },
|
||||
include: {
|
||||
workspaceMember: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
throw new NotFoundException('User is not found');
|
||||
}
|
||||
|
||||
if (!user.workspaceMember) {
|
||||
throw new ForbiddenException('User is not associated to a workspace');
|
||||
if (!user.defaultWorkspaceId) {
|
||||
throw new NotFoundException('User does not have a default workspace');
|
||||
}
|
||||
|
||||
const jwtPayload: JwtPayload = {
|
||||
sub: user.id,
|
||||
workspaceId: user.workspaceMember.workspaceId,
|
||||
workspaceId: user.defaultWorkspaceId,
|
||||
};
|
||||
|
||||
return {
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
|
||||
import { WebHookModule } from 'src/core/web-hook/web-hook.module';
|
||||
import { UserModule as UserV2Module } from 'src/coreV2/user/user.module';
|
||||
import { RefreshTokenModule as RefreshTokenV2Module } from 'src/coreV2/refresh-token/refresh-token.module';
|
||||
|
||||
import { UserModule } from './user/user.module';
|
||||
import { CommentModule } from './comment/comment.module';
|
||||
@ -34,6 +36,8 @@ import { ApiKeyModule } from './api-key/api-key.module';
|
||||
FavoriteModule,
|
||||
ApiKeyModule,
|
||||
WebHookModule,
|
||||
UserV2Module,
|
||||
RefreshTokenV2Module,
|
||||
],
|
||||
exports: [
|
||||
AuthModule,
|
||||
@ -48,6 +52,8 @@ import { ApiKeyModule } from './api-key/api-key.module';
|
||||
FavoriteModule,
|
||||
ApiKeyModule,
|
||||
WebHookModule,
|
||||
UserV2Module,
|
||||
RefreshTokenV2Module,
|
||||
],
|
||||
})
|
||||
export class CoreModule {}
|
||||
|
||||
@ -66,13 +66,6 @@ export class UserService {
|
||||
: await this.workspaceService.createDefaultWorkspace();
|
||||
|
||||
assert(workspace, 'workspace is missing', BadRequestException);
|
||||
|
||||
const userSettings = await this.prismaService.client.userSettings.create({
|
||||
data: { locale: 'en' },
|
||||
});
|
||||
|
||||
const settings = { connect: { id: userSettings.id } };
|
||||
|
||||
// Create user
|
||||
const user = await this.prismaService.client.user.upsert({
|
||||
where: {
|
||||
@ -80,17 +73,7 @@ export class UserService {
|
||||
},
|
||||
create: {
|
||||
...(args.data as Prisma.UserCreateInput),
|
||||
settings,
|
||||
|
||||
workspaceMember: {
|
||||
create: {
|
||||
workspace: {
|
||||
connect: { id: workspace.id },
|
||||
},
|
||||
settings,
|
||||
},
|
||||
},
|
||||
locale: 'en',
|
||||
defaultWorkspaceId: workspace.id,
|
||||
},
|
||||
update: {},
|
||||
...(args.select ? { select: args.select } : {}),
|
||||
|
||||
@ -1,31 +0,0 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import { GraphQLModule } from '@nestjs/graphql';
|
||||
|
||||
import { YogaDriverConfig, YogaDriver } from '@graphql-yoga/nestjs';
|
||||
import GraphQLJSON from 'graphql-type-json';
|
||||
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import config from '../../ormconfig';
|
||||
|
||||
import { UserModule } from './user/user.module';
|
||||
import { RefreshTokenModule } from './refresh-token/refresh-token.module';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
TypeOrmModule.forRoot(config),
|
||||
GraphQLModule.forRoot<YogaDriverConfig>({
|
||||
context: ({ req }) => ({ req }),
|
||||
driver: YogaDriver,
|
||||
autoSchemaFile: true,
|
||||
include: [CoreV2Module],
|
||||
resolvers: { JSON: GraphQLJSON },
|
||||
plugins: [],
|
||||
path: '/graphqlv2',
|
||||
}),
|
||||
UserModule,
|
||||
RefreshTokenModule,
|
||||
],
|
||||
exports: [UserModule],
|
||||
})
|
||||
export class CoreV2Module {}
|
||||
@ -3,7 +3,6 @@ import {
|
||||
PagingStrategies,
|
||||
ReadResolverOpts,
|
||||
} from '@ptc-org/nestjs-query-graphql';
|
||||
import { SortDirection } from '@ptc-org/nestjs-query-core';
|
||||
|
||||
import { JwtAuthGuard } from 'src/guards/jwt.auth.guard';
|
||||
|
||||
@ -26,7 +25,8 @@ export const refreshTokenAutoResolverOpts: AutoResolverOpts<
|
||||
enableTotalCount: true,
|
||||
pagingStrategy: PagingStrategies.CURSOR,
|
||||
read: {
|
||||
defaultSort: [{ field: 'id', direction: SortDirection.DESC }],
|
||||
many: { disabled: true },
|
||||
one: { disabled: true },
|
||||
},
|
||||
create: {
|
||||
many: { disabled: true },
|
||||
|
||||
@ -15,12 +15,12 @@ import {
|
||||
IDField,
|
||||
} from '@ptc-org/nestjs-query-graphql';
|
||||
|
||||
import { User } from 'src/coreV2/user/user.entity';
|
||||
import { UserV2 } from 'src/coreV2/user/user.entity';
|
||||
|
||||
import { BeforeCreateOneRefreshToken } from './hooks/before-create-one-refresh-token.hook';
|
||||
|
||||
@Entity('refresh_tokens')
|
||||
@ObjectType('RefreshToken')
|
||||
@ObjectType('refreshTokenV2')
|
||||
@BeforeCreateOne(BeforeCreateOneRefreshToken)
|
||||
@Authorize({
|
||||
authorize: (context: any) => ({
|
||||
@ -32,9 +32,9 @@ export class RefreshToken {
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
id: string;
|
||||
|
||||
@ManyToOne(() => User, (user) => user.refreshTokens)
|
||||
@ManyToOne(() => UserV2, (user) => user.refreshTokens)
|
||||
@JoinColumn({ name: 'userId' })
|
||||
user: User;
|
||||
user: UserV2;
|
||||
|
||||
@Column()
|
||||
userId: string;
|
||||
|
||||
@ -1,8 +1,12 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
|
||||
import { NestjsQueryGraphQLModule } from '@ptc-org/nestjs-query-graphql';
|
||||
import { NestjsQueryTypeOrmModule } from '@ptc-org/nestjs-query-typeorm';
|
||||
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import config from '../../../ormconfig';
|
||||
|
||||
import { RefreshToken } from './refresh-token.entity';
|
||||
import { refreshTokenAutoResolverOpts } from './refresh-token.auto-resolver-opts';
|
||||
|
||||
@ -10,6 +14,7 @@ import { RefreshTokenService } from './services/refresh-token.service';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
TypeOrmModule.forRoot(config),
|
||||
NestjsQueryGraphQLModule.forFeature({
|
||||
imports: [NestjsQueryTypeOrmModule.forFeature([RefreshToken])],
|
||||
services: [RefreshTokenService],
|
||||
|
||||
@ -4,12 +4,12 @@ import { TypeOrmQueryService } from '@ptc-org/nestjs-query-typeorm';
|
||||
import { Repository } from 'typeorm';
|
||||
|
||||
import { assert } from 'src/utils/assert';
|
||||
import { User } from 'src/coreV2/user/user.entity';
|
||||
import { UserV2 } from 'src/coreV2/user/user.entity';
|
||||
|
||||
export class UserService extends TypeOrmQueryService<User> {
|
||||
export class UserService extends TypeOrmQueryService<UserV2> {
|
||||
constructor(
|
||||
@InjectRepository(User)
|
||||
private readonly userRepository: Repository<User>,
|
||||
@InjectRepository(UserV2)
|
||||
private readonly userRepository: Repository<UserV2>,
|
||||
) {
|
||||
super(userRepository);
|
||||
}
|
||||
|
||||
@ -1,14 +1,12 @@
|
||||
import { SortDirection } from '@ptc-org/nestjs-query-core';
|
||||
import {
|
||||
AutoResolverOpts,
|
||||
ReadResolverOpts,
|
||||
PagingStrategies,
|
||||
} from '@ptc-org/nestjs-query-graphql';
|
||||
|
||||
import { UserV2 } from 'src/coreV2/user/user.entity';
|
||||
import { JwtAuthGuard } from 'src/guards/jwt.auth.guard';
|
||||
|
||||
import { User } from './user.entity';
|
||||
|
||||
export const userAutoResolverOpts: AutoResolverOpts<
|
||||
any,
|
||||
any,
|
||||
@ -18,12 +16,13 @@ export const userAutoResolverOpts: AutoResolverOpts<
|
||||
PagingStrategies
|
||||
>[] = [
|
||||
{
|
||||
EntityClass: User,
|
||||
DTOClass: User,
|
||||
EntityClass: UserV2,
|
||||
DTOClass: UserV2,
|
||||
enableTotalCount: true,
|
||||
pagingStrategy: PagingStrategies.CURSOR,
|
||||
read: {
|
||||
defaultSort: [{ field: 'id', direction: SortDirection.DESC }],
|
||||
many: { disabled: true },
|
||||
one: { disabled: true },
|
||||
},
|
||||
create: {
|
||||
many: { disabled: true },
|
||||
|
||||
@ -13,8 +13,8 @@ import { GraphQLJSONObject } from 'graphql-type-json';
|
||||
|
||||
import { RefreshToken } from 'src/coreV2/refresh-token/refresh-token.entity';
|
||||
|
||||
@Entity('users')
|
||||
@ObjectType('user')
|
||||
@Entity('userV2')
|
||||
@ObjectType('userV2')
|
||||
// @Authorize({
|
||||
// authorize: (context: any) => ({
|
||||
// // FIXME: We do not have this relation in the database
|
||||
@ -23,7 +23,7 @@ import { RefreshToken } from 'src/coreV2/refresh-token/refresh-token.entity';
|
||||
// },
|
||||
// }),
|
||||
// })
|
||||
export class User {
|
||||
export class UserV2 {
|
||||
@IDField(() => ID)
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
id: string;
|
||||
|
||||
@ -5,8 +5,8 @@ import { NestjsQueryTypeOrmModule } from '@ptc-org/nestjs-query-typeorm';
|
||||
|
||||
import { AbilityModule } from 'src/ability/ability.module';
|
||||
import { FileModule } from 'src/core/file/file.module';
|
||||
import { UserV2 } from 'src/coreV2/user/user.entity';
|
||||
|
||||
import { User } from './user.entity';
|
||||
import { UserResolver } from './user.resolver';
|
||||
import { userAutoResolverOpts } from './user.auto-resolver-opts';
|
||||
|
||||
@ -15,7 +15,7 @@ import { UserService } from './services/user.service';
|
||||
@Module({
|
||||
imports: [
|
||||
NestjsQueryGraphQLModule.forFeature({
|
||||
imports: [NestjsQueryTypeOrmModule.forFeature([User])],
|
||||
imports: [NestjsQueryTypeOrmModule.forFeature([UserV2])],
|
||||
services: [UserService],
|
||||
resolvers: userAutoResolverOpts,
|
||||
}),
|
||||
|
||||
@ -26,8 +26,7 @@ import { FileUploadService } from 'src/core/file/services/file-upload.service';
|
||||
import { AuthWorkspace } from 'src/decorators/auth-workspace.decorator';
|
||||
import { assert } from 'src/utils/assert';
|
||||
import { JwtAuthGuard } from 'src/guards/jwt.auth.guard';
|
||||
|
||||
import { User } from './user.entity';
|
||||
import { UserV2 } from 'src/coreV2/user/user.entity';
|
||||
|
||||
import { UserService } from './services/user.service';
|
||||
|
||||
@ -39,7 +38,7 @@ const getHMACKey = (email?: string, key?: string | null) => {
|
||||
};
|
||||
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@Resolver(() => User)
|
||||
@Resolver(() => UserV2)
|
||||
export class UserResolver {
|
||||
constructor(
|
||||
private readonly userService: UserService,
|
||||
@ -47,8 +46,8 @@ export class UserResolver {
|
||||
private readonly fileUploadService: FileUploadService,
|
||||
) {}
|
||||
|
||||
@Query(() => User)
|
||||
async currentUser(@AuthUser() { id }: User) {
|
||||
@Query(() => UserV2)
|
||||
async currentUserV2(@AuthUser() { id }: UserV2) {
|
||||
const user = await this.userService.findById(id);
|
||||
assert(user, 'User not found');
|
||||
return user;
|
||||
@ -57,14 +56,14 @@ export class UserResolver {
|
||||
@ResolveField(() => String, {
|
||||
nullable: false,
|
||||
})
|
||||
displayName(@Parent() parent: User): string {
|
||||
displayName(@Parent() parent: UserV2): string {
|
||||
return `${parent.firstName ?? ''} ${parent.lastName ?? ''}`;
|
||||
}
|
||||
|
||||
@ResolveField(() => String, {
|
||||
nullable: true,
|
||||
})
|
||||
supportUserHash(@Parent() parent: User): string | null {
|
||||
supportUserHash(@Parent() parent: UserV2): string | null {
|
||||
if (this.environmentService.getSupportDriver() !== SupportDriver.Front) {
|
||||
return null;
|
||||
}
|
||||
@ -73,8 +72,8 @@ export class UserResolver {
|
||||
}
|
||||
|
||||
@Mutation(() => String)
|
||||
async uploadProfilePicture(
|
||||
@AuthUser() { id }: User,
|
||||
async uploadProfilePictureV2(
|
||||
@AuthUser() { id }: UserV2,
|
||||
@Args({ name: 'file', type: () => GraphQLUpload })
|
||||
{ createReadStream, filename, mimetype }: FileUpload,
|
||||
): Promise<string> {
|
||||
@ -96,11 +95,11 @@ export class UserResolver {
|
||||
return paths[0];
|
||||
}
|
||||
|
||||
@Mutation(() => User)
|
||||
@Mutation(() => UserV2)
|
||||
@UseGuards(AbilityGuard)
|
||||
@CheckAbilities(DeleteUserAbilityHandler)
|
||||
async deleteUserAccount(
|
||||
@AuthUser() { id: userId }: User,
|
||||
async deleteUserV2(
|
||||
@AuthUser() { id: userId }: UserV2,
|
||||
@AuthWorkspace() { id: workspaceId }: Workspace,
|
||||
) {
|
||||
return this.userService.deleteUser({ userId, workspaceId });
|
||||
|
||||
@ -0,0 +1,5 @@
|
||||
-- DropForeignKey
|
||||
ALTER TABLE "workspace_members" DROP CONSTRAINT "workspace_members_userId_fkey";
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "users" ADD COLUMN "defaultWorkspaceId" TEXT;
|
||||
@ -0,0 +1,20 @@
|
||||
/*
|
||||
Warnings:
|
||||
|
||||
- You are about to drop the column `settingsId` on the `users` table. All the data in the column will be lost.
|
||||
|
||||
*/
|
||||
-- DropForeignKey
|
||||
ALTER TABLE "users" DROP CONSTRAINT "users_settingsId_fkey";
|
||||
|
||||
-- DropIndex
|
||||
DROP INDEX "users_settingsId_key";
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "user_settings" ADD COLUMN "userId" TEXT;
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "users" DROP COLUMN "settingsId";
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "user_settings" ADD CONSTRAINT "user_settings_userId_fkey" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
@ -0,0 +1,11 @@
|
||||
/*
|
||||
Warnings:
|
||||
|
||||
- You are about to drop the column `userId` on the `user_settings` table. All the data in the column will be lost.
|
||||
|
||||
*/
|
||||
-- DropForeignKey
|
||||
ALTER TABLE "user_settings" DROP CONSTRAINT "user_settings_userId_fkey";
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "user_settings" DROP COLUMN "userId";
|
||||
@ -102,18 +102,15 @@ model User {
|
||||
/// @Validator.IsOptional()
|
||||
canImpersonate Boolean @default(false)
|
||||
|
||||
/// @TypeGraphQL.omit(input: true)
|
||||
workspaceMember WorkspaceMember?
|
||||
companies Company[]
|
||||
companies Company[]
|
||||
/// @TypeGraphQL.omit(input: true, output: true)
|
||||
refreshTokens RefreshToken[]
|
||||
comments Comment[]
|
||||
refreshTokens RefreshToken[]
|
||||
comments Comment[]
|
||||
defaultWorkspaceId String?
|
||||
|
||||
authoredActivities Activity[] @relation(name: "authoredActivities")
|
||||
assignedActivities Activity[] @relation(name: "assignedActivities")
|
||||
authoredAttachments Attachment[] @relation(name: "authoredAttachments")
|
||||
settings UserSettings @relation(fields: [settingsId], references: [id])
|
||||
settingsId String @unique
|
||||
|
||||
/// @TypeGraphQL.omit(input: true, output: true)
|
||||
deletedAt DateTime?
|
||||
@ -138,7 +135,6 @@ model UserSettings {
|
||||
/// @Validator.IsString()
|
||||
locale String
|
||||
|
||||
user User?
|
||||
WorkspaceMember WorkspaceMember[]
|
||||
|
||||
createdAt DateTime @default(now())
|
||||
@ -195,7 +191,6 @@ model WorkspaceMember {
|
||||
/// @Validator.IsOptional()
|
||||
allowImpersonation Boolean @default(true)
|
||||
|
||||
user User @relation(fields: [userId], references: [id])
|
||||
userId String @unique
|
||||
/// @TypeGraphQL.omit(input: true, output: false)
|
||||
workspace Workspace @relation(fields: [workspaceId], references: [id])
|
||||
|
||||
@ -19,19 +19,8 @@ export const seedUsers = async (prisma: PrismaClient) => {
|
||||
locale: 'en',
|
||||
passwordHash:
|
||||
'$2b$10$66d.6DuQExxnrfI9rMqOg.U1XIYpagr6Lv05uoWLYbYmtK0HDIvS6', // Applecar2025
|
||||
settingsId: 'twenty-ge256b39-3ec3-4fe3-8997-9dcb1084c109',
|
||||
avatarUrl: null,
|
||||
workspaceMember: {
|
||||
connectOrCreate: {
|
||||
where: {
|
||||
id: '20202020-0687-4c41-b707-ed1bfca972a7',
|
||||
},
|
||||
create: {
|
||||
workspaceId: '20202020-1c25-4d02-bf25-6aeccf7ea419',
|
||||
settingsId: 'twenty-ge256b39-3ec3-4fe3-8997-9dcb1084c109',
|
||||
},
|
||||
},
|
||||
},
|
||||
defaultWorkspaceId: '20202020-1c25-4d02-bf25-6aeccf7ea419',
|
||||
},
|
||||
});
|
||||
|
||||
@ -52,15 +41,8 @@ export const seedUsers = async (prisma: PrismaClient) => {
|
||||
lastName: 'Ive',
|
||||
email: 'jony.ive@apple.dev',
|
||||
locale: 'en',
|
||||
settingsId: 'twenty-ge256b39-3ec3-4fe3-8997-2c4a2035a215',
|
||||
avatarUrl: null,
|
||||
workspaceMember: {
|
||||
create: {
|
||||
id: '20202020-77d5-4cb6-b60a-f4a835a85d61',
|
||||
workspaceId: '20202020-1c25-4d02-bf25-6aeccf7ea419',
|
||||
settingsId: 'twenty-ge256b39-3ec3-4fe3-8997-2c4a2035a215',
|
||||
},
|
||||
},
|
||||
defaultWorkspaceId: '20202020-1c25-4d02-bf25-6aeccf7ea419',
|
||||
},
|
||||
});
|
||||
|
||||
@ -81,15 +63,8 @@ export const seedUsers = async (prisma: PrismaClient) => {
|
||||
lastName: 'Schiler',
|
||||
email: 'phil.schiler@apple.dev',
|
||||
locale: 'en',
|
||||
settingsId: 'twenty-ge256b39-3ec3-4fe3-8997-8e1f2097b328',
|
||||
avatarUrl: null,
|
||||
workspaceMember: {
|
||||
create: {
|
||||
id: '20202020-1553-45c6-a028-5a9064cce07f',
|
||||
workspaceId: '20202020-1c25-4d02-bf25-6aeccf7ea419',
|
||||
settingsId: 'twenty-ge256b39-3ec3-4fe3-8997-8e1f2097b328',
|
||||
},
|
||||
},
|
||||
defaultWorkspaceId: '20202020-1c25-4d02-bf25-6aeccf7ea419',
|
||||
},
|
||||
});
|
||||
|
||||
@ -110,14 +85,6 @@ export const seedUsers = async (prisma: PrismaClient) => {
|
||||
lastName: 'Bochet',
|
||||
email: 'charles@twenty.dev',
|
||||
locale: 'en',
|
||||
settingsId: 'twenty-ge256b39-3ec3-4fe3-8997-5e2d1049c430',
|
||||
workspaceMember: {
|
||||
create: {
|
||||
id: 'twenty-dev-7ed9d213-1c25-4d02-bf35-6aeccf7oa419',
|
||||
workspaceId: 'twenty-dev-7ed9d212-1c25-4d02-bf25-6aeccf7ea420',
|
||||
settingsId: 'twenty-ge256b39-3ec3-4fe3-8997-5e2d1049c430',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
@ -8,6 +8,7 @@ const fieldMetadataTableName = 'fieldMetadata';
|
||||
export enum SeedWorkspaceMemberFieldMetadataIds {
|
||||
FirstName = '20202020-1fa8-4d38-9fa4-0cf696909298',
|
||||
LastName = '20202020-8c37-4163-ba06-1dada334ce3e',
|
||||
AvatarUrl = '20202020-7ba6-40d5-934b-17146183a212',
|
||||
Locale = '20202020-10f6-4df9-8d6f-a760b65bd800',
|
||||
ColorScheme = '20202020-83f2-4c5f-96b0-0c51ecc160e3',
|
||||
AllowImpersonation = '20202020-bb19-44a1-8156-8866f87a5f42',
|
||||
@ -77,6 +78,22 @@ export const seedWorkspaceMemberFieldMetadata = async (
|
||||
icon: 'IconCircleUser',
|
||||
isNullable: false,
|
||||
},
|
||||
{
|
||||
id: SeedWorkspaceMemberFieldMetadataIds.AvatarUrl,
|
||||
objectMetadataId: SeedObjectMetadataIds.WorkspaceMember,
|
||||
isCustom: false,
|
||||
workspaceId: SeedWorkspaceId,
|
||||
isActive: true,
|
||||
type: 'TEXT',
|
||||
name: 'avatarUrl',
|
||||
label: 'Avatar Url',
|
||||
targetColumnMap: {
|
||||
value: 'avatarUrl',
|
||||
},
|
||||
description: 'Workspace member avatar',
|
||||
icon: 'IconFileUpload',
|
||||
isNullable: true,
|
||||
},
|
||||
{
|
||||
id: SeedWorkspaceMemberFieldMetadataIds.UserId,
|
||||
objectMetadataId: SeedObjectMetadataIds.WorkspaceMember,
|
||||
@ -123,7 +140,7 @@ export const seedWorkspaceMemberFieldMetadata = async (
|
||||
},
|
||||
description: 'Preferred color scheme',
|
||||
icon: 'IconColorSwatch',
|
||||
isNullable: false,
|
||||
isNullable: true,
|
||||
},
|
||||
{
|
||||
id: SeedWorkspaceMemberFieldMetadataIds.Locale,
|
||||
|
||||
@ -37,7 +37,7 @@ export const seedWorkspaceMember = async (
|
||||
firstName: 'Tim',
|
||||
lastName: 'Apple',
|
||||
locale: 'en',
|
||||
colorScheme: 'light',
|
||||
colorScheme: 'Light',
|
||||
allowImpersonation: true,
|
||||
userId: WorkspaceMemberUserIds.Tim,
|
||||
},
|
||||
@ -46,7 +46,7 @@ export const seedWorkspaceMember = async (
|
||||
firstName: 'Jony',
|
||||
lastName: 'Ive',
|
||||
locale: 'en',
|
||||
colorScheme: 'light',
|
||||
colorScheme: 'Light',
|
||||
allowImpersonation: true,
|
||||
userId: WorkspaceMemberUserIds.Jony,
|
||||
},
|
||||
@ -55,9 +55,9 @@ export const seedWorkspaceMember = async (
|
||||
firstName: 'Phil',
|
||||
lastName: 'Shiler',
|
||||
locale: 'en',
|
||||
colorScheme: 'light',
|
||||
colorScheme: 'Light',
|
||||
allowImpersonation: true,
|
||||
userId: WorkspaceMemberUserIds.Phil,
|
||||
userId: WorkspaceMemberUserIds.Tim,
|
||||
},
|
||||
])
|
||||
.execute();
|
||||
|
||||
@ -1,4 +1,10 @@
|
||||
import { ObjectType, ID, Field, HideField } from '@nestjs/graphql';
|
||||
import {
|
||||
ObjectType,
|
||||
ID,
|
||||
Field,
|
||||
HideField,
|
||||
registerEnumType,
|
||||
} from '@nestjs/graphql';
|
||||
|
||||
import { CreateDateColumn, UpdateDateColumn } from 'typeorm';
|
||||
import {
|
||||
@ -11,6 +17,11 @@ import {
|
||||
import { ObjectMetadataDTO } from 'src/metadata/object-metadata/dtos/object-metadata.dto';
|
||||
import { RelationMetadataType } from 'src/metadata/relation-metadata/relation-metadata.entity';
|
||||
|
||||
registerEnumType(RelationMetadataType, {
|
||||
name: 'RelationMetadataType',
|
||||
description: 'Type of the relation',
|
||||
});
|
||||
|
||||
@ObjectType('relation')
|
||||
@Authorize({
|
||||
authorize: (context: any) => ({
|
||||
|
||||
@ -1,5 +1,3 @@
|
||||
import { registerEnumType } from '@nestjs/graphql';
|
||||
|
||||
import {
|
||||
Column,
|
||||
CreateDateColumn,
|
||||
@ -22,11 +20,6 @@ export enum RelationMetadataType {
|
||||
MANY_TO_MANY = 'MANY_TO_MANY',
|
||||
}
|
||||
|
||||
registerEnumType(RelationMetadataType, {
|
||||
name: 'RelationMetadataType',
|
||||
description: 'Type of the relation',
|
||||
});
|
||||
|
||||
@Entity('relationMetadata')
|
||||
export class RelationMetadataEntity implements RelationMetadataInterface {
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
|
||||
@ -22,6 +22,11 @@ export const addWorkspaceMemberTable: TenantMigrationTableAction[] = [
|
||||
columnType: 'varchar',
|
||||
action: TenantMigrationColumnActionType.CREATE,
|
||||
},
|
||||
{
|
||||
columnName: 'avatarUrl',
|
||||
columnType: 'varchar',
|
||||
action: TenantMigrationColumnActionType.CREATE,
|
||||
},
|
||||
{
|
||||
columnName: 'colorScheme',
|
||||
columnType: 'varchar',
|
||||
|
||||
@ -0,0 +1,51 @@
|
||||
import { EntityManager } from 'typeorm';
|
||||
|
||||
export const companyPrefillData = async (
|
||||
entityManager: EntityManager,
|
||||
schemaName: string,
|
||||
) => {
|
||||
await entityManager
|
||||
.createQueryBuilder()
|
||||
.insert()
|
||||
.into(`${schemaName}.company`, [
|
||||
'name',
|
||||
'domainName',
|
||||
'address',
|
||||
'employees',
|
||||
])
|
||||
.orIgnore()
|
||||
.values([
|
||||
{
|
||||
name: 'Airbnb',
|
||||
domainName: 'airbnb.com',
|
||||
address: 'San Francisco',
|
||||
employees: 5000,
|
||||
},
|
||||
{
|
||||
name: 'Qonto',
|
||||
domainName: 'qonto.com',
|
||||
address: 'San Francisco',
|
||||
employees: 800,
|
||||
},
|
||||
{
|
||||
name: 'Stripe',
|
||||
domainName: 'stripe.com',
|
||||
address: 'San Francisco',
|
||||
employees: 8000,
|
||||
},
|
||||
{
|
||||
name: 'Figma',
|
||||
domainName: 'figma.com',
|
||||
address: 'San Francisco',
|
||||
employees: 800,
|
||||
},
|
||||
{
|
||||
name: 'Notion',
|
||||
domainName: 'notion.com',
|
||||
address: 'San Francisco',
|
||||
employees: 400,
|
||||
},
|
||||
])
|
||||
.returning('*')
|
||||
.execute();
|
||||
};
|
||||
File diff suppressed because one or more lines are too long
@ -0,0 +1,41 @@
|
||||
import { EntityManager } from 'typeorm';
|
||||
|
||||
export const pipelineStepPrefillData = async (
|
||||
entityManager: EntityManager,
|
||||
schemaName: string,
|
||||
) => {
|
||||
await entityManager
|
||||
.createQueryBuilder()
|
||||
.insert()
|
||||
.into(`${schemaName}.pipelineStep`, ['name', 'color', 'position'])
|
||||
.orIgnore()
|
||||
.values([
|
||||
{
|
||||
name: 'New',
|
||||
color: 'red',
|
||||
position: 0,
|
||||
},
|
||||
{
|
||||
name: 'Screening',
|
||||
color: 'purple',
|
||||
position: 1,
|
||||
},
|
||||
{
|
||||
name: 'Meeting',
|
||||
color: 'sky',
|
||||
position: 2,
|
||||
},
|
||||
{
|
||||
name: 'Proposal',
|
||||
color: 'turquoise',
|
||||
position: 3,
|
||||
},
|
||||
{
|
||||
name: 'Customer',
|
||||
color: 'yellow',
|
||||
position: 4,
|
||||
},
|
||||
])
|
||||
.returning('*')
|
||||
.execute();
|
||||
};
|
||||
@ -1,269 +1,31 @@
|
||||
import { DataSource, EntityManager } from 'typeorm';
|
||||
|
||||
import { ObjectMetadataEntity } from 'src/metadata/object-metadata/object-metadata.entity';
|
||||
import { viewPrefillData } from 'src/tenant-manager/standard-objects-prefill-data/view';
|
||||
import { companyPrefillData } from 'src/tenant-manager/standard-objects-prefill-data/company';
|
||||
import { personPrefillData } from 'src/tenant-manager/standard-objects-prefill-data/person';
|
||||
import { pipelineStepPrefillData } from 'src/tenant-manager/standard-objects-prefill-data/pipeline-step';
|
||||
|
||||
export const standardObjectsPrefillData = async (
|
||||
workspaceDataSource: DataSource,
|
||||
schemaName: string,
|
||||
objectMetadata: ObjectMetadataEntity[],
|
||||
) => {
|
||||
const objectMetadataMap = objectMetadata.reduce((acc, object) => {
|
||||
acc[object.nameSingular] = {
|
||||
id: object.id,
|
||||
fields: object.fields.reduce((acc, field) => {
|
||||
acc[field.name] = field.id;
|
||||
return acc;
|
||||
}, {}),
|
||||
};
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
workspaceDataSource.transaction(async (entityManager: EntityManager) => {
|
||||
const createdCompanies = await entityManager
|
||||
.createQueryBuilder()
|
||||
.insert()
|
||||
.into(`${schemaName}.company`, [
|
||||
'name',
|
||||
'domainName',
|
||||
'address',
|
||||
'employees',
|
||||
])
|
||||
.orIgnore()
|
||||
.values([
|
||||
{
|
||||
name: 'Airbnb',
|
||||
domainName: 'airbnb.com',
|
||||
address: 'San Francisco',
|
||||
employees: 5000,
|
||||
},
|
||||
{
|
||||
name: 'Qonto',
|
||||
domainName: 'qonto.com',
|
||||
address: 'San Francisco',
|
||||
employees: 800,
|
||||
},
|
||||
{
|
||||
name: 'Stripe',
|
||||
domainName: 'stripe.com',
|
||||
address: 'San Francisco',
|
||||
employees: 8000,
|
||||
},
|
||||
{
|
||||
name: 'Figma',
|
||||
domainName: 'figma.com',
|
||||
address: 'San Francisco',
|
||||
employees: 800,
|
||||
},
|
||||
{
|
||||
name: 'Notion',
|
||||
domainName: 'notion.com',
|
||||
address: 'San Francisco',
|
||||
employees: 400,
|
||||
},
|
||||
])
|
||||
.returning('*')
|
||||
.execute();
|
||||
|
||||
const companyIdMap = createdCompanies.raw.reduce((acc, view) => {
|
||||
acc[view.name] = view.id;
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
const createdViews = await entityManager
|
||||
.createQueryBuilder()
|
||||
.insert()
|
||||
.into(`${schemaName}.view`, ['name', 'objectMetadataId', 'type'])
|
||||
.orIgnore()
|
||||
.values([
|
||||
{
|
||||
name: 'All companies',
|
||||
objectMetadataId: 'company',
|
||||
type: 'table',
|
||||
},
|
||||
{
|
||||
name: 'All people',
|
||||
objectMetadataId: 'person',
|
||||
type: 'table',
|
||||
},
|
||||
{
|
||||
name: 'All opportunities',
|
||||
objectMetadataId: 'company',
|
||||
type: 'kanban',
|
||||
},
|
||||
{
|
||||
name: 'All Companies (V2)',
|
||||
objectMetadataId: companyIdMap['Airbnb'],
|
||||
type: 'table',
|
||||
},
|
||||
])
|
||||
.returning('*')
|
||||
.execute();
|
||||
|
||||
const viewIdMap = createdViews.raw.reduce((acc, view) => {
|
||||
acc[view.name] = view.id;
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
await entityManager
|
||||
.createQueryBuilder()
|
||||
.insert()
|
||||
.into(`${schemaName}.viewField`, [
|
||||
'fieldMetadataId',
|
||||
'viewId',
|
||||
'position',
|
||||
'isVisible',
|
||||
'size',
|
||||
])
|
||||
.orIgnore()
|
||||
.values([
|
||||
{
|
||||
fieldMetadataId: 'name',
|
||||
viewId: viewIdMap['All Companies (V2)'],
|
||||
position: 0,
|
||||
isVisible: true,
|
||||
size: 180,
|
||||
},
|
||||
{
|
||||
fieldMetadataId: 'name',
|
||||
viewId: viewIdMap['All companies'],
|
||||
position: 0,
|
||||
isVisible: true,
|
||||
size: 180,
|
||||
},
|
||||
{
|
||||
fieldMetadataId: 'domainName',
|
||||
viewId: viewIdMap['All companies'],
|
||||
position: 1,
|
||||
isVisible: true,
|
||||
size: 100,
|
||||
},
|
||||
{
|
||||
fieldMetadataId: 'accountOwner',
|
||||
viewId: viewIdMap['All companies'],
|
||||
position: 2,
|
||||
isVisible: true,
|
||||
size: 150,
|
||||
},
|
||||
{
|
||||
fieldMetadataId: 'createdAt',
|
||||
viewId: viewIdMap['All companies'],
|
||||
position: 3,
|
||||
isVisible: true,
|
||||
size: 150,
|
||||
},
|
||||
{
|
||||
fieldMetadataId: 'employees',
|
||||
viewId: viewIdMap['All companies'],
|
||||
position: 4,
|
||||
isVisible: true,
|
||||
size: 150,
|
||||
},
|
||||
{
|
||||
fieldMetadataId: 'linkedin',
|
||||
viewId: viewIdMap['All companies'],
|
||||
position: 5,
|
||||
isVisible: true,
|
||||
size: 170,
|
||||
},
|
||||
{
|
||||
fieldMetadataId: 'address',
|
||||
viewId: viewIdMap['All companies'],
|
||||
position: 6,
|
||||
isVisible: true,
|
||||
size: 170,
|
||||
},
|
||||
{
|
||||
fieldMetadataId: 'displayName',
|
||||
viewId: viewIdMap['All people'],
|
||||
position: 0,
|
||||
isVisible: true,
|
||||
size: 210,
|
||||
},
|
||||
{
|
||||
fieldMetadataId: 'email',
|
||||
viewId: viewIdMap['All people'],
|
||||
position: 1,
|
||||
isVisible: true,
|
||||
size: 150,
|
||||
},
|
||||
{
|
||||
fieldMetadataId: 'company',
|
||||
viewId: viewIdMap['All people'],
|
||||
position: 2,
|
||||
isVisible: true,
|
||||
size: 150,
|
||||
},
|
||||
{
|
||||
fieldMetadataId: 'phone',
|
||||
viewId: viewIdMap['All people'],
|
||||
position: 3,
|
||||
isVisible: true,
|
||||
size: 150,
|
||||
},
|
||||
{
|
||||
fieldMetadataId: 'createdAt',
|
||||
viewId: viewIdMap['All people'],
|
||||
position: 4,
|
||||
isVisible: true,
|
||||
size: 150,
|
||||
},
|
||||
{
|
||||
fieldMetadataId: 'city',
|
||||
viewId: viewIdMap['All people'],
|
||||
position: 5,
|
||||
isVisible: true,
|
||||
size: 150,
|
||||
},
|
||||
{
|
||||
fieldMetadataId: 'jobTitle',
|
||||
viewId: viewIdMap['All people'],
|
||||
position: 6,
|
||||
isVisible: true,
|
||||
size: 150,
|
||||
},
|
||||
{
|
||||
fieldMetadataId: 'linkedin',
|
||||
viewId: viewIdMap['All people'],
|
||||
position: 7,
|
||||
isVisible: true,
|
||||
size: 150,
|
||||
},
|
||||
{
|
||||
fieldMetadataId: 'x',
|
||||
viewId: viewIdMap['All people'],
|
||||
position: 8,
|
||||
isVisible: true,
|
||||
size: 150,
|
||||
},
|
||||
{
|
||||
fieldMetadataId: 'amount',
|
||||
viewId: viewIdMap['All opportunities'],
|
||||
position: 0,
|
||||
isVisible: true,
|
||||
size: 180,
|
||||
},
|
||||
{
|
||||
fieldMetadataId: 'probability',
|
||||
viewId: viewIdMap['All opportunities'],
|
||||
position: 1,
|
||||
isVisible: true,
|
||||
size: 150,
|
||||
},
|
||||
{
|
||||
fieldMetadataId: 'closeDate',
|
||||
viewId: viewIdMap['All opportunities'],
|
||||
position: 2,
|
||||
isVisible: true,
|
||||
size: 100,
|
||||
},
|
||||
{
|
||||
fieldMetadataId: 'company',
|
||||
viewId: viewIdMap['All opportunities'],
|
||||
position: 3,
|
||||
isVisible: true,
|
||||
size: 150,
|
||||
},
|
||||
{
|
||||
fieldMetadataId: 'createdAt',
|
||||
viewId: viewIdMap['All opportunities'],
|
||||
position: 4,
|
||||
isVisible: true,
|
||||
size: 150,
|
||||
},
|
||||
{
|
||||
fieldMetadataId: 'pointOfContact',
|
||||
viewId: viewIdMap['All opportunities'],
|
||||
position: 5,
|
||||
isVisible: true,
|
||||
size: 150,
|
||||
},
|
||||
])
|
||||
.execute();
|
||||
await companyPrefillData(entityManager, schemaName);
|
||||
await personPrefillData(entityManager, schemaName);
|
||||
await viewPrefillData(entityManager, schemaName, objectMetadataMap);
|
||||
await pipelineStepPrefillData(entityManager, schemaName);
|
||||
});
|
||||
};
|
||||
|
||||
278
server/src/tenant-manager/standard-objects-prefill-data/view.ts
Normal file
278
server/src/tenant-manager/standard-objects-prefill-data/view.ts
Normal file
@ -0,0 +1,278 @@
|
||||
import { EntityManager } from 'typeorm';
|
||||
|
||||
import { ObjectMetadataEntity } from 'src/metadata/object-metadata/object-metadata.entity';
|
||||
|
||||
export const viewPrefillData = async (
|
||||
entityManager: EntityManager,
|
||||
schemaName: string,
|
||||
objectMetadataMap: Record<string, ObjectMetadataEntity>,
|
||||
) => {
|
||||
// Creating views
|
||||
const createdViews = await entityManager
|
||||
.createQueryBuilder()
|
||||
.insert()
|
||||
.into(`${schemaName}.view`, ['name', 'objectMetadataId', 'type'])
|
||||
.orIgnore()
|
||||
.values([
|
||||
{
|
||||
name: 'All companies',
|
||||
objectMetadataId: 'company',
|
||||
type: 'table',
|
||||
},
|
||||
{
|
||||
name: 'All people',
|
||||
objectMetadataId: 'person',
|
||||
type: 'table',
|
||||
},
|
||||
{
|
||||
name: 'All opportunities',
|
||||
objectMetadataId: 'company',
|
||||
type: 'kanban',
|
||||
},
|
||||
{
|
||||
name: 'All Companies (V2)',
|
||||
objectMetadataId: objectMetadataMap['companyV2'].id,
|
||||
type: 'table',
|
||||
},
|
||||
{
|
||||
name: 'All People (V2)',
|
||||
objectMetadataId: objectMetadataMap['personV2'].id,
|
||||
type: 'table',
|
||||
},
|
||||
{
|
||||
name: 'All Opportunities (V2)',
|
||||
objectMetadataId: objectMetadataMap['companyV2'].id,
|
||||
type: 'kanban',
|
||||
},
|
||||
])
|
||||
.returning('*')
|
||||
.execute();
|
||||
|
||||
const viewIdMap = createdViews.raw.reduce((acc, view) => {
|
||||
acc[view.name] = view.id;
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
// Creating viewFields
|
||||
await entityManager
|
||||
.createQueryBuilder()
|
||||
.insert()
|
||||
.into(`${schemaName}.viewField`, [
|
||||
'fieldMetadataId',
|
||||
'viewId',
|
||||
'position',
|
||||
'isVisible',
|
||||
'size',
|
||||
])
|
||||
.orIgnore()
|
||||
.values([
|
||||
// CompanyV2
|
||||
{
|
||||
fieldMetadataId: objectMetadataMap['companyV2'].fields['name'],
|
||||
viewId: viewIdMap['All Companies (V2)'],
|
||||
position: 0,
|
||||
isVisible: true,
|
||||
size: 180,
|
||||
},
|
||||
{
|
||||
fieldMetadataId: objectMetadataMap['companyV2'].fields['domainName'],
|
||||
viewId: viewIdMap['All Companies (V2)'],
|
||||
position: 1,
|
||||
isVisible: true,
|
||||
size: 100,
|
||||
},
|
||||
// {
|
||||
// fieldMetadataId: objectMetadataMap['companyV2'].fields['accountOwner'],
|
||||
// viewId: viewIdMap['All Companies (V2)'],
|
||||
// position: 2,
|
||||
// isVisible: true,
|
||||
// size: 150,
|
||||
// },
|
||||
// {
|
||||
// fieldMetadataId: 'createdAt',
|
||||
// viewId: viewIdMap['All Companies (V2)'],
|
||||
// position: 3,
|
||||
// isVisible: true,
|
||||
// size: 150,
|
||||
// },
|
||||
{
|
||||
fieldMetadataId: objectMetadataMap['companyV2'].fields['employees'],
|
||||
viewId: viewIdMap['All Companies (V2)'],
|
||||
position: 4,
|
||||
isVisible: true,
|
||||
size: 150,
|
||||
},
|
||||
{
|
||||
fieldMetadataId: objectMetadataMap['companyV2'].fields['linkedinUrl'],
|
||||
viewId: viewIdMap['All Companies (V2)'],
|
||||
position: 5,
|
||||
isVisible: true,
|
||||
size: 170,
|
||||
},
|
||||
{
|
||||
fieldMetadataId: objectMetadataMap['companyV2'].fields['address'],
|
||||
viewId: viewIdMap['All Companies (V2)'],
|
||||
position: 6,
|
||||
isVisible: true,
|
||||
size: 170,
|
||||
},
|
||||
// PeopleV2
|
||||
{
|
||||
fieldMetadataId: objectMetadataMap['personV2'].fields['firstName'], // TODO: change to displayName once we have name field type
|
||||
viewId: viewIdMap['All People (V2)'],
|
||||
position: 0,
|
||||
isVisible: true,
|
||||
size: 210,
|
||||
},
|
||||
{
|
||||
fieldMetadataId: objectMetadataMap['personV2'].fields['email'],
|
||||
viewId: viewIdMap['All People (V2)'],
|
||||
position: 1,
|
||||
isVisible: true,
|
||||
size: 150,
|
||||
},
|
||||
// {
|
||||
// fieldMetadataId: objectMetadataMap['personV2'].fields['company'],
|
||||
// viewId: viewIdMap['All People (V2)'],
|
||||
// position: 2,
|
||||
// isVisible: true,
|
||||
// size: 150,
|
||||
// },
|
||||
{
|
||||
fieldMetadataId: objectMetadataMap['personV2'].fields['phone'],
|
||||
viewId: viewIdMap['All People (V2)'],
|
||||
position: 3,
|
||||
isVisible: true,
|
||||
size: 150,
|
||||
},
|
||||
// {
|
||||
// fieldMetadataId: 'createdAt',
|
||||
// viewId: viewIdMap['All People (V2)'],
|
||||
// position: 4,
|
||||
// isVisible: true,
|
||||
// size: 150,
|
||||
// },
|
||||
{
|
||||
fieldMetadataId: objectMetadataMap['personV2'].fields['city'],
|
||||
viewId: viewIdMap['All People (V2)'],
|
||||
position: 5,
|
||||
isVisible: true,
|
||||
size: 150,
|
||||
},
|
||||
{
|
||||
fieldMetadataId: objectMetadataMap['personV2'].fields['jobTitle'],
|
||||
viewId: viewIdMap['All People (V2)'],
|
||||
position: 6,
|
||||
isVisible: true,
|
||||
size: 150,
|
||||
},
|
||||
{
|
||||
fieldMetadataId: objectMetadataMap['personV2'].fields['linkedinUrl'],
|
||||
viewId: viewIdMap['All People (V2)'],
|
||||
position: 7,
|
||||
isVisible: true,
|
||||
size: 150,
|
||||
},
|
||||
{
|
||||
fieldMetadataId: objectMetadataMap['personV2'].fields['xUrl'],
|
||||
viewId: viewIdMap['All People (V2)'],
|
||||
position: 8,
|
||||
isVisible: true,
|
||||
size: 150,
|
||||
},
|
||||
// Companies
|
||||
{
|
||||
fieldMetadataId: 'name',
|
||||
viewId: viewIdMap['All companies'],
|
||||
position: 0,
|
||||
isVisible: true,
|
||||
size: 180,
|
||||
},
|
||||
{
|
||||
fieldMetadataId: 'domainName',
|
||||
viewId: viewIdMap['All companies'],
|
||||
position: 1,
|
||||
isVisible: true,
|
||||
size: 100,
|
||||
},
|
||||
{
|
||||
fieldMetadataId: 'accountOwner',
|
||||
viewId: viewIdMap['All companies'],
|
||||
position: 2,
|
||||
isVisible: true,
|
||||
size: 150,
|
||||
},
|
||||
{
|
||||
fieldMetadataId: 'createdAt',
|
||||
viewId: viewIdMap['All companies'],
|
||||
position: 3,
|
||||
isVisible: true,
|
||||
size: 150,
|
||||
},
|
||||
{
|
||||
fieldMetadataId: 'employees',
|
||||
viewId: viewIdMap['All companies'],
|
||||
position: 4,
|
||||
isVisible: true,
|
||||
size: 150,
|
||||
},
|
||||
{
|
||||
fieldMetadataId: 'linkedin',
|
||||
viewId: viewIdMap['All companies'],
|
||||
position: 5,
|
||||
isVisible: true,
|
||||
size: 170,
|
||||
},
|
||||
{
|
||||
fieldMetadataId: 'address',
|
||||
viewId: viewIdMap['All companies'],
|
||||
position: 6,
|
||||
isVisible: true,
|
||||
size: 170,
|
||||
},
|
||||
// Opportunities
|
||||
{
|
||||
fieldMetadataId: 'amount',
|
||||
viewId: viewIdMap['All opportunities'],
|
||||
position: 0,
|
||||
isVisible: true,
|
||||
size: 180,
|
||||
},
|
||||
{
|
||||
fieldMetadataId: 'probability',
|
||||
viewId: viewIdMap['All opportunities'],
|
||||
position: 1,
|
||||
isVisible: true,
|
||||
size: 150,
|
||||
},
|
||||
{
|
||||
fieldMetadataId: 'closeDate',
|
||||
viewId: viewIdMap['All opportunities'],
|
||||
position: 2,
|
||||
isVisible: true,
|
||||
size: 100,
|
||||
},
|
||||
{
|
||||
fieldMetadataId: 'company',
|
||||
viewId: viewIdMap['All opportunities'],
|
||||
position: 3,
|
||||
isVisible: true,
|
||||
size: 150,
|
||||
},
|
||||
{
|
||||
fieldMetadataId: 'createdAt',
|
||||
viewId: viewIdMap['All opportunities'],
|
||||
position: 4,
|
||||
isVisible: true,
|
||||
size: 150,
|
||||
},
|
||||
{
|
||||
fieldMetadataId: 'pointOfContact',
|
||||
viewId: viewIdMap['All opportunities'],
|
||||
position: 5,
|
||||
isVisible: true,
|
||||
size: 150,
|
||||
},
|
||||
])
|
||||
.execute();
|
||||
};
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user