diff --git a/packages/twenty-front/src/modules/object-record/record-field/meta-types/display/components/__stories__/DateFieldDisplay.stories.tsx b/packages/twenty-front/src/modules/object-record/record-field/meta-types/display/components/__stories__/DateFieldDisplay.stories.tsx
index 5e59fa27d..d1840916a 100644
--- a/packages/twenty-front/src/modules/object-record/record-field/meta-types/display/components/__stories__/DateFieldDisplay.stories.tsx
+++ b/packages/twenty-front/src/modules/object-record/record-field/meta-types/display/components/__stories__/DateFieldDisplay.stories.tsx
@@ -35,6 +35,7 @@ const meta: Meta = {
iconName: 'IconCalendarEvent',
metadata: {
fieldName: 'Date',
+ objectMetadataNameSingular: 'person',
},
},
hotkeyScope: 'hotkey-scope',
diff --git a/packages/twenty-front/src/modules/object-record/record-field/meta-types/display/components/__stories__/EmailFieldDisplay.stories.tsx b/packages/twenty-front/src/modules/object-record/record-field/meta-types/display/components/__stories__/EmailFieldDisplay.stories.tsx
index cdb48e180..d296465e6 100644
--- a/packages/twenty-front/src/modules/object-record/record-field/meta-types/display/components/__stories__/EmailFieldDisplay.stories.tsx
+++ b/packages/twenty-front/src/modules/object-record/record-field/meta-types/display/components/__stories__/EmailFieldDisplay.stories.tsx
@@ -36,6 +36,7 @@ const meta: Meta = {
metadata: {
fieldName: 'Email',
placeHolder: 'Email',
+ objectMetadataNameSingular: 'person',
},
},
hotkeyScope: 'hotkey-scope',
diff --git a/packages/twenty-front/src/modules/object-record/record-field/meta-types/display/components/__stories__/NumberFieldDisplay.stories.tsx b/packages/twenty-front/src/modules/object-record/record-field/meta-types/display/components/__stories__/NumberFieldDisplay.stories.tsx
index 3c3d8e229..db7fe3c57 100644
--- a/packages/twenty-front/src/modules/object-record/record-field/meta-types/display/components/__stories__/NumberFieldDisplay.stories.tsx
+++ b/packages/twenty-front/src/modules/object-record/record-field/meta-types/display/components/__stories__/NumberFieldDisplay.stories.tsx
@@ -35,6 +35,7 @@ const meta: Meta = {
fieldName: 'Number',
placeHolder: 'Number',
isPositive: true,
+ objectMetadataNameSingular: 'person',
},
},
hotkeyScope: 'hotkey-scope',
diff --git a/packages/twenty-front/src/modules/object-record/record-field/meta-types/display/components/__stories__/TextFieldDisplay.stories.tsx b/packages/twenty-front/src/modules/object-record/record-field/meta-types/display/components/__stories__/TextFieldDisplay.stories.tsx
index e8ae80758..328a6533c 100644
--- a/packages/twenty-front/src/modules/object-record/record-field/meta-types/display/components/__stories__/TextFieldDisplay.stories.tsx
+++ b/packages/twenty-front/src/modules/object-record/record-field/meta-types/display/components/__stories__/TextFieldDisplay.stories.tsx
@@ -34,6 +34,7 @@ const meta: Meta = {
metadata: {
fieldName: 'Text',
placeHolder: 'Text',
+ objectMetadataNameSingular: 'person',
},
},
hotkeyScope: 'hotkey-scope',
diff --git a/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/__stories__/AddressFieldInput.stories.tsx b/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/__stories__/AddressFieldInput.stories.tsx
index 1ac509d20..7241793cb 100644
--- a/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/__stories__/AddressFieldInput.stories.tsx
+++ b/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/__stories__/AddressFieldInput.stories.tsx
@@ -58,6 +58,7 @@ const AddressInputWithContext = ({
metadata: {
fieldName: 'Address',
placeHolder: 'Enter text',
+ objectMetadataNameSingular: 'person',
},
}}
entityId={entityId}
diff --git a/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/__stories__/BooleanFieldInput.stories.tsx b/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/__stories__/BooleanFieldInput.stories.tsx
index 53b026113..934d7b3e4 100644
--- a/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/__stories__/BooleanFieldInput.stories.tsx
+++ b/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/__stories__/BooleanFieldInput.stories.tsx
@@ -47,6 +47,7 @@ const BooleanFieldInputWithContext = ({
type: FieldMetadataType.Boolean,
metadata: {
fieldName: 'Boolean',
+ objectMetadataNameSingular: 'person',
},
}}
entityId={entityId}
diff --git a/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/__stories__/DateFieldInput.stories.tsx b/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/__stories__/DateFieldInput.stories.tsx
index 7b0fd4dca..389e5a6f3 100644
--- a/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/__stories__/DateFieldInput.stories.tsx
+++ b/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/__stories__/DateFieldInput.stories.tsx
@@ -49,6 +49,7 @@ const DateFieldInputWithContext = ({
iconName: 'IconCalendarEvent',
metadata: {
fieldName: 'Date',
+ objectMetadataNameSingular: 'person',
},
}}
entityId={entityId}
diff --git a/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/__stories__/EmailFieldInput.stories.tsx b/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/__stories__/EmailFieldInput.stories.tsx
index f3b65cec3..b96085ee8 100644
--- a/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/__stories__/EmailFieldInput.stories.tsx
+++ b/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/__stories__/EmailFieldInput.stories.tsx
@@ -50,6 +50,7 @@ const EmailFieldInputWithContext = ({
metadata: {
fieldName: 'email',
placeHolder: 'username@email.com',
+ objectMetadataNameSingular: 'person',
},
}}
entityId={entityId}
diff --git a/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/__stories__/NumberFieldInput.stories.tsx b/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/__stories__/NumberFieldInput.stories.tsx
index cc4626de3..832797657 100644
--- a/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/__stories__/NumberFieldInput.stories.tsx
+++ b/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/__stories__/NumberFieldInput.stories.tsx
@@ -50,6 +50,7 @@ const NumberFieldInputWithContext = ({
metadata: {
fieldName: 'number',
placeHolder: 'Enter number',
+ objectMetadataNameSingular: 'person',
},
}}
entityId={entityId}
diff --git a/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/__stories__/RatingFieldInput.stories.tsx b/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/__stories__/RatingFieldInput.stories.tsx
index 022e5f762..2f6217150 100644
--- a/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/__stories__/RatingFieldInput.stories.tsx
+++ b/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/__stories__/RatingFieldInput.stories.tsx
@@ -50,6 +50,7 @@ const RatingFieldInputWithContext = ({
iconName: 'Icon123',
metadata: {
fieldName: 'Rating',
+ objectMetadataNameSingular: 'person',
},
}}
entityId={entityId}
diff --git a/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/__stories__/RelationFieldInput.stories.tsx b/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/__stories__/RelationFieldInput.stories.tsx
index e3ad58656..a6fca0e4d 100644
--- a/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/__stories__/RelationFieldInput.stories.tsx
+++ b/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/__stories__/RelationFieldInput.stories.tsx
@@ -73,6 +73,7 @@ const RelationFieldInputWithContext = ({
relationObjectMetadataNamePlural: 'workspaceMembers',
relationObjectMetadataNameSingular:
CoreObjectNameSingular.WorkspaceMember,
+ objectMetadataNameSingular: 'person',
},
}}
entityId={entityId}
diff --git a/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/__stories__/TextFieldInput.stories.tsx b/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/__stories__/TextFieldInput.stories.tsx
index 6dc17f40d..09d400028 100644
--- a/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/__stories__/TextFieldInput.stories.tsx
+++ b/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/__stories__/TextFieldInput.stories.tsx
@@ -50,6 +50,7 @@ const TextFieldInputWithContext = ({
metadata: {
fieldName: 'Text',
placeHolder: 'Enter text',
+ objectMetadataNameSingular: 'person',
},
}}
entityId={entityId}
diff --git a/packages/twenty-front/src/modules/object-record/record-show/components/RecordShowContainer.tsx b/packages/twenty-front/src/modules/object-record/record-show/components/RecordShowContainer.tsx
index 26a39d366..ac4991b5b 100644
--- a/packages/twenty-front/src/modules/object-record/record-show/components/RecordShowContainer.tsx
+++ b/packages/twenty-front/src/modules/object-record/record-show/components/RecordShowContainer.tsx
@@ -153,6 +153,7 @@ export const RecordShowContainer = ({
metadata: {
fieldName:
labelIdentifierFieldMetadataItem?.name || '',
+ objectMetadataNameSingular: objectNameSingular,
},
},
useUpdateRecord: useUpdateOneObjectRecordMutation,
diff --git a/packages/twenty-front/src/modules/object-record/record-table/components/RecordTableBody.tsx b/packages/twenty-front/src/modules/object-record/record-table/components/RecordTableBody.tsx
index 3f9ad3da5..3947067f4 100644
--- a/packages/twenty-front/src/modules/object-record/record-table/components/RecordTableBody.tsx
+++ b/packages/twenty-front/src/modules/object-record/record-table/components/RecordTableBody.tsx
@@ -1,6 +1,7 @@
import { useRecoilValue } from 'recoil';
import { RecordTableBodyFetchMoreLoader } from '@/object-record/record-table/components/RecordTableBodyFetchMoreLoader';
+import { RecordTablePendingRow } from '@/object-record/record-table/components/RecordTablePendingRow';
import { RecordTableRow } from '@/object-record/record-table/components/RecordTableRow';
import { useRecordTableStates } from '@/object-record/record-table/hooks/internal/useRecordTableStates';
@@ -18,6 +19,7 @@ export const RecordTableBody = ({
return (
<>
+
{tableRowIds.map((recordId, rowIndex) => (
{
+ const { pendingRecordIdState } = useRecordTableStates();
+ const pendingRecordId = useRecoilValue(pendingRecordIdState);
+
+ if (!pendingRecordId) return;
+
+ return (
+
+ );
+};
diff --git a/packages/twenty-front/src/modules/object-record/record-table/hooks/internal/useRecordTableStates.ts b/packages/twenty-front/src/modules/object-record/record-table/hooks/internal/useRecordTableStates.ts
index 924c17a11..1e178da5b 100644
--- a/packages/twenty-front/src/modules/object-record/record-table/hooks/internal/useRecordTableStates.ts
+++ b/packages/twenty-front/src/modules/object-record/record-table/hooks/internal/useRecordTableStates.ts
@@ -12,6 +12,7 @@ import { onColumnsChangeComponentState } from '@/object-record/record-table/stat
import { onEntityCountChangeComponentState } from '@/object-record/record-table/states/onEntityCountChangeComponentState';
import { onToggleColumnFilterComponentState } from '@/object-record/record-table/states/onToggleColumnFilterComponentState';
import { onToggleColumnSortComponentState } from '@/object-record/record-table/states/onToggleColumnSortComponentState';
+import { recordTablePendingRecordIdComponentState } from '@/object-record/record-table/states/recordTablePendingRecordIdComponentState';
import { resizeFieldOffsetComponentState } from '@/object-record/record-table/states/resizeFieldOffsetComponentState';
import { allRowsSelectedStatusComponentSelector } from '@/object-record/record-table/states/selectors/allRowsSelectedStatusComponentSelector';
import { hiddenTableColumnsComponentSelector } from '@/object-record/record-table/states/selectors/hiddenTableColumnsComponentSelector';
@@ -132,5 +133,9 @@ export const useRecordTableStates = (recordTableId?: string) => {
visibleTableColumnsComponentSelector,
scopeId,
),
+ pendingRecordIdState: extractComponentState(
+ recordTablePendingRecordIdComponentState,
+ scopeId,
+ ),
};
};
diff --git a/packages/twenty-front/src/modules/object-record/record-table/hooks/useRecordTable.ts b/packages/twenty-front/src/modules/object-record/record-table/hooks/useRecordTable.ts
index 648b0f440..4ff7c6552 100644
--- a/packages/twenty-front/src/modules/object-record/record-table/hooks/useRecordTable.ts
+++ b/packages/twenty-front/src/modules/object-record/record-table/hooks/useRecordTable.ts
@@ -44,6 +44,7 @@ export const useRecordTable = (props?: useRecordTableProps) => {
selectedRowIdsSelector,
onToggleColumnFilterState,
onToggleColumnSortState,
+ pendingRecordIdState,
} = useRecordTableStates(recordTableId);
const setAvailableTableColumns = useRecoilCallback(
@@ -194,6 +195,8 @@ export const useRecordTable = (props?: useRecordTableProps) => {
const isSomeCellInEditModeState =
useGetIsSomeCellInEditModeState(recordTableId);
+ const setPendingRecordId = useSetRecoilState(pendingRecordIdState);
+
return {
scopeId,
onColumnsChange,
@@ -222,5 +225,6 @@ export const useRecordTable = (props?: useRecordTableProps) => {
setHasUserSelectedAllRows,
setOnToggleColumnFilter,
setOnToggleColumnSort,
+ setPendingRecordId,
};
};
diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/components/RecordTableCell.tsx b/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/components/RecordTableCell.tsx
index 1c997b125..7aee69ebc 100644
--- a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/components/RecordTableCell.tsx
+++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/components/RecordTableCell.tsx
@@ -7,6 +7,7 @@ import { FieldInputEvent } from '@/object-record/record-field/types/FieldInputEv
import { useRecordTableMoveFocus } from '@/object-record/record-table/hooks/useRecordTableMoveFocus';
import { RecordTableCellContainer } from '@/object-record/record-table/record-table-cell/components/RecordTableCellContainer';
import { useCloseRecordTableCell } from '@/object-record/record-table/record-table-cell/hooks/useCloseRecordTableCell';
+import { useUpsertRecord } from '@/object-record/record-table/record-table-cell/hooks/useUpsertRecord';
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
export const RecordTableCell = ({
@@ -15,19 +16,21 @@ export const RecordTableCell = ({
customHotkeyScope: HotkeyScope;
}) => {
const { closeTableCell } = useCloseRecordTableCell();
- const { entityId, fieldDefinition } = useContext(FieldContext);
+ const { upsertRecord } = useUpsertRecord();
const { moveLeft, moveRight, moveDown } = useRecordTableMoveFocus();
+ const { entityId, fieldDefinition } = useContext(FieldContext);
+
const handleEnter: FieldInputEvent = (persistField) => {
- persistField();
+ upsertRecord(persistField);
closeTableCell();
moveDown();
};
const handleSubmit: FieldInputEvent = (persistField) => {
- persistField();
+ upsertRecord(persistField);
closeTableCell();
};
@@ -37,26 +40,26 @@ export const RecordTableCell = ({
};
const handleClickOutside: FieldInputEvent = (persistField) => {
- persistField();
+ upsertRecord(persistField);
closeTableCell();
};
const handleEscape: FieldInputEvent = (persistField) => {
- persistField();
+ upsertRecord(persistField);
closeTableCell();
};
const handleTab: FieldInputEvent = (persistField) => {
- persistField();
+ upsertRecord(persistField);
closeTableCell();
moveRight();
};
const handleShiftTab: FieldInputEvent = (persistField) => {
- persistField();
+ upsertRecord(persistField);
closeTableCell();
moveLeft();
diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/__tests__/useUpsertRecord.test.tsx b/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/__tests__/useUpsertRecord.test.tsx
new file mode 100644
index 000000000..291c7407d
--- /dev/null
+++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/__tests__/useUpsertRecord.test.tsx
@@ -0,0 +1,152 @@
+import { ReactNode } from 'react';
+import { act, renderHook } from '@testing-library/react';
+import { RecoilRoot } from 'recoil';
+import { createState } from 'twenty-ui';
+
+import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
+import { useCreateOneRecord } from '@/object-record/hooks/useCreateOneRecord';
+import { textfieldDefinition } from '@/object-record/record-field/__mocks__/fieldDefinitions';
+import { FieldContext } from '@/object-record/record-field/contexts/FieldContext';
+import { useUpsertRecord } from '@/object-record/record-table/record-table-cell/hooks/useUpsertRecord';
+import { TableHotkeyScope } from '@/object-record/record-table/types/TableHotkeyScope';
+
+const pendingRecordId = 'a7286b9a-c039-4a89-9567-2dfa7953cda9';
+const draftValue = 'updated Name';
+
+jest.mock('@/object-record/hooks/useCreateOneRecord', () => ({
+ __esModule: true,
+ useCreateOneRecord: jest.fn(),
+}));
+
+const draftValueState = createState({
+ key: 'draftValueState',
+ defaultValue: null,
+});
+jest.mock(
+ '@/object-record/record-field/hooks/internal/useRecordFieldInputStates',
+ () => ({
+ __esModule: true,
+ useRecordFieldInputStates: jest.fn(() => ({
+ getDraftValueSelector: () => draftValueState,
+ })),
+ }),
+);
+
+const pendingRecordIdState = createState({
+ key: 'pendingRecordIdState',
+ defaultValue: null,
+});
+jest.mock(
+ '@/object-record/record-table/hooks/internal/useRecordTableStates',
+ () => ({
+ __esModule: true,
+ useRecordTableStates: jest.fn(() => ({
+ pendingRecordIdState: pendingRecordIdState,
+ })),
+ }),
+);
+
+const createOneRecordMock = jest.fn();
+const updateOneRecordMock = jest.fn();
+(useCreateOneRecord as jest.Mock).mockReturnValue({
+ createOneRecord: createOneRecordMock,
+});
+
+const Wrapper = ({
+ children,
+ pendingRecordIdMockedValue,
+ draftValueMockedValue,
+}: {
+ children: ReactNode;
+ pendingRecordIdMockedValue: string | null;
+ draftValueMockedValue: string | null;
+}) => (
+ {
+ snapshot.set(pendingRecordIdState, pendingRecordIdMockedValue);
+ snapshot.set(draftValueState, draftValueMockedValue);
+ }}
+ >
+
+ {children}
+
+
+);
+
+describe('useUpsertRecord', () => {
+ beforeEach(async () => {
+ createOneRecordMock.mockClear();
+ updateOneRecordMock.mockClear();
+ });
+
+ it('calls update record if there is no pending record', async () => {
+ const { result } = renderHook(() => useUpsertRecord(), {
+ wrapper: ({ children }) =>
+ Wrapper({
+ pendingRecordIdMockedValue: null,
+ draftValueMockedValue: null,
+ children,
+ }),
+ });
+
+ await act(async () => {
+ await result.current.upsertRecord(updateOneRecordMock);
+ });
+
+ expect(createOneRecordMock).not.toHaveBeenCalled();
+ expect(updateOneRecordMock).toHaveBeenCalled();
+ });
+
+ it('calls update record if pending record is empty', async () => {
+ const { result } = renderHook(() => useUpsertRecord(), {
+ wrapper: ({ children }) =>
+ Wrapper({
+ pendingRecordIdMockedValue: null,
+ draftValueMockedValue: draftValue,
+ children,
+ }),
+ });
+
+ await act(async () => {
+ await result.current.upsertRecord(updateOneRecordMock);
+ });
+
+ expect(createOneRecordMock).not.toHaveBeenCalled();
+ expect(updateOneRecordMock).toHaveBeenCalled();
+ });
+
+ it('calls create record if pending record is not empty', async () => {
+ const { result } = renderHook(() => useUpsertRecord(), {
+ wrapper: ({ children }) =>
+ Wrapper({
+ pendingRecordIdMockedValue: pendingRecordId,
+ draftValueMockedValue: draftValue,
+ children,
+ }),
+ });
+
+ await act(async () => {
+ await result.current.upsertRecord(updateOneRecordMock);
+ });
+
+ expect(createOneRecordMock).toHaveBeenCalledWith({
+ id: pendingRecordId,
+ name: draftValue,
+ position: 'first',
+ });
+ expect(updateOneRecordMock).not.toHaveBeenCalled();
+ });
+});
diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/useCloseRecordTableCell.ts b/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/useCloseRecordTableCell.ts
index 9ba618747..029366626 100644
--- a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/useCloseRecordTableCell.ts
+++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/useCloseRecordTableCell.ts
@@ -1,3 +1,6 @@
+import { useResetRecoilState } from 'recoil';
+
+import { useRecordTableStates } from '@/object-record/record-table/hooks/internal/useRecordTableStates';
import { useDragSelect } from '@/ui/utilities/drag-select/hooks/useDragSelect';
import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope';
@@ -7,13 +10,17 @@ import { TableHotkeyScope } from '../../types/TableHotkeyScope';
export const useCloseRecordTableCell = () => {
const setHotkeyScope = useSetHotkeyScope();
const { setDragSelectionStartEnabled } = useDragSelect();
+ const { pendingRecordIdState } = useRecordTableStates();
const closeCurrentTableCellInEditMode = useCloseCurrentTableCellInEditMode();
+ const resetRecordTablePendingRecordId =
+ useResetRecoilState(pendingRecordIdState);
const closeTableCell = async () => {
setDragSelectionStartEnabled(true);
closeCurrentTableCellInEditMode();
setHotkeyScope(TableHotkeyScope.TableSoftFocus);
+ resetRecordTablePendingRecordId();
};
return {
diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/useUpsertRecord.ts b/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/useUpsertRecord.ts
new file mode 100644
index 000000000..230e4a5d3
--- /dev/null
+++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/useUpsertRecord.ts
@@ -0,0 +1,41 @@
+import { useContext } from 'react';
+import { useRecoilValue } from 'recoil';
+
+import { useCreateOneRecord } from '@/object-record/hooks/useCreateOneRecord';
+import { FieldContext } from '@/object-record/record-field/contexts/FieldContext';
+import { useRecordFieldInputStates } from '@/object-record/record-field/hooks/internal/useRecordFieldInputStates';
+import { useRecordTableStates } from '@/object-record/record-table/hooks/internal/useRecordTableStates';
+import { isDefined } from '~/utils/isDefined';
+
+export const useUpsertRecord = () => {
+ const { entityId, fieldDefinition } = useContext(FieldContext);
+
+ const { pendingRecordIdState } = useRecordTableStates();
+
+ const pendingRecordId = useRecoilValue(pendingRecordIdState);
+ const fieldName = fieldDefinition.metadata.fieldName;
+ const { getDraftValueSelector } = useRecordFieldInputStates(
+ `${entityId}-${fieldName}`,
+ );
+ const draftValue = useRecoilValue(getDraftValueSelector());
+
+ const objectNameSingular =
+ fieldDefinition.metadata.objectMetadataNameSingular ?? '';
+ const { createOneRecord } = useCreateOneRecord({
+ objectNameSingular,
+ });
+
+ const upsertRecord = (persistField: () => void) => {
+ if (isDefined(pendingRecordId) && isDefined(draftValue)) {
+ createOneRecord({
+ id: pendingRecordId,
+ name: draftValue,
+ position: 'first',
+ });
+ } else if (!pendingRecordId) {
+ persistField();
+ }
+ };
+
+ return { upsertRecord };
+};
diff --git a/packages/twenty-front/src/modules/object-record/record-table/states/recordTablePendingRecordIdComponentState.ts b/packages/twenty-front/src/modules/object-record/record-table/states/recordTablePendingRecordIdComponentState.ts
new file mode 100644
index 000000000..4e594933f
--- /dev/null
+++ b/packages/twenty-front/src/modules/object-record/record-table/states/recordTablePendingRecordIdComponentState.ts
@@ -0,0 +1,8 @@
+import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState';
+
+export const recordTablePendingRecordIdComponentState = createComponentState<
+ string | null
+>({
+ key: 'recordTablePendingRecordIdState',
+ defaultValue: null,
+});
diff --git a/packages/twenty-front/src/pages/object-record/RecordIndexPage.tsx b/packages/twenty-front/src/pages/object-record/RecordIndexPage.tsx
index 286ca0728..dbd2aacba 100644
--- a/packages/twenty-front/src/pages/object-record/RecordIndexPage.tsx
+++ b/packages/twenty-front/src/pages/object-record/RecordIndexPage.tsx
@@ -1,9 +1,9 @@
import { useParams } from 'react-router-dom';
import styled from '@emotion/styled';
+import { v4 } from 'uuid';
-import { useObjectNameSingularFromPlural } from '@/object-metadata/hooks/useObjectNameSingularFromPlural';
-import { useCreateOneRecord } from '@/object-record/hooks/useCreateOneRecord';
import { RecordIndexContainer } from '@/object-record/record-index/components/RecordIndexContainer';
+import { useRecordTable } from '@/object-record/record-table/hooks/useRecordTable';
import { DEFAULT_CELL_SCOPE } from '@/object-record/record-table/record-table-cell/hooks/useOpenRecordTableCell';
import { useSelectedTableCellEditMode } from '@/object-record/record-table/record-table-cell/hooks/useSelectedTableCellEditMode';
import { PageBody } from '@/ui/layout/page/PageBody';
@@ -20,14 +20,6 @@ const StyledIndexContainer = styled.div`
export const RecordIndexPage = () => {
const objectNamePlural = useParams().objectNamePlural ?? '';
- const { objectNameSingular } = useObjectNameSingularFromPlural({
- objectNamePlural,
- });
-
- const { createOneRecord: createOneObject } = useCreateOneRecord({
- objectNameSingular,
- });
-
const recordIndexId = objectNamePlural ?? '';
const setHotkeyScope = useSetHotkeyScope();
@@ -35,12 +27,14 @@ export const RecordIndexPage = () => {
scopeId: recordIndexId,
});
- const handleAddButtonClick = async () => {
- await createOneObject?.({
- position: 'first',
- });
+ const { setPendingRecordId } = useRecordTable({
+ recordTableId: recordIndexId,
+ });
- setSelectedTableCellEditMode(0, 0);
+ const handleAddButtonClick = async () => {
+ setPendingRecordId(v4());
+
+ setSelectedTableCellEditMode(-1, 0);
setHotkeyScope(DEFAULT_CELL_SCOPE.scope, DEFAULT_CELL_SCOPE.customScopes);
};