Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7f1814805d | ||
|
|
34b81adce8 | ||
|
|
cd2e08b912 | ||
|
|
fb21f3ccf5 | ||
|
|
281bb6d783 | ||
|
|
c96c034908 | ||
|
|
5efe69f8d3 | ||
|
|
c2b058a6a7 | ||
|
|
22c9693ce5 | ||
|
|
50ea560e57 | ||
|
|
56056b885d | ||
|
|
db9da3194f | ||
|
|
68f5e70ade | ||
|
|
08077476f3 | ||
|
|
6f0ac88e20 | ||
|
|
17424320e3 | ||
|
|
6360fb3bce | ||
|
|
da1b1f1cbc | ||
|
|
31718d163c | ||
|
|
f47608de07 | ||
|
|
695518a15e |
@@ -143,7 +143,6 @@ jobs:
|
||||
set_env_var "CLICKHOUSE_PASSWORD" "clickhousePassword"
|
||||
|
||||
npx nx run twenty-server:database:init:prod
|
||||
npx nx run twenty-server:database:migrate:prod
|
||||
|
||||
- name: Seed current branch database with test data
|
||||
run: |
|
||||
@@ -296,7 +295,6 @@ jobs:
|
||||
set_env_var "CLICKHOUSE_PASSWORD" "clickhousePassword"
|
||||
|
||||
npx nx run twenty-server:database:init:prod
|
||||
npx nx run twenty-server:database:migrate:prod
|
||||
|
||||
- name: Seed main branch database with test data
|
||||
run: |
|
||||
|
||||
@@ -122,7 +122,6 @@ jobs:
|
||||
PGPASSWORD=postgres psql -h localhost -p 5432 -U postgres -d postgres -c 'CREATE DATABASE "default";'
|
||||
PGPASSWORD=postgres psql -h localhost -p 5432 -U postgres -d postgres -c 'CREATE DATABASE "test";'
|
||||
npx nx run twenty-server:database:init:prod
|
||||
npx nx run twenty-server:database:migrate:prod
|
||||
- name: Worker / Run
|
||||
run: |
|
||||
timeout 30s npx nx run twenty-server:worker || exit_code=$?
|
||||
|
||||
@@ -203,6 +203,7 @@
|
||||
"packages/twenty-utils",
|
||||
"packages/twenty-zapier",
|
||||
"packages/twenty-website",
|
||||
"packages/twenty-website-new",
|
||||
"packages/twenty-docs",
|
||||
"packages/twenty-e2e-testing",
|
||||
"packages/twenty-shared",
|
||||
|
||||
@@ -343,6 +343,7 @@ type Field {
|
||||
defaultValue: JSON
|
||||
options: JSON
|
||||
settings: JSON
|
||||
objectMetadataId: UUID!
|
||||
isLabelSyncedWithName: Boolean
|
||||
morphId: UUID
|
||||
createdAt: DateTime!
|
||||
@@ -540,6 +541,7 @@ input FieldFilter {
|
||||
isActive: BooleanFieldComparison
|
||||
isSystem: BooleanFieldComparison
|
||||
isUIReadOnly: BooleanFieldComparison
|
||||
objectMetadataId: UUIDFilterComparison
|
||||
}
|
||||
|
||||
input IndexFilter {
|
||||
@@ -2273,6 +2275,11 @@ type FieldConnection {
|
||||
edges: [FieldEdge!]!
|
||||
}
|
||||
|
||||
type LogicFunctionLogs {
|
||||
"""Execution Logs"""
|
||||
logs: String!
|
||||
}
|
||||
|
||||
type DeleteTwoFactorAuthenticationMethod {
|
||||
"""Boolean that confirms query was dispatched"""
|
||||
success: Boolean!
|
||||
@@ -2593,11 +2600,6 @@ type UsageAnalytics {
|
||||
userDailyUsage: UsageUserDaily
|
||||
}
|
||||
|
||||
type LogicFunctionLogs {
|
||||
"""Execution Logs"""
|
||||
logs: String!
|
||||
}
|
||||
|
||||
type FrontComponent {
|
||||
id: UUID!
|
||||
name: String!
|
||||
@@ -3090,6 +3092,7 @@ enum AllMetadataName {
|
||||
navigationMenuItem
|
||||
permissionFlag
|
||||
objectPermission
|
||||
fieldPermission
|
||||
frontComponent
|
||||
webhook
|
||||
}
|
||||
@@ -3501,6 +3504,7 @@ type Mutation {
|
||||
createViewGroup(input: CreateViewGroupInput!): ViewGroup!
|
||||
createManyViewGroups(inputs: [CreateViewGroupInput!]!): [ViewGroup!]!
|
||||
updateViewGroup(input: UpdateViewGroupInput!): ViewGroup!
|
||||
updateManyViewGroups(inputs: [UpdateViewGroupInput!]!): [ViewGroup!]!
|
||||
deleteViewGroup(input: DeleteViewGroupInput!): ViewGroup!
|
||||
destroyViewGroup(input: DestroyViewGroupInput!): ViewGroup!
|
||||
updateMessageFolder(input: UpdateMessageFolderInput!): MessageFolder!
|
||||
@@ -4272,8 +4276,8 @@ input CreateFieldInput {
|
||||
defaultValue: JSON
|
||||
options: JSON
|
||||
settings: JSON
|
||||
isLabelSyncedWithName: Boolean
|
||||
objectMetadataId: UUID!
|
||||
isLabelSyncedWithName: Boolean
|
||||
isRemoteCreation: Boolean
|
||||
relationCreationPayload: JSON
|
||||
morphRelationsCreationPayload: [JSON!]
|
||||
@@ -4301,6 +4305,7 @@ input UpdateFieldInput {
|
||||
defaultValue: JSON
|
||||
options: JSON
|
||||
settings: JSON
|
||||
objectMetadataId: UUID
|
||||
isLabelSyncedWithName: Boolean
|
||||
morphRelationsUpdatePayload: [JSON!]
|
||||
}
|
||||
|
||||
@@ -296,6 +296,7 @@ export interface Field {
|
||||
defaultValue?: Scalars['JSON']
|
||||
options?: Scalars['JSON']
|
||||
settings?: Scalars['JSON']
|
||||
objectMetadataId: Scalars['UUID']
|
||||
isLabelSyncedWithName?: Scalars['Boolean']
|
||||
morphId?: Scalars['UUID']
|
||||
createdAt: Scalars['DateTime']
|
||||
@@ -1937,6 +1938,12 @@ export interface FieldConnection {
|
||||
__typename: 'FieldConnection'
|
||||
}
|
||||
|
||||
export interface LogicFunctionLogs {
|
||||
/** Execution Logs */
|
||||
logs: Scalars['String']
|
||||
__typename: 'LogicFunctionLogs'
|
||||
}
|
||||
|
||||
export interface DeleteTwoFactorAuthenticationMethod {
|
||||
/** Boolean that confirms query was dispatched */
|
||||
success: Scalars['Boolean']
|
||||
@@ -2298,12 +2305,6 @@ export interface UsageAnalytics {
|
||||
__typename: 'UsageAnalytics'
|
||||
}
|
||||
|
||||
export interface LogicFunctionLogs {
|
||||
/** Execution Logs */
|
||||
logs: Scalars['String']
|
||||
__typename: 'LogicFunctionLogs'
|
||||
}
|
||||
|
||||
export interface FrontComponent {
|
||||
id: Scalars['UUID']
|
||||
name: Scalars['String']
|
||||
@@ -2676,7 +2677,7 @@ export interface CollectionHash {
|
||||
__typename: 'CollectionHash'
|
||||
}
|
||||
|
||||
export type AllMetadataName = 'fieldMetadata' | 'objectMetadata' | 'view' | 'viewField' | 'viewFieldGroup' | 'viewGroup' | 'viewSort' | 'rowLevelPermissionPredicate' | 'rowLevelPermissionPredicateGroup' | 'viewFilterGroup' | 'index' | 'logicFunction' | 'viewFilter' | 'role' | 'roleTarget' | 'agent' | 'skill' | 'pageLayout' | 'pageLayoutWidget' | 'pageLayoutTab' | 'commandMenuItem' | 'navigationMenuItem' | 'permissionFlag' | 'objectPermission' | 'frontComponent' | 'webhook'
|
||||
export type AllMetadataName = 'fieldMetadata' | 'objectMetadata' | 'view' | 'viewField' | 'viewFieldGroup' | 'viewGroup' | 'viewSort' | 'rowLevelPermissionPredicate' | 'rowLevelPermissionPredicateGroup' | 'viewFilterGroup' | 'index' | 'logicFunction' | 'viewFilter' | 'role' | 'roleTarget' | 'agent' | 'skill' | 'pageLayout' | 'pageLayoutWidget' | 'pageLayoutTab' | 'commandMenuItem' | 'navigationMenuItem' | 'permissionFlag' | 'objectPermission' | 'fieldPermission' | 'frontComponent' | 'webhook'
|
||||
|
||||
export interface MinimalObjectMetadata {
|
||||
id: Scalars['UUID']
|
||||
@@ -2954,6 +2955,7 @@ export interface Mutation {
|
||||
createViewGroup: ViewGroup
|
||||
createManyViewGroups: ViewGroup[]
|
||||
updateViewGroup: ViewGroup
|
||||
updateManyViewGroups: ViewGroup[]
|
||||
deleteViewGroup: ViewGroup
|
||||
destroyViewGroup: ViewGroup
|
||||
updateMessageFolder: MessageFolder
|
||||
@@ -3360,6 +3362,7 @@ export interface FieldGenqlSelection{
|
||||
defaultValue?: boolean | number
|
||||
options?: boolean | number
|
||||
settings?: boolean | number
|
||||
objectMetadataId?: boolean | number
|
||||
isLabelSyncedWithName?: boolean | number
|
||||
morphId?: boolean | number
|
||||
createdAt?: boolean | number
|
||||
@@ -3476,7 +3479,7 @@ export interface ObjectGenqlSelection{
|
||||
__scalar?: boolean | number
|
||||
}
|
||||
|
||||
export interface FieldFilter {and?: (FieldFilter[] | null),or?: (FieldFilter[] | null),id?: (UUIDFilterComparison | null),isCustom?: (BooleanFieldComparison | null),isActive?: (BooleanFieldComparison | null),isSystem?: (BooleanFieldComparison | null),isUIReadOnly?: (BooleanFieldComparison | null)}
|
||||
export interface FieldFilter {and?: (FieldFilter[] | null),or?: (FieldFilter[] | null),id?: (UUIDFilterComparison | null),isCustom?: (BooleanFieldComparison | null),isActive?: (BooleanFieldComparison | null),isSystem?: (BooleanFieldComparison | null),isUIReadOnly?: (BooleanFieldComparison | null),objectMetadataId?: (UUIDFilterComparison | null)}
|
||||
|
||||
export interface IndexFilter {and?: (IndexFilter[] | null),or?: (IndexFilter[] | null),id?: (UUIDFilterComparison | null),isCustom?: (BooleanFieldComparison | null)}
|
||||
|
||||
@@ -5096,6 +5099,13 @@ export interface FieldConnectionGenqlSelection{
|
||||
__scalar?: boolean | number
|
||||
}
|
||||
|
||||
export interface LogicFunctionLogsGenqlSelection{
|
||||
/** Execution Logs */
|
||||
logs?: boolean | number
|
||||
__typename?: boolean | number
|
||||
__scalar?: boolean | number
|
||||
}
|
||||
|
||||
export interface DeleteTwoFactorAuthenticationMethodGenqlSelection{
|
||||
/** Boolean that confirms query was dispatched */
|
||||
success?: boolean | number
|
||||
@@ -5501,13 +5511,6 @@ export interface UsageAnalyticsGenqlSelection{
|
||||
__scalar?: boolean | number
|
||||
}
|
||||
|
||||
export interface LogicFunctionLogsGenqlSelection{
|
||||
/** Execution Logs */
|
||||
logs?: boolean | number
|
||||
__typename?: boolean | number
|
||||
__scalar?: boolean | number
|
||||
}
|
||||
|
||||
export interface FrontComponentGenqlSelection{
|
||||
id?: boolean | number
|
||||
name?: boolean | number
|
||||
@@ -6207,6 +6210,7 @@ export interface MutationGenqlSelection{
|
||||
createViewGroup?: (ViewGroupGenqlSelection & { __args: {input: CreateViewGroupInput} })
|
||||
createManyViewGroups?: (ViewGroupGenqlSelection & { __args: {inputs: CreateViewGroupInput[]} })
|
||||
updateViewGroup?: (ViewGroupGenqlSelection & { __args: {input: UpdateViewGroupInput} })
|
||||
updateManyViewGroups?: (ViewGroupGenqlSelection & { __args: {inputs: UpdateViewGroupInput[]} })
|
||||
deleteViewGroup?: (ViewGroupGenqlSelection & { __args: {input: DeleteViewGroupInput} })
|
||||
destroyViewGroup?: (ViewGroupGenqlSelection & { __args: {input: DestroyViewGroupInput} })
|
||||
updateMessageFolder?: (MessageFolderGenqlSelection & { __args: {input: UpdateMessageFolderInput} })
|
||||
@@ -6527,7 +6531,7 @@ export interface CreateOneFieldMetadataInput {
|
||||
/** The record to create */
|
||||
field: CreateFieldInput}
|
||||
|
||||
export interface CreateFieldInput {type: FieldMetadataType,name: Scalars['String'],label: Scalars['String'],description?: (Scalars['String'] | null),icon?: (Scalars['String'] | null),isCustom?: (Scalars['Boolean'] | null),isActive?: (Scalars['Boolean'] | null),isSystem?: (Scalars['Boolean'] | null),isUIReadOnly?: (Scalars['Boolean'] | null),isNullable?: (Scalars['Boolean'] | null),isUnique?: (Scalars['Boolean'] | null),defaultValue?: (Scalars['JSON'] | null),options?: (Scalars['JSON'] | null),settings?: (Scalars['JSON'] | null),isLabelSyncedWithName?: (Scalars['Boolean'] | null),objectMetadataId: Scalars['UUID'],isRemoteCreation?: (Scalars['Boolean'] | null),relationCreationPayload?: (Scalars['JSON'] | null),morphRelationsCreationPayload?: (Scalars['JSON'][] | null)}
|
||||
export interface CreateFieldInput {type: FieldMetadataType,name: Scalars['String'],label: Scalars['String'],description?: (Scalars['String'] | null),icon?: (Scalars['String'] | null),isCustom?: (Scalars['Boolean'] | null),isActive?: (Scalars['Boolean'] | null),isSystem?: (Scalars['Boolean'] | null),isUIReadOnly?: (Scalars['Boolean'] | null),isNullable?: (Scalars['Boolean'] | null),isUnique?: (Scalars['Boolean'] | null),defaultValue?: (Scalars['JSON'] | null),options?: (Scalars['JSON'] | null),settings?: (Scalars['JSON'] | null),objectMetadataId: Scalars['UUID'],isLabelSyncedWithName?: (Scalars['Boolean'] | null),isRemoteCreation?: (Scalars['Boolean'] | null),relationCreationPayload?: (Scalars['JSON'] | null),morphRelationsCreationPayload?: (Scalars['JSON'][] | null)}
|
||||
|
||||
export interface UpdateOneFieldMetadataInput {
|
||||
/** The id of the record to update */
|
||||
@@ -6535,7 +6539,7 @@ id: Scalars['UUID'],
|
||||
/** The record to update */
|
||||
update: UpdateFieldInput}
|
||||
|
||||
export interface UpdateFieldInput {universalIdentifier?: (Scalars['String'] | null),name?: (Scalars['String'] | null),label?: (Scalars['String'] | null),description?: (Scalars['String'] | null),icon?: (Scalars['String'] | null),isActive?: (Scalars['Boolean'] | null),isSystem?: (Scalars['Boolean'] | null),isUIReadOnly?: (Scalars['Boolean'] | null),isNullable?: (Scalars['Boolean'] | null),isUnique?: (Scalars['Boolean'] | null),defaultValue?: (Scalars['JSON'] | null),options?: (Scalars['JSON'] | null),settings?: (Scalars['JSON'] | null),isLabelSyncedWithName?: (Scalars['Boolean'] | null),morphRelationsUpdatePayload?: (Scalars['JSON'][] | null)}
|
||||
export interface UpdateFieldInput {universalIdentifier?: (Scalars['String'] | null),name?: (Scalars['String'] | null),label?: (Scalars['String'] | null),description?: (Scalars['String'] | null),icon?: (Scalars['String'] | null),isActive?: (Scalars['Boolean'] | null),isSystem?: (Scalars['Boolean'] | null),isUIReadOnly?: (Scalars['Boolean'] | null),isNullable?: (Scalars['Boolean'] | null),isUnique?: (Scalars['Boolean'] | null),defaultValue?: (Scalars['JSON'] | null),options?: (Scalars['JSON'] | null),settings?: (Scalars['JSON'] | null),objectMetadataId?: (Scalars['UUID'] | null),isLabelSyncedWithName?: (Scalars['Boolean'] | null),morphRelationsUpdatePayload?: (Scalars['JSON'][] | null)}
|
||||
|
||||
export interface DeleteOneFieldInput {
|
||||
/** The id of the field to delete. */
|
||||
@@ -8047,6 +8051,14 @@ export interface LogicFunctionLogsInput {applicationId?: (Scalars['UUID'] | null
|
||||
|
||||
|
||||
|
||||
const LogicFunctionLogs_possibleTypes: string[] = ['LogicFunctionLogs']
|
||||
export const isLogicFunctionLogs = (obj?: { __typename?: any } | null): obj is LogicFunctionLogs => {
|
||||
if (!obj?.__typename) throw new Error('__typename is missing in "isLogicFunctionLogs"')
|
||||
return LogicFunctionLogs_possibleTypes.includes(obj.__typename)
|
||||
}
|
||||
|
||||
|
||||
|
||||
const DeleteTwoFactorAuthenticationMethod_possibleTypes: string[] = ['DeleteTwoFactorAuthenticationMethod']
|
||||
export const isDeleteTwoFactorAuthenticationMethod = (obj?: { __typename?: any } | null): obj is DeleteTwoFactorAuthenticationMethod => {
|
||||
if (!obj?.__typename) throw new Error('__typename is missing in "isDeleteTwoFactorAuthenticationMethod"')
|
||||
@@ -8431,14 +8443,6 @@ export interface LogicFunctionLogsInput {applicationId?: (Scalars['UUID'] | null
|
||||
|
||||
|
||||
|
||||
const LogicFunctionLogs_possibleTypes: string[] = ['LogicFunctionLogs']
|
||||
export const isLogicFunctionLogs = (obj?: { __typename?: any } | null): obj is LogicFunctionLogs => {
|
||||
if (!obj?.__typename) throw new Error('__typename is missing in "isLogicFunctionLogs"')
|
||||
return LogicFunctionLogs_possibleTypes.includes(obj.__typename)
|
||||
}
|
||||
|
||||
|
||||
|
||||
const FrontComponent_possibleTypes: string[] = ['FrontComponent']
|
||||
export const isFrontComponent = (obj?: { __typename?: any } | null): obj is FrontComponent => {
|
||||
if (!obj?.__typename) throw new Error('__typename is missing in "isFrontComponent"')
|
||||
@@ -9425,6 +9429,7 @@ export const enumAllMetadataName = {
|
||||
navigationMenuItem: 'navigationMenuItem' as const,
|
||||
permissionFlag: 'permissionFlag' as const,
|
||||
objectPermission: 'objectPermission' as const,
|
||||
fieldPermission: 'fieldPermission' as const,
|
||||
frontComponent: 'frontComponent' as const,
|
||||
webhook: 'webhook' as const
|
||||
}
|
||||
|
||||
@@ -63,8 +63,8 @@ export default {
|
||||
210,
|
||||
227,
|
||||
244,
|
||||
282,
|
||||
283,
|
||||
284,
|
||||
299,
|
||||
300,
|
||||
325,
|
||||
@@ -777,6 +777,9 @@ export default {
|
||||
"settings": [
|
||||
15
|
||||
],
|
||||
"objectMetadataId": [
|
||||
3
|
||||
],
|
||||
"isLabelSyncedWithName": [
|
||||
6
|
||||
],
|
||||
@@ -1164,6 +1167,9 @@ export default {
|
||||
"isUIReadOnly": [
|
||||
43
|
||||
],
|
||||
"objectMetadataId": [
|
||||
42
|
||||
],
|
||||
"__typename": [
|
||||
1
|
||||
]
|
||||
@@ -4458,6 +4464,14 @@ export default {
|
||||
1
|
||||
]
|
||||
},
|
||||
"LogicFunctionLogs": {
|
||||
"logs": [
|
||||
1
|
||||
],
|
||||
"__typename": [
|
||||
1
|
||||
]
|
||||
},
|
||||
"DeleteTwoFactorAuthenticationMethod": {
|
||||
"success": [
|
||||
6
|
||||
@@ -4503,10 +4517,10 @@ export default {
|
||||
},
|
||||
"AuthTokenPair": {
|
||||
"accessOrWorkspaceAgnosticToken": [
|
||||
250
|
||||
251
|
||||
],
|
||||
"refreshToken": [
|
||||
250
|
||||
251
|
||||
],
|
||||
"__typename": [
|
||||
1
|
||||
@@ -4514,7 +4528,7 @@ export default {
|
||||
},
|
||||
"AvailableWorkspacesAndAccessTokens": {
|
||||
"tokens": [
|
||||
251
|
||||
252
|
||||
],
|
||||
"availableWorkspaces": [
|
||||
224
|
||||
@@ -4566,10 +4580,10 @@ export default {
|
||||
},
|
||||
"SignUp": {
|
||||
"loginToken": [
|
||||
250
|
||||
251
|
||||
],
|
||||
"workspace": [
|
||||
256
|
||||
257
|
||||
],
|
||||
"__typename": [
|
||||
1
|
||||
@@ -4577,7 +4591,7 @@ export default {
|
||||
},
|
||||
"TransientToken": {
|
||||
"transientToken": [
|
||||
250
|
||||
251
|
||||
],
|
||||
"__typename": [
|
||||
1
|
||||
@@ -4599,7 +4613,7 @@ export default {
|
||||
},
|
||||
"VerifyEmailAndGetLoginToken": {
|
||||
"loginToken": [
|
||||
250
|
||||
251
|
||||
],
|
||||
"workspaceUrls": [
|
||||
157
|
||||
@@ -4618,7 +4632,7 @@ export default {
|
||||
},
|
||||
"AuthTokens": {
|
||||
"tokens": [
|
||||
251
|
||||
252
|
||||
],
|
||||
"__typename": [
|
||||
1
|
||||
@@ -4626,7 +4640,7 @@ export default {
|
||||
},
|
||||
"LoginToken": {
|
||||
"loginToken": [
|
||||
250
|
||||
251
|
||||
],
|
||||
"__typename": [
|
||||
1
|
||||
@@ -4656,10 +4670,10 @@ export default {
|
||||
},
|
||||
"Impersonate": {
|
||||
"loginToken": [
|
||||
250
|
||||
251
|
||||
],
|
||||
"workspace": [
|
||||
256
|
||||
257
|
||||
],
|
||||
"__typename": [
|
||||
1
|
||||
@@ -4689,10 +4703,10 @@ export default {
|
||||
},
|
||||
"ApplicationTokenPair": {
|
||||
"applicationAccessToken": [
|
||||
250
|
||||
251
|
||||
],
|
||||
"applicationRefreshToken": [
|
||||
250
|
||||
251
|
||||
],
|
||||
"__typename": [
|
||||
1
|
||||
@@ -4764,7 +4778,7 @@ export default {
|
||||
1
|
||||
],
|
||||
"fields": [
|
||||
271
|
||||
272
|
||||
],
|
||||
"__typename": [
|
||||
1
|
||||
@@ -4861,10 +4875,10 @@ export default {
|
||||
6
|
||||
],
|
||||
"objectPermissions": [
|
||||
275
|
||||
276
|
||||
],
|
||||
"fieldPermissions": [
|
||||
276
|
||||
277
|
||||
],
|
||||
"permissionFlags": [
|
||||
1
|
||||
@@ -4914,19 +4928,19 @@ export default {
|
||||
1
|
||||
],
|
||||
"objects": [
|
||||
272
|
||||
],
|
||||
"fields": [
|
||||
271
|
||||
],
|
||||
"logicFunctions": [
|
||||
273
|
||||
],
|
||||
"frontComponents": [
|
||||
"fields": [
|
||||
272
|
||||
],
|
||||
"logicFunctions": [
|
||||
274
|
||||
],
|
||||
"frontComponents": [
|
||||
275
|
||||
],
|
||||
"defaultRole": [
|
||||
277
|
||||
278
|
||||
],
|
||||
"sourcePackage": [
|
||||
1
|
||||
@@ -4986,13 +5000,13 @@ export default {
|
||||
1
|
||||
],
|
||||
"driver": [
|
||||
282
|
||||
],
|
||||
"status": [
|
||||
283
|
||||
],
|
||||
"status": [
|
||||
284
|
||||
],
|
||||
"verificationRecords": [
|
||||
280
|
||||
281
|
||||
],
|
||||
"verifiedAt": [
|
||||
4
|
||||
@@ -5039,7 +5053,7 @@ export default {
|
||||
1
|
||||
],
|
||||
"location": [
|
||||
285
|
||||
286
|
||||
],
|
||||
"__typename": [
|
||||
1
|
||||
@@ -5067,13 +5081,13 @@ export default {
|
||||
},
|
||||
"ImapSmtpCaldavConnectionParameters": {
|
||||
"IMAP": [
|
||||
287
|
||||
288
|
||||
],
|
||||
"SMTP": [
|
||||
287
|
||||
288
|
||||
],
|
||||
"CALDAV": [
|
||||
287
|
||||
288
|
||||
],
|
||||
"__typename": [
|
||||
1
|
||||
@@ -5093,7 +5107,7 @@ export default {
|
||||
3
|
||||
],
|
||||
"connectionParameters": [
|
||||
288
|
||||
289
|
||||
],
|
||||
"__typename": [
|
||||
1
|
||||
@@ -5157,7 +5171,7 @@ export default {
|
||||
1
|
||||
],
|
||||
"dailyUsage": [
|
||||
293
|
||||
294
|
||||
],
|
||||
"__typename": [
|
||||
1
|
||||
@@ -5165,13 +5179,13 @@ export default {
|
||||
},
|
||||
"UsageAnalytics": {
|
||||
"usageByUser": [
|
||||
292
|
||||
293
|
||||
],
|
||||
"usageByOperationType": [
|
||||
292
|
||||
293
|
||||
],
|
||||
"timeSeries": [
|
||||
293
|
||||
294
|
||||
],
|
||||
"periodStart": [
|
||||
4
|
||||
@@ -5180,15 +5194,7 @@ export default {
|
||||
4
|
||||
],
|
||||
"userDailyUsage": [
|
||||
294
|
||||
],
|
||||
"__typename": [
|
||||
1
|
||||
]
|
||||
},
|
||||
"LogicFunctionLogs": {
|
||||
"logs": [
|
||||
1
|
||||
295
|
||||
],
|
||||
"__typename": [
|
||||
1
|
||||
@@ -5235,7 +5241,7 @@ export default {
|
||||
6
|
||||
],
|
||||
"applicationTokenPair": [
|
||||
269
|
||||
270
|
||||
],
|
||||
"__typename": [
|
||||
1
|
||||
@@ -6656,7 +6662,7 @@ export default {
|
||||
}
|
||||
],
|
||||
"checkUserExists": [
|
||||
264,
|
||||
265,
|
||||
{
|
||||
"email": [
|
||||
1,
|
||||
@@ -6668,7 +6674,7 @@ export default {
|
||||
}
|
||||
],
|
||||
"checkWorkspaceInviteHashIsValid": [
|
||||
265,
|
||||
266,
|
||||
{
|
||||
"inviteHash": [
|
||||
1,
|
||||
@@ -6686,7 +6692,7 @@ export default {
|
||||
}
|
||||
],
|
||||
"validatePasswordResetToken": [
|
||||
259,
|
||||
260,
|
||||
{
|
||||
"passwordResetToken": [
|
||||
1,
|
||||
@@ -6769,7 +6775,7 @@ export default {
|
||||
220
|
||||
],
|
||||
"getConnectedImapSmtpCaldavAccount": [
|
||||
289,
|
||||
290,
|
||||
{
|
||||
"id": [
|
||||
3,
|
||||
@@ -6778,7 +6784,7 @@ export default {
|
||||
}
|
||||
],
|
||||
"getAutoCompleteAddress": [
|
||||
284,
|
||||
285,
|
||||
{
|
||||
"address": [
|
||||
1,
|
||||
@@ -6797,7 +6803,7 @@ export default {
|
||||
}
|
||||
],
|
||||
"getAddressDetails": [
|
||||
286,
|
||||
287,
|
||||
{
|
||||
"placeId": [
|
||||
1,
|
||||
@@ -6889,19 +6895,19 @@ export default {
|
||||
}
|
||||
],
|
||||
"getPostgresCredentials": [
|
||||
291
|
||||
292
|
||||
],
|
||||
"findManyPublicDomains": [
|
||||
279
|
||||
280
|
||||
],
|
||||
"getEmailingDomains": [
|
||||
281
|
||||
282
|
||||
],
|
||||
"findManyMarketplaceApps": [
|
||||
278
|
||||
279
|
||||
],
|
||||
"findOneMarketplaceApp": [
|
||||
278,
|
||||
279,
|
||||
{
|
||||
"universalIdentifier": [
|
||||
1,
|
||||
@@ -6924,7 +6930,7 @@ export default {
|
||||
}
|
||||
],
|
||||
"getUsageAnalytics": [
|
||||
295,
|
||||
296,
|
||||
{
|
||||
"input": [
|
||||
365
|
||||
@@ -8130,6 +8136,15 @@ export default {
|
||||
]
|
||||
}
|
||||
],
|
||||
"updateManyViewGroups": [
|
||||
56,
|
||||
{
|
||||
"inputs": [
|
||||
450,
|
||||
"[UpdateViewGroupInput!]!"
|
||||
]
|
||||
}
|
||||
],
|
||||
"deleteViewGroup": [
|
||||
56,
|
||||
{
|
||||
@@ -8300,7 +8315,7 @@ export default {
|
||||
}
|
||||
],
|
||||
"getAuthorizationUrlForSSO": [
|
||||
254,
|
||||
255,
|
||||
{
|
||||
"input": [
|
||||
466,
|
||||
@@ -8309,7 +8324,7 @@ export default {
|
||||
}
|
||||
],
|
||||
"getLoginTokenFromCredentials": [
|
||||
263,
|
||||
264,
|
||||
{
|
||||
"email": [
|
||||
1,
|
||||
@@ -8335,7 +8350,7 @@ export default {
|
||||
}
|
||||
],
|
||||
"signIn": [
|
||||
252,
|
||||
253,
|
||||
{
|
||||
"email": [
|
||||
1,
|
||||
@@ -8357,7 +8372,7 @@ export default {
|
||||
}
|
||||
],
|
||||
"verifyEmailAndGetLoginToken": [
|
||||
260,
|
||||
261,
|
||||
{
|
||||
"emailVerificationToken": [
|
||||
1,
|
||||
@@ -8377,7 +8392,7 @@ export default {
|
||||
}
|
||||
],
|
||||
"verifyEmailAndGetWorkspaceAgnosticToken": [
|
||||
252,
|
||||
253,
|
||||
{
|
||||
"emailVerificationToken": [
|
||||
1,
|
||||
@@ -8393,7 +8408,7 @@ export default {
|
||||
}
|
||||
],
|
||||
"getAuthTokensFromOTP": [
|
||||
262,
|
||||
263,
|
||||
{
|
||||
"otp": [
|
||||
1,
|
||||
@@ -8413,7 +8428,7 @@ export default {
|
||||
}
|
||||
],
|
||||
"signUp": [
|
||||
252,
|
||||
253,
|
||||
{
|
||||
"email": [
|
||||
1,
|
||||
@@ -8435,7 +8450,7 @@ export default {
|
||||
}
|
||||
],
|
||||
"signUpInWorkspace": [
|
||||
257,
|
||||
258,
|
||||
{
|
||||
"email": [
|
||||
1,
|
||||
@@ -8466,13 +8481,13 @@ export default {
|
||||
}
|
||||
],
|
||||
"signUpInNewWorkspace": [
|
||||
257
|
||||
],
|
||||
"generateTransientToken": [
|
||||
258
|
||||
],
|
||||
"generateTransientToken": [
|
||||
259
|
||||
],
|
||||
"getAuthTokensFromLoginToken": [
|
||||
262,
|
||||
263,
|
||||
{
|
||||
"loginToken": [
|
||||
1,
|
||||
@@ -8485,7 +8500,7 @@ export default {
|
||||
}
|
||||
],
|
||||
"authorizeApp": [
|
||||
249,
|
||||
250,
|
||||
{
|
||||
"clientId": [
|
||||
1,
|
||||
@@ -8507,7 +8522,7 @@ export default {
|
||||
}
|
||||
],
|
||||
"renewToken": [
|
||||
262,
|
||||
263,
|
||||
{
|
||||
"appToken": [
|
||||
1,
|
||||
@@ -8516,7 +8531,7 @@ export default {
|
||||
}
|
||||
],
|
||||
"generateApiKeyToken": [
|
||||
261,
|
||||
262,
|
||||
{
|
||||
"apiKeyId": [
|
||||
3,
|
||||
@@ -8529,7 +8544,7 @@ export default {
|
||||
}
|
||||
],
|
||||
"emailPasswordResetLink": [
|
||||
253,
|
||||
254,
|
||||
{
|
||||
"email": [
|
||||
1,
|
||||
@@ -8541,7 +8556,7 @@ export default {
|
||||
}
|
||||
],
|
||||
"updatePasswordViaResetToken": [
|
||||
255,
|
||||
256,
|
||||
{
|
||||
"passwordResetToken": [
|
||||
1,
|
||||
@@ -8642,7 +8657,7 @@ export default {
|
||||
}
|
||||
],
|
||||
"initiateOTPProvisioning": [
|
||||
247,
|
||||
248,
|
||||
{
|
||||
"loginToken": [
|
||||
1,
|
||||
@@ -8655,10 +8670,10 @@ export default {
|
||||
}
|
||||
],
|
||||
"initiateOTPProvisioningForAuthenticatedUser": [
|
||||
247
|
||||
248
|
||||
],
|
||||
"deleteTwoFactorAuthenticationMethod": [
|
||||
246,
|
||||
247,
|
||||
{
|
||||
"twoFactorAuthenticationMethodId": [
|
||||
3,
|
||||
@@ -8667,7 +8682,7 @@ export default {
|
||||
}
|
||||
],
|
||||
"verifyTwoFactorAuthenticationMethodForAuthenticatedUser": [
|
||||
248,
|
||||
249,
|
||||
{
|
||||
"otp": [
|
||||
1,
|
||||
@@ -8773,7 +8788,7 @@ export default {
|
||||
}
|
||||
],
|
||||
"impersonate": [
|
||||
266,
|
||||
267,
|
||||
{
|
||||
"userId": [
|
||||
3,
|
||||
@@ -8795,7 +8810,7 @@ export default {
|
||||
}
|
||||
],
|
||||
"saveImapSmtpCaldavAccount": [
|
||||
290,
|
||||
291,
|
||||
{
|
||||
"accountOwnerId": [
|
||||
3,
|
||||
@@ -8998,13 +9013,13 @@ export default {
|
||||
}
|
||||
],
|
||||
"enablePostgresProxy": [
|
||||
291
|
||||
292
|
||||
],
|
||||
"disablePostgresProxy": [
|
||||
291
|
||||
292
|
||||
],
|
||||
"createPublicDomain": [
|
||||
279,
|
||||
280,
|
||||
{
|
||||
"domain": [
|
||||
1,
|
||||
@@ -9031,14 +9046,14 @@ export default {
|
||||
}
|
||||
],
|
||||
"createEmailingDomain": [
|
||||
281,
|
||||
282,
|
||||
{
|
||||
"domain": [
|
||||
1,
|
||||
"String!"
|
||||
],
|
||||
"driver": [
|
||||
282,
|
||||
283,
|
||||
"EmailingDomainDriver!"
|
||||
]
|
||||
}
|
||||
@@ -9053,7 +9068,7 @@ export default {
|
||||
}
|
||||
],
|
||||
"verifyEmailingDomain": [
|
||||
281,
|
||||
282,
|
||||
{
|
||||
"id": [
|
||||
1,
|
||||
@@ -9130,7 +9145,7 @@ export default {
|
||||
}
|
||||
],
|
||||
"createDevelopmentApplication": [
|
||||
267,
|
||||
268,
|
||||
{
|
||||
"universalIdentifier": [
|
||||
1,
|
||||
@@ -9143,7 +9158,7 @@ export default {
|
||||
}
|
||||
],
|
||||
"generateApplicationToken": [
|
||||
269,
|
||||
270,
|
||||
{
|
||||
"applicationId": [
|
||||
3,
|
||||
@@ -9152,7 +9167,7 @@ export default {
|
||||
}
|
||||
],
|
||||
"syncApplication": [
|
||||
268,
|
||||
269,
|
||||
{
|
||||
"manifest": [
|
||||
15,
|
||||
@@ -9161,7 +9176,7 @@ export default {
|
||||
}
|
||||
],
|
||||
"uploadApplicationFile": [
|
||||
270,
|
||||
271,
|
||||
{
|
||||
"file": [
|
||||
372,
|
||||
@@ -9195,7 +9210,7 @@ export default {
|
||||
}
|
||||
],
|
||||
"renewApplicationToken": [
|
||||
269,
|
||||
270,
|
||||
{
|
||||
"applicationRefreshToken": [
|
||||
1,
|
||||
@@ -10745,12 +10760,12 @@ export default {
|
||||
"settings": [
|
||||
15
|
||||
],
|
||||
"isLabelSyncedWithName": [
|
||||
6
|
||||
],
|
||||
"objectMetadataId": [
|
||||
3
|
||||
],
|
||||
"isLabelSyncedWithName": [
|
||||
6
|
||||
],
|
||||
"isRemoteCreation": [
|
||||
6
|
||||
],
|
||||
@@ -10815,6 +10830,9 @@ export default {
|
||||
"settings": [
|
||||
15
|
||||
],
|
||||
"objectMetadataId": [
|
||||
3
|
||||
],
|
||||
"isLabelSyncedWithName": [
|
||||
6
|
||||
],
|
||||
@@ -11455,7 +11473,7 @@ export default {
|
||||
}
|
||||
],
|
||||
"logicFunctionLogs": [
|
||||
296,
|
||||
246,
|
||||
{
|
||||
"input": [
|
||||
490,
|
||||
|
||||
@@ -32,11 +32,11 @@ has_schema=$(PGPASSWORD=twenty psql -h localhost -U twenty -d default -tAc \
|
||||
|
||||
if [ "$has_schema" = "f" ]; then
|
||||
echo "Database appears to be empty, running initial setup..."
|
||||
NODE_OPTIONS="--max-old-space-size=1500" node ./dist/scripts/setup-db.js
|
||||
NODE_OPTIONS="--max-old-space-size=1500" node ./dist/database/scripts/setup-db.js
|
||||
fi
|
||||
|
||||
# Always run migrations (idempotent — skips already-applied ones)
|
||||
yarn database:migrate:prod
|
||||
yarn database:migrate:prod --force
|
||||
|
||||
yarn command:prod cache:flush
|
||||
yarn command:prod upgrade
|
||||
|
||||
@@ -38,12 +38,6 @@ RUN npx nx run twenty-server:lingui:extract && \
|
||||
|
||||
RUN npx nx run twenty-server:build
|
||||
|
||||
# Bundle setup-db script into a standalone JS file so the final image
|
||||
# doesn't need tsx or the TypeScript source tree at runtime.
|
||||
RUN npx esbuild packages/twenty-server/scripts/setup-db.ts \
|
||||
--bundle --platform=node --outfile=packages/twenty-server/dist/scripts/setup-db.js \
|
||||
--external:typeorm --external:dotenv --external:pg
|
||||
|
||||
# Clean server build output (type declarations and compiled tests are not needed at runtime;
|
||||
# source maps are kept because twenty-infra extracts them from the image for Sentry uploads)
|
||||
RUN find /app/packages/twenty-server/dist -name '*.d.ts' -delete \
|
||||
|
||||
@@ -13,8 +13,7 @@ setup_and_migrate_db() {
|
||||
has_schema=$(psql -tAc "SELECT EXISTS (SELECT 1 FROM information_schema.schemata WHERE schema_name = 'core')" ${PG_DATABASE_URL})
|
||||
if [ "$has_schema" = "f" ]; then
|
||||
echo "Database appears to be empty, running migrations."
|
||||
NODE_OPTIONS="--max-old-space-size=1500" node ./dist/scripts/setup-db.js
|
||||
yarn database:migrate:prod
|
||||
yarn database:init:prod
|
||||
fi
|
||||
|
||||
yarn command:prod cache:flush
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -6686,6 +6686,11 @@ msgstr "Veldnaam"
|
||||
msgid "Field on destination"
|
||||
msgstr "Veld op bestemming"
|
||||
|
||||
#. js-lingui-id: P8JCfj
|
||||
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
|
||||
msgid "field permission"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: RnX5oc
|
||||
#: src/pages/settings/applications/SettingsAvailableApplicationDetails.tsx
|
||||
msgid "fields"
|
||||
|
||||
@@ -6686,6 +6686,11 @@ msgstr "اسم الحقل"
|
||||
msgid "Field on destination"
|
||||
msgstr "حقل في الوجهة"
|
||||
|
||||
#. js-lingui-id: P8JCfj
|
||||
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
|
||||
msgid "field permission"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: RnX5oc
|
||||
#: src/pages/settings/applications/SettingsAvailableApplicationDetails.tsx
|
||||
msgid "fields"
|
||||
|
||||
@@ -6686,6 +6686,11 @@ msgstr "Nom del camp"
|
||||
msgid "Field on destination"
|
||||
msgstr "Camp a la destinació"
|
||||
|
||||
#. js-lingui-id: P8JCfj
|
||||
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
|
||||
msgid "field permission"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: RnX5oc
|
||||
#: src/pages/settings/applications/SettingsAvailableApplicationDetails.tsx
|
||||
msgid "fields"
|
||||
|
||||
@@ -6686,6 +6686,11 @@ msgstr "Název pole"
|
||||
msgid "Field on destination"
|
||||
msgstr "Pole v cíli"
|
||||
|
||||
#. js-lingui-id: P8JCfj
|
||||
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
|
||||
msgid "field permission"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: RnX5oc
|
||||
#: src/pages/settings/applications/SettingsAvailableApplicationDetails.tsx
|
||||
msgid "fields"
|
||||
|
||||
@@ -6686,6 +6686,11 @@ msgstr "Feltnavn"
|
||||
msgid "Field on destination"
|
||||
msgstr "Felt på destination"
|
||||
|
||||
#. js-lingui-id: P8JCfj
|
||||
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
|
||||
msgid "field permission"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: RnX5oc
|
||||
#: src/pages/settings/applications/SettingsAvailableApplicationDetails.tsx
|
||||
msgid "fields"
|
||||
|
||||
@@ -6686,6 +6686,11 @@ msgstr "Feldname"
|
||||
msgid "Field on destination"
|
||||
msgstr "Feld am Ziel"
|
||||
|
||||
#. js-lingui-id: P8JCfj
|
||||
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
|
||||
msgid "field permission"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: RnX5oc
|
||||
#: src/pages/settings/applications/SettingsAvailableApplicationDetails.tsx
|
||||
msgid "fields"
|
||||
|
||||
@@ -6686,6 +6686,11 @@ msgstr "Όνομα Πεδίου"
|
||||
msgid "Field on destination"
|
||||
msgstr "Πεδίο στον προορισμό"
|
||||
|
||||
#. js-lingui-id: P8JCfj
|
||||
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
|
||||
msgid "field permission"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: RnX5oc
|
||||
#: src/pages/settings/applications/SettingsAvailableApplicationDetails.tsx
|
||||
msgid "fields"
|
||||
|
||||
@@ -6681,6 +6681,11 @@ msgstr "Field Name"
|
||||
msgid "Field on destination"
|
||||
msgstr "Field on destination"
|
||||
|
||||
#. js-lingui-id: P8JCfj
|
||||
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
|
||||
msgid "field permission"
|
||||
msgstr "field permission"
|
||||
|
||||
#. js-lingui-id: RnX5oc
|
||||
#: src/pages/settings/applications/SettingsAvailableApplicationDetails.tsx
|
||||
msgid "fields"
|
||||
|
||||
@@ -6686,6 +6686,11 @@ msgstr "Nombre del campo"
|
||||
msgid "Field on destination"
|
||||
msgstr "Campo en destino"
|
||||
|
||||
#. js-lingui-id: P8JCfj
|
||||
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
|
||||
msgid "field permission"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: RnX5oc
|
||||
#: src/pages/settings/applications/SettingsAvailableApplicationDetails.tsx
|
||||
msgid "fields"
|
||||
|
||||
@@ -6686,6 +6686,11 @@ msgstr "Kentän nimi"
|
||||
msgid "Field on destination"
|
||||
msgstr "Kenttä kohteessa"
|
||||
|
||||
#. js-lingui-id: P8JCfj
|
||||
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
|
||||
msgid "field permission"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: RnX5oc
|
||||
#: src/pages/settings/applications/SettingsAvailableApplicationDetails.tsx
|
||||
msgid "fields"
|
||||
|
||||
@@ -6686,6 +6686,11 @@ msgstr "Nom du champ"
|
||||
msgid "Field on destination"
|
||||
msgstr "Champ sur la destination"
|
||||
|
||||
#. js-lingui-id: P8JCfj
|
||||
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
|
||||
msgid "field permission"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: RnX5oc
|
||||
#: src/pages/settings/applications/SettingsAvailableApplicationDetails.tsx
|
||||
msgid "fields"
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -6686,6 +6686,11 @@ msgstr "שם שדה"
|
||||
msgid "Field on destination"
|
||||
msgstr "שדה ביעד"
|
||||
|
||||
#. js-lingui-id: P8JCfj
|
||||
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
|
||||
msgid "field permission"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: RnX5oc
|
||||
#: src/pages/settings/applications/SettingsAvailableApplicationDetails.tsx
|
||||
msgid "fields"
|
||||
|
||||
@@ -6686,6 +6686,11 @@ msgstr "Mező neve"
|
||||
msgid "Field on destination"
|
||||
msgstr "Mező a célállomáson"
|
||||
|
||||
#. js-lingui-id: P8JCfj
|
||||
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
|
||||
msgid "field permission"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: RnX5oc
|
||||
#: src/pages/settings/applications/SettingsAvailableApplicationDetails.tsx
|
||||
msgid "fields"
|
||||
|
||||
@@ -6686,6 +6686,11 @@ msgstr "Nome del campo"
|
||||
msgid "Field on destination"
|
||||
msgstr "Campo sulla destinazione"
|
||||
|
||||
#. js-lingui-id: P8JCfj
|
||||
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
|
||||
msgid "field permission"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: RnX5oc
|
||||
#: src/pages/settings/applications/SettingsAvailableApplicationDetails.tsx
|
||||
msgid "fields"
|
||||
|
||||
@@ -6686,6 +6686,11 @@ msgstr "フィールド名"
|
||||
msgid "Field on destination"
|
||||
msgstr "宛先のフィールド"
|
||||
|
||||
#. js-lingui-id: P8JCfj
|
||||
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
|
||||
msgid "field permission"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: RnX5oc
|
||||
#: src/pages/settings/applications/SettingsAvailableApplicationDetails.tsx
|
||||
msgid "fields"
|
||||
|
||||
@@ -6686,6 +6686,11 @@ msgstr "필드 이름"
|
||||
msgid "Field on destination"
|
||||
msgstr "대상 필드"
|
||||
|
||||
#. js-lingui-id: P8JCfj
|
||||
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
|
||||
msgid "field permission"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: RnX5oc
|
||||
#: src/pages/settings/applications/SettingsAvailableApplicationDetails.tsx
|
||||
msgid "fields"
|
||||
|
||||
@@ -6686,6 +6686,11 @@ msgstr "Veldnaam"
|
||||
msgid "Field on destination"
|
||||
msgstr "Veld bij bestemming"
|
||||
|
||||
#. js-lingui-id: P8JCfj
|
||||
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
|
||||
msgid "field permission"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: RnX5oc
|
||||
#: src/pages/settings/applications/SettingsAvailableApplicationDetails.tsx
|
||||
msgid "fields"
|
||||
|
||||
@@ -6686,6 +6686,11 @@ msgstr "Feltnavn"
|
||||
msgid "Field on destination"
|
||||
msgstr "Felt på destinasjon"
|
||||
|
||||
#. js-lingui-id: P8JCfj
|
||||
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
|
||||
msgid "field permission"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: RnX5oc
|
||||
#: src/pages/settings/applications/SettingsAvailableApplicationDetails.tsx
|
||||
msgid "fields"
|
||||
|
||||
@@ -6686,6 +6686,11 @@ msgstr "Nazwa pola"
|
||||
msgid "Field on destination"
|
||||
msgstr "Pole na miejscu docelowym"
|
||||
|
||||
#. js-lingui-id: P8JCfj
|
||||
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
|
||||
msgid "field permission"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: RnX5oc
|
||||
#: src/pages/settings/applications/SettingsAvailableApplicationDetails.tsx
|
||||
msgid "fields"
|
||||
|
||||
@@ -6681,6 +6681,11 @@ msgstr ""
|
||||
msgid "Field on destination"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: P8JCfj
|
||||
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
|
||||
msgid "field permission"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: RnX5oc
|
||||
#: src/pages/settings/applications/SettingsAvailableApplicationDetails.tsx
|
||||
msgid "fields"
|
||||
|
||||
@@ -6686,6 +6686,11 @@ msgstr "Nome do campo"
|
||||
msgid "Field on destination"
|
||||
msgstr "Campo no destino"
|
||||
|
||||
#. js-lingui-id: P8JCfj
|
||||
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
|
||||
msgid "field permission"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: RnX5oc
|
||||
#: src/pages/settings/applications/SettingsAvailableApplicationDetails.tsx
|
||||
msgid "fields"
|
||||
|
||||
@@ -6686,6 +6686,11 @@ msgstr "Nome do Campo"
|
||||
msgid "Field on destination"
|
||||
msgstr "Campo no destino"
|
||||
|
||||
#. js-lingui-id: P8JCfj
|
||||
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
|
||||
msgid "field permission"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: RnX5oc
|
||||
#: src/pages/settings/applications/SettingsAvailableApplicationDetails.tsx
|
||||
msgid "fields"
|
||||
|
||||
@@ -6686,6 +6686,11 @@ msgstr "Nume câmp"
|
||||
msgid "Field on destination"
|
||||
msgstr "Câmp pe destinație"
|
||||
|
||||
#. js-lingui-id: P8JCfj
|
||||
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
|
||||
msgid "field permission"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: RnX5oc
|
||||
#: src/pages/settings/applications/SettingsAvailableApplicationDetails.tsx
|
||||
msgid "fields"
|
||||
|
||||
Binary file not shown.
@@ -6686,6 +6686,11 @@ msgstr "Име поља"
|
||||
msgid "Field on destination"
|
||||
msgstr "Поље на одредишту"
|
||||
|
||||
#. js-lingui-id: P8JCfj
|
||||
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
|
||||
msgid "field permission"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: RnX5oc
|
||||
#: src/pages/settings/applications/SettingsAvailableApplicationDetails.tsx
|
||||
msgid "fields"
|
||||
|
||||
@@ -6686,6 +6686,11 @@ msgstr "Fältnamn"
|
||||
msgid "Field on destination"
|
||||
msgstr "Fält på destinationen"
|
||||
|
||||
#. js-lingui-id: P8JCfj
|
||||
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
|
||||
msgid "field permission"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: RnX5oc
|
||||
#: src/pages/settings/applications/SettingsAvailableApplicationDetails.tsx
|
||||
msgid "fields"
|
||||
|
||||
@@ -6686,6 +6686,11 @@ msgstr "Alan Adı"
|
||||
msgid "Field on destination"
|
||||
msgstr "Hedefteki alan"
|
||||
|
||||
#. js-lingui-id: P8JCfj
|
||||
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
|
||||
msgid "field permission"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: RnX5oc
|
||||
#: src/pages/settings/applications/SettingsAvailableApplicationDetails.tsx
|
||||
msgid "fields"
|
||||
|
||||
@@ -6686,6 +6686,11 @@ msgstr "Назва поля"
|
||||
msgid "Field on destination"
|
||||
msgstr "Поле на призначенні"
|
||||
|
||||
#. js-lingui-id: P8JCfj
|
||||
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
|
||||
msgid "field permission"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: RnX5oc
|
||||
#: src/pages/settings/applications/SettingsAvailableApplicationDetails.tsx
|
||||
msgid "fields"
|
||||
|
||||
@@ -6686,6 +6686,11 @@ msgstr "Tên Trường"
|
||||
msgid "Field on destination"
|
||||
msgstr "Trường ở điểm đến"
|
||||
|
||||
#. js-lingui-id: P8JCfj
|
||||
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
|
||||
msgid "field permission"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: RnX5oc
|
||||
#: src/pages/settings/applications/SettingsAvailableApplicationDetails.tsx
|
||||
msgid "fields"
|
||||
|
||||
@@ -6686,6 +6686,11 @@ msgstr "字段名称"
|
||||
msgid "Field on destination"
|
||||
msgstr "目的地字段"
|
||||
|
||||
#. js-lingui-id: P8JCfj
|
||||
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
|
||||
msgid "field permission"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: RnX5oc
|
||||
#: src/pages/settings/applications/SettingsAvailableApplicationDetails.tsx
|
||||
msgid "fields"
|
||||
|
||||
@@ -6686,6 +6686,11 @@ msgstr "欄位名稱"
|
||||
msgid "Field on destination"
|
||||
msgstr "目的地資料欄位"
|
||||
|
||||
#. js-lingui-id: P8JCfj
|
||||
#: src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts
|
||||
msgid "field permission"
|
||||
msgstr ""
|
||||
|
||||
#. js-lingui-id: RnX5oc
|
||||
#: src/pages/settings/applications/SettingsAvailableApplicationDetails.tsx
|
||||
msgid "fields"
|
||||
|
||||
@@ -31,8 +31,6 @@ import { useCallback, useState } from 'react';
|
||||
import { type ExtendedUIMessage } from 'twenty-shared/ai';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { REACT_APP_SERVER_BASE_URL } from '~/config';
|
||||
import { cookieStorage } from '~/utils/cookie-storage';
|
||||
|
||||
export const useAgentChat = (
|
||||
uiMessages: ExtendedUIMessage[],
|
||||
ensureThreadIdForSend: () => Promise<string | null>,
|
||||
@@ -94,7 +92,6 @@ export const useAgentChat = (
|
||||
return null;
|
||||
}
|
||||
|
||||
cookieStorage.setItem('tokenPair', JSON.stringify(renewedTokens));
|
||||
setTokenPair(renewedTokens);
|
||||
|
||||
const updatedHeaders = new Headers(init?.headers ?? {});
|
||||
|
||||
@@ -34,7 +34,6 @@ import {
|
||||
import isEmpty from 'lodash.isempty';
|
||||
import { getGenericOperationName, isDefined } from 'twenty-shared/utils';
|
||||
import { REACT_APP_SERVER_BASE_URL } from '~/config';
|
||||
import { cookieStorage } from '~/utils/cookie-storage';
|
||||
import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull';
|
||||
|
||||
const logger = loggerLink(() => 'Twenty');
|
||||
@@ -175,7 +174,6 @@ export class ApolloFactory implements ApolloManager {
|
||||
|
||||
if (isDefined(tokens)) {
|
||||
onTokenPairChange?.(tokens);
|
||||
cookieStorage.setItem('tokenPair', JSON.stringify(tokens));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
+1
@@ -32,6 +32,7 @@ export const useMetadataErrorHandler = () => {
|
||||
logicFunction: t`logic function`,
|
||||
permissionFlag: t`permission flag`,
|
||||
objectPermission: t`object permission`,
|
||||
fieldPermission: t`field permission`,
|
||||
role: t`role`,
|
||||
roleTarget: t`role target`,
|
||||
agent: t`agent`,
|
||||
|
||||
+10
-2
@@ -6,11 +6,13 @@ import { splitViewWithRelated } from '@/metadata-store/utils/splitViewWithRelate
|
||||
import { FIND_MANY_OBJECT_METADATA_ITEMS } from '@/object-metadata/graphql/queries';
|
||||
import { transformPageLayout } from '@/page-layout/utils/transformPageLayout';
|
||||
import { logicFunctionsState } from '@/settings/logic-functions/states/logicFunctionsState';
|
||||
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
|
||||
import { useApolloClient } from '@apollo/client/react';
|
||||
import { useStore } from 'jotai';
|
||||
import { useCallback } from 'react';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import {
|
||||
FeatureFlagKey,
|
||||
FindAllViewsDocument,
|
||||
FindManyCommandMenuItemsDocument,
|
||||
FindAllRecordPageLayoutsDocument,
|
||||
@@ -55,6 +57,9 @@ export const useLoadStaleMetadataEntities = () => {
|
||||
const client = useApolloClient();
|
||||
const store = useStore();
|
||||
const { replaceDraft, applyChanges } = useUpdateMetadataStoreDraft();
|
||||
const isCommandMenuItemEnabled = useIsFeatureEnabled(
|
||||
FeatureFlagKey.IS_COMMAND_MENU_ITEM_ENABLED,
|
||||
);
|
||||
|
||||
const loadStaleMetadataEntities = useCallback(
|
||||
async (staleEntityKeys: MetadataEntityKey[]) => {
|
||||
@@ -194,7 +199,10 @@ export const useLoadStaleMetadataEntities = () => {
|
||||
);
|
||||
}
|
||||
|
||||
if (staleEntityKeys.includes('commandMenuItems')) {
|
||||
if (
|
||||
staleEntityKeys.includes('commandMenuItems') &&
|
||||
isCommandMenuItemEnabled
|
||||
) {
|
||||
fetchPromises.push(
|
||||
client
|
||||
.query({
|
||||
@@ -214,7 +222,7 @@ export const useLoadStaleMetadataEntities = () => {
|
||||
await Promise.all(fetchPromises);
|
||||
applyChanges();
|
||||
},
|
||||
[client, store, replaceDraft, applyChanges],
|
||||
[client, store, replaceDraft, applyChanges, isCommandMenuItemEnabled],
|
||||
);
|
||||
|
||||
return { loadStaleMetadataEntities };
|
||||
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
import type { DropDestination } from '@/navigation-menu-item/common/types/navigationMenuItemDndKitDropDestination';
|
||||
|
||||
export type NavigationMenuItemDropResult = {
|
||||
source: DropDestination;
|
||||
destination: DropDestination | null;
|
||||
draggableId: string;
|
||||
insertBeforeItemId?: string | null;
|
||||
};
|
||||
+2
-2
@@ -1,14 +1,14 @@
|
||||
import { NAVIGATION_MENU_ITEM_SECTION_DROPPABLE_CONFIG } from '@/navigation-menu-item/common/constants/NavigationMenuItemSectionDroppableConfig';
|
||||
import type { DropDestination } from '@/navigation-menu-item/common/types/navigationMenuItemDndKitDropDestination';
|
||||
import type { NavigationMenuItemSection } from '@/navigation-menu-item/common/types/NavigationMenuItemSection';
|
||||
import { canNavigationMenuItemBeDroppedIn } from '@/navigation-menu-item/common/utils/canNavigationMenuItemBeDroppedIn';
|
||||
import type { DropResult } from '@hello-pangea/dnd';
|
||||
|
||||
export const getDropTargetIdFromDestination = ({
|
||||
navigationMenuItemSection,
|
||||
destination,
|
||||
}: {
|
||||
navigationMenuItemSection: NavigationMenuItemSection;
|
||||
destination: DropResult['destination'];
|
||||
destination: DropDestination | null;
|
||||
}): string | null => {
|
||||
if (
|
||||
!destination ||
|
||||
|
||||
-69
@@ -1,69 +0,0 @@
|
||||
import { Droppable } from '@hello-pangea/dnd';
|
||||
import { styled } from '@linaria/react';
|
||||
import { themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
|
||||
import { useIsDropDisabledForSection } from '@/navigation-menu-item/display/dnd/hooks/useIsDropDisabledForSection';
|
||||
|
||||
type NavigationMenuItemDroppableProps = {
|
||||
droppableId: string;
|
||||
children: React.ReactNode;
|
||||
isDragIndicatorVisible?: boolean;
|
||||
showDropLine?: boolean;
|
||||
isWorkspaceSection?: boolean;
|
||||
};
|
||||
|
||||
const StyledDroppableWrapper = styled.div`
|
||||
position: relative;
|
||||
transition: all 150ms ease-in-out;
|
||||
width: 100%;
|
||||
|
||||
&[data-dragging-over='true'] {
|
||||
background-color: ${themeCssVariables.background.transparent.blue};
|
||||
}
|
||||
|
||||
&[data-dragging-over='true'][data-show-drop-line='true']::before {
|
||||
background-color: ${themeCssVariables.color.blue};
|
||||
border-radius: ${themeCssVariables.border.radius.sm}
|
||||
${themeCssVariables.border.radius.sm} 0 0;
|
||||
bottom: 0;
|
||||
content: '';
|
||||
height: 2px;
|
||||
left: 0;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
}
|
||||
`;
|
||||
|
||||
export const NavigationMenuItemDroppable = ({
|
||||
droppableId,
|
||||
children,
|
||||
isDragIndicatorVisible = true,
|
||||
showDropLine = true,
|
||||
isWorkspaceSection = false,
|
||||
}: NavigationMenuItemDroppableProps) => {
|
||||
const isDropDisabled = useIsDropDisabledForSection(isWorkspaceSection);
|
||||
|
||||
return (
|
||||
<Droppable droppableId={droppableId} isDropDisabled={isDropDisabled}>
|
||||
{(provided, snapshot) => (
|
||||
<StyledDroppableWrapper
|
||||
data-dragging-over={
|
||||
snapshot.isDraggingOver && isDragIndicatorVisible
|
||||
? 'true'
|
||||
: undefined
|
||||
}
|
||||
data-show-drop-line={showDropLine ? 'true' : undefined}
|
||||
>
|
||||
<div
|
||||
ref={provided.innerRef}
|
||||
// oxlint-disable-next-line react/jsx-props-no-spreading
|
||||
{...provided.droppableProps}
|
||||
>
|
||||
{children}
|
||||
{provided.placeholder}
|
||||
</div>
|
||||
</StyledDroppableWrapper>
|
||||
)}
|
||||
</Droppable>
|
||||
);
|
||||
};
|
||||
-6
@@ -1,6 +0,0 @@
|
||||
export const DROP_RESULT_OPTIONS = {
|
||||
reason: 'DROP' as const,
|
||||
combine: null,
|
||||
mode: 'FLUID' as const,
|
||||
type: 'DEFAULT' as const,
|
||||
};
|
||||
+3
-2
@@ -1,4 +1,3 @@
|
||||
import type { DropResult, ResponderProvided } from '@hello-pangea/dnd';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { useCallback } from 'react';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
@@ -28,6 +27,8 @@ import { viewsSelector } from '@/views/states/selectors/viewsSelector';
|
||||
import { useStore } from 'jotai';
|
||||
import { NavigationMenuItemType } from 'twenty-shared/types';
|
||||
|
||||
import type { NavigationMenuItemDropResult } from '@/navigation-menu-item/common/types/navigationMenuItemDropResult';
|
||||
|
||||
export const useHandleAddToNavigationDrop = () => {
|
||||
const store = useStore();
|
||||
const { addObjectToDraft } = useAddObjectToNavigationMenuDraft();
|
||||
@@ -50,7 +51,7 @@ export const useHandleAddToNavigationDrop = () => {
|
||||
);
|
||||
|
||||
const handleAddToNavigationDrop = useCallback(
|
||||
(result: DropResult, _provided: ResponderProvided) => {
|
||||
(result: NavigationMenuItemDropResult) => {
|
||||
const { source, destination, draggableId } = result;
|
||||
if (
|
||||
source.droppableId !== ADD_TO_NAV_SOURCE_DROPPABLE_ID ||
|
||||
|
||||
+4
-5
@@ -1,8 +1,9 @@
|
||||
import { type OnDragEndResponder } from '@hello-pangea/dnd';
|
||||
import { useStore } from 'jotai';
|
||||
import { type NavigationMenuItem } from '~/generated-metadata/graphql';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import type { NavigationMenuItemDropResult } from '@/navigation-menu-item/common/types/navigationMenuItemDropResult';
|
||||
|
||||
import { isLayoutCustomizationModeEnabledState } from '@/layout-customization/states/isLayoutCustomizationModeEnabledState';
|
||||
import { useUpdateManyNavigationMenuItems } from '@/navigation-menu-item/common/hooks/useUpdateManyNavigationMenuItems';
|
||||
import { NAVIGATION_MENU_ITEM_SECTION_DROPPABLE_CONFIG } from '@/navigation-menu-item/common/constants/NavigationMenuItemSectionDroppableConfig';
|
||||
@@ -115,10 +116,8 @@ export const useHandleNavigationMenuItemDragAndDrop = (
|
||||
});
|
||||
};
|
||||
|
||||
const handleNavigationMenuItemDragAndDrop: OnDragEndResponder = async (
|
||||
result: Parameters<OnDragEndResponder>[0] & {
|
||||
insertBeforeItemId?: string | null;
|
||||
},
|
||||
const handleNavigationMenuItemDragAndDrop = async (
|
||||
result: NavigationMenuItemDropResult,
|
||||
) => {
|
||||
const { destination, source, draggableId } = result;
|
||||
|
||||
|
||||
+18
-24
@@ -1,6 +1,5 @@
|
||||
import { type DragDropProvider } from '@dnd-kit/react';
|
||||
import { isSortable } from '@dnd-kit/react/sortable';
|
||||
import type { ResponderProvided } from '@hello-pangea/dnd';
|
||||
import { useStore } from 'jotai';
|
||||
import { type ComponentProps, useCallback, useState } from 'react';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
@@ -11,17 +10,16 @@ import { NavigationSections } from '@/navigation-menu-item/common/constants/Navi
|
||||
import { addToNavPayloadRegistryState } from '@/navigation-menu-item/common/states/addToNavPayloadRegistryState';
|
||||
import type { DraggableData } from '@/navigation-menu-item/common/types/navigationMenuItemDndKitDraggableData';
|
||||
import type { DropDestination } from '@/navigation-menu-item/common/types/navigationMenuItemDndKitDropDestination';
|
||||
import type { NavigationMenuItemDropResult } from '@/navigation-menu-item/common/types/navigationMenuItemDropResult';
|
||||
import type { SortableTargetDestination } from '@/navigation-menu-item/common/types/navigationMenuItemDndKitSortableTargetDestination';
|
||||
import type { NavigationMenuItemSection } from '@/navigation-menu-item/common/types/NavigationMenuItemSection';
|
||||
import { canNavigationMenuItemBeDroppedIn } from '@/navigation-menu-item/common/utils/canNavigationMenuItemBeDroppedIn';
|
||||
import { extractFolderIdFromDroppableId } from '@/navigation-menu-item/common/utils/extractFolderIdFromDroppableId';
|
||||
import { getDndKitDropTargetId } from '@/navigation-menu-item/common/utils/getDndKitDropTargetId';
|
||||
import { isNavigationMenuItemFolder } from '@/navigation-menu-item/common/utils/isNavigationMenuItemFolder';
|
||||
import { DROP_RESULT_OPTIONS } from '@/navigation-menu-item/display/dnd/constants/navigationMenuItemDndKitDropResultOptions';
|
||||
import { useHandleAddToNavigationDrop } from '@/navigation-menu-item/display/dnd/hooks/useHandleAddToNavigationDrop';
|
||||
import { useHandleNavigationMenuItemDragAndDrop } from '@/navigation-menu-item/display/dnd/hooks/useHandleNavigationMenuItemDragAndDrop';
|
||||
import { resolveDropTarget } from '@/navigation-menu-item/display/dnd/utils/navigationMenuItemDndKitResolveDropTarget';
|
||||
import { toDropResult } from '@/navigation-menu-item/display/dnd/utils/navigationMenuItemDndKitToDropResult';
|
||||
import { useNavigationMenuItemsData } from '@/navigation-menu-item/display/hooks/useNavigationMenuItemsData';
|
||||
import { useSortedNavigationMenuItems } from '@/navigation-menu-item/display/hooks/useSortedNavigationMenuItems';
|
||||
import { useNavigationMenuItemsDraftState } from '@/navigation-menu-item/edit/hooks/useNavigationMenuItemsDraftState';
|
||||
@@ -172,23 +170,13 @@ export const useNavigationMenuItemDndKit = (
|
||||
destination: DropDestination,
|
||||
insertBeforeItemId?: string | null,
|
||||
) => {
|
||||
const result = toDropResult(
|
||||
id,
|
||||
{
|
||||
sourceDroppableId: source.droppableId,
|
||||
sourceIndex: source.index,
|
||||
},
|
||||
const result: NavigationMenuItemDropResult = {
|
||||
draggableId: id,
|
||||
source,
|
||||
destination,
|
||||
);
|
||||
const provided: ResponderProvided = { announce: () => {} };
|
||||
handleNavigationMenuItemDragAndDrop(
|
||||
{
|
||||
...result,
|
||||
...DROP_RESULT_OPTIONS,
|
||||
...(insertBeforeItemId != null && { insertBeforeItemId }),
|
||||
},
|
||||
provided,
|
||||
);
|
||||
...(insertBeforeItemId != null && { insertBeforeItemId }),
|
||||
};
|
||||
handleNavigationMenuItemDragAndDrop(result);
|
||||
},
|
||||
[handleNavigationMenuItemDragAndDrop],
|
||||
);
|
||||
@@ -351,12 +339,18 @@ export const useNavigationMenuItemDndKit = (
|
||||
destination = fallback;
|
||||
}
|
||||
|
||||
const result = toDropResult(draggableId, data, destination);
|
||||
const provided: ResponderProvided = { announce: () => {} };
|
||||
const dropResult = { ...result, ...DROP_RESULT_OPTIONS };
|
||||
const dropResult: NavigationMenuItemDropResult = {
|
||||
draggableId,
|
||||
source: {
|
||||
droppableId: data?.sourceDroppableId ?? '',
|
||||
index: data?.sourceIndex ?? 0,
|
||||
},
|
||||
destination,
|
||||
...(insertBeforeItemId != null && { insertBeforeItemId }),
|
||||
};
|
||||
|
||||
if (sourceId === ADD_TO_NAV_SOURCE_DROPPABLE_ID) {
|
||||
handleAddToNavigationDrop(dropResult, provided);
|
||||
handleAddToNavigationDrop(dropResult);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -386,7 +380,7 @@ export const useNavigationMenuItemDndKit = (
|
||||
return;
|
||||
}
|
||||
|
||||
handleNavigationMenuItemDragAndDrop(dropResult, provided);
|
||||
handleNavigationMenuItemDragAndDrop(dropResult);
|
||||
};
|
||||
|
||||
const contextValues: NavigationMenuItemDndKitContextValues = {
|
||||
|
||||
-20
@@ -1,20 +0,0 @@
|
||||
import type { DropDestination } from '@/navigation-menu-item/common/types/navigationMenuItemDndKitDropDestination';
|
||||
import type { DraggableData } from '@/navigation-menu-item/common/types/navigationMenuItemDndKitDraggableData';
|
||||
|
||||
export const toDropResult = (
|
||||
draggableId: string,
|
||||
data: DraggableData | undefined,
|
||||
destination: DropDestination | null,
|
||||
): {
|
||||
source: DropDestination;
|
||||
destination: DropDestination | null;
|
||||
draggableId: string;
|
||||
} => {
|
||||
const sourceDroppableId = data?.sourceDroppableId ?? '';
|
||||
const sourceIndex = data?.sourceIndex ?? 0;
|
||||
return {
|
||||
source: { droppableId: sourceDroppableId, index: sourceIndex },
|
||||
destination,
|
||||
draggableId,
|
||||
};
|
||||
};
|
||||
-102
@@ -1,102 +0,0 @@
|
||||
import {
|
||||
type DraggableProvided,
|
||||
type DraggableRubric,
|
||||
type DraggableStateSnapshot,
|
||||
} from '@hello-pangea/dnd';
|
||||
import { useContext } from 'react';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { ThemeContext } from 'twenty-ui/theme-constants';
|
||||
import { type NavigationMenuItem } from '~/generated-metadata/graphql';
|
||||
|
||||
import { NavigationMenuItemIcon } from '@/navigation-menu-item/display/components/NavigationMenuItemIcon';
|
||||
import { getNavigationMenuItemLabel } from '@/navigation-menu-item/display/utils/getNavigationMenuItemLabel';
|
||||
import { getNavigationMenuItemObjectNameSingular } from '@/navigation-menu-item/display/object/utils/getNavigationMenuItemObjectNameSingular';
|
||||
import { getObjectNavigationMenuItemSecondaryLabel } from '@/navigation-menu-item/display/object/utils/getObjectNavigationMenuItemSecondaryLabel';
|
||||
import { objectMetadataItemsSelector } from '@/object-metadata/states/objectMetadataItemsSelector';
|
||||
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
|
||||
import { NavigationDrawerSubItem } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerSubItem';
|
||||
import { getNavigationSubItemLeftAdornment } from '@/ui/navigation/navigation-drawer/utils/getNavigationSubItemLeftAdornment';
|
||||
import { viewsSelector } from '@/views/states/selectors/viewsSelector';
|
||||
import { ViewKey } from '@/views/types/ViewKey';
|
||||
|
||||
type WorkspaceNavigationMenuItemFolderDragCloneProps = {
|
||||
draggableProvided: DraggableProvided;
|
||||
draggableSnapshot: DraggableStateSnapshot;
|
||||
rubric: DraggableRubric;
|
||||
navigationMenuItems: NavigationMenuItem[];
|
||||
navigationMenuItemFolderContentLength: number;
|
||||
selectedNavigationMenuItemIndex: number;
|
||||
};
|
||||
|
||||
export const WorkspaceNavigationMenuItemFolderDragClone = ({
|
||||
draggableProvided,
|
||||
draggableSnapshot,
|
||||
rubric,
|
||||
navigationMenuItems,
|
||||
navigationMenuItemFolderContentLength,
|
||||
selectedNavigationMenuItemIndex,
|
||||
}: WorkspaceNavigationMenuItemFolderDragCloneProps) => {
|
||||
const { theme } = useContext(ThemeContext);
|
||||
const objectMetadataItems = useAtomStateValue(objectMetadataItemsSelector);
|
||||
const views = useAtomStateValue(viewsSelector);
|
||||
const navigationMenuItem = navigationMenuItems[rubric.source.index];
|
||||
|
||||
if (!isDefined(navigationMenuItem)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const label = getNavigationMenuItemLabel(
|
||||
navigationMenuItem,
|
||||
objectMetadataItems,
|
||||
views,
|
||||
);
|
||||
const objectNameSingular = getNavigationMenuItemObjectNameSingular(
|
||||
navigationMenuItem,
|
||||
objectMetadataItems,
|
||||
views,
|
||||
);
|
||||
const view = isDefined(navigationMenuItem.viewId)
|
||||
? views.find((view) => view.id === navigationMenuItem.viewId)
|
||||
: undefined;
|
||||
const isIndexView = view?.key === ViewKey.INDEX;
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={draggableProvided.innerRef}
|
||||
// oxlint-disable-next-line react/jsx-props-no-spreading
|
||||
{...draggableProvided.draggableProps}
|
||||
// oxlint-disable-next-line react/jsx-props-no-spreading
|
||||
{...draggableProvided.dragHandleProps}
|
||||
style={{
|
||||
...draggableProvided.draggableProps.style,
|
||||
background: draggableSnapshot.isDragging
|
||||
? theme.background.transparent.light
|
||||
: undefined,
|
||||
}}
|
||||
>
|
||||
<NavigationDrawerSubItem
|
||||
secondaryLabel={
|
||||
isIndexView
|
||||
? undefined
|
||||
: getObjectNavigationMenuItemSecondaryLabel({
|
||||
objectMetadataItems,
|
||||
navigationMenuItemObjectNameSingular: objectNameSingular ?? '',
|
||||
})
|
||||
}
|
||||
label={label}
|
||||
Icon={() => (
|
||||
<NavigationMenuItemIcon navigationMenuItem={navigationMenuItem} />
|
||||
)}
|
||||
to={undefined}
|
||||
active={false}
|
||||
isDragging={true}
|
||||
subItemState={getNavigationSubItemLeftAdornment({
|
||||
index: rubric.source.index,
|
||||
arrayLength: navigationMenuItemFolderContentLength,
|
||||
selectedIndex: selectedNavigationMenuItemIndex,
|
||||
})}
|
||||
triggerEvent="CLICK"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -20,12 +20,14 @@ export type FieldMetadataItem = Omit<
|
||||
| '__typename'
|
||||
| 'applicationId'
|
||||
| 'defaultValue'
|
||||
| 'objectMetadataId'
|
||||
| 'options'
|
||||
| 'relation'
|
||||
| 'morphRelations'
|
||||
> & {
|
||||
__typename?: string;
|
||||
applicationId?: string;
|
||||
objectMetadataId?: string;
|
||||
defaultValue?: any;
|
||||
options?: FieldMetadataItemOption[] | null;
|
||||
relation?: FieldMetadataItemRelation | null;
|
||||
|
||||
+4
-1
@@ -8,14 +8,17 @@ const StyledContainer = styled.div`
|
||||
align-items: center;
|
||||
background: ${themeCssVariables.background.transparent.light};
|
||||
|
||||
border-radius: 4px;
|
||||
border-radius: ${themeCssVariables.border.radius.sm};
|
||||
color: ${themeCssVariables.font.color.tertiary};
|
||||
display: inline-flex;
|
||||
font-size: ${themeCssVariables.font.size.md};
|
||||
font-weight: ${themeCssVariables.font.weight.regular};
|
||||
gap: ${themeCssVariables.spacing[1]};
|
||||
height: ${themeCssVariables.spacing[3]};
|
||||
overflow: hidden;
|
||||
|
||||
padding: ${themeCssVariables.spacing[1]};
|
||||
user-select: none;
|
||||
`;
|
||||
|
||||
export const ForbiddenFieldDisplay = () => {
|
||||
|
||||
+10
-3
@@ -91,13 +91,20 @@ export const MultiItemFieldInput = <T,>({
|
||||
) {
|
||||
return;
|
||||
}
|
||||
const { isValid, updatedItems } = validateInputAndComputeUpdatedItems();
|
||||
|
||||
if (!isValid) {
|
||||
if (isInputDisplayed) {
|
||||
const { isValid, updatedItems } = validateInputAndComputeUpdatedItems();
|
||||
|
||||
if (!isValid) {
|
||||
return;
|
||||
}
|
||||
|
||||
onChange(updatedItems);
|
||||
onClickOutside(updatedItems, event);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
onChange(updatedItems);
|
||||
onClickOutside(items, event);
|
||||
},
|
||||
listenerId: instanceId,
|
||||
|
||||
@@ -76,6 +76,12 @@ export const useEditPageLayoutWidget = (pageLayoutIdFromProps?: string) => {
|
||||
return;
|
||||
}
|
||||
|
||||
if (widgetType === WidgetType.STANDALONE_RICH_TEXT) {
|
||||
setPageLayoutEditingWidgetId(widgetId);
|
||||
closeSidePanelMenu();
|
||||
return;
|
||||
}
|
||||
|
||||
setSidePanelPage(SidePanelPages.CommandMenuDisplay);
|
||||
closeSidePanelMenu();
|
||||
},
|
||||
|
||||
+29
-13
@@ -1,8 +1,20 @@
|
||||
import { useResetContextStoreStates } from '@/command-menu/hooks/useResetContextStoreStates';
|
||||
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
|
||||
import { addToNavPayloadRegistryState } from '@/navigation-menu-item/common/states/addToNavPayloadRegistryState';
|
||||
import { pendingInsertionNavigationMenuItemState } from '@/navigation-menu-item/common/states/pendingInsertionNavigationMenuItemState';
|
||||
import { selectedNavigationMenuItemIdInEditModeState } from '@/navigation-menu-item/common/states/selectedNavigationMenuItemIdInEditModeState';
|
||||
import { viewableRecordIdState } from '@/object-record/record-side-panel/states/viewableRecordIdState';
|
||||
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||
import { pageLayoutDraggedAreaComponentState } from '@/page-layout/states/pageLayoutDraggedAreaComponentState';
|
||||
import { pageLayoutEditingWidgetIdComponentState } from '@/page-layout/states/pageLayoutEditingWidgetIdComponentState';
|
||||
import { pageLayoutTabSettingsOpenTabIdComponentState } from '@/page-layout/states/pageLayoutTabSettingsOpenTabIdComponentState';
|
||||
import { SIDE_PANEL_COMPONENT_INSTANCE_ID } from '@/side-panel/constants/SidePanelComponentInstanceId';
|
||||
import { SIDE_PANEL_CONTEXT_CHIP_GROUPS_DROPDOWN_ID } from '@/side-panel/constants/SidePanelContextChipGroupsDropdownId';
|
||||
import { SIDE_PANEL_SELECTABLE_LIST_ID } from '@/side-panel/constants/SidePanelSelectableListId';
|
||||
import { SIDE_PANEL_PREVIOUS_COMPONENT_INSTANCE_ID } from '@/side-panel/constants/SidePanelPreviousComponentInstanceId';
|
||||
import { useResetContextStoreStates } from '@/command-menu/hooks/useResetContextStoreStates';
|
||||
import { SIDE_PANEL_SELECTABLE_LIST_ID } from '@/side-panel/constants/SidePanelSelectableListId';
|
||||
import { hasUserSelectedSidePanelListItemState } from '@/side-panel/states/hasUserSelectedSidePanelListItemState';
|
||||
import { isSidePanelClosingState } from '@/side-panel/states/isSidePanelClosingState';
|
||||
import { isSidePanelOpenedState } from '@/side-panel/states/isSidePanelOpenedState';
|
||||
import { sidePanelNavigationMorphItemsByPageState } from '@/side-panel/states/sidePanelNavigationMorphItemsByPageState';
|
||||
import { sidePanelNavigationStackState } from '@/side-panel/states/sidePanelNavigationStackState';
|
||||
import { sidePanelPageInfoState } from '@/side-panel/states/sidePanelPageInfoState';
|
||||
@@ -10,19 +22,10 @@ import { sidePanelPageState } from '@/side-panel/states/sidePanelPageState';
|
||||
import { sidePanelSearchObjectFilterState } from '@/side-panel/states/sidePanelSearchObjectFilterState';
|
||||
import { sidePanelSearchState } from '@/side-panel/states/sidePanelSearchState';
|
||||
import { sidePanelShowHiddenObjectsState } from '@/side-panel/states/sidePanelShowHiddenObjectsState';
|
||||
import { hasUserSelectedSidePanelListItemState } from '@/side-panel/states/hasUserSelectedSidePanelListItemState';
|
||||
import { isSidePanelClosingState } from '@/side-panel/states/isSidePanelClosingState';
|
||||
import { isSidePanelOpenedState } from '@/side-panel/states/isSidePanelOpenedState';
|
||||
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
|
||||
import { viewableRecordIdState } from '@/object-record/record-side-panel/states/viewableRecordIdState';
|
||||
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||
import { pageLayoutDraggedAreaComponentState } from '@/page-layout/states/pageLayoutDraggedAreaComponentState';
|
||||
import { pageLayoutEditingWidgetIdComponentState } from '@/page-layout/states/pageLayoutEditingWidgetIdComponentState';
|
||||
import { pageLayoutTabSettingsOpenTabIdComponentState } from '@/page-layout/states/pageLayoutTabSettingsOpenTabIdComponentState';
|
||||
import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown';
|
||||
import { emitSidePanelCloseEvent } from '@/ui/layout/side-panel/utils/emitSidePanelCloseEvent';
|
||||
import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList';
|
||||
import { getShowPageTabListComponentId } from '@/ui/layout/show-page/utils/getShowPageTabListComponentId';
|
||||
import { emitSidePanelCloseEvent } from '@/ui/layout/side-panel/utils/emitSidePanelCloseEvent';
|
||||
import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTabIdComponentState';
|
||||
import { WORKFLOW_LOGIC_FUNCTION_TAB_LIST_COMPONENT_ID } from '@/workflow/workflow-steps/workflow-actions/code-action/constants/WorkflowLogicFunctionTabListComponentId';
|
||||
import { WorkflowLogicFunctionTabId } from '@/workflow/workflow-steps/workflow-actions/code-action/types/WorkflowLogicFunctionTabId';
|
||||
@@ -41,6 +44,12 @@ export const useSidePanelCloseAnimationCompleteCleanup = () => {
|
||||
|
||||
const { closeDropdown } = useCloseDropdown();
|
||||
|
||||
const resetNavigationMenuItemState = () => {
|
||||
store.set(selectedNavigationMenuItemIdInEditModeState.atom, null);
|
||||
store.set(pendingInsertionNavigationMenuItemState.atom, null);
|
||||
store.set(addToNavPayloadRegistryState.atom, new Map());
|
||||
};
|
||||
|
||||
const sidePanelCloseAnimationCompleteCleanup = useCallback(
|
||||
(options?: { emitSidePanelCloseEvent?: boolean }) => {
|
||||
closeDropdown(SIDE_PANEL_CONTEXT_CHIP_GROUPS_DROPDOWN_ID);
|
||||
@@ -110,6 +119,7 @@ export const useSidePanelCloseAnimationCompleteCleanup = () => {
|
||||
store.set(sidePanelShowHiddenObjectsState.atom, false);
|
||||
store.set(sidePanelNavigationMorphItemsByPageState.atom, new Map());
|
||||
store.set(sidePanelNavigationStackState.atom, []);
|
||||
resetNavigationMenuItemState();
|
||||
resetSelectedItem();
|
||||
store.set(hasUserSelectedSidePanelListItemState.atom, false);
|
||||
|
||||
@@ -136,7 +146,13 @@ export const useSidePanelCloseAnimationCompleteCleanup = () => {
|
||||
);
|
||||
}
|
||||
},
|
||||
[closeDropdown, resetContextStoreStates, resetSelectedItem, store],
|
||||
[
|
||||
closeDropdown,
|
||||
resetContextStoreStates,
|
||||
resetNavigationMenuItemState,
|
||||
resetSelectedItem,
|
||||
store,
|
||||
],
|
||||
);
|
||||
|
||||
return {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { isLayoutCustomizationModeEnabledState } from '@/layout-customization/states/isLayoutCustomizationModeEnabledState';
|
||||
import { addToNavPayloadRegistryState } from '@/navigation-menu-item/common/states/addToNavPayloadRegistryState';
|
||||
import { selectedNavigationMenuItemIdInEditModeState } from '@/navigation-menu-item/common/states/selectedNavigationMenuItemIdInEditModeState';
|
||||
import { SIDE_PANEL_FOCUS_ID } from '@/side-panel/constants/SidePanelFocusId';
|
||||
import { useNavigateSidePanel } from '@/side-panel/hooks/useNavigateSidePanel';
|
||||
@@ -29,7 +28,6 @@ export const useSidePanelMenu = () => {
|
||||
const isSidePanelOpened = store.get(isSidePanelOpenedState.atom);
|
||||
|
||||
if (isSidePanelOpened) {
|
||||
store.set(addToNavPayloadRegistryState.atom, new Map());
|
||||
store.set(isSidePanelOpenedState.atom, false);
|
||||
store.set(isSidePanelClosingState.atom, true);
|
||||
closeAnyOpenDropdown();
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
import { VIEW_GROUP_FRAGMENT } from '@/views/graphql/fragments/viewGroupFragment';
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
export const UPDATE_MANY_VIEW_GROUPS = gql`
|
||||
${VIEW_GROUP_FRAGMENT}
|
||||
mutation UpdateManyViewGroups($inputs: [UpdateViewGroupInput!]!) {
|
||||
updateManyViewGroups(inputs: $inputs) {
|
||||
...ViewGroupFragment
|
||||
}
|
||||
}
|
||||
`;
|
||||
+19
-18
@@ -8,43 +8,44 @@ import { t } from '@lingui/core/macro';
|
||||
import { CrudOperationType } from 'twenty-shared/types';
|
||||
import { useMutation } from '@apollo/client/react';
|
||||
import {
|
||||
type UpdateViewGroupMutationVariables,
|
||||
UpdateViewGroupDocument,
|
||||
type UpdateManyViewGroupsMutationVariables,
|
||||
UpdateManyViewGroupsDocument,
|
||||
} from '~/generated-metadata/graphql';
|
||||
|
||||
export const usePerformViewGroupAPIPersist = () => {
|
||||
const [updateViewGroupMutation] = useMutation(UpdateViewGroupDocument);
|
||||
const [updateManyViewGroupsMutation] = useMutation(
|
||||
UpdateManyViewGroupsDocument,
|
||||
);
|
||||
|
||||
const { handleMetadataError } = useMetadataErrorHandler();
|
||||
const { enqueueErrorSnackBar } = useSnackBar();
|
||||
|
||||
const performViewGroupAPIUpdate = useCallback(
|
||||
async (
|
||||
updateViewGroupInputs: UpdateViewGroupMutationVariables[],
|
||||
updateViewGroupInputs: UpdateManyViewGroupsMutationVariables,
|
||||
): Promise<
|
||||
MetadataRequestResult<
|
||||
Awaited<ReturnType<typeof updateViewGroupMutation>>[]
|
||||
>
|
||||
MetadataRequestResult<Awaited<
|
||||
ReturnType<typeof updateManyViewGroupsMutation>
|
||||
> | null>
|
||||
> => {
|
||||
if (updateViewGroupInputs.length === 0) {
|
||||
if (
|
||||
!Array.isArray(updateViewGroupInputs.inputs) ||
|
||||
updateViewGroupInputs.inputs.length === 0
|
||||
) {
|
||||
return {
|
||||
status: 'successful',
|
||||
response: [],
|
||||
response: null,
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
const results = await Promise.all(
|
||||
updateViewGroupInputs.map((variables) =>
|
||||
updateViewGroupMutation({
|
||||
variables,
|
||||
}),
|
||||
),
|
||||
);
|
||||
const result = await updateManyViewGroupsMutation({
|
||||
variables: updateViewGroupInputs,
|
||||
});
|
||||
|
||||
return {
|
||||
status: 'successful',
|
||||
response: results,
|
||||
response: result,
|
||||
};
|
||||
} catch (error) {
|
||||
if (CombinedGraphQLErrors.is(error)) {
|
||||
@@ -62,7 +63,7 @@ export const usePerformViewGroupAPIPersist = () => {
|
||||
};
|
||||
}
|
||||
},
|
||||
[updateViewGroupMutation, handleMetadataError, enqueueErrorSnackBar],
|
||||
[updateManyViewGroupsMutation, handleMetadataError, enqueueErrorSnackBar],
|
||||
);
|
||||
|
||||
return {
|
||||
|
||||
@@ -67,9 +67,9 @@ export const useSaveCurrentViewGroups = () => {
|
||||
return;
|
||||
}
|
||||
|
||||
await performViewGroupAPIUpdate([
|
||||
{
|
||||
input: {
|
||||
await performViewGroupAPIUpdate({
|
||||
inputs: [
|
||||
{
|
||||
id: existingField.id,
|
||||
update: {
|
||||
isVisible: viewGroupToSave.isVisible,
|
||||
@@ -77,8 +77,8 @@ export const useSaveCurrentViewGroups = () => {
|
||||
fieldValue: viewGroupToSave.fieldValue,
|
||||
},
|
||||
},
|
||||
},
|
||||
]);
|
||||
],
|
||||
});
|
||||
},
|
||||
[
|
||||
store,
|
||||
@@ -109,7 +109,7 @@ export const useSaveCurrentViewGroups = () => {
|
||||
|
||||
const currentViewGroups = view.viewGroups;
|
||||
|
||||
const viewGroupsToUpdate = viewGroupsToSave
|
||||
const viewGroupInputsToUpdate = viewGroupsToSave
|
||||
.map((viewGroupToSave) => {
|
||||
const existingField = currentViewGroups.find(
|
||||
(currentViewGroup) =>
|
||||
@@ -136,13 +136,11 @@ export const useSaveCurrentViewGroups = () => {
|
||||
}
|
||||
|
||||
return {
|
||||
input: {
|
||||
id: existingField.id,
|
||||
update: {
|
||||
isVisible: viewGroupToSave.isVisible,
|
||||
position: viewGroupToSave.position,
|
||||
fieldValue: viewGroupToSave.fieldValue,
|
||||
},
|
||||
id: existingField.id,
|
||||
update: {
|
||||
isVisible: viewGroupToSave.isVisible,
|
||||
position: viewGroupToSave.position,
|
||||
fieldValue: viewGroupToSave.fieldValue,
|
||||
},
|
||||
};
|
||||
})
|
||||
@@ -152,7 +150,7 @@ export const useSaveCurrentViewGroups = () => {
|
||||
throw new Error('mainGroupByFieldMetadataId is required');
|
||||
}
|
||||
|
||||
await performViewGroupAPIUpdate(viewGroupsToUpdate);
|
||||
await performViewGroupAPIUpdate({ inputs: viewGroupInputsToUpdate });
|
||||
},
|
||||
[
|
||||
store,
|
||||
|
||||
@@ -9,8 +9,8 @@
|
||||
"start:prod": "node dist/main",
|
||||
"command:prod": "node dist/command/command",
|
||||
"worker:prod": "node dist/queue-worker/queue-worker",
|
||||
"database:init:prod": "npx ts-node ./scripts/setup-db.ts && yarn database:migrate:prod",
|
||||
"database:migrate:prod": "npx -y typeorm migration:run -d dist/database/typeorm/core/core.datasource",
|
||||
"database:init:prod": "node dist/database/scripts/setup-db.js && yarn database:migrate:prod --force",
|
||||
"database:migrate:prod": "node dist/command/command run-typeorm-migration",
|
||||
"clickhouse:migrate:prod": "node dist/database/clickHouse/migrations/run-migrations.js",
|
||||
"typeorm": "../../node_modules/typeorm/.bin/typeorm"
|
||||
},
|
||||
|
||||
@@ -246,8 +246,8 @@
|
||||
"no-seed": {
|
||||
"cwd": "packages/twenty-server",
|
||||
"commands": [
|
||||
"nx ts-node-no-deps-transpile-only -- ./scripts/truncate-db.ts",
|
||||
"nx ts-node-no-deps-transpile-only -- ./scripts/setup-db.ts",
|
||||
"node dist/database/scripts/truncate-db.js",
|
||||
"node dist/database/scripts/setup-db.js",
|
||||
"nx database:migrate",
|
||||
"nx command-no-deps -- cache:flush"
|
||||
],
|
||||
@@ -256,8 +256,8 @@
|
||||
"seed": {
|
||||
"cwd": "packages/twenty-server",
|
||||
"commands": [
|
||||
"nx ts-node-no-deps-transpile-only -- ./scripts/truncate-db.ts",
|
||||
"nx ts-node-no-deps-transpile-only -- ./scripts/setup-db.ts",
|
||||
"node dist/database/scripts/truncate-db.js",
|
||||
"node dist/database/scripts/setup-db.js",
|
||||
"nx database:migrate",
|
||||
"nx command-no-deps -- cache:flush",
|
||||
"nx command-no-deps -- workspace:seed:dev"
|
||||
|
||||
-21
@@ -1,21 +0,0 @@
|
||||
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
|
||||
|
||||
exports[`UpgradeCommandRunner Workspace upgrade should fail when APP_VERSION is not defined 1`] = `[Error: Cannot run upgrade command when APP_VERSION is not defined, please double check your env variables]`;
|
||||
|
||||
exports[`UpgradeCommandRunner Workspace upgrade should fail when all commands contains invalid semver keys 1`] = `[Error: No previous version found for version 2.0.0. Please review the "allCommands" record. Available versions are: invalid, 2.0.0]`;
|
||||
|
||||
exports[`UpgradeCommandRunner Workspace upgrade should fail when current version commands are not found 1`] = `[Error: No command found for version 42.0.0. Please check the commands record.]`;
|
||||
|
||||
exports[`UpgradeCommandRunner Workspace upgrade should fail when previous version is not found 1`] = `[Error: No previous version found for version 1.0.0. Please review the "allCommands" record. Available versions are: 1.0.0, 2.0.0]`;
|
||||
|
||||
exports[`UpgradeCommandRunner Workspace upgrade should fail when workspace version is not defined 1`] = `
|
||||
[Error: Unable to run the upgrade command. Aborting the upgrade process.
|
||||
Please ensure that all workspaces are on at least the previous minor version (1.0.0).
|
||||
If any workspaces are not on the previous minor version, roll back to that version and run the upgrade command again.]
|
||||
`;
|
||||
|
||||
exports[`UpgradeCommandRunner Workspace upgrade should fail when workspace version is not equal to fromVersion 1`] = `
|
||||
[Error: Unable to run the upgrade command. Aborting the upgrade process.
|
||||
Please ensure that all workspaces are on at least the previous minor version (1.0.0).
|
||||
If any workspaces are not on the previous minor version, roll back to that version and run the upgrade command again.]
|
||||
`;
|
||||
+62
-43
@@ -7,35 +7,41 @@ import {
|
||||
} from 'twenty-shared/testing';
|
||||
import { type Repository } from 'typeorm';
|
||||
|
||||
import { UpgradeCommandRunner } from 'src/database/commands/command-runners/upgrade.command-runner';
|
||||
import {
|
||||
UpgradeCommandRunner,
|
||||
type AllCommands,
|
||||
} from 'src/database/commands/command-runners/upgrade.command-runner';
|
||||
import { CoreMigrationRunnerService } from 'src/database/commands/core-migration-runner/services/core-migration-runner.service';
|
||||
import { UPGRADE_COMMAND_SUPPORTED_VERSIONS } from 'src/engine/constants/upgrade-command-supported-versions.constant';
|
||||
import { CoreEngineVersionService } from 'src/engine/core-engine-version/services/core-engine-version.service';
|
||||
import { type ConfigVariables } from 'src/engine/core-modules/twenty-config/config-variables';
|
||||
import { TwentyConfigService } from 'src/engine/core-modules/twenty-config/twenty-config.service';
|
||||
import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service';
|
||||
import { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager';
|
||||
import { WorkspaceVersionService } from 'src/engine/workspace-manager/workspace-version/services/workspace-version.service';
|
||||
|
||||
const CURRENT_VERSION =
|
||||
UPGRADE_COMMAND_SUPPORTED_VERSIONS[
|
||||
UPGRADE_COMMAND_SUPPORTED_VERSIONS.length - 1
|
||||
];
|
||||
const PREVIOUS_VERSION =
|
||||
UPGRADE_COMMAND_SUPPORTED_VERSIONS[
|
||||
UPGRADE_COMMAND_SUPPORTED_VERSIONS.length - 2
|
||||
];
|
||||
|
||||
class BasicUpgradeCommandRunner extends UpgradeCommandRunner {
|
||||
allCommands = {
|
||||
'1.0.0': [],
|
||||
'2.0.0': [],
|
||||
};
|
||||
allCommands = Object.fromEntries(
|
||||
UPGRADE_COMMAND_SUPPORTED_VERSIONS.map((version) => [version, []]),
|
||||
) as unknown as AllCommands;
|
||||
}
|
||||
|
||||
class InvalidUpgradeCommandRunner extends UpgradeCommandRunner {
|
||||
allCommands = {
|
||||
invalid: [],
|
||||
'2.0.0': [],
|
||||
};
|
||||
}
|
||||
|
||||
type CommandRunnerValues =
|
||||
| typeof BasicUpgradeCommandRunner
|
||||
| typeof InvalidUpgradeCommandRunner;
|
||||
type CommandRunnerValues = typeof BasicUpgradeCommandRunner;
|
||||
|
||||
const generateMockWorkspace = (overrides?: Partial<WorkspaceEntity>) =>
|
||||
({
|
||||
id: 'workspace-id',
|
||||
version: '1.0.0',
|
||||
version: PREVIOUS_VERSION,
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
allowImpersonation: false,
|
||||
@@ -73,12 +79,18 @@ const buildUpgradeCommandModule = async ({
|
||||
twentyConfigService: TwentyConfigService,
|
||||
globalWorkspaceOrmManager: GlobalWorkspaceOrmManager,
|
||||
dataSourceService: DataSourceService,
|
||||
coreEngineVersionService: CoreEngineVersionService,
|
||||
workspaceVersionService: WorkspaceVersionService,
|
||||
coreMigrationRunnerService: CoreMigrationRunnerService,
|
||||
) => {
|
||||
return new commandRunner(
|
||||
workspaceRepository,
|
||||
twentyConfigService,
|
||||
globalWorkspaceOrmManager,
|
||||
dataSourceService,
|
||||
coreEngineVersionService,
|
||||
workspaceVersionService,
|
||||
coreMigrationRunnerService,
|
||||
);
|
||||
},
|
||||
inject: [
|
||||
@@ -86,6 +98,9 @@ const buildUpgradeCommandModule = async ({
|
||||
TwentyConfigService,
|
||||
GlobalWorkspaceOrmManager,
|
||||
DataSourceService,
|
||||
CoreEngineVersionService,
|
||||
WorkspaceVersionService,
|
||||
CoreMigrationRunnerService,
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -98,6 +113,7 @@ const buildUpgradeCommandModule = async ({
|
||||
),
|
||||
update: jest.fn(),
|
||||
find: jest.fn().mockResolvedValue(workspaces),
|
||||
exists: jest.fn().mockResolvedValue(workspaces.length > 0),
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -130,6 +146,12 @@ const buildUpgradeCommandModule = async ({
|
||||
provide: DataSourceService,
|
||||
useValue: mockDataSourceService,
|
||||
},
|
||||
CoreEngineVersionService,
|
||||
WorkspaceVersionService,
|
||||
{
|
||||
provide: CoreMigrationRunnerService,
|
||||
useValue: { run: jest.fn().mockResolvedValue(undefined) },
|
||||
},
|
||||
],
|
||||
}).compile();
|
||||
|
||||
@@ -139,7 +161,7 @@ const buildUpgradeCommandModule = async ({
|
||||
describe('UpgradeCommandRunner', () => {
|
||||
let upgradeCommandRunner: BasicUpgradeCommandRunner;
|
||||
let workspaceRepository: Repository<WorkspaceEntity>;
|
||||
let runCoreMigrationsSpy: jest.SpyInstance;
|
||||
let coreMigrationRunnerService: CoreMigrationRunnerService;
|
||||
|
||||
type BuildModuleAndSetupSpiesArgs = {
|
||||
numberOfWorkspace?: number;
|
||||
@@ -153,7 +175,7 @@ describe('UpgradeCommandRunner', () => {
|
||||
workspaceOverride,
|
||||
workspaces,
|
||||
commandRunner = BasicUpgradeCommandRunner,
|
||||
appVersion = '2.0.0',
|
||||
appVersion = CURRENT_VERSION,
|
||||
}: BuildModuleAndSetupSpiesArgs) => {
|
||||
const generatedWorkspaces = Array.from(
|
||||
{ length: numberOfWorkspace },
|
||||
@@ -176,9 +198,8 @@ describe('UpgradeCommandRunner', () => {
|
||||
jest.spyOn(upgradeCommandRunner['logger'], 'warn').mockImplementation();
|
||||
|
||||
jest.spyOn(upgradeCommandRunner, 'runOnWorkspace');
|
||||
runCoreMigrationsSpy = jest
|
||||
.spyOn(upgradeCommandRunner, 'runCoreMigrations')
|
||||
.mockImplementation(() => Promise.resolve());
|
||||
|
||||
coreMigrationRunnerService = module.get(CoreMigrationRunnerService);
|
||||
|
||||
workspaceRepository = module.get<Repository<WorkspaceEntity>>(
|
||||
getRepositoryToken(WorkspaceEntity),
|
||||
@@ -190,12 +211,10 @@ describe('UpgradeCommandRunner', () => {
|
||||
id: 'higher_version_workspace',
|
||||
version: '42.42.42',
|
||||
});
|
||||
const appVersion = '2.0.0';
|
||||
|
||||
await buildModuleAndSetupSpies({
|
||||
numberOfWorkspace: 0,
|
||||
workspaces: [higherVersionWorkspace],
|
||||
appVersion,
|
||||
});
|
||||
// @ts-expect-error legacy noImplicitAny
|
||||
const passedParams = [];
|
||||
@@ -221,11 +240,9 @@ describe('UpgradeCommandRunner', () => {
|
||||
|
||||
it('should run upgrade over several workspaces', async () => {
|
||||
const numberOfWorkspace = 42;
|
||||
const appVersion = '2.0.0';
|
||||
|
||||
await buildModuleAndSetupSpies({
|
||||
numberOfWorkspace,
|
||||
appVersion,
|
||||
});
|
||||
// @ts-expect-error legacy noImplicitAny
|
||||
const passedParams = [];
|
||||
@@ -240,7 +257,7 @@ describe('UpgradeCommandRunner', () => {
|
||||
expect(workspaceRepository.update).toHaveBeenNthCalledWith(
|
||||
numberOfWorkspace,
|
||||
{ id: expect.any(String) },
|
||||
{ version: appVersion },
|
||||
{ version: CURRENT_VERSION },
|
||||
);
|
||||
expect(upgradeCommandRunner.migrationReport.success.length).toBe(42);
|
||||
expect(upgradeCommandRunner.migrationReport.fail.length).toBe(0);
|
||||
@@ -254,9 +271,9 @@ describe('UpgradeCommandRunner', () => {
|
||||
title: 'even if workspace version and app version differ in patch',
|
||||
context: {
|
||||
input: {
|
||||
appVersion: 'v2.0.0',
|
||||
appVersion: `v${CURRENT_VERSION}`,
|
||||
workspaceOverride: {
|
||||
version: 'v1.0.12',
|
||||
version: `v${PREVIOUS_VERSION.replace('.0', '.12')}`,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -266,9 +283,9 @@ describe('UpgradeCommandRunner', () => {
|
||||
'even if workspace version and app version differ in patch and semantic',
|
||||
context: {
|
||||
input: {
|
||||
appVersion: 'v2.0.0',
|
||||
appVersion: `v${CURRENT_VERSION}`,
|
||||
workspaceOverride: {
|
||||
version: '1.0.12',
|
||||
version: PREVIOUS_VERSION.replace('.0', '.12'),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -277,9 +294,9 @@ describe('UpgradeCommandRunner', () => {
|
||||
title: 'even if app version contains a patch value',
|
||||
context: {
|
||||
input: {
|
||||
appVersion: '2.0.24',
|
||||
appVersion: CURRENT_VERSION.replace('.0', '.24'),
|
||||
workspaceOverride: {
|
||||
version: '1.0.12',
|
||||
version: PREVIOUS_VERSION.replace('.0', '.12'),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -303,7 +320,7 @@ describe('UpgradeCommandRunner', () => {
|
||||
|
||||
expect(failReport.length).toBe(0);
|
||||
expect(successReport.length).toBe(1);
|
||||
expect(runCoreMigrationsSpy).toHaveBeenCalledTimes(1);
|
||||
expect(coreMigrationRunnerService.run).toHaveBeenCalledTimes(1);
|
||||
const { workspaceId } = successReport[0];
|
||||
|
||||
expect(workspaceId).toBe('workspace_0');
|
||||
@@ -316,19 +333,20 @@ describe('UpgradeCommandRunner', () => {
|
||||
input: Omit<BuildModuleAndSetupSpiesArgs, 'numberOfWorkspace'>;
|
||||
output?: {
|
||||
failReportWorkspaceId: string;
|
||||
expectedErrorMessage: string;
|
||||
};
|
||||
}>[] = [
|
||||
{
|
||||
title: 'when workspace version is not equal to fromVersion',
|
||||
context: {
|
||||
input: {
|
||||
appVersion: '2.0.0',
|
||||
workspaceOverride: {
|
||||
version: '0.1.0',
|
||||
},
|
||||
},
|
||||
output: {
|
||||
failReportWorkspaceId: 'workspace_0',
|
||||
expectedErrorMessage: `Unable to run the upgrade command. Aborting the upgrade process.\nPlease ensure that all workspaces are on at least the previous minor version (${PREVIOUS_VERSION}).\nIf any workspaces are not on the previous minor version, roll back to that version and run the upgrade command again.`,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -342,6 +360,7 @@ describe('UpgradeCommandRunner', () => {
|
||||
},
|
||||
output: {
|
||||
failReportWorkspaceId: 'workspace_0',
|
||||
expectedErrorMessage: `Unable to run the upgrade command. Aborting the upgrade process.\nPlease ensure that all workspaces are on at least the previous minor version (${PREVIOUS_VERSION}).\nIf any workspaces are not on the previous minor version, roll back to that version and run the upgrade command again.`,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -353,6 +372,8 @@ describe('UpgradeCommandRunner', () => {
|
||||
},
|
||||
output: {
|
||||
failReportWorkspaceId: 'global',
|
||||
expectedErrorMessage:
|
||||
'APP_VERSION is not defined, please double check your env variables',
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -364,6 +385,8 @@ describe('UpgradeCommandRunner', () => {
|
||||
},
|
||||
output: {
|
||||
failReportWorkspaceId: 'global',
|
||||
expectedErrorMessage:
|
||||
'No command found for version 42.0.0. Please check the commands record.',
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -371,15 +394,11 @@ describe('UpgradeCommandRunner', () => {
|
||||
title: 'when previous version is not found',
|
||||
context: {
|
||||
input: {
|
||||
appVersion: '1.0.0',
|
||||
appVersion: UPGRADE_COMMAND_SUPPORTED_VERSIONS[0],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'when all commands contains invalid semver keys',
|
||||
context: {
|
||||
input: {
|
||||
commandRunner: InvalidUpgradeCommandRunner,
|
||||
output: {
|
||||
failReportWorkspaceId: 'global',
|
||||
expectedErrorMessage: `No previous version found for version ${UPGRADE_COMMAND_SUPPORTED_VERSIONS[0]}. Available versions: ${UPGRADE_COMMAND_SUPPORTED_VERSIONS.join(', ')}`,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -403,7 +422,7 @@ describe('UpgradeCommandRunner', () => {
|
||||
const { workspaceId, error } = failReport[0];
|
||||
|
||||
expect(workspaceId).toBe(output?.failReportWorkspaceId ?? 'global');
|
||||
expect(error).toMatchSnapshot();
|
||||
expect(error).toEqual(new Error(output?.expectedErrorMessage ?? ''));
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
+21
-129
@@ -1,13 +1,9 @@
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
|
||||
import { exec } from 'child_process';
|
||||
import { promisify } from 'util';
|
||||
|
||||
import chalk from 'chalk';
|
||||
import { SemVer } from 'semver';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { WorkspaceActivationStatus } from 'twenty-shared/workspace';
|
||||
import { In, Repository } from 'typeorm';
|
||||
import { Repository } from 'typeorm';
|
||||
|
||||
import {
|
||||
ActiveOrSuspendedWorkspacesMigrationCommandOptions,
|
||||
@@ -17,22 +13,24 @@ import {
|
||||
RunOnWorkspaceArgs,
|
||||
WorkspacesMigrationCommandRunner,
|
||||
} from 'src/database/commands/command-runners/workspaces-migration.command-runner';
|
||||
import { CoreMigrationRunnerService } from 'src/database/commands/core-migration-runner/services/core-migration-runner.service';
|
||||
import { type UpgradeCommandVersion } from 'src/engine/constants/upgrade-command-supported-versions.constant';
|
||||
import { CoreEngineVersionService } from 'src/engine/core-engine-version/services/core-engine-version.service';
|
||||
import { TwentyConfigService } from 'src/engine/core-modules/twenty-config/twenty-config.service';
|
||||
import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
import { type DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service';
|
||||
import { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager';
|
||||
import { WorkspaceVersionService } from 'src/engine/workspace-manager/workspace-version/services/workspace-version.service';
|
||||
import {
|
||||
type CompareVersionMajorAndMinorReturnType,
|
||||
compareVersionMajorAndMinor,
|
||||
} from 'src/utils/version/compare-version-minor-and-major';
|
||||
import { getPreviousVersion } from 'src/utils/version/get-previous-version';
|
||||
|
||||
export type VersionCommands = (
|
||||
| WorkspacesMigrationCommandRunner
|
||||
| ActiveOrSuspendedWorkspacesMigrationCommandRunner
|
||||
)[];
|
||||
export type AllCommands = Record<string, VersionCommands>;
|
||||
const execPromise = promisify(exec);
|
||||
export type AllCommands = Record<UpgradeCommandVersion, VersionCommands>;
|
||||
|
||||
export abstract class UpgradeCommandRunner extends ActiveOrSuspendedWorkspacesMigrationCommandRunner {
|
||||
private fromWorkspaceVersion: SemVer;
|
||||
@@ -47,95 +45,13 @@ export abstract class UpgradeCommandRunner extends ActiveOrSuspendedWorkspacesMi
|
||||
protected readonly twentyConfigService: TwentyConfigService,
|
||||
protected readonly globalWorkspaceOrmManager: GlobalWorkspaceOrmManager,
|
||||
protected readonly dataSourceService: DataSourceService,
|
||||
protected readonly coreEngineVersionService: CoreEngineVersionService,
|
||||
protected readonly workspaceVersionService: WorkspaceVersionService,
|
||||
protected readonly coreMigrationRunnerService: CoreMigrationRunnerService,
|
||||
) {
|
||||
super(workspaceRepository, globalWorkspaceOrmManager, dataSourceService);
|
||||
}
|
||||
|
||||
private async loadActiveOrSuspendedWorkspace() {
|
||||
return await this.workspaceRepository.find({
|
||||
select: ['id', 'version', 'displayName'],
|
||||
where: {
|
||||
activationStatus: In([
|
||||
WorkspaceActivationStatus.ACTIVE,
|
||||
WorkspaceActivationStatus.SUSPENDED,
|
||||
]),
|
||||
},
|
||||
order: {
|
||||
id: 'ASC',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
private async shouldSkipUpgradeIfFreshInstallation(): Promise<boolean> {
|
||||
const activeWorkspaceOrSuspendedWorkspaceCount =
|
||||
await this.loadActiveOrSuspendedWorkspace();
|
||||
|
||||
return activeWorkspaceOrSuspendedWorkspaceCount.length === 0;
|
||||
}
|
||||
|
||||
async runCoreMigrations(): Promise<void> {
|
||||
this.logger.log('Running global database migrations');
|
||||
|
||||
try {
|
||||
this.logger.log('Running core datasource migrations...');
|
||||
const coreResult = await execPromise(
|
||||
'npx -y typeorm migration:run -d dist/database/typeorm/core/core.datasource',
|
||||
);
|
||||
|
||||
this.logger.log(coreResult.stdout);
|
||||
|
||||
this.logger.log('Database migrations completed successfully');
|
||||
} catch (error) {
|
||||
this.logger.error('Error running database migrations:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
private async workspacesThatAreBelowFromWorkspaceVersion(
|
||||
fromWorkspaceVersion: SemVer,
|
||||
): Promise<Pick<WorkspaceEntity, 'id' | 'displayName' | 'version'>[]> {
|
||||
try {
|
||||
const allActiveOrSuspendedWorkspaces =
|
||||
await this.loadActiveOrSuspendedWorkspace();
|
||||
|
||||
if (allActiveOrSuspendedWorkspaces.length === 0) {
|
||||
this.logger.log(
|
||||
'No workspaces found. Running migrations for fresh installation.',
|
||||
);
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
const workspacesThatAreBelowFromWorkspaceVersion =
|
||||
allActiveOrSuspendedWorkspaces.filter((workspace) => {
|
||||
if (!isDefined(workspace.version)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
try {
|
||||
const versionCompareResult = compareVersionMajorAndMinor(
|
||||
workspace.version,
|
||||
fromWorkspaceVersion.version,
|
||||
);
|
||||
|
||||
return versionCompareResult === 'lower';
|
||||
} catch (error) {
|
||||
this.logger.error(
|
||||
`Error checking workspace ${workspace.id} version: ${error.message}`,
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
return workspacesThatAreBelowFromWorkspaceVersion;
|
||||
} catch (error) {
|
||||
this.logger.error('Error checking workspaces below version:', error);
|
||||
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
private setUpgradeContextVersionsAndCommandsForCurrentAppVersion() {
|
||||
const upgradeContextIsAlreadyDefined = [
|
||||
this.currentAppVersion,
|
||||
@@ -147,8 +63,9 @@ export abstract class UpgradeCommandRunner extends ActiveOrSuspendedWorkspacesMi
|
||||
return;
|
||||
}
|
||||
|
||||
const currentAppVersion = this.retrieveCurrentAppVersion();
|
||||
const currentVersionMajorMinor = `${currentAppVersion.major}.${currentAppVersion.minor}.0`;
|
||||
const currentAppVersion = this.coreEngineVersionService.getCurrentVersion();
|
||||
const currentVersionMajorMinor =
|
||||
`${currentAppVersion.major}.${currentAppVersion.minor}.0` as UpgradeCommandVersion;
|
||||
const currentCommands = this.allCommands[currentVersionMajorMinor];
|
||||
|
||||
if (!isDefined(currentCommands)) {
|
||||
@@ -157,17 +74,8 @@ export abstract class UpgradeCommandRunner extends ActiveOrSuspendedWorkspacesMi
|
||||
);
|
||||
}
|
||||
|
||||
const allCommandsVersions = Object.keys(this.allCommands);
|
||||
const previousVersion = getPreviousVersion({
|
||||
currentVersion: currentVersionMajorMinor,
|
||||
versions: allCommandsVersions,
|
||||
});
|
||||
const previousVersion = this.coreEngineVersionService.getPreviousVersion();
|
||||
|
||||
if (!isDefined(previousVersion)) {
|
||||
throw new Error(
|
||||
`No previous version found for version ${currentAppVersion}. Please review the "allCommands" record. Available versions are: ${allCommandsVersions.join(', ')}`,
|
||||
);
|
||||
}
|
||||
this.commands = currentCommands;
|
||||
this.fromWorkspaceVersion = previousVersion;
|
||||
this.currentAppVersion = currentAppVersion;
|
||||
@@ -189,10 +97,12 @@ export abstract class UpgradeCommandRunner extends ActiveOrSuspendedWorkspacesMi
|
||||
try {
|
||||
this.setUpgradeContextVersionsAndCommandsForCurrentAppVersion();
|
||||
|
||||
const shouldSkipUpgradeIfFreshInstallation =
|
||||
await this.shouldSkipUpgradeIfFreshInstallation();
|
||||
// On fresh installs there are no workspaces yet, so skip the
|
||||
// per-workspace upgrade loop (core migrations already ran above).
|
||||
const hasWorkspaces =
|
||||
await this.workspaceVersionService.hasActiveOrSuspendedWorkspaces();
|
||||
|
||||
if (shouldSkipUpgradeIfFreshInstallation) {
|
||||
if (!hasWorkspaces) {
|
||||
this.logger.log(
|
||||
chalk.blue('Fresh installation detected, skipping migration'),
|
||||
);
|
||||
@@ -201,8 +111,8 @@ export abstract class UpgradeCommandRunner extends ActiveOrSuspendedWorkspacesMi
|
||||
}
|
||||
|
||||
const workspacesThatAreBelowFromWorkspaceVersion =
|
||||
await this.workspacesThatAreBelowFromWorkspaceVersion(
|
||||
this.fromWorkspaceVersion,
|
||||
await this.workspaceVersionService.getWorkspacesBelowVersion(
|
||||
this.fromWorkspaceVersion.version,
|
||||
);
|
||||
|
||||
if (workspacesThatAreBelowFromWorkspaceVersion.length > 0) {
|
||||
@@ -234,7 +144,7 @@ If any workspaces are not on the previous minor version, roll back to that versi
|
||||
return;
|
||||
}
|
||||
|
||||
await this.runCoreMigrations();
|
||||
await this.coreMigrationRunnerService.run();
|
||||
await super.runMigrationCommand(passedParams, options);
|
||||
}
|
||||
|
||||
@@ -295,24 +205,6 @@ If any workspaces are not on the previous minor version, roll back to that versi
|
||||
}
|
||||
}
|
||||
|
||||
private retrieveCurrentAppVersion() {
|
||||
const appVersion = this.twentyConfigService.get('APP_VERSION');
|
||||
|
||||
if (!isDefined(appVersion)) {
|
||||
throw new Error(
|
||||
'Cannot run upgrade command when APP_VERSION is not defined, please double check your env variables',
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
return new SemVer(appVersion);
|
||||
} catch {
|
||||
throw new Error(
|
||||
`Should never occur, APP_VERSION is invalid ${appVersion}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private async retrieveWorkspaceVersionAndCompareToWorkspaceFromVersion(
|
||||
workspaceId: string,
|
||||
): Promise<CompareVersionMajorAndMinorReturnType> {
|
||||
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
|
||||
import { CoreMigrationRunnerService } from 'src/database/commands/core-migration-runner/services/core-migration-runner.service';
|
||||
|
||||
@Module({
|
||||
providers: [CoreMigrationRunnerService],
|
||||
exports: [CoreMigrationRunnerService],
|
||||
})
|
||||
export class CoreMigrationRunnerModule {}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user