diff --git a/packages/twenty-client-sdk/src/metadata/generated/schema.graphql b/packages/twenty-client-sdk/src/metadata/generated/schema.graphql index d124db6f1a..2a93838a16 100644 --- a/packages/twenty-client-sdk/src/metadata/generated/schema.graphql +++ b/packages/twenty-client-sdk/src/metadata/generated/schema.graphql @@ -3193,6 +3193,7 @@ type Mutation { updateView(id: String!, input: UpdateViewInput!): View! deleteView(id: String!): Boolean! destroyView(id: String!): Boolean! + upsertViewWidget(input: UpsertViewWidgetInput!): View! createViewSort(input: CreateViewSortInput!): ViewSort! updateViewSort(input: UpdateViewSortInput!): ViewSort! deleteViewSort(input: DeleteViewSortInput!): Boolean! @@ -3510,6 +3511,59 @@ input UpdateViewInput { shouldHideEmptyGroups: Boolean } +input UpsertViewWidgetInput { + """The id of the view widget (page layout widget).""" + widgetId: UUID! + + """The view fields to upsert.""" + viewFields: [UpsertViewWidgetViewFieldInput!] + + """The view filters to upsert.""" + viewFilters: [UpsertViewWidgetViewFilterInput!] + + """The view filter groups to upsert.""" + viewFilterGroups: [UpsertViewWidgetViewFilterGroupInput!] + + """The view sorts to upsert.""" + viewSorts: [UpsertViewWidgetViewSortInput!] +} + +input UpsertViewWidgetViewFieldInput { + """The id of an existing view field to update.""" + viewFieldId: UUID + + """ + The field metadata id. Used to create a new view field when viewFieldId is not provided. + """ + fieldMetadataId: UUID + isVisible: Boolean! + position: Float! + size: Float +} + +input UpsertViewWidgetViewFilterInput { + id: UUID + fieldMetadataId: UUID! + operand: ViewFilterOperand = CONTAINS + value: JSON! + viewFilterGroupId: UUID + positionInViewFilterGroup: Float + subFieldName: String +} + +input UpsertViewWidgetViewFilterGroupInput { + id: UUID + parentViewFilterGroupId: UUID + logicalOperator: ViewFilterGroupLogicalOperator = AND + positionInViewFilterGroup: Float +} + +input UpsertViewWidgetViewSortInput { + id: UUID + fieldMetadataId: UUID! + direction: ViewSortDirection = ASC +} + input CreateViewSortInput { id: UUID fieldMetadataId: UUID! diff --git a/packages/twenty-client-sdk/src/metadata/generated/schema.ts b/packages/twenty-client-sdk/src/metadata/generated/schema.ts index ea574410d5..f7d5564237 100644 --- a/packages/twenty-client-sdk/src/metadata/generated/schema.ts +++ b/packages/twenty-client-sdk/src/metadata/generated/schema.ts @@ -2675,6 +2675,7 @@ export interface Mutation { updateView: View deleteView: Scalars['Boolean'] destroyView: Scalars['Boolean'] + upsertViewWidget: View createViewSort: ViewSort updateViewSort: ViewSort deleteViewSort: Scalars['Boolean'] @@ -5725,6 +5726,7 @@ export interface MutationGenqlSelection{ updateView?: (ViewGenqlSelection & { __args: {id: Scalars['String'], input: UpdateViewInput} }) deleteView?: { __args: {id: Scalars['String']} } destroyView?: { __args: {id: Scalars['String']} } + upsertViewWidget?: (ViewGenqlSelection & { __args: {input: UpsertViewWidgetInput} }) createViewSort?: (ViewSortGenqlSelection & { __args: {input: CreateViewSortInput} }) updateViewSort?: (ViewSortGenqlSelection & { __args: {input: UpdateViewSortInput} }) deleteViewSort?: { __args: {input: DeleteViewSortInput} } @@ -5944,6 +5946,30 @@ export interface CreateViewInput {id?: (Scalars['UUID'] | null),name: Scalars['S export interface UpdateViewInput {id?: (Scalars['UUID'] | null),name?: (Scalars['String'] | null),type?: (ViewType | null),icon?: (Scalars['String'] | null),position?: (Scalars['Float'] | null),isCompact?: (Scalars['Boolean'] | null),openRecordIn?: (ViewOpenRecordIn | null),kanbanAggregateOperation?: (AggregateOperations | null),kanbanAggregateOperationFieldMetadataId?: (Scalars['UUID'] | null),anyFieldFilterValue?: (Scalars['String'] | null),calendarLayout?: (ViewCalendarLayout | null),calendarFieldMetadataId?: (Scalars['UUID'] | null),visibility?: (ViewVisibility | null),mainGroupByFieldMetadataId?: (Scalars['UUID'] | null),shouldHideEmptyGroups?: (Scalars['Boolean'] | null)} +export interface UpsertViewWidgetInput { +/** The id of the view widget (page layout widget). */ +widgetId: Scalars['UUID'], +/** The view fields to upsert. */ +viewFields?: (UpsertViewWidgetViewFieldInput[] | null), +/** The view filters to upsert. */ +viewFilters?: (UpsertViewWidgetViewFilterInput[] | null), +/** The view filter groups to upsert. */ +viewFilterGroups?: (UpsertViewWidgetViewFilterGroupInput[] | null), +/** The view sorts to upsert. */ +viewSorts?: (UpsertViewWidgetViewSortInput[] | null)} + +export interface UpsertViewWidgetViewFieldInput { +/** The id of an existing view field to update. */ +viewFieldId?: (Scalars['UUID'] | null), +/** The field metadata id. Used to create a new view field when viewFieldId is not provided. */ +fieldMetadataId?: (Scalars['UUID'] | null),isVisible: Scalars['Boolean'],position: Scalars['Float'],size?: (Scalars['Float'] | null)} + +export interface UpsertViewWidgetViewFilterInput {id?: (Scalars['UUID'] | null),fieldMetadataId: Scalars['UUID'],operand?: (ViewFilterOperand | null),value: Scalars['JSON'],viewFilterGroupId?: (Scalars['UUID'] | null),positionInViewFilterGroup?: (Scalars['Float'] | null),subFieldName?: (Scalars['String'] | null)} + +export interface UpsertViewWidgetViewFilterGroupInput {id?: (Scalars['UUID'] | null),parentViewFilterGroupId?: (Scalars['UUID'] | null),logicalOperator?: (ViewFilterGroupLogicalOperator | null),positionInViewFilterGroup?: (Scalars['Float'] | null)} + +export interface UpsertViewWidgetViewSortInput {id?: (Scalars['UUID'] | null),fieldMetadataId: Scalars['UUID'],direction?: (ViewSortDirection | null)} + export interface CreateViewSortInput {id?: (Scalars['UUID'] | null),fieldMetadataId: Scalars['UUID'],direction?: (ViewSortDirection | null),viewId: Scalars['UUID']} export interface UpdateViewSortInput { diff --git a/packages/twenty-client-sdk/src/metadata/generated/types.ts b/packages/twenty-client-sdk/src/metadata/generated/types.ts index ba6a53d3e9..9f35405fc8 100644 --- a/packages/twenty-client-sdk/src/metadata/generated/types.ts +++ b/packages/twenty-client-sdk/src/metadata/generated/types.ts @@ -80,9 +80,9 @@ export default { 334, 341, 348, - 379, - 454, - 466 + 384, + 459, + 471 ], "types": { "BillingProductDTO": { @@ -6941,11 +6941,20 @@ export default { ] } ], + "upsertViewWidget": [ + 63, + { + "input": [ + 358, + "UpsertViewWidgetInput!" + ] + } + ], "createViewSort": [ 60, { "input": [ - 358, + 363, "CreateViewSortInput!" ] } @@ -6954,7 +6963,7 @@ export default { 60, { "input": [ - 359, + 364, "UpdateViewSortInput!" ] } @@ -6963,7 +6972,7 @@ export default { 6, { "input": [ - 361, + 366, "DeleteViewSortInput!" ] } @@ -6972,7 +6981,7 @@ export default { 6, { "input": [ - 362, + 367, "DestroyViewSortInput!" ] } @@ -6981,7 +6990,7 @@ export default { 53, { "input": [ - 363, + 368, "UpdateViewFieldInput!" ] } @@ -6990,7 +6999,7 @@ export default { 53, { "input": [ - 365, + 370, "CreateViewFieldInput!" ] } @@ -6999,7 +7008,7 @@ export default { 53, { "inputs": [ - 365, + 370, "[CreateViewFieldInput!]!" ] } @@ -7008,7 +7017,7 @@ export default { 53, { "input": [ - 366, + 371, "DeleteViewFieldInput!" ] } @@ -7017,7 +7026,7 @@ export default { 53, { "input": [ - 367, + 372, "DestroyViewFieldInput!" ] } @@ -7026,7 +7035,7 @@ export default { 62, { "input": [ - 368, + 373, "UpdateViewFieldGroupInput!" ] } @@ -7035,7 +7044,7 @@ export default { 62, { "input": [ - 370, + 375, "CreateViewFieldGroupInput!" ] } @@ -7044,7 +7053,7 @@ export default { 62, { "inputs": [ - 370, + 375, "[CreateViewFieldGroupInput!]!" ] } @@ -7053,7 +7062,7 @@ export default { 62, { "input": [ - 371, + 376, "DeleteViewFieldGroupInput!" ] } @@ -7062,7 +7071,7 @@ export default { 62, { "input": [ - 372, + 377, "DestroyViewFieldGroupInput!" ] } @@ -7071,7 +7080,7 @@ export default { 63, { "input": [ - 373, + 378, "UpsertFieldsWidgetInput!" ] } @@ -7080,7 +7089,7 @@ export default { 2, { "input": [ - 376, + 381, "CreateApiKeyInput!" ] } @@ -7089,7 +7098,7 @@ export default { 2, { "input": [ - 377, + 382, "UpdateApiKeyInput!" ] } @@ -7098,7 +7107,7 @@ export default { 2, { "input": [ - 378, + 383, "RevokeApiKeyInput!" ] } @@ -7140,7 +7149,7 @@ export default { 119, { "type": [ - 379, + 384, "AnalyticsType!" ], "name": [ @@ -7253,7 +7262,7 @@ export default { 120, { "input": [ - 380, + 385, "CreateApprovedAccessDomainInput!" ] } @@ -7262,7 +7271,7 @@ export default { 6, { "input": [ - 381, + 386, "DeleteApprovedAccessDomainInput!" ] } @@ -7271,7 +7280,7 @@ export default { 120, { "input": [ - 382, + 387, "ValidateApprovedAccessDomainInput!" ] } @@ -7280,7 +7289,7 @@ export default { 116, { "input": [ - 383, + 388, "CreatePageLayoutTabInput!" ] } @@ -7293,7 +7302,7 @@ export default { "String!" ], "input": [ - 384, + 389, "UpdatePageLayoutTabInput!" ] } @@ -7311,7 +7320,7 @@ export default { 117, { "input": [ - 385, + 390, "CreatePageLayoutInput!" ] } @@ -7324,7 +7333,7 @@ export default { "String!" ], "input": [ - 386, + 391, "UpdatePageLayoutInput!" ] } @@ -7346,7 +7355,7 @@ export default { "String!" ], "input": [ - 387, + 392, "UpdatePageLayoutWithTabsInput!" ] } @@ -7382,7 +7391,7 @@ export default { 78, { "input": [ - 391, + 396, "CreatePageLayoutWidgetInput!" ] } @@ -7395,7 +7404,7 @@ export default { "String!" ], "input": [ - 392, + 397, "UpdatePageLayoutWidgetInput!" ] } @@ -7422,7 +7431,7 @@ export default { 35, { "input": [ - 393, + 398, "CreateLogicFunctionFromSourceInput!" ] } @@ -7431,7 +7440,7 @@ export default { 158, { "input": [ - 394, + 399, "ExecuteOneLogicFunctionInput!" ] } @@ -7440,7 +7449,7 @@ export default { 6, { "input": [ - 395, + 400, "UpdateLogicFunctionFromSourceInput!" ] } @@ -7449,7 +7458,7 @@ export default { 263, { "input": [ - 397, + 402, "CreateCommandMenuItemInput!" ] } @@ -7458,7 +7467,7 @@ export default { 263, { "input": [ - 398, + 403, "UpdateCommandMenuItemInput!" ] } @@ -7476,7 +7485,7 @@ export default { 34, { "input": [ - 399, + 404, "CreateFrontComponentInput!" ] } @@ -7485,7 +7494,7 @@ export default { 34, { "input": [ - 400, + 405, "UpdateFrontComponentInput!" ] } @@ -7503,7 +7512,7 @@ export default { 49, { "input": [ - 402, + 407, "CreateOneObjectInput!" ] } @@ -7512,7 +7521,7 @@ export default { 49, { "input": [ - 404, + 409, "DeleteOneObjectInput!" ] } @@ -7521,7 +7530,7 @@ export default { 49, { "input": [ - 405, + 410, "UpdateOneObjectInput!" ] } @@ -7530,7 +7539,7 @@ export default { 25, { "input": [ - 407, + 412, "CreateAgentInput!" ] } @@ -7539,7 +7548,7 @@ export default { 25, { "input": [ - 408, + 413, "UpdateAgentInput!" ] } @@ -7570,7 +7579,7 @@ export default { 29, { "createRoleInput": [ - 409, + 414, "CreateRoleInput!" ] } @@ -7579,7 +7588,7 @@ export default { 29, { "updateRoleInput": [ - 410, + 415, "UpdateRoleInput!" ] } @@ -7597,7 +7606,7 @@ export default { 16, { "upsertObjectPermissionsInput": [ - 412, + 417, "UpsertObjectPermissionsInput!" ] } @@ -7606,7 +7615,7 @@ export default { 27, { "upsertPermissionFlagsInput": [ - 414, + 419, "UpsertPermissionFlagsInput!" ] } @@ -7615,7 +7624,7 @@ export default { 26, { "upsertFieldPermissionsInput": [ - 415, + 420, "UpsertFieldPermissionsInput!" ] } @@ -7624,7 +7633,7 @@ export default { 220, { "input": [ - 417, + 422, "UpsertRowLevelPermissionPredicatesInput!" ] } @@ -7655,7 +7664,7 @@ export default { 37, { "input": [ - 420, + 425, "CreateOneFieldMetadataInput!" ] } @@ -7664,7 +7673,7 @@ export default { 37, { "input": [ - 422, + 427, "UpdateOneFieldMetadataInput!" ] } @@ -7673,7 +7682,7 @@ export default { 37, { "input": [ - 424, + 429, "DeleteOneFieldInput!" ] } @@ -7682,7 +7691,7 @@ export default { 59, { "input": [ - 425, + 430, "CreateViewGroupInput!" ] } @@ -7691,7 +7700,7 @@ export default { 59, { "inputs": [ - 425, + 430, "[CreateViewGroupInput!]!" ] } @@ -7700,7 +7709,7 @@ export default { 59, { "input": [ - 426, + 431, "UpdateViewGroupInput!" ] } @@ -7709,7 +7718,7 @@ export default { 59, { "inputs": [ - 426, + 431, "[UpdateViewGroupInput!]!" ] } @@ -7718,7 +7727,7 @@ export default { 59, { "input": [ - 428, + 433, "DeleteViewGroupInput!" ] } @@ -7727,7 +7736,7 @@ export default { 59, { "input": [ - 429, + 434, "DestroyViewGroupInput!" ] } @@ -7736,7 +7745,7 @@ export default { 313, { "input": [ - 430, + 435, "UpdateMessageFolderInput!" ] } @@ -7745,7 +7754,7 @@ export default { 313, { "input": [ - 432, + 437, "UpdateMessageFoldersInput!" ] } @@ -7754,7 +7763,7 @@ export default { 305, { "input": [ - 433, + 438, "UpdateMessageChannelInput!" ] } @@ -7772,7 +7781,7 @@ export default { 300, { "input": [ - 435, + 440, "UpdateCalendarChannelInput!" ] } @@ -7781,7 +7790,7 @@ export default { 320, { "input": [ - 437, + 442, "CreateWebhookInput!" ] } @@ -7790,7 +7799,7 @@ export default { 320, { "input": [ - 438, + 443, "UpdateWebhookInput!" ] } @@ -7856,7 +7865,7 @@ export default { 288, { "input": [ - 440, + 445, "CreateSkillInput!" ] } @@ -7865,7 +7874,7 @@ export default { 288, { "input": [ - 441, + 446, "UpdateSkillInput!" ] } @@ -7923,7 +7932,7 @@ export default { 229, { "input": [ - 442, + 447, "GetAuthorizationUrlForSSOInput!" ] } @@ -8177,7 +8186,7 @@ export default { 188, { "input": [ - 443, + 448, "CreateApplicationRegistrationInput!" ] } @@ -8186,7 +8195,7 @@ export default { 7, { "input": [ - 444, + 449, "UpdateApplicationRegistrationInput!" ] } @@ -8213,7 +8222,7 @@ export default { 5, { "input": [ - 446, + 451, "CreateApplicationRegistrationVariableInput!" ] } @@ -8222,7 +8231,7 @@ export default { 5, { "input": [ - 447, + 452, "UpdateApplicationRegistrationVariableInput!" ] } @@ -8311,7 +8320,7 @@ export default { 6, { "input": [ - 449, + 454, "UpdateWorkspaceMemberSettingsInput!" ] } @@ -8345,7 +8354,7 @@ export default { 69, { "data": [ - 450, + 455, "ActivateWorkspaceInput!" ] } @@ -8354,7 +8363,7 @@ export default { 69, { "data": [ - 451, + 456, "UpdateWorkspaceInput!" ] } @@ -8381,7 +8390,7 @@ export default { 6, { "workspaceMigration": [ - 452, + 457, "WorkspaceMigrationInput!" ] } @@ -8416,7 +8425,7 @@ export default { 196, { "input": [ - 455, + 460, "SetupOIDCSsoInput!" ] } @@ -8425,7 +8434,7 @@ export default { 196, { "input": [ - 456, + 461, "SetupSAMLSsoInput!" ] } @@ -8434,7 +8443,7 @@ export default { 192, { "input": [ - 457, + 462, "DeleteSsoInput!" ] } @@ -8443,7 +8452,7 @@ export default { 193, { "input": [ - 458, + 463, "EditSsoInput!" ] } @@ -8474,7 +8483,7 @@ export default { 284, { "input": [ - 459, + 464, "SendEmailInput!" ] } @@ -8500,7 +8509,7 @@ export default { "String!" ], "connectionParameters": [ - 461, + 466, "EmailAccountConnectionParameters!" ], "id": [ @@ -8512,7 +8521,7 @@ export default { 160, { "input": [ - 463, + 468, "UpdateLabPublicFeatureFlagInput!" ] } @@ -8585,7 +8594,7 @@ export default { 71, { "input": [ - 464, + 469, "CreateOneAppTokenInput!" ] } @@ -8648,7 +8657,7 @@ export default { "String!" ], "fileFolder": [ - 466, + 471, "FileFolder!" ], "filePath": [ @@ -9015,6 +9024,103 @@ export default { 1 ] }, + "UpsertViewWidgetInput": { + "widgetId": [ + 3 + ], + "viewFields": [ + 359 + ], + "viewFilters": [ + 360 + ], + "viewFilterGroups": [ + 361 + ], + "viewSorts": [ + 362 + ], + "__typename": [ + 1 + ] + }, + "UpsertViewWidgetViewFieldInput": { + "viewFieldId": [ + 3 + ], + "fieldMetadataId": [ + 3 + ], + "isVisible": [ + 6 + ], + "position": [ + 11 + ], + "size": [ + 11 + ], + "__typename": [ + 1 + ] + }, + "UpsertViewWidgetViewFilterInput": { + "id": [ + 3 + ], + "fieldMetadataId": [ + 3 + ], + "operand": [ + 58 + ], + "value": [ + 15 + ], + "viewFilterGroupId": [ + 3 + ], + "positionInViewFilterGroup": [ + 11 + ], + "subFieldName": [ + 1 + ], + "__typename": [ + 1 + ] + }, + "UpsertViewWidgetViewFilterGroupInput": { + "id": [ + 3 + ], + "parentViewFilterGroupId": [ + 3 + ], + "logicalOperator": [ + 56 + ], + "positionInViewFilterGroup": [ + 11 + ], + "__typename": [ + 1 + ] + }, + "UpsertViewWidgetViewSortInput": { + "id": [ + 3 + ], + "fieldMetadataId": [ + 3 + ], + "direction": [ + 61 + ], + "__typename": [ + 1 + ] + }, "CreateViewSortInput": { "id": [ 3 @@ -9037,7 +9143,7 @@ export default { 3 ], "update": [ - 360 + 365 ], "__typename": [ 1 @@ -9072,7 +9178,7 @@ export default { 3 ], "update": [ - 364 + 369 ], "__typename": [ 1 @@ -9148,7 +9254,7 @@ export default { 3 ], "update": [ - 369 + 374 ], "__typename": [ 1 @@ -9212,10 +9318,10 @@ export default { 3 ], "groups": [ - 374 + 379 ], "fields": [ - 375 + 380 ], "__typename": [ 1 @@ -9235,7 +9341,7 @@ export default { 6 ], "fields": [ - 375 + 380 ], "__typename": [ 1 @@ -9404,7 +9510,7 @@ export default { 3 ], "tabs": [ - 388 + 393 ], "__typename": [ 1 @@ -9427,7 +9533,7 @@ export default { 82 ], "widgets": [ - 389 + 394 ], "__typename": [ 1 @@ -9450,7 +9556,7 @@ export default { 3 ], "gridPosition": [ - 390 + 395 ], "position": [ 15 @@ -9499,7 +9605,7 @@ export default { 3 ], "gridPosition": [ - 390 + 395 ], "position": [ 15 @@ -9525,7 +9631,7 @@ export default { 3 ], "gridPosition": [ - 390 + 395 ], "position": [ 15 @@ -9597,7 +9703,7 @@ export default { 3 ], "update": [ - 396 + 401 ], "__typename": [ 1 @@ -9757,7 +9863,7 @@ export default { 3 ], "update": [ - 401 + 406 ], "__typename": [ 1 @@ -9776,7 +9882,7 @@ export default { }, "CreateOneObjectInput": { "object": [ - 403 + 408 ], "__typename": [ 1 @@ -9836,7 +9942,7 @@ export default { }, "UpdateOneObjectInput": { "update": [ - 406 + 411 ], "id": [ 3 @@ -10008,7 +10114,7 @@ export default { }, "UpdateRoleInput": { "update": [ - 411 + 416 ], "id": [ 3 @@ -10063,7 +10169,7 @@ export default { 3 ], "objectPermissions": [ - 413 + 418 ], "__typename": [ 1 @@ -10105,7 +10211,7 @@ export default { 3 ], "fieldPermissions": [ - 416 + 421 ], "__typename": [ 1 @@ -10136,10 +10242,10 @@ export default { 3 ], "predicates": [ - 418 + 423 ], "predicateGroups": [ - 419 + 424 ], "__typename": [ 1 @@ -10199,7 +10305,7 @@ export default { }, "CreateOneFieldMetadataInput": { "field": [ - 421 + 426 ], "__typename": [ 1 @@ -10272,7 +10378,7 @@ export default { 3 ], "update": [ - 423 + 428 ], "__typename": [ 1 @@ -10364,7 +10470,7 @@ export default { 3 ], "update": [ - 427 + 432 ], "__typename": [ 1 @@ -10408,7 +10514,7 @@ export default { 3 ], "update": [ - 431 + 436 ], "__typename": [ 1 @@ -10427,7 +10533,7 @@ export default { 3 ], "update": [ - 431 + 436 ], "__typename": [ 1 @@ -10438,7 +10544,7 @@ export default { 3 ], "update": [ - 434 + 439 ], "__typename": [ 1 @@ -10475,7 +10581,7 @@ export default { 3 ], "update": [ - 436 + 441 ], "__typename": [ 1 @@ -10523,7 +10629,7 @@ export default { 3 ], "update": [ - 439 + 444 ], "__typename": [ 1 @@ -10628,7 +10734,7 @@ export default { 1 ], "update": [ - 445 + 450 ], "__typename": [ 1 @@ -10676,7 +10782,7 @@ export default { 1 ], "update": [ - 448 + 453 ], "__typename": [ 1 @@ -10791,7 +10897,7 @@ export default { }, "WorkspaceMigrationInput": { "actions": [ - 453 + 458 ], "__typename": [ 1 @@ -10799,7 +10905,7 @@ export default { }, "WorkspaceMigrationDeleteActionInput": { "type": [ - 454 + 459 ], "metadataName": [ 316 @@ -10894,7 +11000,7 @@ export default { 1 ], "files": [ - 460 + 465 ], "__typename": [ 1 @@ -10913,13 +11019,13 @@ export default { }, "EmailAccountConnectionParameters": { "IMAP": [ - 462 + 467 ], "SMTP": [ - 462 + 467 ], "CALDAV": [ - 462 + 467 ], "__typename": [ 1 @@ -10958,7 +11064,7 @@ export default { }, "CreateOneAppTokenInput": { "appToken": [ - 465 + 470 ], "__typename": [ 1 @@ -10987,7 +11093,7 @@ export default { 221, { "input": [ - 468, + 473, "LogicFunctionLogsInput!" ] } diff --git a/packages/twenty-front/src/generated-metadata/graphql.ts b/packages/twenty-front/src/generated-metadata/graphql.ts index 3b1af6735e..0809c25597 100644 --- a/packages/twenty-front/src/generated-metadata/graphql.ts +++ b/packages/twenty-front/src/generated-metadata/graphql.ts @@ -2555,6 +2555,7 @@ export type Mutation = { upsertObjectPermissions: Array; upsertPermissionFlags: Array; upsertRowLevelPermissionPredicates: UpsertRowLevelPermissionPredicatesResult; + upsertViewWidget: View; validateApprovedAccessDomain: ApprovedAccessDomain; verifyEmailAndGetLoginToken: VerifyEmailAndGetLoginToken; verifyEmailAndGetWorkspaceAgnosticToken: AvailableWorkspacesAndAccessTokens; @@ -3530,6 +3531,11 @@ export type MutationUpsertRowLevelPermissionPredicatesArgs = { }; +export type MutationUpsertViewWidgetArgs = { + input: UpsertViewWidgetInput; +}; + + export type MutationValidateApprovedAccessDomainArgs = { input: ValidateApprovedAccessDomainInput; }; @@ -5393,6 +5399,52 @@ export type UpsertRowLevelPermissionPredicatesResult = { predicates: Array; }; +export type UpsertViewWidgetInput = { + /** The view fields to upsert. */ + viewFields?: InputMaybe>; + /** The view filter groups to upsert. */ + viewFilterGroups?: InputMaybe>; + /** The view filters to upsert. */ + viewFilters?: InputMaybe>; + /** The view sorts to upsert. */ + viewSorts?: InputMaybe>; + /** The id of the view widget (page layout widget). */ + widgetId: Scalars['UUID']; +}; + +export type UpsertViewWidgetViewFieldInput = { + /** The field metadata id. Used to create a new view field when viewFieldId is not provided. */ + fieldMetadataId?: InputMaybe; + isVisible: Scalars['Boolean']; + position: Scalars['Float']; + size?: InputMaybe; + /** The id of an existing view field to update. */ + viewFieldId?: InputMaybe; +}; + +export type UpsertViewWidgetViewFilterGroupInput = { + id?: InputMaybe; + logicalOperator?: InputMaybe; + parentViewFilterGroupId?: InputMaybe; + positionInViewFilterGroup?: InputMaybe; +}; + +export type UpsertViewWidgetViewFilterInput = { + fieldMetadataId: Scalars['UUID']; + id?: InputMaybe; + operand?: InputMaybe; + positionInViewFilterGroup?: InputMaybe; + subFieldName?: InputMaybe; + value: Scalars['JSON']; + viewFilterGroupId?: InputMaybe; +}; + +export type UpsertViewWidgetViewSortInput = { + direction?: InputMaybe; + fieldMetadataId: Scalars['UUID']; + id?: InputMaybe; +}; + export type UsageAnalytics = { __typename?: 'UsageAnalytics'; periodEnd: Scalars['DateTime']; @@ -6700,6 +6752,13 @@ export type UpsertFieldsWidgetMutationVariables = Exact<{ export type UpsertFieldsWidgetMutation = { __typename?: 'Mutation', upsertFieldsWidget: { __typename?: 'View', id: string, name: string, objectMetadataId: string, type: ViewType, key?: ViewKey | null, icon: string, position: number, isCompact: boolean, openRecordIn: ViewOpenRecordIn, kanbanAggregateOperation?: AggregateOperations | null, kanbanAggregateOperationFieldMetadataId?: string | null, mainGroupByFieldMetadataId?: string | null, shouldHideEmptyGroups: boolean, anyFieldFilterValue?: string | null, calendarFieldMetadataId?: string | null, calendarLayout?: ViewCalendarLayout | null, visibility: ViewVisibility, createdByUserWorkspaceId?: string | null, viewFields: Array<{ __typename?: 'ViewField', id: string, fieldMetadataId: string, viewId: string, isVisible: boolean, position: number, size: number, aggregateOperation?: AggregateOperations | null, viewFieldGroupId?: string | null, isActive: boolean, createdAt: string, updatedAt: string, deletedAt?: string | null }>, viewFieldGroups: Array<{ __typename?: 'ViewFieldGroup', id: string, name: string, position: number, isVisible: boolean, viewId: string, isActive: boolean, createdAt: string, updatedAt: string, deletedAt?: string | null, viewFields: Array<{ __typename?: 'ViewField', id: string, fieldMetadataId: string, viewId: string, isVisible: boolean, position: number, size: number, aggregateOperation?: AggregateOperations | null, viewFieldGroupId?: string | null, isActive: boolean, createdAt: string, updatedAt: string, deletedAt?: string | null }> }>, viewFilters: Array<{ __typename?: 'ViewFilter', id: string, fieldMetadataId: string, operand: ViewFilterOperand, value: any, viewFilterGroupId?: string | null, positionInViewFilterGroup?: number | null, subFieldName?: string | null, viewId: string, createdAt: string, updatedAt: string, deletedAt?: string | null }>, viewFilterGroups: Array<{ __typename?: 'ViewFilterGroup', id: string, parentViewFilterGroupId?: string | null, logicalOperator: ViewFilterGroupLogicalOperator, positionInViewFilterGroup?: number | null, viewId: string }>, viewSorts: Array<{ __typename?: 'ViewSort', id: string, fieldMetadataId: string, direction: ViewSortDirection, viewId: string, createdAt: string, deletedAt?: string | null, updatedAt: string }>, viewGroups: Array<{ __typename?: 'ViewGroup', id: string, isVisible: boolean, fieldValue: string, position: number, viewId: string, createdAt: string, updatedAt: string, deletedAt?: string | null }> } }; +export type UpsertViewWidgetMutationVariables = Exact<{ + input: UpsertViewWidgetInput; +}>; + + +export type UpsertViewWidgetMutation = { __typename?: 'Mutation', upsertViewWidget: { __typename?: 'View', id: string, name: string, objectMetadataId: string, type: ViewType, key?: ViewKey | null, icon: string, position: number, isCompact: boolean, openRecordIn: ViewOpenRecordIn, kanbanAggregateOperation?: AggregateOperations | null, kanbanAggregateOperationFieldMetadataId?: string | null, mainGroupByFieldMetadataId?: string | null, shouldHideEmptyGroups: boolean, anyFieldFilterValue?: string | null, calendarFieldMetadataId?: string | null, calendarLayout?: ViewCalendarLayout | null, visibility: ViewVisibility, createdByUserWorkspaceId?: string | null, viewFields: Array<{ __typename?: 'ViewField', id: string, fieldMetadataId: string, viewId: string, isVisible: boolean, position: number, size: number, aggregateOperation?: AggregateOperations | null, viewFieldGroupId?: string | null, isActive: boolean, createdAt: string, updatedAt: string, deletedAt?: string | null }>, viewFieldGroups: Array<{ __typename?: 'ViewFieldGroup', id: string, name: string, position: number, isVisible: boolean, viewId: string, isActive: boolean, createdAt: string, updatedAt: string, deletedAt?: string | null, viewFields: Array<{ __typename?: 'ViewField', id: string, fieldMetadataId: string, viewId: string, isVisible: boolean, position: number, size: number, aggregateOperation?: AggregateOperations | null, viewFieldGroupId?: string | null, isActive: boolean, createdAt: string, updatedAt: string, deletedAt?: string | null }> }>, viewFilters: Array<{ __typename?: 'ViewFilter', id: string, fieldMetadataId: string, operand: ViewFilterOperand, value: any, viewFilterGroupId?: string | null, positionInViewFilterGroup?: number | null, subFieldName?: string | null, viewId: string, createdAt: string, updatedAt: string, deletedAt?: string | null }>, viewFilterGroups: Array<{ __typename?: 'ViewFilterGroup', id: string, parentViewFilterGroupId?: string | null, logicalOperator: ViewFilterGroupLogicalOperator, positionInViewFilterGroup?: number | null, viewId: string }>, viewSorts: Array<{ __typename?: 'ViewSort', id: string, fieldMetadataId: string, direction: ViewSortDirection, viewId: string, createdAt: string, deletedAt?: string | null, updatedAt: string }>, viewGroups: Array<{ __typename?: 'ViewGroup', id: string, isVisible: boolean, fieldValue: string, position: number, viewId: string, createdAt: string, updatedAt: string, deletedAt?: string | null }> } }; + export type FindAllPageLayoutsQueryVariables = Exact<{ [key: string]: never; }>; @@ -7919,6 +7978,7 @@ export const ResetPageLayoutToDefaultDocument = {"kind":"Document","definitions" export const ResetPageLayoutWidgetToDefaultDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"ResetPageLayoutWidgetToDefault"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"resetPageLayoutWidgetToDefault"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PageLayoutWidgetFragment"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"PageLayoutWidgetFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PageLayoutWidget"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"applicationId"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"type"}},{"kind":"Field","name":{"kind":"Name","value":"objectMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"isActive"}},{"kind":"Field","name":{"kind":"Name","value":"deletedAt"}},{"kind":"Field","name":{"kind":"Name","value":"conditionalDisplay"}},{"kind":"Field","name":{"kind":"Name","value":"conditionalAvailabilityExpression"}},{"kind":"Field","name":{"kind":"Name","value":"gridPosition"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"column"}},{"kind":"Field","name":{"kind":"Name","value":"columnSpan"}},{"kind":"Field","name":{"kind":"Name","value":"row"}},{"kind":"Field","name":{"kind":"Name","value":"rowSpan"}}]}},{"kind":"Field","name":{"kind":"Name","value":"position"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PageLayoutWidgetGridPosition"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"layoutMode"}},{"kind":"Field","name":{"kind":"Name","value":"row"}},{"kind":"Field","name":{"kind":"Name","value":"column"}},{"kind":"Field","name":{"kind":"Name","value":"rowSpan"}},{"kind":"Field","name":{"kind":"Name","value":"columnSpan"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PageLayoutWidgetVerticalListPosition"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"layoutMode"}},{"kind":"Field","name":{"kind":"Name","value":"index"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PageLayoutWidgetCanvasPosition"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"layoutMode"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"configuration"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"BarChartConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}},{"kind":"Field","name":{"kind":"Name","value":"aggregateFieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"aggregateOperation"}},{"kind":"Field","name":{"kind":"Name","value":"primaryAxisGroupByFieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"primaryAxisGroupBySubFieldName"}},{"kind":"Field","name":{"kind":"Name","value":"primaryAxisDateGranularity"}},{"kind":"Field","name":{"kind":"Name","value":"primaryAxisOrderBy"}},{"kind":"Field","name":{"kind":"Name","value":"primaryAxisManualSortOrder"}},{"kind":"Field","name":{"kind":"Name","value":"secondaryAxisGroupByFieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"secondaryAxisGroupBySubFieldName"}},{"kind":"Field","name":{"kind":"Name","value":"secondaryAxisGroupByDateGranularity"}},{"kind":"Field","name":{"kind":"Name","value":"secondaryAxisOrderBy"}},{"kind":"Field","name":{"kind":"Name","value":"secondaryAxisManualSortOrder"}},{"kind":"Field","name":{"kind":"Name","value":"omitNullValues"}},{"kind":"Field","name":{"kind":"Name","value":"axisNameDisplay"}},{"kind":"Field","name":{"kind":"Name","value":"displayDataLabel"}},{"kind":"Field","name":{"kind":"Name","value":"displayLegend"}},{"kind":"Field","name":{"kind":"Name","value":"rangeMin"}},{"kind":"Field","name":{"kind":"Name","value":"rangeMax"}},{"kind":"Field","name":{"kind":"Name","value":"color"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"filter"}},{"kind":"Field","name":{"kind":"Name","value":"groupMode"}},{"kind":"Field","name":{"kind":"Name","value":"layout"}},{"kind":"Field","name":{"kind":"Name","value":"isCumulative"}},{"kind":"Field","name":{"kind":"Name","value":"splitMultiValueFields"}},{"kind":"Field","name":{"kind":"Name","value":"timezone"}},{"kind":"Field","name":{"kind":"Name","value":"firstDayOfTheWeek"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"LineChartConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}},{"kind":"Field","name":{"kind":"Name","value":"aggregateFieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"aggregateOperation"}},{"kind":"Field","name":{"kind":"Name","value":"primaryAxisGroupByFieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"primaryAxisGroupBySubFieldName"}},{"kind":"Field","name":{"kind":"Name","value":"primaryAxisDateGranularity"}},{"kind":"Field","name":{"kind":"Name","value":"primaryAxisOrderBy"}},{"kind":"Field","name":{"kind":"Name","value":"primaryAxisManualSortOrder"}},{"kind":"Field","name":{"kind":"Name","value":"secondaryAxisGroupByFieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"secondaryAxisGroupBySubFieldName"}},{"kind":"Field","name":{"kind":"Name","value":"secondaryAxisGroupByDateGranularity"}},{"kind":"Field","name":{"kind":"Name","value":"secondaryAxisOrderBy"}},{"kind":"Field","name":{"kind":"Name","value":"secondaryAxisManualSortOrder"}},{"kind":"Field","name":{"kind":"Name","value":"omitNullValues"}},{"kind":"Field","name":{"kind":"Name","value":"axisNameDisplay"}},{"kind":"Field","name":{"kind":"Name","value":"displayDataLabel"}},{"kind":"Field","name":{"kind":"Name","value":"displayLegend"}},{"kind":"Field","name":{"kind":"Name","value":"rangeMin"}},{"kind":"Field","name":{"kind":"Name","value":"rangeMax"}},{"kind":"Field","name":{"kind":"Name","value":"color"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"filter"}},{"kind":"Field","name":{"kind":"Name","value":"isStacked"}},{"kind":"Field","name":{"kind":"Name","value":"isCumulative"}},{"kind":"Field","name":{"kind":"Name","value":"splitMultiValueFields"}},{"kind":"Field","name":{"kind":"Name","value":"timezone"}},{"kind":"Field","name":{"kind":"Name","value":"firstDayOfTheWeek"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PieChartConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}},{"kind":"Field","name":{"kind":"Name","value":"groupByFieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"aggregateFieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"aggregateOperation"}},{"kind":"Field","name":{"kind":"Name","value":"groupBySubFieldName"}},{"kind":"Field","name":{"kind":"Name","value":"dateGranularity"}},{"kind":"Field","name":{"kind":"Name","value":"orderBy"}},{"kind":"Field","name":{"kind":"Name","value":"manualSortOrder"}},{"kind":"Field","name":{"kind":"Name","value":"displayDataLabel"}},{"kind":"Field","name":{"kind":"Name","value":"showCenterMetric"}},{"kind":"Field","name":{"kind":"Name","value":"displayLegend"}},{"kind":"Field","name":{"kind":"Name","value":"hideEmptyCategory"}},{"kind":"Field","name":{"kind":"Name","value":"splitMultiValueFields"}},{"kind":"Field","name":{"kind":"Name","value":"color"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"filter"}},{"kind":"Field","name":{"kind":"Name","value":"timezone"}},{"kind":"Field","name":{"kind":"Name","value":"firstDayOfTheWeek"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"AggregateChartConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}},{"kind":"Field","name":{"kind":"Name","value":"aggregateFieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"aggregateOperation"}},{"kind":"Field","name":{"kind":"Name","value":"label"}},{"kind":"Field","name":{"kind":"Name","value":"displayDataLabel"}},{"kind":"Field","name":{"kind":"Name","value":"format"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"filter"}},{"kind":"Field","name":{"kind":"Name","value":"prefix"}},{"kind":"Field","name":{"kind":"Name","value":"suffix"}},{"kind":"Field","name":{"kind":"Name","value":"timezone"}},{"kind":"Field","name":{"kind":"Name","value":"firstDayOfTheWeek"}},{"kind":"Field","name":{"kind":"Name","value":"ratioAggregateConfig"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"fieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"optionValue"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"GaugeChartConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}},{"kind":"Field","name":{"kind":"Name","value":"aggregateFieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"aggregateOperation"}},{"kind":"Field","name":{"kind":"Name","value":"displayDataLabel"}},{"kind":"Field","name":{"kind":"Name","value":"color"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"filter"}},{"kind":"Field","name":{"kind":"Name","value":"timezone"}},{"kind":"Field","name":{"kind":"Name","value":"firstDayOfTheWeek"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"IframeConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}},{"kind":"Field","name":{"kind":"Name","value":"url"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"StandaloneRichTextConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}},{"kind":"Field","name":{"kind":"Name","value":"body"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"blocknote"}},{"kind":"Field","name":{"kind":"Name","value":"markdown"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"CalendarConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"EmailsConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"EmailThreadConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"FieldConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}},{"kind":"Field","name":{"kind":"Name","value":"fieldDisplayMode"}},{"kind":"Field","name":{"kind":"Name","value":"fieldMetadataId"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"FieldRichTextConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"FieldsConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}},{"kind":"Field","name":{"kind":"Name","value":"viewId"}},{"kind":"Field","name":{"kind":"Name","value":"newFieldDefaultVisibility"}},{"kind":"Field","name":{"kind":"Name","value":"shouldAllowUserToSeeHiddenFields"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"FilesConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"NotesConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TasksConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TimelineConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ViewConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"RecordTableConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}},{"kind":"Field","name":{"kind":"Name","value":"viewId"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"WorkflowConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"WorkflowRunConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"WorkflowVersionConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"FrontComponentConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}},{"kind":"Field","name":{"kind":"Name","value":"frontComponentId"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"pageLayoutTabId"}}]}}]} as unknown as DocumentNode; export const UpdatePageLayoutWithTabsAndWidgetsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpdatePageLayoutWithTabsAndWidgets"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UpdatePageLayoutWithTabsInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"updatePageLayoutWithTabsAndWidgets"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}},{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"type"}},{"kind":"Field","name":{"kind":"Name","value":"objectMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"defaultTabToFocusOnMobileAndSidePanelId"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"deletedAt"}},{"kind":"Field","name":{"kind":"Name","value":"tabs"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PageLayoutTabFragment"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"PageLayoutWidgetFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PageLayoutWidget"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"applicationId"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"type"}},{"kind":"Field","name":{"kind":"Name","value":"objectMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"isActive"}},{"kind":"Field","name":{"kind":"Name","value":"deletedAt"}},{"kind":"Field","name":{"kind":"Name","value":"conditionalDisplay"}},{"kind":"Field","name":{"kind":"Name","value":"conditionalAvailabilityExpression"}},{"kind":"Field","name":{"kind":"Name","value":"gridPosition"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"column"}},{"kind":"Field","name":{"kind":"Name","value":"columnSpan"}},{"kind":"Field","name":{"kind":"Name","value":"row"}},{"kind":"Field","name":{"kind":"Name","value":"rowSpan"}}]}},{"kind":"Field","name":{"kind":"Name","value":"position"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PageLayoutWidgetGridPosition"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"layoutMode"}},{"kind":"Field","name":{"kind":"Name","value":"row"}},{"kind":"Field","name":{"kind":"Name","value":"column"}},{"kind":"Field","name":{"kind":"Name","value":"rowSpan"}},{"kind":"Field","name":{"kind":"Name","value":"columnSpan"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PageLayoutWidgetVerticalListPosition"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"layoutMode"}},{"kind":"Field","name":{"kind":"Name","value":"index"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PageLayoutWidgetCanvasPosition"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"layoutMode"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"configuration"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"BarChartConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}},{"kind":"Field","name":{"kind":"Name","value":"aggregateFieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"aggregateOperation"}},{"kind":"Field","name":{"kind":"Name","value":"primaryAxisGroupByFieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"primaryAxisGroupBySubFieldName"}},{"kind":"Field","name":{"kind":"Name","value":"primaryAxisDateGranularity"}},{"kind":"Field","name":{"kind":"Name","value":"primaryAxisOrderBy"}},{"kind":"Field","name":{"kind":"Name","value":"primaryAxisManualSortOrder"}},{"kind":"Field","name":{"kind":"Name","value":"secondaryAxisGroupByFieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"secondaryAxisGroupBySubFieldName"}},{"kind":"Field","name":{"kind":"Name","value":"secondaryAxisGroupByDateGranularity"}},{"kind":"Field","name":{"kind":"Name","value":"secondaryAxisOrderBy"}},{"kind":"Field","name":{"kind":"Name","value":"secondaryAxisManualSortOrder"}},{"kind":"Field","name":{"kind":"Name","value":"omitNullValues"}},{"kind":"Field","name":{"kind":"Name","value":"axisNameDisplay"}},{"kind":"Field","name":{"kind":"Name","value":"displayDataLabel"}},{"kind":"Field","name":{"kind":"Name","value":"displayLegend"}},{"kind":"Field","name":{"kind":"Name","value":"rangeMin"}},{"kind":"Field","name":{"kind":"Name","value":"rangeMax"}},{"kind":"Field","name":{"kind":"Name","value":"color"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"filter"}},{"kind":"Field","name":{"kind":"Name","value":"groupMode"}},{"kind":"Field","name":{"kind":"Name","value":"layout"}},{"kind":"Field","name":{"kind":"Name","value":"isCumulative"}},{"kind":"Field","name":{"kind":"Name","value":"splitMultiValueFields"}},{"kind":"Field","name":{"kind":"Name","value":"timezone"}},{"kind":"Field","name":{"kind":"Name","value":"firstDayOfTheWeek"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"LineChartConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}},{"kind":"Field","name":{"kind":"Name","value":"aggregateFieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"aggregateOperation"}},{"kind":"Field","name":{"kind":"Name","value":"primaryAxisGroupByFieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"primaryAxisGroupBySubFieldName"}},{"kind":"Field","name":{"kind":"Name","value":"primaryAxisDateGranularity"}},{"kind":"Field","name":{"kind":"Name","value":"primaryAxisOrderBy"}},{"kind":"Field","name":{"kind":"Name","value":"primaryAxisManualSortOrder"}},{"kind":"Field","name":{"kind":"Name","value":"secondaryAxisGroupByFieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"secondaryAxisGroupBySubFieldName"}},{"kind":"Field","name":{"kind":"Name","value":"secondaryAxisGroupByDateGranularity"}},{"kind":"Field","name":{"kind":"Name","value":"secondaryAxisOrderBy"}},{"kind":"Field","name":{"kind":"Name","value":"secondaryAxisManualSortOrder"}},{"kind":"Field","name":{"kind":"Name","value":"omitNullValues"}},{"kind":"Field","name":{"kind":"Name","value":"axisNameDisplay"}},{"kind":"Field","name":{"kind":"Name","value":"displayDataLabel"}},{"kind":"Field","name":{"kind":"Name","value":"displayLegend"}},{"kind":"Field","name":{"kind":"Name","value":"rangeMin"}},{"kind":"Field","name":{"kind":"Name","value":"rangeMax"}},{"kind":"Field","name":{"kind":"Name","value":"color"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"filter"}},{"kind":"Field","name":{"kind":"Name","value":"isStacked"}},{"kind":"Field","name":{"kind":"Name","value":"isCumulative"}},{"kind":"Field","name":{"kind":"Name","value":"splitMultiValueFields"}},{"kind":"Field","name":{"kind":"Name","value":"timezone"}},{"kind":"Field","name":{"kind":"Name","value":"firstDayOfTheWeek"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PieChartConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}},{"kind":"Field","name":{"kind":"Name","value":"groupByFieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"aggregateFieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"aggregateOperation"}},{"kind":"Field","name":{"kind":"Name","value":"groupBySubFieldName"}},{"kind":"Field","name":{"kind":"Name","value":"dateGranularity"}},{"kind":"Field","name":{"kind":"Name","value":"orderBy"}},{"kind":"Field","name":{"kind":"Name","value":"manualSortOrder"}},{"kind":"Field","name":{"kind":"Name","value":"displayDataLabel"}},{"kind":"Field","name":{"kind":"Name","value":"showCenterMetric"}},{"kind":"Field","name":{"kind":"Name","value":"displayLegend"}},{"kind":"Field","name":{"kind":"Name","value":"hideEmptyCategory"}},{"kind":"Field","name":{"kind":"Name","value":"splitMultiValueFields"}},{"kind":"Field","name":{"kind":"Name","value":"color"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"filter"}},{"kind":"Field","name":{"kind":"Name","value":"timezone"}},{"kind":"Field","name":{"kind":"Name","value":"firstDayOfTheWeek"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"AggregateChartConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}},{"kind":"Field","name":{"kind":"Name","value":"aggregateFieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"aggregateOperation"}},{"kind":"Field","name":{"kind":"Name","value":"label"}},{"kind":"Field","name":{"kind":"Name","value":"displayDataLabel"}},{"kind":"Field","name":{"kind":"Name","value":"format"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"filter"}},{"kind":"Field","name":{"kind":"Name","value":"prefix"}},{"kind":"Field","name":{"kind":"Name","value":"suffix"}},{"kind":"Field","name":{"kind":"Name","value":"timezone"}},{"kind":"Field","name":{"kind":"Name","value":"firstDayOfTheWeek"}},{"kind":"Field","name":{"kind":"Name","value":"ratioAggregateConfig"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"fieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"optionValue"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"GaugeChartConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}},{"kind":"Field","name":{"kind":"Name","value":"aggregateFieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"aggregateOperation"}},{"kind":"Field","name":{"kind":"Name","value":"displayDataLabel"}},{"kind":"Field","name":{"kind":"Name","value":"color"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"filter"}},{"kind":"Field","name":{"kind":"Name","value":"timezone"}},{"kind":"Field","name":{"kind":"Name","value":"firstDayOfTheWeek"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"IframeConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}},{"kind":"Field","name":{"kind":"Name","value":"url"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"StandaloneRichTextConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}},{"kind":"Field","name":{"kind":"Name","value":"body"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"blocknote"}},{"kind":"Field","name":{"kind":"Name","value":"markdown"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"CalendarConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"EmailsConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"EmailThreadConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"FieldConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}},{"kind":"Field","name":{"kind":"Name","value":"fieldDisplayMode"}},{"kind":"Field","name":{"kind":"Name","value":"fieldMetadataId"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"FieldRichTextConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"FieldsConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}},{"kind":"Field","name":{"kind":"Name","value":"viewId"}},{"kind":"Field","name":{"kind":"Name","value":"newFieldDefaultVisibility"}},{"kind":"Field","name":{"kind":"Name","value":"shouldAllowUserToSeeHiddenFields"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"FilesConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"NotesConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TasksConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TimelineConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ViewConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"RecordTableConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}},{"kind":"Field","name":{"kind":"Name","value":"viewId"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"WorkflowConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"WorkflowRunConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"WorkflowVersionConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"FrontComponentConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}},{"kind":"Field","name":{"kind":"Name","value":"frontComponentId"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"pageLayoutTabId"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"PageLayoutTabFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PageLayoutTab"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"applicationId"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"position"}},{"kind":"Field","name":{"kind":"Name","value":"layoutMode"}},{"kind":"Field","name":{"kind":"Name","value":"widgets"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PageLayoutWidgetFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"pageLayoutId"}},{"kind":"Field","name":{"kind":"Name","value":"isActive"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]} as unknown as DocumentNode; export const UpsertFieldsWidgetDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpsertFieldsWidget"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UpsertFieldsWidgetInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"upsertFieldsWidget"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ViewFragment"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ViewFieldFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ViewField"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"fieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"viewId"}},{"kind":"Field","name":{"kind":"Name","value":"isVisible"}},{"kind":"Field","name":{"kind":"Name","value":"position"}},{"kind":"Field","name":{"kind":"Name","value":"size"}},{"kind":"Field","name":{"kind":"Name","value":"aggregateOperation"}},{"kind":"Field","name":{"kind":"Name","value":"viewFieldGroupId"}},{"kind":"Field","name":{"kind":"Name","value":"isActive"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"deletedAt"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ViewFieldGroupFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ViewFieldGroup"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"position"}},{"kind":"Field","name":{"kind":"Name","value":"isVisible"}},{"kind":"Field","name":{"kind":"Name","value":"viewId"}},{"kind":"Field","name":{"kind":"Name","value":"isActive"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"deletedAt"}},{"kind":"Field","name":{"kind":"Name","value":"viewFields"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ViewFieldFragment"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ViewFilterFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ViewFilter"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"fieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"operand"}},{"kind":"Field","name":{"kind":"Name","value":"value"}},{"kind":"Field","name":{"kind":"Name","value":"viewFilterGroupId"}},{"kind":"Field","name":{"kind":"Name","value":"positionInViewFilterGroup"}},{"kind":"Field","name":{"kind":"Name","value":"subFieldName"}},{"kind":"Field","name":{"kind":"Name","value":"viewId"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"deletedAt"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ViewFilterGroupFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ViewFilterGroup"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"parentViewFilterGroupId"}},{"kind":"Field","name":{"kind":"Name","value":"logicalOperator"}},{"kind":"Field","name":{"kind":"Name","value":"positionInViewFilterGroup"}},{"kind":"Field","name":{"kind":"Name","value":"viewId"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ViewSortFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ViewSort"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"fieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"direction"}},{"kind":"Field","name":{"kind":"Name","value":"viewId"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"deletedAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ViewGroupFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ViewGroup"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"isVisible"}},{"kind":"Field","name":{"kind":"Name","value":"fieldValue"}},{"kind":"Field","name":{"kind":"Name","value":"position"}},{"kind":"Field","name":{"kind":"Name","value":"viewId"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"deletedAt"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ViewFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"View"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"objectMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"type"}},{"kind":"Field","name":{"kind":"Name","value":"key"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"position"}},{"kind":"Field","name":{"kind":"Name","value":"isCompact"}},{"kind":"Field","name":{"kind":"Name","value":"openRecordIn"}},{"kind":"Field","name":{"kind":"Name","value":"kanbanAggregateOperation"}},{"kind":"Field","name":{"kind":"Name","value":"kanbanAggregateOperationFieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"mainGroupByFieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"shouldHideEmptyGroups"}},{"kind":"Field","name":{"kind":"Name","value":"anyFieldFilterValue"}},{"kind":"Field","name":{"kind":"Name","value":"calendarFieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"calendarLayout"}},{"kind":"Field","name":{"kind":"Name","value":"visibility"}},{"kind":"Field","name":{"kind":"Name","value":"createdByUserWorkspaceId"}},{"kind":"Field","name":{"kind":"Name","value":"viewFields"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ViewFieldFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"viewFieldGroups"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ViewFieldGroupFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"viewFilters"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ViewFilterFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"viewFilterGroups"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ViewFilterGroupFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"viewSorts"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ViewSortFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"viewGroups"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ViewGroupFragment"}}]}}]}}]} as unknown as DocumentNode; +export const UpsertViewWidgetDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpsertViewWidget"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UpsertViewWidgetInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"upsertViewWidget"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ViewFragment"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ViewFieldFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ViewField"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"fieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"viewId"}},{"kind":"Field","name":{"kind":"Name","value":"isVisible"}},{"kind":"Field","name":{"kind":"Name","value":"position"}},{"kind":"Field","name":{"kind":"Name","value":"size"}},{"kind":"Field","name":{"kind":"Name","value":"aggregateOperation"}},{"kind":"Field","name":{"kind":"Name","value":"viewFieldGroupId"}},{"kind":"Field","name":{"kind":"Name","value":"isActive"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"deletedAt"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ViewFieldGroupFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ViewFieldGroup"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"position"}},{"kind":"Field","name":{"kind":"Name","value":"isVisible"}},{"kind":"Field","name":{"kind":"Name","value":"viewId"}},{"kind":"Field","name":{"kind":"Name","value":"isActive"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"deletedAt"}},{"kind":"Field","name":{"kind":"Name","value":"viewFields"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ViewFieldFragment"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ViewFilterFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ViewFilter"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"fieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"operand"}},{"kind":"Field","name":{"kind":"Name","value":"value"}},{"kind":"Field","name":{"kind":"Name","value":"viewFilterGroupId"}},{"kind":"Field","name":{"kind":"Name","value":"positionInViewFilterGroup"}},{"kind":"Field","name":{"kind":"Name","value":"subFieldName"}},{"kind":"Field","name":{"kind":"Name","value":"viewId"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"deletedAt"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ViewFilterGroupFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ViewFilterGroup"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"parentViewFilterGroupId"}},{"kind":"Field","name":{"kind":"Name","value":"logicalOperator"}},{"kind":"Field","name":{"kind":"Name","value":"positionInViewFilterGroup"}},{"kind":"Field","name":{"kind":"Name","value":"viewId"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ViewSortFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ViewSort"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"fieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"direction"}},{"kind":"Field","name":{"kind":"Name","value":"viewId"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"deletedAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ViewGroupFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ViewGroup"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"isVisible"}},{"kind":"Field","name":{"kind":"Name","value":"fieldValue"}},{"kind":"Field","name":{"kind":"Name","value":"position"}},{"kind":"Field","name":{"kind":"Name","value":"viewId"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"deletedAt"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ViewFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"View"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"objectMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"type"}},{"kind":"Field","name":{"kind":"Name","value":"key"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"position"}},{"kind":"Field","name":{"kind":"Name","value":"isCompact"}},{"kind":"Field","name":{"kind":"Name","value":"openRecordIn"}},{"kind":"Field","name":{"kind":"Name","value":"kanbanAggregateOperation"}},{"kind":"Field","name":{"kind":"Name","value":"kanbanAggregateOperationFieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"mainGroupByFieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"shouldHideEmptyGroups"}},{"kind":"Field","name":{"kind":"Name","value":"anyFieldFilterValue"}},{"kind":"Field","name":{"kind":"Name","value":"calendarFieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"calendarLayout"}},{"kind":"Field","name":{"kind":"Name","value":"visibility"}},{"kind":"Field","name":{"kind":"Name","value":"createdByUserWorkspaceId"}},{"kind":"Field","name":{"kind":"Name","value":"viewFields"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ViewFieldFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"viewFieldGroups"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ViewFieldGroupFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"viewFilters"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ViewFilterFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"viewFilterGroups"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ViewFilterGroupFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"viewSorts"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ViewSortFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"viewGroups"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ViewGroupFragment"}}]}}]}}]} as unknown as DocumentNode; export const FindAllPageLayoutsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"FindAllPageLayouts"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"getPageLayouts"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PageLayoutFragment"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"PageLayoutWidgetFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PageLayoutWidget"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"applicationId"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"type"}},{"kind":"Field","name":{"kind":"Name","value":"objectMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"isActive"}},{"kind":"Field","name":{"kind":"Name","value":"deletedAt"}},{"kind":"Field","name":{"kind":"Name","value":"conditionalDisplay"}},{"kind":"Field","name":{"kind":"Name","value":"conditionalAvailabilityExpression"}},{"kind":"Field","name":{"kind":"Name","value":"gridPosition"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"column"}},{"kind":"Field","name":{"kind":"Name","value":"columnSpan"}},{"kind":"Field","name":{"kind":"Name","value":"row"}},{"kind":"Field","name":{"kind":"Name","value":"rowSpan"}}]}},{"kind":"Field","name":{"kind":"Name","value":"position"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PageLayoutWidgetGridPosition"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"layoutMode"}},{"kind":"Field","name":{"kind":"Name","value":"row"}},{"kind":"Field","name":{"kind":"Name","value":"column"}},{"kind":"Field","name":{"kind":"Name","value":"rowSpan"}},{"kind":"Field","name":{"kind":"Name","value":"columnSpan"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PageLayoutWidgetVerticalListPosition"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"layoutMode"}},{"kind":"Field","name":{"kind":"Name","value":"index"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PageLayoutWidgetCanvasPosition"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"layoutMode"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"configuration"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"BarChartConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}},{"kind":"Field","name":{"kind":"Name","value":"aggregateFieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"aggregateOperation"}},{"kind":"Field","name":{"kind":"Name","value":"primaryAxisGroupByFieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"primaryAxisGroupBySubFieldName"}},{"kind":"Field","name":{"kind":"Name","value":"primaryAxisDateGranularity"}},{"kind":"Field","name":{"kind":"Name","value":"primaryAxisOrderBy"}},{"kind":"Field","name":{"kind":"Name","value":"primaryAxisManualSortOrder"}},{"kind":"Field","name":{"kind":"Name","value":"secondaryAxisGroupByFieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"secondaryAxisGroupBySubFieldName"}},{"kind":"Field","name":{"kind":"Name","value":"secondaryAxisGroupByDateGranularity"}},{"kind":"Field","name":{"kind":"Name","value":"secondaryAxisOrderBy"}},{"kind":"Field","name":{"kind":"Name","value":"secondaryAxisManualSortOrder"}},{"kind":"Field","name":{"kind":"Name","value":"omitNullValues"}},{"kind":"Field","name":{"kind":"Name","value":"axisNameDisplay"}},{"kind":"Field","name":{"kind":"Name","value":"displayDataLabel"}},{"kind":"Field","name":{"kind":"Name","value":"displayLegend"}},{"kind":"Field","name":{"kind":"Name","value":"rangeMin"}},{"kind":"Field","name":{"kind":"Name","value":"rangeMax"}},{"kind":"Field","name":{"kind":"Name","value":"color"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"filter"}},{"kind":"Field","name":{"kind":"Name","value":"groupMode"}},{"kind":"Field","name":{"kind":"Name","value":"layout"}},{"kind":"Field","name":{"kind":"Name","value":"isCumulative"}},{"kind":"Field","name":{"kind":"Name","value":"splitMultiValueFields"}},{"kind":"Field","name":{"kind":"Name","value":"timezone"}},{"kind":"Field","name":{"kind":"Name","value":"firstDayOfTheWeek"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"LineChartConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}},{"kind":"Field","name":{"kind":"Name","value":"aggregateFieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"aggregateOperation"}},{"kind":"Field","name":{"kind":"Name","value":"primaryAxisGroupByFieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"primaryAxisGroupBySubFieldName"}},{"kind":"Field","name":{"kind":"Name","value":"primaryAxisDateGranularity"}},{"kind":"Field","name":{"kind":"Name","value":"primaryAxisOrderBy"}},{"kind":"Field","name":{"kind":"Name","value":"primaryAxisManualSortOrder"}},{"kind":"Field","name":{"kind":"Name","value":"secondaryAxisGroupByFieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"secondaryAxisGroupBySubFieldName"}},{"kind":"Field","name":{"kind":"Name","value":"secondaryAxisGroupByDateGranularity"}},{"kind":"Field","name":{"kind":"Name","value":"secondaryAxisOrderBy"}},{"kind":"Field","name":{"kind":"Name","value":"secondaryAxisManualSortOrder"}},{"kind":"Field","name":{"kind":"Name","value":"omitNullValues"}},{"kind":"Field","name":{"kind":"Name","value":"axisNameDisplay"}},{"kind":"Field","name":{"kind":"Name","value":"displayDataLabel"}},{"kind":"Field","name":{"kind":"Name","value":"displayLegend"}},{"kind":"Field","name":{"kind":"Name","value":"rangeMin"}},{"kind":"Field","name":{"kind":"Name","value":"rangeMax"}},{"kind":"Field","name":{"kind":"Name","value":"color"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"filter"}},{"kind":"Field","name":{"kind":"Name","value":"isStacked"}},{"kind":"Field","name":{"kind":"Name","value":"isCumulative"}},{"kind":"Field","name":{"kind":"Name","value":"splitMultiValueFields"}},{"kind":"Field","name":{"kind":"Name","value":"timezone"}},{"kind":"Field","name":{"kind":"Name","value":"firstDayOfTheWeek"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PieChartConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}},{"kind":"Field","name":{"kind":"Name","value":"groupByFieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"aggregateFieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"aggregateOperation"}},{"kind":"Field","name":{"kind":"Name","value":"groupBySubFieldName"}},{"kind":"Field","name":{"kind":"Name","value":"dateGranularity"}},{"kind":"Field","name":{"kind":"Name","value":"orderBy"}},{"kind":"Field","name":{"kind":"Name","value":"manualSortOrder"}},{"kind":"Field","name":{"kind":"Name","value":"displayDataLabel"}},{"kind":"Field","name":{"kind":"Name","value":"showCenterMetric"}},{"kind":"Field","name":{"kind":"Name","value":"displayLegend"}},{"kind":"Field","name":{"kind":"Name","value":"hideEmptyCategory"}},{"kind":"Field","name":{"kind":"Name","value":"splitMultiValueFields"}},{"kind":"Field","name":{"kind":"Name","value":"color"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"filter"}},{"kind":"Field","name":{"kind":"Name","value":"timezone"}},{"kind":"Field","name":{"kind":"Name","value":"firstDayOfTheWeek"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"AggregateChartConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}},{"kind":"Field","name":{"kind":"Name","value":"aggregateFieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"aggregateOperation"}},{"kind":"Field","name":{"kind":"Name","value":"label"}},{"kind":"Field","name":{"kind":"Name","value":"displayDataLabel"}},{"kind":"Field","name":{"kind":"Name","value":"format"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"filter"}},{"kind":"Field","name":{"kind":"Name","value":"prefix"}},{"kind":"Field","name":{"kind":"Name","value":"suffix"}},{"kind":"Field","name":{"kind":"Name","value":"timezone"}},{"kind":"Field","name":{"kind":"Name","value":"firstDayOfTheWeek"}},{"kind":"Field","name":{"kind":"Name","value":"ratioAggregateConfig"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"fieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"optionValue"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"GaugeChartConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}},{"kind":"Field","name":{"kind":"Name","value":"aggregateFieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"aggregateOperation"}},{"kind":"Field","name":{"kind":"Name","value":"displayDataLabel"}},{"kind":"Field","name":{"kind":"Name","value":"color"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"filter"}},{"kind":"Field","name":{"kind":"Name","value":"timezone"}},{"kind":"Field","name":{"kind":"Name","value":"firstDayOfTheWeek"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"IframeConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}},{"kind":"Field","name":{"kind":"Name","value":"url"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"StandaloneRichTextConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}},{"kind":"Field","name":{"kind":"Name","value":"body"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"blocknote"}},{"kind":"Field","name":{"kind":"Name","value":"markdown"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"CalendarConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"EmailsConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"EmailThreadConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"FieldConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}},{"kind":"Field","name":{"kind":"Name","value":"fieldDisplayMode"}},{"kind":"Field","name":{"kind":"Name","value":"fieldMetadataId"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"FieldRichTextConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"FieldsConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}},{"kind":"Field","name":{"kind":"Name","value":"viewId"}},{"kind":"Field","name":{"kind":"Name","value":"newFieldDefaultVisibility"}},{"kind":"Field","name":{"kind":"Name","value":"shouldAllowUserToSeeHiddenFields"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"FilesConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"NotesConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TasksConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TimelineConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ViewConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"RecordTableConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}},{"kind":"Field","name":{"kind":"Name","value":"viewId"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"WorkflowConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"WorkflowRunConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"WorkflowVersionConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"FrontComponentConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}},{"kind":"Field","name":{"kind":"Name","value":"frontComponentId"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"pageLayoutTabId"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"PageLayoutTabFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PageLayoutTab"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"applicationId"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"position"}},{"kind":"Field","name":{"kind":"Name","value":"layoutMode"}},{"kind":"Field","name":{"kind":"Name","value":"widgets"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PageLayoutWidgetFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"pageLayoutId"}},{"kind":"Field","name":{"kind":"Name","value":"isActive"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"PageLayoutFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PageLayout"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"objectMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"type"}},{"kind":"Field","name":{"kind":"Name","value":"defaultTabToFocusOnMobileAndSidePanelId"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"tabs"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PageLayoutTabFragment"}}]}}]}}]} as unknown as DocumentNode; export const FindAllRecordPageLayoutsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"FindAllRecordPageLayouts"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"getPageLayouts"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"pageLayoutType"},"value":{"kind":"EnumValue","value":"RECORD_PAGE"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PageLayoutFragment"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"PageLayoutWidgetFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PageLayoutWidget"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"applicationId"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"type"}},{"kind":"Field","name":{"kind":"Name","value":"objectMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"isActive"}},{"kind":"Field","name":{"kind":"Name","value":"deletedAt"}},{"kind":"Field","name":{"kind":"Name","value":"conditionalDisplay"}},{"kind":"Field","name":{"kind":"Name","value":"conditionalAvailabilityExpression"}},{"kind":"Field","name":{"kind":"Name","value":"gridPosition"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"column"}},{"kind":"Field","name":{"kind":"Name","value":"columnSpan"}},{"kind":"Field","name":{"kind":"Name","value":"row"}},{"kind":"Field","name":{"kind":"Name","value":"rowSpan"}}]}},{"kind":"Field","name":{"kind":"Name","value":"position"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PageLayoutWidgetGridPosition"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"layoutMode"}},{"kind":"Field","name":{"kind":"Name","value":"row"}},{"kind":"Field","name":{"kind":"Name","value":"column"}},{"kind":"Field","name":{"kind":"Name","value":"rowSpan"}},{"kind":"Field","name":{"kind":"Name","value":"columnSpan"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PageLayoutWidgetVerticalListPosition"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"layoutMode"}},{"kind":"Field","name":{"kind":"Name","value":"index"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PageLayoutWidgetCanvasPosition"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"layoutMode"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"configuration"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"BarChartConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}},{"kind":"Field","name":{"kind":"Name","value":"aggregateFieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"aggregateOperation"}},{"kind":"Field","name":{"kind":"Name","value":"primaryAxisGroupByFieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"primaryAxisGroupBySubFieldName"}},{"kind":"Field","name":{"kind":"Name","value":"primaryAxisDateGranularity"}},{"kind":"Field","name":{"kind":"Name","value":"primaryAxisOrderBy"}},{"kind":"Field","name":{"kind":"Name","value":"primaryAxisManualSortOrder"}},{"kind":"Field","name":{"kind":"Name","value":"secondaryAxisGroupByFieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"secondaryAxisGroupBySubFieldName"}},{"kind":"Field","name":{"kind":"Name","value":"secondaryAxisGroupByDateGranularity"}},{"kind":"Field","name":{"kind":"Name","value":"secondaryAxisOrderBy"}},{"kind":"Field","name":{"kind":"Name","value":"secondaryAxisManualSortOrder"}},{"kind":"Field","name":{"kind":"Name","value":"omitNullValues"}},{"kind":"Field","name":{"kind":"Name","value":"axisNameDisplay"}},{"kind":"Field","name":{"kind":"Name","value":"displayDataLabel"}},{"kind":"Field","name":{"kind":"Name","value":"displayLegend"}},{"kind":"Field","name":{"kind":"Name","value":"rangeMin"}},{"kind":"Field","name":{"kind":"Name","value":"rangeMax"}},{"kind":"Field","name":{"kind":"Name","value":"color"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"filter"}},{"kind":"Field","name":{"kind":"Name","value":"groupMode"}},{"kind":"Field","name":{"kind":"Name","value":"layout"}},{"kind":"Field","name":{"kind":"Name","value":"isCumulative"}},{"kind":"Field","name":{"kind":"Name","value":"splitMultiValueFields"}},{"kind":"Field","name":{"kind":"Name","value":"timezone"}},{"kind":"Field","name":{"kind":"Name","value":"firstDayOfTheWeek"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"LineChartConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}},{"kind":"Field","name":{"kind":"Name","value":"aggregateFieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"aggregateOperation"}},{"kind":"Field","name":{"kind":"Name","value":"primaryAxisGroupByFieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"primaryAxisGroupBySubFieldName"}},{"kind":"Field","name":{"kind":"Name","value":"primaryAxisDateGranularity"}},{"kind":"Field","name":{"kind":"Name","value":"primaryAxisOrderBy"}},{"kind":"Field","name":{"kind":"Name","value":"primaryAxisManualSortOrder"}},{"kind":"Field","name":{"kind":"Name","value":"secondaryAxisGroupByFieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"secondaryAxisGroupBySubFieldName"}},{"kind":"Field","name":{"kind":"Name","value":"secondaryAxisGroupByDateGranularity"}},{"kind":"Field","name":{"kind":"Name","value":"secondaryAxisOrderBy"}},{"kind":"Field","name":{"kind":"Name","value":"secondaryAxisManualSortOrder"}},{"kind":"Field","name":{"kind":"Name","value":"omitNullValues"}},{"kind":"Field","name":{"kind":"Name","value":"axisNameDisplay"}},{"kind":"Field","name":{"kind":"Name","value":"displayDataLabel"}},{"kind":"Field","name":{"kind":"Name","value":"displayLegend"}},{"kind":"Field","name":{"kind":"Name","value":"rangeMin"}},{"kind":"Field","name":{"kind":"Name","value":"rangeMax"}},{"kind":"Field","name":{"kind":"Name","value":"color"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"filter"}},{"kind":"Field","name":{"kind":"Name","value":"isStacked"}},{"kind":"Field","name":{"kind":"Name","value":"isCumulative"}},{"kind":"Field","name":{"kind":"Name","value":"splitMultiValueFields"}},{"kind":"Field","name":{"kind":"Name","value":"timezone"}},{"kind":"Field","name":{"kind":"Name","value":"firstDayOfTheWeek"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PieChartConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}},{"kind":"Field","name":{"kind":"Name","value":"groupByFieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"aggregateFieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"aggregateOperation"}},{"kind":"Field","name":{"kind":"Name","value":"groupBySubFieldName"}},{"kind":"Field","name":{"kind":"Name","value":"dateGranularity"}},{"kind":"Field","name":{"kind":"Name","value":"orderBy"}},{"kind":"Field","name":{"kind":"Name","value":"manualSortOrder"}},{"kind":"Field","name":{"kind":"Name","value":"displayDataLabel"}},{"kind":"Field","name":{"kind":"Name","value":"showCenterMetric"}},{"kind":"Field","name":{"kind":"Name","value":"displayLegend"}},{"kind":"Field","name":{"kind":"Name","value":"hideEmptyCategory"}},{"kind":"Field","name":{"kind":"Name","value":"splitMultiValueFields"}},{"kind":"Field","name":{"kind":"Name","value":"color"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"filter"}},{"kind":"Field","name":{"kind":"Name","value":"timezone"}},{"kind":"Field","name":{"kind":"Name","value":"firstDayOfTheWeek"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"AggregateChartConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}},{"kind":"Field","name":{"kind":"Name","value":"aggregateFieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"aggregateOperation"}},{"kind":"Field","name":{"kind":"Name","value":"label"}},{"kind":"Field","name":{"kind":"Name","value":"displayDataLabel"}},{"kind":"Field","name":{"kind":"Name","value":"format"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"filter"}},{"kind":"Field","name":{"kind":"Name","value":"prefix"}},{"kind":"Field","name":{"kind":"Name","value":"suffix"}},{"kind":"Field","name":{"kind":"Name","value":"timezone"}},{"kind":"Field","name":{"kind":"Name","value":"firstDayOfTheWeek"}},{"kind":"Field","name":{"kind":"Name","value":"ratioAggregateConfig"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"fieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"optionValue"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"GaugeChartConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}},{"kind":"Field","name":{"kind":"Name","value":"aggregateFieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"aggregateOperation"}},{"kind":"Field","name":{"kind":"Name","value":"displayDataLabel"}},{"kind":"Field","name":{"kind":"Name","value":"color"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"filter"}},{"kind":"Field","name":{"kind":"Name","value":"timezone"}},{"kind":"Field","name":{"kind":"Name","value":"firstDayOfTheWeek"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"IframeConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}},{"kind":"Field","name":{"kind":"Name","value":"url"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"StandaloneRichTextConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}},{"kind":"Field","name":{"kind":"Name","value":"body"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"blocknote"}},{"kind":"Field","name":{"kind":"Name","value":"markdown"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"CalendarConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"EmailsConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"EmailThreadConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"FieldConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}},{"kind":"Field","name":{"kind":"Name","value":"fieldDisplayMode"}},{"kind":"Field","name":{"kind":"Name","value":"fieldMetadataId"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"FieldRichTextConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"FieldsConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}},{"kind":"Field","name":{"kind":"Name","value":"viewId"}},{"kind":"Field","name":{"kind":"Name","value":"newFieldDefaultVisibility"}},{"kind":"Field","name":{"kind":"Name","value":"shouldAllowUserToSeeHiddenFields"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"FilesConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"NotesConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TasksConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TimelineConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ViewConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"RecordTableConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}},{"kind":"Field","name":{"kind":"Name","value":"viewId"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"WorkflowConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"WorkflowRunConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"WorkflowVersionConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"FrontComponentConfiguration"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"configurationType"}},{"kind":"Field","name":{"kind":"Name","value":"frontComponentId"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"pageLayoutTabId"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"PageLayoutTabFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PageLayoutTab"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"applicationId"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"position"}},{"kind":"Field","name":{"kind":"Name","value":"layoutMode"}},{"kind":"Field","name":{"kind":"Name","value":"widgets"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PageLayoutWidgetFragment"}}]}},{"kind":"Field","name":{"kind":"Name","value":"pageLayoutId"}},{"kind":"Field","name":{"kind":"Name","value":"isActive"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"PageLayoutFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PageLayout"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"objectMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"type"}},{"kind":"Field","name":{"kind":"Name","value":"defaultTabToFocusOnMobileAndSidePanelId"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"tabs"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PageLayoutTabFragment"}}]}}]}}]} as unknown as DocumentNode; export const BarChartDataDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"BarChartData"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"BarChartDataInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"barChartData"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"data"}},{"kind":"Field","name":{"kind":"Name","value":"indexBy"}},{"kind":"Field","name":{"kind":"Name","value":"keys"}},{"kind":"Field","name":{"kind":"Name","value":"series"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"key"}},{"kind":"Field","name":{"kind":"Name","value":"label"}}]}},{"kind":"Field","name":{"kind":"Name","value":"xAxisLabel"}},{"kind":"Field","name":{"kind":"Name","value":"yAxisLabel"}},{"kind":"Field","name":{"kind":"Name","value":"showLegend"}},{"kind":"Field","name":{"kind":"Name","value":"showDataLabels"}},{"kind":"Field","name":{"kind":"Name","value":"layout"}},{"kind":"Field","name":{"kind":"Name","value":"groupMode"}},{"kind":"Field","name":{"kind":"Name","value":"hasTooManyGroups"}},{"kind":"Field","name":{"kind":"Name","value":"formattedToRawLookup"}}]}}]}}]} as unknown as DocumentNode; diff --git a/packages/twenty-front/src/modules/object-record/record-table-widget/components/RecordTableWidgetViewLoadEffect.tsx b/packages/twenty-front/src/modules/object-record/record-table-widget/components/RecordTableWidgetViewLoadEffect.tsx index ea5e3a0905..9a620f0631 100644 --- a/packages/twenty-front/src/modules/object-record/record-table-widget/components/RecordTableWidgetViewLoadEffect.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table-widget/components/RecordTableWidgetViewLoadEffect.tsx @@ -1,6 +1,7 @@ import { type EnrichedObjectMetadataItem } from '@/object-metadata/types/EnrichedObjectMetadataItem'; import { useLoadRecordIndexStates } from '@/object-record/record-index/hooks/useLoadRecordIndexStates'; import { lastLoadedRecordTableWidgetViewIdComponentState } from '@/object-record/record-table-widget/states/lastLoadedRecordTableWidgetViewIdComponentState'; +import { computeRecordTableWidgetViewLoadContentSignature } from '@/object-record/record-table-widget/utils/computeRecordTableWidgetViewLoadContentSignature'; import { useIsPageLayoutInEditMode } from '@/page-layout/hooks/useIsPageLayoutInEditMode'; import { recordTableWidgetViewDraftByWidgetIdComponentFamilySelector } from '@/page-layout/states/selectors/recordTableWidgetViewDraftByWidgetIdComponentFamilySelector'; import { constructViewFromRecordTableWidgetViewSnapshot } from '@/page-layout/widgets/record-table/utils/constructViewFromRecordTableWidgetViewSnapshot'; @@ -60,11 +61,17 @@ export const RecordTableWidgetViewLoadEffect = ({ return; } - if ( + const contentSignature = + computeRecordTableWidgetViewLoadContentSignature(currentView); + + const lastLoadedMatches = viewId === lastLoadedRecordTableWidgetViewId?.viewId && objectMetadataItem.updatedAt === - lastLoadedRecordTableWidgetViewId?.objectMetadataItemUpdatedAt - ) { + lastLoadedRecordTableWidgetViewId?.objectMetadataItemUpdatedAt && + contentSignature === + lastLoadedRecordTableWidgetViewId?.loadedViewContentSignature; + + if (lastLoadedMatches) { return; } @@ -73,6 +80,7 @@ export const RecordTableWidgetViewLoadEffect = ({ setLastLoadedRecordTableWidgetViewId({ viewId, objectMetadataItemUpdatedAt: objectMetadataItem.updatedAt, + loadedViewContentSignature: contentSignature, }); }, [ viewId, diff --git a/packages/twenty-front/src/modules/object-record/record-table-widget/states/lastLoadedRecordTableWidgetViewIdComponentState.ts b/packages/twenty-front/src/modules/object-record/record-table-widget/states/lastLoadedRecordTableWidgetViewIdComponentState.ts index 884a3e896c..f26d59d7c0 100644 --- a/packages/twenty-front/src/modules/object-record/record-table-widget/states/lastLoadedRecordTableWidgetViewIdComponentState.ts +++ b/packages/twenty-front/src/modules/object-record/record-table-widget/states/lastLoadedRecordTableWidgetViewIdComponentState.ts @@ -5,6 +5,7 @@ export const lastLoadedRecordTableWidgetViewIdComponentState = createAtomComponentState<{ viewId: string; objectMetadataItemUpdatedAt: string; + loadedViewContentSignature: string; } | null>({ key: 'lastLoadedRecordTableWidgetViewIdComponentState', defaultValue: null, diff --git a/packages/twenty-front/src/modules/object-record/record-table-widget/utils/computeRecordTableWidgetViewLoadContentSignature.ts b/packages/twenty-front/src/modules/object-record/record-table-widget/utils/computeRecordTableWidgetViewLoadContentSignature.ts new file mode 100644 index 0000000000..3614632bbc --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-table-widget/utils/computeRecordTableWidgetViewLoadContentSignature.ts @@ -0,0 +1,34 @@ +import { type View } from '@/views/types/View'; + +export const computeRecordTableWidgetViewLoadContentSignature = ( + view: View, +): string => + JSON.stringify({ + fields: [...view.viewFields] + .sort((fieldA, fieldB) => fieldA.position - fieldB.position) + .map((field) => ({ + id: field.id, + fieldMetadataId: field.fieldMetadataId, + isVisible: field.isVisible, + position: field.position, + })), + filters: [...view.viewFilters] + .sort((filterA, filterB) => filterA.id.localeCompare(filterB.id)) + .map((filter) => ({ + id: filter.id, + fieldMetadataId: filter.fieldMetadataId, + operand: filter.operand, + value: filter.value, + viewFilterGroupId: filter.viewFilterGroupId, + })), + filterGroups: (view.viewFilterGroups ?? []).map((group) => ({ + id: group.id, + logicalOperator: group.logicalOperator, + parentViewFilterGroupId: group.parentViewFilterGroupId, + })), + sorts: view.viewSorts.map((sort) => ({ + id: sort.id, + fieldMetadataId: sort.fieldMetadataId, + direction: sort.direction, + })), + }); diff --git a/packages/twenty-front/src/modules/page-layout/graphql/mutations/upsertViewWidget.ts b/packages/twenty-front/src/modules/page-layout/graphql/mutations/upsertViewWidget.ts new file mode 100644 index 0000000000..6b9e632963 --- /dev/null +++ b/packages/twenty-front/src/modules/page-layout/graphql/mutations/upsertViewWidget.ts @@ -0,0 +1,11 @@ +import { VIEW_FRAGMENT } from '@/views/graphql/fragments/viewFragment'; +import { gql } from '@apollo/client'; + +export const UPSERT_VIEW_WIDGET = gql` + ${VIEW_FRAGMENT} + mutation UpsertViewWidget($input: UpsertViewWidgetInput!) { + upsertViewWidget(input: $input) { + ...ViewFragment + } + } +`; diff --git a/packages/twenty-front/src/modules/page-layout/hooks/useCreatePendingRecordTableWidgetViews.ts b/packages/twenty-front/src/modules/page-layout/hooks/useCreatePendingRecordTableWidgetViews.ts index d79631c687..64155184ed 100644 --- a/packages/twenty-front/src/modules/page-layout/hooks/useCreatePendingRecordTableWidgetViews.ts +++ b/packages/twenty-front/src/modules/page-layout/hooks/useCreatePendingRecordTableWidgetViews.ts @@ -1,10 +1,8 @@ import { pageLayoutDraftComponentState } from '@/page-layout/states/pageLayoutDraftComponentState'; import { pageLayoutPersistedComponentState } from '@/page-layout/states/pageLayoutPersistedComponentState'; import { recordTableWidgetViewDraftComponentState } from '@/page-layout/states/recordTableWidgetViewDraftComponentState'; -import { recordTableWidgetViewPersistedComponentState } from '@/page-layout/states/recordTableWidgetViewPersistedComponentState'; import { getWidgetConfigurationViewId } from '@/page-layout/utils/getWidgetConfigurationViewId'; import { usePerformViewAPIPersist } from '@/views/hooks/internal/usePerformViewAPIPersist'; -import { usePerformViewFieldAPIPersist } from '@/views/hooks/internal/usePerformViewFieldAPIPersist'; import { useStore } from 'jotai'; import { useCallback } from 'react'; import { isDefined } from 'twenty-shared/utils'; @@ -13,7 +11,6 @@ import { WidgetType } from '~/generated-metadata/graphql'; export const useCreatePendingRecordTableWidgetViews = () => { const { performViewAPICreate, performViewAPIDestroy } = usePerformViewAPIPersist(); - const { performViewFieldAPICreate } = usePerformViewFieldAPIPersist(); const store = useStore(); const createPendingRecordTableWidgetViews = useCallback( @@ -101,19 +98,6 @@ export const useCreatePendingRecordTableWidgetViews = () => { `Failed to create view for RECORD_TABLE widget ${widget.id}`, ); } - - const viewFieldInputs = widgetViewDraft.viewFields.map((field) => ({ - id: field.id, - viewId: field.viewId, - fieldMetadataId: field.fieldMetadataId, - position: field.position, - size: field.size, - isVisible: field.isVisible, - })); - - if (viewFieldInputs.length > 0) { - await performViewFieldAPICreate({ inputs: viewFieldInputs }); - } } for (const [widgetId, viewId] of persistedRecordTableWidgets) { @@ -121,20 +105,8 @@ export const useCreatePendingRecordTableWidgetViews = () => { await performViewAPIDestroy({ id: viewId }); } } - - store.set( - recordTableWidgetViewPersistedComponentState.atomFamily({ - instanceId: pageLayoutId, - }), - recordTableWidgetViewDraft, - ); }, - [ - performViewAPICreate, - performViewAPIDestroy, - performViewFieldAPICreate, - store, - ], + [performViewAPICreate, performViewAPIDestroy, store], ); return { createPendingRecordTableWidgetViews }; diff --git a/packages/twenty-front/src/modules/page-layout/hooks/useHasRecordTableWidgetViewChanges.ts b/packages/twenty-front/src/modules/page-layout/hooks/useHasRecordTableWidgetViewChanges.ts new file mode 100644 index 0000000000..6e80ad16ee --- /dev/null +++ b/packages/twenty-front/src/modules/page-layout/hooks/useHasRecordTableWidgetViewChanges.ts @@ -0,0 +1,32 @@ +import { recordTableWidgetViewDraftComponentState } from '@/page-layout/states/recordTableWidgetViewDraftComponentState'; +import { recordTableWidgetViewPersistedComponentState } from '@/page-layout/states/recordTableWidgetViewPersistedComponentState'; +import { useStore } from 'jotai'; +import { useCallback } from 'react'; +import { isDeeplyEqual } from '~/utils/isDeeplyEqual'; + +export const useHasRecordTableWidgetViewChanges = () => { + const store = useStore(); + + const hasRecordTableWidgetViewChanges = useCallback( + (pageLayoutId: string): boolean => { + const recordTableWidgetViewDraft = store.get( + recordTableWidgetViewDraftComponentState.atomFamily({ + instanceId: pageLayoutId, + }), + ); + const recordTableWidgetViewPersisted = store.get( + recordTableWidgetViewPersistedComponentState.atomFamily({ + instanceId: pageLayoutId, + }), + ); + + return !isDeeplyEqual( + recordTableWidgetViewDraft, + recordTableWidgetViewPersisted, + ); + }, + [store], + ); + + return { hasRecordTableWidgetViewChanges }; +}; diff --git a/packages/twenty-front/src/modules/page-layout/hooks/useHasRecordTableWidgets.ts b/packages/twenty-front/src/modules/page-layout/hooks/useHasRecordTableWidgets.ts deleted file mode 100644 index 4c4b863cae..0000000000 --- a/packages/twenty-front/src/modules/page-layout/hooks/useHasRecordTableWidgets.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { pageLayoutDraftComponentState } from '@/page-layout/states/pageLayoutDraftComponentState'; -import { useStore } from 'jotai'; -import { useCallback } from 'react'; -import { WidgetConfigurationType } from '~/generated-metadata/graphql'; - -export const useHasRecordTableWidgets = () => { - const store = useStore(); - - const hasRecordTableWidgets = useCallback( - (pageLayoutId: string): boolean => { - const pageLayoutDraft = store.get( - pageLayoutDraftComponentState.atomFamily({ - instanceId: pageLayoutId, - }), - ); - - return pageLayoutDraft.tabs.some((tab) => - tab.widgets.some( - (widget) => - widget.configuration.configurationType === - WidgetConfigurationType.RECORD_TABLE, - ), - ); - }, - [store], - ); - - return { hasRecordTableWidgets }; -}; diff --git a/packages/twenty-front/src/modules/page-layout/hooks/useResetDraftPageLayoutToPersistedPageLayout.ts b/packages/twenty-front/src/modules/page-layout/hooks/useResetDraftPageLayoutToPersistedPageLayout.ts index ccfb394c27..17dd0f611a 100644 --- a/packages/twenty-front/src/modules/page-layout/hooks/useResetDraftPageLayoutToPersistedPageLayout.ts +++ b/packages/twenty-front/src/modules/page-layout/hooks/useResetDraftPageLayoutToPersistedPageLayout.ts @@ -9,6 +9,8 @@ import { fieldsWidgetUngroupedFieldsPersistedComponentState } from '@/page-layou import { pageLayoutCurrentLayoutsComponentState } from '@/page-layout/states/pageLayoutCurrentLayoutsComponentState'; import { pageLayoutDraftComponentState } from '@/page-layout/states/pageLayoutDraftComponentState'; import { pageLayoutPersistedComponentState } from '@/page-layout/states/pageLayoutPersistedComponentState'; +import { recordTableWidgetViewDraftComponentState } from '@/page-layout/states/recordTableWidgetViewDraftComponentState'; +import { recordTableWidgetViewPersistedComponentState } from '@/page-layout/states/recordTableWidgetViewPersistedComponentState'; import { convertPageLayoutToTabLayouts } from '@/page-layout/utils/convertPageLayoutToTabLayouts'; import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTabIdComponentState'; import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow'; @@ -83,6 +85,17 @@ export const useResetDraftPageLayoutToPersistedPageLayout = ({ componentInstanceId, ); + const recordTableWidgetViewDraftState = useAtomComponentStateCallbackState( + recordTableWidgetViewDraftComponentState, + componentInstanceId, + ); + + const recordTableWidgetViewPersistedState = + useAtomComponentStateCallbackState( + recordTableWidgetViewPersistedComponentState, + componentInstanceId, + ); + const resetDraftPageLayoutToPersistedPageLayout = useCallback(() => { const pageLayoutPersisted = store.get(pageLayoutPersistedState); @@ -132,6 +145,14 @@ export const useResetDraftPageLayoutToPersistedPageLayout = ({ fieldsWidgetEditorModeDraftState, fieldsWidgetEditorModePersisted, ); + + const recordTableWidgetViewPersisted = store.get( + recordTableWidgetViewPersistedState, + ); + store.set( + recordTableWidgetViewDraftState, + recordTableWidgetViewPersisted, + ); } }, [ pageLayoutDraftState, @@ -143,6 +164,8 @@ export const useResetDraftPageLayoutToPersistedPageLayout = ({ fieldsWidgetUngroupedFieldsPersistedState, fieldsWidgetEditorModeDraftState, fieldsWidgetEditorModePersistedState, + recordTableWidgetViewDraftState, + recordTableWidgetViewPersistedState, activeTabId, store, ]); diff --git a/packages/twenty-front/src/modules/page-layout/hooks/useSavePageLayoutWidgetsData.ts b/packages/twenty-front/src/modules/page-layout/hooks/useSavePageLayoutWidgetsData.ts index 03dcc3b096..dc1d94d561 100644 --- a/packages/twenty-front/src/modules/page-layout/hooks/useSavePageLayoutWidgetsData.ts +++ b/packages/twenty-front/src/modules/page-layout/hooks/useSavePageLayoutWidgetsData.ts @@ -1,18 +1,18 @@ import { useHasFieldsWidgetChanges } from '@/page-layout/hooks/useHasFieldsWidgetChanges'; -import { useHasRecordTableWidgets } from '@/page-layout/hooks/useHasRecordTableWidgets'; +import { useHasRecordTableWidgetViewChanges } from '@/page-layout/hooks/useHasRecordTableWidgetViewChanges'; import { useSaveFieldsWidgetGroups } from '@/page-layout/hooks/useSaveFieldsWidgetGroups'; -import { useSaveRecordTableWidgetsViewDataOnDashboardSave } from '@/page-layout/widgets/record-table/hooks/useSaveRecordTableWidgetsViewDataOnDashboardSave'; +import { useSaveRecordTableWidgetViews } from '@/page-layout/hooks/useSaveRecordTableWidgetViews'; import { useCallback } from 'react'; export const useSavePageLayoutWidgetsData = () => { const { hasFieldsWidgetChanges } = useHasFieldsWidgetChanges(); - const { hasRecordTableWidgets } = useHasRecordTableWidgets(); + const { hasRecordTableWidgetViewChanges } = + useHasRecordTableWidgetViewChanges(); const { saveFieldsWidgetGroups } = useSaveFieldsWidgetGroups(); - const { saveRecordTableWidgetsViewDataOnDashboardSave } = - useSaveRecordTableWidgetsViewDataOnDashboardSave(); + const { saveRecordTableWidgetViews } = useSaveRecordTableWidgetViews(); const savePageLayoutWidgetsData = useCallback( async (pageLayoutId: string) => { @@ -20,15 +20,15 @@ export const useSavePageLayoutWidgetsData = () => { await saveFieldsWidgetGroups(pageLayoutId); } - if (hasRecordTableWidgets(pageLayoutId)) { - await saveRecordTableWidgetsViewDataOnDashboardSave(pageLayoutId); + if (hasRecordTableWidgetViewChanges(pageLayoutId)) { + await saveRecordTableWidgetViews(pageLayoutId); } }, [ hasFieldsWidgetChanges, + hasRecordTableWidgetViewChanges, saveFieldsWidgetGroups, - saveRecordTableWidgetsViewDataOnDashboardSave, - hasRecordTableWidgets, + saveRecordTableWidgetViews, ], ); diff --git a/packages/twenty-front/src/modules/page-layout/hooks/useSaveRecordTableWidgetViews.ts b/packages/twenty-front/src/modules/page-layout/hooks/useSaveRecordTableWidgetViews.ts new file mode 100644 index 0000000000..b4e55c8464 --- /dev/null +++ b/packages/twenty-front/src/modules/page-layout/hooks/useSaveRecordTableWidgetViews.ts @@ -0,0 +1,115 @@ +import { UPSERT_VIEW_WIDGET } from '@/page-layout/graphql/mutations/upsertViewWidget'; +import { useHasRecordTableWidgetViewChanges } from '@/page-layout/hooks/useHasRecordTableWidgetViewChanges'; +import { pageLayoutDraftComponentState } from '@/page-layout/states/pageLayoutDraftComponentState'; +import { recordTableWidgetViewDraftComponentState } from '@/page-layout/states/recordTableWidgetViewDraftComponentState'; +import { recordTableWidgetViewPersistedComponentState } from '@/page-layout/states/recordTableWidgetViewPersistedComponentState'; +import { getWidgetConfigurationViewId } from '@/page-layout/utils/getWidgetConfigurationViewId'; +import { useMutation } from '@apollo/client/react'; +import { useStore } from 'jotai'; +import { useCallback } from 'react'; +import { isDefined } from 'twenty-shared/utils'; +import { + type UpsertViewWidgetInput, + type ViewFragmentFragment, + WidgetType, +} from '~/generated-metadata/graphql'; + +export const useSaveRecordTableWidgetViews = () => { + const [upsertViewWidgetMutation] = useMutation< + { upsertViewWidget: ViewFragmentFragment }, + { input: UpsertViewWidgetInput } + >(UPSERT_VIEW_WIDGET); + + const { hasRecordTableWidgetViewChanges } = + useHasRecordTableWidgetViewChanges(); + + const store = useStore(); + + const saveRecordTableWidgetViews = useCallback( + async (pageLayoutId: string) => { + if (!hasRecordTableWidgetViewChanges(pageLayoutId)) { + return; + } + + const draft = store.get( + pageLayoutDraftComponentState.atomFamily({ + instanceId: pageLayoutId, + }), + ); + + const recordTableWidgetViewDraft = store.get( + recordTableWidgetViewDraftComponentState.atomFamily({ + instanceId: pageLayoutId, + }), + ); + + const draftRecordTableWidgets = draft.tabs + .flatMap((tab) => tab.widgets) + .filter((widget) => widget.type === WidgetType.RECORD_TABLE); + + for (const widget of draftRecordTableWidgets) { + const viewId = getWidgetConfigurationViewId(widget.configuration); + + if (!isDefined(viewId)) { + continue; + } + + const widgetViewDraft = recordTableWidgetViewDraft[widget.id]; + + if (!isDefined(widgetViewDraft)) { + continue; + } + + await upsertViewWidgetMutation({ + variables: { + input: { + widgetId: widget.id, + viewFields: widgetViewDraft.viewFields.map((field) => ({ + ...(isDefined(field.id) ? { viewFieldId: field.id } : {}), + fieldMetadataId: field.fieldMetadataId, + isVisible: field.isVisible, + position: field.position, + size: field.size, + })), + viewFilters: widgetViewDraft.viewFilters.map((filter) => ({ + id: filter.id, + fieldMetadataId: filter.fieldMetadataId, + operand: filter.operand, + value: filter.value, + viewFilterGroupId: filter.viewFilterGroupId ?? undefined, + positionInViewFilterGroup: + filter.positionInViewFilterGroup ?? undefined, + subFieldName: filter.subFieldName ?? undefined, + })), + viewFilterGroups: widgetViewDraft.viewFilterGroups.map( + (group) => ({ + id: group.id, + parentViewFilterGroupId: + group.parentViewFilterGroupId ?? undefined, + logicalOperator: group.logicalOperator, + positionInViewFilterGroup: + group.positionInViewFilterGroup ?? undefined, + }), + ), + viewSorts: widgetViewDraft.viewSorts.map((sort) => ({ + id: sort.id, + fieldMetadataId: sort.fieldMetadataId, + direction: sort.direction, + })), + }, + }, + }); + } + + store.set( + recordTableWidgetViewPersistedComponentState.atomFamily({ + instanceId: pageLayoutId, + }), + recordTableWidgetViewDraft, + ); + }, + [hasRecordTableWidgetViewChanges, store, upsertViewWidgetMutation], + ); + + return { saveRecordTableWidgetViews }; +}; diff --git a/packages/twenty-front/src/modules/page-layout/widgets/record-table/hooks/useAddDraftViewForRecordTableWidget.ts b/packages/twenty-front/src/modules/page-layout/widgets/record-table/hooks/useAddDraftViewForRecordTableWidget.ts index 1811d97288..c747c8a298 100644 --- a/packages/twenty-front/src/modules/page-layout/widgets/record-table/hooks/useAddDraftViewForRecordTableWidget.ts +++ b/packages/twenty-front/src/modules/page-layout/widgets/record-table/hooks/useAddDraftViewForRecordTableWidget.ts @@ -70,7 +70,13 @@ export const useAddDraftViewForRecordTableWidget = (pageLayoutId: string) => { store.set(recordTableWidgetViewDraftState, (prev) => ({ ...prev, - [widgetId]: { view: flatView, viewFields: flatViewFields }, + [widgetId]: { + view: flatView, + viewFields: flatViewFields, + viewFilters: [], + viewFilterGroups: [], + viewSorts: [], + }, })); requestAnimationFrame(() => { diff --git a/packages/twenty-front/src/modules/page-layout/widgets/record-table/hooks/useInitializeRecordTableWidgetViewDraft.ts b/packages/twenty-front/src/modules/page-layout/widgets/record-table/hooks/useInitializeRecordTableWidgetViewDraft.ts index 247bf2bf6d..8f67244ad8 100644 --- a/packages/twenty-front/src/modules/page-layout/widgets/record-table/hooks/useInitializeRecordTableWidgetViewDraft.ts +++ b/packages/twenty-front/src/modules/page-layout/widgets/record-table/hooks/useInitializeRecordTableWidgetViewDraft.ts @@ -1,4 +1,7 @@ import { type FlatViewField } from '@/metadata-store/types/FlatViewField'; +import { type FlatViewFilter } from '@/metadata-store/types/FlatViewFilter'; +import { type FlatViewFilterGroup } from '@/metadata-store/types/FlatViewFilterGroup'; +import { type FlatViewSort } from '@/metadata-store/types/FlatViewSort'; import { recordTableWidgetViewDraftComponentState } from '@/page-layout/states/recordTableWidgetViewDraftComponentState'; import { recordTableWidgetViewPersistedComponentState } from '@/page-layout/states/recordTableWidgetViewPersistedComponentState'; import { type RecordTableWidgetViewSnapshot } from '@/page-layout/widgets/record-table/types/RecordTableWidgetViewSnapshot'; @@ -38,16 +41,43 @@ export const useInitializeRecordTableWidgetViewDraft = ({ return; } - const { viewFields, ...viewProps } = view; + const { + viewFields, + viewFilters, + viewFilterGroups, + viewSorts, + viewGroups: _viewGroups, + ...viewProps + } = view; const flatViewFields: FlatViewField[] = viewFields.map((field) => ({ ...field, viewId: view.id, })); + const flatViewFilters: FlatViewFilter[] = viewFilters.map((filter) => ({ + ...filter, + viewId: view.id, + })); + + const flatViewFilterGroups: FlatViewFilterGroup[] = ( + viewFilterGroups ?? [] + ).map((group) => ({ + ...group, + viewId: view.id, + })); + + const flatViewSorts: FlatViewSort[] = viewSorts.map((sort) => ({ + ...sort, + viewId: view.id, + })); + const snapshot: RecordTableWidgetViewSnapshot = { view: viewProps, viewFields: flatViewFields, + viewFilters: flatViewFilters, + viewFilterGroups: flatViewFilterGroups, + viewSorts: flatViewSorts, }; store.set(recordTableWidgetViewDraftState, (prev) => ({ diff --git a/packages/twenty-front/src/modules/page-layout/widgets/record-table/hooks/useMapRecordFieldToViewFieldWithCurrentAggregateOperation.ts b/packages/twenty-front/src/modules/page-layout/widgets/record-table/hooks/useMapRecordFieldToViewFieldWithCurrentAggregateOperation.ts deleted file mode 100644 index acf3643705..0000000000 --- a/packages/twenty-front/src/modules/page-layout/widgets/record-table/hooks/useMapRecordFieldToViewFieldWithCurrentAggregateOperation.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { type RecordField } from '@/object-record/record-field/types/RecordField'; -import { viewFieldAggregateOperationState } from '@/object-record/record-table/record-table-footer/states/viewFieldAggregateOperationState'; -import { convertExtendedAggregateOperationToAggregateOperation } from '@/object-record/utils/convertExtendedAggregateOperationToAggregateOperation'; -import { type ViewField } from '@/views/types/ViewField'; -import { mapRecordFieldToViewField } from '@/views/utils/mapRecordFieldToViewField'; -import { useStore } from 'jotai'; -import { useCallback } from 'react'; -import { isDefined } from 'twenty-shared/utils'; - -export const useMapRecordFieldToViewFieldWithCurrentAggregateOperation = () => { - const store = useStore(); - - const mapRecordFieldToViewFieldWithCurrentAggregateOperation = useCallback( - (recordField: RecordField): Omit => { - const viewField = mapRecordFieldToViewField(recordField); - - const extendedAggregateOperation = store.get( - viewFieldAggregateOperationState.atomFamily({ - viewFieldId: recordField.id, - }), - ); - - return { - ...viewField, - aggregateOperation: isDefined(extendedAggregateOperation) - ? convertExtendedAggregateOperationToAggregateOperation( - extendedAggregateOperation, - ) - : viewField.aggregateOperation, - }; - }, - [store], - ); - - return { mapRecordFieldToViewFieldWithCurrentAggregateOperation }; -}; diff --git a/packages/twenty-front/src/modules/page-layout/widgets/record-table/hooks/useRecordTableWidgetFieldCallbacks.ts b/packages/twenty-front/src/modules/page-layout/widgets/record-table/hooks/useRecordTableWidgetFieldCallbacks.ts new file mode 100644 index 0000000000..c90edcf026 --- /dev/null +++ b/packages/twenty-front/src/modules/page-layout/widgets/record-table/hooks/useRecordTableWidgetFieldCallbacks.ts @@ -0,0 +1,81 @@ +import { type RecordField } from '@/object-record/record-field/types/RecordField'; +import { recordTableWidgetViewDraftComponentState } from '@/page-layout/states/recordTableWidgetViewDraftComponentState'; +import { useAtomComponentStateCallbackState } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateCallbackState'; +import { useStore } from 'jotai'; +import { isDefined } from 'twenty-shared/utils'; + +type UseRecordTableWidgetFieldCallbacksParams = { + pageLayoutId: string; + widgetId: string; + viewId: string; +}; + +export const useRecordTableWidgetFieldCallbacks = ({ + pageLayoutId, + widgetId, + viewId, +}: UseRecordTableWidgetFieldCallbacksParams) => { + const recordTableWidgetViewDraftState = useAtomComponentStateCallbackState( + recordTableWidgetViewDraftComponentState, + pageLayoutId, + ); + + const store = useStore(); + + const handleFieldUpdated = ( + viewFieldId: string, + update: Partial<{ position: number; isVisible: boolean }>, + ) => { + store.set(recordTableWidgetViewDraftState, (prev) => { + const widgetViewDraft = prev[widgetId]; + + if (!isDefined(widgetViewDraft)) { + return prev; + } + + return { + ...prev, + [widgetId]: { + ...widgetViewDraft, + viewFields: widgetViewDraft.viewFields.map((field) => + field.id === viewFieldId || + field.clientRecordFieldId === viewFieldId + ? { ...field, ...update } + : field, + ), + }, + }; + }); + }; + + const handleFieldCreated = (recordField: RecordField) => { + store.set(recordTableWidgetViewDraftState, (prev) => { + const widgetViewDraft = prev[widgetId]; + + if (!isDefined(widgetViewDraft)) { + return prev; + } + + return { + ...prev, + [widgetId]: { + ...widgetViewDraft, + viewFields: [ + ...widgetViewDraft.viewFields, + { + fieldMetadataId: recordField.fieldMetadataItemId, + position: recordField.position, + isVisible: recordField.isVisible, + size: recordField.size, + isActive: true, + viewId, + clientRecordFieldId: recordField.id, + }, + ], + }, + }; + }); + }; + + return { handleFieldUpdated, handleFieldCreated }; +}; diff --git a/packages/twenty-front/src/modules/page-layout/widgets/record-table/hooks/useRecordTableWidgetFilterCallbacks.ts b/packages/twenty-front/src/modules/page-layout/widgets/record-table/hooks/useRecordTableWidgetFilterCallbacks.ts new file mode 100644 index 0000000000..c8ddaa9e60 --- /dev/null +++ b/packages/twenty-front/src/modules/page-layout/widgets/record-table/hooks/useRecordTableWidgetFilterCallbacks.ts @@ -0,0 +1,77 @@ +import { currentRecordFilterGroupsComponentState } from '@/object-record/record-filter-group/states/currentRecordFilterGroupsComponentState'; +import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState'; +import { recordTableWidgetViewDraftComponentState } from '@/page-layout/states/recordTableWidgetViewDraftComponentState'; +import { useAtomComponentStateCallbackState } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateCallbackState'; +import { mapRecordFilterGroupToViewFilterGroup } from '@/views/utils/mapRecordFilterGroupToViewFilterGroup'; +import { useStore } from 'jotai'; +import { isDefined } from 'twenty-shared/utils'; + +type UseRecordTableWidgetFilterCallbacksParams = { + pageLayoutId: string; + widgetId: string; + viewId: string; + recordIndexId: string; +}; + +export const useRecordTableWidgetFilterCallbacks = ({ + pageLayoutId, + widgetId, + viewId, + recordIndexId, +}: UseRecordTableWidgetFilterCallbacksParams) => { + const recordTableWidgetViewDraftState = useAtomComponentStateCallbackState( + recordTableWidgetViewDraftComponentState, + pageLayoutId, + ); + + const currentRecordFiltersState = useAtomComponentStateCallbackState( + currentRecordFiltersComponentState, + recordIndexId, + ); + + const currentRecordFilterGroupsState = useAtomComponentStateCallbackState( + currentRecordFilterGroupsComponentState, + recordIndexId, + ); + + const store = useStore(); + + const handleFilterUpdate = () => { + const currentRecordFilters = store.get(currentRecordFiltersState); + const currentRecordFilterGroups = store.get(currentRecordFilterGroupsState); + + store.set(recordTableWidgetViewDraftState, (prev) => { + const widgetViewDraft = prev[widgetId]; + + if (!isDefined(widgetViewDraft)) { + return prev; + } + + return { + ...prev, + [widgetId]: { + ...widgetViewDraft, + viewFilters: currentRecordFilters.map((recordFilter) => ({ + id: recordFilter.id, + fieldMetadataId: recordFilter.fieldMetadataId, + operand: recordFilter.operand, + value: recordFilter.value, + viewId, + viewFilterGroupId: recordFilter.recordFilterGroupId ?? null, + positionInViewFilterGroup: + recordFilter.positionInRecordFilterGroup ?? null, + subFieldName: recordFilter.subFieldName ?? null, + })), + viewFilterGroups: currentRecordFilterGroups.map((recordFilterGroup) => + mapRecordFilterGroupToViewFilterGroup({ + recordFilterGroup, + view: { id: viewId }, + }), + ), + }, + }; + }); + }; + + return { handleFilterUpdate }; +}; diff --git a/packages/twenty-front/src/modules/page-layout/widgets/record-table/hooks/useRecordTableWidgetSortCallbacks.ts b/packages/twenty-front/src/modules/page-layout/widgets/record-table/hooks/useRecordTableWidgetSortCallbacks.ts new file mode 100644 index 0000000000..067c1251cc --- /dev/null +++ b/packages/twenty-front/src/modules/page-layout/widgets/record-table/hooks/useRecordTableWidgetSortCallbacks.ts @@ -0,0 +1,58 @@ +import { currentRecordSortsComponentState } from '@/object-record/record-sort/states/currentRecordSortsComponentState'; +import { recordTableWidgetViewDraftComponentState } from '@/page-layout/states/recordTableWidgetViewDraftComponentState'; +import { useAtomComponentStateCallbackState } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateCallbackState'; +import { useStore } from 'jotai'; +import { isDefined } from 'twenty-shared/utils'; + +type UseRecordTableWidgetSortCallbacksParams = { + pageLayoutId: string; + widgetId: string; + viewId: string; + recordIndexId: string; +}; + +export const useRecordTableWidgetSortCallbacks = ({ + pageLayoutId, + widgetId, + viewId, + recordIndexId, +}: UseRecordTableWidgetSortCallbacksParams) => { + const recordTableWidgetViewDraftState = useAtomComponentStateCallbackState( + recordTableWidgetViewDraftComponentState, + pageLayoutId, + ); + + const currentRecordSortsState = useAtomComponentStateCallbackState( + currentRecordSortsComponentState, + recordIndexId, + ); + + const store = useStore(); + + const handleSortUpdate = () => { + const currentRecordSorts = store.get(currentRecordSortsState); + + store.set(recordTableWidgetViewDraftState, (prev) => { + const widgetViewDraft = prev[widgetId]; + + if (!isDefined(widgetViewDraft)) { + return prev; + } + + return { + ...prev, + [widgetId]: { + ...widgetViewDraft, + viewSorts: currentRecordSorts.map((recordSort) => ({ + id: recordSort.id, + fieldMetadataId: recordSort.fieldMetadataId, + direction: recordSort.direction, + viewId, + })), + }, + }; + }); + }; + + return { handleSortUpdate }; +}; diff --git a/packages/twenty-front/src/modules/page-layout/widgets/record-table/hooks/useReorderRecordTableWidgetFields.ts b/packages/twenty-front/src/modules/page-layout/widgets/record-table/hooks/useReorderRecordTableWidgetFields.ts index 82f7a788af..4936e241a5 100644 --- a/packages/twenty-front/src/modules/page-layout/widgets/record-table/hooks/useReorderRecordTableWidgetFields.ts +++ b/packages/twenty-front/src/modules/page-layout/widgets/record-table/hooks/useReorderRecordTableWidgetFields.ts @@ -1,5 +1,6 @@ import { recordTableWidgetViewDraftComponentState } from '@/page-layout/states/recordTableWidgetViewDraftComponentState'; import { type RecordTableWidgetViewFieldItem } from '@/page-layout/widgets/record-table/types/RecordTableWidgetViewFieldItem'; +import { getRecordTableWidgetDraftViewFieldClientId } from '@/page-layout/widgets/record-table/utils/getRecordTableWidgetDraftViewFieldClientId'; import { useAtomComponentStateCallbackState } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateCallbackState'; import { useStore } from 'jotai'; import { useCallback } from 'react'; @@ -54,7 +55,10 @@ export const useReorderRecordTableWidgetFields = ({ [widgetId]: { ...widgetViewDraft, viewFields: widgetViewDraft.viewFields.map((field) => { - const newPosition = updatedPositions.get(field.id); + const newPosition = updatedPositions.get( + getRecordTableWidgetDraftViewFieldClientId(field), + ); + return newPosition !== undefined ? { ...field, position: newPosition } : field; diff --git a/packages/twenty-front/src/modules/page-layout/widgets/record-table/hooks/useSaveRecordTableWidgetFiltersToView.ts b/packages/twenty-front/src/modules/page-layout/widgets/record-table/hooks/useSaveRecordTableWidgetFiltersToView.ts deleted file mode 100644 index f6965c8a33..0000000000 --- a/packages/twenty-front/src/modules/page-layout/widgets/record-table/hooks/useSaveRecordTableWidgetFiltersToView.ts +++ /dev/null @@ -1,162 +0,0 @@ -import { currentRecordFilterGroupsComponentState } from '@/object-record/record-filter-group/states/currentRecordFilterGroupsComponentState'; -import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState'; -import { useAtomComponentStateCallbackState } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateCallbackState'; -import { usePerformViewFilterAPIPersist } from '@/views/hooks/internal/usePerformViewFilterAPIPersist'; -import { usePerformViewFilterGroupAPIPersist } from '@/views/hooks/internal/usePerformViewFilterGroupAPIPersist'; -import { viewsSelector } from '@/views/states/selectors/viewsSelector'; -import { getViewFilterGroupsToCreate } from '@/views/utils/getViewFilterGroupsToCreate'; -import { getViewFilterGroupsToDelete } from '@/views/utils/getViewFilterGroupsToDelete'; -import { getViewFilterGroupsToUpdate } from '@/views/utils/getViewFilterGroupsToUpdate'; -import { getViewFiltersToCreate } from '@/views/utils/getViewFiltersToCreate'; -import { getViewFiltersToDelete } from '@/views/utils/getViewFiltersToDelete'; -import { getViewFiltersToUpdate } from '@/views/utils/getViewFiltersToUpdate'; -import { mapRecordFilterGroupToViewFilterGroup } from '@/views/utils/mapRecordFilterGroupToViewFilterGroup'; -import { mapRecordFilterToViewFilter } from '@/views/utils/mapRecordFilterToViewFilter'; -import { useStore } from 'jotai'; -import { useCallback } from 'react'; -import { isDefined } from 'twenty-shared/utils'; - -export const useSaveRecordTableWidgetFiltersToView = ( - viewId: string, - instanceId: string, -) => { - const { - performViewFilterAPICreate, - performViewFilterAPIUpdate, - performViewFilterAPIDelete, - } = usePerformViewFilterAPIPersist(); - - const { - performViewFilterGroupAPICreate, - performViewFilterGroupAPIUpdate, - performViewFilterGroupAPIDestroy, - } = usePerformViewFilterGroupAPIPersist(); - - const currentRecordFiltersCallbackState = useAtomComponentStateCallbackState( - currentRecordFiltersComponentState, - instanceId, - ); - - const currentRecordFilterGroupsCallbackState = - useAtomComponentStateCallbackState( - currentRecordFilterGroupsComponentState, - instanceId, - ); - - const store = useStore(); - - const saveRecordTableWidgetFiltersToView = useCallback(async () => { - const views = store.get(viewsSelector.atom); - const currentView = views.find((view) => view.id === viewId); - - if (!isDefined(currentView)) { - return; - } - - const currentRecordFilters = store.get(currentRecordFiltersCallbackState); - const currentRecordFilterGroups = store.get( - currentRecordFilterGroupsCallbackState, - ); - - const newViewFilterGroups = currentRecordFilterGroups.map( - (recordFilterGroup) => - mapRecordFilterGroupToViewFilterGroup({ - recordFilterGroup, - view: currentView, - }), - ); - - const currentViewFilterGroups = currentView.viewFilterGroups ?? []; - - const viewFilterGroupsToCreate = getViewFilterGroupsToCreate( - currentViewFilterGroups, - newViewFilterGroups, - ); - const viewFilterGroupsToUpdate = getViewFilterGroupsToUpdate( - currentViewFilterGroups, - newViewFilterGroups, - ); - const viewFilterGroupsToDelete = getViewFilterGroupsToDelete( - currentViewFilterGroups, - newViewFilterGroups, - ); - - await performViewFilterGroupAPICreate( - viewFilterGroupsToCreate, - currentView, - ); - await performViewFilterGroupAPIUpdate(viewFilterGroupsToUpdate); - - const newViewFilters = currentRecordFilters.map( - mapRecordFilterToViewFilter, - ); - - const currentViewFilters = currentView.viewFilters ?? []; - - const viewFiltersToCreate = getViewFiltersToCreate( - currentViewFilters, - newViewFilters, - ); - const viewFiltersToUpdate = getViewFiltersToUpdate( - currentViewFilters, - newViewFilters, - ); - const viewFiltersToDelete = getViewFiltersToDelete( - currentViewFilters, - newViewFilters, - ); - - await performViewFilterAPIDelete( - viewFiltersToDelete.map((viewFilter) => ({ - input: { id: viewFilter.id }, - })), - ); - - await performViewFilterAPIUpdate( - viewFiltersToUpdate.map((viewFilter) => ({ - input: { - id: viewFilter.id, - update: { - value: viewFilter.value, - operand: viewFilter.operand, - positionInViewFilterGroup: viewFilter.positionInViewFilterGroup, - viewFilterGroupId: viewFilter.viewFilterGroupId, - subFieldName: viewFilter.subFieldName ?? null, - }, - }, - })), - ); - - await performViewFilterAPICreate( - viewFiltersToCreate.map((viewFilter) => ({ - input: { - id: viewFilter.id, - fieldMetadataId: viewFilter.fieldMetadataId, - viewId: currentView.id, - value: viewFilter.value, - operand: viewFilter.operand, - viewFilterGroupId: viewFilter.viewFilterGroupId, - positionInViewFilterGroup: viewFilter.positionInViewFilterGroup, - subFieldName: viewFilter.subFieldName ?? null, - }, - })), - ); - - await performViewFilterGroupAPIDestroy( - viewFilterGroupsToDelete.map((viewFilterGroup) => viewFilterGroup.id), - ); - }, [ - store, - viewId, - currentRecordFiltersCallbackState, - currentRecordFilterGroupsCallbackState, - performViewFilterAPICreate, - performViewFilterAPIUpdate, - performViewFilterAPIDelete, - performViewFilterGroupAPICreate, - performViewFilterGroupAPIUpdate, - performViewFilterGroupAPIDestroy, - ]); - - return { saveRecordTableWidgetFiltersToView }; -}; diff --git a/packages/twenty-front/src/modules/page-layout/widgets/record-table/hooks/useSaveRecordTableWidgetSortsToView.ts b/packages/twenty-front/src/modules/page-layout/widgets/record-table/hooks/useSaveRecordTableWidgetSortsToView.ts deleted file mode 100644 index ce5e9e7714..0000000000 --- a/packages/twenty-front/src/modules/page-layout/widgets/record-table/hooks/useSaveRecordTableWidgetSortsToView.ts +++ /dev/null @@ -1,86 +0,0 @@ -import { type RecordSort } from '@/object-record/record-sort/types/RecordSort'; -import { usePerformViewSortAPIPersist } from '@/views/hooks/internal/usePerformViewSortAPIPersist'; -import { viewsSelector } from '@/views/states/selectors/viewsSelector'; -import { getViewSortsToCreate } from '@/views/utils/getViewSortsToCreate'; -import { getViewSortsToDelete } from '@/views/utils/getViewSortsToDelete'; -import { getViewSortsToUpdate } from '@/views/utils/getViewSortsToUpdate'; -import { mapRecordSortToViewSort } from '@/views/utils/mapRecordSortToViewSort'; -import { useStore } from 'jotai'; -import { useCallback } from 'react'; -import { isDefined } from 'twenty-shared/utils'; - -export const useSaveRecordTableWidgetSortsToView = (viewId: string) => { - const { - performViewSortAPICreate, - performViewSortAPIUpdate, - performViewSortAPIDelete, - } = usePerformViewSortAPIPersist(); - - const store = useStore(); - - const saveRecordTableWidgetSortsToView = useCallback( - async (recordSorts: RecordSort[]) => { - const views = store.get(viewsSelector.atom); - const currentView = views.find((view) => view.id === viewId); - - if (!isDefined(currentView)) { - return; - } - - const currentViewSorts = currentView.viewSorts ?? []; - const newViewSorts = recordSorts.map(mapRecordSortToViewSort); - - const viewSortsToCreate = getViewSortsToCreate( - currentViewSorts, - newViewSorts, - ); - const viewSortsToUpdate = getViewSortsToUpdate( - currentViewSorts, - newViewSorts, - ); - const viewSortsToDelete = getViewSortsToDelete( - currentViewSorts, - newViewSorts, - ); - - await performViewSortAPICreate( - viewSortsToCreate.map((viewSort) => ({ - input: { - id: viewSort.id, - fieldMetadataId: viewSort.fieldMetadataId, - viewId: currentView.id, - direction: viewSort.direction, - }, - })), - ); - - await performViewSortAPIUpdate( - viewSortsToUpdate.map((viewSort) => ({ - input: { - id: viewSort.id, - update: { - direction: viewSort.direction, - }, - }, - })), - ); - - await performViewSortAPIDelete( - viewSortsToDelete.map((viewSort) => ({ - input: { - id: viewSort.id, - }, - })), - ); - }, - [ - store, - viewId, - performViewSortAPICreate, - performViewSortAPIUpdate, - performViewSortAPIDelete, - ], - ); - - return { saveRecordTableWidgetSortsToView }; -}; diff --git a/packages/twenty-front/src/modules/page-layout/widgets/record-table/hooks/useSaveRecordTableWidgetsViewDataOnDashboardSave.ts b/packages/twenty-front/src/modules/page-layout/widgets/record-table/hooks/useSaveRecordTableWidgetsViewDataOnDashboardSave.ts deleted file mode 100644 index 1b47fe1b6f..0000000000 --- a/packages/twenty-front/src/modules/page-layout/widgets/record-table/hooks/useSaveRecordTableWidgetsViewDataOnDashboardSave.ts +++ /dev/null @@ -1,319 +0,0 @@ -import { objectMetadataItemsSelector } from '@/object-metadata/states/objectMetadataItemsSelector'; -import { currentRecordFieldsComponentState } from '@/object-record/record-field/states/currentRecordFieldsComponentState'; -import { currentRecordFilterGroupsComponentState } from '@/object-record/record-filter-group/states/currentRecordFilterGroupsComponentState'; -import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState'; -import { currentRecordSortsComponentState } from '@/object-record/record-sort/states/currentRecordSortsComponentState'; -import { getRecordIndexIdFromObjectNamePluralAndViewId } from '@/object-record/utils/getRecordIndexIdFromObjectNamePluralAndViewId'; -import { pageLayoutDraftComponentState } from '@/page-layout/states/pageLayoutDraftComponentState'; -import { recordTableWidgetViewDraftComponentState } from '@/page-layout/states/recordTableWidgetViewDraftComponentState'; -import { useMapRecordFieldToViewFieldWithCurrentAggregateOperation } from '@/page-layout/widgets/record-table/hooks/useMapRecordFieldToViewFieldWithCurrentAggregateOperation'; -import { computeViewFieldsToCreateAndUpdate } from '@/page-layout/widgets/record-table/utils/computeViewFieldsToCreateAndUpdate'; -import { usePerformViewFieldAPIPersist } from '@/views/hooks/internal/usePerformViewFieldAPIPersist'; -import { usePerformViewFilterAPIPersist } from '@/views/hooks/internal/usePerformViewFilterAPIPersist'; -import { usePerformViewFilterGroupAPIPersist } from '@/views/hooks/internal/usePerformViewFilterGroupAPIPersist'; -import { usePerformViewSortAPIPersist } from '@/views/hooks/internal/usePerformViewSortAPIPersist'; -import { viewsSelector } from '@/views/states/selectors/viewsSelector'; -import { getViewFilterGroupsToCreate } from '@/views/utils/getViewFilterGroupsToCreate'; -import { getViewFilterGroupsToDelete } from '@/views/utils/getViewFilterGroupsToDelete'; -import { getViewFilterGroupsToUpdate } from '@/views/utils/getViewFilterGroupsToUpdate'; -import { getViewFiltersToCreate } from '@/views/utils/getViewFiltersToCreate'; -import { getViewFiltersToDelete } from '@/views/utils/getViewFiltersToDelete'; -import { getViewFiltersToUpdate } from '@/views/utils/getViewFiltersToUpdate'; -import { getViewSortsToCreate } from '@/views/utils/getViewSortsToCreate'; -import { getViewSortsToDelete } from '@/views/utils/getViewSortsToDelete'; -import { getViewSortsToUpdate } from '@/views/utils/getViewSortsToUpdate'; -import { mapRecordFilterGroupToViewFilterGroup } from '@/views/utils/mapRecordFilterGroupToViewFilterGroup'; -import { mapRecordFilterToViewFilter } from '@/views/utils/mapRecordFilterToViewFilter'; -import { mapRecordSortToViewSort } from '@/views/utils/mapRecordSortToViewSort'; -import { useStore } from 'jotai'; -import { useCallback } from 'react'; -import { isDefined } from 'twenty-shared/utils'; -import { WidgetConfigurationType } from '~/generated-metadata/graphql'; - -export const useSaveRecordTableWidgetsViewDataOnDashboardSave = () => { - const { - performViewFilterAPICreate, - performViewFilterAPIUpdate, - performViewFilterAPIDelete, - } = usePerformViewFilterAPIPersist(); - - const { - performViewFilterGroupAPICreate, - performViewFilterGroupAPIUpdate, - performViewFilterGroupAPIDestroy, - } = usePerformViewFilterGroupAPIPersist(); - - const { - performViewSortAPICreate, - performViewSortAPIUpdate, - performViewSortAPIDelete, - } = usePerformViewSortAPIPersist(); - - const { performViewFieldAPICreate, performViewFieldAPIUpdate } = - usePerformViewFieldAPIPersist(); - - const { mapRecordFieldToViewFieldWithCurrentAggregateOperation } = - useMapRecordFieldToViewFieldWithCurrentAggregateOperation(); - - const store = useStore(); - - const saveRecordTableWidgetsViewDataOnDashboardSave = useCallback( - async (pageLayoutId: string) => { - const pageLayoutDraft = store.get( - pageLayoutDraftComponentState.atomFamily({ - instanceId: pageLayoutId, - }), - ); - const views = store.get(viewsSelector.atom); - const objectMetadataItems = store.get(objectMetadataItemsSelector.atom); - const recordTableWidgetViewDraft = store.get( - recordTableWidgetViewDraftComponentState.atomFamily({ - instanceId: pageLayoutId, - }), - ); - - const recordTableWidgets = pageLayoutDraft.tabs.flatMap((tab) => - tab.widgets.filter( - (widget) => - widget.configuration.configurationType === - WidgetConfigurationType.RECORD_TABLE, - ), - ); - - for (const widget of recordTableWidgets) { - const configuration = widget.configuration; - - if (!('viewId' in configuration) || !isDefined(configuration.viewId)) { - continue; - } - - const viewId = configuration.viewId as string; - const currentView = views.find((view) => view.id === viewId); - - if (!isDefined(currentView) || !isDefined(widget.objectMetadataId)) { - continue; - } - - const objectMetadataItem = objectMetadataItems.find( - (item) => item.id === widget.objectMetadataId, - ); - - if (!isDefined(objectMetadataItem)) { - continue; - } - - const recordIndexId = getRecordIndexIdFromObjectNamePluralAndViewId( - objectMetadataItem.namePlural, - viewId, - ); - - const currentRecordFilters = store.get( - currentRecordFiltersComponentState.atomFamily({ - instanceId: recordIndexId, - }), - ); - - const currentRecordFilterGroups = store.get( - currentRecordFilterGroupsComponentState.atomFamily({ - instanceId: recordIndexId, - }), - ); - - const currentRecordSorts = store.get( - currentRecordSortsComponentState.atomFamily({ - instanceId: recordIndexId, - }), - ); - - const newViewFilterGroups = currentRecordFilterGroups.map( - (recordFilterGroup) => - mapRecordFilterGroupToViewFilterGroup({ - recordFilterGroup, - view: currentView, - }), - ); - - const existingViewFilterGroups = currentView.viewFilterGroups ?? []; - - const viewFilterGroupsToCreate = getViewFilterGroupsToCreate( - existingViewFilterGroups, - newViewFilterGroups, - ); - const viewFilterGroupsToUpdate = getViewFilterGroupsToUpdate( - existingViewFilterGroups, - newViewFilterGroups, - ); - const viewFilterGroupsToDelete = getViewFilterGroupsToDelete( - existingViewFilterGroups, - newViewFilterGroups, - ); - - const newViewFilters = currentRecordFilters.map( - mapRecordFilterToViewFilter, - ); - const existingViewFilters = currentView.viewFilters ?? []; - - const viewFiltersToCreate = getViewFiltersToCreate( - existingViewFilters, - newViewFilters, - ); - const viewFiltersToUpdate = getViewFiltersToUpdate( - existingViewFilters, - newViewFilters, - ); - const viewFiltersToDelete = getViewFiltersToDelete( - existingViewFilters, - newViewFilters, - ); - - const newViewSorts = currentRecordSorts.map(mapRecordSortToViewSort); - const existingViewSorts = currentView.viewSorts ?? []; - - const viewSortsToCreate = getViewSortsToCreate( - existingViewSorts, - newViewSorts, - ); - const viewSortsToUpdate = getViewSortsToUpdate( - existingViewSorts, - newViewSorts, - ); - const viewSortsToDelete = getViewSortsToDelete( - existingViewSorts, - newViewSorts, - ); - - const currentRecordFields = store.get( - currentRecordFieldsComponentState.atomFamily({ - instanceId: recordIndexId, - }), - ); - - const recordIndexViewFields = currentRecordFields.map( - mapRecordFieldToViewFieldWithCurrentAggregateOperation, - ); - - const draftSnapshot = recordTableWidgetViewDraft[widget.id]; - const draftViewFields = draftSnapshot?.viewFields ?? []; - const metadataStoreViewFields = - draftViewFields.length > 0 - ? draftViewFields - : (currentView.viewFields ?? []); - - const { viewFieldsToCreate, viewFieldsToUpdate } = - computeViewFieldsToCreateAndUpdate({ - newViewFields: metadataStoreViewFields, - existingViewFields: recordIndexViewFields, - viewId, - }); - - const hasNoRecordTableWidgetChanges = - viewFilterGroupsToCreate.length === 0 && - viewFilterGroupsToUpdate.length === 0 && - viewFilterGroupsToDelete.length === 0 && - viewFiltersToCreate.length === 0 && - viewFiltersToUpdate.length === 0 && - viewFiltersToDelete.length === 0 && - viewSortsToCreate.length === 0 && - viewSortsToUpdate.length === 0 && - viewSortsToDelete.length === 0 && - viewFieldsToCreate.length === 0 && - viewFieldsToUpdate.length === 0; - - if (hasNoRecordTableWidgetChanges) { - continue; - } - - await performViewFilterGroupAPICreate( - viewFilterGroupsToCreate, - currentView, - ); - await performViewFilterGroupAPIUpdate(viewFilterGroupsToUpdate); - - await performViewFilterAPIDelete( - viewFiltersToDelete.map((viewFilter) => ({ - input: { id: viewFilter.id }, - })), - ); - await performViewFilterAPIUpdate( - viewFiltersToUpdate.map((viewFilter) => ({ - input: { - id: viewFilter.id, - update: { - value: viewFilter.value, - operand: viewFilter.operand, - positionInViewFilterGroup: viewFilter.positionInViewFilterGroup, - viewFilterGroupId: viewFilter.viewFilterGroupId, - subFieldName: viewFilter.subFieldName ?? null, - }, - }, - })), - ); - await performViewFilterAPICreate( - viewFiltersToCreate.map((viewFilter) => ({ - input: { - id: viewFilter.id, - fieldMetadataId: viewFilter.fieldMetadataId, - viewId: currentView.id, - value: viewFilter.value, - operand: viewFilter.operand, - viewFilterGroupId: viewFilter.viewFilterGroupId, - positionInViewFilterGroup: viewFilter.positionInViewFilterGroup, - subFieldName: viewFilter.subFieldName ?? null, - }, - })), - ); - await performViewFilterGroupAPIDestroy( - viewFilterGroupsToDelete.map((viewFilterGroup) => viewFilterGroup.id), - ); - - await performViewSortAPICreate( - viewSortsToCreate.map((viewSort) => ({ - input: { - id: viewSort.id, - fieldMetadataId: viewSort.fieldMetadataId, - viewId: currentView.id, - direction: viewSort.direction, - }, - })), - ); - await performViewSortAPIUpdate( - viewSortsToUpdate.map((viewSort) => ({ - input: { - id: viewSort.id, - update: { - direction: viewSort.direction, - }, - }, - })), - ); - await performViewSortAPIDelete( - viewSortsToDelete.map((viewSort) => ({ - input: { id: viewSort.id }, - })), - ); - - await Promise.all([ - performViewFieldAPICreate({ inputs: viewFieldsToCreate }), - performViewFieldAPIUpdate(viewFieldsToUpdate), - ]); - } - }, - [ - store, - mapRecordFieldToViewFieldWithCurrentAggregateOperation, - performViewFieldAPICreate, - performViewFieldAPIUpdate, - performViewFilterAPICreate, - performViewFilterAPIUpdate, - performViewFilterAPIDelete, - performViewFilterGroupAPICreate, - performViewFilterGroupAPIUpdate, - performViewFilterGroupAPIDestroy, - performViewSortAPICreate, - performViewSortAPIUpdate, - performViewSortAPIDelete, - ], - ); - - return { saveRecordTableWidgetsViewDataOnDashboardSave }; -}; diff --git a/packages/twenty-front/src/modules/page-layout/widgets/record-table/hooks/useToggleRecordTableWidgetFieldVisibility.ts b/packages/twenty-front/src/modules/page-layout/widgets/record-table/hooks/useToggleRecordTableWidgetFieldVisibility.ts index 304d169ed6..49ef5450c9 100644 --- a/packages/twenty-front/src/modules/page-layout/widgets/record-table/hooks/useToggleRecordTableWidgetFieldVisibility.ts +++ b/packages/twenty-front/src/modules/page-layout/widgets/record-table/hooks/useToggleRecordTableWidgetFieldVisibility.ts @@ -34,7 +34,10 @@ export const useToggleRecordTableWidgetFieldVisibility = ({ [widgetId]: { ...widgetViewDraft, viewFields: widgetViewDraft.viewFields.map((field) => - field.id === viewFieldId ? { ...field, isVisible } : field, + field.id === viewFieldId || + field.clientRecordFieldId === viewFieldId + ? { ...field, isVisible } + : field, ), }, }; diff --git a/packages/twenty-front/src/modules/page-layout/widgets/record-table/types/RecordTableWidgetViewSnapshot.ts b/packages/twenty-front/src/modules/page-layout/widgets/record-table/types/RecordTableWidgetViewSnapshot.ts index a33e4667f3..7f37685ccb 100644 --- a/packages/twenty-front/src/modules/page-layout/widgets/record-table/types/RecordTableWidgetViewSnapshot.ts +++ b/packages/twenty-front/src/modules/page-layout/widgets/record-table/types/RecordTableWidgetViewSnapshot.ts @@ -1,7 +1,18 @@ import { type FlatView } from '@/metadata-store/types/FlatView'; import { type FlatViewField } from '@/metadata-store/types/FlatViewField'; +import { type FlatViewFilter } from '@/metadata-store/types/FlatViewFilter'; +import { type FlatViewFilterGroup } from '@/metadata-store/types/FlatViewFilterGroup'; +import { type FlatViewSort } from '@/metadata-store/types/FlatViewSort'; + +export type RecordTableWidgetDraftViewField = Omit & { + id?: string; + clientRecordFieldId?: string; +}; export type RecordTableWidgetViewSnapshot = { view: FlatView; - viewFields: FlatViewField[]; + viewFields: RecordTableWidgetDraftViewField[]; + viewFilters: FlatViewFilter[]; + viewFilterGroups: FlatViewFilterGroup[]; + viewSorts: FlatViewSort[]; }; diff --git a/packages/twenty-front/src/modules/page-layout/widgets/record-table/utils/__tests__/computeViewFieldsToCreateAndUpdate.test.ts b/packages/twenty-front/src/modules/page-layout/widgets/record-table/utils/__tests__/computeViewFieldsToCreateAndUpdate.test.ts deleted file mode 100644 index df177fdd48..0000000000 --- a/packages/twenty-front/src/modules/page-layout/widgets/record-table/utils/__tests__/computeViewFieldsToCreateAndUpdate.test.ts +++ /dev/null @@ -1,208 +0,0 @@ -import { v4 } from 'uuid'; - -import { AggregateOperations } from '@/object-record/record-table/constants/AggregateOperations'; -import { computeViewFieldsToCreateAndUpdate } from '@/page-layout/widgets/record-table/utils/computeViewFieldsToCreateAndUpdate'; -import { type ViewField } from '@/views/types/ViewField'; - -const buildViewField = ( - overrides: Partial>, -): Omit => ({ - id: v4(), - fieldMetadataId: v4(), - position: 0, - isActive: true, - isVisible: true, - size: 100, - aggregateOperation: null, - ...overrides, -}); - -const buildExistingViewField = (overrides: Partial): ViewField => ({ - id: v4(), - fieldMetadataId: v4(), - position: 0, - isActive: true, - isVisible: true, - size: 100, - aggregateOperation: null, - ...overrides, -}); - -describe('computeViewFieldsToCreateAndUpdate', () => { - it('adds new fields with no matching fieldMetadataId to viewFieldsToCreate', () => { - const viewId = v4(); - const newViewField = buildViewField({ - position: 0, - isVisible: true, - size: 150, - }); - - const result = computeViewFieldsToCreateAndUpdate({ - newViewFields: [newViewField], - existingViewFields: [], - viewId, - }); - - expect(result.viewFieldsToCreate).toHaveLength(1); - expect(result.viewFieldsToCreate[0]).toEqual({ - id: newViewField.id, - fieldMetadataId: newViewField.fieldMetadataId, - position: newViewField.position, - isVisible: newViewField.isVisible, - size: newViewField.size, - aggregateOperation: newViewField.aggregateOperation, - viewId, - }); - expect(result.viewFieldsToUpdate).toHaveLength(0); - }); - - it('adds existing fields with changed properties to viewFieldsToUpdate', () => { - const viewId = v4(); - const fieldMetadataId = v4(); - const existingFieldId = v4(); - - const existingViewField = buildExistingViewField({ - id: existingFieldId, - fieldMetadataId, - position: 0, - isVisible: true, - size: 100, - aggregateOperation: null, - }); - - const newViewField = buildViewField({ - fieldMetadataId, - position: 2, - isVisible: false, - size: 200, - aggregateOperation: AggregateOperations.COUNT, - }); - - const result = computeViewFieldsToCreateAndUpdate({ - newViewFields: [newViewField], - existingViewFields: [existingViewField], - viewId, - }); - - expect(result.viewFieldsToCreate).toHaveLength(0); - expect(result.viewFieldsToUpdate).toHaveLength(1); - expect(result.viewFieldsToUpdate[0]).toEqual({ - input: { - id: existingFieldId, - update: { - isVisible: false, - position: 2, - size: 200, - aggregateOperation: AggregateOperations.COUNT, - }, - }, - }); - }); - - it('skips existing fields with identical properties', () => { - const viewId = v4(); - const fieldMetadataId = v4(); - - const existingViewField = buildExistingViewField({ - fieldMetadataId, - position: 1, - isVisible: true, - size: 120, - aggregateOperation: AggregateOperations.SUM, - }); - - const newViewField = buildViewField({ - fieldMetadataId, - position: 1, - isVisible: true, - size: 120, - aggregateOperation: AggregateOperations.SUM, - }); - - const result = computeViewFieldsToCreateAndUpdate({ - newViewFields: [newViewField], - existingViewFields: [existingViewField], - viewId, - }); - - expect(result.viewFieldsToCreate).toHaveLength(0); - expect(result.viewFieldsToUpdate).toHaveLength(0); - }); - - it('returns empty arrays when newViewFields is empty', () => { - const viewId = v4(); - - const existingViewField = buildExistingViewField({}); - - const result = computeViewFieldsToCreateAndUpdate({ - newViewFields: [], - existingViewFields: [existingViewField], - viewId, - }); - - expect(result.viewFieldsToCreate).toHaveLength(0); - expect(result.viewFieldsToUpdate).toHaveLength(0); - }); - - it('handles a mix of creates and updates in a single call', () => { - const viewId = v4(); - const existingFieldMetadataId = v4(); - const newFieldMetadataId = v4(); - const existingFieldId = v4(); - - const existingViewField = buildExistingViewField({ - id: existingFieldId, - fieldMetadataId: existingFieldMetadataId, - position: 0, - isVisible: true, - size: 100, - aggregateOperation: null, - }); - - const updatedNewViewField = buildViewField({ - fieldMetadataId: existingFieldMetadataId, - position: 5, - isVisible: false, - size: 100, - aggregateOperation: null, - }); - - const brandNewViewField = buildViewField({ - fieldMetadataId: newFieldMetadataId, - position: 1, - isVisible: true, - size: 80, - aggregateOperation: AggregateOperations.MIN, - }); - - const result = computeViewFieldsToCreateAndUpdate({ - newViewFields: [updatedNewViewField, brandNewViewField], - existingViewFields: [existingViewField], - viewId, - }); - - expect(result.viewFieldsToCreate).toHaveLength(1); - expect(result.viewFieldsToCreate[0]).toEqual({ - id: brandNewViewField.id, - fieldMetadataId: newFieldMetadataId, - position: 1, - isVisible: true, - size: 80, - aggregateOperation: AggregateOperations.MIN, - viewId, - }); - - expect(result.viewFieldsToUpdate).toHaveLength(1); - expect(result.viewFieldsToUpdate[0]).toEqual({ - input: { - id: existingFieldId, - update: { - isVisible: false, - position: 5, - size: 100, - aggregateOperation: null, - }, - }, - }); - }); -}); diff --git a/packages/twenty-front/src/modules/page-layout/widgets/record-table/utils/computeViewFieldsToCreateAndUpdate.ts b/packages/twenty-front/src/modules/page-layout/widgets/record-table/utils/computeViewFieldsToCreateAndUpdate.ts deleted file mode 100644 index fc2b1c8a5e..0000000000 --- a/packages/twenty-front/src/modules/page-layout/widgets/record-table/utils/computeViewFieldsToCreateAndUpdate.ts +++ /dev/null @@ -1,87 +0,0 @@ -import { type ViewField } from '@/views/types/ViewField'; -import { isDefined } from 'twenty-shared/utils'; -import { - type CreateViewFieldInput, - type UpdateViewFieldMutationVariables, -} from '~/generated-metadata/graphql'; -import { isDeeplyEqual } from '~/utils/isDeeplyEqual'; - -type ViewFieldsToCreateAndUpdate = { - viewFieldsToCreate: CreateViewFieldInput[]; - viewFieldsToUpdate: UpdateViewFieldMutationVariables[]; -}; - -export const computeViewFieldsToCreateAndUpdate = ({ - newViewFields, - existingViewFields, - viewId, -}: { - newViewFields: Omit[]; - existingViewFields: ViewField[]; - viewId: string; -}): ViewFieldsToCreateAndUpdate => { - return newViewFields.reduce( - (accumulator, newViewField) => { - const existingViewField = existingViewFields.find( - (existingField) => - existingField.fieldMetadataId === newViewField.fieldMetadataId, - ); - - if (!isDefined(existingViewField)) { - return { - viewFieldsToCreate: [ - ...accumulator.viewFieldsToCreate, - { - id: newViewField.id, - fieldMetadataId: newViewField.fieldMetadataId, - position: newViewField.position, - isVisible: newViewField.isVisible, - size: newViewField.size, - aggregateOperation: newViewField.aggregateOperation, - viewId, - }, - ], - viewFieldsToUpdate: accumulator.viewFieldsToUpdate, - }; - } - - if ( - isDeeplyEqual( - { - position: existingViewField.position, - size: existingViewField.size, - isVisible: existingViewField.isVisible, - aggregateOperation: existingViewField.aggregateOperation, - }, - { - position: newViewField.position, - size: newViewField.size, - isVisible: newViewField.isVisible, - aggregateOperation: newViewField.aggregateOperation, - }, - ) - ) { - return accumulator; - } - - return { - viewFieldsToCreate: accumulator.viewFieldsToCreate, - viewFieldsToUpdate: [ - ...accumulator.viewFieldsToUpdate, - { - input: { - id: existingViewField.id, - update: { - isVisible: newViewField.isVisible, - position: newViewField.position, - size: newViewField.size, - aggregateOperation: newViewField.aggregateOperation, - }, - }, - }, - ], - }; - }, - { viewFieldsToCreate: [], viewFieldsToUpdate: [] }, - ); -}; diff --git a/packages/twenty-front/src/modules/page-layout/widgets/record-table/utils/constructViewFromRecordTableWidgetViewSnapshot.ts b/packages/twenty-front/src/modules/page-layout/widgets/record-table/utils/constructViewFromRecordTableWidgetViewSnapshot.ts index 2de2adfbee..77e6313e2e 100644 --- a/packages/twenty-front/src/modules/page-layout/widgets/record-table/utils/constructViewFromRecordTableWidgetViewSnapshot.ts +++ b/packages/twenty-front/src/modules/page-layout/widgets/record-table/utils/constructViewFromRecordTableWidgetViewSnapshot.ts @@ -1,13 +1,22 @@ import { type RecordTableWidgetViewSnapshot } from '@/page-layout/widgets/record-table/types/RecordTableWidgetViewSnapshot'; +import { getRecordTableWidgetDraftViewFieldClientId } from '@/page-layout/widgets/record-table/utils/getRecordTableWidgetDraftViewFieldClientId'; import { type View } from '@/views/types/View'; export const constructViewFromRecordTableWidgetViewSnapshot = ( snapshot: RecordTableWidgetViewSnapshot, ): View => ({ ...snapshot.view, - viewFields: snapshot.viewFields, - viewFilters: [], - viewSorts: [], + viewFields: snapshot.viewFields.map((draftField) => { + const { clientRecordFieldId: _clientRecordFieldId, ...fieldRest } = + draftField; + + return { + ...fieldRest, + id: getRecordTableWidgetDraftViewFieldClientId(draftField), + }; + }), + viewFilters: snapshot.viewFilters, + viewFilterGroups: snapshot.viewFilterGroups, + viewSorts: snapshot.viewSorts, viewGroups: [], - viewFilterGroups: [], }); diff --git a/packages/twenty-front/src/modules/page-layout/widgets/record-table/utils/getRecordTableWidgetDraftViewFieldClientId.ts b/packages/twenty-front/src/modules/page-layout/widgets/record-table/utils/getRecordTableWidgetDraftViewFieldClientId.ts new file mode 100644 index 0000000000..7657777e7e --- /dev/null +++ b/packages/twenty-front/src/modules/page-layout/widgets/record-table/utils/getRecordTableWidgetDraftViewFieldClientId.ts @@ -0,0 +1,21 @@ +import { type RecordTableWidgetDraftViewField } from '@/page-layout/widgets/record-table/types/RecordTableWidgetViewSnapshot'; +import { isDefined } from 'twenty-shared/utils'; + +type RecordTableWidgetDraftViewFieldIdentity = Pick< + RecordTableWidgetDraftViewField, + 'id' | 'clientRecordFieldId' +>; + +export const getRecordTableWidgetDraftViewFieldClientId = ( + field: RecordTableWidgetDraftViewFieldIdentity, +): string => { + if (isDefined(field.id)) { + return field.id; + } + + if (isDefined(field.clientRecordFieldId)) { + return field.clientRecordFieldId; + } + + return ''; +}; diff --git a/packages/twenty-front/src/modules/side-panel/pages/page-layout/components/dashboard/SidePanelDashboardRecordTableSettings.tsx b/packages/twenty-front/src/modules/side-panel/pages/page-layout/components/dashboard/SidePanelDashboardRecordTableSettings.tsx index a33615395c..e5270a4dd8 100644 --- a/packages/twenty-front/src/modules/side-panel/pages/page-layout/components/dashboard/SidePanelDashboardRecordTableSettings.tsx +++ b/packages/twenty-front/src/modules/side-panel/pages/page-layout/components/dashboard/SidePanelDashboardRecordTableSettings.tsx @@ -1,5 +1,6 @@ import { CommandMenuItem } from '@/command-menu/components/CommandMenuItem'; import { CommandMenuItemDropdown } from '@/command-menu/components/CommandMenuItemDropdown'; +import { useRecordTableWidgetFieldCallbacks } from '@/page-layout/widgets/record-table/hooks/useRecordTableWidgetFieldCallbacks'; import { WidgetComponentInstanceContext } from '@/page-layout/widgets/states/contexts/WidgetComponentInstanceContext'; import { SidePanelGroup } from '@/side-panel/components/SidePanelGroup'; import { SidePanelList } from '@/side-panel/components/SidePanelList'; @@ -65,6 +66,13 @@ export const SidePanelDashboardRecordTableSettings = () => { viewId, }); + const { handleFieldUpdated, handleFieldCreated } = + useRecordTableWidgetFieldCallbacks({ + pageLayoutId, + widgetId: widgetInEditMode?.id ?? '', + viewId: viewId ?? '', + }); + if (!isDefined(widgetInEditMode)) { return null; } @@ -136,6 +144,8 @@ export const SidePanelDashboardRecordTableSettings = () => { } dropdownPlacement="bottom-end" diff --git a/packages/twenty-front/src/modules/side-panel/pages/page-layout/components/record-table-settings/RecordTableFieldsDropdownContent.tsx b/packages/twenty-front/src/modules/side-panel/pages/page-layout/components/record-table-settings/RecordTableFieldsDropdownContent.tsx index da7338eb16..c08c8f3349 100644 --- a/packages/twenty-front/src/modules/side-panel/pages/page-layout/components/record-table-settings/RecordTableFieldsDropdownContent.tsx +++ b/packages/twenty-front/src/modules/side-panel/pages/page-layout/components/record-table-settings/RecordTableFieldsDropdownContent.tsx @@ -1,4 +1,5 @@ import { useObjectMetadataItemById } from '@/object-metadata/hooks/useObjectMetadataItemById'; +import { type RecordField } from '@/object-record/record-field/types/RecordField'; import { RecordFieldsComponentInstanceContext } from '@/object-record/record-field/states/context/RecordFieldsComponentInstanceContext'; import { getRecordIndexIdFromObjectNamePluralAndViewId } from '@/object-record/utils/getRecordIndexIdFromObjectNamePluralAndViewId'; import { RecordTableFieldsDropdownHiddenFieldsContent } from '@/side-panel/pages/page-layout/components/record-table-settings/RecordTableFieldsDropdownHiddenFieldsContent'; @@ -8,11 +9,18 @@ import { useState } from 'react'; type RecordTableFieldsDropdownContentProps = { viewId: string; objectMetadataId: string; + onFieldUpdated?: ( + viewFieldId: string, + update: Partial<{ position: number; isVisible: boolean }>, + ) => void; + onFieldCreated?: (recordField: RecordField) => void; }; export const RecordTableFieldsDropdownContent = ({ viewId, objectMetadataId, + onFieldUpdated, + onFieldCreated, }: RecordTableFieldsDropdownContentProps) => { const { objectMetadataItem } = useObjectMetadataItemById({ objectId: objectMetadataId, @@ -34,12 +42,15 @@ export const RecordTableFieldsDropdownContent = ({ objectMetadataId={objectMetadataId} recordIndexId={recordIndexId} onBack={() => setShowHiddenFields(false)} + onFieldUpdated={onFieldUpdated} + onFieldCreated={onFieldCreated} /> ) : ( setShowHiddenFields(true)} + onFieldUpdated={onFieldUpdated} /> )} diff --git a/packages/twenty-front/src/modules/side-panel/pages/page-layout/components/record-table-settings/RecordTableFieldsDropdownHiddenFieldsContent.tsx b/packages/twenty-front/src/modules/side-panel/pages/page-layout/components/record-table-settings/RecordTableFieldsDropdownHiddenFieldsContent.tsx index 581872bf9a..d6a1e48948 100644 --- a/packages/twenty-front/src/modules/side-panel/pages/page-layout/components/record-table-settings/RecordTableFieldsDropdownHiddenFieldsContent.tsx +++ b/packages/twenty-front/src/modules/side-panel/pages/page-layout/components/record-table-settings/RecordTableFieldsDropdownHiddenFieldsContent.tsx @@ -20,12 +20,19 @@ type RecordTableFieldsDropdownHiddenFieldsContentProps = { objectMetadataId: string; recordIndexId: string; onBack: () => void; + onFieldUpdated?: ( + viewFieldId: string, + update: Partial<{ isVisible: boolean }>, + ) => void; + onFieldCreated?: (recordField: RecordField) => void; }; export const RecordTableFieldsDropdownHiddenFieldsContent = ({ objectMetadataId, recordIndexId, onBack, + onFieldUpdated, + onFieldCreated, }: RecordTableFieldsDropdownHiddenFieldsContentProps) => { const { objectMetadataItem } = useObjectMetadataItemById({ objectId: objectMetadataId, @@ -61,6 +68,7 @@ export const RecordTableFieldsDropdownHiddenFieldsContent = ({ if (isDefined(existingRecordField)) { updateRecordField(fieldMetadataId, { isVisible: true }); + onFieldUpdated?.(existingRecordField.id, { isVisible: true }); } else { const lastPosition = currentRecordFields.toSorted(sortByProperty('position', 'desc'))?.[0] @@ -75,6 +83,7 @@ export const RecordTableFieldsDropdownHiddenFieldsContent = ({ }; upsertRecordField(newRecordField); + onFieldCreated?.(newRecordField); } }; diff --git a/packages/twenty-front/src/modules/side-panel/pages/page-layout/components/record-table-settings/RecordTableFieldsDropdownVisibleFieldsContent.tsx b/packages/twenty-front/src/modules/side-panel/pages/page-layout/components/record-table-settings/RecordTableFieldsDropdownVisibleFieldsContent.tsx index 5c6d7a0c43..6770535803 100644 --- a/packages/twenty-front/src/modules/side-panel/pages/page-layout/components/record-table-settings/RecordTableFieldsDropdownVisibleFieldsContent.tsx +++ b/packages/twenty-front/src/modules/side-panel/pages/page-layout/components/record-table-settings/RecordTableFieldsDropdownVisibleFieldsContent.tsx @@ -21,12 +21,17 @@ type RecordTableFieldsDropdownVisibleFieldsContentProps = { objectMetadataId: string; recordIndexId: string; onShowHiddenFields: () => void; + onFieldUpdated?: ( + viewFieldId: string, + update: Partial<{ position: number; isVisible: boolean }>, + ) => void; }; export const RecordTableFieldsDropdownVisibleFieldsContent = ({ objectMetadataId, recordIndexId, onShowHiddenFields, + onFieldUpdated, }: RecordTableFieldsDropdownVisibleFieldsContentProps) => { const { objectMetadataItem } = useObjectMetadataItemById({ objectId: objectMetadataId, @@ -71,14 +76,24 @@ export const RecordTableFieldsDropdownVisibleFieldsContent = ({ return; } - reorderVisibleRecordFields({ + const updatedField = reorderVisibleRecordFields({ fromIndex: result.source.index - 1, toIndex: result.destination.index - 1, }); + + if (isDefined(updatedField)) { + onFieldUpdated?.(updatedField.id, { position: updatedField.position }); + } }; const handleHideField = (fieldMetadataId: string) => { - updateRecordField(fieldMetadataId, { isVisible: false }); + const updatedField = updateRecordField(fieldMetadataId, { + isVisible: false, + }); + + if (isDefined(updatedField)) { + onFieldUpdated?.(updatedField.id, { isVisible: false }); + } }; return ( diff --git a/packages/twenty-front/src/modules/side-panel/pages/page-layout/components/record-table-settings/RecordTableSettingsFilters.tsx b/packages/twenty-front/src/modules/side-panel/pages/page-layout/components/record-table-settings/RecordTableSettingsFilters.tsx index bb45cd073b..daa3d4d101 100644 --- a/packages/twenty-front/src/modules/side-panel/pages/page-layout/components/record-table-settings/RecordTableSettingsFilters.tsx +++ b/packages/twenty-front/src/modules/side-panel/pages/page-layout/components/record-table-settings/RecordTableSettingsFilters.tsx @@ -3,6 +3,7 @@ import { AdvancedFilterSidePanelContainer } from '@/object-record/advanced-filte import { RecordFilterGroupsComponentInstanceContext } from '@/object-record/record-filter-group/states/context/RecordFilterGroupsComponentInstanceContext'; import { RecordFiltersComponentInstanceContext } from '@/object-record/record-filter/states/context/RecordFiltersComponentInstanceContext'; import { getRecordIndexIdFromObjectNamePluralAndViewId } from '@/object-record/utils/getRecordIndexIdFromObjectNamePluralAndViewId'; +import { useRecordTableWidgetFilterCallbacks } from '@/page-layout/widgets/record-table/hooks/useRecordTableWidgetFilterCallbacks'; import { useRecordTableWidgetViewForDisplay } from '@/page-layout/widgets/record-table/hooks/useRecordTableWidgetViewForDisplay'; import { RecordTableSettingsFiltersInitializeStateEffect } from '@/side-panel/pages/page-layout/components/record-table-settings/RecordTableSettingsFiltersInitializeStateEffect'; import { InputLabel } from '@/ui/input/components/InputLabel'; @@ -45,6 +46,13 @@ export const RecordTableSettingsFilters = ({ viewId, ); + const { handleFilterUpdate } = useRecordTableWidgetFilterCallbacks({ + pageLayoutId, + widgetId, + viewId, + recordIndexId, + }); + if (!isDefined(view)) { return null; } @@ -61,6 +69,7 @@ export const RecordTableSettingsFilters = ({ diff --git a/packages/twenty-front/src/modules/side-panel/pages/page-layout/components/record-table-settings/RecordTableSettingsSortsContent.tsx b/packages/twenty-front/src/modules/side-panel/pages/page-layout/components/record-table-settings/RecordTableSettingsSortsContent.tsx index f5d49db046..76ed6493c8 100644 --- a/packages/twenty-front/src/modules/side-panel/pages/page-layout/components/record-table-settings/RecordTableSettingsSortsContent.tsx +++ b/packages/twenty-front/src/modules/side-panel/pages/page-layout/components/record-table-settings/RecordTableSettingsSortsContent.tsx @@ -24,11 +24,13 @@ const StyledAddButtonContainer = styled.div` type RecordTableSettingsSortsContentProps = { sortableFieldOptions: Array>; directionOptions: Array>; + onUpdate?: () => void; }; export const RecordTableSettingsSortsContent = ({ sortableFieldOptions, directionOptions, + onUpdate, }: RecordTableSettingsSortsContentProps) => { const [currentRecordSorts, setCurrentRecordSorts] = useAtomComponentState( currentRecordSortsComponentState, @@ -48,12 +50,14 @@ export const RecordTableSettingsSortsContent = ({ }; setCurrentRecordSorts([...currentRecordSorts, newSort]); + onUpdate?.(); }; const handleRemoveSort = (sortId: string) => { setCurrentRecordSorts( currentRecordSorts.filter((sort) => sort.id !== sortId), ); + onUpdate?.(); }; const handleFieldChange = (sortId: string, fieldMetadataId: string) => { @@ -62,6 +66,7 @@ export const RecordTableSettingsSortsContent = ({ sort.id === sortId ? { ...sort, fieldMetadataId } : sort, ), ); + onUpdate?.(); }; const handleDirectionChange = ( @@ -73,6 +78,7 @@ export const RecordTableSettingsSortsContent = ({ sort.id === sortId ? { ...sort, direction } : sort, ), ); + onUpdate?.(); }; return ( diff --git a/packages/twenty-front/src/modules/side-panel/pages/page-layout/hooks/useRecordTableSettingsDescriptions.ts b/packages/twenty-front/src/modules/side-panel/pages/page-layout/hooks/useRecordTableSettingsDescriptions.ts index 0bc6450c82..9f36050342 100644 --- a/packages/twenty-front/src/modules/side-panel/pages/page-layout/hooks/useRecordTableSettingsDescriptions.ts +++ b/packages/twenty-front/src/modules/side-panel/pages/page-layout/hooks/useRecordTableSettingsDescriptions.ts @@ -1,8 +1,9 @@ import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems'; -import { currentRecordFieldsComponentState } from '@/object-record/record-field/states/currentRecordFieldsComponentState'; +import { visibleRecordFieldsComponentSelector } from '@/object-record/record-field/states/visibleRecordFieldsComponentSelector'; import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState'; import { currentRecordSortsComponentState } from '@/object-record/record-sort/states/currentRecordSortsComponentState'; import { getRecordIndexIdFromObjectNamePluralAndViewId } from '@/object-record/utils/getRecordIndexIdFromObjectNamePluralAndViewId'; +import { useAtomComponentSelectorValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentSelectorValue'; import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue'; import { useViewById } from '@/views/hooks/useViewById'; import { t } from '@lingui/core/macro'; @@ -45,22 +46,20 @@ export const useRecordTableSettingsDescriptions = ({ recordIndexId, ); - const currentRecordFields = useAtomComponentStateValue( - currentRecordFieldsComponentState, + const visibleRecordFields = useAtomComponentSelectorValue( + visibleRecordFieldsComponentSelector, recordIndexId, ); const hasViewId = isDefined(viewId); const activeVisibleFieldLabels: Array = hasViewId - ? currentRecordFields - .filter((field) => field.isVisible) - .map( - (field) => - sourceObjectMetadataItem?.fields.find( - (metaField) => metaField.id === field.fieldMetadataItemId, - )?.label, - ) + ? visibleRecordFields.map( + (field) => + sourceObjectMetadataItem?.fields.find( + (metaField) => metaField.id === field.fieldMetadataItemId, + )?.label, + ) : []; const activeFilterLabels: Array = diff --git a/packages/twenty-server/src/engine/metadata-modules/view/dtos/inputs/upsert-view-widget-view-field.input.ts b/packages/twenty-server/src/engine/metadata-modules/view/dtos/inputs/upsert-view-widget-view-field.input.ts new file mode 100644 index 0000000000..e8a1f9af43 --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/view/dtos/inputs/upsert-view-widget-view-field.input.ts @@ -0,0 +1,40 @@ +import { Field, InputType } from '@nestjs/graphql'; + +import { IsBoolean, IsNumber, IsOptional, IsUUID } from 'class-validator'; + +import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars'; +import { AtLeastOneOf } from 'src/engine/metadata-modules/view-field-group/dtos/validators/at-least-one-of.validator'; + +@InputType() +@AtLeastOneOf(['viewFieldId', 'fieldMetadataId']) +export class UpsertViewWidgetViewFieldInput { + @IsOptional() + @IsUUID() + @Field(() => UUIDScalarType, { + nullable: true, + description: 'The id of an existing view field to update.', + }) + viewFieldId?: string; + + @IsOptional() + @IsUUID() + @Field(() => UUIDScalarType, { + nullable: true, + description: + 'The field metadata id. Used to create a new view field when viewFieldId is not provided.', + }) + fieldMetadataId?: string; + + @IsBoolean() + @Field() + isVisible: boolean; + + @IsNumber() + @Field() + position: number; + + @IsOptional() + @IsNumber() + @Field({ nullable: true }) + size?: number; +} diff --git a/packages/twenty-server/src/engine/metadata-modules/view/dtos/inputs/upsert-view-widget-view-filter-group.input.ts b/packages/twenty-server/src/engine/metadata-modules/view/dtos/inputs/upsert-view-widget-view-filter-group.input.ts new file mode 100644 index 0000000000..8003a02924 --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/view/dtos/inputs/upsert-view-widget-view-filter-group.input.ts @@ -0,0 +1,30 @@ +import { Field, InputType } from '@nestjs/graphql'; + +import { IsNumber, IsOptional, IsUUID } from 'class-validator'; +import { ViewFilterGroupLogicalOperator } from 'twenty-shared/types'; + +import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars'; + +@InputType() +export class UpsertViewWidgetViewFilterGroupInput { + @IsOptional() + @IsUUID() + @Field(() => UUIDScalarType, { nullable: true }) + id?: string; + + @IsOptional() + @IsUUID() + @Field(() => UUIDScalarType, { nullable: true }) + parentViewFilterGroupId?: string; + + @Field(() => ViewFilterGroupLogicalOperator, { + nullable: true, + defaultValue: ViewFilterGroupLogicalOperator.AND, + }) + logicalOperator?: ViewFilterGroupLogicalOperator; + + @IsOptional() + @IsNumber() + @Field({ nullable: true }) + positionInViewFilterGroup?: number; +} diff --git a/packages/twenty-server/src/engine/metadata-modules/view/dtos/inputs/upsert-view-widget-view-filter.input.ts b/packages/twenty-server/src/engine/metadata-modules/view/dtos/inputs/upsert-view-widget-view-filter.input.ts new file mode 100644 index 0000000000..195dcf22de --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/view/dtos/inputs/upsert-view-widget-view-filter.input.ts @@ -0,0 +1,51 @@ +import { Field, InputType } from '@nestjs/graphql'; + +import { + IsDefined, + IsEnum, + IsNumber, + IsOptional, + IsString, + IsUUID, +} from 'class-validator'; +import GraphQLJSON from 'graphql-type-json'; +import { ViewFilterOperand } from 'twenty-shared/types'; + +import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars'; +import { type ViewFilterValue } from 'src/engine/metadata-modules/view-filter/types/view-filter-value.type'; + +@InputType() +export class UpsertViewWidgetViewFilterInput { + @IsOptional() + @IsUUID() + @Field(() => UUIDScalarType, { nullable: true }) + id?: string; + + @IsUUID() + @Field(() => UUIDScalarType, { nullable: false }) + fieldMetadataId: string; + + @IsOptional() + @IsEnum(ViewFilterOperand) + @Field({ nullable: true, defaultValue: ViewFilterOperand.CONTAINS }) + operand?: ViewFilterOperand; + + @IsDefined() + @Field(() => GraphQLJSON, { nullable: false }) + value: ViewFilterValue; + + @IsOptional() + @IsUUID() + @Field(() => UUIDScalarType, { nullable: true }) + viewFilterGroupId?: string; + + @IsOptional() + @IsNumber() + @Field({ nullable: true }) + positionInViewFilterGroup?: number; + + @IsOptional() + @IsString() + @Field({ nullable: true }) + subFieldName?: string; +} diff --git a/packages/twenty-server/src/engine/metadata-modules/view/dtos/inputs/upsert-view-widget-view-sort.input.ts b/packages/twenty-server/src/engine/metadata-modules/view/dtos/inputs/upsert-view-widget-view-sort.input.ts new file mode 100644 index 0000000000..af6e8e1a03 --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/view/dtos/inputs/upsert-view-widget-view-sort.input.ts @@ -0,0 +1,26 @@ +import { Field, InputType } from '@nestjs/graphql'; + +import { IsEnum, IsOptional, IsUUID } from 'class-validator'; +import { ViewSortDirection } from 'twenty-shared/types'; + +import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars'; + +@InputType() +export class UpsertViewWidgetViewSortInput { + @IsOptional() + @IsUUID() + @Field(() => UUIDScalarType, { nullable: true }) + id?: string; + + @IsUUID() + @Field(() => UUIDScalarType, { nullable: false }) + fieldMetadataId: string; + + @IsOptional() + @IsEnum(ViewSortDirection) + @Field(() => ViewSortDirection, { + nullable: true, + defaultValue: ViewSortDirection.ASC, + }) + direction?: ViewSortDirection; +} diff --git a/packages/twenty-server/src/engine/metadata-modules/view/dtos/inputs/upsert-view-widget.input.ts b/packages/twenty-server/src/engine/metadata-modules/view/dtos/inputs/upsert-view-widget.input.ts new file mode 100644 index 0000000000..3dd8464391 --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/view/dtos/inputs/upsert-view-widget.input.ts @@ -0,0 +1,61 @@ +import { Field, InputType } from '@nestjs/graphql'; + +import { Type } from 'class-transformer'; +import { + IsNotEmpty, + IsOptional, + IsUUID, + ValidateNested, +} from 'class-validator'; + +import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars'; +import { UpsertViewWidgetViewFieldInput } from 'src/engine/metadata-modules/view/dtos/inputs/upsert-view-widget-view-field.input'; +import { UpsertViewWidgetViewFilterGroupInput } from 'src/engine/metadata-modules/view/dtos/inputs/upsert-view-widget-view-filter-group.input'; +import { UpsertViewWidgetViewFilterInput } from 'src/engine/metadata-modules/view/dtos/inputs/upsert-view-widget-view-filter.input'; +import { UpsertViewWidgetViewSortInput } from 'src/engine/metadata-modules/view/dtos/inputs/upsert-view-widget-view-sort.input'; + +@InputType() +export class UpsertViewWidgetInput { + @IsUUID() + @IsNotEmpty() + @Field(() => UUIDScalarType, { + description: 'The id of the view widget (page layout widget).', + }) + widgetId: string; + + @IsOptional() + @ValidateNested({ each: true }) + @Type(() => UpsertViewWidgetViewFieldInput) + @Field(() => [UpsertViewWidgetViewFieldInput], { + nullable: true, + description: 'The view fields to upsert.', + }) + viewFields?: UpsertViewWidgetViewFieldInput[]; + + @IsOptional() + @ValidateNested({ each: true }) + @Type(() => UpsertViewWidgetViewFilterInput) + @Field(() => [UpsertViewWidgetViewFilterInput], { + nullable: true, + description: 'The view filters to upsert.', + }) + viewFilters?: UpsertViewWidgetViewFilterInput[]; + + @IsOptional() + @ValidateNested({ each: true }) + @Type(() => UpsertViewWidgetViewFilterGroupInput) + @Field(() => [UpsertViewWidgetViewFilterGroupInput], { + nullable: true, + description: 'The view filter groups to upsert.', + }) + viewFilterGroups?: UpsertViewWidgetViewFilterGroupInput[]; + + @IsOptional() + @ValidateNested({ each: true }) + @Type(() => UpsertViewWidgetViewSortInput) + @Field(() => [UpsertViewWidgetViewSortInput], { + nullable: true, + description: 'The view sorts to upsert.', + }) + viewSorts?: UpsertViewWidgetViewSortInput[]; +} diff --git a/packages/twenty-server/src/engine/metadata-modules/view/exceptions/view.exception.ts b/packages/twenty-server/src/engine/metadata-modules/view/exceptions/view.exception.ts index 3a7b92e503..d289a50c13 100644 --- a/packages/twenty-server/src/engine/metadata-modules/view/exceptions/view.exception.ts +++ b/packages/twenty-server/src/engine/metadata-modules/view/exceptions/view.exception.ts @@ -21,6 +21,7 @@ export enum ViewExceptionCode { INVALID_VIEW_DATA = 'INVALID_VIEW_DATA', VIEW_CREATE_PERMISSION_DENIED = 'VIEW_CREATE_PERMISSION_DENIED', VIEW_MODIFY_PERMISSION_DENIED = 'VIEW_MODIFY_PERMISSION_DENIED', + VIEW_WIDGET_NOT_FOUND = 'VIEW_WIDGET_NOT_FOUND', } export enum ViewExceptionMessageKey { diff --git a/packages/twenty-server/src/engine/metadata-modules/view/resolvers/view.resolver.ts b/packages/twenty-server/src/engine/metadata-modules/view/resolvers/view.resolver.ts index a5b86ffd18..7b14881248 100644 --- a/packages/twenty-server/src/engine/metadata-modules/view/resolvers/view.resolver.ts +++ b/packages/twenty-server/src/engine/metadata-modules/view/resolvers/view.resolver.ts @@ -1,4 +1,4 @@ -import { UseFilters, UseGuards } from '@nestjs/common'; +import { UseFilters, UseGuards, UsePipes } from '@nestjs/common'; import { Args, Context, @@ -8,10 +8,12 @@ import { ResolveField, } from '@nestjs/graphql'; +import { PermissionFlagType } from 'twenty-shared/constants'; import { ViewType, ViewVisibility } from 'twenty-shared/types'; import { isDefined } from 'twenty-shared/utils'; import { MetadataResolver } from 'src/engine/api/graphql/graphql-config/decorators/metadata-resolver.decorator'; +import { ResolverValidationPipe } from 'src/engine/core-modules/graphql/pipes/resolver-validation.pipe'; import { I18nService } from 'src/engine/core-modules/i18n/i18n.service'; import { type I18nContext } from 'src/engine/core-modules/i18n/types/i18n-context.type'; import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity'; @@ -20,6 +22,7 @@ import { AuthUserWorkspaceId } from 'src/engine/decorators/auth/auth-user-worksp import { AuthWorkspace } from 'src/engine/decorators/auth/auth-workspace.decorator'; import { CustomPermissionGuard } from 'src/engine/guards/custom-permission.guard'; import { NoPermissionGuard } from 'src/engine/guards/no-permission.guard'; +import { SettingsPermissionGuard } from 'src/engine/guards/settings-permission.guard'; import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard'; import { resolveObjectMetadataStandardOverride } from 'src/engine/metadata-modules/object-metadata/utils/resolve-object-metadata-standard-override.util'; import { ViewFieldGroupDTO } from 'src/engine/metadata-modules/view-field-group/dtos/view-field-group.dto'; @@ -34,7 +37,10 @@ import { UpdateViewPermissionGuard } from 'src/engine/metadata-modules/view-perm import { ViewSortDTO } from 'src/engine/metadata-modules/view-sort/dtos/view-sort.dto'; import { CreateViewInput } from 'src/engine/metadata-modules/view/dtos/inputs/create-view.input'; import { UpdateViewInput } from 'src/engine/metadata-modules/view/dtos/inputs/update-view.input'; +import { UpsertViewWidgetInput } from 'src/engine/metadata-modules/view/dtos/inputs/upsert-view-widget.input'; import { ViewDTO } from 'src/engine/metadata-modules/view/dtos/view.dto'; +import { type ViewEntity } from 'src/engine/metadata-modules/view/entities/view.entity'; +import { ViewWidgetUpsertService } from 'src/engine/metadata-modules/view/services/view-widget-upsert.service'; import { ViewService } from 'src/engine/metadata-modules/view/services/view.service'; import { ViewGraphqlApiExceptionFilter } from 'src/engine/metadata-modules/view/utils/view-graphql-api-exception.filter'; @@ -44,6 +50,7 @@ import { ViewGraphqlApiExceptionFilter } from 'src/engine/metadata-modules/view/ export class ViewResolver { constructor( private readonly viewService: ViewService, + private readonly viewWidgetUpsertService: ViewWidgetUpsertService, private readonly i18nService: I18nService, ) {} @@ -197,6 +204,19 @@ export class ViewResolver { return isDefined(deletedView); } + @Mutation(() => ViewDTO) + @UseGuards(SettingsPermissionGuard(PermissionFlagType.LAYOUTS)) + @UsePipes(ResolverValidationPipe) + async upsertViewWidget( + @Args('input') input: UpsertViewWidgetInput, + @AuthWorkspace() { id: workspaceId }: WorkspaceEntity, + ): Promise { + return await this.viewWidgetUpsertService.upsertViewWidget({ + input, + workspaceId, + }); + } + @ResolveField(() => [ViewFieldDTO]) async viewFields( @Parent() view: ViewDTO, diff --git a/packages/twenty-server/src/engine/metadata-modules/view/services/view-widget-upsert.service.ts b/packages/twenty-server/src/engine/metadata-modules/view/services/view-widget-upsert.service.ts new file mode 100644 index 0000000000..3270c9d561 --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/view/services/view-widget-upsert.service.ts @@ -0,0 +1,859 @@ +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; + +import { t } from '@lingui/core/macro'; +import { + ViewFilterGroupLogicalOperator, + ViewFilterOperand, + ViewSortDirection, +} from 'twenty-shared/types'; +import { isDefined } from 'twenty-shared/utils'; +import { IsNull, Repository } from 'typeorm'; +import { v4 } from 'uuid'; + +import { ApplicationService } from 'src/engine/core-modules/application/application.service'; +import { WorkspaceManyOrAllFlatEntityMapsCacheService } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.service'; +import { type FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type'; +import { addFlatEntityToFlatEntityMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/add-flat-entity-to-flat-entity-maps-or-throw.util'; +import { findFlatEntityByIdInFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps.util'; +import { resolveEntityRelationUniversalIdentifiers } from 'src/engine/metadata-modules/flat-entity/utils/resolve-entity-relation-universal-identifiers.util'; +import { splitEntitiesByRemovalStrategy } from 'src/engine/metadata-modules/flat-entity/utils/split-entities-by-removal-strategy.util'; +import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type'; +import { isFlatPageLayoutWidgetConfigurationOfType } from 'src/engine/metadata-modules/flat-page-layout-widget/utils/is-flat-page-layout-widget-configuration-of-type.util'; +import { DEFAULT_VIEW_FIELD_SIZE } from 'src/engine/metadata-modules/flat-view-field/constants/default-view-field-size.constant'; +import { type FlatViewField } from 'src/engine/metadata-modules/flat-view-field/types/flat-view-field.type'; +import { fromViewFieldOverridesToUniversalOverrides } from 'src/engine/metadata-modules/flat-view-field/utils/from-view-field-overrides-to-universal-overrides.util'; +import { type FlatViewFilterGroupMaps } from 'src/engine/metadata-modules/flat-view-filter-group/types/flat-view-filter-group-maps.type'; +import { type FlatViewFilterGroup } from 'src/engine/metadata-modules/flat-view-filter-group/types/flat-view-filter-group.type'; +import { type FlatViewFilter } from 'src/engine/metadata-modules/flat-view-filter/types/flat-view-filter.type'; +import { type FlatViewSort } from 'src/engine/metadata-modules/flat-view-sort/types/flat-view-sort.type'; +import { type FlatViewMaps } from 'src/engine/metadata-modules/flat-view/types/flat-view-maps.type'; +import { WidgetConfigurationType } from 'src/engine/metadata-modules/page-layout-widget/enums/widget-configuration-type.type'; +import { isCallerOverridingEntity } from 'src/engine/metadata-modules/utils/is-caller-overriding-entity.util'; +import { sanitizeOverridableEntityInput } from 'src/engine/metadata-modules/utils/sanitize-overridable-entity-input.util'; +import { type UpsertViewWidgetViewFieldInput } from 'src/engine/metadata-modules/view/dtos/inputs/upsert-view-widget-view-field.input'; +import { type UpsertViewWidgetViewFilterGroupInput } from 'src/engine/metadata-modules/view/dtos/inputs/upsert-view-widget-view-filter-group.input'; +import { type UpsertViewWidgetViewFilterInput } from 'src/engine/metadata-modules/view/dtos/inputs/upsert-view-widget-view-filter.input'; +import { type UpsertViewWidgetViewSortInput } from 'src/engine/metadata-modules/view/dtos/inputs/upsert-view-widget-view-sort.input'; +import { type UpsertViewWidgetInput } from 'src/engine/metadata-modules/view/dtos/inputs/upsert-view-widget.input'; +import { ViewEntity } from 'src/engine/metadata-modules/view/entities/view.entity'; +import { + ViewException, + ViewExceptionCode, +} from 'src/engine/metadata-modules/view/exceptions/view.exception'; +import { WorkspaceMigrationBuilderException } from 'src/engine/workspace-manager/workspace-migration/exceptions/workspace-migration-builder-exception'; +import { WorkspaceMigrationValidateBuildAndRunService } from 'src/engine/workspace-manager/workspace-migration/services/workspace-migration-validate-build-and-run-service'; + +const EMPTY_FIELD_OPS = { + fieldsToCreate: [] as FlatViewField[], + fieldsToUpdate: [] as FlatViewField[], +}; + +const EMPTY_FILTER_GROUP_OPS = { + filterGroupsToCreate: [] as FlatViewFilterGroup[], + filterGroupsToUpdate: [] as FlatViewFilterGroup[], + filterGroupsToRemove: [] as FlatViewFilterGroup[], +}; + +const EMPTY_FILTER_OPS = { + filtersToCreate: [] as FlatViewFilter[], + filtersToUpdate: [] as FlatViewFilter[], + filtersToRemove: [] as FlatViewFilter[], +}; + +const EMPTY_SORT_OPS = { + sortsToCreate: [] as FlatViewSort[], + sortsToUpdate: [] as FlatViewSort[], + sortsToRemove: [] as FlatViewSort[], +}; + +@Injectable() +export class ViewWidgetUpsertService { + constructor( + private readonly workspaceMigrationValidateBuildAndRunService: WorkspaceMigrationValidateBuildAndRunService, + private readonly workspaceManyOrAllFlatEntityMapsCacheService: WorkspaceManyOrAllFlatEntityMapsCacheService, + private readonly applicationService: ApplicationService, + @InjectRepository(ViewEntity) + private readonly viewRepository: Repository, + ) {} + + async upsertViewWidget({ + input, + workspaceId, + }: { + input: UpsertViewWidgetInput; + workspaceId: string; + }): Promise { + const { widgetId } = input; + + const { workspaceCustomFlatApplication } = + await this.applicationService.findWorkspaceTwentyStandardAndCustomApplicationOrThrow( + { workspaceId }, + ); + + const { + flatPageLayoutWidgetMaps, + flatFieldMetadataMaps, + flatViewFieldMaps, + flatViewFilterMaps, + flatViewFilterGroupMaps, + flatViewSortMaps, + flatViewMaps, + } = + await this.workspaceManyOrAllFlatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps( + { + workspaceId, + flatMapsKeys: [ + 'flatPageLayoutWidgetMaps', + 'flatFieldMetadataMaps', + 'flatViewFieldMaps', + 'flatViewFilterMaps', + 'flatViewFilterGroupMaps', + 'flatViewSortMaps', + 'flatViewMaps', + ], + }, + ); + + const widget = findFlatEntityByIdInFlatEntityMaps({ + flatEntityId: widgetId, + flatEntityMaps: flatPageLayoutWidgetMaps, + }); + + if ( + !isDefined(widget) || + !isFlatPageLayoutWidgetConfigurationOfType( + widget, + WidgetConfigurationType.RECORD_TABLE, + ) + ) { + throw new ViewException( + t`Record table widget not found`, + ViewExceptionCode.VIEW_WIDGET_NOT_FOUND, + ); + } + + const viewId = widget.configuration.viewId; + + if (!isDefined(viewId)) { + throw new ViewException( + t`Record table widget has no associated view`, + ViewExceptionCode.VIEW_WIDGET_NOT_FOUND, + ); + } + + const flatView = findFlatEntityByIdInFlatEntityMaps({ + flatEntityId: viewId, + flatEntityMaps: flatViewMaps, + }); + + if (!isDefined(flatView)) { + throw new ViewException( + t`View not found for widget`, + ViewExceptionCode.VIEW_NOT_FOUND, + ); + } + + const upsertContext = { + viewId, + workspaceId, + applicationId: workspaceCustomFlatApplication.id, + applicationUniversalIdentifier: + workspaceCustomFlatApplication.universalIdentifier, + now: new Date().toISOString(), + }; + + if ( + !isDefined(input.viewFields) && + !isDefined(input.viewFilterGroups) && + !isDefined(input.viewFilters) && + !isDefined(input.viewSorts) + ) { + const view = await this.viewRepository.findOne({ + where: { + id: upsertContext.viewId, + workspaceId: upsertContext.workspaceId, + deletedAt: IsNull(), + }, + }); + + if (!isDefined(view)) { + throw new ViewException( + t`View not found`, + ViewExceptionCode.VIEW_NOT_FOUND, + ); + } + + return view; + } + + const existingViewFields = Object.values( + flatViewFieldMaps.byUniversalIdentifier, + ) + .filter(isDefined) + .filter((field) => field.isActive && field.viewId === viewId); + + const existingViewFilters = Object.values( + flatViewFilterMaps.byUniversalIdentifier, + ) + .filter(isDefined) + .filter((filter) => filter.viewId === viewId); + + const existingViewFilterGroups = Object.values( + flatViewFilterGroupMaps.byUniversalIdentifier, + ) + .filter(isDefined) + .filter((group) => group.viewId === viewId); + + const existingViewSorts = Object.values( + flatViewSortMaps.byUniversalIdentifier, + ) + .filter(isDefined) + .filter((sort) => sort.viewId === viewId); + + const viewFieldOperations = isDefined(input.viewFields) + ? this.computeViewFieldOperations({ + inputFields: input.viewFields, + existingViewFields, + ...upsertContext, + flatFieldMetadataMaps, + flatViewMaps, + }) + : EMPTY_FIELD_OPS; + + const viewFilterGroupOperations = isDefined(input.viewFilterGroups) + ? this.computeViewFilterGroupOperations({ + inputFilterGroups: input.viewFilterGroups, + existingViewFilterGroups, + ...upsertContext, + flatViewMaps, + flatViewFilterGroupMaps, + }) + : EMPTY_FILTER_GROUP_OPS; + + const optimisticFilterGroupMaps = + viewFilterGroupOperations.filterGroupsToCreate.reduce( + (maps, group) => + addFlatEntityToFlatEntityMapsOrThrow({ + flatEntity: group, + flatEntityMaps: maps, + }), + flatViewFilterGroupMaps, + ); + + const viewFilterOperations = isDefined(input.viewFilters) + ? this.computeViewFilterOperations({ + inputFilters: input.viewFilters, + existingViewFilters, + ...upsertContext, + flatFieldMetadataMaps, + flatViewMaps, + flatViewFilterGroupMaps: optimisticFilterGroupMaps, + }) + : EMPTY_FILTER_OPS; + + const viewSortOperations = isDefined(input.viewSorts) + ? this.computeViewSortOperations({ + inputSorts: input.viewSorts, + existingViewSorts, + ...upsertContext, + flatFieldMetadataMaps, + flatViewMaps, + }) + : EMPTY_SORT_OPS; + + const { + toHardDelete: filterGroupsToDelete, + toDeactivate: filterGroupsToDeactivate, + } = splitEntitiesByRemovalStrategy({ + entitiesToRemove: viewFilterGroupOperations.filterGroupsToRemove, + workspaceCustomApplicationUniversalIdentifier: + upsertContext.applicationUniversalIdentifier, + now: upsertContext.now, + }); + + const { toHardDelete: filtersToDelete, toDeactivate: filtersToDeactivate } = + splitEntitiesByRemovalStrategy({ + entitiesToRemove: viewFilterOperations.filtersToRemove, + workspaceCustomApplicationUniversalIdentifier: + upsertContext.applicationUniversalIdentifier, + now: upsertContext.now, + }); + + const { toHardDelete: sortsToDelete, toDeactivate: sortsToDeactivate } = + splitEntitiesByRemovalStrategy({ + entitiesToRemove: viewSortOperations.sortsToRemove, + workspaceCustomApplicationUniversalIdentifier: + upsertContext.applicationUniversalIdentifier, + now: upsertContext.now, + }); + + const validateAndBuildResult = + await this.workspaceMigrationValidateBuildAndRunService.validateBuildAndRunWorkspaceMigration( + { + allFlatEntityOperationByMetadataName: { + viewField: { + flatEntityToCreate: viewFieldOperations.fieldsToCreate, + flatEntityToDelete: [], + flatEntityToUpdate: viewFieldOperations.fieldsToUpdate, + }, + viewFilterGroup: { + flatEntityToCreate: + viewFilterGroupOperations.filterGroupsToCreate, + flatEntityToDelete: filterGroupsToDelete, + flatEntityToUpdate: [ + ...viewFilterGroupOperations.filterGroupsToUpdate, + ...filterGroupsToDeactivate, + ], + }, + viewFilter: { + flatEntityToCreate: viewFilterOperations.filtersToCreate, + flatEntityToDelete: filtersToDelete, + flatEntityToUpdate: [ + ...viewFilterOperations.filtersToUpdate, + ...filtersToDeactivate, + ], + }, + viewSort: { + flatEntityToCreate: viewSortOperations.sortsToCreate, + flatEntityToDelete: sortsToDelete, + flatEntityToUpdate: [ + ...viewSortOperations.sortsToUpdate, + ...sortsToDeactivate, + ], + }, + }, + workspaceId: upsertContext.workspaceId, + isSystemBuild: false, + applicationUniversalIdentifier: + upsertContext.applicationUniversalIdentifier, + }, + ); + + if (validateAndBuildResult.status === 'fail') { + throw new WorkspaceMigrationBuilderException( + validateAndBuildResult, + 'Multiple validation errors occurred while upserting view widget', + ); + } + + const view = await this.viewRepository.findOne({ + where: { + id: upsertContext.viewId, + workspaceId: upsertContext.workspaceId, + deletedAt: IsNull(), + }, + }); + + if (!isDefined(view)) { + throw new ViewException( + t`View not found after upsert`, + ViewExceptionCode.VIEW_NOT_FOUND, + ); + } + + return view; + } + + private computeViewFieldOperations({ + inputFields, + existingViewFields, + viewId, + workspaceId, + applicationId, + applicationUniversalIdentifier, + now, + flatFieldMetadataMaps, + flatViewMaps, + }: { + inputFields: UpsertViewWidgetViewFieldInput[]; + existingViewFields: FlatViewField[]; + viewId: string; + workspaceId: string; + applicationId: string; + applicationUniversalIdentifier: string; + now: string; + flatFieldMetadataMaps: FlatEntityMaps; + flatViewMaps: FlatViewMaps; + }): { + fieldsToCreate: FlatViewField[]; + fieldsToUpdate: FlatViewField[]; + } { + const fieldsToCreate: FlatViewField[] = []; + const fieldsToUpdate: FlatViewField[] = []; + + for (const inputField of inputFields) { + const existingField = isDefined(inputField.viewFieldId) + ? existingViewFields.find((f) => f.id === inputField.viewFieldId) + : isDefined(inputField.fieldMetadataId) + ? existingViewFields.find( + (f) => f.fieldMetadataId === inputField.fieldMetadataId, + ) + : undefined; + + if (isDefined(existingField)) { + const resolvedIsVisible = isDefined(existingField.overrides?.isVisible) + ? existingField.overrides.isVisible + : existingField.isVisible; + const resolvedPosition = isDefined(existingField.overrides?.position) + ? existingField.overrides.position + : existingField.position; + const resolvedSize = isDefined(existingField.overrides?.size) + ? existingField.overrides.size + : existingField.size; + + const hasChanged = + resolvedIsVisible !== inputField.isVisible || + resolvedPosition !== inputField.position || + (isDefined(inputField.size) && resolvedSize !== inputField.size); + + if (!hasChanged) { + continue; + } + + const shouldOverride = isCallerOverridingEntity({ + callerApplicationUniversalIdentifier: applicationUniversalIdentifier, + entityApplicationUniversalIdentifier: + existingField.applicationUniversalIdentifier, + workspaceCustomApplicationUniversalIdentifier: + applicationUniversalIdentifier, + }); + + const { overrides, updatedEditableProperties: sanitizedFieldProps } = + sanitizeOverridableEntityInput({ + metadataName: 'viewField', + existingFlatEntity: existingField, + updatedEditableProperties: { + isVisible: inputField.isVisible, + position: inputField.position, + ...(isDefined(inputField.size) ? { size: inputField.size } : {}), + }, + shouldOverride, + }); + + const updatedField: FlatViewField = { + ...existingField, + ...sanitizedFieldProps, + overrides, + updatedAt: now, + }; + + if (isDefined(overrides)) { + updatedField.universalOverrides = + fromViewFieldOverridesToUniversalOverrides({ + overrides, + viewFieldGroupUniversalIdentifierById: {}, + }); + } else { + updatedField.universalOverrides = null; + } + + fieldsToUpdate.push(updatedField); + continue; + } + + if (!isDefined(inputField.fieldMetadataId)) { + continue; + } + + const fieldMetadata = findFlatEntityByIdInFlatEntityMaps({ + flatEntityId: inputField.fieldMetadataId, + flatEntityMaps: flatFieldMetadataMaps, + }); + + if (!isDefined(fieldMetadata)) { + continue; + } + + const { fieldMetadataUniversalIdentifier, viewUniversalIdentifier } = + resolveEntityRelationUniversalIdentifiers({ + metadataName: 'viewField', + foreignKeyValues: { + fieldMetadataId: inputField.fieldMetadataId, + viewId, + }, + flatEntityMaps: { + flatFieldMetadataMaps, + flatViewMaps, + }, + }); + + fieldsToCreate.push({ + id: v4(), + workspaceId, + applicationId, + universalIdentifier: v4(), + applicationUniversalIdentifier, + fieldMetadataId: inputField.fieldMetadataId, + fieldMetadataUniversalIdentifier, + viewId, + viewUniversalIdentifier, + viewFieldGroupId: null, + viewFieldGroupUniversalIdentifier: null, + isVisible: inputField.isVisible, + size: inputField.size ?? DEFAULT_VIEW_FIELD_SIZE, + position: inputField.position, + aggregateOperation: null, + overrides: null, + universalOverrides: null, + isActive: true, + createdAt: now, + updatedAt: now, + deletedAt: null, + }); + } + + return { fieldsToCreate, fieldsToUpdate }; + } + + private computeViewFilterGroupOperations({ + inputFilterGroups, + existingViewFilterGroups, + viewId, + workspaceId, + applicationId, + applicationUniversalIdentifier, + now, + flatViewMaps, + flatViewFilterGroupMaps, + }: { + inputFilterGroups: UpsertViewWidgetViewFilterGroupInput[]; + existingViewFilterGroups: FlatViewFilterGroup[]; + viewId: string; + workspaceId: string; + applicationId: string; + applicationUniversalIdentifier: string; + now: string; + flatViewMaps: FlatViewMaps; + flatViewFilterGroupMaps: FlatViewFilterGroupMaps; + }): { + filterGroupsToCreate: FlatViewFilterGroup[]; + filterGroupsToUpdate: FlatViewFilterGroup[]; + filterGroupsToRemove: FlatViewFilterGroup[]; + } { + const filterGroupsToCreate: FlatViewFilterGroup[] = []; + const filterGroupsToUpdate: FlatViewFilterGroup[] = []; + const inputFilterGroupIds = new Set( + inputFilterGroups.map((g) => g.id).filter(isDefined), + ); + + for (const inputGroup of inputFilterGroups) { + const existingGroup = isDefined(inputGroup.id) + ? existingViewFilterGroups.find((g) => g.id === inputGroup.id) + : undefined; + + if (!isDefined(existingGroup)) { + const filterGroupId = inputGroup.id ?? v4(); + + const { + viewUniversalIdentifier, + parentViewFilterGroupUniversalIdentifier, + } = resolveEntityRelationUniversalIdentifiers({ + metadataName: 'viewFilterGroup', + foreignKeyValues: { + viewId, + parentViewFilterGroupId: inputGroup.parentViewFilterGroupId, + }, + flatEntityMaps: { flatViewMaps, flatViewFilterGroupMaps }, + }); + + filterGroupsToCreate.push({ + id: filterGroupId, + workspaceId, + applicationId, + universalIdentifier: filterGroupId, + applicationUniversalIdentifier, + viewId, + viewUniversalIdentifier, + logicalOperator: + inputGroup.logicalOperator ?? ViewFilterGroupLogicalOperator.AND, + parentViewFilterGroupId: inputGroup.parentViewFilterGroupId ?? null, + parentViewFilterGroupUniversalIdentifier, + positionInViewFilterGroup: + inputGroup.positionInViewFilterGroup ?? null, + viewFilterIds: [], + viewFilterUniversalIdentifiers: [], + childViewFilterGroupIds: [], + childViewFilterGroupUniversalIdentifiers: [], + createdAt: now, + updatedAt: now, + deletedAt: null, + }); + } else { + const hasChanged = + existingGroup.logicalOperator !== inputGroup.logicalOperator || + existingGroup.positionInViewFilterGroup !== + inputGroup.positionInViewFilterGroup || + existingGroup.parentViewFilterGroupId !== + inputGroup.parentViewFilterGroupId; + + if (hasChanged) { + const resolvedParentId = + inputGroup.parentViewFilterGroupId ?? + existingGroup.parentViewFilterGroupId; + + const { parentViewFilterGroupUniversalIdentifier } = + resolveEntityRelationUniversalIdentifiers({ + metadataName: 'viewFilterGroup', + foreignKeyValues: { + parentViewFilterGroupId: resolvedParentId, + }, + flatEntityMaps: { flatViewFilterGroupMaps }, + }); + + filterGroupsToUpdate.push({ + ...existingGroup, + logicalOperator: + inputGroup.logicalOperator ?? existingGroup.logicalOperator, + positionInViewFilterGroup: + inputGroup.positionInViewFilterGroup ?? + existingGroup.positionInViewFilterGroup, + parentViewFilterGroupId: resolvedParentId, + parentViewFilterGroupUniversalIdentifier, + updatedAt: now, + }); + } + } + } + + const filterGroupsToRemove = existingViewFilterGroups.filter( + (g) => !inputFilterGroupIds.has(g.id), + ); + + return { filterGroupsToCreate, filterGroupsToUpdate, filterGroupsToRemove }; + } + + private computeViewFilterOperations({ + inputFilters, + existingViewFilters, + viewId, + workspaceId, + applicationId, + applicationUniversalIdentifier, + now, + flatFieldMetadataMaps, + flatViewMaps, + flatViewFilterGroupMaps, + }: { + inputFilters: UpsertViewWidgetViewFilterInput[]; + existingViewFilters: FlatViewFilter[]; + viewId: string; + workspaceId: string; + applicationId: string; + applicationUniversalIdentifier: string; + now: string; + flatFieldMetadataMaps: FlatEntityMaps; + flatViewMaps: FlatViewMaps; + flatViewFilterGroupMaps: FlatViewFilterGroupMaps; + }): { + filtersToCreate: FlatViewFilter[]; + filtersToUpdate: FlatViewFilter[]; + filtersToRemove: FlatViewFilter[]; + } { + const filtersToCreate: FlatViewFilter[] = []; + const filtersToUpdate: FlatViewFilter[] = []; + const inputFilterIds = new Set( + inputFilters.map((f) => f.id).filter(isDefined), + ); + + for (const inputFilter of inputFilters) { + const existingFilter = isDefined(inputFilter.id) + ? existingViewFilters.find((f) => f.id === inputFilter.id) + : undefined; + + if (!isDefined(existingFilter)) { + const filterId = inputFilter.id ?? v4(); + + const { + fieldMetadataUniversalIdentifier, + viewUniversalIdentifier, + viewFilterGroupUniversalIdentifier, + } = resolveEntityRelationUniversalIdentifiers({ + metadataName: 'viewFilter', + foreignKeyValues: { + fieldMetadataId: inputFilter.fieldMetadataId, + viewId, + viewFilterGroupId: inputFilter.viewFilterGroupId, + }, + flatEntityMaps: { + flatFieldMetadataMaps, + flatViewMaps, + flatViewFilterGroupMaps, + }, + }); + + filtersToCreate.push({ + id: filterId, + workspaceId, + applicationId, + universalIdentifier: v4(), + applicationUniversalIdentifier, + fieldMetadataId: inputFilter.fieldMetadataId, + fieldMetadataUniversalIdentifier, + viewId, + viewUniversalIdentifier, + operand: inputFilter.operand ?? ViewFilterOperand.CONTAINS, + value: inputFilter.value, + viewFilterGroupId: inputFilter.viewFilterGroupId ?? null, + viewFilterGroupUniversalIdentifier, + positionInViewFilterGroup: + inputFilter.positionInViewFilterGroup ?? null, + subFieldName: inputFilter.subFieldName ?? null, + createdAt: now, + updatedAt: now, + deletedAt: null, + }); + } else { + const hasChanged = + existingFilter.fieldMetadataId !== inputFilter.fieldMetadataId || + existingFilter.operand !== inputFilter.operand || + JSON.stringify(existingFilter.value) !== + JSON.stringify(inputFilter.value) || + existingFilter.viewFilterGroupId !== inputFilter.viewFilterGroupId || + existingFilter.positionInViewFilterGroup !== + inputFilter.positionInViewFilterGroup || + existingFilter.subFieldName !== inputFilter.subFieldName; + + if (hasChanged) { + const { + fieldMetadataUniversalIdentifier, + viewFilterGroupUniversalIdentifier, + } = resolveEntityRelationUniversalIdentifiers({ + metadataName: 'viewFilter', + foreignKeyValues: { + fieldMetadataId: inputFilter.fieldMetadataId, + viewFilterGroupId: inputFilter.viewFilterGroupId, + }, + flatEntityMaps: { + flatFieldMetadataMaps, + flatViewFilterGroupMaps, + }, + }); + + filtersToUpdate.push({ + ...existingFilter, + fieldMetadataId: inputFilter.fieldMetadataId, + fieldMetadataUniversalIdentifier, + operand: inputFilter.operand ?? existingFilter.operand, + value: inputFilter.value, + viewFilterGroupId: + inputFilter.viewFilterGroupId ?? existingFilter.viewFilterGroupId, + viewFilterGroupUniversalIdentifier, + positionInViewFilterGroup: + inputFilter.positionInViewFilterGroup ?? + existingFilter.positionInViewFilterGroup, + subFieldName: + inputFilter.subFieldName ?? existingFilter.subFieldName, + updatedAt: now, + }); + } + } + } + + const filtersToRemove = existingViewFilters.filter( + (f) => !inputFilterIds.has(f.id), + ); + + return { filtersToCreate, filtersToUpdate, filtersToRemove }; + } + + private computeViewSortOperations({ + inputSorts, + existingViewSorts, + viewId, + workspaceId, + applicationId, + applicationUniversalIdentifier, + now, + flatFieldMetadataMaps, + flatViewMaps, + }: { + inputSorts: UpsertViewWidgetViewSortInput[]; + existingViewSorts: FlatViewSort[]; + viewId: string; + workspaceId: string; + applicationId: string; + applicationUniversalIdentifier: string; + now: string; + flatFieldMetadataMaps: FlatEntityMaps; + flatViewMaps: FlatViewMaps; + }): { + sortsToCreate: FlatViewSort[]; + sortsToUpdate: FlatViewSort[]; + sortsToRemove: FlatViewSort[]; + } { + const sortsToCreate: FlatViewSort[] = []; + const sortsToUpdate: FlatViewSort[] = []; + const inputSortIds = new Set(inputSorts.map((s) => s.id).filter(isDefined)); + + for (const inputSort of inputSorts) { + const existingSort = isDefined(inputSort.id) + ? existingViewSorts.find((s) => s.id === inputSort.id) + : undefined; + + if (!isDefined(existingSort)) { + const sortId = inputSort.id ?? v4(); + + const { fieldMetadataUniversalIdentifier, viewUniversalIdentifier } = + resolveEntityRelationUniversalIdentifiers({ + metadataName: 'viewSort', + foreignKeyValues: { + fieldMetadataId: inputSort.fieldMetadataId, + viewId, + }, + flatEntityMaps: { + flatFieldMetadataMaps, + flatViewMaps, + }, + }); + + sortsToCreate.push({ + id: sortId, + workspaceId, + applicationId, + universalIdentifier: v4(), + applicationUniversalIdentifier, + fieldMetadataId: inputSort.fieldMetadataId, + fieldMetadataUniversalIdentifier, + viewId, + viewUniversalIdentifier, + direction: inputSort.direction ?? ViewSortDirection.ASC, + createdAt: now, + updatedAt: now, + deletedAt: null, + }); + } else { + const hasChanged = + existingSort.fieldMetadataId !== inputSort.fieldMetadataId || + existingSort.direction !== inputSort.direction; + + if (hasChanged) { + const { fieldMetadataUniversalIdentifier } = + resolveEntityRelationUniversalIdentifiers({ + metadataName: 'viewSort', + foreignKeyValues: { + fieldMetadataId: inputSort.fieldMetadataId, + }, + flatEntityMaps: { + flatFieldMetadataMaps, + }, + }); + + sortsToUpdate.push({ + ...existingSort, + fieldMetadataId: inputSort.fieldMetadataId, + fieldMetadataUniversalIdentifier, + direction: inputSort.direction ?? existingSort.direction, + updatedAt: now, + }); + } + } + } + + const sortsToRemove = existingViewSorts.filter( + (s) => !inputSortIds.has(s.id), + ); + + return { sortsToCreate, sortsToUpdate, sortsToRemove }; + } +} diff --git a/packages/twenty-server/src/engine/metadata-modules/view/utils/view-graphql-api-exception-handler.util.ts b/packages/twenty-server/src/engine/metadata-modules/view/utils/view-graphql-api-exception-handler.util.ts index 107c77ef4d..b20d9b58b6 100644 --- a/packages/twenty-server/src/engine/metadata-modules/view/utils/view-graphql-api-exception-handler.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/view/utils/view-graphql-api-exception-handler.util.ts @@ -59,6 +59,8 @@ export const viewGraphqlApiExceptionHandler = (error: Error, i18n: I18n) => { throw new ForbiddenError(error.message, { userFriendlyMessage: error.userFriendlyMessage, }); + case ViewExceptionCode.VIEW_WIDGET_NOT_FOUND: + throw new NotFoundError(error.message); default: { return assertUnreachable(error.code); } diff --git a/packages/twenty-server/src/engine/metadata-modules/view/view.module.ts b/packages/twenty-server/src/engine/metadata-modules/view/view.module.ts index 4e9bbde8f9..c84edfd00f 100644 --- a/packages/twenty-server/src/engine/metadata-modules/view/view.module.ts +++ b/packages/twenty-server/src/engine/metadata-modules/view/view.module.ts @@ -12,6 +12,7 @@ import { ViewFieldModule } from 'src/engine/metadata-modules/view-field/view-fie import { ViewFilterModule } from 'src/engine/metadata-modules/view-filter/view-filter.module'; import { ViewPermissionsModule } from 'src/engine/metadata-modules/view-permissions/view-permissions.module'; import { ViewSortModule } from 'src/engine/metadata-modules/view-sort/view-sort.module'; +import { ViewWidgetUpsertService } from 'src/engine/metadata-modules/view/services/view-widget-upsert.service'; import { ViewController } from 'src/engine/metadata-modules/view/controllers/view.controller'; import { ViewEntity } from 'src/engine/metadata-modules/view/entities/view.entity'; import { ViewResolver } from 'src/engine/metadata-modules/view/resolvers/view.resolver'; @@ -44,6 +45,7 @@ import { WorkspaceMigrationModule } from 'src/engine/workspace-manager/workspace ViewResolver, ViewQueryParamsService, ViewToolsFactory, + ViewWidgetUpsertService, ], exports: [ ViewService, diff --git a/packages/twenty-server/test/integration/metadata/suites/view/upsert-view-widget.integration-spec.ts b/packages/twenty-server/test/integration/metadata/suites/view/upsert-view-widget.integration-spec.ts new file mode 100644 index 0000000000..c0e9c08158 --- /dev/null +++ b/packages/twenty-server/test/integration/metadata/suites/view/upsert-view-widget.integration-spec.ts @@ -0,0 +1,1119 @@ +import { + VIEW_FIELD_GQL_FIELDS, + VIEW_FILTER_GQL_FIELDS, + VIEW_FILTER_GROUP_GQL_FIELDS, + VIEW_GQL_FIELDS, + VIEW_SORT_GQL_FIELDS, +} from 'test/integration/constants/view-gql-fields.constants'; +import { createOnePageLayoutTab } from 'test/integration/metadata/suites/page-layout-tab/utils/create-one-page-layout-tab.util'; +import { destroyOnePageLayoutTab } from 'test/integration/metadata/suites/page-layout-tab/utils/destroy-one-page-layout-tab.util'; +import { createOnePageLayoutWidget } from 'test/integration/metadata/suites/page-layout-widget/utils/create-one-page-layout-widget.util'; +import { destroyOnePageLayoutWidget } from 'test/integration/metadata/suites/page-layout-widget/utils/destroy-one-page-layout-widget.util'; +import { + fetchTestFieldMetadataIds, + type TestFieldMetadataIds, +} from 'test/integration/metadata/suites/page-layout-widget/utils/fetch-test-field-metadata-ids.util'; +import { createOnePageLayout } from 'test/integration/metadata/suites/page-layout/utils/create-one-page-layout.util'; +import { destroyOnePageLayout } from 'test/integration/metadata/suites/page-layout/utils/destroy-one-page-layout.util'; +import { findViewFields } from 'test/integration/metadata/suites/view-field/utils/find-view-fields.util'; +import { findViewFilters } from 'test/integration/metadata/suites/view-filter/utils/find-view-filters.util'; +import { createOneView } from 'test/integration/metadata/suites/view/utils/create-one-view.util'; +import { destroyOneView } from 'test/integration/metadata/suites/view/utils/destroy-one-view.util'; +import { upsertViewWidget } from 'test/integration/metadata/suites/view/utils/upsert-view-widget.util'; +import { + ViewFilterGroupLogicalOperator, + ViewFilterOperand, + ViewSortDirection, + ViewType, +} from 'twenty-shared/types'; +import { v4 as uuidv4 } from 'uuid'; + +import { WidgetConfigurationType } from 'src/engine/metadata-modules/page-layout-widget/enums/widget-configuration-type.type'; +import { WidgetType } from 'src/engine/metadata-modules/page-layout-widget/enums/widget-type.enum'; + +type ViewWidgetTestSetup = { + pageLayoutId: string; + pageLayoutTabId: string; + widgetId: string; + viewId: string; + labelIdentifierFieldMetadataId: string | null; + viewFields: Array<{ + id: string; + fieldMetadataId: string; + position: number; + isVisible: boolean; + }>; + fieldMetadataIds: string[]; +}; + +const VIEW_WITH_ALL_RELATIONS_GQL_FIELDS = ` + ${VIEW_GQL_FIELDS} + viewFields { + ${VIEW_FIELD_GQL_FIELDS} + } + viewFilters { + ${VIEW_FILTER_GQL_FIELDS} + } + viewFilterGroups { + ${VIEW_FILTER_GROUP_GQL_FIELDS} + } + viewSorts { + ${VIEW_SORT_GQL_FIELDS} + } +`; + +describe('upsertViewWidget', () => { + let testSetup: ViewWidgetTestSetup; + + beforeAll(async () => { + const testFieldMetadataIds: TestFieldMetadataIds = + await fetchTestFieldMetadataIds(); + + const { data: layoutData } = await createOnePageLayout({ + expectToFail: false, + input: { name: 'Test Page Layout For View Widget' }, + }); + + const pageLayoutId = layoutData.createPageLayout.id; + + const { data: tabData } = await createOnePageLayoutTab({ + expectToFail: false, + input: { + title: 'Test Tab For View Widget', + pageLayoutId, + }, + }); + + const pageLayoutTabId = tabData.createPageLayoutTab.id; + + const { data: viewData } = await createOneView({ + expectToFail: false, + input: { + name: 'testViewWidgetView', + objectMetadataId: testFieldMetadataIds.objectMetadataId, + icon: 'IconTable', + type: ViewType.TABLE, + }, + }); + + const viewId = viewData.createView.id; + + const { data: widgetData } = await createOnePageLayoutWidget({ + expectToFail: false, + input: { + title: 'Test Record Table Widget', + type: WidgetType.RECORD_TABLE, + pageLayoutTabId, + objectMetadataId: testFieldMetadataIds.objectMetadataId, + gridPosition: { row: 0, column: 0, rowSpan: 1, columnSpan: 1 }, + configuration: { + configurationType: WidgetConfigurationType.RECORD_TABLE, + viewId, + }, + }, + }); + + const widgetId = widgetData.createPageLayoutWidget.id; + + const objectMetadataRows = await global.testDataSource.query( + `SELECT "labelIdentifierFieldMetadataId" + FROM core."objectMetadata" + WHERE id = $1`, + [testFieldMetadataIds.objectMetadataId], + ); + + const labelIdentifierFieldMetadataId = + objectMetadataRows[0]?.labelIdentifierFieldMetadataId ?? null; + + const allFieldMetadataRows = await global.testDataSource.query( + `SELECT id FROM core."fieldMetadata" + WHERE "objectMetadataId" = $1 + AND "isActive" = true + ORDER BY id + LIMIT 10`, + [testFieldMetadataIds.objectMetadataId], + ); + + const allFieldMetadataIds: string[] = allFieldMetadataRows.map( + (row: { id: string }) => row.id, + ); + + if (labelIdentifierFieldMetadataId) { + const idx = allFieldMetadataIds.indexOf(labelIdentifierFieldMetadataId); + + if (idx > 0) { + allFieldMetadataIds.splice(idx, 1); + allFieldMetadataIds.unshift(labelIdentifierFieldMetadataId); + } else if (idx === -1) { + allFieldMetadataIds.unshift(labelIdentifierFieldMetadataId); + } + } + + const seedFieldMetadataIds = allFieldMetadataIds.slice(0, 3); + + await upsertViewWidget({ + expectToFail: false, + input: { + widgetId, + viewFields: seedFieldMetadataIds.map((fmId, index) => ({ + fieldMetadataId: fmId, + isVisible: true, + position: index, + })), + }, + }); + + const { data: fieldsData } = await findViewFields({ + viewId, + gqlFields: 'id fieldMetadataId position isVisible', + expectToFail: false, + }); + + testSetup = { + pageLayoutId, + pageLayoutTabId, + widgetId, + viewId, + labelIdentifierFieldMetadataId, + viewFields: fieldsData.getViewFields, + fieldMetadataIds: allFieldMetadataIds, + }; + }); + + afterAll(async () => { + await destroyOnePageLayoutWidget({ + expectToFail: false, + input: { id: testSetup.widgetId }, + }); + await destroyOneView({ + expectToFail: false, + viewId: testSetup.viewId, + }); + await destroyOnePageLayoutTab({ + expectToFail: false, + input: { id: testSetup.pageLayoutTabId }, + }); + await destroyOnePageLayout({ + expectToFail: false, + input: { id: testSetup.pageLayoutId }, + }); + }); + + describe('view fields', () => { + it('should create a new view field via fieldMetadataId', async () => { + const existingFieldMetadataIds = new Set( + testSetup.viewFields.map((f) => f.fieldMetadataId), + ); + const newFieldMetadataId = testSetup.fieldMetadataIds.find( + (id) => !existingFieldMetadataIds.has(id), + ); + + expect(newFieldMetadataId).toBeDefined(); + + const { data, errors } = await upsertViewWidget({ + expectToFail: false, + input: { + widgetId: testSetup.widgetId, + viewFields: [ + ...testSetup.viewFields.map((f) => ({ + viewFieldId: f.id, + fieldMetadataId: f.fieldMetadataId, + isVisible: f.isVisible, + position: f.position, + })), + { + fieldMetadataId: newFieldMetadataId!, + isVisible: true, + position: 99, + }, + ], + }, + gqlFields: VIEW_WITH_ALL_RELATIONS_GQL_FIELDS, + }); + + expect(errors).toBeUndefined(); + expect(data.upsertViewWidget).toBeDefined(); + + const { data: fieldsData } = await findViewFields({ + viewId: testSetup.viewId, + gqlFields: 'id fieldMetadataId position isVisible', + expectToFail: false, + }); + + const createdField = fieldsData.getViewFields.find( + (f: { fieldMetadataId: string }) => + f.fieldMetadataId === newFieldMetadataId!, + ); + + expect(createdField).toBeDefined(); + expect(createdField!.isVisible).toBe(true); + expect(createdField!.position).toBe(99); + }); + + it('should update an existing view field position and visibility', async () => { + const targetField = testSetup.viewFields.find( + (field) => + field.fieldMetadataId !== testSetup.labelIdentifierFieldMetadataId, + )!; + + expect(targetField).toBeDefined(); + + const labelIdentifierField = testSetup.viewFields.find( + (field) => + field.fieldMetadataId === testSetup.labelIdentifierFieldMetadataId, + ); + + await upsertViewWidget({ + expectToFail: false, + input: { + widgetId: testSetup.widgetId, + viewFields: [ + ...(labelIdentifierField + ? [ + { + viewFieldId: labelIdentifierField.id, + fieldMetadataId: labelIdentifierField.fieldMetadataId, + isVisible: true, + position: 0, + }, + ] + : []), + { + viewFieldId: targetField.id, + fieldMetadataId: targetField.fieldMetadataId, + isVisible: false, + position: 42, + }, + ], + }, + }); + + const { data: fieldsData } = await findViewFields({ + viewId: testSetup.viewId, + gqlFields: 'id isVisible position', + expectToFail: false, + }); + + const updatedField = fieldsData.getViewFields.find( + (f: { id: string }) => f.id === targetField.id, + ); + + expect(updatedField).toBeDefined(); + expect(updatedField!.isVisible).toBe(false); + expect(updatedField!.position).toBe(42); + }); + + it('should not modify fields when viewFields is omitted', async () => { + const { data: fieldsBefore } = await findViewFields({ + viewId: testSetup.viewId, + gqlFields: 'id position isVisible', + expectToFail: false, + }); + + await upsertViewWidget({ + expectToFail: false, + input: { + widgetId: testSetup.widgetId, + viewSorts: [], + }, + }); + + const { data: fieldsAfter } = await findViewFields({ + viewId: testSetup.viewId, + gqlFields: 'id position isVisible', + expectToFail: false, + }); + + expect(fieldsAfter.getViewFields.length).toBe( + fieldsBefore.getViewFields.length, + ); + }); + }); + + describe('view filter groups', () => { + it('should create a new filter group', async () => { + const filterGroupId = uuidv4(); + + const { data, errors } = await upsertViewWidget({ + expectToFail: false, + input: { + widgetId: testSetup.widgetId, + viewFilterGroups: [ + { + id: filterGroupId, + logicalOperator: ViewFilterGroupLogicalOperator.AND, + }, + ], + }, + gqlFields: VIEW_WITH_ALL_RELATIONS_GQL_FIELDS, + }); + + expect(errors).toBeUndefined(); + expect(data.upsertViewWidget).toBeDefined(); + + const filterGroups = await global.testDataSource.query( + `SELECT id, "logicalOperator" + FROM core."viewFilterGroup" + WHERE id = $1 AND "deletedAt" IS NULL`, + [filterGroupId], + ); + + expect(filterGroups.length).toBe(1); + expect(filterGroups[0].logicalOperator).toBe( + ViewFilterGroupLogicalOperator.AND, + ); + }); + + it('should update an existing filter group logical operator', async () => { + const filterGroupId = uuidv4(); + + await upsertViewWidget({ + expectToFail: false, + input: { + widgetId: testSetup.widgetId, + viewFilterGroups: [ + { + id: filterGroupId, + logicalOperator: ViewFilterGroupLogicalOperator.AND, + }, + ], + }, + }); + + await upsertViewWidget({ + expectToFail: false, + input: { + widgetId: testSetup.widgetId, + viewFilterGroups: [ + { + id: filterGroupId, + logicalOperator: ViewFilterGroupLogicalOperator.OR, + }, + ], + }, + }); + + const filterGroups = await global.testDataSource.query( + `SELECT id, "logicalOperator" + FROM core."viewFilterGroup" + WHERE id = $1 AND "deletedAt" IS NULL`, + [filterGroupId], + ); + + expect(filterGroups.length).toBe(1); + expect(filterGroups[0].logicalOperator).toBe( + ViewFilterGroupLogicalOperator.OR, + ); + }); + + it('should remove filter groups not included in the input', async () => { + const groupToKeepId = uuidv4(); + const groupToRemoveId = uuidv4(); + + await upsertViewWidget({ + expectToFail: false, + input: { + widgetId: testSetup.widgetId, + viewFilterGroups: [ + { + id: groupToKeepId, + logicalOperator: ViewFilterGroupLogicalOperator.AND, + }, + { + id: groupToRemoveId, + logicalOperator: ViewFilterGroupLogicalOperator.OR, + }, + ], + }, + }); + + await upsertViewWidget({ + expectToFail: false, + input: { + widgetId: testSetup.widgetId, + viewFilterGroups: [ + { + id: groupToKeepId, + logicalOperator: ViewFilterGroupLogicalOperator.AND, + }, + ], + }, + }); + + const removedGroup = await global.testDataSource.query( + `SELECT id FROM core."viewFilterGroup" + WHERE id = $1 AND "deletedAt" IS NULL`, + [groupToRemoveId], + ); + + expect(removedGroup.length).toBe(0); + + const keptGroup = await global.testDataSource.query( + `SELECT id FROM core."viewFilterGroup" + WHERE id = $1 AND "deletedAt" IS NULL`, + [groupToKeepId], + ); + + expect(keptGroup.length).toBe(1); + }); + + it('should create a nested filter group with parent reference', async () => { + const parentGroupId = uuidv4(); + const childGroupId = uuidv4(); + + await upsertViewWidget({ + expectToFail: false, + input: { + widgetId: testSetup.widgetId, + viewFilterGroups: [ + { + id: parentGroupId, + logicalOperator: ViewFilterGroupLogicalOperator.AND, + }, + ], + }, + }); + + const { errors } = await upsertViewWidget({ + expectToFail: false, + input: { + widgetId: testSetup.widgetId, + viewFilterGroups: [ + { + id: parentGroupId, + logicalOperator: ViewFilterGroupLogicalOperator.AND, + }, + { + id: childGroupId, + logicalOperator: ViewFilterGroupLogicalOperator.OR, + parentViewFilterGroupId: parentGroupId, + }, + ], + }, + }); + + expect(errors).toBeUndefined(); + + const childGroup = await global.testDataSource.query( + `SELECT id, "parentViewFilterGroupId" + FROM core."viewFilterGroup" + WHERE id = $1 AND "deletedAt" IS NULL`, + [childGroupId], + ); + + expect(childGroup.length).toBe(1); + expect(childGroup[0].parentViewFilterGroupId).toBe(parentGroupId); + }); + }); + + describe('view filters', () => { + it('should create a new filter', async () => { + const fieldMetadataId = testSetup.fieldMetadataIds[0]; + + const { data, errors } = await upsertViewWidget({ + expectToFail: false, + input: { + widgetId: testSetup.widgetId, + viewFilters: [ + { + fieldMetadataId, + operand: ViewFilterOperand.CONTAINS, + value: 'test-value', + }, + ], + }, + gqlFields: VIEW_WITH_ALL_RELATIONS_GQL_FIELDS, + }); + + expect(errors).toBeUndefined(); + expect(data.upsertViewWidget).toBeDefined(); + + const { data: filtersData } = await findViewFilters({ + viewId: testSetup.viewId, + gqlFields: 'id fieldMetadataId operand value', + expectToFail: false, + }); + + const createdFilter = filtersData.getViewFilters.find( + (f: { fieldMetadataId: string; value: unknown }) => + f.fieldMetadataId === fieldMetadataId && f.value === 'test-value', + ); + + expect(createdFilter).toBeDefined(); + expect(createdFilter!.operand).toBe(ViewFilterOperand.CONTAINS); + }); + + it('should update an existing filter value and operand', async () => { + const filterId = uuidv4(); + const fieldMetadataId = testSetup.fieldMetadataIds[0]; + + await upsertViewWidget({ + expectToFail: false, + input: { + widgetId: testSetup.widgetId, + viewFilters: [ + { + id: filterId, + fieldMetadataId, + operand: ViewFilterOperand.CONTAINS, + value: 'initial', + }, + ], + }, + }); + + await upsertViewWidget({ + expectToFail: false, + input: { + widgetId: testSetup.widgetId, + viewFilters: [ + { + id: filterId, + fieldMetadataId, + operand: ViewFilterOperand.IS, + value: 'updated', + }, + ], + }, + }); + + const { data: filtersData } = await findViewFilters({ + viewId: testSetup.viewId, + gqlFields: 'id operand value', + expectToFail: false, + }); + + const updatedFilter = filtersData.getViewFilters.find( + (f: { id: string }) => f.id === filterId, + ); + + expect(updatedFilter).toBeDefined(); + expect(updatedFilter!.operand).toBe(ViewFilterOperand.IS); + expect(updatedFilter!.value).toBe('updated'); + }); + + it('should remove filters not included in the input', async () => { + const filterToKeepId = uuidv4(); + const filterToRemoveId = uuidv4(); + const fieldMetadataId = testSetup.fieldMetadataIds[0]; + + await upsertViewWidget({ + expectToFail: false, + input: { + widgetId: testSetup.widgetId, + viewFilters: [ + { + id: filterToKeepId, + fieldMetadataId, + operand: ViewFilterOperand.CONTAINS, + value: 'keep', + }, + { + id: filterToRemoveId, + fieldMetadataId, + operand: ViewFilterOperand.CONTAINS, + value: 'remove', + }, + ], + }, + }); + + await upsertViewWidget({ + expectToFail: false, + input: { + widgetId: testSetup.widgetId, + viewFilters: [ + { + id: filterToKeepId, + fieldMetadataId, + operand: ViewFilterOperand.CONTAINS, + value: 'keep', + }, + ], + }, + }); + + const { data: filtersData } = await findViewFilters({ + viewId: testSetup.viewId, + gqlFields: 'id', + expectToFail: false, + }); + + const keptFilter = filtersData.getViewFilters.find( + (f: { id: string }) => f.id === filterToKeepId, + ); + + expect(keptFilter).toBeDefined(); + + const removedFilter = filtersData.getViewFilters.find( + (f: { id: string }) => f.id === filterToRemoveId, + ); + + expect(removedFilter).toBeUndefined(); + }); + + it('should create a filter within a filter group', async () => { + const filterGroupId = uuidv4(); + const filterId = uuidv4(); + const fieldMetadataId = testSetup.fieldMetadataIds[0]; + + await upsertViewWidget({ + expectToFail: false, + input: { + widgetId: testSetup.widgetId, + viewFilterGroups: [ + { + id: filterGroupId, + logicalOperator: ViewFilterGroupLogicalOperator.AND, + }, + ], + viewFilters: [ + { + id: filterId, + fieldMetadataId, + operand: ViewFilterOperand.CONTAINS, + value: 'grouped-filter', + viewFilterGroupId: filterGroupId, + }, + ], + }, + }); + + const filters = await global.testDataSource.query( + `SELECT id, "viewFilterGroupId" + FROM core."viewFilter" + WHERE id = $1 AND "deletedAt" IS NULL`, + [filterId], + ); + + expect(filters.length).toBe(1); + expect(filters[0].viewFilterGroupId).toBe(filterGroupId); + }); + }); + + describe('view sorts', () => { + afterEach(async () => { + await upsertViewWidget({ + expectToFail: false, + input: { + widgetId: testSetup.widgetId, + viewSorts: [], + }, + }); + }); + + it('should create a new sort', async () => { + const sortId = uuidv4(); + const fieldMetadataId = testSetup.fieldMetadataIds[0]; + + await upsertViewWidget({ + expectToFail: false, + input: { + widgetId: testSetup.widgetId, + viewSorts: [ + { + id: sortId, + fieldMetadataId, + direction: ViewSortDirection.DESC, + }, + ], + }, + }); + + const sorts = await global.testDataSource.query( + `SELECT id, "fieldMetadataId", direction + FROM core."viewSort" + WHERE id = $1 AND "deletedAt" IS NULL`, + [sortId], + ); + + expect(sorts.length).toBe(1); + expect(sorts[0].fieldMetadataId).toBe(fieldMetadataId); + expect(sorts[0].direction).toBe(ViewSortDirection.DESC); + }); + + it('should update an existing sort direction', async () => { + const sortId = uuidv4(); + const fieldMetadataId = testSetup.fieldMetadataIds[1]; + + await upsertViewWidget({ + expectToFail: false, + input: { + widgetId: testSetup.widgetId, + viewSorts: [ + { + id: sortId, + fieldMetadataId, + direction: ViewSortDirection.ASC, + }, + ], + }, + }); + + await upsertViewWidget({ + expectToFail: false, + input: { + widgetId: testSetup.widgetId, + viewSorts: [ + { + id: sortId, + fieldMetadataId, + direction: ViewSortDirection.DESC, + }, + ], + }, + }); + + const sorts = await global.testDataSource.query( + `SELECT id, direction + FROM core."viewSort" + WHERE id = $1 AND "deletedAt" IS NULL`, + [sortId], + ); + + expect(sorts.length).toBe(1); + expect(sorts[0].direction).toBe(ViewSortDirection.DESC); + }); + + it('should remove sorts not included in the input', async () => { + const sortToKeepId = uuidv4(); + const sortToRemoveId = uuidv4(); + const fieldMetadataId = testSetup.fieldMetadataIds[0]; + const secondFieldMetadataId = testSetup.fieldMetadataIds[1]; + + await upsertViewWidget({ + expectToFail: false, + input: { + widgetId: testSetup.widgetId, + viewSorts: [ + { + id: sortToKeepId, + fieldMetadataId, + direction: ViewSortDirection.ASC, + }, + { + id: sortToRemoveId, + fieldMetadataId: secondFieldMetadataId, + direction: ViewSortDirection.DESC, + }, + ], + }, + }); + + await upsertViewWidget({ + expectToFail: false, + input: { + widgetId: testSetup.widgetId, + viewSorts: [ + { + id: sortToKeepId, + fieldMetadataId, + direction: ViewSortDirection.ASC, + }, + ], + }, + }); + + const keptSort = await global.testDataSource.query( + `SELECT id FROM core."viewSort" + WHERE id = $1 AND "deletedAt" IS NULL`, + [sortToKeepId], + ); + + expect(keptSort.length).toBe(1); + + const removedSort = await global.testDataSource.query( + `SELECT id FROM core."viewSort" + WHERE id = $1 AND "deletedAt" IS NULL`, + [sortToRemoveId], + ); + + expect(removedSort.length).toBe(0); + }); + }); + + describe('combined operations', () => { + it('should upsert view fields, filters, filter groups, and sorts in a single call', async () => { + const filterGroupId = uuidv4(); + const filterId = uuidv4(); + const sortId = uuidv4(); + const fieldMetadataId = testSetup.fieldMetadataIds[2]; + const targetField = testSetup.viewFields.find( + (field) => + field.fieldMetadataId !== testSetup.labelIdentifierFieldMetadataId, + )!; + + expect(targetField).toBeDefined(); + + const labelIdentifierField = testSetup.viewFields.find( + (field) => + field.fieldMetadataId === testSetup.labelIdentifierFieldMetadataId, + ); + + const { data, errors } = await upsertViewWidget({ + expectToFail: false, + input: { + widgetId: testSetup.widgetId, + viewFields: [ + ...(labelIdentifierField + ? [ + { + viewFieldId: labelIdentifierField.id, + fieldMetadataId: labelIdentifierField.fieldMetadataId, + isVisible: true, + position: 0, + }, + ] + : []), + { + viewFieldId: targetField.id, + fieldMetadataId: targetField.fieldMetadataId, + isVisible: true, + position: 1, + }, + ], + viewFilterGroups: [ + { + id: filterGroupId, + logicalOperator: ViewFilterGroupLogicalOperator.AND, + }, + ], + viewFilters: [ + { + id: filterId, + fieldMetadataId, + operand: ViewFilterOperand.CONTAINS, + value: 'combined-test', + viewFilterGroupId: filterGroupId, + }, + ], + viewSorts: [ + { + id: sortId, + fieldMetadataId, + direction: ViewSortDirection.DESC, + }, + ], + }, + gqlFields: VIEW_WITH_ALL_RELATIONS_GQL_FIELDS, + }); + + expect(errors).toBeUndefined(); + expect(data.upsertViewWidget).toBeDefined(); + expect(data.upsertViewWidget.id).toBeDefined(); + + const filterGroups = await global.testDataSource.query( + `SELECT id FROM core."viewFilterGroup" + WHERE id = $1 AND "deletedAt" IS NULL`, + [filterGroupId], + ); + + expect(filterGroups.length).toBe(1); + + const filters = await global.testDataSource.query( + `SELECT id, value FROM core."viewFilter" + WHERE id = $1 AND "deletedAt" IS NULL`, + [filterId], + ); + + expect(filters.length).toBe(1); + expect(filters[0].value).toBe('combined-test'); + + const sorts = await global.testDataSource.query( + `SELECT id, direction FROM core."viewSort" + WHERE id = $1 AND "deletedAt" IS NULL`, + [sortId], + ); + + expect(sorts.length).toBe(1); + expect(sorts[0].direction).toBe(ViewSortDirection.DESC); + + await upsertViewWidget({ + expectToFail: false, + input: { + widgetId: testSetup.widgetId, + viewSorts: [], + viewFilters: [], + viewFilterGroups: [], + }, + }); + }); + }); + + describe('validation', () => { + it('should fail when widget id does not exist', async () => { + const { errors } = await upsertViewWidget({ + expectToFail: true, + input: { + widgetId: uuidv4(), + viewFields: [ + { + fieldMetadataId: testSetup.fieldMetadataIds[0], + isVisible: true, + position: 0, + }, + ], + }, + }); + + expect(errors).toBeDefined(); + expect(errors.length).toBeGreaterThan(0); + }); + + it('should return the view without changes when no sub-entity arrays are provided', async () => { + const { data, errors } = await upsertViewWidget({ + expectToFail: false, + input: { + widgetId: testSetup.widgetId, + }, + gqlFields: VIEW_GQL_FIELDS, + }); + + expect(errors).toBeUndefined(); + expect(data.upsertViewWidget).toBeDefined(); + expect(data.upsertViewWidget.id).toBeDefined(); + }); + + it('should remove all filters when an empty array is provided', async () => { + const filterId = uuidv4(); + const fieldMetadataId = testSetup.fieldMetadataIds[0]; + + await upsertViewWidget({ + expectToFail: false, + input: { + widgetId: testSetup.widgetId, + viewFilters: [ + { + id: filterId, + fieldMetadataId, + operand: ViewFilterOperand.CONTAINS, + value: 'to-be-removed', + }, + ], + }, + }); + + const { data: filtersBefore } = await findViewFilters({ + viewId: testSetup.viewId, + gqlFields: 'id', + expectToFail: false, + }); + + const filterExistsBefore = filtersBefore.getViewFilters.find( + (f: { id: string }) => f.id === filterId, + ); + + expect(filterExistsBefore).toBeDefined(); + + await upsertViewWidget({ + expectToFail: false, + input: { + widgetId: testSetup.widgetId, + viewFilters: [], + }, + }); + + const { data: filtersAfter } = await findViewFilters({ + viewId: testSetup.viewId, + gqlFields: 'id', + expectToFail: false, + }); + + const filterExistsAfter = filtersAfter.getViewFilters.find( + (f: { id: string }) => f.id === filterId, + ); + + expect(filterExistsAfter).toBeUndefined(); + }); + + it('should remove all sorts when an empty array is provided', async () => { + const sortId = uuidv4(); + const fieldMetadataId = testSetup.fieldMetadataIds[3]; + + await upsertViewWidget({ + expectToFail: false, + input: { + widgetId: testSetup.widgetId, + viewSorts: [ + { + id: sortId, + fieldMetadataId, + direction: ViewSortDirection.ASC, + }, + ], + }, + }); + + await upsertViewWidget({ + expectToFail: false, + input: { + widgetId: testSetup.widgetId, + viewSorts: [], + }, + }); + + const sortsAfter = await global.testDataSource.query( + `SELECT id FROM core."viewSort" + WHERE id = $1 AND "deletedAt" IS NULL`, + [sortId], + ); + + expect(sortsAfter.length).toBe(0); + }); + }); + + describe('return type', () => { + it('should return a view with the expected fields and relations', async () => { + const targetField = testSetup.viewFields.find( + (field) => + field.fieldMetadataId !== testSetup.labelIdentifierFieldMetadataId, + )!; + + expect(targetField).toBeDefined(); + + const labelIdentifierField = testSetup.viewFields.find( + (field) => + field.fieldMetadataId === testSetup.labelIdentifierFieldMetadataId, + ); + + const { data } = await upsertViewWidget({ + expectToFail: false, + input: { + widgetId: testSetup.widgetId, + viewFields: [ + ...(labelIdentifierField + ? [ + { + viewFieldId: labelIdentifierField.id, + fieldMetadataId: labelIdentifierField.fieldMetadataId, + isVisible: true, + position: 0, + }, + ] + : []), + { + viewFieldId: targetField.id, + fieldMetadataId: targetField.fieldMetadataId, + isVisible: true, + position: 1, + }, + ], + }, + gqlFields: VIEW_WITH_ALL_RELATIONS_GQL_FIELDS, + }); + + const view = data.upsertViewWidget; + + expect(view.id).toBeDefined(); + expect(view.name).toBeDefined(); + expect(view.objectMetadataId).toBeDefined(); + expect(view.workspaceId).toBeDefined(); + expect(view.createdAt).toBeDefined(); + expect(view.updatedAt).toBeDefined(); + expect(Array.isArray(view.viewFields)).toBe(true); + expect(Array.isArray(view.viewFilters)).toBe(true); + expect(Array.isArray(view.viewFilterGroups)).toBe(true); + expect(Array.isArray(view.viewSorts)).toBe(true); + }); + }); +}); diff --git a/packages/twenty-server/test/integration/metadata/suites/view/utils/upsert-view-widget-query-factory.util.ts b/packages/twenty-server/test/integration/metadata/suites/view/utils/upsert-view-widget-query-factory.util.ts new file mode 100644 index 0000000000..9a2c87ce8b --- /dev/null +++ b/packages/twenty-server/test/integration/metadata/suites/view/utils/upsert-view-widget-query-factory.util.ts @@ -0,0 +1,23 @@ +import gql from 'graphql-tag'; +import { VIEW_GQL_FIELDS } from 'test/integration/constants/view-gql-fields.constants'; + +import { type UpsertViewWidgetInput } from 'src/engine/metadata-modules/view/dtos/inputs/upsert-view-widget.input'; + +export const upsertViewWidgetQueryFactory = ({ + gqlFields = VIEW_GQL_FIELDS, + input, +}: { + gqlFields?: string; + input: UpsertViewWidgetInput; +}) => ({ + query: gql` + mutation UpsertViewWidget($input: UpsertViewWidgetInput!) { + upsertViewWidget(input: $input) { + ${gqlFields} + } + } + `, + variables: { + input, + }, +}); diff --git a/packages/twenty-server/test/integration/metadata/suites/view/utils/upsert-view-widget.util.ts b/packages/twenty-server/test/integration/metadata/suites/view/utils/upsert-view-widget.util.ts new file mode 100644 index 0000000000..74c93b4a01 --- /dev/null +++ b/packages/twenty-server/test/integration/metadata/suites/view/utils/upsert-view-widget.util.ts @@ -0,0 +1,43 @@ +import { makeMetadataAPIRequest } from 'test/integration/metadata/suites/utils/make-metadata-api-request.util'; +import { upsertViewWidgetQueryFactory } from 'test/integration/metadata/suites/view/utils/upsert-view-widget-query-factory.util'; +import { type CommonResponseBody } from 'test/integration/metadata/types/common-response-body.type'; +import { warnIfErrorButNotExpectedToFail } from 'test/integration/metadata/utils/warn-if-error-but-not-expected-to-fail.util'; +import { warnIfNoErrorButExpectedToFail } from 'test/integration/metadata/utils/warn-if-no-error-but-expected-to-fail.util'; + +import { type UpsertViewWidgetInput } from 'src/engine/metadata-modules/view/dtos/inputs/upsert-view-widget.input'; +import { type ViewDTO } from 'src/engine/metadata-modules/view/dtos/view.dto'; + +export const upsertViewWidget = async ({ + input, + gqlFields, + expectToFail, +}: { + input: UpsertViewWidgetInput; + gqlFields?: string; + expectToFail?: boolean | null; +}): CommonResponseBody<{ + upsertViewWidget: ViewDTO; +}> => { + const graphqlOperation = upsertViewWidgetQueryFactory({ + input, + gqlFields, + }); + + const response = await makeMetadataAPIRequest(graphqlOperation); + + if (expectToFail === true) { + warnIfNoErrorButExpectedToFail({ + response, + errorMessage: 'Upsert view widget should have failed but did not', + }); + } + + if (expectToFail === false) { + warnIfErrorButNotExpectedToFail({ + response, + errorMessage: 'Upsert view widget has failed but should not', + }); + } + + return { data: response.body.data, errors: response.body.errors }; +};