breaking (soft) - Migrate viewGroup.fieldMetadataId -> view.mainGroupByFieldMetadataId (2/3) (#16277)
Should be merged once https://github.com/twentyhq/twenty/pull/16206 has been released + command run to prod In this PR - Remove usage of viewGroup.fieldMetadataId, both in BE and FE states. - But we still need to properly populate it until we fully remove viewGroup.fieldMetadataId from db and ORM entity (upcoming 3rd PR out of 3). fieldMetadataId was removed from CoreViewGroup type and CreateViewGroupInput and is determined BE-side based on the associated view's mainGroupByFieldMetadataId. **I expect this means a downtime on viewGroup creation, until both FE and BE are deployed and cache is flushed.** This seems acceptable to me as it only regards viewGroup creation. - this information is replaced by view.mainGroupByFieldMetadataID - Handle view group creation, update and deletion in the BE as a side-effect of a view creation, update or deletion. Optimistic effects are still used - Add validation at view creation or update regarding mainGroupByFieldMetadata Left to do in 3rd PR - Remove viewGroup.fieldMetadataId from db and ORM entity - Restore feature allowing to update an existing grouped view's group by field (already OK on BE side but need to rebuild FE optimistic)
This commit is contained in:
@@ -753,7 +753,6 @@ export type CoreViewGroup = {
|
||||
__typename?: 'CoreViewGroup';
|
||||
createdAt: Scalars['DateTime'];
|
||||
deletedAt?: Maybe<Scalars['DateTime']>;
|
||||
fieldMetadataId: Scalars['UUID'];
|
||||
fieldValue: Scalars['String'];
|
||||
id: Scalars['UUID'];
|
||||
isVisible: Scalars['Boolean'];
|
||||
@@ -963,7 +962,6 @@ export type CreateViewFilterInput = {
|
||||
};
|
||||
|
||||
export type CreateViewGroupInput = {
|
||||
fieldMetadataId: Scalars['UUID'];
|
||||
fieldValue: Scalars['String'];
|
||||
id?: InputMaybe<Scalars['UUID']>;
|
||||
isVisible?: InputMaybe<Scalars['Boolean']>;
|
||||
@@ -6069,9 +6067,9 @@ export type ViewFilterFragmentFragment = { __typename?: 'CoreViewFilter', id: st
|
||||
|
||||
export type ViewFilterGroupFragmentFragment = { __typename?: 'CoreViewFilterGroup', id: string, parentViewFilterGroupId?: string | null, logicalOperator: ViewFilterGroupLogicalOperator, positionInViewFilterGroup?: number | null, viewId: string };
|
||||
|
||||
export type ViewFragmentFragment = { __typename?: 'CoreView', 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, anyFieldFilterValue?: string | null, calendarFieldMetadataId?: string | null, calendarLayout?: ViewCalendarLayout | null, visibility: ViewVisibility, createdByUserWorkspaceId?: string | null, viewFields: Array<{ __typename?: 'CoreViewField', id: string, fieldMetadataId: string, viewId: string, isVisible: boolean, position: number, size: number, aggregateOperation?: AggregateOperations | null, createdAt: string, updatedAt: string, deletedAt?: string | null }>, viewFilters: Array<{ __typename?: 'CoreViewFilter', 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?: 'CoreViewFilterGroup', id: string, parentViewFilterGroupId?: string | null, logicalOperator: ViewFilterGroupLogicalOperator, positionInViewFilterGroup?: number | null, viewId: string }>, viewSorts: Array<{ __typename?: 'CoreViewSort', id: string, fieldMetadataId: string, direction: ViewSortDirection, viewId: string }>, viewGroups: Array<{ __typename?: 'CoreViewGroup', id: string, fieldMetadataId: string, isVisible: boolean, fieldValue: string, position: number, viewId: string, createdAt: string, updatedAt: string, deletedAt?: string | null }> };
|
||||
export type ViewFragmentFragment = { __typename?: 'CoreView', 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, anyFieldFilterValue?: string | null, calendarFieldMetadataId?: string | null, calendarLayout?: ViewCalendarLayout | null, visibility: ViewVisibility, createdByUserWorkspaceId?: string | null, viewFields: Array<{ __typename?: 'CoreViewField', id: string, fieldMetadataId: string, viewId: string, isVisible: boolean, position: number, size: number, aggregateOperation?: AggregateOperations | null, createdAt: string, updatedAt: string, deletedAt?: string | null }>, viewFilters: Array<{ __typename?: 'CoreViewFilter', 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?: 'CoreViewFilterGroup', id: string, parentViewFilterGroupId?: string | null, logicalOperator: ViewFilterGroupLogicalOperator, positionInViewFilterGroup?: number | null, viewId: string }>, viewSorts: Array<{ __typename?: 'CoreViewSort', id: string, fieldMetadataId: string, direction: ViewSortDirection, viewId: string }>, viewGroups: Array<{ __typename?: 'CoreViewGroup', id: string, isVisible: boolean, fieldValue: string, position: number, viewId: string, createdAt: string, updatedAt: string, deletedAt?: string | null }> };
|
||||
|
||||
export type ViewGroupFragmentFragment = { __typename?: 'CoreViewGroup', id: string, fieldMetadataId: string, isVisible: boolean, fieldValue: string, position: number, viewId: string, createdAt: string, updatedAt: string, deletedAt?: string | null };
|
||||
export type ViewGroupFragmentFragment = { __typename?: 'CoreViewGroup', id: string, isVisible: boolean, fieldValue: string, position: number, viewId: string, createdAt: string, updatedAt: string, deletedAt?: string | null };
|
||||
|
||||
export type ViewSortFragmentFragment = { __typename?: 'CoreViewSort', id: string, fieldMetadataId: string, direction: ViewSortDirection, viewId: string };
|
||||
|
||||
@@ -6080,7 +6078,7 @@ export type CreateCoreViewMutationVariables = Exact<{
|
||||
}>;
|
||||
|
||||
|
||||
export type CreateCoreViewMutation = { __typename?: 'Mutation', createCoreView: { __typename?: 'CoreView', 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, anyFieldFilterValue?: string | null, calendarFieldMetadataId?: string | null, calendarLayout?: ViewCalendarLayout | null, visibility: ViewVisibility, createdByUserWorkspaceId?: string | null, viewFields: Array<{ __typename?: 'CoreViewField', id: string, fieldMetadataId: string, viewId: string, isVisible: boolean, position: number, size: number, aggregateOperation?: AggregateOperations | null, createdAt: string, updatedAt: string, deletedAt?: string | null }>, viewFilters: Array<{ __typename?: 'CoreViewFilter', 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?: 'CoreViewFilterGroup', id: string, parentViewFilterGroupId?: string | null, logicalOperator: ViewFilterGroupLogicalOperator, positionInViewFilterGroup?: number | null, viewId: string }>, viewSorts: Array<{ __typename?: 'CoreViewSort', id: string, fieldMetadataId: string, direction: ViewSortDirection, viewId: string }>, viewGroups: Array<{ __typename?: 'CoreViewGroup', id: string, fieldMetadataId: string, isVisible: boolean, fieldValue: string, position: number, viewId: string, createdAt: string, updatedAt: string, deletedAt?: string | null }> } };
|
||||
export type CreateCoreViewMutation = { __typename?: 'Mutation', createCoreView: { __typename?: 'CoreView', 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, anyFieldFilterValue?: string | null, calendarFieldMetadataId?: string | null, calendarLayout?: ViewCalendarLayout | null, visibility: ViewVisibility, createdByUserWorkspaceId?: string | null, viewFields: Array<{ __typename?: 'CoreViewField', id: string, fieldMetadataId: string, viewId: string, isVisible: boolean, position: number, size: number, aggregateOperation?: AggregateOperations | null, createdAt: string, updatedAt: string, deletedAt?: string | null }>, viewFilters: Array<{ __typename?: 'CoreViewFilter', 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?: 'CoreViewFilterGroup', id: string, parentViewFilterGroupId?: string | null, logicalOperator: ViewFilterGroupLogicalOperator, positionInViewFilterGroup?: number | null, viewId: string }>, viewSorts: Array<{ __typename?: 'CoreViewSort', id: string, fieldMetadataId: string, direction: ViewSortDirection, viewId: string }>, viewGroups: Array<{ __typename?: 'CoreViewGroup', id: string, isVisible: boolean, fieldValue: string, position: number, viewId: string, createdAt: string, updatedAt: string, deletedAt?: string | null }> } };
|
||||
|
||||
export type CreateCoreViewFieldMutationVariables = Exact<{
|
||||
input: CreateViewFieldInput;
|
||||
@@ -6108,7 +6106,7 @@ export type CreateCoreViewGroupMutationVariables = Exact<{
|
||||
}>;
|
||||
|
||||
|
||||
export type CreateCoreViewGroupMutation = { __typename?: 'Mutation', createCoreViewGroup: { __typename?: 'CoreViewGroup', id: string, fieldMetadataId: string, isVisible: boolean, fieldValue: string, position: number, viewId: string, createdAt: string, updatedAt: string, deletedAt?: string | null } };
|
||||
export type CreateCoreViewGroupMutation = { __typename?: 'Mutation', createCoreViewGroup: { __typename?: 'CoreViewGroup', id: string, isVisible: boolean, fieldValue: string, position: number, viewId: string, createdAt: string, updatedAt: string, deletedAt?: string | null } };
|
||||
|
||||
export type CreateCoreViewSortMutationVariables = Exact<{
|
||||
input: CreateViewSortInput;
|
||||
@@ -6129,7 +6127,7 @@ export type CreateManyCoreViewGroupsMutationVariables = Exact<{
|
||||
}>;
|
||||
|
||||
|
||||
export type CreateManyCoreViewGroupsMutation = { __typename?: 'Mutation', createManyCoreViewGroups: Array<{ __typename?: 'CoreViewGroup', id: string, fieldMetadataId: string, isVisible: boolean, fieldValue: string, position: number, viewId: string, createdAt: string, updatedAt: string, deletedAt?: string | null }> };
|
||||
export type CreateManyCoreViewGroupsMutation = { __typename?: 'Mutation', createManyCoreViewGroups: Array<{ __typename?: 'CoreViewGroup', id: string, isVisible: boolean, fieldValue: string, position: number, viewId: string, createdAt: string, updatedAt: string, deletedAt?: string | null }> };
|
||||
|
||||
export type DeleteCoreViewMutationVariables = Exact<{
|
||||
id: Scalars['String'];
|
||||
@@ -6164,7 +6162,7 @@ export type DeleteCoreViewGroupMutationVariables = Exact<{
|
||||
}>;
|
||||
|
||||
|
||||
export type DeleteCoreViewGroupMutation = { __typename?: 'Mutation', deleteCoreViewGroup: { __typename?: 'CoreViewGroup', id: string, fieldMetadataId: string, isVisible: boolean, fieldValue: string, position: number, viewId: string, createdAt: string, updatedAt: string, deletedAt?: string | null } };
|
||||
export type DeleteCoreViewGroupMutation = { __typename?: 'Mutation', deleteCoreViewGroup: { __typename?: 'CoreViewGroup', id: string, isVisible: boolean, fieldValue: string, position: number, viewId: string, createdAt: string, updatedAt: string, deletedAt?: string | null } };
|
||||
|
||||
export type DeleteCoreViewSortMutationVariables = Exact<{
|
||||
id: Scalars['String'];
|
||||
@@ -6206,7 +6204,7 @@ export type DestroyCoreViewGroupMutationVariables = Exact<{
|
||||
}>;
|
||||
|
||||
|
||||
export type DestroyCoreViewGroupMutation = { __typename?: 'Mutation', destroyCoreViewGroup: { __typename?: 'CoreViewGroup', id: string, fieldMetadataId: string, isVisible: boolean, fieldValue: string, position: number, viewId: string, createdAt: string, updatedAt: string, deletedAt?: string | null } };
|
||||
export type DestroyCoreViewGroupMutation = { __typename?: 'Mutation', destroyCoreViewGroup: { __typename?: 'CoreViewGroup', id: string, isVisible: boolean, fieldValue: string, position: number, viewId: string, createdAt: string, updatedAt: string, deletedAt?: string | null } };
|
||||
|
||||
export type DestroyCoreViewSortMutationVariables = Exact<{
|
||||
id: Scalars['String'];
|
||||
@@ -6221,7 +6219,7 @@ export type UpdateCoreViewMutationVariables = Exact<{
|
||||
}>;
|
||||
|
||||
|
||||
export type UpdateCoreViewMutation = { __typename?: 'Mutation', updateCoreView: { __typename?: 'CoreView', 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, anyFieldFilterValue?: string | null, calendarFieldMetadataId?: string | null, calendarLayout?: ViewCalendarLayout | null, visibility: ViewVisibility, createdByUserWorkspaceId?: string | null, viewFields: Array<{ __typename?: 'CoreViewField', id: string, fieldMetadataId: string, viewId: string, isVisible: boolean, position: number, size: number, aggregateOperation?: AggregateOperations | null, createdAt: string, updatedAt: string, deletedAt?: string | null }>, viewFilters: Array<{ __typename?: 'CoreViewFilter', 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?: 'CoreViewFilterGroup', id: string, parentViewFilterGroupId?: string | null, logicalOperator: ViewFilterGroupLogicalOperator, positionInViewFilterGroup?: number | null, viewId: string }>, viewSorts: Array<{ __typename?: 'CoreViewSort', id: string, fieldMetadataId: string, direction: ViewSortDirection, viewId: string }>, viewGroups: Array<{ __typename?: 'CoreViewGroup', id: string, fieldMetadataId: string, isVisible: boolean, fieldValue: string, position: number, viewId: string, createdAt: string, updatedAt: string, deletedAt?: string | null }> } };
|
||||
export type UpdateCoreViewMutation = { __typename?: 'Mutation', updateCoreView: { __typename?: 'CoreView', 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, anyFieldFilterValue?: string | null, calendarFieldMetadataId?: string | null, calendarLayout?: ViewCalendarLayout | null, visibility: ViewVisibility, createdByUserWorkspaceId?: string | null, viewFields: Array<{ __typename?: 'CoreViewField', id: string, fieldMetadataId: string, viewId: string, isVisible: boolean, position: number, size: number, aggregateOperation?: AggregateOperations | null, createdAt: string, updatedAt: string, deletedAt?: string | null }>, viewFilters: Array<{ __typename?: 'CoreViewFilter', 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?: 'CoreViewFilterGroup', id: string, parentViewFilterGroupId?: string | null, logicalOperator: ViewFilterGroupLogicalOperator, positionInViewFilterGroup?: number | null, viewId: string }>, viewSorts: Array<{ __typename?: 'CoreViewSort', id: string, fieldMetadataId: string, direction: ViewSortDirection, viewId: string }>, viewGroups: Array<{ __typename?: 'CoreViewGroup', id: string, isVisible: boolean, fieldValue: string, position: number, viewId: string, createdAt: string, updatedAt: string, deletedAt?: string | null }> } };
|
||||
|
||||
export type UpdateCoreViewFieldMutationVariables = Exact<{
|
||||
input: UpdateViewFieldInput;
|
||||
@@ -6250,7 +6248,7 @@ export type UpdateCoreViewGroupMutationVariables = Exact<{
|
||||
}>;
|
||||
|
||||
|
||||
export type UpdateCoreViewGroupMutation = { __typename?: 'Mutation', updateCoreViewGroup: { __typename?: 'CoreViewGroup', id: string, fieldMetadataId: string, isVisible: boolean, fieldValue: string, position: number, viewId: string, createdAt: string, updatedAt: string, deletedAt?: string | null } };
|
||||
export type UpdateCoreViewGroupMutation = { __typename?: 'Mutation', updateCoreViewGroup: { __typename?: 'CoreViewGroup', id: string, isVisible: boolean, fieldValue: string, position: number, viewId: string, createdAt: string, updatedAt: string, deletedAt?: string | null } };
|
||||
|
||||
export type UpdateCoreViewSortMutationVariables = Exact<{
|
||||
id: Scalars['String'];
|
||||
@@ -6263,7 +6261,7 @@ export type UpdateCoreViewSortMutation = { __typename?: 'Mutation', updateCoreVi
|
||||
export type FindAllCoreViewsQueryVariables = Exact<{ [key: string]: never; }>;
|
||||
|
||||
|
||||
export type FindAllCoreViewsQuery = { __typename?: 'Query', getCoreViews: Array<{ __typename?: 'CoreView', 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, anyFieldFilterValue?: string | null, calendarFieldMetadataId?: string | null, calendarLayout?: ViewCalendarLayout | null, visibility: ViewVisibility, createdByUserWorkspaceId?: string | null, viewFields: Array<{ __typename?: 'CoreViewField', id: string, fieldMetadataId: string, viewId: string, isVisible: boolean, position: number, size: number, aggregateOperation?: AggregateOperations | null, createdAt: string, updatedAt: string, deletedAt?: string | null }>, viewFilters: Array<{ __typename?: 'CoreViewFilter', 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?: 'CoreViewFilterGroup', id: string, parentViewFilterGroupId?: string | null, logicalOperator: ViewFilterGroupLogicalOperator, positionInViewFilterGroup?: number | null, viewId: string }>, viewSorts: Array<{ __typename?: 'CoreViewSort', id: string, fieldMetadataId: string, direction: ViewSortDirection, viewId: string }>, viewGroups: Array<{ __typename?: 'CoreViewGroup', id: string, fieldMetadataId: string, isVisible: boolean, fieldValue: string, position: number, viewId: string, createdAt: string, updatedAt: string, deletedAt?: string | null }> }> };
|
||||
export type FindAllCoreViewsQuery = { __typename?: 'Query', getCoreViews: Array<{ __typename?: 'CoreView', 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, anyFieldFilterValue?: string | null, calendarFieldMetadataId?: string | null, calendarLayout?: ViewCalendarLayout | null, visibility: ViewVisibility, createdByUserWorkspaceId?: string | null, viewFields: Array<{ __typename?: 'CoreViewField', id: string, fieldMetadataId: string, viewId: string, isVisible: boolean, position: number, size: number, aggregateOperation?: AggregateOperations | null, createdAt: string, updatedAt: string, deletedAt?: string | null }>, viewFilters: Array<{ __typename?: 'CoreViewFilter', 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?: 'CoreViewFilterGroup', id: string, parentViewFilterGroupId?: string | null, logicalOperator: ViewFilterGroupLogicalOperator, positionInViewFilterGroup?: number | null, viewId: string }>, viewSorts: Array<{ __typename?: 'CoreViewSort', id: string, fieldMetadataId: string, direction: ViewSortDirection, viewId: string }>, viewGroups: Array<{ __typename?: 'CoreViewGroup', id: string, isVisible: boolean, fieldValue: string, position: number, viewId: string, createdAt: string, updatedAt: string, deletedAt?: string | null }> }> };
|
||||
|
||||
export type FindManyCoreViewFieldsQueryVariables = Exact<{
|
||||
viewId: Scalars['String'];
|
||||
@@ -6291,7 +6289,7 @@ export type FindManyCoreViewGroupsQueryVariables = Exact<{
|
||||
}>;
|
||||
|
||||
|
||||
export type FindManyCoreViewGroupsQuery = { __typename?: 'Query', getCoreViewGroups: Array<{ __typename?: 'CoreViewGroup', id: string, fieldMetadataId: string, isVisible: boolean, fieldValue: string, position: number, viewId: string, createdAt: string, updatedAt: string, deletedAt?: string | null }> };
|
||||
export type FindManyCoreViewGroupsQuery = { __typename?: 'Query', getCoreViewGroups: Array<{ __typename?: 'CoreViewGroup', id: string, isVisible: boolean, fieldValue: string, position: number, viewId: string, createdAt: string, updatedAt: string, deletedAt?: string | null }> };
|
||||
|
||||
export type FindManyCoreViewSortsQueryVariables = Exact<{
|
||||
viewId?: InputMaybe<Scalars['String']>;
|
||||
@@ -6305,14 +6303,14 @@ export type FindManyCoreViewsQueryVariables = Exact<{
|
||||
}>;
|
||||
|
||||
|
||||
export type FindManyCoreViewsQuery = { __typename?: 'Query', getCoreViews: Array<{ __typename?: 'CoreView', 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, anyFieldFilterValue?: string | null, calendarFieldMetadataId?: string | null, calendarLayout?: ViewCalendarLayout | null, visibility: ViewVisibility, createdByUserWorkspaceId?: string | null, viewFields: Array<{ __typename?: 'CoreViewField', id: string, fieldMetadataId: string, viewId: string, isVisible: boolean, position: number, size: number, aggregateOperation?: AggregateOperations | null, createdAt: string, updatedAt: string, deletedAt?: string | null }>, viewFilters: Array<{ __typename?: 'CoreViewFilter', 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?: 'CoreViewFilterGroup', id: string, parentViewFilterGroupId?: string | null, logicalOperator: ViewFilterGroupLogicalOperator, positionInViewFilterGroup?: number | null, viewId: string }>, viewSorts: Array<{ __typename?: 'CoreViewSort', id: string, fieldMetadataId: string, direction: ViewSortDirection, viewId: string }>, viewGroups: Array<{ __typename?: 'CoreViewGroup', id: string, fieldMetadataId: string, isVisible: boolean, fieldValue: string, position: number, viewId: string, createdAt: string, updatedAt: string, deletedAt?: string | null }> }> };
|
||||
export type FindManyCoreViewsQuery = { __typename?: 'Query', getCoreViews: Array<{ __typename?: 'CoreView', 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, anyFieldFilterValue?: string | null, calendarFieldMetadataId?: string | null, calendarLayout?: ViewCalendarLayout | null, visibility: ViewVisibility, createdByUserWorkspaceId?: string | null, viewFields: Array<{ __typename?: 'CoreViewField', id: string, fieldMetadataId: string, viewId: string, isVisible: boolean, position: number, size: number, aggregateOperation?: AggregateOperations | null, createdAt: string, updatedAt: string, deletedAt?: string | null }>, viewFilters: Array<{ __typename?: 'CoreViewFilter', 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?: 'CoreViewFilterGroup', id: string, parentViewFilterGroupId?: string | null, logicalOperator: ViewFilterGroupLogicalOperator, positionInViewFilterGroup?: number | null, viewId: string }>, viewSorts: Array<{ __typename?: 'CoreViewSort', id: string, fieldMetadataId: string, direction: ViewSortDirection, viewId: string }>, viewGroups: Array<{ __typename?: 'CoreViewGroup', id: string, isVisible: boolean, fieldValue: string, position: number, viewId: string, createdAt: string, updatedAt: string, deletedAt?: string | null }> }> };
|
||||
|
||||
export type FindOneCoreViewQueryVariables = Exact<{
|
||||
id: Scalars['String'];
|
||||
}>;
|
||||
|
||||
|
||||
export type FindOneCoreViewQuery = { __typename?: 'Query', getCoreView?: { __typename?: 'CoreView', 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, anyFieldFilterValue?: string | null, calendarFieldMetadataId?: string | null, calendarLayout?: ViewCalendarLayout | null, visibility: ViewVisibility, createdByUserWorkspaceId?: string | null, viewFields: Array<{ __typename?: 'CoreViewField', id: string, fieldMetadataId: string, viewId: string, isVisible: boolean, position: number, size: number, aggregateOperation?: AggregateOperations | null, createdAt: string, updatedAt: string, deletedAt?: string | null }>, viewFilters: Array<{ __typename?: 'CoreViewFilter', 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?: 'CoreViewFilterGroup', id: string, parentViewFilterGroupId?: string | null, logicalOperator: ViewFilterGroupLogicalOperator, positionInViewFilterGroup?: number | null, viewId: string }>, viewSorts: Array<{ __typename?: 'CoreViewSort', id: string, fieldMetadataId: string, direction: ViewSortDirection, viewId: string }>, viewGroups: Array<{ __typename?: 'CoreViewGroup', id: string, fieldMetadataId: string, isVisible: boolean, fieldValue: string, position: number, viewId: string, createdAt: string, updatedAt: string, deletedAt?: string | null }> } | null };
|
||||
export type FindOneCoreViewQuery = { __typename?: 'Query', getCoreView?: { __typename?: 'CoreView', 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, anyFieldFilterValue?: string | null, calendarFieldMetadataId?: string | null, calendarLayout?: ViewCalendarLayout | null, visibility: ViewVisibility, createdByUserWorkspaceId?: string | null, viewFields: Array<{ __typename?: 'CoreViewField', id: string, fieldMetadataId: string, viewId: string, isVisible: boolean, position: number, size: number, aggregateOperation?: AggregateOperations | null, createdAt: string, updatedAt: string, deletedAt?: string | null }>, viewFilters: Array<{ __typename?: 'CoreViewFilter', 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?: 'CoreViewFilterGroup', id: string, parentViewFilterGroupId?: string | null, logicalOperator: ViewFilterGroupLogicalOperator, positionInViewFilterGroup?: number | null, viewId: string }>, viewSorts: Array<{ __typename?: 'CoreViewSort', id: string, fieldMetadataId: string, direction: ViewSortDirection, viewId: string }>, viewGroups: Array<{ __typename?: 'CoreViewGroup', id: string, isVisible: boolean, fieldValue: string, position: number, viewId: string, createdAt: string, updatedAt: string, deletedAt?: string | null }> } | null };
|
||||
|
||||
export type FindOneCoreViewFieldQueryVariables = Exact<{
|
||||
id: Scalars['String'];
|
||||
@@ -6340,7 +6338,7 @@ export type FindOneCoreViewGroupQueryVariables = Exact<{
|
||||
}>;
|
||||
|
||||
|
||||
export type FindOneCoreViewGroupQuery = { __typename?: 'Query', getCoreViewGroup?: { __typename?: 'CoreViewGroup', id: string, fieldMetadataId: string, isVisible: boolean, fieldValue: string, position: number, viewId: string, createdAt: string, updatedAt: string, deletedAt?: string | null } | null };
|
||||
export type FindOneCoreViewGroupQuery = { __typename?: 'Query', getCoreViewGroup?: { __typename?: 'CoreViewGroup', id: string, isVisible: boolean, fieldValue: string, position: number, viewId: string, createdAt: string, updatedAt: string, deletedAt?: string | null } | null };
|
||||
|
||||
export type FindOneCoreViewSortQueryVariables = Exact<{
|
||||
id: Scalars['String'];
|
||||
@@ -7142,7 +7140,6 @@ export const ViewSortFragmentFragmentDoc = gql`
|
||||
export const ViewGroupFragmentFragmentDoc = gql`
|
||||
fragment ViewGroupFragment on CoreViewGroup {
|
||||
id
|
||||
fieldMetadataId
|
||||
isVisible
|
||||
fieldValue
|
||||
position
|
||||
@@ -7165,6 +7162,7 @@ export const ViewFragmentFragmentDoc = gql`
|
||||
openRecordIn
|
||||
kanbanAggregateOperation
|
||||
kanbanAggregateOperationFieldMetadataId
|
||||
mainGroupByFieldMetadataId
|
||||
anyFieldFilterValue
|
||||
calendarFieldMetadataId
|
||||
calendarLayout
|
||||
|
||||
@@ -753,7 +753,6 @@ export type CoreViewGroup = {
|
||||
__typename?: 'CoreViewGroup';
|
||||
createdAt: Scalars['DateTime'];
|
||||
deletedAt?: Maybe<Scalars['DateTime']>;
|
||||
fieldMetadataId: Scalars['UUID'];
|
||||
fieldValue: Scalars['String'];
|
||||
id: Scalars['UUID'];
|
||||
isVisible: Scalars['Boolean'];
|
||||
@@ -946,7 +945,6 @@ export type CreateViewFilterInput = {
|
||||
};
|
||||
|
||||
export type CreateViewGroupInput = {
|
||||
fieldMetadataId: Scalars['UUID'];
|
||||
fieldValue: Scalars['String'];
|
||||
id?: InputMaybe<Scalars['UUID']>;
|
||||
isVisible?: InputMaybe<Scalars['Boolean']>;
|
||||
@@ -4847,9 +4845,9 @@ export type ViewFilterFragmentFragment = { __typename?: 'CoreViewFilter', id: an
|
||||
|
||||
export type ViewFilterGroupFragmentFragment = { __typename?: 'CoreViewFilterGroup', id: any, parentViewFilterGroupId?: any | null, logicalOperator: ViewFilterGroupLogicalOperator, positionInViewFilterGroup?: number | null, viewId: any };
|
||||
|
||||
export type ViewFragmentFragment = { __typename?: 'CoreView', id: any, name: string, objectMetadataId: any, type: ViewType, key?: ViewKey | null, icon: string, position: number, isCompact: boolean, openRecordIn: ViewOpenRecordIn, kanbanAggregateOperation?: AggregateOperations | null, kanbanAggregateOperationFieldMetadataId?: any | null, anyFieldFilterValue?: string | null, calendarFieldMetadataId?: any | null, calendarLayout?: ViewCalendarLayout | null, visibility: ViewVisibility, createdByUserWorkspaceId?: any | null, viewFields: Array<{ __typename?: 'CoreViewField', id: any, fieldMetadataId: any, viewId: any, isVisible: boolean, position: number, size: number, aggregateOperation?: AggregateOperations | null, createdAt: string, updatedAt: string, deletedAt?: string | null }>, viewFilters: Array<{ __typename?: 'CoreViewFilter', id: any, fieldMetadataId: any, operand: ViewFilterOperand, value: any, viewFilterGroupId?: any | null, positionInViewFilterGroup?: number | null, subFieldName?: string | null, viewId: any, createdAt: string, updatedAt: string, deletedAt?: string | null }>, viewFilterGroups: Array<{ __typename?: 'CoreViewFilterGroup', id: any, parentViewFilterGroupId?: any | null, logicalOperator: ViewFilterGroupLogicalOperator, positionInViewFilterGroup?: number | null, viewId: any }>, viewSorts: Array<{ __typename?: 'CoreViewSort', id: any, fieldMetadataId: any, direction: ViewSortDirection, viewId: any }>, viewGroups: Array<{ __typename?: 'CoreViewGroup', id: any, fieldMetadataId: any, isVisible: boolean, fieldValue: string, position: number, viewId: any, createdAt: string, updatedAt: string, deletedAt?: string | null }> };
|
||||
export type ViewFragmentFragment = { __typename?: 'CoreView', id: any, name: string, objectMetadataId: any, type: ViewType, key?: ViewKey | null, icon: string, position: number, isCompact: boolean, openRecordIn: ViewOpenRecordIn, kanbanAggregateOperation?: AggregateOperations | null, kanbanAggregateOperationFieldMetadataId?: any | null, mainGroupByFieldMetadataId?: any | null, anyFieldFilterValue?: string | null, calendarFieldMetadataId?: any | null, calendarLayout?: ViewCalendarLayout | null, visibility: ViewVisibility, createdByUserWorkspaceId?: any | null, viewFields: Array<{ __typename?: 'CoreViewField', id: any, fieldMetadataId: any, viewId: any, isVisible: boolean, position: number, size: number, aggregateOperation?: AggregateOperations | null, createdAt: string, updatedAt: string, deletedAt?: string | null }>, viewFilters: Array<{ __typename?: 'CoreViewFilter', id: any, fieldMetadataId: any, operand: ViewFilterOperand, value: any, viewFilterGroupId?: any | null, positionInViewFilterGroup?: number | null, subFieldName?: string | null, viewId: any, createdAt: string, updatedAt: string, deletedAt?: string | null }>, viewFilterGroups: Array<{ __typename?: 'CoreViewFilterGroup', id: any, parentViewFilterGroupId?: any | null, logicalOperator: ViewFilterGroupLogicalOperator, positionInViewFilterGroup?: number | null, viewId: any }>, viewSorts: Array<{ __typename?: 'CoreViewSort', id: any, fieldMetadataId: any, direction: ViewSortDirection, viewId: any }>, viewGroups: Array<{ __typename?: 'CoreViewGroup', id: any, isVisible: boolean, fieldValue: string, position: number, viewId: any, createdAt: string, updatedAt: string, deletedAt?: string | null }> };
|
||||
|
||||
export type ViewGroupFragmentFragment = { __typename?: 'CoreViewGroup', id: any, fieldMetadataId: any, isVisible: boolean, fieldValue: string, position: number, viewId: any, createdAt: string, updatedAt: string, deletedAt?: string | null };
|
||||
export type ViewGroupFragmentFragment = { __typename?: 'CoreViewGroup', id: any, isVisible: boolean, fieldValue: string, position: number, viewId: any, createdAt: string, updatedAt: string, deletedAt?: string | null };
|
||||
|
||||
export type ViewSortFragmentFragment = { __typename?: 'CoreViewSort', id: any, fieldMetadataId: any, direction: ViewSortDirection, viewId: any };
|
||||
|
||||
@@ -4858,7 +4856,7 @@ export type CreateCoreViewMutationVariables = Exact<{
|
||||
}>;
|
||||
|
||||
|
||||
export type CreateCoreViewMutation = { __typename?: 'Mutation', createCoreView: { __typename?: 'CoreView', id: any, name: string, objectMetadataId: any, type: ViewType, key?: ViewKey | null, icon: string, position: number, isCompact: boolean, openRecordIn: ViewOpenRecordIn, kanbanAggregateOperation?: AggregateOperations | null, kanbanAggregateOperationFieldMetadataId?: any | null, anyFieldFilterValue?: string | null, calendarFieldMetadataId?: any | null, calendarLayout?: ViewCalendarLayout | null, visibility: ViewVisibility, createdByUserWorkspaceId?: any | null, viewFields: Array<{ __typename?: 'CoreViewField', id: any, fieldMetadataId: any, viewId: any, isVisible: boolean, position: number, size: number, aggregateOperation?: AggregateOperations | null, createdAt: string, updatedAt: string, deletedAt?: string | null }>, viewFilters: Array<{ __typename?: 'CoreViewFilter', id: any, fieldMetadataId: any, operand: ViewFilterOperand, value: any, viewFilterGroupId?: any | null, positionInViewFilterGroup?: number | null, subFieldName?: string | null, viewId: any, createdAt: string, updatedAt: string, deletedAt?: string | null }>, viewFilterGroups: Array<{ __typename?: 'CoreViewFilterGroup', id: any, parentViewFilterGroupId?: any | null, logicalOperator: ViewFilterGroupLogicalOperator, positionInViewFilterGroup?: number | null, viewId: any }>, viewSorts: Array<{ __typename?: 'CoreViewSort', id: any, fieldMetadataId: any, direction: ViewSortDirection, viewId: any }>, viewGroups: Array<{ __typename?: 'CoreViewGroup', id: any, fieldMetadataId: any, isVisible: boolean, fieldValue: string, position: number, viewId: any, createdAt: string, updatedAt: string, deletedAt?: string | null }> } };
|
||||
export type CreateCoreViewMutation = { __typename?: 'Mutation', createCoreView: { __typename?: 'CoreView', id: any, name: string, objectMetadataId: any, type: ViewType, key?: ViewKey | null, icon: string, position: number, isCompact: boolean, openRecordIn: ViewOpenRecordIn, kanbanAggregateOperation?: AggregateOperations | null, kanbanAggregateOperationFieldMetadataId?: any | null, mainGroupByFieldMetadataId?: any | null, anyFieldFilterValue?: string | null, calendarFieldMetadataId?: any | null, calendarLayout?: ViewCalendarLayout | null, visibility: ViewVisibility, createdByUserWorkspaceId?: any | null, viewFields: Array<{ __typename?: 'CoreViewField', id: any, fieldMetadataId: any, viewId: any, isVisible: boolean, position: number, size: number, aggregateOperation?: AggregateOperations | null, createdAt: string, updatedAt: string, deletedAt?: string | null }>, viewFilters: Array<{ __typename?: 'CoreViewFilter', id: any, fieldMetadataId: any, operand: ViewFilterOperand, value: any, viewFilterGroupId?: any | null, positionInViewFilterGroup?: number | null, subFieldName?: string | null, viewId: any, createdAt: string, updatedAt: string, deletedAt?: string | null }>, viewFilterGroups: Array<{ __typename?: 'CoreViewFilterGroup', id: any, parentViewFilterGroupId?: any | null, logicalOperator: ViewFilterGroupLogicalOperator, positionInViewFilterGroup?: number | null, viewId: any }>, viewSorts: Array<{ __typename?: 'CoreViewSort', id: any, fieldMetadataId: any, direction: ViewSortDirection, viewId: any }>, viewGroups: Array<{ __typename?: 'CoreViewGroup', id: any, isVisible: boolean, fieldValue: string, position: number, viewId: any, createdAt: string, updatedAt: string, deletedAt?: string | null }> } };
|
||||
|
||||
export type CreateCoreViewFieldMutationVariables = Exact<{
|
||||
input: CreateViewFieldInput;
|
||||
@@ -4886,7 +4884,7 @@ export type CreateCoreViewGroupMutationVariables = Exact<{
|
||||
}>;
|
||||
|
||||
|
||||
export type CreateCoreViewGroupMutation = { __typename?: 'Mutation', createCoreViewGroup: { __typename?: 'CoreViewGroup', id: any, fieldMetadataId: any, isVisible: boolean, fieldValue: string, position: number, viewId: any, createdAt: string, updatedAt: string, deletedAt?: string | null } };
|
||||
export type CreateCoreViewGroupMutation = { __typename?: 'Mutation', createCoreViewGroup: { __typename?: 'CoreViewGroup', id: any, isVisible: boolean, fieldValue: string, position: number, viewId: any, createdAt: string, updatedAt: string, deletedAt?: string | null } };
|
||||
|
||||
export type CreateCoreViewSortMutationVariables = Exact<{
|
||||
input: CreateViewSortInput;
|
||||
@@ -4907,7 +4905,7 @@ export type CreateManyCoreViewGroupsMutationVariables = Exact<{
|
||||
}>;
|
||||
|
||||
|
||||
export type CreateManyCoreViewGroupsMutation = { __typename?: 'Mutation', createManyCoreViewGroups: Array<{ __typename?: 'CoreViewGroup', id: any, fieldMetadataId: any, isVisible: boolean, fieldValue: string, position: number, viewId: any, createdAt: string, updatedAt: string, deletedAt?: string | null }> };
|
||||
export type CreateManyCoreViewGroupsMutation = { __typename?: 'Mutation', createManyCoreViewGroups: Array<{ __typename?: 'CoreViewGroup', id: any, isVisible: boolean, fieldValue: string, position: number, viewId: any, createdAt: string, updatedAt: string, deletedAt?: string | null }> };
|
||||
|
||||
export type DeleteCoreViewMutationVariables = Exact<{
|
||||
id: Scalars['String'];
|
||||
@@ -4942,7 +4940,7 @@ export type DeleteCoreViewGroupMutationVariables = Exact<{
|
||||
}>;
|
||||
|
||||
|
||||
export type DeleteCoreViewGroupMutation = { __typename?: 'Mutation', deleteCoreViewGroup: { __typename?: 'CoreViewGroup', id: any, fieldMetadataId: any, isVisible: boolean, fieldValue: string, position: number, viewId: any, createdAt: string, updatedAt: string, deletedAt?: string | null } };
|
||||
export type DeleteCoreViewGroupMutation = { __typename?: 'Mutation', deleteCoreViewGroup: { __typename?: 'CoreViewGroup', id: any, isVisible: boolean, fieldValue: string, position: number, viewId: any, createdAt: string, updatedAt: string, deletedAt?: string | null } };
|
||||
|
||||
export type DeleteCoreViewSortMutationVariables = Exact<{
|
||||
id: Scalars['String'];
|
||||
@@ -4984,7 +4982,7 @@ export type DestroyCoreViewGroupMutationVariables = Exact<{
|
||||
}>;
|
||||
|
||||
|
||||
export type DestroyCoreViewGroupMutation = { __typename?: 'Mutation', destroyCoreViewGroup: { __typename?: 'CoreViewGroup', id: any, fieldMetadataId: any, isVisible: boolean, fieldValue: string, position: number, viewId: any, createdAt: string, updatedAt: string, deletedAt?: string | null } };
|
||||
export type DestroyCoreViewGroupMutation = { __typename?: 'Mutation', destroyCoreViewGroup: { __typename?: 'CoreViewGroup', id: any, isVisible: boolean, fieldValue: string, position: number, viewId: any, createdAt: string, updatedAt: string, deletedAt?: string | null } };
|
||||
|
||||
export type DestroyCoreViewSortMutationVariables = Exact<{
|
||||
id: Scalars['String'];
|
||||
@@ -4999,7 +4997,7 @@ export type UpdateCoreViewMutationVariables = Exact<{
|
||||
}>;
|
||||
|
||||
|
||||
export type UpdateCoreViewMutation = { __typename?: 'Mutation', updateCoreView: { __typename?: 'CoreView', id: any, name: string, objectMetadataId: any, type: ViewType, key?: ViewKey | null, icon: string, position: number, isCompact: boolean, openRecordIn: ViewOpenRecordIn, kanbanAggregateOperation?: AggregateOperations | null, kanbanAggregateOperationFieldMetadataId?: any | null, anyFieldFilterValue?: string | null, calendarFieldMetadataId?: any | null, calendarLayout?: ViewCalendarLayout | null, visibility: ViewVisibility, createdByUserWorkspaceId?: any | null, viewFields: Array<{ __typename?: 'CoreViewField', id: any, fieldMetadataId: any, viewId: any, isVisible: boolean, position: number, size: number, aggregateOperation?: AggregateOperations | null, createdAt: string, updatedAt: string, deletedAt?: string | null }>, viewFilters: Array<{ __typename?: 'CoreViewFilter', id: any, fieldMetadataId: any, operand: ViewFilterOperand, value: any, viewFilterGroupId?: any | null, positionInViewFilterGroup?: number | null, subFieldName?: string | null, viewId: any, createdAt: string, updatedAt: string, deletedAt?: string | null }>, viewFilterGroups: Array<{ __typename?: 'CoreViewFilterGroup', id: any, parentViewFilterGroupId?: any | null, logicalOperator: ViewFilterGroupLogicalOperator, positionInViewFilterGroup?: number | null, viewId: any }>, viewSorts: Array<{ __typename?: 'CoreViewSort', id: any, fieldMetadataId: any, direction: ViewSortDirection, viewId: any }>, viewGroups: Array<{ __typename?: 'CoreViewGroup', id: any, fieldMetadataId: any, isVisible: boolean, fieldValue: string, position: number, viewId: any, createdAt: string, updatedAt: string, deletedAt?: string | null }> } };
|
||||
export type UpdateCoreViewMutation = { __typename?: 'Mutation', updateCoreView: { __typename?: 'CoreView', id: any, name: string, objectMetadataId: any, type: ViewType, key?: ViewKey | null, icon: string, position: number, isCompact: boolean, openRecordIn: ViewOpenRecordIn, kanbanAggregateOperation?: AggregateOperations | null, kanbanAggregateOperationFieldMetadataId?: any | null, mainGroupByFieldMetadataId?: any | null, anyFieldFilterValue?: string | null, calendarFieldMetadataId?: any | null, calendarLayout?: ViewCalendarLayout | null, visibility: ViewVisibility, createdByUserWorkspaceId?: any | null, viewFields: Array<{ __typename?: 'CoreViewField', id: any, fieldMetadataId: any, viewId: any, isVisible: boolean, position: number, size: number, aggregateOperation?: AggregateOperations | null, createdAt: string, updatedAt: string, deletedAt?: string | null }>, viewFilters: Array<{ __typename?: 'CoreViewFilter', id: any, fieldMetadataId: any, operand: ViewFilterOperand, value: any, viewFilterGroupId?: any | null, positionInViewFilterGroup?: number | null, subFieldName?: string | null, viewId: any, createdAt: string, updatedAt: string, deletedAt?: string | null }>, viewFilterGroups: Array<{ __typename?: 'CoreViewFilterGroup', id: any, parentViewFilterGroupId?: any | null, logicalOperator: ViewFilterGroupLogicalOperator, positionInViewFilterGroup?: number | null, viewId: any }>, viewSorts: Array<{ __typename?: 'CoreViewSort', id: any, fieldMetadataId: any, direction: ViewSortDirection, viewId: any }>, viewGroups: Array<{ __typename?: 'CoreViewGroup', id: any, isVisible: boolean, fieldValue: string, position: number, viewId: any, createdAt: string, updatedAt: string, deletedAt?: string | null }> } };
|
||||
|
||||
export type UpdateCoreViewFieldMutationVariables = Exact<{
|
||||
input: UpdateViewFieldInput;
|
||||
@@ -5028,7 +5026,7 @@ export type UpdateCoreViewGroupMutationVariables = Exact<{
|
||||
}>;
|
||||
|
||||
|
||||
export type UpdateCoreViewGroupMutation = { __typename?: 'Mutation', updateCoreViewGroup: { __typename?: 'CoreViewGroup', id: any, fieldMetadataId: any, isVisible: boolean, fieldValue: string, position: number, viewId: any, createdAt: string, updatedAt: string, deletedAt?: string | null } };
|
||||
export type UpdateCoreViewGroupMutation = { __typename?: 'Mutation', updateCoreViewGroup: { __typename?: 'CoreViewGroup', id: any, isVisible: boolean, fieldValue: string, position: number, viewId: any, createdAt: string, updatedAt: string, deletedAt?: string | null } };
|
||||
|
||||
export type UpdateCoreViewSortMutationVariables = Exact<{
|
||||
id: Scalars['String'];
|
||||
@@ -5041,7 +5039,7 @@ export type UpdateCoreViewSortMutation = { __typename?: 'Mutation', updateCoreVi
|
||||
export type FindAllCoreViewsQueryVariables = Exact<{ [key: string]: never; }>;
|
||||
|
||||
|
||||
export type FindAllCoreViewsQuery = { __typename?: 'Query', getCoreViews: Array<{ __typename?: 'CoreView', id: any, name: string, objectMetadataId: any, type: ViewType, key?: ViewKey | null, icon: string, position: number, isCompact: boolean, openRecordIn: ViewOpenRecordIn, kanbanAggregateOperation?: AggregateOperations | null, kanbanAggregateOperationFieldMetadataId?: any | null, anyFieldFilterValue?: string | null, calendarFieldMetadataId?: any | null, calendarLayout?: ViewCalendarLayout | null, visibility: ViewVisibility, createdByUserWorkspaceId?: any | null, viewFields: Array<{ __typename?: 'CoreViewField', id: any, fieldMetadataId: any, viewId: any, isVisible: boolean, position: number, size: number, aggregateOperation?: AggregateOperations | null, createdAt: string, updatedAt: string, deletedAt?: string | null }>, viewFilters: Array<{ __typename?: 'CoreViewFilter', id: any, fieldMetadataId: any, operand: ViewFilterOperand, value: any, viewFilterGroupId?: any | null, positionInViewFilterGroup?: number | null, subFieldName?: string | null, viewId: any, createdAt: string, updatedAt: string, deletedAt?: string | null }>, viewFilterGroups: Array<{ __typename?: 'CoreViewFilterGroup', id: any, parentViewFilterGroupId?: any | null, logicalOperator: ViewFilterGroupLogicalOperator, positionInViewFilterGroup?: number | null, viewId: any }>, viewSorts: Array<{ __typename?: 'CoreViewSort', id: any, fieldMetadataId: any, direction: ViewSortDirection, viewId: any }>, viewGroups: Array<{ __typename?: 'CoreViewGroup', id: any, fieldMetadataId: any, isVisible: boolean, fieldValue: string, position: number, viewId: any, createdAt: string, updatedAt: string, deletedAt?: string | null }> }> };
|
||||
export type FindAllCoreViewsQuery = { __typename?: 'Query', getCoreViews: Array<{ __typename?: 'CoreView', id: any, name: string, objectMetadataId: any, type: ViewType, key?: ViewKey | null, icon: string, position: number, isCompact: boolean, openRecordIn: ViewOpenRecordIn, kanbanAggregateOperation?: AggregateOperations | null, kanbanAggregateOperationFieldMetadataId?: any | null, mainGroupByFieldMetadataId?: any | null, anyFieldFilterValue?: string | null, calendarFieldMetadataId?: any | null, calendarLayout?: ViewCalendarLayout | null, visibility: ViewVisibility, createdByUserWorkspaceId?: any | null, viewFields: Array<{ __typename?: 'CoreViewField', id: any, fieldMetadataId: any, viewId: any, isVisible: boolean, position: number, size: number, aggregateOperation?: AggregateOperations | null, createdAt: string, updatedAt: string, deletedAt?: string | null }>, viewFilters: Array<{ __typename?: 'CoreViewFilter', id: any, fieldMetadataId: any, operand: ViewFilterOperand, value: any, viewFilterGroupId?: any | null, positionInViewFilterGroup?: number | null, subFieldName?: string | null, viewId: any, createdAt: string, updatedAt: string, deletedAt?: string | null }>, viewFilterGroups: Array<{ __typename?: 'CoreViewFilterGroup', id: any, parentViewFilterGroupId?: any | null, logicalOperator: ViewFilterGroupLogicalOperator, positionInViewFilterGroup?: number | null, viewId: any }>, viewSorts: Array<{ __typename?: 'CoreViewSort', id: any, fieldMetadataId: any, direction: ViewSortDirection, viewId: any }>, viewGroups: Array<{ __typename?: 'CoreViewGroup', id: any, isVisible: boolean, fieldValue: string, position: number, viewId: any, createdAt: string, updatedAt: string, deletedAt?: string | null }> }> };
|
||||
|
||||
export type FindManyCoreViewFieldsQueryVariables = Exact<{
|
||||
viewId: Scalars['String'];
|
||||
@@ -5069,7 +5067,7 @@ export type FindManyCoreViewGroupsQueryVariables = Exact<{
|
||||
}>;
|
||||
|
||||
|
||||
export type FindManyCoreViewGroupsQuery = { __typename?: 'Query', getCoreViewGroups: Array<{ __typename?: 'CoreViewGroup', id: any, fieldMetadataId: any, isVisible: boolean, fieldValue: string, position: number, viewId: any, createdAt: string, updatedAt: string, deletedAt?: string | null }> };
|
||||
export type FindManyCoreViewGroupsQuery = { __typename?: 'Query', getCoreViewGroups: Array<{ __typename?: 'CoreViewGroup', id: any, isVisible: boolean, fieldValue: string, position: number, viewId: any, createdAt: string, updatedAt: string, deletedAt?: string | null }> };
|
||||
|
||||
export type FindManyCoreViewSortsQueryVariables = Exact<{
|
||||
viewId?: InputMaybe<Scalars['String']>;
|
||||
@@ -5083,14 +5081,14 @@ export type FindManyCoreViewsQueryVariables = Exact<{
|
||||
}>;
|
||||
|
||||
|
||||
export type FindManyCoreViewsQuery = { __typename?: 'Query', getCoreViews: Array<{ __typename?: 'CoreView', id: any, name: string, objectMetadataId: any, type: ViewType, key?: ViewKey | null, icon: string, position: number, isCompact: boolean, openRecordIn: ViewOpenRecordIn, kanbanAggregateOperation?: AggregateOperations | null, kanbanAggregateOperationFieldMetadataId?: any | null, anyFieldFilterValue?: string | null, calendarFieldMetadataId?: any | null, calendarLayout?: ViewCalendarLayout | null, visibility: ViewVisibility, createdByUserWorkspaceId?: any | null, viewFields: Array<{ __typename?: 'CoreViewField', id: any, fieldMetadataId: any, viewId: any, isVisible: boolean, position: number, size: number, aggregateOperation?: AggregateOperations | null, createdAt: string, updatedAt: string, deletedAt?: string | null }>, viewFilters: Array<{ __typename?: 'CoreViewFilter', id: any, fieldMetadataId: any, operand: ViewFilterOperand, value: any, viewFilterGroupId?: any | null, positionInViewFilterGroup?: number | null, subFieldName?: string | null, viewId: any, createdAt: string, updatedAt: string, deletedAt?: string | null }>, viewFilterGroups: Array<{ __typename?: 'CoreViewFilterGroup', id: any, parentViewFilterGroupId?: any | null, logicalOperator: ViewFilterGroupLogicalOperator, positionInViewFilterGroup?: number | null, viewId: any }>, viewSorts: Array<{ __typename?: 'CoreViewSort', id: any, fieldMetadataId: any, direction: ViewSortDirection, viewId: any }>, viewGroups: Array<{ __typename?: 'CoreViewGroup', id: any, fieldMetadataId: any, isVisible: boolean, fieldValue: string, position: number, viewId: any, createdAt: string, updatedAt: string, deletedAt?: string | null }> }> };
|
||||
export type FindManyCoreViewsQuery = { __typename?: 'Query', getCoreViews: Array<{ __typename?: 'CoreView', id: any, name: string, objectMetadataId: any, type: ViewType, key?: ViewKey | null, icon: string, position: number, isCompact: boolean, openRecordIn: ViewOpenRecordIn, kanbanAggregateOperation?: AggregateOperations | null, kanbanAggregateOperationFieldMetadataId?: any | null, mainGroupByFieldMetadataId?: any | null, anyFieldFilterValue?: string | null, calendarFieldMetadataId?: any | null, calendarLayout?: ViewCalendarLayout | null, visibility: ViewVisibility, createdByUserWorkspaceId?: any | null, viewFields: Array<{ __typename?: 'CoreViewField', id: any, fieldMetadataId: any, viewId: any, isVisible: boolean, position: number, size: number, aggregateOperation?: AggregateOperations | null, createdAt: string, updatedAt: string, deletedAt?: string | null }>, viewFilters: Array<{ __typename?: 'CoreViewFilter', id: any, fieldMetadataId: any, operand: ViewFilterOperand, value: any, viewFilterGroupId?: any | null, positionInViewFilterGroup?: number | null, subFieldName?: string | null, viewId: any, createdAt: string, updatedAt: string, deletedAt?: string | null }>, viewFilterGroups: Array<{ __typename?: 'CoreViewFilterGroup', id: any, parentViewFilterGroupId?: any | null, logicalOperator: ViewFilterGroupLogicalOperator, positionInViewFilterGroup?: number | null, viewId: any }>, viewSorts: Array<{ __typename?: 'CoreViewSort', id: any, fieldMetadataId: any, direction: ViewSortDirection, viewId: any }>, viewGroups: Array<{ __typename?: 'CoreViewGroup', id: any, isVisible: boolean, fieldValue: string, position: number, viewId: any, createdAt: string, updatedAt: string, deletedAt?: string | null }> }> };
|
||||
|
||||
export type FindOneCoreViewQueryVariables = Exact<{
|
||||
id: Scalars['String'];
|
||||
}>;
|
||||
|
||||
|
||||
export type FindOneCoreViewQuery = { __typename?: 'Query', getCoreView?: { __typename?: 'CoreView', id: any, name: string, objectMetadataId: any, type: ViewType, key?: ViewKey | null, icon: string, position: number, isCompact: boolean, openRecordIn: ViewOpenRecordIn, kanbanAggregateOperation?: AggregateOperations | null, kanbanAggregateOperationFieldMetadataId?: any | null, anyFieldFilterValue?: string | null, calendarFieldMetadataId?: any | null, calendarLayout?: ViewCalendarLayout | null, visibility: ViewVisibility, createdByUserWorkspaceId?: any | null, viewFields: Array<{ __typename?: 'CoreViewField', id: any, fieldMetadataId: any, viewId: any, isVisible: boolean, position: number, size: number, aggregateOperation?: AggregateOperations | null, createdAt: string, updatedAt: string, deletedAt?: string | null }>, viewFilters: Array<{ __typename?: 'CoreViewFilter', id: any, fieldMetadataId: any, operand: ViewFilterOperand, value: any, viewFilterGroupId?: any | null, positionInViewFilterGroup?: number | null, subFieldName?: string | null, viewId: any, createdAt: string, updatedAt: string, deletedAt?: string | null }>, viewFilterGroups: Array<{ __typename?: 'CoreViewFilterGroup', id: any, parentViewFilterGroupId?: any | null, logicalOperator: ViewFilterGroupLogicalOperator, positionInViewFilterGroup?: number | null, viewId: any }>, viewSorts: Array<{ __typename?: 'CoreViewSort', id: any, fieldMetadataId: any, direction: ViewSortDirection, viewId: any }>, viewGroups: Array<{ __typename?: 'CoreViewGroup', id: any, fieldMetadataId: any, isVisible: boolean, fieldValue: string, position: number, viewId: any, createdAt: string, updatedAt: string, deletedAt?: string | null }> } | null };
|
||||
export type FindOneCoreViewQuery = { __typename?: 'Query', getCoreView?: { __typename?: 'CoreView', id: any, name: string, objectMetadataId: any, type: ViewType, key?: ViewKey | null, icon: string, position: number, isCompact: boolean, openRecordIn: ViewOpenRecordIn, kanbanAggregateOperation?: AggregateOperations | null, kanbanAggregateOperationFieldMetadataId?: any | null, mainGroupByFieldMetadataId?: any | null, anyFieldFilterValue?: string | null, calendarFieldMetadataId?: any | null, calendarLayout?: ViewCalendarLayout | null, visibility: ViewVisibility, createdByUserWorkspaceId?: any | null, viewFields: Array<{ __typename?: 'CoreViewField', id: any, fieldMetadataId: any, viewId: any, isVisible: boolean, position: number, size: number, aggregateOperation?: AggregateOperations | null, createdAt: string, updatedAt: string, deletedAt?: string | null }>, viewFilters: Array<{ __typename?: 'CoreViewFilter', id: any, fieldMetadataId: any, operand: ViewFilterOperand, value: any, viewFilterGroupId?: any | null, positionInViewFilterGroup?: number | null, subFieldName?: string | null, viewId: any, createdAt: string, updatedAt: string, deletedAt?: string | null }>, viewFilterGroups: Array<{ __typename?: 'CoreViewFilterGroup', id: any, parentViewFilterGroupId?: any | null, logicalOperator: ViewFilterGroupLogicalOperator, positionInViewFilterGroup?: number | null, viewId: any }>, viewSorts: Array<{ __typename?: 'CoreViewSort', id: any, fieldMetadataId: any, direction: ViewSortDirection, viewId: any }>, viewGroups: Array<{ __typename?: 'CoreViewGroup', id: any, isVisible: boolean, fieldValue: string, position: number, viewId: any, createdAt: string, updatedAt: string, deletedAt?: string | null }> } | null };
|
||||
|
||||
export type FindOneCoreViewFieldQueryVariables = Exact<{
|
||||
id: Scalars['String'];
|
||||
@@ -5118,7 +5116,7 @@ export type FindOneCoreViewGroupQueryVariables = Exact<{
|
||||
}>;
|
||||
|
||||
|
||||
export type FindOneCoreViewGroupQuery = { __typename?: 'Query', getCoreViewGroup?: { __typename?: 'CoreViewGroup', id: any, fieldMetadataId: any, isVisible: boolean, fieldValue: string, position: number, viewId: any, createdAt: string, updatedAt: string, deletedAt?: string | null } | null };
|
||||
export type FindOneCoreViewGroupQuery = { __typename?: 'Query', getCoreViewGroup?: { __typename?: 'CoreViewGroup', id: any, isVisible: boolean, fieldValue: string, position: number, viewId: any, createdAt: string, updatedAt: string, deletedAt?: string | null } | null };
|
||||
|
||||
export type FindOneCoreViewSortQueryVariables = Exact<{
|
||||
id: Scalars['String'];
|
||||
@@ -5299,7 +5297,6 @@ export const ViewSortFragmentFragmentDoc = gql`
|
||||
export const ViewGroupFragmentFragmentDoc = gql`
|
||||
fragment ViewGroupFragment on CoreViewGroup {
|
||||
id
|
||||
fieldMetadataId
|
||||
isVisible
|
||||
fieldValue
|
||||
position
|
||||
@@ -5322,6 +5319,7 @@ export const ViewFragmentFragmentDoc = gql`
|
||||
openRecordIn
|
||||
kanbanAggregateOperation
|
||||
kanbanAggregateOperationFieldMetadataId
|
||||
mainGroupByFieldMetadataId
|
||||
anyFieldFilterValue
|
||||
calendarFieldMetadataId
|
||||
calendarLayout
|
||||
|
||||
+6
-6
@@ -42,7 +42,6 @@ export const useSetViewTypeFromLayoutOptionsMenu = () => {
|
||||
({
|
||||
id: v4(),
|
||||
__typename: 'ViewGroup',
|
||||
fieldMetadataId: randomFieldForKanban,
|
||||
fieldValue: option.value,
|
||||
isVisible: true,
|
||||
position: index,
|
||||
@@ -55,14 +54,15 @@ export const useSetViewTypeFromLayoutOptionsMenu = () => {
|
||||
fieldValue: '',
|
||||
position: viewGroupsToCreate.length,
|
||||
isVisible: true,
|
||||
fieldMetadataId: randomFieldForKanban,
|
||||
} satisfies ViewGroup);
|
||||
|
||||
await createViewGroups({
|
||||
inputs: viewGroupsToCreate.map(({ __typename, ...viewGroup }) => ({
|
||||
...viewGroup,
|
||||
viewId: currentViewId,
|
||||
})),
|
||||
createCoreViewGroupInputs: {
|
||||
inputs: viewGroupsToCreate.map(({ __typename, ...viewGroup }) => ({
|
||||
...viewGroup,
|
||||
viewId: currentViewId,
|
||||
})),
|
||||
},
|
||||
});
|
||||
|
||||
return viewGroupsToCreate;
|
||||
|
||||
+13
-6
@@ -8,12 +8,14 @@ import { originalDragSelectionComponentState } from '@/object-record/record-drag
|
||||
import { processGroupDrop } from '@/object-record/record-drag/utils/processGroupDrop';
|
||||
import { recordGroupDefinitionFamilyState } from '@/object-record/record-group/states/recordGroupDefinitionFamilyState';
|
||||
import { RECORD_INDEX_REMOVE_SORTING_MODAL_ID } from '@/object-record/record-index/constants/RecordIndexRemoveSortingModalId';
|
||||
import { recordIndexGroupFieldMetadataItemComponentState } from '@/object-record/record-index/states/recordIndexGroupFieldMetadataComponentState';
|
||||
import { recordIndexRecordIdsByGroupComponentFamilyState } from '@/object-record/record-index/states/recordIndexRecordIdsByGroupComponentFamilyState';
|
||||
import { currentRecordSortsComponentState } from '@/object-record/record-sort/states/currentRecordSortsComponentState';
|
||||
import { useRecordTableContextOrThrow } from '@/object-record/record-table/contexts/RecordTableContext';
|
||||
import { selectedRowIdsComponentSelector } from '@/object-record/record-table/states/selectors/selectedRowIdsComponentSelector';
|
||||
import { useModal } from '@/ui/layout/modal/hooks/useModal';
|
||||
import { useRecoilComponentCallbackState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackState';
|
||||
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
|
||||
import { getSnapshotValue } from '@/ui/utilities/state/utils/getSnapshotValue';
|
||||
|
||||
export const useProcessTableWithGroupRecordDrop = () => {
|
||||
@@ -47,6 +49,10 @@ export const useProcessTableWithGroupRecordDrop = () => {
|
||||
originalDragSelectionComponentState,
|
||||
);
|
||||
|
||||
const groupFieldMetadata = useRecoilComponentValue(
|
||||
recordIndexGroupFieldMetadataItemComponentState,
|
||||
);
|
||||
|
||||
const processTableWithGroupRecordDrop = useRecoilCallback(
|
||||
({ snapshot }) =>
|
||||
(result: DropResult) => {
|
||||
@@ -63,7 +69,7 @@ export const useProcessTableWithGroupRecordDrop = () => {
|
||||
}
|
||||
|
||||
const fieldMetadata = objectMetadataItem.fields.find(
|
||||
(field) => field.id === destinationRecordGroup.fieldMetadataId,
|
||||
(field) => field.id === groupFieldMetadata?.id,
|
||||
);
|
||||
|
||||
if (!isDefined(fieldMetadata)) {
|
||||
@@ -110,14 +116,15 @@ export const useProcessTableWithGroupRecordDrop = () => {
|
||||
});
|
||||
},
|
||||
[
|
||||
currentRecordSortsCallbackState,
|
||||
objectMetadataItem.fields,
|
||||
recordIdsByGroupFamilyState,
|
||||
updateOneRow,
|
||||
openModal,
|
||||
selectedRowIdsSelector,
|
||||
originalDragSelectionCallbackState,
|
||||
isDraggingRecordCallbackState,
|
||||
selectedRowIdsSelector,
|
||||
currentRecordSortsCallbackState,
|
||||
recordIdsByGroupFamilyState,
|
||||
groupFieldMetadata?.id,
|
||||
openModal,
|
||||
updateOneRow,
|
||||
],
|
||||
);
|
||||
|
||||
|
||||
+8
-4
@@ -1,21 +1,25 @@
|
||||
import { type FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
|
||||
import { useCurrentRecordGroupDefinition } from '@/object-record/record-group/hooks/useCurrentRecordGroupDefinition';
|
||||
import { recordIndexGroupFieldMetadataItemComponentState } from '@/object-record/record-index/states/recordIndexGroupFieldMetadataComponentState';
|
||||
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
|
||||
import { useMemo } from 'react';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
export const useRecordGroupFilter = (fields: FieldMetadataItem[]) => {
|
||||
const currentRecordGroupDefinition = useCurrentRecordGroupDefinition();
|
||||
const groupFieldMetadata = useRecoilComponentValue(
|
||||
recordIndexGroupFieldMetadataItemComponentState,
|
||||
);
|
||||
|
||||
const recordGroupFilter = useMemo(() => {
|
||||
if (isDefined(currentRecordGroupDefinition)) {
|
||||
const fieldMetadataItem = fields.find(
|
||||
(fieldMetadataItem) =>
|
||||
fieldMetadataItem.id === currentRecordGroupDefinition.fieldMetadataId,
|
||||
(fieldMetadataItem) => fieldMetadataItem.id === groupFieldMetadata?.id,
|
||||
);
|
||||
|
||||
if (!fieldMetadataItem) {
|
||||
throw new Error(
|
||||
`Field metadata item with id ${currentRecordGroupDefinition.fieldMetadataId} not found`,
|
||||
`Field metadata item with id ${groupFieldMetadata?.id} not found`,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -31,7 +35,7 @@ export const useRecordGroupFilter = (fields: FieldMetadataItem[]) => {
|
||||
}
|
||||
|
||||
return {};
|
||||
}, [currentRecordGroupDefinition, fields]);
|
||||
}, [currentRecordGroupDefinition, fields, groupFieldMetadata?.id]);
|
||||
|
||||
return { recordGroupFilter };
|
||||
};
|
||||
|
||||
+16
-4
@@ -3,7 +3,9 @@ import { useSetRecordGroups } from '@/object-record/record-group/hooks/useSetRec
|
||||
import { recordGroupDefinitionFamilyState } from '@/object-record/record-group/states/recordGroupDefinitionFamilyState';
|
||||
import { visibleRecordGroupIdsComponentFamilySelector } from '@/object-record/record-group/states/selectors/visibleRecordGroupIdsComponentFamilySelector';
|
||||
import { type RecordGroupDefinition } from '@/object-record/record-group/types/RecordGroupDefinition';
|
||||
import { recordIndexGroupFieldMetadataItemComponentState } from '@/object-record/record-index/states/recordIndexGroupFieldMetadataComponentState';
|
||||
import { useRecoilComponentCallbackState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackState';
|
||||
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
|
||||
import { getSnapshotValue } from '@/ui/utilities/state/utils/getSnapshotValue';
|
||||
import { useSaveCurrentViewGroups } from '@/views/hooks/useSaveCurrentViewGroups';
|
||||
import { type ViewType } from '@/views/types/ViewType';
|
||||
@@ -37,6 +39,10 @@ export const useReorderRecordGroups = ({
|
||||
|
||||
const { saveViewGroups } = useSaveCurrentViewGroups();
|
||||
|
||||
const groupFieldMetadata = useRecoilComponentValue(
|
||||
recordIndexGroupFieldMetadataItemComponentState,
|
||||
);
|
||||
|
||||
const reorderRecordGroups = useRecoilCallback(
|
||||
({ snapshot }) =>
|
||||
({ fromIndex, toIndex }: ReorderRecordGroupsParams) => {
|
||||
@@ -80,17 +86,23 @@ export const useReorderRecordGroups = ({
|
||||
];
|
||||
}, []);
|
||||
|
||||
setRecordGroups(
|
||||
updatedRecordGroups,
|
||||
if (!isDefined(groupFieldMetadata?.id)) {
|
||||
throw new Error('mainGroupByFieldMetadataId is required');
|
||||
}
|
||||
|
||||
setRecordGroups({
|
||||
mainGroupByFieldMetadataId: groupFieldMetadata?.id,
|
||||
recordGroups: updatedRecordGroups,
|
||||
recordIndexId,
|
||||
objectMetadataItem.id,
|
||||
);
|
||||
objectMetadataItemId: objectMetadataItem.id,
|
||||
});
|
||||
saveViewGroups(
|
||||
mapRecordGroupDefinitionsToViewGroups(updatedRecordGroups),
|
||||
);
|
||||
},
|
||||
[
|
||||
objectMetadataItem.id,
|
||||
groupFieldMetadata?.id,
|
||||
recordIndexId,
|
||||
saveViewGroups,
|
||||
setRecordGroups,
|
||||
|
||||
+30
-15
@@ -16,11 +16,17 @@ import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
|
||||
export const useSetRecordGroups = () => {
|
||||
const setRecordGroups = useRecoilCallback(
|
||||
({ snapshot, set }) =>
|
||||
(
|
||||
recordGroups: RecordGroupDefinition[],
|
||||
recordIndexId: string,
|
||||
objectMetadataItemId: string,
|
||||
) => {
|
||||
({
|
||||
mainGroupByFieldMetadataId,
|
||||
recordGroups,
|
||||
recordIndexId,
|
||||
objectMetadataItemId,
|
||||
}: {
|
||||
mainGroupByFieldMetadataId: string;
|
||||
recordGroups: RecordGroupDefinition[];
|
||||
recordIndexId: string;
|
||||
objectMetadataItemId: string;
|
||||
}) => {
|
||||
const objectMetadataItems = snapshot
|
||||
.getLoadable(objectMetadataItemsState)
|
||||
.getValue();
|
||||
@@ -40,7 +46,8 @@ export const useSetRecordGroups = () => {
|
||||
instanceId: recordIndexId,
|
||||
}),
|
||||
);
|
||||
const fieldMetadataId = recordGroups?.[0]?.fieldMetadataId;
|
||||
|
||||
const fieldMetadataId = mainGroupByFieldMetadataId;
|
||||
const fieldMetadata = fieldMetadataId
|
||||
? objectMetadataItem.fields.find(
|
||||
(field) => field.id === fieldMetadataId,
|
||||
@@ -105,26 +112,34 @@ export const useSetRecordGroups = () => {
|
||||
);
|
||||
|
||||
const setRecordGroupsFromViewGroups = useCallback(
|
||||
(
|
||||
viewId: string,
|
||||
viewGroups: ViewGroup[],
|
||||
objectMetadataItem: ObjectMetadataItem,
|
||||
) => {
|
||||
({
|
||||
viewId,
|
||||
mainGroupByFieldMetadataId,
|
||||
viewGroups,
|
||||
objectMetadataItem,
|
||||
}: {
|
||||
viewId: string;
|
||||
mainGroupByFieldMetadataId: string;
|
||||
viewGroups: ViewGroup[];
|
||||
objectMetadataItem: ObjectMetadataItem;
|
||||
}) => {
|
||||
const recordIndexId = getRecordIndexIdFromObjectNamePluralAndViewId(
|
||||
objectMetadataItem.namePlural,
|
||||
viewId,
|
||||
);
|
||||
|
||||
const newGroupDefinitions = mapViewGroupsToRecordGroupDefinitions({
|
||||
mainGroupByFieldMetadataId,
|
||||
objectMetadataItem,
|
||||
viewGroups,
|
||||
});
|
||||
|
||||
setRecordGroups(
|
||||
newGroupDefinitions,
|
||||
setRecordGroups({
|
||||
mainGroupByFieldMetadataId,
|
||||
recordGroups: newGroupDefinitions,
|
||||
recordIndexId,
|
||||
objectMetadataItem.id,
|
||||
);
|
||||
objectMetadataItemId: objectMetadataItem.id,
|
||||
});
|
||||
},
|
||||
[setRecordGroups],
|
||||
);
|
||||
|
||||
-1
@@ -6,7 +6,6 @@ export const enum RecordGroupDefinitionType {
|
||||
|
||||
export type RecordGroupDefinition = {
|
||||
id: string;
|
||||
fieldMetadataId: string;
|
||||
type: RecordGroupDefinitionType;
|
||||
title: string;
|
||||
value: string | null;
|
||||
|
||||
+39
-36
@@ -2,17 +2,21 @@ import { useContextStoreObjectMetadataItemOrThrow } from '@/context-store/hooks/
|
||||
import { contextStoreCurrentViewIdComponentState } from '@/context-store/states/contextStoreCurrentViewIdComponentState';
|
||||
import { type FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
|
||||
import { useSetRecordGroups } from '@/object-record/record-group/hooks/useSetRecordGroups';
|
||||
import { useLoadRecordIndexStates } from '@/object-record/record-index/hooks/useLoadRecordIndexStates';
|
||||
import { useRecoilComponentCallbackState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackState';
|
||||
import { usePersistView } from '@/views/hooks/internal/usePersistView';
|
||||
import { usePersistViewGroupRecords } from '@/views/hooks/internal/usePersistViewGroup';
|
||||
import { useGetViewFromPrefetchState } from '@/views/hooks/useGetViewFromPrefetchState';
|
||||
import { type ViewGroup } from '@/views/types/ViewGroup';
|
||||
import { convertCoreViewToView } from '@/views/utils/convertCoreViewToView';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { v4 } from 'uuid';
|
||||
import { type CoreView } from '~/generated/graphql';
|
||||
import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull';
|
||||
|
||||
export const useHandleRecordGroupField = () => {
|
||||
const { createViewGroups, deleteViewGroups } = usePersistViewGroupRecords();
|
||||
const { deleteViewGroups } = usePersistViewGroupRecords();
|
||||
|
||||
const currentViewIdCallbackState = useRecoilComponentCallbackState(
|
||||
contextStoreCurrentViewIdComponentState,
|
||||
@@ -25,6 +29,7 @@ export const useHandleRecordGroupField = () => {
|
||||
const { setRecordGroupsFromViewGroups } = useSetRecordGroups();
|
||||
|
||||
const { updateView } = usePersistView();
|
||||
const { loadRecordIndexStates } = useLoadRecordIndexStates();
|
||||
|
||||
const handleRecordGroupFieldChange = useRecoilCallback(
|
||||
({ snapshot }) =>
|
||||
@@ -50,16 +55,29 @@ export const useHandleRecordGroupField = () => {
|
||||
return;
|
||||
}
|
||||
|
||||
await updateView({
|
||||
const updatedViewResult = await updateView({
|
||||
id: view.id,
|
||||
input: {
|
||||
mainGroupByFieldMetadataId: fieldMetadataItem.id,
|
||||
},
|
||||
});
|
||||
|
||||
if (updatedViewResult.status === 'successful') {
|
||||
const updatedCoreView = updatedViewResult.response.data
|
||||
?.updateCoreView as CoreView;
|
||||
|
||||
if (isDefined(updatedCoreView)) {
|
||||
const updatedViewConverted = convertCoreViewToView(updatedCoreView);
|
||||
await loadRecordIndexStates(
|
||||
updatedViewConverted,
|
||||
objectMetadataItem,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const existingGroupKeys = new Set(
|
||||
view.viewGroups.map(
|
||||
(group) => `${group.fieldMetadataId}:${group.fieldValue}`,
|
||||
(group) => `${view.mainGroupByFieldMetadataId}:${group.fieldValue}`,
|
||||
),
|
||||
);
|
||||
|
||||
@@ -79,7 +97,6 @@ export const useHandleRecordGroupField = () => {
|
||||
fieldValue: option.value,
|
||||
isVisible: true,
|
||||
position: index,
|
||||
fieldMetadataId: fieldMetadataItem.id,
|
||||
}) satisfies ViewGroup,
|
||||
);
|
||||
|
||||
@@ -93,45 +110,23 @@ export const useHandleRecordGroupField = () => {
|
||||
fieldValue: '',
|
||||
isVisible: true,
|
||||
position: fieldMetadataItem.options.length,
|
||||
fieldMetadataId: fieldMetadataItem.id,
|
||||
} satisfies ViewGroup);
|
||||
}
|
||||
|
||||
const viewGroupsToDelete = view.viewGroups.filter(
|
||||
(group) => group.fieldMetadataId !== fieldMetadataItem.id,
|
||||
);
|
||||
|
||||
const newViewGroupsList = [
|
||||
...view.viewGroups.filter(
|
||||
(group) => group.fieldMetadataId === fieldMetadataItem.id,
|
||||
(_group) =>
|
||||
view.mainGroupByFieldMetadataId === fieldMetadataItem.id,
|
||||
),
|
||||
...viewGroupsToCreate,
|
||||
];
|
||||
|
||||
setRecordGroupsFromViewGroups(
|
||||
view.id,
|
||||
newViewGroupsList,
|
||||
setRecordGroupsFromViewGroups({
|
||||
viewId: view.id,
|
||||
mainGroupByFieldMetadataId: fieldMetadataItem.id,
|
||||
viewGroups: newViewGroupsList,
|
||||
objectMetadataItem,
|
||||
);
|
||||
|
||||
if (viewGroupsToCreate.length > 0) {
|
||||
await createViewGroups({
|
||||
inputs: viewGroupsToCreate.map(({ __typename, ...viewGroup }) => ({
|
||||
...viewGroup,
|
||||
viewId: view.id,
|
||||
})),
|
||||
});
|
||||
}
|
||||
|
||||
if (viewGroupsToDelete.length > 0) {
|
||||
await deleteViewGroups(
|
||||
viewGroupsToDelete.map((group) => ({
|
||||
input: {
|
||||
id: group.id,
|
||||
},
|
||||
})),
|
||||
);
|
||||
}
|
||||
});
|
||||
},
|
||||
[
|
||||
currentViewIdCallbackState,
|
||||
@@ -139,8 +134,7 @@ export const useHandleRecordGroupField = () => {
|
||||
updateView,
|
||||
setRecordGroupsFromViewGroups,
|
||||
objectMetadataItem,
|
||||
createViewGroups,
|
||||
deleteViewGroups,
|
||||
loadRecordIndexStates,
|
||||
],
|
||||
);
|
||||
|
||||
@@ -173,7 +167,16 @@ export const useHandleRecordGroupField = () => {
|
||||
})),
|
||||
);
|
||||
|
||||
setRecordGroupsFromViewGroups(view.id, [], objectMetadataItem);
|
||||
if (!isDefined(view.mainGroupByFieldMetadataId)) {
|
||||
throw new Error('mainGroupByFieldMetadataId is required');
|
||||
}
|
||||
|
||||
setRecordGroupsFromViewGroups({
|
||||
viewId: view.id,
|
||||
mainGroupByFieldMetadataId: view.mainGroupByFieldMetadataId,
|
||||
viewGroups: [],
|
||||
objectMetadataItem,
|
||||
});
|
||||
},
|
||||
[
|
||||
deleteViewGroups,
|
||||
|
||||
+7
-6
@@ -178,11 +178,12 @@ export const useLoadRecordIndexStates = () => {
|
||||
|
||||
onViewFieldsChange(view.viewFields, objectMetadataItem);
|
||||
|
||||
setRecordGroupsFromViewGroups(
|
||||
view.id,
|
||||
view.viewGroups,
|
||||
setRecordGroupsFromViewGroups({
|
||||
viewId: view.id,
|
||||
mainGroupByFieldMetadataId: view.mainGroupByFieldMetadataId ?? '',
|
||||
viewGroups: view.viewGroups,
|
||||
objectMetadataItem,
|
||||
);
|
||||
});
|
||||
|
||||
setContextStoreTargetedRecordsRuleComponentState((prev) => ({
|
||||
...prev,
|
||||
@@ -199,9 +200,9 @@ export const useLoadRecordIndexStates = () => {
|
||||
view.calendarFieldMetadataId ?? null,
|
||||
);
|
||||
|
||||
if (isDefined(view.viewGroups?.[0]?.fieldMetadataId)) {
|
||||
if (isDefined(view.mainGroupByFieldMetadataId)) {
|
||||
const recordIndexGroupFieldMetadataItemId =
|
||||
view.viewGroups?.[0]?.fieldMetadataId;
|
||||
view.mainGroupByFieldMetadataId;
|
||||
|
||||
const { fieldMetadataItem: recordIndexGroupFieldMetadataItem } =
|
||||
getFieldMetadataItemByIdOrThrow(
|
||||
|
||||
+7
-1
@@ -1,9 +1,11 @@
|
||||
import { useObjectPermissionsForObject } from '@/object-record/hooks/useObjectPermissionsForObject';
|
||||
import { useCurrentRecordGroupId } from '@/object-record/record-group/hooks/useCurrentRecordGroupId';
|
||||
import { recordGroupDefinitionFamilyState } from '@/object-record/record-group/states/recordGroupDefinitionFamilyState';
|
||||
import { recordIndexGroupFieldMetadataItemComponentState } from '@/object-record/record-index/states/recordIndexGroupFieldMetadataComponentState';
|
||||
import { useRecordTableContextOrThrow } from '@/object-record/record-table/contexts/RecordTableContext';
|
||||
import { useCreateNewIndexRecord } from '@/object-record/record-table/hooks/useCreateNewIndexRecord';
|
||||
import { RecordTableActionRow } from '@/object-record/record-table/record-table-row/components/RecordTableActionRow';
|
||||
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { IconPlus } from 'twenty-ui/display';
|
||||
@@ -17,12 +19,16 @@ export const RecordTableRecordGroupSectionAddNew = () => {
|
||||
recordGroupDefinitionFamilyState(currentRecordGroupId),
|
||||
);
|
||||
|
||||
const mainGroupByFieldMetadata = useRecoilComponentValue(
|
||||
recordIndexGroupFieldMetadataItemComponentState,
|
||||
);
|
||||
|
||||
const { createNewIndexRecord } = useCreateNewIndexRecord({
|
||||
objectMetadataItem,
|
||||
});
|
||||
|
||||
const fieldMetadataItem = objectMetadataItem.fields.find(
|
||||
(field) => field.id === recordGroup?.fieldMetadataId,
|
||||
(field) => field.id === mainGroupByFieldMetadata?.id,
|
||||
);
|
||||
|
||||
const objectPermissions = useObjectPermissionsForObject(
|
||||
|
||||
@@ -24,7 +24,7 @@ export const VIEW_FRAGMENT = gql`
|
||||
openRecordIn
|
||||
kanbanAggregateOperation
|
||||
kanbanAggregateOperationFieldMetadataId
|
||||
# mainGroupByFieldMetadataId
|
||||
mainGroupByFieldMetadataId
|
||||
anyFieldFilterValue
|
||||
calendarFieldMetadataId
|
||||
calendarLayout
|
||||
|
||||
@@ -3,7 +3,6 @@ import { gql } from '@apollo/client';
|
||||
export const VIEW_GROUP_FRAGMENT = gql`
|
||||
fragment ViewGroupFragment on CoreViewGroup {
|
||||
id
|
||||
fieldMetadataId
|
||||
isVisible
|
||||
fieldValue
|
||||
position
|
||||
|
||||
@@ -32,9 +32,11 @@ export const usePersistViewGroupRecords = () => {
|
||||
const { enqueueErrorSnackBar } = useSnackBar();
|
||||
|
||||
const createViewGroups = useCallback(
|
||||
async (
|
||||
createCoreViewGroupInputs: CreateManyCoreViewGroupsMutationVariables,
|
||||
): Promise<
|
||||
async ({
|
||||
createCoreViewGroupInputs,
|
||||
}: {
|
||||
createCoreViewGroupInputs: CreateManyCoreViewGroupsMutationVariables;
|
||||
}): Promise<
|
||||
MetadataRequestResult<Awaited<
|
||||
ReturnType<typeof createManyCoreViewGroupsMutation>
|
||||
> | null>
|
||||
|
||||
@@ -11,9 +11,9 @@ import { usePersistView } from '@/views/hooks/internal/usePersistView';
|
||||
import { usePersistViewField } from '@/views/hooks/internal/usePersistViewField';
|
||||
import { usePersistViewFilterRecords } from '@/views/hooks/internal/usePersistViewFilter';
|
||||
import { usePersistViewFilterGroupRecords } from '@/views/hooks/internal/usePersistViewFilterGroup';
|
||||
import { usePersistViewGroupRecords } from '@/views/hooks/internal/usePersistViewGroup';
|
||||
import { usePersistViewSortRecords } from '@/views/hooks/internal/usePersistViewSort';
|
||||
import { useRefreshCoreViewsByObjectMetadataId } from '@/views/hooks/useRefreshCoreViewsByObjectMetadataId';
|
||||
import { useTriggerViewGroupOptimisticEffect } from '@/views/optimistic-effects/hooks/useTriggerViewGroupOptimisticEffect';
|
||||
import { isPersistingViewFieldsState } from '@/views/states/isPersistingViewFieldsState';
|
||||
import { coreViewFromViewIdFamilySelector } from '@/views/states/selectors/coreViewFromViewIdFamilySelector';
|
||||
import { type GraphQLView } from '@/views/types/GraphQLView';
|
||||
@@ -28,7 +28,10 @@ import { mapRecordSortToViewSort } from '@/views/utils/mapRecordSortToViewSort';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { v4 } from 'uuid';
|
||||
import { ViewCalendarLayout } from '~/generated-metadata/graphql';
|
||||
import {
|
||||
type CoreViewGroup,
|
||||
ViewCalendarLayout,
|
||||
} from '~/generated-metadata/graphql';
|
||||
import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull';
|
||||
|
||||
export const useCreateViewFromCurrentView = (viewBarComponentId?: string) => {
|
||||
@@ -47,7 +50,8 @@ export const useCreateViewFromCurrentView = (viewBarComponentId?: string) => {
|
||||
|
||||
const { createViewSorts } = usePersistViewSortRecords();
|
||||
|
||||
const { createViewGroups } = usePersistViewGroupRecords();
|
||||
const { triggerViewGroupOptimisticEffect } =
|
||||
useTriggerViewGroupOptimisticEffect();
|
||||
|
||||
const { createViewFilters } = usePersistViewFilterRecords();
|
||||
|
||||
@@ -182,7 +186,10 @@ export const useCreateViewFromCurrentView = (viewBarComponentId?: string) => {
|
||||
}
|
||||
|
||||
if (type === ViewType.Kanban) {
|
||||
if (!isDefined(mainGroupByFieldMetadataId)) {
|
||||
if (
|
||||
!isDefined(mainGroupByFieldMetadataId) ||
|
||||
mainGroupByFieldMetadataId === ''
|
||||
) {
|
||||
throw new Error('Kanban view must have a kanban field');
|
||||
}
|
||||
|
||||
@@ -194,33 +201,38 @@ export const useCreateViewFromCurrentView = (viewBarComponentId?: string) => {
|
||||
({
|
||||
id: v4(),
|
||||
__typename: 'ViewGroup',
|
||||
fieldMetadataId: mainGroupByFieldMetadataId,
|
||||
fieldValue: option.value,
|
||||
isVisible: true,
|
||||
position: index,
|
||||
}) satisfies ViewGroup,
|
||||
) ?? [];
|
||||
|
||||
viewGroupsToCreate.push({
|
||||
__typename: 'ViewGroup',
|
||||
id: v4(),
|
||||
fieldValue: '',
|
||||
position: viewGroupsToCreate.length,
|
||||
isVisible: true,
|
||||
fieldMetadataId: mainGroupByFieldMetadataId,
|
||||
} satisfies ViewGroup);
|
||||
|
||||
const groupResult = await createViewGroups({
|
||||
inputs: viewGroupsToCreate.map(({ __typename, ...viewGroup }) => ({
|
||||
...viewGroup,
|
||||
viewId: newViewId,
|
||||
})),
|
||||
});
|
||||
|
||||
if (groupResult.status === 'failed') {
|
||||
set(isPersistingViewFieldsState, false);
|
||||
return undefined;
|
||||
if (
|
||||
objectMetadataItem.fields.find(
|
||||
(field) => field.id === mainGroupByFieldMetadataId,
|
||||
)?.isNullable === true
|
||||
) {
|
||||
viewGroupsToCreate.push({
|
||||
__typename: 'ViewGroup',
|
||||
id: v4(),
|
||||
fieldValue: '',
|
||||
position: viewGroupsToCreate.length,
|
||||
isVisible: true,
|
||||
} satisfies ViewGroup);
|
||||
}
|
||||
|
||||
triggerViewGroupOptimisticEffect({
|
||||
createdViewGroups: viewGroupsToCreate.map(
|
||||
({ __typename, ...viewGroup }) =>
|
||||
({
|
||||
...viewGroup,
|
||||
viewId: newViewId,
|
||||
createdAt: new Date().toISOString(),
|
||||
updatedAt: new Date().toISOString(),
|
||||
deletedAt: null,
|
||||
}) as Omit<CoreViewGroup, 'workspaceId'>,
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
if (shouldCopyFiltersAndSortsAndAggregate === true) {
|
||||
@@ -292,7 +304,6 @@ export const useCreateViewFromCurrentView = (viewBarComponentId?: string) => {
|
||||
anyFieldFilterValue,
|
||||
objectMetadataItem.fields,
|
||||
objectMetadataItem.id,
|
||||
createViewGroups,
|
||||
currentRecordFilterGroups,
|
||||
currentRecordFilters,
|
||||
currentRecordSorts,
|
||||
@@ -300,6 +311,7 @@ export const useCreateViewFromCurrentView = (viewBarComponentId?: string) => {
|
||||
createViewFilters,
|
||||
createViewSorts,
|
||||
refreshCoreViewsByObjectMetadataId,
|
||||
triggerViewGroupOptimisticEffect,
|
||||
],
|
||||
);
|
||||
|
||||
|
||||
@@ -10,9 +10,13 @@ export const useGetViewGroupsFilters = (): RecordFilter[] => {
|
||||
currentView?.viewGroups
|
||||
.filter((recordGroup) => !recordGroup.isVisible)
|
||||
.map((recordGroup) => {
|
||||
if (!isDefined(currentView.mainGroupByFieldMetadataId)) {
|
||||
throw new Error('mainGroupByFieldMetadataId is required');
|
||||
}
|
||||
|
||||
return {
|
||||
id: recordGroup.id,
|
||||
fieldMetadataId: recordGroup.fieldMetadataId,
|
||||
fieldMetadataId: currentView.mainGroupByFieldMetadataId,
|
||||
value: JSON.stringify([recordGroup.fieldValue]),
|
||||
operand: ViewFilterOperand.IS_NOT,
|
||||
displayValue: '',
|
||||
|
||||
@@ -24,11 +24,13 @@ export const useRefreshAllCoreViews = (
|
||||
.getLoadable(coreViewsState)
|
||||
.getValue();
|
||||
|
||||
const coreViewsFromResult = result.data.getCoreViews;
|
||||
|
||||
if (
|
||||
isDefined(result.data?.getCoreViews) &&
|
||||
!isDeeplyEqual(currentCoreViews, result.data.getCoreViews)
|
||||
!isDeeplyEqual(currentCoreViews, coreViewsFromResult)
|
||||
) {
|
||||
set(coreViewsState, result.data.getCoreViews);
|
||||
set(coreViewsState, coreViewsFromResult);
|
||||
}
|
||||
|
||||
return result.data?.getCoreViews;
|
||||
|
||||
+5
-5
@@ -49,18 +49,18 @@ export const useRefreshCoreViewsByObjectMetadataId = () => {
|
||||
)
|
||||
.getValue();
|
||||
|
||||
if (
|
||||
isDeeplyEqual(coreViewsForObjectMetadataId, result.data.getCoreViews)
|
||||
) {
|
||||
const coreViewsFromResult = result.data.getCoreViews;
|
||||
|
||||
if (isDeeplyEqual(coreViewsForObjectMetadataId, coreViewsFromResult)) {
|
||||
return;
|
||||
}
|
||||
|
||||
set(
|
||||
coreViewsByObjectMetadataIdFamilySelector(objectMetadataId),
|
||||
result.data.getCoreViews,
|
||||
coreViewsFromResult,
|
||||
);
|
||||
|
||||
for (const coreView of result.data.getCoreViews) {
|
||||
for (const coreView of coreViewsFromResult) {
|
||||
const existingView = coreViewsForObjectMetadataId.find(
|
||||
(coreViewForObjectMetadata) =>
|
||||
coreViewForObjectMetadata.id === coreView.id,
|
||||
|
||||
@@ -45,9 +45,7 @@ export const useSaveCurrentViewGroups = () => {
|
||||
|
||||
const existingField = currentViewGroups.find(
|
||||
(currentViewGroup) =>
|
||||
currentViewGroup.fieldValue === viewGroupToSave.fieldValue &&
|
||||
currentViewGroup.fieldMetadataId ===
|
||||
viewGroupToSave.fieldMetadataId,
|
||||
currentViewGroup.fieldValue === viewGroupToSave.fieldValue,
|
||||
);
|
||||
|
||||
if (isUndefinedOrNull(existingField)) {
|
||||
@@ -76,7 +74,6 @@ export const useSaveCurrentViewGroups = () => {
|
||||
update: {
|
||||
isVisible: viewGroupToSave.isVisible,
|
||||
position: viewGroupToSave.position,
|
||||
fieldMetadataId: viewGroupToSave.fieldMetadataId,
|
||||
fieldValue: viewGroupToSave.fieldValue,
|
||||
},
|
||||
},
|
||||
@@ -118,9 +115,7 @@ export const useSaveCurrentViewGroups = () => {
|
||||
.map((viewGroupToSave) => {
|
||||
const existingField = currentViewGroups.find(
|
||||
(currentViewGroup) =>
|
||||
currentViewGroup.fieldValue === viewGroupToSave.fieldValue &&
|
||||
currentViewGroup.fieldMetadataId ===
|
||||
viewGroupToSave.fieldMetadataId,
|
||||
currentViewGroup.fieldValue === viewGroupToSave.fieldValue,
|
||||
);
|
||||
|
||||
if (isUndefinedOrNull(existingField)) {
|
||||
@@ -148,7 +143,6 @@ export const useSaveCurrentViewGroups = () => {
|
||||
update: {
|
||||
isVisible: viewGroupToSave.isVisible,
|
||||
position: viewGroupToSave.position,
|
||||
fieldMetadataId: viewGroupToSave.fieldMetadataId,
|
||||
fieldValue: viewGroupToSave.fieldValue,
|
||||
},
|
||||
},
|
||||
@@ -160,18 +154,22 @@ export const useSaveCurrentViewGroups = () => {
|
||||
(viewFieldToSave) =>
|
||||
!currentViewGroups.some(
|
||||
(currentViewGroup) =>
|
||||
currentViewGroup.fieldValue === viewFieldToSave.fieldValue &&
|
||||
currentViewGroup.fieldMetadataId ===
|
||||
viewFieldToSave.fieldMetadataId,
|
||||
currentViewGroup.fieldValue === viewFieldToSave.fieldValue,
|
||||
),
|
||||
);
|
||||
|
||||
if (!isDefined(view.mainGroupByFieldMetadataId)) {
|
||||
throw new Error('mainGroupByFieldMetadataId is required');
|
||||
}
|
||||
|
||||
await Promise.all([
|
||||
createViewGroups({
|
||||
inputs: viewGroupsToCreate.map(({ __typename, ...viewGroup }) => ({
|
||||
...viewGroup,
|
||||
viewId: view.id,
|
||||
})),
|
||||
createCoreViewGroupInputs: {
|
||||
inputs: viewGroupsToCreate.map((viewGroupToCreate) => ({
|
||||
...viewGroupToCreate,
|
||||
viewId: view.id,
|
||||
})),
|
||||
},
|
||||
}),
|
||||
updateViewGroups(viewGroupsToUpdate),
|
||||
]);
|
||||
|
||||
+1
@@ -6,6 +6,7 @@ import { useRecoilCallback } from 'recoil';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { type CoreViewGroup } from '~/generated/graphql';
|
||||
import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
|
||||
|
||||
type UpdatedDeletedCoreViewGroup = {
|
||||
createdViewGroups?: Omit<CoreViewGroup, 'workspaceId'>[];
|
||||
updatedViewGroups?: Omit<CoreViewGroup, 'workspaceId'>[];
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
export type ViewGroup = {
|
||||
__typename: 'ViewGroup';
|
||||
id: string;
|
||||
fieldMetadataId: string;
|
||||
isVisible: boolean;
|
||||
fieldValue: string;
|
||||
position: number;
|
||||
|
||||
@@ -4,13 +4,12 @@ import { type CoreViewGroup } from '~/generated/graphql';
|
||||
export const convertCoreViewGroupToViewGroup = (
|
||||
coreViewGroup: Pick<
|
||||
CoreViewGroup,
|
||||
'id' | 'fieldMetadataId' | 'isVisible' | 'fieldValue' | 'position'
|
||||
'id' | 'isVisible' | 'fieldValue' | 'position'
|
||||
>,
|
||||
): ViewGroup => {
|
||||
return {
|
||||
__typename: 'ViewGroup',
|
||||
id: coreViewGroup.id,
|
||||
fieldMetadataId: coreViewGroup.fieldMetadataId,
|
||||
isVisible: coreViewGroup.isVisible,
|
||||
fieldValue: coreViewGroup.fieldValue,
|
||||
position: coreViewGroup.position,
|
||||
|
||||
+4
-7
@@ -8,9 +8,11 @@ import { isDefined } from 'twenty-shared/utils';
|
||||
import { FieldMetadataType } from '~/generated-metadata/graphql';
|
||||
|
||||
export const mapViewGroupsToRecordGroupDefinitions = ({
|
||||
mainGroupByFieldMetadataId,
|
||||
objectMetadataItem,
|
||||
viewGroups,
|
||||
}: {
|
||||
mainGroupByFieldMetadataId: string;
|
||||
objectMetadataItem: ObjectMetadataItem;
|
||||
viewGroups: ViewGroup[];
|
||||
}): RecordGroupDefinition[] => {
|
||||
@@ -18,10 +20,10 @@ export const mapViewGroupsToRecordGroupDefinitions = ({
|
||||
return [];
|
||||
}
|
||||
|
||||
const fieldMetadataId = viewGroups?.[0]?.fieldMetadataId;
|
||||
const selectFieldMetadataItem = objectMetadataItem.fields.find(
|
||||
(field) =>
|
||||
field.id === fieldMetadataId && field.type === FieldMetadataType.SELECT,
|
||||
field.id === mainGroupByFieldMetadataId &&
|
||||
field.type === FieldMetadataType.SELECT,
|
||||
);
|
||||
|
||||
if (!selectFieldMetadataItem) {
|
||||
@@ -36,10 +38,6 @@ export const mapViewGroupsToRecordGroupDefinitions = ({
|
||||
|
||||
const recordGroupDefinitionsFromViewGroups = viewGroups
|
||||
.map((viewGroup) => {
|
||||
if (viewGroup.fieldMetadataId !== selectFieldMetadataItem.id) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const selectedOption = selectFieldMetadataItem.options?.find(
|
||||
(option) => option.value === viewGroup.fieldValue,
|
||||
);
|
||||
@@ -50,7 +48,6 @@ export const mapViewGroupsToRecordGroupDefinitions = ({
|
||||
|
||||
return {
|
||||
id: viewGroup.id,
|
||||
fieldMetadataId: viewGroup.fieldMetadataId,
|
||||
type: !isDefined(selectedOption)
|
||||
? RecordGroupDefinitionType.NoValue
|
||||
: RecordGroupDefinitionType.Value,
|
||||
|
||||
@@ -7,7 +7,6 @@ export const recordGroupDefinitionToViewGroup = (
|
||||
return {
|
||||
__typename: 'ViewGroup',
|
||||
id: recordGroup.id,
|
||||
fieldMetadataId: recordGroup.fieldMetadataId,
|
||||
position: recordGroup.position,
|
||||
isVisible: recordGroup.isVisible ?? true,
|
||||
fieldValue: recordGroup.value ?? '',
|
||||
|
||||
@@ -28,6 +28,7 @@ export const mockedViewsData: View[] = [
|
||||
type: ViewType.Table,
|
||||
icon: 'IconSkyline',
|
||||
key: ViewKey.Index,
|
||||
mainGroupByFieldMetadataId: null,
|
||||
kanbanAggregateOperation: AggregateOperations.COUNT,
|
||||
kanbanAggregateOperationFieldMetadataId: '',
|
||||
position: 0,
|
||||
@@ -48,6 +49,7 @@ export const mockedViewsData: View[] = [
|
||||
type: ViewType.Table,
|
||||
icon: 'IconPerson',
|
||||
key: ViewKey.Index,
|
||||
mainGroupByFieldMetadataId: null,
|
||||
kanbanAggregateOperation: AggregateOperations.COUNT,
|
||||
kanbanAggregateOperationFieldMetadataId: '',
|
||||
position: 0,
|
||||
@@ -68,6 +70,7 @@ export const mockedViewsData: View[] = [
|
||||
type: ViewType.Kanban,
|
||||
icon: 'IconOpportunity',
|
||||
key: ViewKey.Index,
|
||||
mainGroupByFieldMetadataId: null,
|
||||
kanbanAggregateOperation: AggregateOperations.COUNT,
|
||||
kanbanAggregateOperationFieldMetadataId: '',
|
||||
position: 0,
|
||||
@@ -88,6 +91,7 @@ export const mockedViewsData: View[] = [
|
||||
type: ViewType.Table,
|
||||
icon: 'IconSkyline',
|
||||
key: null,
|
||||
mainGroupByFieldMetadataId: null,
|
||||
kanbanAggregateOperation: AggregateOperations.COUNT,
|
||||
kanbanAggregateOperationFieldMetadataId: '',
|
||||
position: 0,
|
||||
|
||||
@@ -1008,7 +1008,7 @@ export class ConfigVariables {
|
||||
description: 'Maximum complexity allowed for Common API queries',
|
||||
type: ConfigVariableType.NUMBER,
|
||||
})
|
||||
COMMON_QUERY_COMPLEXITY_LIMIT = 50;
|
||||
COMMON_QUERY_COMPLEXITY_LIMIT = 2000;
|
||||
|
||||
@ConfigVariablesMetadata({
|
||||
group: ConfigVariablesGroup.RATE_LIMITING,
|
||||
|
||||
-4
@@ -65,10 +65,6 @@ export const ALL_METADATA_RELATED_METADATA_BY_FOREIGN_KEY = {
|
||||
metadataName: 'view',
|
||||
flatEntityForeignKeyAggregator: 'viewGroupIds',
|
||||
},
|
||||
fieldMetadataId: {
|
||||
metadataName: 'fieldMetadata',
|
||||
flatEntityForeignKeyAggregator: 'viewGroupIds',
|
||||
},
|
||||
},
|
||||
index: {
|
||||
objectMetadataId: {
|
||||
|
||||
+5
-5
@@ -27,7 +27,7 @@ describe('addFlatEntityToFlatEntityAndRelatedEntityMapsThroughMutationOrThrow',
|
||||
applicationId,
|
||||
});
|
||||
|
||||
const mockFieldMEtadata = getFlatFieldMetadataMock({
|
||||
const mockFieldMetadata = getFlatFieldMetadataMock({
|
||||
objectMetadataId,
|
||||
id: '202020-71a3-4856-a3d0-d08cea0ecec6',
|
||||
type: FieldMetadataType.DATE,
|
||||
@@ -35,9 +35,9 @@ describe('addFlatEntityToFlatEntityAndRelatedEntityMapsThroughMutationOrThrow',
|
||||
applicationId,
|
||||
universalIdentifier: 'field-universal-1',
|
||||
viewFieldIds: [],
|
||||
viewGroupIds: [],
|
||||
viewFilterIds: [],
|
||||
calendarViewIds: [],
|
||||
mainGroupByFieldMetadataViewIds: [],
|
||||
});
|
||||
|
||||
const mockView: Pick<FlatView, 'id'> & Partial<FlatView> = {
|
||||
@@ -49,13 +49,13 @@ describe('addFlatEntityToFlatEntityAndRelatedEntityMapsThroughMutationOrThrow',
|
||||
viewFilterIds: [],
|
||||
viewGroupIds: [],
|
||||
applicationId,
|
||||
calendarFieldMetadataId: mockFieldMEtadata.id,
|
||||
calendarFieldMetadataId: mockFieldMetadata.id,
|
||||
};
|
||||
|
||||
const flatEntityAndRelatedMapsToMutate: MetadataFlatEntityAndRelatedFlatEntityMaps<'view'> =
|
||||
{
|
||||
flatFieldMetadataMaps: addFlatEntityToFlatEntityMapsOrThrow({
|
||||
flatEntity: mockFieldMEtadata,
|
||||
flatEntity: mockFieldMetadata,
|
||||
flatEntityMaps: createEmptyFlatEntityMaps(),
|
||||
}),
|
||||
flatObjectMetadataMaps: addFlatEntityToFlatEntityMapsOrThrow({
|
||||
@@ -85,7 +85,7 @@ describe('addFlatEntityToFlatEntityAndRelatedEntityMapsThroughMutationOrThrow',
|
||||
|
||||
expect(
|
||||
flatEntityAndRelatedMapsToMutate.flatFieldMetadataMaps.byId[
|
||||
mockFieldMEtadata.id
|
||||
mockFieldMetadata.id
|
||||
],
|
||||
).toMatchObject<Partial<FlatFieldMetadata>>({
|
||||
calendarViewIds: [mockView.id],
|
||||
|
||||
+1
-1
@@ -34,10 +34,10 @@ describe('deleteFlatEntityFromFlatEntityAndRelatedEntityMapsThroughMutationOrThr
|
||||
type: FieldMetadataType.DATE,
|
||||
universalIdentifier: 'field-universal-1',
|
||||
viewFieldIds: [],
|
||||
viewGroupIds: [],
|
||||
viewFilterIds: [],
|
||||
workspaceId,
|
||||
calendarViewIds: [viewId],
|
||||
mainGroupByFieldMetadataViewIds: [],
|
||||
applicationId,
|
||||
});
|
||||
|
||||
|
||||
-1
@@ -21,7 +21,6 @@ export const getFlatFieldMetadataMock = <T extends FieldMetadataType>(
|
||||
return {
|
||||
calendarViewIds: [],
|
||||
viewFilterIds: [],
|
||||
viewGroupIds: [],
|
||||
kanbanAggregateOperationViewIds: [],
|
||||
viewFieldIds: [],
|
||||
createdAt,
|
||||
|
||||
-1
@@ -35,7 +35,6 @@ export const getRelationTargetFlatFieldMetadataMock = ({
|
||||
calendarViewIds: [],
|
||||
mainGroupByFieldMetadataViewIds: [],
|
||||
viewFilterIds: [],
|
||||
viewGroupIds: [],
|
||||
viewFieldIds: [],
|
||||
kanbanAggregateOperationViewIds: [],
|
||||
createdAt,
|
||||
|
||||
-4
@@ -137,7 +137,6 @@ exports[`fromCreateFieldInputToFlatFieldMetadatasToCreate MORPH_RELATION test su
|
||||
"updatedAt": Any<ClockDate>,
|
||||
"viewFieldIds": [],
|
||||
"viewFilterIds": [],
|
||||
"viewGroupIds": [],
|
||||
"workspaceId": Any<String>,
|
||||
},
|
||||
{
|
||||
@@ -176,7 +175,6 @@ exports[`fromCreateFieldInputToFlatFieldMetadatasToCreate MORPH_RELATION test su
|
||||
"updatedAt": Any<ClockDate>,
|
||||
"viewFieldIds": [],
|
||||
"viewFilterIds": [],
|
||||
"viewGroupIds": [],
|
||||
"workspaceId": Any<String>,
|
||||
},
|
||||
{
|
||||
@@ -213,7 +211,6 @@ exports[`fromCreateFieldInputToFlatFieldMetadatasToCreate MORPH_RELATION test su
|
||||
"updatedAt": Any<ClockDate>,
|
||||
"viewFieldIds": [],
|
||||
"viewFilterIds": [],
|
||||
"viewGroupIds": [],
|
||||
"workspaceId": Any<String>,
|
||||
},
|
||||
{
|
||||
@@ -252,7 +249,6 @@ exports[`fromCreateFieldInputToFlatFieldMetadatasToCreate MORPH_RELATION test su
|
||||
"updatedAt": Any<ClockDate>,
|
||||
"viewFieldIds": [],
|
||||
"viewFilterIds": [],
|
||||
"viewGroupIds": [],
|
||||
"workspaceId": Any<String>,
|
||||
},
|
||||
],
|
||||
|
||||
+26
-38
@@ -41,43 +41,36 @@ export class WorkspaceFlatFieldMetadataMapCacheService extends WorkspaceCachePro
|
||||
async computeForCache(
|
||||
workspaceId: string,
|
||||
): Promise<FlatEntityMaps<FlatFieldMetadata>> {
|
||||
const [fieldMetadatas, viewFields, viewFilters, viewGroups, views] =
|
||||
await Promise.all([
|
||||
this.fieldMetadataRepository.find({
|
||||
where: { workspaceId },
|
||||
withDeleted: true,
|
||||
}),
|
||||
this.viewFieldRepository.find({
|
||||
where: { workspaceId },
|
||||
select: ['id', 'fieldMetadataId'],
|
||||
withDeleted: true,
|
||||
}),
|
||||
this.viewFilterRepository.find({
|
||||
where: { workspaceId },
|
||||
select: ['id', 'fieldMetadataId'],
|
||||
withDeleted: true,
|
||||
}),
|
||||
this.viewGroupRepository.find({
|
||||
where: { workspaceId },
|
||||
select: ['id', 'fieldMetadataId'],
|
||||
withDeleted: true,
|
||||
}),
|
||||
this.viewRepository.find({
|
||||
where: { workspaceId },
|
||||
select: [
|
||||
'id',
|
||||
'kanbanAggregateOperationFieldMetadataId',
|
||||
'calendarFieldMetadataId',
|
||||
'mainGroupByFieldMetadataId',
|
||||
],
|
||||
withDeleted: true,
|
||||
}),
|
||||
]);
|
||||
const [fieldMetadatas, viewFields, viewFilters, views] = await Promise.all([
|
||||
this.fieldMetadataRepository.find({
|
||||
where: { workspaceId },
|
||||
withDeleted: true,
|
||||
}),
|
||||
this.viewFieldRepository.find({
|
||||
where: { workspaceId },
|
||||
select: ['id', 'fieldMetadataId'],
|
||||
withDeleted: true,
|
||||
}),
|
||||
this.viewFilterRepository.find({
|
||||
where: { workspaceId },
|
||||
select: ['id', 'fieldMetadataId'],
|
||||
withDeleted: true,
|
||||
}),
|
||||
this.viewRepository.find({
|
||||
where: { workspaceId },
|
||||
select: [
|
||||
'id',
|
||||
'kanbanAggregateOperationFieldMetadataId',
|
||||
'calendarFieldMetadataId',
|
||||
'mainGroupByFieldMetadataId',
|
||||
],
|
||||
withDeleted: true,
|
||||
}),
|
||||
]);
|
||||
|
||||
const [
|
||||
viewFieldsByFieldId,
|
||||
viewFiltersByFieldId,
|
||||
viewGroupsByFieldId,
|
||||
calendarViewsByFieldId,
|
||||
kanbanViewsByFieldId,
|
||||
mainGroupByFieldMetadataViewsByFieldId,
|
||||
@@ -91,10 +84,6 @@ export class WorkspaceFlatFieldMetadataMapCacheService extends WorkspaceCachePro
|
||||
entities: viewFilters,
|
||||
foreignKey: 'fieldMetadataId',
|
||||
},
|
||||
{
|
||||
entities: viewGroups,
|
||||
foreignKey: 'fieldMetadataId',
|
||||
},
|
||||
{
|
||||
entities: views,
|
||||
foreignKey: 'calendarFieldMetadataId',
|
||||
@@ -117,7 +106,6 @@ export class WorkspaceFlatFieldMetadataMapCacheService extends WorkspaceCachePro
|
||||
...fieldMetadataEntity,
|
||||
viewFields: viewFieldsByFieldId.get(fieldMetadataEntity.id) || [],
|
||||
viewFilters: viewFiltersByFieldId.get(fieldMetadataEntity.id) || [],
|
||||
viewGroups: viewGroupsByFieldId.get(fieldMetadataEntity.id) || [],
|
||||
kanbanAggregateOperationViews:
|
||||
kanbanViewsByFieldId.get(fieldMetadataEntity.id) || [],
|
||||
calendarViews: calendarViewsByFieldId.get(fieldMetadataEntity.id) || [],
|
||||
|
||||
+4
-3
@@ -14,19 +14,20 @@ export const FIELD_METADATA_RELATION_PROPERTIES = [
|
||||
'kanbanAggregateOperationViews',
|
||||
'calendarViews',
|
||||
'mainGroupByFieldMetadataViews',
|
||||
'viewGroups',
|
||||
] as const satisfies (keyof FieldMetadataEntity)[];
|
||||
|
||||
export type FieldMetadataEntityRelationProperties =
|
||||
(typeof FIELD_METADATA_RELATION_PROPERTIES)[number];
|
||||
|
||||
export type FlatFieldMetadata<T extends FieldMetadataType = FieldMetadataType> =
|
||||
Omit<FieldMetadataEntity<T>, FieldMetadataEntityRelationProperties> & {
|
||||
Omit<
|
||||
FieldMetadataEntity<T>,
|
||||
FieldMetadataEntityRelationProperties | 'viewGroups'
|
||||
> & {
|
||||
universalIdentifier: string;
|
||||
viewFieldIds: string[];
|
||||
viewFilterIds: string[];
|
||||
kanbanAggregateOperationViewIds: string[];
|
||||
calendarViewIds: string[];
|
||||
mainGroupByFieldMetadataViewIds: string[];
|
||||
viewGroupIds: string[];
|
||||
};
|
||||
|
||||
-1
@@ -26,7 +26,6 @@ export const fromFieldMetadataEntityToFlatFieldMetadata = <
|
||||
mainGroupByFieldMetadataViewIds:
|
||||
fieldMetadataEntity.mainGroupByFieldMetadataViews?.map(({ id }) => id) ??
|
||||
[],
|
||||
viewGroupIds: fieldMetadataEntity.viewGroups.map(({ id }) => id),
|
||||
viewFieldIds: fieldMetadataEntity.viewFields.map(({ id }) => id),
|
||||
viewFilterIds: fieldMetadataEntity.viewFilters.map(({ id }) => id),
|
||||
universalIdentifier:
|
||||
|
||||
-1
@@ -60,7 +60,6 @@ export const getDefaultFlatFieldMetadata = ({
|
||||
morphId: null,
|
||||
applicationId: workspaceCustomApplicationId,
|
||||
viewFilterIds: [],
|
||||
viewGroupIds: [],
|
||||
kanbanAggregateOperationViewIds: [],
|
||||
} as const satisfies FlatFieldMetadata;
|
||||
};
|
||||
|
||||
+7
-1
@@ -17,7 +17,10 @@ type HandleEnumFlatFieldMetadataOptionsUpdateSideEffectsArgs = FromTo<
|
||||
FlatFieldMetadata<EnumFieldMetadataType>,
|
||||
'flatFieldMetadata'
|
||||
> &
|
||||
Pick<AllFlatEntityMaps, 'flatViewFilterMaps' | 'flatViewGroupMaps'>;
|
||||
Pick<
|
||||
AllFlatEntityMaps,
|
||||
'flatViewFilterMaps' | 'flatViewGroupMaps' | 'flatViewMaps'
|
||||
>;
|
||||
|
||||
type EnumFieldMetadataSideEffectResult = FlatViewGroupsToDeleteUpdateAndCreate &
|
||||
FlatViewFiltersToDeleteAndUpdate;
|
||||
@@ -36,6 +39,7 @@ export const handleEnumFlatFieldMetadataUpdateSideEffects = ({
|
||||
toFlatFieldMetadata,
|
||||
flatViewFilterMaps,
|
||||
flatViewGroupMaps,
|
||||
flatViewMaps,
|
||||
}: HandleEnumFlatFieldMetadataOptionsUpdateSideEffectsArgs): EnumFieldMetadataSideEffectResult => {
|
||||
const sideEffectResult = structuredClone(
|
||||
EMPTY_ENUM_FIELD_METADATA_SIDE_EFFECT_RESULT,
|
||||
@@ -69,6 +73,7 @@ export const handleEnumFlatFieldMetadataUpdateSideEffects = ({
|
||||
flatViewGroupsToDelete,
|
||||
flatViewGroupsToUpdate,
|
||||
} = recomputeViewGroupsOnFlatFieldMetadataOptionsUpdate({
|
||||
flatViewMaps,
|
||||
flatViewGroupMaps,
|
||||
fromFlatFieldMetadata,
|
||||
update: optionsPropertyUpdate,
|
||||
@@ -82,6 +87,7 @@ export const handleEnumFlatFieldMetadataUpdateSideEffects = ({
|
||||
if (fromFlatFieldMetadata.isNullable !== toFlatFieldMetadata.isNullable) {
|
||||
const { flatViewGroupsToCreate, flatViewGroupsToDelete } =
|
||||
recomputeViewGroupsOnEnumFlatFieldMetadataIsNullableUpdate({
|
||||
flatViewMaps,
|
||||
flatViewGroupMaps,
|
||||
fromFlatFieldMetadata,
|
||||
toFlatFieldMetadata,
|
||||
|
||||
+10
-1
@@ -46,8 +46,16 @@ export const handleFieldMetadataDeactivationSideEffects = ({
|
||||
flatEntityMaps: flatViewFieldMaps,
|
||||
});
|
||||
|
||||
const flatViewsAffected: FlatView[] =
|
||||
findManyFlatEntityByIdInFlatEntityMapsOrThrow({
|
||||
flatEntityIds: fromFlatFieldMetadata.mainGroupByFieldMetadataViewIds,
|
||||
flatEntityMaps: flatViewMaps,
|
||||
});
|
||||
|
||||
const flatViewGroups = findManyFlatEntityByIdInFlatEntityMapsOrThrow({
|
||||
flatEntityIds: fromFlatFieldMetadata.viewGroupIds,
|
||||
flatEntityIds: flatViewsAffected.flatMap(
|
||||
(flatView) => flatView.viewGroupIds,
|
||||
),
|
||||
flatEntityMaps: flatViewGroupMaps,
|
||||
});
|
||||
|
||||
@@ -60,6 +68,7 @@ export const handleFieldMetadataDeactivationSideEffects = ({
|
||||
...new Set([
|
||||
...Object.keys(flatViewGroupRecordByViewId),
|
||||
...fromFlatFieldMetadata.calendarViewIds,
|
||||
...fromFlatFieldMetadata.mainGroupByFieldMetadataViewIds,
|
||||
]),
|
||||
];
|
||||
|
||||
|
||||
+1
@@ -100,6 +100,7 @@ export const handleFlatFieldMetadataUpdateSideEffect = ({
|
||||
flatViewGroupsToDelete,
|
||||
flatViewGroupsToUpdate,
|
||||
} = handleEnumFlatFieldMetadataUpdateSideEffects({
|
||||
flatViewMaps,
|
||||
flatViewFilterMaps,
|
||||
flatViewGroupMaps,
|
||||
fromFlatFieldMetadata,
|
||||
|
||||
+11
-3
@@ -12,7 +12,7 @@ type RecomputeViewGroupsOnEnumFlatFieldMetadataIsNullableUpdateArgs = FromTo<
|
||||
FlatFieldMetadata,
|
||||
'flatFieldMetadata'
|
||||
> &
|
||||
Pick<AllFlatEntityMaps, 'flatViewGroupMaps'>;
|
||||
Pick<AllFlatEntityMaps, 'flatViewMaps' | 'flatViewGroupMaps'>;
|
||||
|
||||
type EnumFieldMetadataIsNullableUpdateSideEffect = {
|
||||
flatViewGroupsToDelete: FlatViewGroup[];
|
||||
@@ -25,6 +25,7 @@ const EMPTY_ENUM_FIELD_METADATA_IS_NULLABLE_UPDATE_SIDE_EFFECT_RESULT: EnumField
|
||||
};
|
||||
|
||||
export const recomputeViewGroupsOnEnumFlatFieldMetadataIsNullableUpdate = ({
|
||||
flatViewMaps,
|
||||
flatViewGroupMaps: allFlatViewGroups,
|
||||
fromFlatFieldMetadata,
|
||||
toFlatFieldMetadata,
|
||||
@@ -35,8 +36,15 @@ export const recomputeViewGroupsOnEnumFlatFieldMetadataIsNullableUpdate = ({
|
||||
const sideEffectResult = structuredClone(
|
||||
EMPTY_ENUM_FIELD_METADATA_IS_NULLABLE_UPDATE_SIDE_EFFECT_RESULT,
|
||||
);
|
||||
const flatViewsAffected = findManyFlatEntityByIdInFlatEntityMapsOrThrow({
|
||||
flatEntityIds: fromFlatFieldMetadata.mainGroupByFieldMetadataViewIds,
|
||||
flatEntityMaps: flatViewMaps,
|
||||
});
|
||||
|
||||
const flatViewGroups = findManyFlatEntityByIdInFlatEntityMapsOrThrow({
|
||||
flatEntityIds: fromFlatFieldMetadata.viewGroupIds,
|
||||
flatEntityIds: flatViewsAffected.flatMap(
|
||||
(flatView) => flatView.viewGroupIds,
|
||||
),
|
||||
flatEntityMaps: allFlatViewGroups,
|
||||
});
|
||||
const { flatViewGroupRecordByViewId, highestViewGroupPositionByViewId } =
|
||||
@@ -60,7 +68,6 @@ export const recomputeViewGroupsOnEnumFlatFieldMetadataIsNullableUpdate = ({
|
||||
const createdAt = new Date();
|
||||
|
||||
sideEffectResult.flatViewGroupsToCreate.push({
|
||||
fieldMetadataId: toFlatFieldMetadata.id,
|
||||
id: viewGroupId,
|
||||
universalIdentifier: viewGroupId,
|
||||
fieldValue: '',
|
||||
@@ -72,6 +79,7 @@ export const recomputeViewGroupsOnEnumFlatFieldMetadataIsNullableUpdate = ({
|
||||
deletedAt: null,
|
||||
viewId,
|
||||
applicationId: toFlatFieldMetadata.applicationId,
|
||||
fieldMetadataId: fromFlatFieldMetadata.id,
|
||||
});
|
||||
} else if (isDefined(emptyValueFlatViewGroup)) {
|
||||
sideEffectResult.flatViewGroupsToDelete.push(emptyValueFlatViewGroup);
|
||||
|
||||
+11
-4
@@ -17,7 +17,7 @@ import { type PropertyUpdate } from 'src/engine/workspace-manager/workspace-migr
|
||||
type RecomputeViewGroupsOnFlatFieldMetadataOptionsUpdateArgs = {
|
||||
fromFlatFieldMetadata: FlatFieldMetadata<EnumFieldMetadataType>;
|
||||
update: PropertyUpdate<FlatFieldMetadata<EnumFieldMetadataType>, 'options'>;
|
||||
} & Pick<AllFlatEntityMaps, 'flatViewGroupMaps'>;
|
||||
} & Pick<AllFlatEntityMaps, 'flatViewMaps' | 'flatViewGroupMaps'>;
|
||||
|
||||
export type FlatViewGroupsToDeleteUpdateAndCreate = {
|
||||
flatViewGroupsToDelete: FlatViewGroup[];
|
||||
@@ -25,6 +25,7 @@ export type FlatViewGroupsToDeleteUpdateAndCreate = {
|
||||
flatViewGroupsToCreate: FlatViewGroup[];
|
||||
};
|
||||
export const recomputeViewGroupsOnFlatFieldMetadataOptionsUpdate = ({
|
||||
flatViewMaps,
|
||||
flatViewGroupMaps,
|
||||
fromFlatFieldMetadata,
|
||||
update,
|
||||
@@ -39,8 +40,15 @@ export const recomputeViewGroupsOnFlatFieldMetadataOptionsUpdate = ({
|
||||
toOptions: update.to,
|
||||
});
|
||||
|
||||
const flatViewsAffected = findManyFlatEntityByIdInFlatEntityMapsOrThrow({
|
||||
flatEntityIds: fromFlatFieldMetadata.mainGroupByFieldMetadataViewIds,
|
||||
flatEntityMaps: flatViewMaps,
|
||||
});
|
||||
|
||||
const flatViewGroups = findManyFlatEntityByIdInFlatEntityMapsOrThrow({
|
||||
flatEntityIds: fromFlatFieldMetadata.viewGroupIds,
|
||||
flatEntityIds: flatViewsAffected.flatMap(
|
||||
(flatView) => flatView.viewGroupIds,
|
||||
),
|
||||
flatEntityMaps: flatViewGroupMaps,
|
||||
});
|
||||
|
||||
@@ -63,8 +71,7 @@ export const recomputeViewGroupsOnFlatFieldMetadataOptionsUpdate = ({
|
||||
const flatViewGroupsToUpdate = updatedFieldMetadataOptions.flatMap(
|
||||
({ from: fromOption, to: toOption }) =>
|
||||
flatViewGroups.flatMap((flatViewGroup) =>
|
||||
flatViewGroup.fieldValue === fromOption.value &&
|
||||
flatViewGroup.fieldMetadataId === fromFlatFieldMetadata.id
|
||||
flatViewGroup.fieldValue === fromOption.value
|
||||
? { ...flatViewGroup, fieldValue: toOption.value }
|
||||
: [],
|
||||
),
|
||||
|
||||
-1
@@ -1,7 +1,6 @@
|
||||
import { type ViewGroupEntityRelationProperties } from 'src/engine/metadata-modules/flat-view-group/types/flat-view-group.type';
|
||||
|
||||
export const VIEW_GROUP_ENTITY_RELATION_PROPERTIES = [
|
||||
'fieldMetadata',
|
||||
'view',
|
||||
'workspace',
|
||||
] as const satisfies ViewGroupEntityRelationProperties[];
|
||||
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
import {
|
||||
FlatEntityMapsException,
|
||||
FlatEntityMapsExceptionCode,
|
||||
} from 'src/engine/metadata-modules/flat-entity/exceptions/flat-entity-maps.exception';
|
||||
import { type AllFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/all-flat-entity-maps.type';
|
||||
import { type FlatViewGroup } from 'src/engine/metadata-modules/flat-view-group/types/flat-view-group.type';
|
||||
|
||||
type ComputeFlatViewGroupsOnViewCreateArgs = {
|
||||
flatViewToCreateId: string;
|
||||
mainGroupByFieldMetadataId: string;
|
||||
} & Pick<AllFlatEntityMaps, 'flatFieldMetadataMaps'>;
|
||||
|
||||
export const computeFlatViewGroupsOnViewCreate = ({
|
||||
flatViewToCreateId,
|
||||
mainGroupByFieldMetadataId,
|
||||
flatFieldMetadataMaps,
|
||||
}: ComputeFlatViewGroupsOnViewCreateArgs): FlatViewGroup[] => {
|
||||
const mainGroupByFieldMetadata =
|
||||
flatFieldMetadataMaps.byId[mainGroupByFieldMetadataId];
|
||||
|
||||
if (!isDefined(mainGroupByFieldMetadata)) {
|
||||
throw new FlatEntityMapsException(
|
||||
'mainGroupByFieldMetadataId not found',
|
||||
FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
const createdAt = new Date();
|
||||
|
||||
const flatViewGroupsFromOptions: FlatViewGroup[] = (
|
||||
mainGroupByFieldMetadata.options ?? []
|
||||
).map((option, index) => {
|
||||
const viewGroupId = v4();
|
||||
|
||||
return {
|
||||
id: viewGroupId,
|
||||
fieldMetadataId: mainGroupByFieldMetadata.id,
|
||||
viewId: flatViewToCreateId,
|
||||
workspaceId: mainGroupByFieldMetadata.workspaceId,
|
||||
createdAt,
|
||||
updatedAt: createdAt,
|
||||
deletedAt: null,
|
||||
universalIdentifier: viewGroupId,
|
||||
isVisible: true,
|
||||
fieldValue: option.value,
|
||||
position: index,
|
||||
applicationId: mainGroupByFieldMetadata.applicationId,
|
||||
};
|
||||
});
|
||||
|
||||
const flatViewGroups: FlatViewGroup[] = [...flatViewGroupsFromOptions];
|
||||
|
||||
if (mainGroupByFieldMetadata.isNullable === true) {
|
||||
const emptyGroupId = v4();
|
||||
|
||||
flatViewGroups.push({
|
||||
id: emptyGroupId,
|
||||
fieldMetadataId: mainGroupByFieldMetadata.id,
|
||||
viewId: flatViewToCreateId,
|
||||
workspaceId: mainGroupByFieldMetadata.workspaceId,
|
||||
createdAt,
|
||||
updatedAt: createdAt,
|
||||
deletedAt: null,
|
||||
universalIdentifier: emptyGroupId,
|
||||
isVisible: true,
|
||||
fieldValue: '',
|
||||
position: flatViewGroupsFromOptions.length,
|
||||
applicationId: mainGroupByFieldMetadata.applicationId,
|
||||
});
|
||||
}
|
||||
|
||||
return flatViewGroups;
|
||||
};
|
||||
+5
-3
@@ -8,15 +8,17 @@ export const fromCreateViewGroupInputToFlatViewGroupToCreate = ({
|
||||
createViewGroupInput: rawCreateViewGroupInput,
|
||||
workspaceId,
|
||||
workspaceCustomApplicationId,
|
||||
mainGroupByFieldMetadataId,
|
||||
}: {
|
||||
createViewGroupInput: CreateViewGroupInput;
|
||||
workspaceId: string;
|
||||
workspaceCustomApplicationId: string;
|
||||
mainGroupByFieldMetadataId: string;
|
||||
}): FlatViewGroup => {
|
||||
const { fieldMetadataId, viewId, ...createViewGroupInput } =
|
||||
const { viewId, ...createViewGroupInput } =
|
||||
trimAndRemoveDuplicatedWhitespacesFromObjectStringProperties(
|
||||
rawCreateViewGroupInput,
|
||||
['fieldMetadataId', 'fieldValue', 'id', 'viewId'],
|
||||
['fieldValue', 'id', 'viewId'],
|
||||
);
|
||||
|
||||
const createdAt = new Date();
|
||||
@@ -24,7 +26,7 @@ export const fromCreateViewGroupInputToFlatViewGroupToCreate = ({
|
||||
|
||||
return {
|
||||
id: viewGroupId,
|
||||
fieldMetadataId,
|
||||
fieldMetadataId: mainGroupByFieldMetadataId, // Mandatory because non-nullable until we completely remove it
|
||||
viewId,
|
||||
workspaceId,
|
||||
createdAt: createdAt,
|
||||
|
||||
+29
-3
@@ -1,6 +1,12 @@
|
||||
import { trimAndRemoveDuplicatedWhitespacesFromObjectStringProperties } from 'twenty-shared/utils';
|
||||
import {
|
||||
isDefined,
|
||||
trimAndRemoveDuplicatedWhitespacesFromObjectStringProperties,
|
||||
} from 'twenty-shared/utils';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
import { type AllFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/all-flat-entity-maps.type';
|
||||
import { type FlatViewGroup } from 'src/engine/metadata-modules/flat-view-group/types/flat-view-group.type';
|
||||
import { computeFlatViewGroupsOnViewCreate } from 'src/engine/metadata-modules/flat-view-group/utils/compute-flat-view-groups-on-view-create.util';
|
||||
import { type FlatView } from 'src/engine/metadata-modules/flat-view/types/flat-view.type';
|
||||
import { type CreateViewInput } from 'src/engine/metadata-modules/view/dtos/inputs/create-view.input';
|
||||
import { ViewOpenRecordIn } from 'src/engine/metadata-modules/view/enums/view-open-record-in';
|
||||
@@ -12,12 +18,17 @@ export const fromCreateViewInputToFlatViewToCreate = ({
|
||||
workspaceId,
|
||||
createdByUserWorkspaceId,
|
||||
workspaceCustomApplicationId,
|
||||
flatFieldMetadataMaps,
|
||||
}: {
|
||||
createViewInput: CreateViewInput;
|
||||
workspaceId: string;
|
||||
createdByUserWorkspaceId?: string;
|
||||
workspaceCustomApplicationId: string;
|
||||
}): FlatView => {
|
||||
flatFieldMetadataMaps: AllFlatEntityMaps['flatFieldMetadataMaps'];
|
||||
}): {
|
||||
flatViewToCreate: FlatView;
|
||||
flatViewGroupsToCreate: FlatViewGroup[];
|
||||
} => {
|
||||
const { objectMetadataId, ...createViewInput } =
|
||||
trimAndRemoveDuplicatedWhitespacesFromObjectStringProperties(
|
||||
rawCreateViewInput,
|
||||
@@ -27,7 +38,7 @@ export const fromCreateViewInputToFlatViewToCreate = ({
|
||||
const createdAt = new Date();
|
||||
const viewId = createViewInput.id ?? v4();
|
||||
|
||||
return {
|
||||
const flatViewToCreate = {
|
||||
id: viewId,
|
||||
objectMetadataId,
|
||||
workspaceId,
|
||||
@@ -58,4 +69,19 @@ export const fromCreateViewInputToFlatViewToCreate = ({
|
||||
viewGroupIds: [],
|
||||
applicationId: workspaceCustomApplicationId,
|
||||
};
|
||||
|
||||
let flatViewGroupsToCreate: FlatViewGroup[] = [];
|
||||
|
||||
if (isDefined(flatViewToCreate.mainGroupByFieldMetadataId)) {
|
||||
flatViewGroupsToCreate = computeFlatViewGroupsOnViewCreate({
|
||||
flatViewToCreateId: flatViewToCreate.id,
|
||||
mainGroupByFieldMetadataId: flatViewToCreate.mainGroupByFieldMetadataId,
|
||||
flatFieldMetadataMaps,
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
flatViewToCreate,
|
||||
flatViewGroupsToCreate,
|
||||
};
|
||||
};
|
||||
|
||||
+43
-2
@@ -5,9 +5,14 @@ import {
|
||||
trimAndRemoveDuplicatedWhitespacesFromObjectStringProperties,
|
||||
} from 'twenty-shared/utils';
|
||||
|
||||
import { type FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { type FlatViewGroupMaps } from 'src/engine/metadata-modules/flat-view-group/types/flat-view-group-maps.type';
|
||||
import { type FlatViewGroup } from 'src/engine/metadata-modules/flat-view-group/types/flat-view-group.type';
|
||||
import { FLAT_VIEW_EDITABLE_PROPERTIES } from 'src/engine/metadata-modules/flat-view/constants/flat-view-editable-properties.constant';
|
||||
import { type FlatViewMaps } from 'src/engine/metadata-modules/flat-view/types/flat-view-maps.type';
|
||||
import { type FlatView } from 'src/engine/metadata-modules/flat-view/types/flat-view.type';
|
||||
import { handleFlatViewUpdateSideEffect } from 'src/engine/metadata-modules/flat-view/utils/handle-flat-view-update-side-effect.util';
|
||||
import { type UpdateViewInput } from 'src/engine/metadata-modules/view/dtos/inputs/update-view.input';
|
||||
import {
|
||||
ViewException,
|
||||
@@ -18,10 +23,20 @@ import { mergeUpdateInExistingRecord } from 'src/utils/merge-update-in-existing-
|
||||
export const fromUpdateViewInputToFlatViewToUpdateOrThrow = ({
|
||||
updateViewInput: rawUpdateViewInput,
|
||||
flatViewMaps,
|
||||
flatViewGroupMaps,
|
||||
flatFieldMetadataMaps,
|
||||
userWorkspaceId,
|
||||
}: {
|
||||
updateViewInput: UpdateViewInput;
|
||||
flatViewMaps: FlatViewMaps;
|
||||
}): FlatView => {
|
||||
flatViewGroupMaps: FlatViewGroupMaps;
|
||||
flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>;
|
||||
userWorkspaceId?: string;
|
||||
}): {
|
||||
flatViewToUpdate: FlatView;
|
||||
flatViewGroupsToDelete: FlatViewGroup[];
|
||||
flatViewGroupsToCreate: FlatViewGroup[];
|
||||
} => {
|
||||
const { id: viewToUpdateId } =
|
||||
trimAndRemoveDuplicatedWhitespacesFromObjectStringProperties(
|
||||
rawUpdateViewInput,
|
||||
@@ -42,9 +57,35 @@ export const fromUpdateViewInputToFlatViewToUpdateOrThrow = ({
|
||||
FLAT_VIEW_EDITABLE_PROPERTIES,
|
||||
);
|
||||
|
||||
return mergeUpdateInExistingRecord({
|
||||
const flatViewToUpdate = mergeUpdateInExistingRecord({
|
||||
existing: existingFlatViewToUpdate,
|
||||
properties: FLAT_VIEW_EDITABLE_PROPERTIES,
|
||||
update: updatedEditableFieldProperties,
|
||||
});
|
||||
|
||||
// If changing visibility from WORKSPACE to UNLISTED, ensure createdByUserWorkspaceId is set
|
||||
// This prevents the view from disappearing for the user making the change
|
||||
if (
|
||||
isDefined(rawUpdateViewInput.visibility) &&
|
||||
rawUpdateViewInput.visibility === 'UNLISTED' &&
|
||||
existingFlatViewToUpdate.visibility === 'WORKSPACE' &&
|
||||
isDefined(userWorkspaceId)
|
||||
) {
|
||||
// Re-allocate the view to the current user
|
||||
flatViewToUpdate.createdByUserWorkspaceId = userWorkspaceId;
|
||||
}
|
||||
|
||||
const { flatViewGroupsToDelete, flatViewGroupsToCreate } =
|
||||
handleFlatViewUpdateSideEffect({
|
||||
fromFlatView: existingFlatViewToUpdate,
|
||||
toFlatView: flatViewToUpdate,
|
||||
flatViewGroupMaps: flatViewGroupMaps,
|
||||
flatFieldMetadataMaps: flatFieldMetadataMaps,
|
||||
});
|
||||
|
||||
return {
|
||||
flatViewToUpdate,
|
||||
flatViewGroupsToDelete,
|
||||
flatViewGroupsToCreate,
|
||||
};
|
||||
};
|
||||
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
import { type FromTo } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { type AllFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/all-flat-entity-maps.type';
|
||||
import { findManyFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/find-many-flat-entity-by-id-in-flat-entity-maps-or-throw.util';
|
||||
import { type FlatViewGroup } from 'src/engine/metadata-modules/flat-view-group/types/flat-view-group.type';
|
||||
import { computeFlatViewGroupsOnViewCreate } from 'src/engine/metadata-modules/flat-view-group/utils/compute-flat-view-groups-on-view-create.util';
|
||||
import { type FlatView } from 'src/engine/metadata-modules/flat-view/types/flat-view.type';
|
||||
|
||||
export type FlatViewUpdateSideEffects = {
|
||||
flatViewGroupsToDelete: FlatViewGroup[];
|
||||
flatViewGroupsToCreate: FlatViewGroup[];
|
||||
};
|
||||
|
||||
type HandleFlatViewUpdateSideEffectArgs = FromTo<FlatView, 'flatView'> &
|
||||
Pick<AllFlatEntityMaps, 'flatViewGroupMaps' | 'flatFieldMetadataMaps'>;
|
||||
|
||||
export const FLAT_VIEW_UPDATE_EMPTY_SIDE_EFFECTS: FlatViewUpdateSideEffects = {
|
||||
flatViewGroupsToDelete: [],
|
||||
flatViewGroupsToCreate: [],
|
||||
};
|
||||
|
||||
export const handleFlatViewUpdateSideEffect = ({
|
||||
fromFlatView,
|
||||
toFlatView,
|
||||
flatViewGroupMaps,
|
||||
flatFieldMetadataMaps,
|
||||
}: HandleFlatViewUpdateSideEffectArgs): FlatViewUpdateSideEffects => {
|
||||
const sideEffectResult = structuredClone(FLAT_VIEW_UPDATE_EMPTY_SIDE_EFFECTS);
|
||||
|
||||
const newMainGroupByFieldMetadataId = toFlatView.mainGroupByFieldMetadataId;
|
||||
|
||||
const hasMainGroupByFieldMetadataIdChanged =
|
||||
fromFlatView.mainGroupByFieldMetadataId !== newMainGroupByFieldMetadataId;
|
||||
|
||||
if (!hasMainGroupByFieldMetadataIdChanged) {
|
||||
return sideEffectResult;
|
||||
}
|
||||
|
||||
if (fromFlatView.viewGroupIds.length > 0) {
|
||||
sideEffectResult.flatViewGroupsToDelete =
|
||||
findManyFlatEntityByIdInFlatEntityMapsOrThrow({
|
||||
flatEntityIds: fromFlatView.viewGroupIds,
|
||||
flatEntityMaps: flatViewGroupMaps,
|
||||
});
|
||||
}
|
||||
|
||||
if (!isDefined(newMainGroupByFieldMetadataId)) {
|
||||
return sideEffectResult;
|
||||
}
|
||||
|
||||
sideEffectResult.flatViewGroupsToCreate = computeFlatViewGroupsOnViewCreate({
|
||||
flatViewToCreateId: toFlatView.id,
|
||||
mainGroupByFieldMetadataId: newMainGroupByFieldMetadataId,
|
||||
flatFieldMetadataMaps,
|
||||
});
|
||||
|
||||
return sideEffectResult;
|
||||
};
|
||||
+7
-2
@@ -7,6 +7,7 @@ import { FindManyOptions, FindOneOptions, Repository } from 'typeorm';
|
||||
|
||||
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 { AllFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/all-flat-entity-maps.type';
|
||||
import { computeFlatEntityMapsFromTo } from 'src/engine/metadata-modules/flat-entity/utils/compute-flat-entity-maps-from-to.util';
|
||||
import { findManyFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/find-many-flat-entity-by-id-in-flat-entity-maps-or-throw.util';
|
||||
import { FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
@@ -385,6 +386,7 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
|
||||
objectMetadata: flatObjectMetadataToCreate,
|
||||
workspaceId,
|
||||
workspaceCustomApplicationId: workspaceCustomFlatApplication.id,
|
||||
flatFieldMetadataMaps: flatFieldMetadataMapsFromTo.to,
|
||||
});
|
||||
|
||||
const flatDefaultViewFieldsToCreate =
|
||||
@@ -476,10 +478,12 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
|
||||
objectMetadata,
|
||||
workspaceId,
|
||||
workspaceCustomApplicationId,
|
||||
flatFieldMetadataMaps,
|
||||
}: {
|
||||
workspaceCustomApplicationId: string;
|
||||
objectMetadata: FlatObjectMetadata;
|
||||
workspaceId: string;
|
||||
flatFieldMetadataMaps: AllFlatEntityMaps['flatFieldMetadataMaps'];
|
||||
}) {
|
||||
const defaultViewInput = {
|
||||
objectMetadataId: objectMetadata.id,
|
||||
@@ -490,13 +494,14 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
|
||||
workspaceId: workspaceId,
|
||||
};
|
||||
|
||||
const flatViewFromCreateInput = fromCreateViewInputToFlatViewToCreate({
|
||||
const { flatViewToCreate } = fromCreateViewInputToFlatViewToCreate({
|
||||
createViewInput: defaultViewInput,
|
||||
workspaceId,
|
||||
workspaceCustomApplicationId,
|
||||
flatFieldMetadataMaps,
|
||||
});
|
||||
|
||||
return flatViewFromCreateInput;
|
||||
return flatViewToCreate;
|
||||
}
|
||||
|
||||
private async computeFlatViewFieldsToCreate({
|
||||
|
||||
+6
-14
@@ -28,7 +28,6 @@ export const buildDefaultFlatFieldMetadatasForCustomObject = ({
|
||||
type: FieldMetadataType.UUID,
|
||||
id: idFieldId,
|
||||
viewFieldIds: [],
|
||||
viewGroupIds: [],
|
||||
kanbanAggregateOperationViewIds: [],
|
||||
calendarViewIds: [],
|
||||
mainGroupByFieldMetadataViewIds: [],
|
||||
@@ -66,10 +65,9 @@ export const buildDefaultFlatFieldMetadatasForCustomObject = ({
|
||||
type: FieldMetadataType.TEXT,
|
||||
id: nameFieldId,
|
||||
viewFieldIds: [],
|
||||
viewGroupIds: [],
|
||||
mainGroupByFieldMetadataViewIds: [],
|
||||
kanbanAggregateOperationViewIds: [],
|
||||
calendarViewIds: [],
|
||||
mainGroupByFieldMetadataViewIds: [],
|
||||
isLabelSyncedWithName: false,
|
||||
isUnique: false,
|
||||
objectMetadataId,
|
||||
@@ -104,10 +102,9 @@ export const buildDefaultFlatFieldMetadatasForCustomObject = ({
|
||||
type: FieldMetadataType.DATE_TIME,
|
||||
id: createdAtFieldId,
|
||||
viewFieldIds: [],
|
||||
viewGroupIds: [],
|
||||
mainGroupByFieldMetadataViewIds: [],
|
||||
kanbanAggregateOperationViewIds: [],
|
||||
calendarViewIds: [],
|
||||
mainGroupByFieldMetadataViewIds: [],
|
||||
isLabelSyncedWithName: false,
|
||||
isUnique: false,
|
||||
objectMetadataId,
|
||||
@@ -142,10 +139,9 @@ export const buildDefaultFlatFieldMetadatasForCustomObject = ({
|
||||
type: FieldMetadataType.DATE_TIME,
|
||||
id: updatedAtFieldId,
|
||||
viewFieldIds: [],
|
||||
viewGroupIds: [],
|
||||
mainGroupByFieldMetadataViewIds: [],
|
||||
kanbanAggregateOperationViewIds: [],
|
||||
calendarViewIds: [],
|
||||
mainGroupByFieldMetadataViewIds: [],
|
||||
isLabelSyncedWithName: false,
|
||||
isUnique: false,
|
||||
objectMetadataId,
|
||||
@@ -180,10 +176,9 @@ export const buildDefaultFlatFieldMetadatasForCustomObject = ({
|
||||
type: FieldMetadataType.DATE_TIME,
|
||||
id: deletedAtFieldId,
|
||||
viewFieldIds: [],
|
||||
viewGroupIds: [],
|
||||
mainGroupByFieldMetadataViewIds: [],
|
||||
kanbanAggregateOperationViewIds: [],
|
||||
calendarViewIds: [],
|
||||
mainGroupByFieldMetadataViewIds: [],
|
||||
isLabelSyncedWithName: false,
|
||||
isUnique: false,
|
||||
objectMetadataId,
|
||||
@@ -218,10 +213,9 @@ export const buildDefaultFlatFieldMetadatasForCustomObject = ({
|
||||
type: FieldMetadataType.ACTOR,
|
||||
id: createdByFieldId,
|
||||
viewFieldIds: [],
|
||||
viewGroupIds: [],
|
||||
mainGroupByFieldMetadataViewIds: [],
|
||||
kanbanAggregateOperationViewIds: [],
|
||||
calendarViewIds: [],
|
||||
mainGroupByFieldMetadataViewIds: [],
|
||||
isLabelSyncedWithName: false,
|
||||
isUnique: false,
|
||||
objectMetadataId,
|
||||
@@ -255,10 +249,9 @@ export const buildDefaultFlatFieldMetadatasForCustomObject = ({
|
||||
type: FieldMetadataType.POSITION,
|
||||
id: positionFieldId,
|
||||
viewFieldIds: [],
|
||||
viewGroupIds: [],
|
||||
mainGroupByFieldMetadataViewIds: [],
|
||||
kanbanAggregateOperationViewIds: [],
|
||||
calendarViewIds: [],
|
||||
mainGroupByFieldMetadataViewIds: [],
|
||||
isLabelSyncedWithName: false,
|
||||
isUnique: false,
|
||||
objectMetadataId,
|
||||
@@ -293,7 +286,6 @@ export const buildDefaultFlatFieldMetadatasForCustomObject = ({
|
||||
type: FieldMetadataType.TS_VECTOR,
|
||||
mainGroupByFieldMetadataViewIds: [],
|
||||
viewFieldIds: [],
|
||||
viewGroupIds: [],
|
||||
kanbanAggregateOperationViewIds: [],
|
||||
calendarViewIds: [],
|
||||
id: searchVectorFieldId,
|
||||
|
||||
-2
@@ -65,7 +65,6 @@ const generateSourceFlatFieldMetadata = ({
|
||||
kanbanAggregateOperationViewIds: [],
|
||||
viewFilterIds: [],
|
||||
viewFieldIds: [],
|
||||
viewGroupIds: [],
|
||||
createdAt,
|
||||
updatedAt: createdAt,
|
||||
defaultValue: null,
|
||||
@@ -133,7 +132,6 @@ const generateTargetFlatFieldMetadata = ({
|
||||
viewFieldIds: [],
|
||||
kanbanAggregateOperationViewIds: [],
|
||||
viewFilterIds: [],
|
||||
viewGroupIds: [],
|
||||
id: sourceFlatFieldMetadata.relationTargetFieldMetadataId,
|
||||
name: sourceFlatObjectMetadata.nameSingular,
|
||||
label: sourceFlatObjectMetadata.labelSingular,
|
||||
|
||||
-4
@@ -17,10 +17,6 @@ export class CreateViewGroupInput {
|
||||
@Field(() => UUIDScalarType, { nullable: true })
|
||||
id?: string;
|
||||
|
||||
@IsUUID()
|
||||
@Field(() => UUIDScalarType, { nullable: false })
|
||||
fieldMetadataId: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsBoolean()
|
||||
@Field({ nullable: true, defaultValue: true })
|
||||
|
||||
@@ -9,9 +9,6 @@ export class ViewGroupDTO {
|
||||
@IDField(() => UUIDScalarType)
|
||||
id: string;
|
||||
|
||||
@Field(() => UUIDScalarType, { nullable: false })
|
||||
fieldMetadataId: string;
|
||||
|
||||
@Field({ nullable: false, defaultValue: true })
|
||||
isVisible: boolean;
|
||||
|
||||
|
||||
+1
@@ -19,6 +19,7 @@ export enum ViewGroupExceptionCode {
|
||||
VIEW_GROUP_NOT_FOUND = 'VIEW_GROUP_NOT_FOUND',
|
||||
INVALID_VIEW_GROUP_DATA = 'INVALID_VIEW_GROUP_DATA',
|
||||
VIEW_NOT_FOUND = 'VIEW_NOT_FOUND',
|
||||
MISSING_MAIN_GROUP_BY_FIELD_METADATA_ID = 'MISSING_MAIN_GROUP_BY_FIELD_METADATA_ID',
|
||||
}
|
||||
|
||||
export enum ViewGroupExceptionMessageKey {
|
||||
|
||||
+2
-2
@@ -11,14 +11,14 @@ import { DestroyViewGroupInput } from 'src/engine/metadata-modules/view-group/dt
|
||||
import { UpdateViewGroupInput } from 'src/engine/metadata-modules/view-group/dtos/inputs/update-view-group.input';
|
||||
import { ViewGroupDTO } from 'src/engine/metadata-modules/view-group/dtos/view-group.dto';
|
||||
import { ViewGroupService } from 'src/engine/metadata-modules/view-group/services/view-group.service';
|
||||
import { ViewGroupGraphqlApiExceptionFilter } from 'src/engine/metadata-modules/view-group/utils/view-group-graphql-api-exception.filter';
|
||||
import { CreateViewGroupPermissionGuard } from 'src/engine/metadata-modules/view-permissions/guards/create-view-group-permission.guard';
|
||||
import { DeleteViewGroupPermissionGuard } from 'src/engine/metadata-modules/view-permissions/guards/delete-view-group-permission.guard';
|
||||
import { DestroyViewGroupPermissionGuard } from 'src/engine/metadata-modules/view-permissions/guards/destroy-view-group-permission.guard';
|
||||
import { UpdateViewGroupPermissionGuard } from 'src/engine/metadata-modules/view-permissions/guards/update-view-group-permission.guard';
|
||||
import { ViewGraphqlApiExceptionFilter } from 'src/engine/metadata-modules/view/utils/view-graphql-api-exception.filter';
|
||||
|
||||
@Resolver(() => ViewGroupDTO)
|
||||
@UseFilters(ViewGraphqlApiExceptionFilter)
|
||||
@UseFilters(ViewGroupGraphqlApiExceptionFilter)
|
||||
@UseGuards(WorkspaceAuthGuard)
|
||||
export class ViewGroupResolver {
|
||||
constructor(private readonly viewGroupService: ViewGroupService) {}
|
||||
|
||||
+16
-3
@@ -92,12 +92,25 @@ export class ViewGroupService {
|
||||
);
|
||||
|
||||
const flatViewGroupsToCreate = createViewGroupInputs.map(
|
||||
(createViewGroupInput) =>
|
||||
fromCreateViewGroupInputToFlatViewGroupToCreate({
|
||||
(createViewGroupInput) => {
|
||||
const mainGroupByFieldMetadataId =
|
||||
flatViewMaps.byId[createViewGroupInput.viewId]
|
||||
?.mainGroupByFieldMetadataId;
|
||||
|
||||
if (!isDefined(mainGroupByFieldMetadataId)) {
|
||||
throw new ViewGroupException(
|
||||
'The associated view is not a grouped view: mainGroupByFieldMetadataId is missing.',
|
||||
ViewGroupExceptionCode.MISSING_MAIN_GROUP_BY_FIELD_METADATA_ID,
|
||||
);
|
||||
}
|
||||
|
||||
return fromCreateViewGroupInputToFlatViewGroupToCreate({
|
||||
createViewGroupInput,
|
||||
workspaceId,
|
||||
workspaceCustomApplicationId: workspaceCustomFlatApplication.id,
|
||||
}),
|
||||
mainGroupByFieldMetadataId,
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
const validateAndBuildResult =
|
||||
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
import { type I18n } from '@lingui/core';
|
||||
|
||||
import { UserInputError } from 'src/engine/core-modules/graphql/utils/graphql-errors.util';
|
||||
import {
|
||||
ViewGroupException,
|
||||
ViewGroupExceptionCode,
|
||||
} from 'src/engine/metadata-modules/view-group/exceptions/view-group.exception';
|
||||
import { viewGraphqlApiExceptionHandler } from 'src/engine/metadata-modules/view/utils/view-graphql-api-exception-handler.util';
|
||||
|
||||
export const viewGroupGraphqlApiExceptionHandler = (
|
||||
error: Error,
|
||||
i18n: I18n,
|
||||
) => {
|
||||
if (error instanceof ViewGroupException) {
|
||||
if (
|
||||
error.code ===
|
||||
ViewGroupExceptionCode.MISSING_MAIN_GROUP_BY_FIELD_METADATA_ID
|
||||
) {
|
||||
throw new UserInputError(error.message, {
|
||||
userFriendlyMessage: error.userFriendlyMessage,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return viewGraphqlApiExceptionHandler(error, i18n);
|
||||
};
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
import {
|
||||
Catch,
|
||||
type ExceptionFilter,
|
||||
type ExecutionContext,
|
||||
Injectable,
|
||||
} from '@nestjs/common';
|
||||
import { GqlExecutionContext } from '@nestjs/graphql';
|
||||
|
||||
import { SOURCE_LOCALE } from 'twenty-shared/translations';
|
||||
|
||||
import { I18nService } from 'src/engine/core-modules/i18n/i18n.service';
|
||||
import { ViewGroupException } from 'src/engine/metadata-modules/view-group/exceptions/view-group.exception';
|
||||
import { viewGroupGraphqlApiExceptionHandler } from 'src/engine/metadata-modules/view-group/utils/view-group-graphql-api-exception-handler.util';
|
||||
import { WorkspaceMigrationBuilderExceptionV2 } from 'src/engine/workspace-manager/workspace-migration-v2/exceptions/workspace-migration-builder-exception-v2';
|
||||
|
||||
@Catch(ViewGroupException, WorkspaceMigrationBuilderExceptionV2)
|
||||
@Injectable()
|
||||
export class ViewGroupGraphqlApiExceptionFilter implements ExceptionFilter {
|
||||
constructor(private readonly i18nService: I18nService) {}
|
||||
|
||||
catch(
|
||||
exception: ViewGroupException | WorkspaceMigrationBuilderExceptionV2,
|
||||
host: ExecutionContext,
|
||||
) {
|
||||
const gqlContext = GqlExecutionContext.create(host);
|
||||
const ctx = gqlContext.getContext();
|
||||
const userLocale = ctx.req?.locale ?? SOURCE_LOCALE;
|
||||
const i18n = this.i18nService.getI18nInstance(userLocale);
|
||||
|
||||
return viewGroupGraphqlApiExceptionHandler(exception, i18n);
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
|
||||
import { ApplicationModule } from 'src/engine/core-modules/application/application.module';
|
||||
import { FeatureFlagModule } from 'src/engine/core-modules/feature-flag/feature-flag.module';
|
||||
import { I18nModule } from 'src/engine/core-modules/i18n/i18n.module';
|
||||
import { WorkspaceManyOrAllFlatEntityMapsCacheModule } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.module';
|
||||
import { PermissionsModule } from 'src/engine/metadata-modules/permissions/permissions.module';
|
||||
import { ViewGroupController } from 'src/engine/metadata-modules/view-group/controllers/view-group.controller';
|
||||
@@ -20,6 +21,7 @@ import { WorkspaceMigrationV2Module } from 'src/engine/workspace-manager/workspa
|
||||
WorkspaceCacheStorageModule,
|
||||
ApplicationModule,
|
||||
FeatureFlagModule,
|
||||
I18nModule,
|
||||
PermissionsModule,
|
||||
WorkspaceMigrationV2Module,
|
||||
WorkspaceManyOrAllFlatEntityMapsCacheModule,
|
||||
|
||||
+1
-1
@@ -91,5 +91,5 @@ export class UpdateViewInput {
|
||||
@IsOptional()
|
||||
@IsUUID()
|
||||
@Field(() => UUIDScalarType, { nullable: true })
|
||||
mainGroupByFieldMetadataId?: string;
|
||||
mainGroupByFieldMetadataId?: string | null;
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { APP_LOCALES, SOURCE_LOCALE } from 'twenty-shared/translations';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { IsNull, Repository } from 'typeorm';
|
||||
|
||||
import { ApplicationService } from 'src/engine/core-modules/application/application.service';
|
||||
@@ -56,6 +56,7 @@ export class ViewService {
|
||||
flatObjectMetadataMaps,
|
||||
flatViewMaps: existingFlatViewMaps,
|
||||
flatFieldMetadataMaps: existingFlatFieldMetadataMaps,
|
||||
flatViewGroupMaps: existingFlatViewGroupMaps,
|
||||
} = await this.flatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps(
|
||||
{
|
||||
workspaceId,
|
||||
@@ -63,16 +64,19 @@ export class ViewService {
|
||||
'flatObjectMetadataMaps',
|
||||
'flatViewMaps',
|
||||
'flatFieldMetadataMaps',
|
||||
'flatViewGroupMaps',
|
||||
],
|
||||
},
|
||||
);
|
||||
|
||||
const flatViewFromCreateInput = fromCreateViewInputToFlatViewToCreate({
|
||||
createViewInput,
|
||||
workspaceId,
|
||||
createdByUserWorkspaceId,
|
||||
workspaceCustomApplicationId: workspaceCustomFlatApplication.id,
|
||||
});
|
||||
const { flatViewToCreate, flatViewGroupsToCreate } =
|
||||
fromCreateViewInputToFlatViewToCreate({
|
||||
createViewInput,
|
||||
workspaceId,
|
||||
createdByUserWorkspaceId,
|
||||
workspaceCustomApplicationId: workspaceCustomFlatApplication.id,
|
||||
flatFieldMetadataMaps: existingFlatFieldMetadataMaps,
|
||||
});
|
||||
|
||||
const validateAndBuildResult =
|
||||
await this.workspaceMigrationValidateBuildAndRunService.validateBuildAndRunWorkspaceMigration(
|
||||
@@ -80,7 +84,13 @@ export class ViewService {
|
||||
fromToAllFlatEntityMaps: {
|
||||
flatViewMaps: computeFlatEntityMapsFromTo({
|
||||
flatEntityMaps: existingFlatViewMaps,
|
||||
flatEntityToCreate: [flatViewFromCreateInput],
|
||||
flatEntityToCreate: [flatViewToCreate],
|
||||
flatEntityToDelete: [],
|
||||
flatEntityToUpdate: [],
|
||||
}),
|
||||
flatViewGroupMaps: computeFlatEntityMapsFromTo({
|
||||
flatEntityMaps: existingFlatViewGroupMaps,
|
||||
flatEntityToCreate: flatViewGroupsToCreate,
|
||||
flatEntityToDelete: [],
|
||||
flatEntityToUpdate: [],
|
||||
}),
|
||||
@@ -112,7 +122,7 @@ export class ViewService {
|
||||
);
|
||||
|
||||
return findFlatEntityByIdInFlatEntityMapsOrThrow({
|
||||
flatEntityId: flatViewFromCreateInput.id,
|
||||
flatEntityId: flatViewToCreate.id,
|
||||
flatEntityMaps: recomputedExistingFlatViewMaps,
|
||||
});
|
||||
}
|
||||
@@ -129,35 +139,27 @@ export class ViewService {
|
||||
const {
|
||||
flatViewMaps: existingFlatViewMaps,
|
||||
flatFieldMetadataMaps: existingFlatFieldMetadataMaps,
|
||||
} =
|
||||
await this.flatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps(
|
||||
{
|
||||
workspaceId,
|
||||
flatMapsKeys: ['flatViewMaps', 'flatFieldMetadataMaps'],
|
||||
},
|
||||
);
|
||||
flatViewGroupMaps: existingFlatViewGroupMaps,
|
||||
} = await this.flatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps(
|
||||
{
|
||||
workspaceId,
|
||||
flatMapsKeys: [
|
||||
'flatViewMaps',
|
||||
'flatFieldMetadataMaps',
|
||||
'flatViewGroupMaps',
|
||||
],
|
||||
},
|
||||
);
|
||||
|
||||
const flatViewFromUpdateInput =
|
||||
const { flatViewToUpdate, flatViewGroupsToDelete, flatViewGroupsToCreate } =
|
||||
fromUpdateViewInputToFlatViewToUpdateOrThrow({
|
||||
updateViewInput,
|
||||
flatViewMaps: existingFlatViewMaps,
|
||||
flatViewGroupMaps: existingFlatViewGroupMaps,
|
||||
flatFieldMetadataMaps: existingFlatFieldMetadataMaps,
|
||||
userWorkspaceId,
|
||||
});
|
||||
|
||||
const existingFlatView = existingFlatViewMaps.byId[updateViewInput.id];
|
||||
|
||||
// If changing visibility from WORKSPACE to UNLISTED, ensure createdByUserWorkspaceId is set
|
||||
// This prevents the view from disappearing for the user making the change
|
||||
if (
|
||||
isDefined(existingFlatView) &&
|
||||
isDefined(updateViewInput.visibility) &&
|
||||
updateViewInput.visibility === 'UNLISTED' &&
|
||||
existingFlatView.visibility === 'WORKSPACE' &&
|
||||
isDefined(userWorkspaceId)
|
||||
) {
|
||||
// Re-allocate the view to the current user
|
||||
flatViewFromUpdateInput.createdByUserWorkspaceId = userWorkspaceId;
|
||||
}
|
||||
|
||||
const validateAndBuildResult =
|
||||
await this.workspaceMigrationValidateBuildAndRunService.validateBuildAndRunWorkspaceMigration(
|
||||
{
|
||||
@@ -166,7 +168,13 @@ export class ViewService {
|
||||
flatEntityMaps: existingFlatViewMaps,
|
||||
flatEntityToCreate: [],
|
||||
flatEntityToDelete: [],
|
||||
flatEntityToUpdate: [flatViewFromUpdateInput],
|
||||
flatEntityToUpdate: [flatViewToUpdate],
|
||||
}),
|
||||
flatViewGroupMaps: computeFlatEntityMapsFromTo({
|
||||
flatEntityMaps: existingFlatViewGroupMaps,
|
||||
flatEntityToCreate: flatViewGroupsToCreate,
|
||||
flatEntityToDelete: flatViewGroupsToDelete,
|
||||
flatEntityToUpdate: [],
|
||||
}),
|
||||
},
|
||||
dependencyAllFlatEntityMaps: {
|
||||
@@ -174,6 +182,9 @@ export class ViewService {
|
||||
},
|
||||
buildOptions: {
|
||||
isSystemBuild: false,
|
||||
inferDeletionFromMissingEntities: {
|
||||
viewGroup: true,
|
||||
},
|
||||
},
|
||||
workspaceId,
|
||||
},
|
||||
@@ -385,7 +396,6 @@ export class ViewService {
|
||||
if (view.visibility === ViewVisibility.WORKSPACE) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (
|
||||
view.visibility === ViewVisibility.UNLISTED &&
|
||||
isDefined(userWorkspaceId) &&
|
||||
@@ -425,7 +435,6 @@ export class ViewService {
|
||||
if (view.visibility === ViewVisibility.WORKSPACE) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (
|
||||
view.visibility === ViewVisibility.UNLISTED &&
|
||||
isDefined(userWorkspaceId) &&
|
||||
|
||||
+5
@@ -3,6 +3,7 @@ import { assertUnreachable } from 'twenty-shared/utils';
|
||||
|
||||
import {
|
||||
ForbiddenError,
|
||||
InternalServerError,
|
||||
NotFoundError,
|
||||
UserInputError,
|
||||
} from 'src/engine/core-modules/graphql/utils/graphql-errors.util';
|
||||
@@ -118,6 +119,10 @@ export const viewGraphqlApiExceptionHandler = (error: Error, i18n: I18n) => {
|
||||
throw new UserInputError(error.message, {
|
||||
userFriendlyMessage: error.userFriendlyMessage,
|
||||
});
|
||||
case ViewGroupExceptionCode.MISSING_MAIN_GROUP_BY_FIELD_METADATA_ID:
|
||||
throw new InternalServerError(error.message, {
|
||||
userFriendlyMessage: error.userFriendlyMessage,
|
||||
});
|
||||
default: {
|
||||
return assertUnreachable(error.code);
|
||||
}
|
||||
|
||||
-1
@@ -148,7 +148,6 @@ describe('WorkspaceEntityManager', () => {
|
||||
workspaceId: 'test-workspace-id',
|
||||
viewFieldIds: [],
|
||||
viewFilterIds: [],
|
||||
viewGroupIds: [],
|
||||
kanbanAggregateOperationViewIds: [],
|
||||
calendarViewIds: [],
|
||||
mainGroupByFieldMetadataViewIds: [],
|
||||
|
||||
-1
@@ -91,7 +91,6 @@ describe('WorkspaceRepository', () => {
|
||||
relationTargetObjectMetadataId: null,
|
||||
calendarViewIds: [],
|
||||
viewFilterIds: [],
|
||||
viewGroupIds: [],
|
||||
kanbanAggregateOperationViewIds: [],
|
||||
viewFieldIds: [],
|
||||
mainGroupByFieldMetadataViewIds: [],
|
||||
|
||||
-1
@@ -99,7 +99,6 @@ export const createStandardFieldFlatMetadata = <
|
||||
morphId: null,
|
||||
viewFieldIds: [],
|
||||
viewFilterIds: [],
|
||||
viewGroupIds: [],
|
||||
kanbanAggregateOperationViewIds: [],
|
||||
calendarViewIds: [],
|
||||
mainGroupByFieldMetadataViewIds: [],
|
||||
|
||||
-1
@@ -108,7 +108,6 @@ export const createStandardRelationFieldFlatMetadata = <
|
||||
morphId: null,
|
||||
viewFieldIds: [],
|
||||
viewFilterIds: [],
|
||||
viewGroupIds: [],
|
||||
kanbanAggregateOperationViewIds: [],
|
||||
calendarViewIds: [],
|
||||
mainGroupByFieldMetadataViewIds: [],
|
||||
|
||||
-4
@@ -36,7 +36,6 @@ exports[`flatEntityDeletedCreatedUpdatedMatrixDispatcher It should detect a crea
|
||||
"updatedAt": 2024-01-01T00:00:00.000Z,
|
||||
"viewFieldIds": [],
|
||||
"viewFilterIds": [],
|
||||
"viewGroupIds": [],
|
||||
"workspaceId": "workspace-id-1",
|
||||
},
|
||||
},
|
||||
@@ -101,7 +100,6 @@ exports[`flatEntityDeletedCreatedUpdatedMatrixDispatcher It should detect a dele
|
||||
"updatedAt": 2024-01-01T00:00:00.000Z,
|
||||
"viewFieldIds": [],
|
||||
"viewFilterIds": [],
|
||||
"viewGroupIds": [],
|
||||
"workspaceId": "workspace-id-1",
|
||||
},
|
||||
},
|
||||
@@ -184,7 +182,6 @@ exports[`flatEntityDeletedCreatedUpdatedMatrixDispatcher It should detect create
|
||||
"updatedAt": 2024-01-01T00:00:00.000Z,
|
||||
"viewFieldIds": [],
|
||||
"viewFilterIds": [],
|
||||
"viewGroupIds": [],
|
||||
"workspaceId": "workspace-id-1",
|
||||
},
|
||||
},
|
||||
@@ -231,7 +228,6 @@ exports[`flatEntityDeletedCreatedUpdatedMatrixDispatcher It should detect create
|
||||
"updatedAt": 2024-01-01T00:00:00.000Z,
|
||||
"viewFieldIds": [],
|
||||
"viewFilterIds": [],
|
||||
"viewGroupIds": [],
|
||||
"workspaceId": "workspace-id-1",
|
||||
},
|
||||
},
|
||||
|
||||
+16
-30
@@ -20,7 +20,6 @@ export class FlatViewGroupValidatorService {
|
||||
optimisticFlatEntityMapsAndRelatedFlatEntityMaps: {
|
||||
flatViewGroupMaps: optimisticFlatViewGroupMaps,
|
||||
flatViewMaps,
|
||||
flatFieldMetadataMaps,
|
||||
},
|
||||
}: FlatEntityUpdateValidationArgs<
|
||||
typeof ALL_METADATA_NAME.viewGroup
|
||||
@@ -53,10 +52,17 @@ export class FlatViewGroupValidatorService {
|
||||
}),
|
||||
};
|
||||
|
||||
if (!isDefined(updatedFlatViewGroup.fieldValue)) {
|
||||
validationResult.errors.push({
|
||||
code: ViewExceptionCode.INVALID_VIEW_DATA,
|
||||
message: t`Field value is required`,
|
||||
userFriendlyMessage: msg`Field value is required`,
|
||||
});
|
||||
}
|
||||
|
||||
validationResult.flatEntityMinimalInformation = {
|
||||
id: updatedFlatViewGroup.id,
|
||||
viewId: updatedFlatViewGroup.viewId,
|
||||
fieldMetadataId: updatedFlatViewGroup.fieldMetadataId,
|
||||
};
|
||||
|
||||
const flatView = findFlatEntityByIdInFlatEntityMaps({
|
||||
@@ -72,19 +78,6 @@ export class FlatViewGroupValidatorService {
|
||||
});
|
||||
}
|
||||
|
||||
const flatFieldMetadata = findFlatEntityByIdInFlatEntityMaps({
|
||||
flatEntityId: updatedFlatViewGroup.fieldMetadataId,
|
||||
flatEntityMaps: flatFieldMetadataMaps,
|
||||
});
|
||||
|
||||
if (!isDefined(flatFieldMetadata)) {
|
||||
validationResult.errors.push({
|
||||
code: ViewExceptionCode.INVALID_VIEW_DATA,
|
||||
message: t`View group to update parent field not found`,
|
||||
userFriendlyMessage: msg`View group to update parent field not found`,
|
||||
});
|
||||
}
|
||||
|
||||
return validationResult;
|
||||
}
|
||||
|
||||
@@ -122,7 +115,6 @@ export class FlatViewGroupValidatorService {
|
||||
flatEntityToValidate: flatViewGroupToValidate,
|
||||
optimisticFlatEntityMapsAndRelatedFlatEntityMaps: {
|
||||
flatViewGroupMaps: optimisticFlatViewGroupMaps,
|
||||
flatFieldMetadataMaps,
|
||||
flatViewMaps,
|
||||
},
|
||||
}: FlatEntityValidationArgs<
|
||||
@@ -134,7 +126,6 @@ export class FlatViewGroupValidatorService {
|
||||
flatEntityMinimalInformation: {
|
||||
id: flatViewGroupToValidate.id,
|
||||
viewId: flatViewGroupToValidate.viewId,
|
||||
fieldMetadataId: flatViewGroupToValidate.fieldMetadataId,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -151,19 +142,6 @@ export class FlatViewGroupValidatorService {
|
||||
});
|
||||
}
|
||||
|
||||
const flatFieldMetadata = findFlatEntityByIdInFlatEntityMaps({
|
||||
flatEntityId: flatViewGroupToValidate.fieldMetadataId,
|
||||
flatEntityMaps: flatFieldMetadataMaps,
|
||||
});
|
||||
|
||||
if (!isDefined(flatFieldMetadata)) {
|
||||
validationResult.errors.push({
|
||||
code: ViewExceptionCode.INVALID_VIEW_DATA,
|
||||
message: t`Field metadata not found`,
|
||||
userFriendlyMessage: msg`Field metadata not found`,
|
||||
});
|
||||
}
|
||||
|
||||
const flatView = flatViewMaps.byId[flatViewGroupToValidate.viewId];
|
||||
|
||||
if (!isDefined(flatView)) {
|
||||
@@ -176,6 +154,14 @@ export class FlatViewGroupValidatorService {
|
||||
return validationResult;
|
||||
}
|
||||
|
||||
if (!isDefined(flatViewGroupToValidate.fieldValue)) {
|
||||
validationResult.errors.push({
|
||||
code: ViewExceptionCode.INVALID_VIEW_DATA,
|
||||
message: t`Field value is required`,
|
||||
userFriendlyMessage: msg`Field value is required`,
|
||||
});
|
||||
}
|
||||
|
||||
return validationResult;
|
||||
}
|
||||
}
|
||||
|
||||
+111
@@ -1,14 +1,17 @@
|
||||
import { msg, t } from '@lingui/core/macro';
|
||||
import { type ALL_METADATA_NAME } from 'twenty-shared/metadata';
|
||||
import { FieldMetadataType } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { findFlatEntityByIdInFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps.util';
|
||||
import { type FlatView } from 'src/engine/metadata-modules/flat-view/types/flat-view.type';
|
||||
import { ViewType } from 'src/engine/metadata-modules/view/enums/view-type.enum';
|
||||
import { ViewExceptionCode } from 'src/engine/metadata-modules/view/exceptions/view.exception';
|
||||
import { findFlatEntityPropertyUpdate } from 'src/engine/workspace-manager/workspace-migration-v2/utils/find-flat-entity-property-update.util';
|
||||
import { type FailedFlatEntityValidation } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/types/failed-flat-entity-validation.type';
|
||||
import { type FlatEntityUpdateValidationArgs } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/flat-entity-update-validation-args.type';
|
||||
import { type FlatEntityValidationArgs } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/flat-entity-validation-args.type';
|
||||
import { fromFlatEntityPropertiesUpdatesToPartialFlatEntity } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/utils/from-flat-entity-properties-updates-to-partial-flat-entity';
|
||||
|
||||
export class FlatViewValidatorService {
|
||||
constructor() {}
|
||||
@@ -39,8 +42,19 @@ export class FlatViewValidatorService {
|
||||
message: t`View not found`,
|
||||
userFriendlyMessage: msg`View not found`,
|
||||
});
|
||||
|
||||
return validationResult;
|
||||
}
|
||||
|
||||
const partialUpdates = fromFlatEntityPropertiesUpdatesToPartialFlatEntity({
|
||||
updates: flatEntityUpdates,
|
||||
});
|
||||
|
||||
const updatedFlatView: FlatView = {
|
||||
...existingFlatView,
|
||||
...partialUpdates,
|
||||
};
|
||||
|
||||
const kanbanAggregateOperationFieldMetadataIdUpdate =
|
||||
findFlatEntityPropertyUpdate({
|
||||
property: 'kanbanAggregateOperationFieldMetadataId',
|
||||
@@ -64,6 +78,70 @@ export class FlatViewValidatorService {
|
||||
});
|
||||
}
|
||||
|
||||
const viewBecomesKanban =
|
||||
updatedFlatView.type === ViewType.KANBAN &&
|
||||
existingFlatView.type !== ViewType.KANBAN;
|
||||
|
||||
if (viewBecomesKanban) {
|
||||
if (!isDefined(updatedFlatView.mainGroupByFieldMetadataId)) {
|
||||
validationResult.errors.push({
|
||||
code: ViewExceptionCode.INVALID_VIEW_DATA,
|
||||
message: t`Kanban view must have a main group by field`,
|
||||
userFriendlyMessage: msg`Kanban view must have a main group by field`,
|
||||
});
|
||||
|
||||
return validationResult;
|
||||
}
|
||||
|
||||
const mainGroupByFieldMetadata = findFlatEntityByIdInFlatEntityMaps({
|
||||
flatEntityId: updatedFlatView.mainGroupByFieldMetadataId,
|
||||
flatEntityMaps: flatFieldMetadataMaps,
|
||||
});
|
||||
|
||||
if (!isDefined(mainGroupByFieldMetadata)) {
|
||||
validationResult.errors.push({
|
||||
code: ViewExceptionCode.INVALID_VIEW_DATA,
|
||||
message: t`Kanban main group by field metadata not found`,
|
||||
userFriendlyMessage: msg`Kanban main group by field metadata not found`,
|
||||
});
|
||||
} else if (mainGroupByFieldMetadata.type !== FieldMetadataType.SELECT) {
|
||||
validationResult.errors.push({
|
||||
code: ViewExceptionCode.INVALID_VIEW_DATA,
|
||||
message: t`Kanban main group by field must be a SELECT field`,
|
||||
userFriendlyMessage: msg`Kanban main group by field must be a select field`,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const updatedMainGroupByFieldMetadataId =
|
||||
updatedFlatView.mainGroupByFieldMetadataId;
|
||||
|
||||
const mainGroupByFieldMetadataIsAddedOrUpdated =
|
||||
isDefined(updatedMainGroupByFieldMetadataId) &&
|
||||
existingFlatView.mainGroupByFieldMetadataId !==
|
||||
updatedMainGroupByFieldMetadataId;
|
||||
|
||||
if (mainGroupByFieldMetadataIsAddedOrUpdated && !viewBecomesKanban) {
|
||||
const mainGroupByFieldMetadata = findFlatEntityByIdInFlatEntityMaps({
|
||||
flatEntityId: updatedMainGroupByFieldMetadataId,
|
||||
flatEntityMaps: flatFieldMetadataMaps,
|
||||
});
|
||||
|
||||
if (!isDefined(mainGroupByFieldMetadata)) {
|
||||
validationResult.errors.push({
|
||||
code: ViewExceptionCode.INVALID_VIEW_DATA,
|
||||
message: t`Kanban main group by field metadata not found`,
|
||||
userFriendlyMessage: msg`Kanban main group by field metadata not found`,
|
||||
});
|
||||
} else if (mainGroupByFieldMetadata.type !== FieldMetadataType.SELECT) {
|
||||
validationResult.errors.push({
|
||||
code: ViewExceptionCode.INVALID_VIEW_DATA,
|
||||
message: t`Kanban main group by field must be a SELECT field`,
|
||||
userFriendlyMessage: msg`Kanban main group by field must be a select field`,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return validationResult;
|
||||
}
|
||||
|
||||
@@ -150,6 +228,39 @@ export class FlatViewValidatorService {
|
||||
});
|
||||
}
|
||||
|
||||
const isKanban = flatViewToValidate.type === ViewType.KANBAN;
|
||||
|
||||
if (isKanban) {
|
||||
if (!isDefined(flatViewToValidate.mainGroupByFieldMetadataId)) {
|
||||
validationResult.errors.push({
|
||||
code: ViewExceptionCode.INVALID_VIEW_DATA,
|
||||
message: t`Kanban view must have a main group by field`,
|
||||
userFriendlyMessage: msg`Kanban view must have a main group by field`,
|
||||
});
|
||||
|
||||
return validationResult;
|
||||
}
|
||||
|
||||
const mainGroupByFieldMetadata = findFlatEntityByIdInFlatEntityMaps({
|
||||
flatEntityId: flatViewToValidate.mainGroupByFieldMetadataId,
|
||||
flatEntityMaps: flatFieldMetadataMaps,
|
||||
});
|
||||
|
||||
if (!isDefined(mainGroupByFieldMetadata)) {
|
||||
validationResult.errors.push({
|
||||
code: ViewExceptionCode.INVALID_VIEW_DATA,
|
||||
message: t`Kanban main group by field metadata not found`,
|
||||
userFriendlyMessage: msg`Kanban main group by field metadata not found`,
|
||||
});
|
||||
} else if (mainGroupByFieldMetadata.type !== FieldMetadataType.SELECT) {
|
||||
validationResult.errors.push({
|
||||
code: ViewExceptionCode.INVALID_VIEW_DATA,
|
||||
message: t`Kanban main group by field must be a SELECT field`,
|
||||
userFriendlyMessage: msg`Kanban main group by field must be a select field`,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return validationResult;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ export const VIEW_GQL_FIELDS = `
|
||||
objectMetadataId
|
||||
type
|
||||
key
|
||||
mainGroupByFieldMetadataId
|
||||
icon
|
||||
position
|
||||
isCompact
|
||||
@@ -50,7 +51,6 @@ export const VIEW_FILTER_GQL_FIELDS = `
|
||||
|
||||
export const VIEW_GROUP_GQL_FIELDS = `
|
||||
id
|
||||
fieldMetadataId
|
||||
fieldValue
|
||||
isVisible
|
||||
position
|
||||
|
||||
+1
@@ -9,6 +9,7 @@ exports[`successful find view with all sub-relations (e2e) Company View Structur
|
||||
"id": Any<String>,
|
||||
"isCompact": false,
|
||||
"key": "INDEX",
|
||||
"mainGroupByFieldMetadataId": null,
|
||||
"name": "All Companies",
|
||||
"objectMetadataId": Any<String>,
|
||||
"openRecordIn": "SIDE_PANEL",
|
||||
|
||||
+14
@@ -1,3 +1,4 @@
|
||||
import { createOneSelectFieldMetadataForIntegrationTests } from 'test/integration/metadata/suites/field-metadata/utils/create-one-select-field-metadata-for-integration-tests.util';
|
||||
import { createOneObjectMetadata } from 'test/integration/metadata/suites/object-metadata/utils/create-one-object-metadata.util';
|
||||
import { deleteOneObjectMetadata } from 'test/integration/metadata/suites/object-metadata/utils/delete-one-object-metadata.util';
|
||||
import { updateOneObjectMetadata } from 'test/integration/metadata/suites/object-metadata/utils/update-one-object-metadata.util';
|
||||
@@ -9,6 +10,7 @@ import { ViewType } from 'src/engine/metadata-modules/view/enums/view-type.enum'
|
||||
|
||||
describe('Create core view', () => {
|
||||
let testObjectMetadataId: string;
|
||||
let testSelectFieldMetadataId: string;
|
||||
|
||||
beforeAll(async () => {
|
||||
const {
|
||||
@@ -27,6 +29,15 @@ describe('Create core view', () => {
|
||||
});
|
||||
|
||||
testObjectMetadataId = objectMetadataId;
|
||||
|
||||
const { selectFieldMetadataId } =
|
||||
await createOneSelectFieldMetadataForIntegrationTests({
|
||||
input: {
|
||||
objectMetadataId,
|
||||
},
|
||||
});
|
||||
|
||||
testSelectFieldMetadataId = selectFieldMetadataId;
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
@@ -52,6 +63,7 @@ describe('Create core view', () => {
|
||||
objectMetadataId: testObjectMetadataId,
|
||||
icon: 'IconDeal',
|
||||
type: ViewType.KANBAN,
|
||||
mainGroupByFieldMetadataId: testSelectFieldMetadataId,
|
||||
position: 1,
|
||||
isCompact: true,
|
||||
openRecordIn: ViewOpenRecordIn.SIDE_PANEL,
|
||||
@@ -63,6 +75,7 @@ describe('Create core view', () => {
|
||||
assertViewStructure(data.createCoreView, {
|
||||
name: 'Kanban View',
|
||||
objectMetadataId: testObjectMetadataId,
|
||||
mainGroupByFieldMetadataId: testSelectFieldMetadataId,
|
||||
type: ViewType.KANBAN,
|
||||
key: null,
|
||||
icon: 'IconDeal',
|
||||
@@ -90,6 +103,7 @@ describe('Create core view', () => {
|
||||
objectMetadataId: input.objectMetadataId,
|
||||
icon: input.icon,
|
||||
type: ViewType.TABLE,
|
||||
mainGroupByFieldMetadataId: null,
|
||||
key: null,
|
||||
position: 0,
|
||||
isCompact: false,
|
||||
|
||||
+27
@@ -1,4 +1,5 @@
|
||||
import { expectOneNotInternalServerErrorSnapshot } from 'test/integration/graphql/utils/expect-one-not-internal-server-error-snapshot.util';
|
||||
import { createOneSelectFieldMetadataForIntegrationTests } from 'test/integration/metadata/suites/field-metadata/utils/create-one-select-field-metadata-for-integration-tests.util';
|
||||
import { createOneObjectMetadata } from 'test/integration/metadata/suites/object-metadata/utils/create-one-object-metadata.util';
|
||||
import { deleteOneObjectMetadata } from 'test/integration/metadata/suites/object-metadata/utils/delete-one-object-metadata.util';
|
||||
import { updateOneObjectMetadata } from 'test/integration/metadata/suites/object-metadata/utils/update-one-object-metadata.util';
|
||||
@@ -11,6 +12,7 @@ const TEST_NOT_EXISTING_VIEW_ID = '20202020-0000-4000-8000-000000000000';
|
||||
|
||||
describe('Update core view', () => {
|
||||
let testObjectMetadataId: string;
|
||||
let testSelectFieldMetadataId: string;
|
||||
|
||||
beforeAll(async () => {
|
||||
const {
|
||||
@@ -28,7 +30,31 @@ describe('Update core view', () => {
|
||||
},
|
||||
});
|
||||
|
||||
const { selectFieldMetadataId } =
|
||||
await createOneSelectFieldMetadataForIntegrationTests({
|
||||
expectToFail: false,
|
||||
input: {
|
||||
objectMetadataId,
|
||||
options: [
|
||||
{
|
||||
label: 'Option 1',
|
||||
value: 'OPTION_1',
|
||||
color: 'blue',
|
||||
position: 0,
|
||||
},
|
||||
{ label: 'Option 2', value: 'OPTION_2', color: 'red', position: 1 },
|
||||
{
|
||||
label: 'Option 3',
|
||||
value: 'OPTION_3',
|
||||
color: 'green',
|
||||
position: 2,
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
testObjectMetadataId = objectMetadataId;
|
||||
testSelectFieldMetadataId = selectFieldMetadataId;
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
@@ -65,6 +91,7 @@ describe('Update core view', () => {
|
||||
id: view.id,
|
||||
name: 'Updated View',
|
||||
type: ViewType.KANBAN,
|
||||
mainGroupByFieldMetadataId: testSelectFieldMetadataId,
|
||||
isCompact: true,
|
||||
};
|
||||
|
||||
|
||||
+9
-63
@@ -1,17 +1,15 @@
|
||||
import { expectOneNotInternalServerErrorSnapshot } from 'test/integration/graphql/utils/expect-one-not-internal-server-error-snapshot.util';
|
||||
import { createTestViewWithGraphQL } from 'test/integration/graphql/utils/view-graphql.util';
|
||||
import { createOneFieldMetadata } from 'test/integration/metadata/suites/field-metadata/utils/create-one-field-metadata.util';
|
||||
import { createOneSelectFieldMetadataForIntegrationTests } from 'test/integration/metadata/suites/field-metadata/utils/create-one-select-field-metadata-for-integration-tests.util';
|
||||
import { createOneObjectMetadata } from 'test/integration/metadata/suites/object-metadata/utils/create-one-object-metadata.util';
|
||||
import { deleteOneObjectMetadata } from 'test/integration/metadata/suites/object-metadata/utils/delete-one-object-metadata.util';
|
||||
import { updateOneObjectMetadata } from 'test/integration/metadata/suites/object-metadata/utils/update-one-object-metadata.util';
|
||||
import { createOneCoreViewGroup } from 'test/integration/metadata/suites/view-group/utils/create-one-core-view-group.util';
|
||||
import { deleteOneCoreViewGroup } from 'test/integration/metadata/suites/view-group/utils/delete-one-core-view-group.util';
|
||||
import { destroyOneCoreViewGroup } from 'test/integration/metadata/suites/view-group/utils/destroy-one-core-view-group.util';
|
||||
import { findCoreViewGroups } from 'test/integration/metadata/suites/view-group/utils/find-core-view-groups.util';
|
||||
import { updateOneCoreViewGroup } from 'test/integration/metadata/suites/view-group/utils/update-one-core-view-group.util';
|
||||
import { destroyOneCoreView } from 'test/integration/metadata/suites/view/utils/destroy-one-core-view.util';
|
||||
import { assertViewGroupStructure } from 'test/integration/utils/view-test.util';
|
||||
import { FieldMetadataType } from 'twenty-shared/types';
|
||||
|
||||
const TEST_NOT_EXISTING_VIEW_GROUP_ID = '20202020-0000-4000-8000-000000000003';
|
||||
|
||||
@@ -38,22 +36,14 @@ describe('View Group Resolver', () => {
|
||||
|
||||
testObjectMetadataId = objectMetadataId;
|
||||
|
||||
const {
|
||||
data: {
|
||||
createOneField: { id: fieldMetadataId },
|
||||
},
|
||||
} = await createOneFieldMetadata({
|
||||
expectToFail: false,
|
||||
input: {
|
||||
name: 'testField',
|
||||
label: 'Test Field',
|
||||
type: FieldMetadataType.TEXT,
|
||||
objectMetadataId: testObjectMetadataId,
|
||||
isLabelSyncedWithName: true,
|
||||
},
|
||||
});
|
||||
const { selectFieldMetadataId } =
|
||||
await createOneSelectFieldMetadataForIntegrationTests({
|
||||
input: {
|
||||
objectMetadataId: testObjectMetadataId,
|
||||
},
|
||||
});
|
||||
|
||||
testFieldMetadataId = fieldMetadataId;
|
||||
testFieldMetadataId = selectFieldMetadataId;
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
@@ -76,6 +66,7 @@ describe('View Group Resolver', () => {
|
||||
const view = await createTestViewWithGraphQL({
|
||||
name: 'Test View for Groups',
|
||||
objectMetadataId: testObjectMetadataId,
|
||||
mainGroupByFieldMetadataId: testFieldMetadataId,
|
||||
});
|
||||
|
||||
testViewId = view.id;
|
||||
@@ -88,51 +79,12 @@ describe('View Group Resolver', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('getCoreViewGroups', () => {
|
||||
it('should return empty array when no view groups exist', async () => {
|
||||
const { data } = await findCoreViewGroups({
|
||||
viewId: testViewId,
|
||||
expectToFail: false,
|
||||
});
|
||||
|
||||
expect(data.getCoreViewGroups).toEqual([]);
|
||||
});
|
||||
|
||||
it('should return view groups for a specific view', async () => {
|
||||
await createOneCoreViewGroup({
|
||||
expectToFail: false,
|
||||
input: {
|
||||
viewId: testViewId,
|
||||
fieldMetadataId: testFieldMetadataId,
|
||||
isVisible: true,
|
||||
fieldValue: 'active',
|
||||
position: 0,
|
||||
},
|
||||
});
|
||||
|
||||
const { data } = await findCoreViewGroups({
|
||||
viewId: testViewId,
|
||||
expectToFail: false,
|
||||
});
|
||||
|
||||
expect(data.getCoreViewGroups).toHaveLength(1);
|
||||
assertViewGroupStructure(data.getCoreViewGroups[0], {
|
||||
fieldMetadataId: testFieldMetadataId,
|
||||
isVisible: true,
|
||||
fieldValue: 'active',
|
||||
position: 0,
|
||||
viewId: testViewId,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('createCoreViewGroup', () => {
|
||||
it('should create a new view group', async () => {
|
||||
const { data } = await createOneCoreViewGroup({
|
||||
expectToFail: false,
|
||||
input: {
|
||||
viewId: testViewId,
|
||||
fieldMetadataId: testFieldMetadataId,
|
||||
isVisible: false,
|
||||
fieldValue: 'inactive',
|
||||
position: 1,
|
||||
@@ -140,7 +92,6 @@ describe('View Group Resolver', () => {
|
||||
});
|
||||
|
||||
assertViewGroupStructure(data.createCoreViewGroup, {
|
||||
fieldMetadataId: testFieldMetadataId,
|
||||
isVisible: false,
|
||||
fieldValue: 'inactive',
|
||||
position: 1,
|
||||
@@ -153,7 +104,6 @@ describe('View Group Resolver', () => {
|
||||
expectToFail: false,
|
||||
input: {
|
||||
viewId: testViewId,
|
||||
fieldMetadataId: testFieldMetadataId,
|
||||
isVisible: true,
|
||||
fieldValue: '',
|
||||
position: 2,
|
||||
@@ -161,7 +111,6 @@ describe('View Group Resolver', () => {
|
||||
});
|
||||
|
||||
assertViewGroupStructure(data.createCoreViewGroup, {
|
||||
fieldMetadataId: testFieldMetadataId,
|
||||
isVisible: true,
|
||||
fieldValue: '',
|
||||
position: 2,
|
||||
@@ -175,7 +124,6 @@ describe('View Group Resolver', () => {
|
||||
expectToFail: false,
|
||||
input: {
|
||||
viewId: testViewId,
|
||||
fieldMetadataId: testFieldMetadataId,
|
||||
isVisible: true,
|
||||
fieldValue: 'original',
|
||||
position: 0,
|
||||
@@ -224,7 +172,6 @@ describe('View Group Resolver', () => {
|
||||
expectToFail: false,
|
||||
input: {
|
||||
viewId: testViewId,
|
||||
fieldMetadataId: testFieldMetadataId,
|
||||
isVisible: true,
|
||||
fieldValue: 'to delete',
|
||||
position: 0,
|
||||
@@ -263,7 +210,6 @@ describe('View Group Resolver', () => {
|
||||
expectToFail: false,
|
||||
input: {
|
||||
viewId: testViewId,
|
||||
fieldMetadataId: testFieldMetadataId,
|
||||
isVisible: true,
|
||||
fieldValue: 'to destroy',
|
||||
position: 0,
|
||||
|
||||
-104
@@ -1,104 +0,0 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`View Group Resolver - Failing Create Many Operations - v2 should accumulate multiple validation errors when some inputs are invalid 1`] = `
|
||||
{
|
||||
"extensions": {
|
||||
"code": "METADATA_VALIDATION_FAILED",
|
||||
"errors": {
|
||||
"agent": [],
|
||||
"cronTrigger": [],
|
||||
"databaseEventTrigger": [],
|
||||
"fieldMetadata": [],
|
||||
"index": [],
|
||||
"objectMetadata": [],
|
||||
"pageLayoutTab": [],
|
||||
"pageLayoutWidget": [],
|
||||
"role": [],
|
||||
"roleTarget": [],
|
||||
"routeTrigger": [],
|
||||
"serverlessFunction": [],
|
||||
"view": [],
|
||||
"viewField": [],
|
||||
"viewFilter": [],
|
||||
"viewGroup": [
|
||||
{
|
||||
"errors": [
|
||||
{
|
||||
"code": "INVALID_VIEW_DATA",
|
||||
"message": "Field metadata not found",
|
||||
"userFriendlyMessage": "Field metadata not found",
|
||||
},
|
||||
],
|
||||
"flatEntityMinimalInformation": {
|
||||
"fieldMetadataId": Any<String>,
|
||||
"id": Any<String>,
|
||||
"viewId": Any<String>,
|
||||
},
|
||||
"status": "fail",
|
||||
"type": "create_view_group",
|
||||
},
|
||||
{
|
||||
"errors": [
|
||||
{
|
||||
"code": "INVALID_VIEW_DATA",
|
||||
"message": "View not found",
|
||||
"userFriendlyMessage": "View not found",
|
||||
},
|
||||
],
|
||||
"flatEntityMinimalInformation": {
|
||||
"fieldMetadataId": Any<String>,
|
||||
"id": Any<String>,
|
||||
"viewId": Any<String>,
|
||||
},
|
||||
"status": "fail",
|
||||
"type": "create_view_group",
|
||||
},
|
||||
{
|
||||
"errors": [
|
||||
{
|
||||
"code": "INVALID_VIEW_DATA",
|
||||
"message": "Field metadata not found",
|
||||
"userFriendlyMessage": "Field metadata not found",
|
||||
},
|
||||
{
|
||||
"code": "INVALID_VIEW_DATA",
|
||||
"message": "View not found",
|
||||
"userFriendlyMessage": "View not found",
|
||||
},
|
||||
],
|
||||
"flatEntityMinimalInformation": {
|
||||
"fieldMetadataId": Any<String>,
|
||||
"id": Any<String>,
|
||||
"viewId": Any<String>,
|
||||
},
|
||||
"status": "fail",
|
||||
"type": "create_view_group",
|
||||
},
|
||||
],
|
||||
},
|
||||
"message": "Validation failed for 0 object(s) and 0 field(s)",
|
||||
"summary": {
|
||||
"invalidAgent": 0,
|
||||
"invalidCronTrigger": 0,
|
||||
"invalidDatabaseEventTrigger": 0,
|
||||
"invalidFieldMetadata": 0,
|
||||
"invalidIndex": 0,
|
||||
"invalidObjectMetadata": 0,
|
||||
"invalidPageLayoutTab": 0,
|
||||
"invalidPageLayoutWidget": 0,
|
||||
"invalidRole": 0,
|
||||
"invalidRoleTarget": 0,
|
||||
"invalidRouteTrigger": 0,
|
||||
"invalidServerlessFunction": 0,
|
||||
"invalidView": 0,
|
||||
"invalidViewField": 0,
|
||||
"invalidViewFilter": 0,
|
||||
"invalidViewGroup": 0,
|
||||
"totalErrors": 0,
|
||||
},
|
||||
"userFriendlyMessage": "Validation failed for 0 object(s) and 0 field(s)",
|
||||
},
|
||||
"message": "Multiple validation errors occurred while creating view groups",
|
||||
"name": "GraphQLError",
|
||||
}
|
||||
`;
|
||||
-152
@@ -1,152 +0,0 @@
|
||||
import { createManyCoreViewGroups } from 'test/integration/metadata/suites/view-group/utils/create-many-core-view-groups.util';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { expectOneNotInternalServerErrorSnapshot } from 'test/integration/graphql/utils/expect-one-not-internal-server-error-snapshot.util';
|
||||
import { createOneFieldMetadata } from 'test/integration/metadata/suites/field-metadata/utils/create-one-field-metadata.util';
|
||||
import { createOneObjectMetadata } from 'test/integration/metadata/suites/object-metadata/utils/create-one-object-metadata.util';
|
||||
import { deleteOneObjectMetadata } from 'test/integration/metadata/suites/object-metadata/utils/delete-one-object-metadata.util';
|
||||
import { updateOneObjectMetadata } from 'test/integration/metadata/suites/object-metadata/utils/update-one-object-metadata.util';
|
||||
import { createOneCoreView } from 'test/integration/metadata/suites/view/utils/create-one-core-view.util';
|
||||
import { FieldMetadataType } from 'twenty-shared/types';
|
||||
|
||||
import { type CreateViewGroupInput } from 'src/engine/metadata-modules/view-group/dtos/inputs/create-view-group.input';
|
||||
|
||||
describe('View Group Resolver - Failing Create Many Operations - v2', () => {
|
||||
let testSetup: {
|
||||
testViewId: string;
|
||||
testObjectMetadataId: string;
|
||||
firstTestFieldMetadataId: string;
|
||||
secondTestFieldMetadataId: string;
|
||||
};
|
||||
|
||||
beforeAll(async () => {
|
||||
const {
|
||||
data: {
|
||||
createOneObject: { id: objectMetadataId },
|
||||
},
|
||||
} = await createOneObjectMetadata({
|
||||
expectToFail: false,
|
||||
input: {
|
||||
nameSingular: 'myGroupTestObjectV2',
|
||||
namePlural: 'myGroupTestObjectsV2',
|
||||
labelSingular: 'My Group Test Object v2',
|
||||
labelPlural: 'My Group Test Objects v2',
|
||||
icon: 'Icon123',
|
||||
},
|
||||
});
|
||||
|
||||
const {
|
||||
data: {
|
||||
createOneField: { id: firstTestFieldMetadataId },
|
||||
},
|
||||
} = await createOneFieldMetadata({
|
||||
expectToFail: false,
|
||||
input: {
|
||||
name: 'testField',
|
||||
label: 'Test Field',
|
||||
type: FieldMetadataType.TEXT,
|
||||
objectMetadataId,
|
||||
isLabelSyncedWithName: true,
|
||||
},
|
||||
gqlFields: `
|
||||
id
|
||||
name
|
||||
label
|
||||
isLabelSyncedWithName
|
||||
`,
|
||||
});
|
||||
|
||||
const {
|
||||
data: {
|
||||
createOneField: { id: secondTestFieldMetadataId },
|
||||
},
|
||||
} = await createOneFieldMetadata({
|
||||
expectToFail: false,
|
||||
input: {
|
||||
name: 'secondTestField',
|
||||
label: 'Test Field',
|
||||
type: FieldMetadataType.TEXT,
|
||||
objectMetadataId,
|
||||
isLabelSyncedWithName: false,
|
||||
},
|
||||
gqlFields: `
|
||||
id
|
||||
name
|
||||
label
|
||||
isLabelSyncedWithName
|
||||
`,
|
||||
});
|
||||
|
||||
const {
|
||||
data: {
|
||||
createCoreView: { id: testViewId },
|
||||
},
|
||||
} = await createOneCoreView({
|
||||
input: {
|
||||
icon: 'icon123',
|
||||
objectMetadataId,
|
||||
name: 'TestViewForGroups',
|
||||
},
|
||||
expectToFail: false,
|
||||
});
|
||||
|
||||
testSetup = {
|
||||
testViewId,
|
||||
testObjectMetadataId: objectMetadataId,
|
||||
firstTestFieldMetadataId,
|
||||
secondTestFieldMetadataId,
|
||||
};
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await updateOneObjectMetadata({
|
||||
input: {
|
||||
idToUpdate: testSetup.testObjectMetadataId,
|
||||
updatePayload: {
|
||||
isActive: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
await deleteOneObjectMetadata({
|
||||
expectToFail: false,
|
||||
input: { idToDelete: testSetup.testObjectMetadataId },
|
||||
});
|
||||
});
|
||||
|
||||
it('should accumulate multiple validation errors when some inputs are invalid', async () => {
|
||||
const invalidViewId = uuidv4();
|
||||
const invalidFieldMetadataId = uuidv4();
|
||||
|
||||
const inputs: CreateViewGroupInput[] = [
|
||||
{
|
||||
fieldMetadataId: invalidFieldMetadataId,
|
||||
viewId: testSetup.testViewId,
|
||||
position: 0,
|
||||
isVisible: true,
|
||||
fieldValue: 'Invalid Group A',
|
||||
},
|
||||
{
|
||||
fieldMetadataId: testSetup.firstTestFieldMetadataId,
|
||||
viewId: invalidViewId,
|
||||
position: 1,
|
||||
isVisible: true,
|
||||
fieldValue: 'Invalid Group B',
|
||||
},
|
||||
{
|
||||
fieldMetadataId: invalidFieldMetadataId,
|
||||
viewId: invalidViewId,
|
||||
position: 2,
|
||||
isVisible: true,
|
||||
fieldValue: 'Invalid Group C',
|
||||
},
|
||||
];
|
||||
|
||||
const { errors } = await createManyCoreViewGroups({
|
||||
inputs,
|
||||
expectToFail: true,
|
||||
});
|
||||
|
||||
expectOneNotInternalServerErrorSnapshot({
|
||||
errors,
|
||||
});
|
||||
});
|
||||
});
|
||||
+9
-6
@@ -1,4 +1,5 @@
|
||||
import { createOneFieldMetadata } from 'test/integration/metadata/suites/field-metadata/utils/create-one-field-metadata.util';
|
||||
import { createOneSelectFieldMetadataForIntegrationTests } from 'test/integration/metadata/suites/field-metadata/utils/create-one-select-field-metadata-for-integration-tests.util';
|
||||
import { createOneObjectMetadata } from 'test/integration/metadata/suites/object-metadata/utils/create-one-object-metadata.util';
|
||||
import { deleteOneObjectMetadata } from 'test/integration/metadata/suites/object-metadata/utils/delete-one-object-metadata.util';
|
||||
import { updateOneObjectMetadata } from 'test/integration/metadata/suites/object-metadata/utils/update-one-object-metadata.util';
|
||||
@@ -100,6 +101,13 @@ describe('View Group Resolver - Successful Create Many Operations - v2', () => {
|
||||
`,
|
||||
});
|
||||
|
||||
const { selectFieldMetadataId } =
|
||||
await createOneSelectFieldMetadataForIntegrationTests({
|
||||
input: {
|
||||
objectMetadataId,
|
||||
},
|
||||
});
|
||||
|
||||
const {
|
||||
data: {
|
||||
createCoreView: { id: testViewId },
|
||||
@@ -109,6 +117,7 @@ describe('View Group Resolver - Successful Create Many Operations - v2', () => {
|
||||
icon: 'icon123',
|
||||
objectMetadataId,
|
||||
name: 'TestViewForGroups',
|
||||
mainGroupByFieldMetadataId: selectFieldMetadataId,
|
||||
},
|
||||
expectToFail: false,
|
||||
});
|
||||
@@ -164,21 +173,18 @@ describe('View Group Resolver - Successful Create Many Operations - v2', () => {
|
||||
it('should successfully create multiple view groups in batch', async () => {
|
||||
const inputs: CreateViewGroupInput[] = [
|
||||
{
|
||||
fieldMetadataId: testSetup.firstTestFieldMetadataId,
|
||||
viewId: testSetup.testViewId,
|
||||
position: 0,
|
||||
isVisible: true,
|
||||
fieldValue: 'Group A',
|
||||
},
|
||||
{
|
||||
fieldMetadataId: testSetup.secondTestFieldMetadataId,
|
||||
viewId: testSetup.testViewId,
|
||||
position: 1,
|
||||
isVisible: false,
|
||||
fieldValue: 'Group B',
|
||||
},
|
||||
{
|
||||
fieldMetadataId: testSetup.thirdTestFieldMetadataId,
|
||||
viewId: testSetup.testViewId,
|
||||
position: 2,
|
||||
isVisible: true,
|
||||
@@ -200,7 +206,6 @@ describe('View Group Resolver - Successful Create Many Operations - v2', () => {
|
||||
|
||||
createdViewGroups.forEach((viewGroup, index) => {
|
||||
expect(viewGroup).toMatchObject({
|
||||
fieldMetadataId: inputs[index].fieldMetadataId,
|
||||
viewId: testSetup.testViewId,
|
||||
position: inputs[index].position,
|
||||
isVisible: inputs[index].isVisible,
|
||||
@@ -214,7 +219,6 @@ describe('View Group Resolver - Successful Create Many Operations - v2', () => {
|
||||
it('should successfully create single view group using batch endpoint', async () => {
|
||||
const inputs: CreateViewGroupInput[] = [
|
||||
{
|
||||
fieldMetadataId: testSetup.firstTestFieldMetadataId,
|
||||
viewId: testSetup.testViewId,
|
||||
position: 5,
|
||||
isVisible: true,
|
||||
@@ -237,7 +241,6 @@ describe('View Group Resolver - Successful Create Many Operations - v2', () => {
|
||||
const viewGroup = createdViewGroups[0];
|
||||
|
||||
expect(viewGroup).toMatchObject({
|
||||
fieldMetadataId: testSetup.firstTestFieldMetadataId,
|
||||
viewId: testSetup.testViewId,
|
||||
position: 5,
|
||||
isVisible: true,
|
||||
|
||||
@@ -15,6 +15,8 @@ export const createTestViewWithGraphQL = async (
|
||||
icon: viewData.icon,
|
||||
type: viewData.type,
|
||||
position: viewData.position,
|
||||
mainGroupByFieldMetadataId:
|
||||
viewData.mainGroupByFieldMetadataId ?? undefined,
|
||||
isCompact: viewData.isCompact,
|
||||
openRecordIn: viewData.openRecordIn,
|
||||
visibility: viewData.visibility,
|
||||
|
||||
+10
@@ -1,4 +1,5 @@
|
||||
import { createOneFieldMetadata } from 'test/integration/metadata/suites/field-metadata/utils/create-one-field-metadata.util';
|
||||
import { createOneSelectFieldMetadataForIntegrationTests } from 'test/integration/metadata/suites/field-metadata/utils/create-one-select-field-metadata-for-integration-tests.util';
|
||||
import { updateOneFieldMetadata } from 'test/integration/metadata/suites/field-metadata/utils/update-one-field-metadata.util';
|
||||
import { createOneObjectMetadata } from 'test/integration/metadata/suites/object-metadata/utils/create-one-object-metadata.util';
|
||||
import { deleteOneObjectMetadata } from 'test/integration/metadata/suites/object-metadata/utils/delete-one-object-metadata.util';
|
||||
@@ -271,12 +272,21 @@ describe('calendar-field-deactivation-deletes-views', () => {
|
||||
});
|
||||
|
||||
it('should delete calendar view but not other view types when calendar field is deactivated', async () => {
|
||||
const { selectFieldMetadataId } =
|
||||
await createOneSelectFieldMetadataForIntegrationTests({
|
||||
input: {
|
||||
objectMetadataId: testSetup.objectMetadataId,
|
||||
name: 'selectField',
|
||||
},
|
||||
});
|
||||
|
||||
const {
|
||||
data: { createCoreView: kanbanViewWithSameObject },
|
||||
} = await createOneCoreView({
|
||||
input: {
|
||||
name: generateRecordName('Kanban View'),
|
||||
objectMetadataId: testSetup.objectMetadataId,
|
||||
mainGroupByFieldMetadataId: selectFieldMetadataId,
|
||||
type: ViewType.KANBAN,
|
||||
icon: 'IconLayoutKanban',
|
||||
},
|
||||
|
||||
+4
-93
@@ -3,7 +3,6 @@ import { updateOneFieldMetadata } from 'test/integration/metadata/suites/field-m
|
||||
import { createOneObjectMetadata } from 'test/integration/metadata/suites/object-metadata/utils/create-one-object-metadata.util';
|
||||
import { deleteOneObjectMetadata } from 'test/integration/metadata/suites/object-metadata/utils/delete-one-object-metadata.util';
|
||||
import { updateOneObjectMetadata } from 'test/integration/metadata/suites/object-metadata/utils/update-one-object-metadata.util';
|
||||
import { createOneCoreViewGroup } from 'test/integration/metadata/suites/view-group/utils/create-one-core-view-group.util';
|
||||
import { createOneCoreView } from 'test/integration/metadata/suites/view/utils/create-one-core-view.util';
|
||||
import { findOneCoreView } from 'test/integration/metadata/suites/view/utils/find-one-core-view.util';
|
||||
import { generateRecordName } from 'test/integration/utils/generate-record-name';
|
||||
@@ -29,7 +28,7 @@ type TestSetup = {
|
||||
viewWithoutGroupId: string;
|
||||
};
|
||||
|
||||
describe('view-group-field-deactivation-deletes-views', () => {
|
||||
describe('field-for-group-by-deactivation-deletes-views', () => {
|
||||
let testSetup: TestSetup;
|
||||
|
||||
const verifyViewExists = async (viewId: string, shouldExist: boolean) => {
|
||||
@@ -126,22 +125,13 @@ describe('view-group-field-deactivation-deletes-views', () => {
|
||||
name: generateRecordName('View With Group'),
|
||||
objectMetadataId,
|
||||
type: ViewType.TABLE,
|
||||
mainGroupByFieldMetadataId: groupByFieldMetadataId,
|
||||
icon: 'IconTable',
|
||||
},
|
||||
gqlFields: VIEW_FIELDS,
|
||||
expectToFail: false,
|
||||
});
|
||||
|
||||
await createOneCoreViewGroup({
|
||||
input: {
|
||||
fieldMetadataId: groupByFieldMetadataId,
|
||||
fieldValue: 'OPTION_1',
|
||||
viewId: viewWithGroup.id,
|
||||
},
|
||||
gqlFields: 'id',
|
||||
expectToFail: false,
|
||||
});
|
||||
|
||||
const {
|
||||
data: { createCoreView: viewWithoutGroup },
|
||||
} = await createOneCoreView({
|
||||
@@ -180,7 +170,7 @@ describe('view-group-field-deactivation-deletes-views', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should delete view when field used in view group is deactivated', async () => {
|
||||
it('should delete view when mainGroupByFieldMetadata is deactivated', async () => {
|
||||
await verifyViewExists(testSetup.viewWithGroupId, true);
|
||||
await verifyViewExists(testSetup.viewWithoutGroupId, true);
|
||||
|
||||
@@ -208,22 +198,13 @@ describe('view-group-field-deactivation-deletes-views', () => {
|
||||
name: generateRecordName('Second View With Group'),
|
||||
objectMetadataId: testSetup.objectMetadataId,
|
||||
type: ViewType.TABLE,
|
||||
mainGroupByFieldMetadataId: testSetup.groupByFieldMetadataId,
|
||||
icon: 'IconTable',
|
||||
},
|
||||
gqlFields: VIEW_FIELDS,
|
||||
expectToFail: false,
|
||||
});
|
||||
|
||||
await createOneCoreViewGroup({
|
||||
input: {
|
||||
fieldMetadataId: testSetup.groupByFieldMetadataId,
|
||||
fieldValue: 'OPTION_2',
|
||||
viewId: secondViewWithGroup.id,
|
||||
},
|
||||
gqlFields: 'id',
|
||||
expectToFail: false,
|
||||
});
|
||||
|
||||
await verifyViewExists(testSetup.viewWithGroupId, true);
|
||||
await verifyViewExists(secondViewWithGroup.id, true);
|
||||
await verifyViewExists(testSetup.viewWithoutGroupId, true);
|
||||
@@ -234,74 +215,4 @@ describe('view-group-field-deactivation-deletes-views', () => {
|
||||
await verifyViewExists(secondViewWithGroup.id, false);
|
||||
await verifyViewExists(testSetup.viewWithoutGroupId, true);
|
||||
});
|
||||
|
||||
it('should handle deactivation when view has multiple view groups with different fields', async () => {
|
||||
const {
|
||||
data: {
|
||||
createOneField: { id: anotherGroupByFieldMetadataId },
|
||||
},
|
||||
} = await createOneFieldMetadata({
|
||||
expectToFail: false,
|
||||
input: {
|
||||
name: 'anotherGroupByField',
|
||||
type: FieldMetadataType.SELECT,
|
||||
label: 'Another Group By Field',
|
||||
objectMetadataId: testSetup.objectMetadataId,
|
||||
options: [
|
||||
{
|
||||
label: 'Status A',
|
||||
value: 'STATUS_A',
|
||||
color: 'purple',
|
||||
position: 0,
|
||||
},
|
||||
{
|
||||
label: 'Status B',
|
||||
value: 'STATUS_B',
|
||||
color: 'orange',
|
||||
position: 1,
|
||||
},
|
||||
],
|
||||
},
|
||||
gqlFields: 'id',
|
||||
});
|
||||
|
||||
const {
|
||||
data: { createCoreView: viewWithMultipleGroups },
|
||||
} = await createOneCoreView({
|
||||
input: {
|
||||
name: generateRecordName('View With Multiple Groups'),
|
||||
objectMetadataId: testSetup.objectMetadataId,
|
||||
type: ViewType.TABLE,
|
||||
icon: 'IconTable',
|
||||
},
|
||||
gqlFields: VIEW_FIELDS,
|
||||
expectToFail: false,
|
||||
});
|
||||
|
||||
await createOneCoreViewGroup({
|
||||
input: {
|
||||
fieldMetadataId: testSetup.groupByFieldMetadataId,
|
||||
fieldValue: 'OPTION_1',
|
||||
viewId: viewWithMultipleGroups.id,
|
||||
},
|
||||
gqlFields: 'id',
|
||||
expectToFail: false,
|
||||
});
|
||||
|
||||
await createOneCoreViewGroup({
|
||||
input: {
|
||||
fieldMetadataId: anotherGroupByFieldMetadataId,
|
||||
fieldValue: 'STATUS_A',
|
||||
viewId: viewWithMultipleGroups.id,
|
||||
},
|
||||
gqlFields: 'id',
|
||||
expectToFail: false,
|
||||
});
|
||||
|
||||
await verifyViewExists(viewWithMultipleGroups.id, true);
|
||||
|
||||
await deactivateFieldAndVerify(testSetup.groupByFieldMetadataId);
|
||||
|
||||
await verifyViewExists(viewWithMultipleGroups.id, false);
|
||||
});
|
||||
});
|
||||
+31
@@ -1,4 +1,5 @@
|
||||
import { createOneFieldMetadata } from 'test/integration/metadata/suites/field-metadata/utils/create-one-field-metadata.util';
|
||||
import { createOneSelectFieldMetadataForIntegrationTests } from 'test/integration/metadata/suites/field-metadata/utils/create-one-select-field-metadata-for-integration-tests.util';
|
||||
import { updateOneFieldMetadata } from 'test/integration/metadata/suites/field-metadata/utils/update-one-field-metadata.util';
|
||||
import { createOneObjectMetadata } from 'test/integration/metadata/suites/object-metadata/utils/create-one-object-metadata.util';
|
||||
import { deleteOneObjectMetadata } from 'test/integration/metadata/suites/object-metadata/utils/delete-one-object-metadata.util';
|
||||
@@ -28,6 +29,7 @@ type TestSetup = {
|
||||
objectMetadataId: string;
|
||||
aggregateFieldMetadataId: string;
|
||||
nonAggregateFieldMetadataId: string;
|
||||
selectFieldMetadataId: string;
|
||||
viewWithAggregateId: string;
|
||||
viewWithoutAggregateId: string;
|
||||
};
|
||||
@@ -115,6 +117,29 @@ describe('kanban-aggregate-field-deactivation-nullifies-kanban-properties', () =
|
||||
gqlFields: 'id',
|
||||
});
|
||||
|
||||
const { selectFieldMetadataId } =
|
||||
await createOneSelectFieldMetadataForIntegrationTests({
|
||||
input: {
|
||||
name: 'selectField',
|
||||
objectMetadataId,
|
||||
options: [
|
||||
{
|
||||
label: 'Option 1',
|
||||
value: 'OPTION_1',
|
||||
color: 'blue',
|
||||
position: 0,
|
||||
},
|
||||
{ label: 'Option 2', value: 'OPTION_2', color: 'red', position: 1 },
|
||||
{
|
||||
label: 'Option 3',
|
||||
value: 'OPTION_3',
|
||||
color: 'green',
|
||||
position: 2,
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
const {
|
||||
data: { createCoreView: viewWithAggregate },
|
||||
} = await createOneCoreView({
|
||||
@@ -122,6 +147,7 @@ describe('kanban-aggregate-field-deactivation-nullifies-kanban-properties', () =
|
||||
name: generateRecordName('Kanban View With Aggregate'),
|
||||
objectMetadataId,
|
||||
type: ViewType.KANBAN,
|
||||
mainGroupByFieldMetadataId: selectFieldMetadataId,
|
||||
kanbanAggregateOperationFieldMetadataId: aggregateFieldMetadataId,
|
||||
kanbanAggregateOperation: AggregateOperations.SUM,
|
||||
icon: 'IconLayoutKanban',
|
||||
@@ -136,6 +162,7 @@ describe('kanban-aggregate-field-deactivation-nullifies-kanban-properties', () =
|
||||
input: {
|
||||
name: generateRecordName('Kanban View Without Aggregate'),
|
||||
objectMetadataId,
|
||||
mainGroupByFieldMetadataId: selectFieldMetadataId,
|
||||
type: ViewType.KANBAN,
|
||||
icon: 'IconLayoutKanban',
|
||||
},
|
||||
@@ -147,6 +174,7 @@ describe('kanban-aggregate-field-deactivation-nullifies-kanban-properties', () =
|
||||
objectMetadataId,
|
||||
aggregateFieldMetadataId,
|
||||
nonAggregateFieldMetadataId,
|
||||
selectFieldMetadataId,
|
||||
viewWithAggregateId: viewWithAggregate.id,
|
||||
viewWithoutAggregateId: viewWithoutAggregate.id,
|
||||
};
|
||||
@@ -253,6 +281,7 @@ describe('kanban-aggregate-field-deactivation-nullifies-kanban-properties', () =
|
||||
name: generateRecordName('Second Kanban View With Aggregate'),
|
||||
objectMetadataId: testSetup.objectMetadataId,
|
||||
type: ViewType.KANBAN,
|
||||
mainGroupByFieldMetadataId: testSetup.selectFieldMetadataId,
|
||||
kanbanAggregateOperationFieldMetadataId:
|
||||
testSetup.aggregateFieldMetadataId,
|
||||
kanbanAggregateOperation: AggregateOperations.MAX,
|
||||
@@ -318,6 +347,7 @@ describe('kanban-aggregate-field-deactivation-nullifies-kanban-properties', () =
|
||||
name: generateRecordName('Kanban View With MIN'),
|
||||
objectMetadataId: testSetup.objectMetadataId,
|
||||
type: ViewType.KANBAN,
|
||||
mainGroupByFieldMetadataId: testSetup.selectFieldMetadataId,
|
||||
kanbanAggregateOperationFieldMetadataId:
|
||||
testSetup.aggregateFieldMetadataId,
|
||||
kanbanAggregateOperation: AggregateOperations.MIN,
|
||||
@@ -334,6 +364,7 @@ describe('kanban-aggregate-field-deactivation-nullifies-kanban-properties', () =
|
||||
name: generateRecordName('Kanban View With AVG'),
|
||||
objectMetadataId: testSetup.objectMetadataId,
|
||||
type: ViewType.KANBAN,
|
||||
mainGroupByFieldMetadataId: testSetup.selectFieldMetadataId,
|
||||
kanbanAggregateOperationFieldMetadataId:
|
||||
testSetup.aggregateFieldMetadataId,
|
||||
kanbanAggregateOperation: AggregateOperations.AVG,
|
||||
|
||||
+66
-194
@@ -5,13 +5,9 @@ import { createOneObjectMetadata } from 'test/integration/metadata/suites/object
|
||||
import { deleteOneObjectMetadata } from 'test/integration/metadata/suites/object-metadata/utils/delete-one-object-metadata.util';
|
||||
import { getMockCreateObjectInput } from 'test/integration/metadata/suites/object-metadata/utils/generate-mock-create-object-metadata-input';
|
||||
import { updateOneObjectMetadata } from 'test/integration/metadata/suites/object-metadata/utils/update-one-object-metadata.util';
|
||||
import { createOneCoreViewGroup } from 'test/integration/metadata/suites/view-group/utils/create-one-core-view-group.util';
|
||||
import { findCoreViewGroups } from 'test/integration/metadata/suites/view-group/utils/find-core-view-groups.util';
|
||||
import { createOneCoreView } from 'test/integration/metadata/suites/view/utils/create-one-core-view.util';
|
||||
import {
|
||||
FieldMetadataType,
|
||||
type EnumFieldMetadataType,
|
||||
} from 'twenty-shared/types';
|
||||
import { FieldMetadataType } from 'twenty-shared/types';
|
||||
|
||||
import {
|
||||
type FieldMetadataComplexOption,
|
||||
@@ -37,16 +33,10 @@ const fakeOptionUpdate = ({ value, label, ...option }: Option) => ({
|
||||
label: `${label} updated`,
|
||||
});
|
||||
|
||||
const testFieldMetadataTypes: EnumFieldMetadataType[] = [
|
||||
FieldMetadataType.SELECT,
|
||||
FieldMetadataType.MULTI_SELECT,
|
||||
];
|
||||
|
||||
describe('update-one-field-metadata-view-groups-side-effect-v2', () => {
|
||||
let objectMetadataIdToDelete: string;
|
||||
|
||||
const createObjectWithSelectFieldAndView = async (
|
||||
fieldType: EnumFieldMetadataType,
|
||||
initialOptions: Option[],
|
||||
) => {
|
||||
const singular = 'sideEffect';
|
||||
@@ -69,11 +59,11 @@ describe('update-one-field-metadata-view-groups-side-effect-v2', () => {
|
||||
|
||||
const {
|
||||
data: { createOneField },
|
||||
} = await createOneFieldMetadata<typeof fieldType>({
|
||||
} = await createOneFieldMetadata({
|
||||
expectToFail: false,
|
||||
input: {
|
||||
objectMetadataId: createOneObject.id,
|
||||
type: fieldType,
|
||||
type: FieldMetadataType.SELECT,
|
||||
name: 'statusField',
|
||||
label: 'Status Field',
|
||||
isLabelSyncedWithName: true,
|
||||
@@ -90,6 +80,7 @@ describe('update-one-field-metadata-view-groups-side-effect-v2', () => {
|
||||
icon: 'IconTable',
|
||||
name: 'Test View',
|
||||
objectMetadataId: createOneObject.id,
|
||||
mainGroupByFieldMetadataId: createOneField.id,
|
||||
type: ViewType.TABLE,
|
||||
},
|
||||
expectToFail: false,
|
||||
@@ -120,38 +111,30 @@ describe('update-one-field-metadata-view-groups-side-effect-v2', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe.each(testFieldMetadataTypes)('%s field type', (fieldType) => {
|
||||
it('should delete all view groups when all enum field options are deleted', async () => {
|
||||
describe('SELECT field type', () => {
|
||||
it('deleted and adds view groups when options are deleted and added', async () => {
|
||||
const initialOptions = generateOptions(3);
|
||||
const { fieldMetadataId, viewId } =
|
||||
await createObjectWithSelectFieldAndView(fieldType, initialOptions);
|
||||
|
||||
for (const [index, option] of initialOptions.entries()) {
|
||||
await createOneCoreViewGroup({
|
||||
input: {
|
||||
viewId,
|
||||
fieldMetadataId,
|
||||
fieldValue: option.value,
|
||||
isVisible: true,
|
||||
position: index,
|
||||
},
|
||||
expectToFail: false,
|
||||
gqlFields: 'id fieldValue',
|
||||
});
|
||||
}
|
||||
await createObjectWithSelectFieldAndView(initialOptions);
|
||||
|
||||
const {
|
||||
data: { getCoreViewGroups: initialViewGroups },
|
||||
} = await findCoreViewGroups({
|
||||
viewId,
|
||||
gqlFields: 'id fieldValue fieldMetadataId',
|
||||
gqlFields: 'id fieldValue',
|
||||
expectToFail: false,
|
||||
});
|
||||
|
||||
expect(
|
||||
initialViewGroups.filter((vg) => vg.fieldMetadataId === fieldMetadataId)
|
||||
.length,
|
||||
).toBe(3);
|
||||
expect(initialViewGroups.length).toBe(4);
|
||||
const initialFieldValues = [...initialViewGroups]
|
||||
.map((vg) => vg.fieldValue)
|
||||
.sort();
|
||||
const expectedFieldValues = [
|
||||
...initialOptions.map((opt) => opt.value),
|
||||
'',
|
||||
].sort();
|
||||
|
||||
expect(initialFieldValues).toEqual(expectedFieldValues);
|
||||
|
||||
await updateOneFieldMetadata({
|
||||
input: {
|
||||
@@ -175,82 +158,22 @@ describe('update-one-field-metadata-view-groups-side-effect-v2', () => {
|
||||
data: { getCoreViewGroups: updatedViewGroups },
|
||||
} = await findCoreViewGroups({
|
||||
viewId,
|
||||
gqlFields: 'id fieldValue fieldMetadataId',
|
||||
gqlFields: 'id fieldValue viewId',
|
||||
expectToFail: false,
|
||||
});
|
||||
|
||||
expect(
|
||||
updatedViewGroups.filter((vg) => vg.fieldMetadataId === fieldMetadataId)
|
||||
.length,
|
||||
).toBe(0);
|
||||
expect(updatedViewGroups.length).toBe(2);
|
||||
const updatedFieldValues = updatedViewGroups
|
||||
.map((vg) => vg.fieldValue)
|
||||
.sort();
|
||||
const expectedUpdatedFieldValues = ['', 'NEW_OPTION_VALUE'].sort();
|
||||
|
||||
expect(updatedFieldValues).toEqual(expectedUpdatedFieldValues);
|
||||
});
|
||||
|
||||
it('should update view group when option value is updated', async () => {
|
||||
const initialOptions = generateOptions(1);
|
||||
const { fieldMetadataId, fieldOptions, viewId } =
|
||||
await createObjectWithSelectFieldAndView(fieldType, initialOptions);
|
||||
|
||||
await createOneCoreViewGroup({
|
||||
input: {
|
||||
viewId,
|
||||
fieldMetadataId,
|
||||
fieldValue: initialOptions[0].value,
|
||||
isVisible: true,
|
||||
position: 0,
|
||||
},
|
||||
expectToFail: false,
|
||||
gqlFields: 'id fieldValue',
|
||||
});
|
||||
|
||||
const updatedOptions = [fakeOptionUpdate(fieldOptions[0])];
|
||||
|
||||
await updateOneFieldMetadata({
|
||||
input: {
|
||||
idToUpdate: fieldMetadataId,
|
||||
updatePayload: {
|
||||
options: updatedOptions,
|
||||
},
|
||||
},
|
||||
gqlFields: 'id options',
|
||||
expectToFail: false,
|
||||
});
|
||||
|
||||
const {
|
||||
data: { getCoreViewGroups: updatedViewGroups },
|
||||
} = await findCoreViewGroups({
|
||||
viewId,
|
||||
gqlFields: 'id fieldValue fieldMetadataId',
|
||||
expectToFail: false,
|
||||
});
|
||||
|
||||
const viewGroupsForField = updatedViewGroups.filter(
|
||||
(vg) => vg.fieldMetadataId === fieldMetadataId,
|
||||
);
|
||||
|
||||
expect(viewGroupsForField.length).toBe(1);
|
||||
expect(viewGroupsForField[0].fieldValue).toBe(
|
||||
`${initialOptions[0].value}_UPDATED`,
|
||||
);
|
||||
});
|
||||
|
||||
it('should update multiple view groups when multiple option values are updated', async () => {
|
||||
it('updates specific view groups when some options are updated', async () => {
|
||||
const initialOptions = generateOptions(3);
|
||||
const { fieldMetadataId, fieldOptions, viewId } =
|
||||
await createObjectWithSelectFieldAndView(fieldType, initialOptions);
|
||||
|
||||
for (const [index, option] of initialOptions.entries()) {
|
||||
await createOneCoreViewGroup({
|
||||
input: {
|
||||
viewId,
|
||||
fieldMetadataId,
|
||||
fieldValue: option.value,
|
||||
isVisible: true,
|
||||
position: index,
|
||||
},
|
||||
expectToFail: false,
|
||||
gqlFields: 'id fieldValue',
|
||||
});
|
||||
}
|
||||
await createObjectWithSelectFieldAndView(initialOptions);
|
||||
|
||||
const updatedOptions = fieldOptions.map((opt) => fakeOptionUpdate(opt));
|
||||
|
||||
@@ -269,43 +192,26 @@ describe('update-one-field-metadata-view-groups-side-effect-v2', () => {
|
||||
data: { getCoreViewGroups: updatedViewGroups },
|
||||
} = await findCoreViewGroups({
|
||||
viewId,
|
||||
gqlFields: 'id fieldValue fieldMetadataId',
|
||||
gqlFields: 'id fieldValue',
|
||||
expectToFail: false,
|
||||
});
|
||||
|
||||
const viewGroupsForField = updatedViewGroups.filter(
|
||||
(vg) => vg.fieldMetadataId === fieldMetadataId,
|
||||
);
|
||||
|
||||
expect(viewGroupsForField.length).toBe(3);
|
||||
const actualFieldValues = viewGroupsForField
|
||||
expect(updatedViewGroups.length).toBe(4);
|
||||
const updatedFieldValues = updatedViewGroups
|
||||
.map((vg) => vg.fieldValue)
|
||||
.sort();
|
||||
const expectedFieldValues = initialOptions
|
||||
.map((opt) => `${opt.value}_UPDATED`)
|
||||
.sort();
|
||||
const expectedUpdatedFieldValues = [
|
||||
...updatedOptions.map((opt) => opt.value),
|
||||
'',
|
||||
].sort();
|
||||
|
||||
expect(actualFieldValues).toEqual(expectedFieldValues);
|
||||
expect(updatedFieldValues).toEqual(expectedUpdatedFieldValues);
|
||||
});
|
||||
|
||||
it('should delete specific view groups when their options are removed', async () => {
|
||||
it('deletes specific view groups when some of all options are removed', async () => {
|
||||
const initialOptions = generateOptions(5);
|
||||
const { fieldMetadataId, fieldOptions, viewId } =
|
||||
await createObjectWithSelectFieldAndView(fieldType, initialOptions);
|
||||
|
||||
for (const [index, option] of initialOptions.entries()) {
|
||||
await createOneCoreViewGroup({
|
||||
input: {
|
||||
viewId,
|
||||
fieldMetadataId,
|
||||
fieldValue: option.value,
|
||||
isVisible: true,
|
||||
position: index,
|
||||
},
|
||||
expectToFail: false,
|
||||
gqlFields: 'id fieldValue',
|
||||
});
|
||||
}
|
||||
await createObjectWithSelectFieldAndView(initialOptions);
|
||||
|
||||
const updatedOptions = fieldOptions.slice(2);
|
||||
|
||||
@@ -324,44 +230,26 @@ describe('update-one-field-metadata-view-groups-side-effect-v2', () => {
|
||||
data: { getCoreViewGroups: updatedViewGroups },
|
||||
} = await findCoreViewGroups({
|
||||
viewId,
|
||||
gqlFields: 'id fieldValue fieldMetadataId',
|
||||
gqlFields: 'id fieldValue',
|
||||
expectToFail: false,
|
||||
});
|
||||
|
||||
const viewGroupsForField = updatedViewGroups.filter(
|
||||
(vg) => vg.fieldMetadataId === fieldMetadataId,
|
||||
);
|
||||
expect(updatedViewGroups.length).toBe(4);
|
||||
const remainingFieldValues = updatedViewGroups
|
||||
.map((vg) => vg.fieldValue)
|
||||
.sort();
|
||||
const expectedRemainingFieldValues = [
|
||||
...updatedOptions.map((opt) => opt.value),
|
||||
'',
|
||||
].sort();
|
||||
|
||||
expect(viewGroupsForField.length).toBe(3);
|
||||
const remainingFieldValues = viewGroupsForField.map(
|
||||
(vg) => vg.fieldValue,
|
||||
);
|
||||
|
||||
expect(remainingFieldValues).not.toContain(initialOptions[0].value);
|
||||
expect(remainingFieldValues).not.toContain(initialOptions[1].value);
|
||||
expect(remainingFieldValues).toContain(initialOptions[2].value);
|
||||
expect(remainingFieldValues).toContain(initialOptions[3].value);
|
||||
expect(remainingFieldValues).toContain(initialOptions[4].value);
|
||||
expect(remainingFieldValues).toEqual(expectedRemainingFieldValues);
|
||||
});
|
||||
|
||||
it('should preserve view groups when no options are changed', async () => {
|
||||
it('preserves view groups when no options are changed', async () => {
|
||||
const initialOptions = generateOptions(3);
|
||||
const { fieldMetadataId, fieldOptions, viewId } =
|
||||
await createObjectWithSelectFieldAndView(fieldType, initialOptions);
|
||||
|
||||
for (const [index, option] of initialOptions.entries()) {
|
||||
await createOneCoreViewGroup({
|
||||
input: {
|
||||
viewId,
|
||||
fieldMetadataId,
|
||||
fieldValue: option.value,
|
||||
isVisible: true,
|
||||
position: index,
|
||||
},
|
||||
expectToFail: false,
|
||||
gqlFields: 'id fieldValue',
|
||||
});
|
||||
}
|
||||
await createObjectWithSelectFieldAndView(initialOptions);
|
||||
|
||||
await updateOneFieldMetadata({
|
||||
input: {
|
||||
@@ -378,41 +266,26 @@ describe('update-one-field-metadata-view-groups-side-effect-v2', () => {
|
||||
data: { getCoreViewGroups: updatedViewGroups },
|
||||
} = await findCoreViewGroups({
|
||||
viewId,
|
||||
gqlFields: 'id fieldValue fieldMetadataId',
|
||||
gqlFields: 'id fieldValue',
|
||||
expectToFail: false,
|
||||
});
|
||||
|
||||
const viewGroupsForField = updatedViewGroups.filter(
|
||||
(vg) => vg.fieldMetadataId === fieldMetadataId,
|
||||
);
|
||||
|
||||
expect(viewGroupsForField.length).toBe(3);
|
||||
const actualFieldValues = viewGroupsForField
|
||||
expect(updatedViewGroups.length).toBe(4);
|
||||
const actualFieldValues = updatedViewGroups
|
||||
.map((vg) => vg.fieldValue)
|
||||
.sort();
|
||||
const expectedFieldValues = initialOptions.map((opt) => opt.value).sort();
|
||||
const expectedFieldValues = [
|
||||
...initialOptions.map((opt) => opt.value),
|
||||
'',
|
||||
].sort();
|
||||
|
||||
expect(actualFieldValues).toEqual(expectedFieldValues);
|
||||
});
|
||||
|
||||
it('should handle adding new options while maintaining existing view groups', async () => {
|
||||
it('adds new view groups when new options are added', async () => {
|
||||
const initialOptions = generateOptions(3);
|
||||
const { fieldMetadataId, fieldOptions, viewId } =
|
||||
await createObjectWithSelectFieldAndView(fieldType, initialOptions);
|
||||
|
||||
for (const [index, option] of initialOptions.entries()) {
|
||||
await createOneCoreViewGroup({
|
||||
input: {
|
||||
viewId,
|
||||
fieldMetadataId,
|
||||
fieldValue: option.value,
|
||||
isVisible: true,
|
||||
position: index,
|
||||
},
|
||||
expectToFail: false,
|
||||
gqlFields: 'id fieldValue',
|
||||
});
|
||||
}
|
||||
await createObjectWithSelectFieldAndView(initialOptions);
|
||||
|
||||
const newOptions = generateOptions(6).slice(3);
|
||||
const updatedOptions = [...fieldOptions, ...newOptions];
|
||||
@@ -432,19 +305,18 @@ describe('update-one-field-metadata-view-groups-side-effect-v2', () => {
|
||||
data: { getCoreViewGroups: updatedViewGroups },
|
||||
} = await findCoreViewGroups({
|
||||
viewId,
|
||||
gqlFields: 'id fieldValue fieldMetadataId',
|
||||
gqlFields: 'id fieldValue',
|
||||
expectToFail: false,
|
||||
});
|
||||
|
||||
const viewGroupsForField = updatedViewGroups.filter(
|
||||
(vg) => vg.fieldMetadataId === fieldMetadataId,
|
||||
);
|
||||
|
||||
expect(viewGroupsForField.length).toBe(6);
|
||||
const actualFieldValues = viewGroupsForField
|
||||
expect(updatedViewGroups.length).toBe(7);
|
||||
const actualFieldValues = updatedViewGroups
|
||||
.map((vg) => vg.fieldValue)
|
||||
.sort();
|
||||
const expectedFieldValues = updatedOptions.map((opt) => opt.value).sort();
|
||||
const expectedFieldValues = [
|
||||
...updatedOptions.map((opt) => opt.value),
|
||||
'',
|
||||
].sort();
|
||||
|
||||
expect(actualFieldValues).toEqual(expectedFieldValues);
|
||||
});
|
||||
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
import { createOneFieldMetadata } from 'test/integration/metadata/suites/field-metadata/utils/create-one-field-metadata.util';
|
||||
import {
|
||||
type FieldMetadataComplexOption,
|
||||
FieldMetadataType,
|
||||
} from 'twenty-shared/types';
|
||||
|
||||
import { type CreateOneFieldMetadataInput } from 'src/engine/metadata-modules/field-metadata/dtos/create-field.input';
|
||||
|
||||
export const createOneSelectFieldMetadataForIntegrationTests = async ({
|
||||
input,
|
||||
expectToFail = false,
|
||||
}: {
|
||||
input: Partial<CreateOneFieldMetadataInput> & {
|
||||
objectMetadataId: string;
|
||||
options?: FieldMetadataComplexOption[];
|
||||
name?: string;
|
||||
};
|
||||
expectToFail?: boolean;
|
||||
}) => {
|
||||
const {
|
||||
data: {
|
||||
createOneField: { id: selectFieldMetadataId },
|
||||
},
|
||||
} = await createOneFieldMetadata({
|
||||
expectToFail,
|
||||
input: {
|
||||
name: 'normalField',
|
||||
type: FieldMetadataType.SELECT,
|
||||
label: 'Select Field',
|
||||
options: [
|
||||
{ label: 'Option 1', value: 'OPTION_1', color: 'blue', position: 0 },
|
||||
{ label: 'Option 2', value: 'OPTION_2', color: 'red', position: 1 },
|
||||
{ label: 'Option 3', value: 'OPTION_3', color: 'green', position: 2 },
|
||||
],
|
||||
...input,
|
||||
},
|
||||
gqlFields: 'id',
|
||||
});
|
||||
|
||||
return { selectFieldMetadataId };
|
||||
};
|
||||
+285
@@ -0,0 +1,285 @@
|
||||
import { faker } from '@faker-js/faker';
|
||||
import { createOneFieldMetadata } from 'test/integration/metadata/suites/field-metadata/utils/create-one-field-metadata.util';
|
||||
import { createOneObjectMetadata } from 'test/integration/metadata/suites/object-metadata/utils/create-one-object-metadata.util';
|
||||
import { deleteOneObjectMetadata } from 'test/integration/metadata/suites/object-metadata/utils/delete-one-object-metadata.util';
|
||||
import { getMockCreateObjectInput } from 'test/integration/metadata/suites/object-metadata/utils/generate-mock-create-object-metadata-input';
|
||||
import { updateOneObjectMetadata } from 'test/integration/metadata/suites/object-metadata/utils/update-one-object-metadata.util';
|
||||
import { findCoreViewGroups } from 'test/integration/metadata/suites/view-group/utils/find-core-view-groups.util';
|
||||
import { createOneCoreView } from 'test/integration/metadata/suites/view/utils/create-one-core-view.util';
|
||||
import { updateOneCoreView } from 'test/integration/metadata/suites/view/utils/update-one-core-view.util';
|
||||
import {
|
||||
FieldMetadataType,
|
||||
type EnumFieldMetadataType,
|
||||
} from 'twenty-shared/types';
|
||||
|
||||
import {
|
||||
type FieldMetadataComplexOption,
|
||||
type FieldMetadataDefaultOption,
|
||||
} from 'src/engine/metadata-modules/field-metadata/dtos/options.input';
|
||||
import { ViewType } from 'src/engine/metadata-modules/view/enums/view-type.enum';
|
||||
|
||||
type Option = FieldMetadataDefaultOption | FieldMetadataComplexOption;
|
||||
|
||||
const generateOption = (index: number): Option => ({
|
||||
label: `Option ${index}`,
|
||||
value: `OPTION_${index}`,
|
||||
color: 'green',
|
||||
position: index,
|
||||
});
|
||||
|
||||
const generateOptions = (length: number) =>
|
||||
Array.from({ length }, (_value, index) => generateOption(index));
|
||||
|
||||
const testFieldMetadataTypes: EnumFieldMetadataType[] = [
|
||||
FieldMetadataType.SELECT,
|
||||
FieldMetadataType.MULTI_SELECT,
|
||||
];
|
||||
|
||||
describe('update-one-view-view-groups-side-effect-v2', () => {
|
||||
let objectMetadataIdToDelete: string;
|
||||
|
||||
const createObjectWithTwoSelectFieldsAndKanbanViewAndGroupedTableView =
|
||||
async (initialOptions: Option[]) => {
|
||||
const singular = 'viewSideEffect';
|
||||
const plural = 'viewSideEffects';
|
||||
|
||||
const {
|
||||
data: { createOneObject },
|
||||
} = await createOneObjectMetadata({
|
||||
expectToFail: false,
|
||||
input: getMockCreateObjectInput({
|
||||
labelSingular: singular,
|
||||
labelPlural: plural,
|
||||
nameSingular: singular,
|
||||
namePlural: plural,
|
||||
isLabelSyncedWithName: false,
|
||||
}),
|
||||
});
|
||||
|
||||
objectMetadataIdToDelete = createOneObject.id;
|
||||
|
||||
const {
|
||||
data: { createOneField: createOneStatusField },
|
||||
} = await createOneFieldMetadata<typeof FieldMetadataType.SELECT>({
|
||||
expectToFail: false,
|
||||
input: {
|
||||
objectMetadataId: createOneObject.id,
|
||||
type: FieldMetadataType.SELECT,
|
||||
name: 'statusField',
|
||||
label: 'Status Field',
|
||||
isLabelSyncedWithName: true,
|
||||
options: initialOptions,
|
||||
},
|
||||
gqlFields: 'id options',
|
||||
});
|
||||
|
||||
const phaseFieldOptions = generateOptions(2);
|
||||
|
||||
const {
|
||||
data: { createOneField: createOnePhaseField },
|
||||
} = await createOneFieldMetadata<typeof FieldMetadataType.SELECT>({
|
||||
expectToFail: false,
|
||||
input: {
|
||||
objectMetadataId: createOneObject.id,
|
||||
type: FieldMetadataType.SELECT,
|
||||
name: 'phaseField',
|
||||
label: 'Phase Field',
|
||||
isLabelSyncedWithName: true,
|
||||
options: phaseFieldOptions,
|
||||
},
|
||||
gqlFields: 'id options',
|
||||
});
|
||||
|
||||
const {
|
||||
data: { createCoreView: view },
|
||||
} = await createOneCoreView({
|
||||
input: {
|
||||
id: faker.string.uuid(),
|
||||
icon: 'IconKanban',
|
||||
name: 'Kanban View',
|
||||
objectMetadataId: createOneObject.id,
|
||||
type: ViewType.KANBAN,
|
||||
mainGroupByFieldMetadataId: createOneStatusField.id,
|
||||
},
|
||||
expectToFail: false,
|
||||
gqlFields: 'id',
|
||||
});
|
||||
|
||||
const {
|
||||
data: { createCoreView: groupedTableView },
|
||||
} = await createOneCoreView({
|
||||
input: {
|
||||
id: faker.string.uuid(),
|
||||
icon: 'IconKanban',
|
||||
name: 'Grouped Table View',
|
||||
objectMetadataId: createOneObject.id,
|
||||
type: ViewType.TABLE,
|
||||
mainGroupByFieldMetadataId: createOneStatusField.id,
|
||||
},
|
||||
expectToFail: false,
|
||||
gqlFields: 'id',
|
||||
});
|
||||
|
||||
return {
|
||||
objectMetadataId: createOneObject.id,
|
||||
statusFieldMetadataId: createOneStatusField.id,
|
||||
phaseFieldMetadataId: createOnePhaseField.id,
|
||||
statusFieldOptions: createOneStatusField.options ?? [],
|
||||
viewId: view.id,
|
||||
groupedTableViewId: groupedTableView.id,
|
||||
phaseFieldOptions,
|
||||
};
|
||||
};
|
||||
|
||||
afterEach(async () => {
|
||||
await updateOneObjectMetadata({
|
||||
expectToFail: false,
|
||||
input: {
|
||||
idToUpdate: objectMetadataIdToDelete,
|
||||
updatePayload: {
|
||||
isActive: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
await deleteOneObjectMetadata({
|
||||
expectToFail: false,
|
||||
input: { idToDelete: objectMetadataIdToDelete },
|
||||
});
|
||||
});
|
||||
|
||||
describe.each(testFieldMetadataTypes)('%s field type', () => {
|
||||
it('should delete all view groups when mainGroupByFieldMetadataId is removed', async () => {
|
||||
const initialOptions = generateOptions(3);
|
||||
|
||||
const { groupedTableViewId } =
|
||||
await createObjectWithTwoSelectFieldsAndKanbanViewAndGroupedTableView(
|
||||
initialOptions,
|
||||
);
|
||||
|
||||
const {
|
||||
data: { getCoreViewGroups: initialViewGroups },
|
||||
} = await findCoreViewGroups({
|
||||
viewId: groupedTableViewId,
|
||||
gqlFields: 'id fieldValue',
|
||||
expectToFail: false,
|
||||
});
|
||||
|
||||
expect(initialViewGroups.length).toBe(4);
|
||||
|
||||
await updateOneCoreView({
|
||||
viewId: groupedTableViewId,
|
||||
input: {
|
||||
id: groupedTableViewId,
|
||||
mainGroupByFieldMetadataId: null,
|
||||
},
|
||||
gqlFields: 'id mainGroupByFieldMetadataId',
|
||||
expectToFail: false,
|
||||
});
|
||||
|
||||
const {
|
||||
data: { getCoreViewGroups: updatedViewGroups },
|
||||
} = await findCoreViewGroups({
|
||||
viewId: groupedTableViewId,
|
||||
gqlFields: 'id fieldValue',
|
||||
expectToFail: false,
|
||||
});
|
||||
|
||||
expect(updatedViewGroups.length).toBe(0);
|
||||
});
|
||||
|
||||
it('should delete and recreate view groups when mainGroupByFieldMetadataId changes', async () => {
|
||||
const initialOptions = generateOptions(3);
|
||||
|
||||
const { phaseFieldOptions, phaseFieldMetadataId, viewId } =
|
||||
await createObjectWithTwoSelectFieldsAndKanbanViewAndGroupedTableView(
|
||||
initialOptions,
|
||||
);
|
||||
|
||||
const {
|
||||
data: { getCoreViewGroups: initialViewGroups },
|
||||
} = await findCoreViewGroups({
|
||||
viewId,
|
||||
gqlFields: 'id fieldValue',
|
||||
expectToFail: false,
|
||||
});
|
||||
|
||||
expect(initialViewGroups.length).toBe(initialOptions.length + 1); // null option
|
||||
|
||||
await updateOneCoreView({
|
||||
viewId,
|
||||
input: {
|
||||
id: viewId,
|
||||
mainGroupByFieldMetadataId: phaseFieldMetadataId,
|
||||
},
|
||||
gqlFields: 'id mainGroupByFieldMetadataId',
|
||||
expectToFail: false,
|
||||
});
|
||||
|
||||
const {
|
||||
data: { getCoreViewGroups: updatedViewGroups },
|
||||
} = await findCoreViewGroups({
|
||||
viewId,
|
||||
gqlFields: 'id fieldValue',
|
||||
expectToFail: false,
|
||||
});
|
||||
|
||||
expect(updatedViewGroups.length).toBe(phaseFieldOptions.length + 1);
|
||||
|
||||
const actualFieldValues = updatedViewGroups
|
||||
.map((viewGroup) => viewGroup.fieldValue)
|
||||
.sort();
|
||||
const expectedFieldValues = [...phaseFieldOptions, { value: '' }]
|
||||
.map((option) => option.value)
|
||||
.sort();
|
||||
|
||||
expect(actualFieldValues).toEqual(expectedFieldValues);
|
||||
});
|
||||
|
||||
it('should preserve view groups when mainGroupByFieldMetadataId does not change', async () => {
|
||||
const initialOptions = generateOptions(3);
|
||||
|
||||
const { statusFieldOptions, viewId } =
|
||||
await createObjectWithTwoSelectFieldsAndKanbanViewAndGroupedTableView(
|
||||
initialOptions,
|
||||
);
|
||||
|
||||
const {
|
||||
data: { getCoreViewGroups: initialViewGroups },
|
||||
} = await findCoreViewGroups({
|
||||
viewId,
|
||||
gqlFields: 'id fieldValue',
|
||||
expectToFail: false,
|
||||
});
|
||||
|
||||
expect(initialViewGroups.length).toBe(statusFieldOptions.length + 1);
|
||||
|
||||
await updateOneCoreView({
|
||||
viewId,
|
||||
input: {
|
||||
id: viewId,
|
||||
name: 'Renamed Kanban View',
|
||||
},
|
||||
gqlFields: 'id name mainGroupByFieldMetadataId',
|
||||
expectToFail: false,
|
||||
});
|
||||
|
||||
const {
|
||||
data: { getCoreViewGroups: updatedViewGroups },
|
||||
} = await findCoreViewGroups({
|
||||
viewId,
|
||||
gqlFields: 'id fieldValue',
|
||||
expectToFail: false,
|
||||
});
|
||||
|
||||
expect(updatedViewGroups.length).toBe(statusFieldOptions.length + 1);
|
||||
const actualFieldValues = updatedViewGroups
|
||||
.map((viewGroup) => viewGroup.fieldValue)
|
||||
.sort();
|
||||
const expectedFieldValues = [...statusFieldOptions, { value: '' }]
|
||||
.map((option) => option.value)
|
||||
.sort();
|
||||
|
||||
expect(actualFieldValues).toEqual(expectedFieldValues);
|
||||
});
|
||||
});
|
||||
});
|
||||
+2
-2
@@ -24,8 +24,8 @@ exports[`View Group REST API POST /metadata/viewGroups should fail to create vie
|
||||
"errors": [
|
||||
{
|
||||
"code": "INVALID_VIEW_DATA",
|
||||
"message": "Field metadata not found",
|
||||
"userFriendlyMessage": "Field metadata not found",
|
||||
"message": "Field value is required",
|
||||
"userFriendlyMessage": "Field value is required",
|
||||
},
|
||||
],
|
||||
"flatEntityMinimalInformation": {
|
||||
|
||||
+29
-92
@@ -14,12 +14,8 @@ import {
|
||||
} from 'test/integration/rest/utils/view-rest-api.util';
|
||||
import { assertViewGroupStructure } from 'test/integration/utils/view-test.util';
|
||||
import { extractRecordIdsAndDatesAsExpectAny } from 'test/utils/extract-record-ids-and-dates-as-expect-any';
|
||||
import { jestExpectToBeDefined } from 'test/utils/jest-expect-to-be-defined.util.test';
|
||||
import { FieldMetadataType } from 'twenty-shared/types';
|
||||
|
||||
import { type ViewGroupDTO } from 'src/engine/metadata-modules/view-group/dtos/view-group.dto';
|
||||
import { type ViewGroupEntity } from 'src/engine/metadata-modules/view-group/entities/view-group.entity';
|
||||
|
||||
describe('View Group REST API', () => {
|
||||
let testObjectMetadataId: string;
|
||||
let testFieldMetadataId: string;
|
||||
@@ -47,9 +43,14 @@ describe('View Group REST API', () => {
|
||||
const createFieldInput = {
|
||||
name: 'testField',
|
||||
label: 'Test Field',
|
||||
type: FieldMetadataType.TEXT,
|
||||
type: FieldMetadataType.SELECT,
|
||||
objectMetadataId: testObjectMetadataId,
|
||||
isLabelSyncedWithName: true,
|
||||
options: [
|
||||
{ label: 'Option 1', value: 'OPTION_1', color: 'blue', position: 0 },
|
||||
{ label: 'Option 2', value: 'OPTION_2', color: 'red', position: 1 },
|
||||
{ label: 'Option 3', value: 'OPTION_3', color: 'green', position: 2 },
|
||||
],
|
||||
};
|
||||
|
||||
const {
|
||||
@@ -72,6 +73,7 @@ describe('View Group REST API', () => {
|
||||
const testView = await createTestViewWithRestApi({
|
||||
name: 'Test View for Group Integration',
|
||||
objectMetadataId: testObjectMetadataId,
|
||||
mainGroupByFieldMetadataId: testFieldMetadataId,
|
||||
});
|
||||
|
||||
testViewId = testView.id;
|
||||
@@ -106,17 +108,6 @@ describe('View Group REST API', () => {
|
||||
});
|
||||
|
||||
describe('GET /metadata/viewGroups', () => {
|
||||
it('should return empty array when no view groups exist', async () => {
|
||||
const response = await makeRestAPIRequest({
|
||||
method: 'get',
|
||||
path: `/metadata/viewGroups?viewId=${testViewId}`,
|
||||
bearer: APPLE_JANE_ADMIN_ACCESS_TOKEN,
|
||||
});
|
||||
|
||||
assertRestApiSuccessfulResponse(response);
|
||||
expect(response.body).toEqual([]);
|
||||
});
|
||||
|
||||
it('should return all view groups for workspace when no viewId provided', async () => {
|
||||
const response = await makeRestAPIRequest({
|
||||
method: 'get',
|
||||
@@ -129,16 +120,6 @@ describe('View Group REST API', () => {
|
||||
});
|
||||
|
||||
it('should return view groups for a specific view after creating one', async () => {
|
||||
const viewGroup = await createTestViewGroupWithRestApi({
|
||||
viewId: testViewId,
|
||||
fieldMetadataId: testFieldMetadataId,
|
||||
fieldValue: 'test-field-value',
|
||||
isVisible: true,
|
||||
position: 0,
|
||||
});
|
||||
|
||||
testViewGroupId = viewGroup.id;
|
||||
|
||||
const response = await makeRestAPIRequest({
|
||||
method: 'get',
|
||||
path: `/metadata/viewGroups?viewId=${testViewId}`,
|
||||
@@ -148,79 +129,36 @@ describe('View Group REST API', () => {
|
||||
assertRestApiSuccessfulResponse(response);
|
||||
expect(Array.isArray(response.body)).toBe(true);
|
||||
|
||||
const returnedViewGroup = response.body.find(
|
||||
(el: ViewGroupDTO) => el.id === viewGroup.id,
|
||||
);
|
||||
const returnedViewGroups = response.body;
|
||||
|
||||
jestExpectToBeDefined(returnedViewGroup);
|
||||
expect(returnedViewGroups).toHaveLength(4);
|
||||
// For a nullable field with three options, we expect groups for OPTION_1, OPTION_2, OPTION_3, and '' (empty string)
|
||||
const expectedFieldValues = ['OPTION_1', 'OPTION_2', 'OPTION_3', ''];
|
||||
|
||||
assertViewGroupStructure(returnedViewGroup, {
|
||||
id: viewGroup.id,
|
||||
viewId: testViewId,
|
||||
fieldValue: 'test-field-value',
|
||||
isVisible: true,
|
||||
position: 0,
|
||||
// Check structure and visibility for each group
|
||||
expectedFieldValues.forEach((expectedFieldValue) => {
|
||||
const group = returnedViewGroups.find(
|
||||
(group: any) => group.fieldValue === expectedFieldValue,
|
||||
);
|
||||
|
||||
expect(group).toBeDefined();
|
||||
expect(group.isVisible).toBe(true);
|
||||
expect(group.viewId).toBe(testViewId);
|
||||
});
|
||||
|
||||
testViewGroupId = viewGroup.id;
|
||||
});
|
||||
|
||||
it('should return multiple view groups for a view', async () => {
|
||||
const viewGroup1 = await createTestViewGroupWithRestApi({
|
||||
viewId: testViewId,
|
||||
fieldMetadataId: testFieldMetadataId,
|
||||
fieldValue: 'group-1',
|
||||
position: 0,
|
||||
});
|
||||
|
||||
const viewGroup2 = await createTestViewGroupWithRestApi({
|
||||
viewId: testViewId,
|
||||
fieldMetadataId: testFieldMetadataId,
|
||||
fieldValue: 'group-2',
|
||||
position: 1,
|
||||
});
|
||||
|
||||
const response = await makeRestAPIRequest({
|
||||
method: 'get',
|
||||
path: `/metadata/viewGroups?viewId=${testViewId}`,
|
||||
bearer: APPLE_JANE_ADMIN_ACCESS_TOKEN,
|
||||
});
|
||||
|
||||
assertRestApiSuccessfulResponse(response);
|
||||
expect(Array.isArray(response.body)).toBe(true);
|
||||
expect(response.body).toHaveLength(2);
|
||||
|
||||
const group1 = response.body.find(
|
||||
(group: ViewGroupEntity) => group.id === viewGroup1.id,
|
||||
);
|
||||
const group2 = response.body.find(
|
||||
(group: ViewGroupEntity) => group.id === viewGroup2.id,
|
||||
);
|
||||
|
||||
assertViewGroupStructure(group1, {
|
||||
fieldValue: 'group-1',
|
||||
position: 0,
|
||||
});
|
||||
|
||||
assertViewGroupStructure(group2, {
|
||||
fieldValue: 'group-2',
|
||||
position: 1,
|
||||
});
|
||||
|
||||
testViewGroupId = viewGroup2.id;
|
||||
});
|
||||
});
|
||||
|
||||
describe('GET /metadata/viewGroups/:id', () => {
|
||||
it('should return a specific view group by id', async () => {
|
||||
const viewGroup = await createTestViewGroupWithRestApi({
|
||||
viewId: testViewId,
|
||||
fieldMetadataId: testFieldMetadataId,
|
||||
fieldValue: 'specific-group',
|
||||
isVisible: false,
|
||||
const viewGroupsFromViewReponse = await makeRestAPIRequest({
|
||||
method: 'get',
|
||||
path: `/metadata/viewGroups?viewId=${testViewId}`,
|
||||
bearer: APPLE_JANE_ADMIN_ACCESS_TOKEN,
|
||||
});
|
||||
|
||||
testViewGroupId = viewGroup.id;
|
||||
const viewGroup = viewGroupsFromViewReponse.body.find(
|
||||
(group: any) => group.fieldValue === 'OPTION_1',
|
||||
);
|
||||
|
||||
const response = await makeRestAPIRequest({
|
||||
method: 'get',
|
||||
@@ -232,8 +170,8 @@ describe('View Group REST API', () => {
|
||||
assertViewGroupStructure(response.body, {
|
||||
id: viewGroup.id,
|
||||
viewId: testViewId,
|
||||
fieldValue: 'specific-group',
|
||||
isVisible: false,
|
||||
fieldValue: 'OPTION_1',
|
||||
isVisible: true,
|
||||
});
|
||||
|
||||
testViewGroupId = viewGroup.id;
|
||||
@@ -244,7 +182,6 @@ describe('View Group REST API', () => {
|
||||
it('should create a new view group', async () => {
|
||||
const viewGroupData = {
|
||||
viewId: testViewId,
|
||||
fieldMetadataId: testFieldMetadataId,
|
||||
fieldValue: 'new-group-value',
|
||||
isVisible: true,
|
||||
position: 5,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { createOneSelectFieldMetadataForIntegrationTests } from 'test/integration/metadata/suites/field-metadata/utils/create-one-select-field-metadata-for-integration-tests.util';
|
||||
import { createOneObjectMetadata } from 'test/integration/metadata/suites/object-metadata/utils/create-one-object-metadata.util';
|
||||
import { deleteOneObjectMetadata } from 'test/integration/metadata/suites/object-metadata/utils/delete-one-object-metadata.util';
|
||||
import { updateOneObjectMetadata } from 'test/integration/metadata/suites/object-metadata/utils/update-one-object-metadata.util';
|
||||
@@ -17,6 +18,7 @@ import { ViewType } from 'src/engine/metadata-modules/view/enums/view-type.enum'
|
||||
|
||||
describe('View REST API', () => {
|
||||
let testObjectMetadataId: string;
|
||||
let testSelectFieldMetadataId: string;
|
||||
let testViewId: string | undefined;
|
||||
|
||||
beforeAll(async () => {
|
||||
@@ -35,6 +37,15 @@ describe('View REST API', () => {
|
||||
});
|
||||
|
||||
testObjectMetadataId = objectMetadataId;
|
||||
|
||||
const { selectFieldMetadataId } =
|
||||
await createOneSelectFieldMetadataForIntegrationTests({
|
||||
input: {
|
||||
objectMetadataId,
|
||||
},
|
||||
});
|
||||
|
||||
testSelectFieldMetadataId = selectFieldMetadataId;
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
@@ -129,6 +140,7 @@ describe('View REST API', () => {
|
||||
isCompact: true,
|
||||
openRecordIn: ViewOpenRecordIn.SIDE_PANEL,
|
||||
objectMetadataId: testObjectMetadataId,
|
||||
mainGroupByFieldMetadataId: testSelectFieldMetadataId,
|
||||
});
|
||||
|
||||
testViewId = kanbanView.id;
|
||||
@@ -206,6 +218,7 @@ describe('View REST API', () => {
|
||||
type: ViewType.KANBAN,
|
||||
isCompact: true,
|
||||
openRecordIn: ViewOpenRecordIn.SIDE_PANEL,
|
||||
mainGroupByFieldMetadataId: testSelectFieldMetadataId,
|
||||
};
|
||||
|
||||
const response = await makeRestAPIRequest({
|
||||
|
||||
@@ -78,7 +78,6 @@ export const assertViewGroupStructure = (
|
||||
) => {
|
||||
expect(viewGroup).toBeDefined();
|
||||
expect(viewGroup.id).toBeDefined();
|
||||
expect(viewGroup.fieldMetadataId).toBeDefined();
|
||||
expect(viewGroup.viewId).toBeDefined();
|
||||
expect(viewGroup.fieldValue).toBeDefined();
|
||||
expect(typeof viewGroup.isVisible).toBe('boolean');
|
||||
|
||||
Reference in New Issue
Block a user