diff --git a/packages/twenty-front/src/modules/action-menu/actions/record-actions/constants/DefaultRecordActionsConfig.tsx b/packages/twenty-front/src/modules/action-menu/actions/record-actions/constants/DefaultRecordActionsConfig.tsx index 7aafd48006..070ce765e2 100644 --- a/packages/twenty-front/src/modules/action-menu/actions/record-actions/constants/DefaultRecordActionsConfig.tsx +++ b/packages/twenty-front/src/modules/action-menu/actions/record-actions/constants/DefaultRecordActionsConfig.tsx @@ -5,7 +5,7 @@ import { ExportMultipleRecordsAction } from '@/action-menu/actions/record-action import { MergeMultipleRecordsAction } from '@/action-menu/actions/record-actions/multiple-records/components/MergeMultipleRecordsAction'; import { RestoreMultipleRecordsAction } from '@/action-menu/actions/record-actions/multiple-records/components/RestoreMultipleRecordsAction'; import { MultipleRecordsActionKeys } from '@/action-menu/actions/record-actions/multiple-records/types/MultipleRecordsActionKeys'; -import { CreateNewTableRecordNoSelectionRecordAction } from '@/action-menu/actions/record-actions/no-selection/components/CreateNewTableRecordNoSelectionRecordAction'; +import { CreateNewIndexRecordNoSelectionRecordAction } from '@/action-menu/actions/record-actions/no-selection/components/CreateNewIndexRecordNoSelectionRecordAction'; import { CreateNewViewNoSelectionRecord } from '@/action-menu/actions/record-actions/no-selection/components/CreateNewViewNoSelectionRecord'; import { HideDeletedRecordsNoSelectionRecordAction } from '@/action-menu/actions/record-actions/no-selection/components/HideDeletedRecordsNoSelectionRecordAction'; import { ImportRecordsNoSelectionRecordAction } from '@/action-menu/actions/record-actions/no-selection/components/ImportRecordsNoSelectionRecordAction'; @@ -103,7 +103,7 @@ export const DEFAULT_RECORD_ACTIONS_CONFIG: Record< !hasAnySoftDeleteFilterOnView) ?? false, availableOn: [ActionViewType.INDEX_PAGE_NO_SELECTION], - component: , + component: , }, [SingleRecordActionKeys.DELETE]: { type: ActionType.Standard, diff --git a/packages/twenty-front/src/modules/action-menu/actions/record-actions/no-selection/components/CreateNewTableRecordNoSelectionRecordAction.tsx b/packages/twenty-front/src/modules/action-menu/actions/record-actions/no-selection/components/CreateNewIndexRecordNoSelectionRecordAction.tsx similarity index 90% rename from packages/twenty-front/src/modules/action-menu/actions/record-actions/no-selection/components/CreateNewTableRecordNoSelectionRecordAction.tsx rename to packages/twenty-front/src/modules/action-menu/actions/record-actions/no-selection/components/CreateNewIndexRecordNoSelectionRecordAction.tsx index 0c830c5811..85f39bf554 100644 --- a/packages/twenty-front/src/modules/action-menu/actions/record-actions/no-selection/components/CreateNewTableRecordNoSelectionRecordAction.tsx +++ b/packages/twenty-front/src/modules/action-menu/actions/record-actions/no-selection/components/CreateNewIndexRecordNoSelectionRecordAction.tsx @@ -2,7 +2,7 @@ import { Action } from '@/action-menu/actions/components/Action'; import { useContextStoreObjectMetadataItemOrThrow } from '@/context-store/hooks/useContextStoreObjectMetadataItemOrThrow'; import { useCreateNewIndexRecord } from '@/object-record/record-table/hooks/useCreateNewIndexRecord'; -export const CreateNewTableRecordNoSelectionRecordAction = () => { +export const CreateNewIndexRecordNoSelectionRecordAction = () => { const { objectMetadataItem } = useContextStoreObjectMetadataItemOrThrow(); const { createNewIndexRecord } = useCreateNewIndexRecord({ diff --git a/packages/twenty-front/src/modules/command-menu/components/__stories__/CommandMenu.stories.tsx b/packages/twenty-front/src/modules/command-menu/components/__stories__/CommandMenu.stories.tsx index 53bff5bf01..c2a2855113 100644 --- a/packages/twenty-front/src/modules/command-menu/components/__stories__/CommandMenu.stories.tsx +++ b/packages/twenty-front/src/modules/command-menu/components/__stories__/CommandMenu.stories.tsx @@ -26,6 +26,7 @@ import { CommandMenuPages } from '@/command-menu/types/CommandMenuPages'; import { ContextStoreComponentInstanceContext } from '@/context-store/states/contexts/ContextStoreComponentInstanceContext'; import { ContextStoreViewType } from '@/context-store/types/ContextStoreViewType'; import { RecordComponentInstanceContextsWrapper } from '@/object-record/components/RecordComponentInstanceContextsWrapper'; +import { ViewComponentInstanceContext } from '@/views/states/contexts/ViewComponentInstanceContext'; import { HttpResponse, graphql } from 'msw'; import { IconDotsVertical } from 'twenty-ui/display'; import { I18nFrontDecorator } from '~/testing/decorators/I18nFrontDecorator'; @@ -42,17 +43,21 @@ const ContextStoreDecorator: Decorator = (Story) => { - - - - - + + + + + ); diff --git a/packages/twenty-front/src/modules/object-record/hooks/useCreateOneRecord.ts b/packages/twenty-front/src/modules/object-record/hooks/useCreateOneRecord.ts index 563840a070..20ceacc492 100644 --- a/packages/twenty-front/src/modules/object-record/hooks/useCreateOneRecord.ts +++ b/packages/twenty-front/src/modules/object-record/hooks/useCreateOneRecord.ts @@ -26,6 +26,7 @@ import { getCreateOneRecordMutationResponseField } from '@/object-record/utils/g import { sanitizeRecordInput } from '@/object-record/utils/sanitizeRecordInput'; import { useRecoilValue } from 'recoil'; import { CustomError, isDefined } from 'twenty-shared/utils'; + type useCreateOneRecordProps = { objectNameSingular: string; recordGqlFields?: RecordGqlOperationGqlRecordFields; diff --git a/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumnHeader.tsx b/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumnHeader.tsx index d6b10a589c..36ac81fa54 100644 --- a/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumnHeader.tsx +++ b/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumnHeader.tsx @@ -9,21 +9,14 @@ import { RecordBoardColumnHeaderAggregateDropdown } from '@/object-record/record import { RECORD_BOARD_COLUMN_WIDTH } from '@/object-record/record-board/constants/RecordBoardColumnWidth'; import { RecordBoardColumnContext } from '@/object-record/record-board/record-board-column/contexts/RecordBoardColumnContext'; import { hasAnySoftDeleteFilterOnViewComponentSelector } from '@/object-record/record-filter/states/hasAnySoftDeleteFilterOnView'; -import { recordGroupDefinitionsComponentSelector } from '@/object-record/record-group/states/selectors/recordGroupDefinitionsComponentSelector'; import { RecordGroupDefinitionType } from '@/object-record/record-group/types/RecordGroupDefinition'; import { recordIndexAggregateDisplayLabelComponentState } from '@/object-record/record-index/states/recordIndexAggregateDisplayLabelComponentState'; import { recordIndexAggregateDisplayValueForGroupValueComponentFamilyState } from '@/object-record/record-index/states/recordIndexAggregateDisplayValueForGroupValueComponentFamilyState'; -import { recordIndexRecordIdsByGroupComponentFamilyState } from '@/object-record/record-index/states/recordIndexRecordIdsByGroupComponentFamilyState'; -import { useUpsertRecordsInStore } from '@/object-record/record-store/hooks/useUpsertRecordsInStore'; import { useCreateNewIndexRecord } from '@/object-record/record-table/hooks/useCreateNewIndexRecord'; import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown'; import { useToggleDropdown } from '@/ui/layout/dropdown/hooks/useToggleDropdown'; -import { useRecoilComponentCallbackState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackState'; import { useRecoilComponentFamilyValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyValue'; import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; -import { getSnapshotValue } from '@/ui/utilities/state/utils/getSnapshotValue'; -import { useRecoilCallback } from 'recoil'; -import { findByProperty, isDefined } from 'twenty-shared/utils'; import { Tag } from 'twenty-ui/components'; import { IconDotsVertical, IconPlus } from 'twenty-ui/display'; import { LightIconButton } from 'twenty-ui/input'; @@ -104,62 +97,21 @@ export const RecordBoardColumnHeader = () => { recordIndexAggregateDisplayValueForGroupValueComponentFamilyState, { groupValue: columnDefinition?.value ?? '' }, ); - const { upsertRecordsInStore } = useUpsertRecordsInStore(); - - const recordIndexRecordIdsByGroupCallbackState = - useRecoilComponentCallbackState( - recordIndexRecordIdsByGroupComponentFamilyState, - ); const recordIndexAggregateDisplayLabel = useRecoilComponentValue( recordIndexAggregateDisplayLabelComponentState, ); - const recordGroupDefinitions = useRecoilComponentValue( - recordGroupDefinitionsComponentSelector, - ); - const { toggleDropdown } = useToggleDropdown(); const dropdownId = `record-board-column-dropdown-${columnDefinition.id}`; - const handleCreateNewRecordClick = useRecoilCallback( - ({ set, snapshot }) => - async () => { - const createdRecord = await createNewIndexRecord({ - position: 'first', - [selectFieldMetadataItem.name]: columnDefinition.value, - }); - - const recordGroup = recordGroupDefinitions.find( - findByProperty('value', createdRecord[selectFieldMetadataItem.name]), - ); - - if (isDefined(recordGroup)) { - const currentRecordIds = getSnapshotValue( - snapshot, - recordIndexRecordIdsByGroupCallbackState(recordGroup.id), - ); - - const newRecordIds = [createdRecord.id, ...currentRecordIds]; - - set( - recordIndexRecordIdsByGroupCallbackState(recordGroup.id), - newRecordIds, - ); - } - - upsertRecordsInStore([createdRecord]); - }, - [ - createNewIndexRecord, - columnDefinition, - recordGroupDefinitions, - recordIndexRecordIdsByGroupCallbackState, - upsertRecordsInStore, - selectFieldMetadataItem, - ], - ); + const handleCreateNewRecordClick = async () => { + await createNewIndexRecord({ + position: 'first', + [selectFieldMetadataItem.name]: columnDefinition.value, + }); + }; return ( diff --git a/packages/twenty-front/src/modules/object-record/record-table/hooks/useCreateNewIndexRecord.ts b/packages/twenty-front/src/modules/object-record/record-table/hooks/useCreateNewIndexRecord.ts index 6862ac96cf..30b02580ef 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/hooks/useCreateNewIndexRecord.ts +++ b/packages/twenty-front/src/modules/object-record/record-table/hooks/useCreateNewIndexRecord.ts @@ -2,17 +2,24 @@ import { useOpenRecordInCommandMenu } from '@/command-menu/hooks/useOpenRecordIn import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; import { getLabelIdentifierFieldMetadataItem } from '@/object-metadata/utils/getLabelIdentifierFieldMetadataItem'; import { useCreateOneRecord } from '@/object-record/hooks/useCreateOneRecord'; +import { recordGroupDefinitionsComponentSelector } from '@/object-record/record-group/states/selectors/recordGroupDefinitionsComponentSelector'; +import { recordIndexGroupFieldMetadataItemComponentState } from '@/object-record/record-index/states/recordIndexGroupFieldMetadataComponentState'; import { recordIndexOpenRecordInState } from '@/object-record/record-index/states/recordIndexOpenRecordInState'; +import { recordIndexRecordIdsByGroupComponentFamilyState } from '@/object-record/record-index/states/recordIndexRecordIdsByGroupComponentFamilyState'; +import { useUpsertRecordsInStore } from '@/object-record/record-store/hooks/useUpsertRecordsInStore'; import { useBuildRecordInputFromFilters } from '@/object-record/record-table/hooks/useBuildRecordInputFromFilters'; import { useRecordTitleCell } from '@/object-record/record-title-cell/hooks/useRecordTitleCell'; import { RecordTitleCellContainerType } from '@/object-record/record-title-cell/types/RecordTitleCellContainerType'; import { type ObjectRecord } from '@/object-record/types/ObjectRecord'; import { canOpenObjectInSidePanel } from '@/object-record/utils/canOpenObjectInSidePanel'; import { getRecordFieldInputInstanceId } from '@/object-record/utils/getRecordFieldInputId'; +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 { ViewOpenRecordInType } from '@/views/types/ViewOpenRecordInType'; import { useRecoilCallback } from 'recoil'; import { AppPath } from 'twenty-shared/types'; -import { isDefined } from 'twenty-shared/utils'; +import { findByProperty, isDefined } from 'twenty-shared/utils'; import { v4 } from 'uuid'; import { useNavigateApp } from '~/hooks/useNavigateApp'; @@ -23,6 +30,19 @@ type UseCreateNewIndexRecordProps = { export const useCreateNewIndexRecord = ({ objectMetadataItem, }: UseCreateNewIndexRecordProps) => { + const recordGroupDefinitions = useRecoilComponentValue( + recordGroupDefinitionsComponentSelector, + ); + + const recordIndexRecordIdsByGroupCallbackState = + useRecoilComponentCallbackState( + recordIndexRecordIdsByGroupComponentFamilyState, + ); + + const recordIndexGroupFieldMetadataItem = useRecoilComponentValue( + recordIndexGroupFieldMetadataItemComponentState, + ); + const { openRecordInCommandMenu } = useOpenRecordInCommandMenu(); const { createOneRecord } = useCreateOneRecord({ @@ -30,6 +50,8 @@ export const useCreateNewIndexRecord = ({ shouldMatchRootQueryFilter: true, }); + const { upsertRecordsInStore } = useUpsertRecordsInStore(); + const navigate = useNavigateApp(); const { openRecordTitleCell } = useRecordTitleCell(); @@ -39,7 +61,7 @@ export const useCreateNewIndexRecord = ({ }); const createNewIndexRecord = useRecoilCallback( - ({ snapshot }) => + ({ snapshot, set }) => async (recordInput?: Partial) => { const recordId = v4(); const recordInputFromFilters = buildRecordInputFromFilters(); @@ -85,6 +107,40 @@ export const useCreateNewIndexRecord = ({ }); } + if (isDefined(recordIndexGroupFieldMetadataItem)) { + const recordGroup = recordGroupDefinitions.find( + findByProperty( + 'value', + createdRecord[recordIndexGroupFieldMetadataItem.name], + ), + ); + + if (isDefined(recordGroup)) { + const currentRecordIds = getSnapshotValue( + snapshot, + recordIndexRecordIdsByGroupCallbackState(recordGroup.id), + ); + + if (recordInput?.position === 'first') { + const newRecordIds = [createdRecord.id, ...currentRecordIds]; + + set( + recordIndexRecordIdsByGroupCallbackState(recordGroup.id), + newRecordIds, + ); + } else { + const newRecordIds = [...currentRecordIds, createdRecord.id]; + + set( + recordIndexRecordIdsByGroupCallbackState(recordGroup.id), + newRecordIds, + ); + } + } + } + + upsertRecordsInStore([createdRecord]); + return createdRecord; }, [ @@ -94,6 +150,10 @@ export const useCreateNewIndexRecord = ({ objectMetadataItem, openRecordInCommandMenu, openRecordTitleCell, + recordGroupDefinitions, + recordIndexGroupFieldMetadataItem, + recordIndexRecordIdsByGroupCallbackState, + upsertRecordsInStore, ], );