diff --git a/packages/twenty-front/src/modules/object-record/record-index/hooks/useHandleRecordGroupField.ts b/packages/twenty-front/src/modules/object-record/record-index/hooks/useHandleRecordGroupField.ts index 58a3543b5d1..473a62d7287 100644 --- a/packages/twenty-front/src/modules/object-record/record-index/hooks/useHandleRecordGroupField.ts +++ b/packages/twenty-front/src/modules/object-record/record-index/hooks/useHandleRecordGroupField.ts @@ -4,7 +4,7 @@ import { type FieldMetadataItem } from '@/object-metadata/types/FieldMetadataIte 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 { usePerformViewAPIUpdate } from '@/views/hooks/internal/usePerformViewAPIUpdate'; import { useGetViewFromPrefetchState } from '@/views/hooks/useGetViewFromPrefetchState'; import { useRefreshCoreViewsByObjectMetadataId } from '@/views/hooks/useRefreshCoreViewsByObjectMetadataId'; import { type ViewGroup } from '@/views/types/ViewGroup'; @@ -26,7 +26,7 @@ export const useHandleRecordGroupField = () => { const { setRecordGroupsFromViewGroups } = useSetRecordGroups(); - const { updateView } = usePersistView(); + const { performViewAPIUpdate } = usePerformViewAPIUpdate(); const { loadRecordIndexStates } = useLoadRecordIndexStates(); const { refreshCoreViewsByObjectMetadataId } = useRefreshCoreViewsByObjectMetadataId(); @@ -55,7 +55,7 @@ export const useHandleRecordGroupField = () => { return; } - const updatedViewResult = await updateView({ + const updatedViewResult = await performViewAPIUpdate({ id: view.id, input: { mainGroupByFieldMetadataId: fieldMetadataItem.id, @@ -133,7 +133,7 @@ export const useHandleRecordGroupField = () => { [ currentViewIdCallbackState, getViewFromPrefetchState, - updateView, + performViewAPIUpdate, setRecordGroupsFromViewGroups, objectMetadataItem, refreshCoreViewsByObjectMetadataId, @@ -162,14 +162,18 @@ export const useHandleRecordGroupField = () => { return; } - await updateView({ + await performViewAPIUpdate({ id: view.id, input: { mainGroupByFieldMetadataId: null, }, }); }, - [currentViewIdCallbackState, getViewFromPrefetchState, updateView], + [ + currentViewIdCallbackState, + getViewFromPrefetchState, + performViewAPIUpdate, + ], ); return { handleRecordGroupFieldChange, resetRecordGroupField }; diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-footer/hooks/useViewFieldAggregateOperation.tsx b/packages/twenty-front/src/modules/object-record/record-table/record-table-footer/hooks/useViewFieldAggregateOperation.tsx index 66bb50a250c..717ed5dd337 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-footer/hooks/useViewFieldAggregateOperation.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-footer/hooks/useViewFieldAggregateOperation.tsx @@ -3,7 +3,7 @@ import { RecordTableColumnAggregateFooterDropdownContext } from '@/object-record import { viewFieldAggregateOperationState } from '@/object-record/record-table/record-table-footer/states/viewFieldAggregateOperationState'; import { type ExtendedAggregateOperations } from '@/object-record/record-table/types/ExtendedAggregateOperations'; import { convertExtendedAggregateOperationToAggregateOperation } from '@/object-record/utils/convertExtendedAggregateOperationToAggregateOperation'; -import { usePersistViewField } from '@/views/hooks/internal/usePersistViewField'; +import { usePerformViewFieldAPIPersist } from '@/views/hooks/internal/usePerformViewFieldAPIPersist'; import { useGetCurrentViewOnly } from '@/views/hooks/useGetCurrentViewOnly'; import { useRefreshCoreViewsByObjectMetadataId } from '@/views/hooks/useRefreshCoreViewsByObjectMetadataId'; import { useContext } from 'react'; @@ -24,14 +24,14 @@ export const useViewFieldAggregateOperation = () => { useRefreshCoreViewsByObjectMetadataId(); const { objectMetadataItem } = useRecordIndexContextOrThrow(); - const { updateViewFields } = usePersistViewField(); + const { performViewFieldAPIUpdate } = usePerformViewFieldAPIPersist(); const updateViewFieldAggregateOperation = async ( aggregateOperation: ExtendedAggregateOperations | null, ) => { if (!currentViewField) { throw new Error('ViewField not found'); } - await updateViewFields([ + await performViewFieldAPIUpdate([ { input: { id: currentViewField.id, diff --git a/packages/twenty-front/src/modules/ui/input/components/IconPicker.tsx b/packages/twenty-front/src/modules/ui/input/components/IconPicker.tsx index f963294ff79..408c00f846e 100644 --- a/packages/twenty-front/src/modules/ui/input/components/IconPicker.tsx +++ b/packages/twenty-front/src/modules/ui/input/components/IconPicker.tsx @@ -9,6 +9,8 @@ import { SelectableList } from '@/ui/layout/selectable-list/components/Selectabl import { arrayToChunks } from '~/utils/array/arrayToChunks'; import { ICON_PICKER_DROPDOWN_CONTENT_WIDTH } from '@/ui/input/components/constants/IconPickerDropdownContentWidth'; +import { IconPickerScrollEffect } from '@/ui/input/hooks/IconPickerScrollEffect'; +import { iconPickerVisibleCountState } from '@/ui/input/states/iconPickerVisibleCountState'; import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent'; import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown'; import { type DropdownOffset } from '@/ui/layout/dropdown/types/DropdownOffset'; @@ -25,8 +27,6 @@ import { type IconButtonVariant, LightIconButton, } from 'twenty-ui/input'; -import { IconPickerScrollEffect } from '@/ui/input/hooks/IconPickerScrollEffect'; -import { iconPickerVisibleCountState } from '@/ui/input/states/iconPickerVisibleCountState'; export type IconPickerProps = { disabled?: boolean; @@ -86,7 +86,7 @@ const convertIconKeyToLabel = (iconKey: string) => type IconPickerIconProps = { iconKey: string; - onClick: () => void; + onSelect: () => void; selectedIconKey?: string; Icon: IconComponent; focusedIconKey?: string; @@ -94,7 +94,7 @@ type IconPickerIconProps = { const IconPickerIcon = ({ iconKey, - onClick, + onSelect, selectedIconKey, Icon, focusedIconKey, @@ -106,7 +106,7 @@ const IconPickerIcon = ({ return ( - + @@ -301,7 +301,7 @@ export const IconPicker = ({ { + onSelect={() => { onChange({ iconKey, Icon: getIcon(iconKey) }); closeDropdown(dropdownId); }} diff --git a/packages/twenty-front/src/modules/ui/layout/resizable-panel/components/ResizablePanelEdge.tsx b/packages/twenty-front/src/modules/ui/layout/resizable-panel/components/ResizablePanelEdge.tsx index b3e648d8a7b..7c75379d585 100644 --- a/packages/twenty-front/src/modules/ui/layout/resizable-panel/components/ResizablePanelEdge.tsx +++ b/packages/twenty-front/src/modules/ui/layout/resizable-panel/components/ResizablePanelEdge.tsx @@ -19,7 +19,7 @@ const StyledEdge = styled.div` side === 'right' ? 'right' : 'left'}: -${RESIZE_EDGE_WIDTH_PX / 2}px; width: ${RESIZE_EDGE_WIDTH_PX}px; cursor: col-resize; - z-index: 100; + display: flex; align-items: center; justify-content: center; diff --git a/packages/twenty-front/src/modules/views/hooks/internal/usePersistView.ts b/packages/twenty-front/src/modules/views/hooks/internal/usePerformViewAPIPersist.ts similarity index 76% rename from packages/twenty-front/src/modules/views/hooks/internal/usePersistView.ts rename to packages/twenty-front/src/modules/views/hooks/internal/usePerformViewAPIPersist.ts index 700c6b67263..a9a6f1eb602 100644 --- a/packages/twenty-front/src/modules/views/hooks/internal/usePersistView.ts +++ b/packages/twenty-front/src/modules/views/hooks/internal/usePerformViewAPIPersist.ts @@ -12,16 +12,13 @@ import { v4 } from 'uuid'; import { type CreateCoreViewMutationVariables, type DestroyCoreViewMutationVariables, - type UpdateCoreViewMutationVariables, useCreateCoreViewMutation, useDestroyCoreViewMutation, - useUpdateCoreViewMutation, ViewType, } from '~/generated/graphql'; -export const usePersistView = () => { +export const usePerformViewAPIPersist = () => { const [createCoreViewMutation] = useCreateCoreViewMutation(); - const [updateCoreViewMutation] = useUpdateCoreViewMutation(); const [destroyCoreViewMutation] = useDestroyCoreViewMutation(); const { triggerViewGroupOptimisticEffectAtViewCreation } = useViewsSideEffectsOnViewGroups(); @@ -29,7 +26,7 @@ export const usePersistView = () => { const { handleMetadataError } = useMetadataErrorHandler(); const { enqueueErrorSnackBar } = useSnackBar(); - const createView = useCallback( + const performViewAPICreate = useCallback( async ( variables: CreateCoreViewMutationVariables, objectMetadataItemId: string, @@ -93,41 +90,7 @@ export const usePersistView = () => { ], ); - const updateView = useCallback( - async ( - variables: UpdateCoreViewMutationVariables, - ): Promise< - MetadataRequestResult>> - > => { - try { - const result = await updateCoreViewMutation({ - variables, - }); - - return { - status: 'successful', - response: result, - }; - } catch (error) { - if (error instanceof ApolloError) { - handleMetadataError(error, { - primaryMetadataName: 'view', - operationType: CrudOperationType.UPDATE, - }); - } else { - enqueueErrorSnackBar({ message: t`An error occurred.` }); - } - - return { - status: 'failed', - error, - }; - } - }, - [updateCoreViewMutation, handleMetadataError, enqueueErrorSnackBar], - ); - - const destroyView = useCallback( + const performViewAPIDestroy = useCallback( async ( variables: DestroyCoreViewMutationVariables, ): Promise< @@ -162,8 +125,7 @@ export const usePersistView = () => { ); return { - createView, - updateView, - destroyView, + performViewAPICreate, + performViewAPIDestroy, }; }; diff --git a/packages/twenty-front/src/modules/views/hooks/internal/usePerformViewAPIUpdate.ts b/packages/twenty-front/src/modules/views/hooks/internal/usePerformViewAPIUpdate.ts new file mode 100644 index 00000000000..3d51b024394 --- /dev/null +++ b/packages/twenty-front/src/modules/views/hooks/internal/usePerformViewAPIUpdate.ts @@ -0,0 +1,86 @@ +import { useCallback } from 'react'; + +import { useMetadataErrorHandler } from '@/metadata-error-handler/hooks/useMetadataErrorHandler'; +import { type MetadataRequestResult } from '@/object-metadata/types/MetadataRequestResult.type'; +import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar'; +import { coreViewsState } from '@/views/states/coreViewState'; +import { ApolloError } from '@apollo/client'; +import { t } from '@lingui/core/macro'; +import { useSetRecoilState } from 'recoil'; +import { CrudOperationType } from 'twenty-shared/types'; +import { upsertPropertiesOfItemIntoArrayOfObjectsComparingId } from 'twenty-shared/utils'; +import { + type CoreView, + type UpdateCoreViewMutationVariables, + useUpdateCoreViewMutation, +} from '~/generated/graphql'; + +export const usePerformViewAPIUpdate = () => { + const [updateCoreViewMutation] = useUpdateCoreViewMutation(); + + const { handleMetadataError } = useMetadataErrorHandler(); + const { enqueueErrorSnackBar } = useSnackBar(); + + const setCoreViews = useSetRecoilState(coreViewsState); + + const performViewAPIUpdate = useCallback( + async ( + variables: UpdateCoreViewMutationVariables, + ): Promise< + MetadataRequestResult>> + > => { + try { + setCoreViews((currentCoreViews) => + upsertPropertiesOfItemIntoArrayOfObjectsComparingId( + currentCoreViews, + { + ...variables.input, + id: variables.id, + } as CoreView, + ), + ); + + const result = await updateCoreViewMutation({ + variables, + }); + + setCoreViews((currentCoreViews) => + upsertPropertiesOfItemIntoArrayOfObjectsComparingId( + currentCoreViews, + { + ...result.data?.updateCoreView, + id: variables.id, + } as CoreView, + ), + ); + + return { + status: 'successful', + response: result, + }; + } catch (error) { + if (error instanceof ApolloError) { + handleMetadataError(error, { + primaryMetadataName: 'view', + operationType: CrudOperationType.UPDATE, + }); + } else { + enqueueErrorSnackBar({ message: t`An error occurred.` }); + } + + return { + status: 'failed', + error, + }; + } + }, + [ + setCoreViews, + updateCoreViewMutation, + handleMetadataError, + enqueueErrorSnackBar, + ], + ); + + return { performViewAPIUpdate }; +}; diff --git a/packages/twenty-front/src/modules/views/hooks/internal/usePersistViewField.ts b/packages/twenty-front/src/modules/views/hooks/internal/usePerformViewFieldAPIPersist.ts similarity index 95% rename from packages/twenty-front/src/modules/views/hooks/internal/usePersistViewField.ts rename to packages/twenty-front/src/modules/views/hooks/internal/usePerformViewFieldAPIPersist.ts index f8614fcb1ff..6932f6198c2 100644 --- a/packages/twenty-front/src/modules/views/hooks/internal/usePersistViewField.ts +++ b/packages/twenty-front/src/modules/views/hooks/internal/usePerformViewFieldAPIPersist.ts @@ -18,7 +18,7 @@ import { useDestroyCoreViewFieldMutation, useUpdateCoreViewFieldMutation, } from '~/generated/graphql'; -export const usePersistViewField = () => { +export const usePerformViewFieldAPIPersist = () => { const { triggerViewFieldOptimisticEffect } = useTriggerViewFieldOptimisticEffect(); @@ -31,7 +31,7 @@ export const usePersistViewField = () => { const { handleMetadataError } = useMetadataErrorHandler(); const { enqueueErrorSnackBar } = useSnackBar(); - const createViewFields = useCallback( + const performViewFieldAPICreate = useCallback( async ( createCoreViewFieldInputs: CreateManyCoreViewFieldsMutationVariables, ): Promise< @@ -92,7 +92,7 @@ export const usePersistViewField = () => { ], ); - const updateViewFields = useCallback( + const performViewFieldAPIUpdate = useCallback( async ( createCoreViewFieldInputs: UpdateCoreViewFieldMutationVariables[], ): Promise< @@ -154,7 +154,7 @@ export const usePersistViewField = () => { ], ); - const deleteViewFields = useCallback( + const performViewFieldAPIDelete = useCallback( async ( deleteCoreViewFieldInputs: DeleteCoreViewFieldMutationVariables[], ): Promise< @@ -216,7 +216,7 @@ export const usePersistViewField = () => { ], ); - const destroyViewFields = useCallback( + const performViewFieldAPIDestroy = useCallback( async ( destroyCoreViewFieldInputs: DestroyCoreViewFieldMutationVariables[], ): Promise< @@ -264,9 +264,9 @@ export const usePersistViewField = () => { ); return { - createViewFields, - updateViewFields, - deleteViewFields, - destroyViewFields, + performViewFieldAPICreate, + performViewFieldAPIUpdate, + performViewFieldAPIDelete, + performViewFieldAPIDestroy, }; }; diff --git a/packages/twenty-front/src/modules/views/hooks/internal/usePersistViewFilter.ts b/packages/twenty-front/src/modules/views/hooks/internal/usePerformViewFilterAPIPersist.ts similarity index 95% rename from packages/twenty-front/src/modules/views/hooks/internal/usePersistViewFilter.ts rename to packages/twenty-front/src/modules/views/hooks/internal/usePerformViewFilterAPIPersist.ts index ec61b7b3ee9..d7ea4734331 100644 --- a/packages/twenty-front/src/modules/views/hooks/internal/usePersistViewFilter.ts +++ b/packages/twenty-front/src/modules/views/hooks/internal/usePerformViewFilterAPIPersist.ts @@ -19,7 +19,7 @@ import { useUpdateCoreViewFilterMutation, } from '~/generated/graphql'; -export const usePersistViewFilterRecords = () => { +export const usePerformViewFilterAPIPersist = () => { const { triggerViewFilterOptimisticEffect } = useTriggerViewFilterOptimisticEffect(); const [createCoreViewFilterMutation] = useCreateCoreViewFilterMutation(); @@ -30,7 +30,7 @@ export const usePersistViewFilterRecords = () => { const { handleMetadataError } = useMetadataErrorHandler(); const { enqueueErrorSnackBar } = useSnackBar(); - const createViewFilters = useCallback( + const performViewFilterAPICreate = useCallback( async ( createCoreViewFilterInputs: CreateCoreViewFilterMutationVariables[], ): Promise< @@ -92,7 +92,7 @@ export const usePersistViewFilterRecords = () => { ], ); - const updateViewFilters = useCallback( + const performViewFilterAPIUpdate = useCallback( async ( updateCoreViewFilterInputs: UpdateCoreViewFilterMutationVariables[], ): Promise< @@ -154,7 +154,7 @@ export const usePersistViewFilterRecords = () => { ], ); - const deleteViewFilters = useCallback( + const performViewFilterAPIDelete = useCallback( async ( deleteCoreViewFilterInputs: DeleteCoreViewFilterMutationVariables[], ): Promise< @@ -216,7 +216,7 @@ export const usePersistViewFilterRecords = () => { ], ); - const destroyViewFilters = useCallback( + const performViewFilterAPIDestroy = useCallback( async ( destroyCoreViewFilterInputs: DestroyCoreViewFilterMutationVariables[], ): Promise< @@ -264,9 +264,9 @@ export const usePersistViewFilterRecords = () => { ); return { - createViewFilters, - updateViewFilters, - deleteViewFilters, - destroyViewFilters, + performViewFilterAPICreate, + performViewFilterAPIUpdate, + performViewFilterAPIDelete, + performViewFilterAPIDestroy, }; }; diff --git a/packages/twenty-front/src/modules/views/hooks/internal/usePersistViewFilterGroup.ts b/packages/twenty-front/src/modules/views/hooks/internal/usePerformViewFilterGroupAPIPersist.ts similarity index 92% rename from packages/twenty-front/src/modules/views/hooks/internal/usePersistViewFilterGroup.ts rename to packages/twenty-front/src/modules/views/hooks/internal/usePerformViewFilterGroupAPIPersist.ts index b4e2282d9b2..2269d68710a 100644 --- a/packages/twenty-front/src/modules/views/hooks/internal/usePersistViewFilterGroup.ts +++ b/packages/twenty-front/src/modules/views/hooks/internal/usePerformViewFilterGroupAPIPersist.ts @@ -9,7 +9,7 @@ import { useApolloClient } from '@apollo/client'; import { isDefined } from 'twenty-shared/utils'; import { type CoreViewFilterGroup } from '~/generated/graphql'; -export const usePersistViewFilterGroupRecords = () => { +export const usePerformViewFilterGroupAPIPersist = () => { const apolloClient = useApolloClient(); const createCoreViewFilterGroupRecord = useCallback( @@ -39,7 +39,7 @@ export const usePersistViewFilterGroupRecords = () => { [apolloClient], ); - const createViewFilterGroups = useCallback( + const performViewFilterGroupAPICreate = useCallback( async ( viewFilterGroupsToCreate: ViewFilterGroup[], view: Pick, @@ -80,7 +80,7 @@ export const usePersistViewFilterGroupRecords = () => { [createCoreViewFilterGroupRecord], ); - const updateViewFilterGroups = useCallback( + const performViewFilterGroupAPIUpdate = useCallback( (viewFilterGroupsToUpdate: ViewFilterGroup[]) => { if (!viewFilterGroupsToUpdate.length) return; return Promise.all( @@ -104,7 +104,7 @@ export const usePersistViewFilterGroupRecords = () => { [apolloClient], ); - const deleteViewFilterGroups = useCallback( + const performViewFilterGroupAPIDelete = useCallback( (viewFilterGroupIdsToDelete: string[]) => { if (!viewFilterGroupIdsToDelete.length) return; return Promise.all( @@ -122,8 +122,8 @@ export const usePersistViewFilterGroupRecords = () => { ); return { - createViewFilterGroups, - updateViewFilterGroups, - deleteViewFilterGroups, + performViewFilterGroupAPICreate, + performViewFilterGroupAPIUpdate, + performViewFilterGroupAPIDelete, }; }; diff --git a/packages/twenty-front/src/modules/views/hooks/internal/usePersistViewGroup.ts b/packages/twenty-front/src/modules/views/hooks/internal/usePerformViewGroupAPIPersist.ts similarity index 95% rename from packages/twenty-front/src/modules/views/hooks/internal/usePersistViewGroup.ts rename to packages/twenty-front/src/modules/views/hooks/internal/usePerformViewGroupAPIPersist.ts index d7dd66f4baf..6d6437a95e3 100644 --- a/packages/twenty-front/src/modules/views/hooks/internal/usePersistViewGroup.ts +++ b/packages/twenty-front/src/modules/views/hooks/internal/usePerformViewGroupAPIPersist.ts @@ -13,7 +13,7 @@ import { useUpdateCoreViewGroupMutation, } from '~/generated/graphql'; -export const usePersistViewGroupRecords = () => { +export const usePerformViewGroupAPIPersist = () => { const { triggerViewGroupOptimisticEffect } = useTriggerViewGroupOptimisticEffect(); @@ -22,7 +22,7 @@ export const usePersistViewGroupRecords = () => { const { handleMetadataError } = useMetadataErrorHandler(); const { enqueueErrorSnackBar } = useSnackBar(); - const updateViewGroups = useCallback( + const performViewGroupAPIUpdate = useCallback( async ( updateCoreViewGroupInputs: UpdateCoreViewGroupMutationVariables[], ): Promise< @@ -85,6 +85,6 @@ export const usePersistViewGroupRecords = () => { ); return { - updateViewGroups, + performViewGroupAPIUpdate, }; }; diff --git a/packages/twenty-front/src/modules/views/hooks/internal/usePersistViewSort.ts b/packages/twenty-front/src/modules/views/hooks/internal/usePerformViewSortAPIPersist.ts similarity index 91% rename from packages/twenty-front/src/modules/views/hooks/internal/usePersistViewSort.ts rename to packages/twenty-front/src/modules/views/hooks/internal/usePerformViewSortAPIPersist.ts index c300ec37cdc..ae9fd481eea 100644 --- a/packages/twenty-front/src/modules/views/hooks/internal/usePersistViewSort.ts +++ b/packages/twenty-front/src/modules/views/hooks/internal/usePerformViewSortAPIPersist.ts @@ -10,13 +10,13 @@ import { useApolloClient } from '@apollo/client'; import { isDefined } from 'twenty-shared/utils'; import { type CoreViewSort } from '~/generated/graphql'; -export const usePersistViewSortRecords = () => { +export const usePerformViewSortAPIPersist = () => { const apolloClient = useApolloClient(); const { triggerViewSortOptimisticEffect } = useTriggerViewSortOptimisticEffect(); - const createViewSorts = useCallback( + const performViewSortAPICreate = useCallback( ( viewSortsToCreate: CoreViewSortEssential[], view: Pick, @@ -49,7 +49,7 @@ export const usePersistViewSortRecords = () => { [apolloClient, triggerViewSortOptimisticEffect], ); - const updateViewSorts = useCallback( + const performViewSortAPIUpdate = useCallback( (viewSortsToUpdate: CoreViewSortEssential[]) => { if (!viewSortsToUpdate.length) return; return Promise.all( @@ -77,7 +77,7 @@ export const usePersistViewSortRecords = () => { [apolloClient, triggerViewSortOptimisticEffect], ); - const deleteViewSorts = useCallback( + const performViewSortAPIDelete = useCallback( (viewSortsToDelete: Pick[]) => { if (!viewSortsToDelete.length) return; return Promise.all( @@ -100,8 +100,8 @@ export const usePersistViewSortRecords = () => { ); return { - createViewSorts, - updateViewSorts, - deleteViewSorts, + performViewSortAPICreate, + performViewSortAPIUpdate, + performViewSortAPIDelete, }; }; diff --git a/packages/twenty-front/src/modules/views/hooks/useCreateViewFromCurrentView.ts b/packages/twenty-front/src/modules/views/hooks/useCreateViewFromCurrentView.ts index b17abae067e..5f28265c71b 100644 --- a/packages/twenty-front/src/modules/views/hooks/useCreateViewFromCurrentView.ts +++ b/packages/twenty-front/src/modules/views/hooks/useCreateViewFromCurrentView.ts @@ -7,11 +7,11 @@ import { currentRecordSortsComponentState } from '@/object-record/record-sort/st 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 { 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 { usePersistViewSortRecords } from '@/views/hooks/internal/usePersistViewSort'; +import { usePerformViewAPIPersist } from '@/views/hooks/internal/usePerformViewAPIPersist'; +import { usePerformViewFieldAPIPersist } from '@/views/hooks/internal/usePerformViewFieldAPIPersist'; +import { usePerformViewFilterAPIPersist } from '@/views/hooks/internal/usePerformViewFilterAPIPersist'; +import { usePerformViewFilterGroupAPIPersist } from '@/views/hooks/internal/usePerformViewFilterGroupAPIPersist'; +import { usePerformViewSortAPIPersist } from '@/views/hooks/internal/usePerformViewSortAPIPersist'; import { useRefreshCoreViewsByObjectMetadataId } from '@/views/hooks/useRefreshCoreViewsByObjectMetadataId'; import { coreViewFromViewIdFamilySelector } from '@/views/states/selectors/coreViewFromViewIdFamilySelector'; import { type GraphQLView } from '@/views/types/GraphQLView'; @@ -29,7 +29,7 @@ import { ViewCalendarLayout } from '~/generated-metadata/graphql'; import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull'; export const useCreateViewFromCurrentView = (viewBarComponentId?: string) => { - const { createView } = usePersistView(); + const { performViewAPICreate } = usePerformViewAPIPersist(); const currentViewIdCallbackState = useRecoilComponentCallbackState( contextStoreCurrentViewIdComponentState, @@ -40,13 +40,14 @@ export const useCreateViewFromCurrentView = (viewBarComponentId?: string) => { anyFieldFilterValueComponentState, ); - const { createViewFields } = usePersistViewField(); + const { performViewFieldAPICreate } = usePerformViewFieldAPIPersist(); - const { createViewSorts } = usePersistViewSortRecords(); + const { performViewSortAPICreate } = usePerformViewSortAPIPersist(); - const { createViewFilters } = usePersistViewFilterRecords(); + const { performViewFilterAPICreate } = usePerformViewFilterAPIPersist(); - const { createViewFilterGroups } = usePersistViewFilterGroupRecords(); + const { performViewFilterGroupAPICreate } = + usePerformViewFilterGroupAPIPersist(); const { objectMetadataItem } = useRecordIndexContextOrThrow(); @@ -113,7 +114,7 @@ export const useCreateViewFromCurrentView = (viewBarComponentId?: string) => { const viewType = type ?? sourceView.type; - const result = await createView( + const result = await performViewAPICreate( { input: { id: id ?? v4(), @@ -160,7 +161,7 @@ export const useCreateViewFromCurrentView = (viewBarComponentId?: string) => { throw new Error('Failed to create view'); } - const fieldResult = await createViewFields({ + const fieldResult = await performViewFieldAPICreate({ inputs: sourceView.viewFields.map( ({ __typename, id: _id, ...viewField }) => ({ ...viewField, @@ -202,7 +203,7 @@ export const useCreateViewFromCurrentView = (viewBarComponentId?: string) => { id: v4(), })); - await createViewFilterGroups(viewFilterGroupsToCreate, { + await performViewFilterGroupAPICreate(viewFilterGroupsToCreate, { id: newViewId, }); @@ -221,13 +222,15 @@ export const useCreateViewFromCurrentView = (viewBarComponentId?: string) => { }), ); - const filterResult = await createViewFilters(createViewFilterInputs); + const filterResult = await performViewFilterAPICreate( + createViewFilterInputs, + ); if (filterResult.status === 'failed') { return undefined; } - await createViewSorts(viewSortsToCreate, { id: newViewId }); + await performViewSortAPICreate(viewSortsToCreate, { id: newViewId }); } await refreshCoreViewsByObjectMetadataId(objectMetadataItem.id); @@ -236,17 +239,17 @@ export const useCreateViewFromCurrentView = (viewBarComponentId?: string) => { }, [ currentViewIdCallbackState, - createView, + performViewAPICreate, anyFieldFilterValue, objectMetadataItem, - createViewFields, + performViewFieldAPICreate, refreshCoreViewsByObjectMetadataId, currentRecordFilterGroups, currentRecordFilters, currentRecordSorts, - createViewFilterGroups, - createViewFilters, - createViewSorts, + performViewFilterGroupAPICreate, + performViewFilterAPICreate, + performViewSortAPICreate, ], ); diff --git a/packages/twenty-front/src/modules/views/hooks/useSaveAnyFieldFilterToView.ts b/packages/twenty-front/src/modules/views/hooks/useSaveAnyFieldFilterToView.ts index 3d5d98258de..981e25a8a2f 100644 --- a/packages/twenty-front/src/modules/views/hooks/useSaveAnyFieldFilterToView.ts +++ b/packages/twenty-front/src/modules/views/hooks/useSaveAnyFieldFilterToView.ts @@ -1,6 +1,6 @@ import { anyFieldFilterValueComponentState } from '@/object-record/record-filter/states/anyFieldFilterValueComponentState'; import { useRecoilComponentCallbackState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackState'; -import { usePersistView } from '@/views/hooks/internal/usePersistView'; +import { usePerformViewAPIUpdate } from '@/views/hooks/internal/usePerformViewAPIUpdate'; import { useCanPersistViewChanges } from '@/views/hooks/useCanPersistViewChanges'; import { useGetCurrentViewOnly } from '@/views/hooks/useGetCurrentViewOnly'; import { convertUpdateViewInputToCore } from '@/views/utils/convertUpdateViewInputToCore'; @@ -9,7 +9,7 @@ import { isDefined } from 'twenty-shared/utils'; export const useSaveAnyFieldFilterToView = () => { const { canPersistChanges } = useCanPersistViewChanges(); - const { updateView } = usePersistView(); + const { performViewAPIUpdate } = usePerformViewAPIUpdate(); const { currentView } = useGetCurrentViewOnly(); @@ -31,7 +31,7 @@ export const useSaveAnyFieldFilterToView = () => { .getValue(); if (currentAnyFieldFilterValue !== currentViewAnyFieldFilterValue) { - await updateView({ + await performViewAPIUpdate({ id: currentView.id, input: convertUpdateViewInputToCore({ ...currentView, @@ -42,7 +42,7 @@ export const useSaveAnyFieldFilterToView = () => { }, [ canPersistChanges, - updateView, + performViewAPIUpdate, anyFieldFilterValueCallbackState, currentView, ], diff --git a/packages/twenty-front/src/modules/views/hooks/useSaveCurrentViewFields.ts b/packages/twenty-front/src/modules/views/hooks/useSaveCurrentViewFields.ts index d54635d5869..ced04f275a7 100644 --- a/packages/twenty-front/src/modules/views/hooks/useSaveCurrentViewFields.ts +++ b/packages/twenty-front/src/modules/views/hooks/useSaveCurrentViewFields.ts @@ -2,7 +2,7 @@ import { useRecoilCallback } from 'recoil'; import { contextStoreCurrentViewIdComponentState } from '@/context-store/states/contextStoreCurrentViewIdComponentState'; import { useRecoilComponentCallbackState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackState'; -import { usePersistViewField } from '@/views/hooks/internal/usePersistViewField'; +import { usePerformViewFieldAPIPersist } from '@/views/hooks/internal/usePerformViewFieldAPIPersist'; import { useCanPersistViewChanges } from '@/views/hooks/useCanPersistViewChanges'; import { useGetViewFromPrefetchState } from '@/views/hooks/useGetViewFromPrefetchState'; import { type ViewField } from '@/views/types/ViewField'; @@ -15,7 +15,8 @@ import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull'; export const useSaveCurrentViewFields = () => { const { canPersistChanges } = useCanPersistViewChanges(); - const { createViewFields, updateViewFields } = usePersistViewField(); + const { performViewFieldAPICreate, performViewFieldAPIUpdate } = + usePerformViewFieldAPIPersist(); const { getViewFromPrefetchState } = useGetViewFromPrefetchState(); @@ -123,16 +124,16 @@ export const useSaveCurrentViewFields = () => { ); await Promise.all([ - createViewFields({ inputs: viewFieldsToCreate }), - updateViewFields(viewFieldsToUpdate), + performViewFieldAPICreate({ inputs: viewFieldsToCreate }), + performViewFieldAPIUpdate(viewFieldsToUpdate), ]); }, [ canPersistChanges, - createViewFields, + performViewFieldAPICreate, currentViewIdCallbackState, getViewFromPrefetchState, - updateViewFields, + performViewFieldAPIUpdate, ], ); diff --git a/packages/twenty-front/src/modules/views/hooks/useSaveCurrentViewGroups.ts b/packages/twenty-front/src/modules/views/hooks/useSaveCurrentViewGroups.ts index c7ee2ef48db..28668cf0054 100644 --- a/packages/twenty-front/src/modules/views/hooks/useSaveCurrentViewGroups.ts +++ b/packages/twenty-front/src/modules/views/hooks/useSaveCurrentViewGroups.ts @@ -2,7 +2,7 @@ import { useRecoilCallback } from 'recoil'; import { contextStoreCurrentViewIdComponentState } from '@/context-store/states/contextStoreCurrentViewIdComponentState'; import { useRecoilComponentCallbackState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackState'; -import { usePersistViewGroupRecords } from '@/views/hooks/internal/usePersistViewGroup'; +import { usePerformViewGroupAPIPersist } from '@/views/hooks/internal/usePerformViewGroupAPIPersist'; import { useCanPersistViewChanges } from '@/views/hooks/useCanPersistViewChanges'; import { useGetViewFromPrefetchState } from '@/views/hooks/useGetViewFromPrefetchState'; import { type ViewGroup } from '@/views/types/ViewGroup'; @@ -12,7 +12,7 @@ import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull'; export const useSaveCurrentViewGroups = () => { const { canPersistChanges } = useCanPersistViewChanges(); - const { updateViewGroups } = usePersistViewGroupRecords(); + const { performViewGroupAPIUpdate } = usePerformViewGroupAPIPersist(); const { getViewFromPrefetchState } = useGetViewFromPrefetchState(); @@ -67,7 +67,7 @@ export const useSaveCurrentViewGroups = () => { return; } - await updateViewGroups([ + await performViewGroupAPIUpdate([ { input: { id: existingField.id, @@ -84,7 +84,7 @@ export const useSaveCurrentViewGroups = () => { canPersistChanges, currentViewIdCallbackState, getViewFromPrefetchState, - updateViewGroups, + performViewGroupAPIUpdate, ], ); @@ -154,13 +154,13 @@ export const useSaveCurrentViewGroups = () => { throw new Error('mainGroupByFieldMetadataId is required'); } - await updateViewGroups(viewGroupsToUpdate); + await performViewGroupAPIUpdate(viewGroupsToUpdate); }, [ canPersistChanges, currentViewIdCallbackState, getViewFromPrefetchState, - updateViewGroups, + performViewGroupAPIUpdate, ], ); diff --git a/packages/twenty-front/src/modules/views/hooks/useSaveRecordFilterGroupsToViewFilterGroups.ts b/packages/twenty-front/src/modules/views/hooks/useSaveRecordFilterGroupsToViewFilterGroups.ts index 48e775159f5..d8a3a2b977d 100644 --- a/packages/twenty-front/src/modules/views/hooks/useSaveRecordFilterGroupsToViewFilterGroups.ts +++ b/packages/twenty-front/src/modules/views/hooks/useSaveRecordFilterGroupsToViewFilterGroups.ts @@ -1,7 +1,7 @@ import { currentRecordFilterGroupsComponentState } from '@/object-record/record-filter-group/states/currentRecordFilterGroupsComponentState'; import { useRecoilComponentCallbackState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackState'; import { getSnapshotValue } from '@/ui/utilities/state/utils/getSnapshotValue'; -import { usePersistViewFilterGroupRecords } from '@/views/hooks/internal/usePersistViewFilterGroup'; +import { usePerformViewFilterGroupAPIPersist } from '@/views/hooks/internal/usePerformViewFilterGroupAPIPersist'; import { useCanPersistViewChanges } from '@/views/hooks/useCanPersistViewChanges'; import { useGetCurrentViewOnly } from '@/views/hooks/useGetCurrentViewOnly'; import { getViewFilterGroupsToCreate } from '@/views/utils/getViewFilterGroupsToCreate'; @@ -14,10 +14,10 @@ import { isDefined } from 'twenty-shared/utils'; export const useSaveRecordFilterGroupsToViewFilterGroups = () => { const { canPersistChanges } = useCanPersistViewChanges(); const { - createViewFilterGroups, - updateViewFilterGroups, - deleteViewFilterGroups, - } = usePersistViewFilterGroupRecords(); + performViewFilterGroupAPICreate, + performViewFilterGroupAPIUpdate, + performViewFilterGroupAPIDelete, + } = usePerformViewFilterGroupAPIPersist(); const { currentView } = useGetCurrentViewOnly(); @@ -65,17 +65,20 @@ export const useSaveRecordFilterGroupsToViewFilterGroups = () => { (viewFilterGroup) => viewFilterGroup.id, ); - await createViewFilterGroups(viewFilterGroupsToCreate, currentView); - await updateViewFilterGroups(viewFilterGroupsToUpdate); - await deleteViewFilterGroups(viewFilterGroupIdsToDelete); + await performViewFilterGroupAPICreate( + viewFilterGroupsToCreate, + currentView, + ); + await performViewFilterGroupAPIUpdate(viewFilterGroupsToUpdate); + await performViewFilterGroupAPIDelete(viewFilterGroupIdsToDelete); }, [ canPersistChanges, currentView, currentRecordFilterGroupsCallbackState, - createViewFilterGroups, - updateViewFilterGroups, - deleteViewFilterGroups, + performViewFilterGroupAPICreate, + performViewFilterGroupAPIUpdate, + performViewFilterGroupAPIDelete, ], ); diff --git a/packages/twenty-front/src/modules/views/hooks/useSaveRecordFiltersToViewFilters.ts b/packages/twenty-front/src/modules/views/hooks/useSaveRecordFiltersToViewFilters.ts index 2fe47022eac..d2423c2c4ca 100644 --- a/packages/twenty-front/src/modules/views/hooks/useSaveRecordFiltersToViewFilters.ts +++ b/packages/twenty-front/src/modules/views/hooks/useSaveRecordFiltersToViewFilters.ts @@ -1,7 +1,7 @@ import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState'; import { useRecoilComponentCallbackState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackState'; import { getSnapshotValue } from '@/ui/utilities/state/utils/getSnapshotValue'; -import { usePersistViewFilterRecords } from '@/views/hooks/internal/usePersistViewFilter'; +import { usePerformViewFilterAPIPersist } from '@/views/hooks/internal/usePerformViewFilterAPIPersist'; import { useCanPersistViewChanges } from '@/views/hooks/useCanPersistViewChanges'; import { useGetCurrentViewOnly } from '@/views/hooks/useGetCurrentViewOnly'; import { getViewFiltersToCreate } from '@/views/utils/getViewFiltersToCreate'; @@ -13,8 +13,11 @@ import { isDefined } from 'twenty-shared/utils'; export const useSaveRecordFiltersToViewFilters = () => { const { canPersistChanges } = useCanPersistViewChanges(); - const { createViewFilters, updateViewFilters, deleteViewFilters } = - usePersistViewFilterRecords(); + const { + performViewFilterAPICreate, + performViewFilterAPIUpdate, + performViewFilterAPIDelete, + } = usePerformViewFilterAPIPersist(); const { currentView } = useGetCurrentViewOnly(); @@ -93,17 +96,23 @@ export const useSaveRecordFiltersToViewFilters = () => { }), ); - const createResult = await createViewFilters(createViewFilterInputs); + const createResult = await performViewFilterAPICreate( + createViewFilterInputs, + ); if (createResult.status === 'failed') { return; } - const updateResult = await updateViewFilters(updateViewFilterInputs); + const updateResult = await performViewFilterAPIUpdate( + updateViewFilterInputs, + ); if (updateResult.status === 'failed') { return; } - const deleteResult = await deleteViewFilters(deleteViewFilterInputs); + const deleteResult = await performViewFilterAPIDelete( + deleteViewFilterInputs, + ); if (deleteResult.status === 'failed') { return; } @@ -112,9 +121,9 @@ export const useSaveRecordFiltersToViewFilters = () => { canPersistChanges, currentView, currentRecordFiltersCallbackState, - createViewFilters, - updateViewFilters, - deleteViewFilters, + performViewFilterAPICreate, + performViewFilterAPIUpdate, + performViewFilterAPIDelete, ], ); diff --git a/packages/twenty-front/src/modules/views/hooks/useSaveRecordSortsToViewSorts.ts b/packages/twenty-front/src/modules/views/hooks/useSaveRecordSortsToViewSorts.ts index ed0afb24fa8..a9217bce89d 100644 --- a/packages/twenty-front/src/modules/views/hooks/useSaveRecordSortsToViewSorts.ts +++ b/packages/twenty-front/src/modules/views/hooks/useSaveRecordSortsToViewSorts.ts @@ -1,7 +1,7 @@ import { currentRecordSortsComponentState } from '@/object-record/record-sort/states/currentRecordSortsComponentState'; import { useRecoilComponentCallbackState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackState'; import { getSnapshotValue } from '@/ui/utilities/state/utils/getSnapshotValue'; -import { usePersistViewSortRecords } from '@/views/hooks/internal/usePersistViewSort'; +import { usePerformViewSortAPIPersist } from '@/views/hooks/internal/usePerformViewSortAPIPersist'; import { useCanPersistViewChanges } from '@/views/hooks/useCanPersistViewChanges'; import { useGetCurrentViewOnly } from '@/views/hooks/useGetCurrentViewOnly'; import { getViewSortsToCreate } from '@/views/utils/getViewSortsToCreate'; @@ -13,8 +13,11 @@ import { isDefined } from 'twenty-shared/utils'; export const useSaveRecordSortsToViewSorts = () => { const { canPersistChanges } = useCanPersistViewChanges(); - const { createViewSorts, updateViewSorts, deleteViewSorts } = - usePersistViewSortRecords(); + const { + performViewSortAPICreate, + performViewSortAPIUpdate, + performViewSortAPIDelete, + } = usePerformViewSortAPIPersist(); const { currentView } = useGetCurrentViewOnly(); @@ -55,17 +58,17 @@ export const useSaveRecordSortsToViewSorts = () => { newViewSorts, ); - await createViewSorts(viewSortsToCreate, currentView); - await updateViewSorts(viewSortsToUpdate); - await deleteViewSorts(viewSortsToDelete); + await performViewSortAPICreate(viewSortsToCreate, currentView); + await performViewSortAPIUpdate(viewSortsToUpdate); + await performViewSortAPIDelete(viewSortsToDelete); }, [ canPersistChanges, currentView, currentRecordSortsCallbackState, - createViewSorts, - updateViewSorts, - deleteViewSorts, + performViewSortAPICreate, + performViewSortAPIUpdate, + performViewSortAPIDelete, ], ); diff --git a/packages/twenty-front/src/modules/views/hooks/useUpdateViewAggregate.ts b/packages/twenty-front/src/modules/views/hooks/useUpdateViewAggregate.ts index 83a8d5d5cb1..2eaf2fe90a6 100644 --- a/packages/twenty-front/src/modules/views/hooks/useUpdateViewAggregate.ts +++ b/packages/twenty-front/src/modules/views/hooks/useUpdateViewAggregate.ts @@ -4,7 +4,7 @@ import { useLoadRecordIndexStates } from '@/object-record/record-index/hooks/use import { type ExtendedAggregateOperations } from '@/object-record/record-table/types/ExtendedAggregateOperations'; import { convertExtendedAggregateOperationToAggregateOperation } from '@/object-record/utils/convertExtendedAggregateOperationToAggregateOperation'; import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; -import { usePersistView } from '@/views/hooks/internal/usePersistView'; +import { usePerformViewAPIUpdate } from '@/views/hooks/internal/usePerformViewAPIUpdate'; import { useCanPersistViewChanges } from '@/views/hooks/useCanPersistViewChanges'; import { coreViewsState } from '@/views/states/coreViewState'; import { convertCoreViewToView } from '@/views/utils/convertCoreViewToView'; @@ -20,7 +20,7 @@ export const useUpdateViewAggregate = () => { const currentViewId = useRecoilComponentValue( contextStoreCurrentViewIdComponentState, ); - const { updateView } = usePersistView(); + const { performViewAPIUpdate } = usePerformViewAPIUpdate(); const { loadRecordIndexStates } = useLoadRecordIndexStates(); const updateViewAggregate = useRecoilCallback( @@ -50,7 +50,7 @@ export const useUpdateViewAggregate = () => { return; } - const updatedViewResult = await updateView({ + const updatedViewResult = await performViewAPIUpdate({ id: currentViewId, input: { kanbanAggregateOperationFieldMetadataId, @@ -78,7 +78,12 @@ export const useUpdateViewAggregate = () => { loadRecordIndexStates(updatedView, objectMetadataItem); } }, - [canPersistChanges, currentViewId, updateView, loadRecordIndexStates], + [ + canPersistChanges, + currentViewId, + performViewAPIUpdate, + loadRecordIndexStates, + ], ); return { diff --git a/packages/twenty-front/src/modules/views/utils/convertCoreViewToView.ts b/packages/twenty-front/src/modules/views/utils/convertCoreViewToView.ts index 10ff2a85a50..c5032f6a03b 100644 --- a/packages/twenty-front/src/modules/views/utils/convertCoreViewToView.ts +++ b/packages/twenty-front/src/modules/views/utils/convertCoreViewToView.ts @@ -25,13 +25,13 @@ export const convertCoreViewToView = ( key: convertedKey, objectMetadataId: coreView.objectMetadataId, isCompact: coreView.isCompact, - viewFields: coreView.viewFields.map((viewField) => + viewFields: coreView.viewFields?.map((viewField) => convertCoreViewFieldToViewField(viewField), ), - viewGroups: coreView.viewGroups.map((viewGroup) => + viewGroups: coreView.viewGroups?.map((viewGroup) => convertCoreViewGroupToViewGroup(viewGroup), ), - viewFilters: coreView.viewFilters.map((viewFilter) => + viewFilters: coreView.viewFilters?.map((viewFilter) => convertCoreViewFilterToViewFilter(viewFilter), ), viewFilterGroups: coreView.viewFilterGroups?.map( diff --git a/packages/twenty-front/src/modules/views/view-picker/components/ViewPickerListContent.tsx b/packages/twenty-front/src/modules/views/view-picker/components/ViewPickerListContent.tsx index 07328e61963..1fb6b5ba39c 100644 --- a/packages/twenty-front/src/modules/views/view-picker/components/ViewPickerListContent.tsx +++ b/packages/twenty-front/src/modules/views/view-picker/components/ViewPickerListContent.tsx @@ -11,7 +11,7 @@ import { DropdownMenuSectionLabel } from '@/ui/layout/dropdown/components/Dropdo import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator'; import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown'; import { useSetRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentState'; -import { usePersistView } from '@/views/hooks/internal/usePersistView'; +import { usePerformViewAPIUpdate } from '@/views/hooks/internal/usePerformViewAPIUpdate'; import { useChangeView } from '@/views/hooks/useChangeView'; import { useGetCurrentViewOnly } from '@/views/hooks/useGetCurrentViewOnly'; import { useOpenCreateViewDropdown } from '@/views/hooks/useOpenCreateViewDropown'; @@ -61,7 +61,7 @@ export const ViewPickerListContent = () => { const { setViewPickerMode } = useViewPickerMode(); - const { updateView } = usePersistView(); + const { performViewAPIUpdate } = usePerformViewAPIUpdate(); const { changeView } = useChangeView(); const { closeDropdown } = useCloseDropdown(); @@ -98,12 +98,15 @@ export const ViewPickerListContent = () => { Promise.all( viewsReordered.map(async (view, index) => { if (view.position !== index) { - await updateView({ id: view.id, input: { position: index } }); + await performViewAPIUpdate({ + id: view.id, + input: { position: index }, + }); } }), ); }, - [updateView, workspaceViews], + [performViewAPIUpdate, workspaceViews], ); const handleUnlistedDragEnd = useCallback( @@ -118,12 +121,15 @@ export const ViewPickerListContent = () => { Promise.all( viewsReordered.map(async (view, index) => { if (view.position !== index) { - await updateView({ id: view.id, input: { position: index } }); + await performViewAPIUpdate({ + id: view.id, + input: { position: index }, + }); } }), ); }, - [updateView, unlistedViews], + [performViewAPIUpdate, unlistedViews], ); return ( diff --git a/packages/twenty-front/src/modules/views/view-picker/hooks/useDestroyViewFromCurrentState.ts b/packages/twenty-front/src/modules/views/view-picker/hooks/useDestroyViewFromCurrentState.ts index 01ae7e5606e..cf3a6833780 100644 --- a/packages/twenty-front/src/modules/views/view-picker/hooks/useDestroyViewFromCurrentState.ts +++ b/packages/twenty-front/src/modules/views/view-picker/hooks/useDestroyViewFromCurrentState.ts @@ -3,7 +3,7 @@ import { useRecoilCallback, useRecoilValue } from 'recoil'; import { useContextStoreObjectMetadataItemOrThrow } from '@/context-store/hooks/useContextStoreObjectMetadataItemOrThrow'; import { useRecoilComponentCallbackState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackState'; import { getSnapshotValue } from '@/ui/utilities/state/utils/getSnapshotValue'; -import { usePersistView } from '@/views/hooks/internal/usePersistView'; +import { usePerformViewAPIPersist } from '@/views/hooks/internal/usePerformViewAPIPersist'; import { useChangeView } from '@/views/hooks/useChangeView'; import { useGetCurrentViewOnly } from '@/views/hooks/useGetCurrentViewOnly'; import { coreViewsByObjectMetadataIdFamilySelector } from '@/views/states/selectors/coreViewsByObjectMetadataIdFamilySelector'; @@ -44,7 +44,7 @@ export const useDestroyViewFromCurrentState = (viewBarInstanceId?: string) => { const { changeView } = useChangeView(); - const { destroyView } = usePersistView(); + const { performViewAPIDestroy } = usePerformViewAPIPersist(); const destroyViewFromCurrentState = useRecoilCallback( ({ set, snapshot }) => @@ -74,14 +74,14 @@ export const useDestroyViewFromCurrentState = (viewBarInstanceId?: string) => { views.filter((view) => view.id !== viewPickerReferenceViewId), ); - await destroyView({ id: viewPickerReferenceViewId }); + await performViewAPIDestroy({ id: viewPickerReferenceViewId }); }, [ currentView, closeAndResetViewPicker, objectMetadataItem.id, changeView, - destroyView, + performViewAPIDestroy, viewPickerIsDirtyCallbackState, viewPickerIsPersistingCallbackState, viewPickerReferenceViewIdCallbackState, diff --git a/packages/twenty-front/src/modules/views/view-picker/hooks/useUpdateViewFromCurrentState.ts b/packages/twenty-front/src/modules/views/view-picker/hooks/useUpdateViewFromCurrentState.ts index 5e8443e8931..74c2ceb1c7a 100644 --- a/packages/twenty-front/src/modules/views/view-picker/hooks/useUpdateViewFromCurrentState.ts +++ b/packages/twenty-front/src/modules/views/view-picker/hooks/useUpdateViewFromCurrentState.ts @@ -1,8 +1,7 @@ import { useRecoilComponentCallbackState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackState'; import { getSnapshotValue } from '@/ui/utilities/state/utils/getSnapshotValue'; -import { usePersistView } from '@/views/hooks/internal/usePersistView'; +import { usePerformViewAPIUpdate } from '@/views/hooks/internal/usePerformViewAPIUpdate'; import { useCanPersistViewChanges } from '@/views/hooks/useCanPersistViewChanges'; -import { useChangeView } from '@/views/hooks/useChangeView'; import { useCloseAndResetViewPicker } from '@/views/view-picker/hooks/useCloseAndResetViewPicker'; import { viewPickerInputNameComponentState } from '@/views/view-picker/states/viewPickerInputNameComponentState'; import { viewPickerIsDirtyComponentState } from '@/views/view-picker/states/viewPickerIsDirtyComponentState'; @@ -39,8 +38,7 @@ export const useUpdateViewFromCurrentState = () => { viewPickerVisibilityComponentState, ); - const { updateView } = usePersistView(); - const { changeView } = useChangeView(); + const { performViewAPIUpdate } = usePerformViewAPIUpdate(); const updateViewFromCurrentState = useRecoilCallback( ({ set, snapshot }) => @@ -71,7 +69,7 @@ export const useUpdateViewFromCurrentState = () => { viewPickerVisibilityCallbackState, ); - await updateView({ + await performViewAPIUpdate({ id: viewPickerReferenceViewId, input: { name: viewPickerInputName, @@ -79,7 +77,6 @@ export const useUpdateViewFromCurrentState = () => { visibility: visibility, }, }); - changeView(viewPickerReferenceViewId); }, [ canPersistChanges, @@ -90,8 +87,7 @@ export const useUpdateViewFromCurrentState = () => { viewPickerInputNameCallbackState, viewPickerSelectedIconCallbackState, viewPickerVisibilityCallbackState, - updateView, - changeView, + performViewAPIUpdate, ], ); diff --git a/packages/twenty-shared/src/utils/array/__tests__/upsertIntoArrayOfObjectComparingId.test.ts b/packages/twenty-shared/src/utils/array/__tests__/upsertIntoArrayOfObjectsComparingId.test.ts similarity index 96% rename from packages/twenty-shared/src/utils/array/__tests__/upsertIntoArrayOfObjectComparingId.test.ts rename to packages/twenty-shared/src/utils/array/__tests__/upsertIntoArrayOfObjectsComparingId.test.ts index 2dd64554b8f..78a4ccec8ec 100644 --- a/packages/twenty-shared/src/utils/array/__tests__/upsertIntoArrayOfObjectComparingId.test.ts +++ b/packages/twenty-shared/src/utils/array/__tests__/upsertIntoArrayOfObjectsComparingId.test.ts @@ -1,4 +1,4 @@ -import { upsertIntoArrayOfObjectsComparingId } from '@/utils/array/upsertIntoArrayOfObjectComparingId'; +import { upsertIntoArrayOfObjectsComparingId } from '@/utils/array/upsertIntoArrayOfObjectsComparingId'; type TestObject = { id: string; diff --git a/packages/twenty-shared/src/utils/array/__tests__/upsertPropertiesOfItemIntoArrayOfObjectsComparingId.test.ts b/packages/twenty-shared/src/utils/array/__tests__/upsertPropertiesOfItemIntoArrayOfObjectsComparingId.test.ts new file mode 100644 index 00000000000..c4d9acfb290 --- /dev/null +++ b/packages/twenty-shared/src/utils/array/__tests__/upsertPropertiesOfItemIntoArrayOfObjectsComparingId.test.ts @@ -0,0 +1,107 @@ +import { upsertPropertiesOfItemIntoArrayOfObjectsComparingId } from '@/utils/array/upsertPropertiesOfItemIntoArrayOfObjectsComparingId'; + +type TestObject = { + id: string; + name: string; + value: number; +}; + +const mockTestObjects: TestObject[] = [ + { + id: '1', + name: 'Test 1', + value: 100, + }, + { + id: '2', + name: 'Test 2', + value: 200, + }, + { + id: '3', + name: 'Test 3', + value: 300, + }, +]; + +describe('upsertPropertiesOfItemIntoArrayOfObjectsComparingId', () => { + it('should insert partial properties into empty array', () => { + const partialItem = { id: '1', name: 'New Item' }; + + expect( + upsertPropertiesOfItemIntoArrayOfObjectsComparingId( + [], + partialItem, + ), + ).toStrictEqual([partialItem]); + }); + + it('should append item when id does not exist', () => { + const newItem: TestObject = { + id: '4', + name: 'Test 4', + value: 400, + }; + + expect( + upsertPropertiesOfItemIntoArrayOfObjectsComparingId( + mockTestObjects, + newItem, + ), + ).toStrictEqual([...mockTestObjects, newItem]); + }); + + it('should merge partial properties into existing item', () => { + const partialUpdate = { id: '2', name: 'Updated Test 2' }; + + expect( + upsertPropertiesOfItemIntoArrayOfObjectsComparingId( + mockTestObjects, + partialUpdate, + ), + ).toStrictEqual([ + mockTestObjects[0], + { id: '2', name: 'Updated Test 2', value: 200 }, + mockTestObjects[2], + ]); + }); + + it('should replace all properties when full item is provided', () => { + const fullUpdate: TestObject = { + id: '1', + name: 'Replaced Test 1', + value: 999, + }; + + expect( + upsertPropertiesOfItemIntoArrayOfObjectsComparingId( + mockTestObjects, + fullUpdate, + ), + ).toStrictEqual([fullUpdate, mockTestObjects[1], mockTestObjects[2]]); + }); + + it('should not mutate original array when updating', () => { + const originalArray = [...mockTestObjects]; + const partialUpdate = { id: '2', value: 999 }; + + upsertPropertiesOfItemIntoArrayOfObjectsComparingId( + mockTestObjects, + partialUpdate, + ); + + expect(mockTestObjects).toStrictEqual(originalArray); + }); + + it('should not mutate original array when inserting', () => { + const originalArray = [...mockTestObjects]; + const newItem: TestObject = { id: '5', name: 'Test 5', value: 500 }; + + upsertPropertiesOfItemIntoArrayOfObjectsComparingId( + mockTestObjects, + newItem, + ); + + expect(mockTestObjects).toStrictEqual(originalArray); + }); +}); diff --git a/packages/twenty-shared/src/utils/array/upsertIntoArrayOfObjectComparingId.ts b/packages/twenty-shared/src/utils/array/upsertIntoArrayOfObjectsComparingId.ts similarity index 100% rename from packages/twenty-shared/src/utils/array/upsertIntoArrayOfObjectComparingId.ts rename to packages/twenty-shared/src/utils/array/upsertIntoArrayOfObjectsComparingId.ts diff --git a/packages/twenty-shared/src/utils/array/upsertPropertiesOfItemIntoArrayOfObjectsComparingId.ts b/packages/twenty-shared/src/utils/array/upsertPropertiesOfItemIntoArrayOfObjectsComparingId.ts new file mode 100644 index 00000000000..503ff2af070 --- /dev/null +++ b/packages/twenty-shared/src/utils/array/upsertPropertiesOfItemIntoArrayOfObjectsComparingId.ts @@ -0,0 +1,31 @@ +import { findById } from '@/utils/array/findById'; + +export const upsertPropertiesOfItemIntoArrayOfObjectsComparingId = < + T extends { id: string }, +>( + arrayToUpsertInto: T[], + propertiesToUpsert: Partial & { id: string }, +): T[] => { + const alreadyExistingItemIndex = arrayToUpsertInto.findIndex( + findById(propertiesToUpsert.id), + ); + + const shouldReplaceItem = alreadyExistingItemIndex > -1; + + if (shouldReplaceItem) { + const newArray = [...arrayToUpsertInto]; + + const itemToUpsert = { + ...arrayToUpsertInto[alreadyExistingItemIndex], + ...propertiesToUpsert, + } as T; + + newArray.splice(alreadyExistingItemIndex, 1, itemToUpsert); + + return newArray; + } else { + return arrayToUpsertInto.concat({ + ...propertiesToUpsert, + } as T); + } +}; diff --git a/packages/twenty-shared/src/utils/index.ts b/packages/twenty-shared/src/utils/index.ts index 3a0385a0405..6f0a7964f68 100644 --- a/packages/twenty-shared/src/utils/index.ts +++ b/packages/twenty-shared/src/utils/index.ts @@ -17,7 +17,8 @@ export { isNonEmptyArray } from './array/isNonEmptyArray'; export { mapById } from './array/mapById'; export { mapByProperty } from './array/mapByProperty'; export { sumByProperty } from './array/sumByProperty'; -export { upsertIntoArrayOfObjectsComparingId } from './array/upsertIntoArrayOfObjectComparingId'; +export { upsertIntoArrayOfObjectsComparingId } from './array/upsertIntoArrayOfObjectsComparingId'; +export { upsertPropertiesOfItemIntoArrayOfObjectsComparingId } from './array/upsertPropertiesOfItemIntoArrayOfObjectsComparingId'; export { assertUnreachable } from './assertUnreachable'; export { computeDiffBetweenObjects } from './compute-diff-between-objects'; export { isPlainDateAfter } from './date/isPlainDateAfter';