diff --git a/packages/twenty-front/src/modules/object-record/record-table/components/RecordTable.tsx b/packages/twenty-front/src/modules/object-record/record-table/components/RecordTable.tsx
index 5407b756d..8e5db342d 100644
--- a/packages/twenty-front/src/modules/object-record/record-table/components/RecordTable.tsx
+++ b/packages/twenty-front/src/modules/object-record/record-table/components/RecordTable.tsx
@@ -1,12 +1,15 @@
import styled from '@emotion/styled';
-import { isNonEmptyString } from '@sniptt/guards';
+import { isNonEmptyString, isNull } from '@sniptt/guards';
+import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
+import { RecordTableContextProvider } from '@/object-record/record-table/components/RecordTableContextProvider';
+import { RecordTableEmptyState } from '@/object-record/record-table/components/RecordTableEmptyState';
+import { useRecordTableStates } from '@/object-record/record-table/hooks/internal/useRecordTableStates';
import { RecordTableBody } from '@/object-record/record-table/record-table-body/components/RecordTableBody';
import { RecordTableBodyEffect } from '@/object-record/record-table/record-table-body/components/RecordTableBodyEffect';
-import { RecordTableContextProvider } from '@/object-record/record-table/components/RecordTableContextProvider';
import { RecordTableHeader } from '@/object-record/record-table/record-table-header/components/RecordTableHeader';
-import { useRecordTableStates } from '@/object-record/record-table/hooks/internal/useRecordTableStates';
import { RecordTableScope } from '@/object-record/record-table/scopes/RecordTableScope';
+import { useRecoilValue } from 'recoil';
const StyledTable = styled.table`
border-radius: ${({ theme }) => theme.border.radius.sm};
@@ -32,6 +35,27 @@ export const RecordTable = ({
}: RecordTableProps) => {
const { scopeId } = useRecordTableStates(recordTableId);
+ const {
+ isRecordTableInitialLoadingState,
+ tableRowIdsState,
+ pendingRecordIdState,
+ } = useRecordTableStates(recordTableId);
+
+ const isRecordTableInitialLoading = useRecoilValue(
+ isRecordTableInitialLoadingState,
+ );
+
+ const tableRowIds = useRecoilValue(tableRowIdsState);
+
+ const pendingRecordId = useRecoilValue(pendingRecordIdState);
+
+ const { objectMetadataItem: foundObjectMetadataItem } = useObjectMetadataItem(
+ { objectNameSingular },
+ );
+
+ const objectLabel = foundObjectMetadataItem?.labelSingular;
+ const isRemote = foundObjectMetadataItem?.isRemote ?? false;
+
if (!isNonEmptyString(objectNameSingular)) {
return <>>;
}
@@ -45,11 +69,22 @@ export const RecordTable = ({
objectNameSingular={objectNameSingular}
recordTableId={recordTableId}
>
-
-
-
-
-
+
+ {!isRecordTableInitialLoading &&
+ tableRowIds.length === 0 &&
+ isNull(pendingRecordId) ? (
+
+ ) : (
+
+
+
+
+ )}
);
diff --git a/packages/twenty-front/src/modules/object-record/record-table/components/RecordTableContextProvider.tsx b/packages/twenty-front/src/modules/object-record/record-table/components/RecordTableContextProvider.tsx
index 7925fb8fb..47fa787d2 100644
--- a/packages/twenty-front/src/modules/object-record/record-table/components/RecordTableContextProvider.tsx
+++ b/packages/twenty-front/src/modules/object-record/record-table/components/RecordTableContextProvider.tsx
@@ -13,7 +13,7 @@ import {
useOpenRecordTableCellV2,
} from '@/object-record/record-table/record-table-cell/hooks/useOpenRecordTableCellV2';
import { useTriggerContextMenu } from '@/object-record/record-table/record-table-cell/hooks/useTriggerContextMenu';
-import { useUpsertRecordV2 } from '@/object-record/record-table/record-table-cell/hooks/useUpsertRecordV2';
+import { useUpsertRecord } from '@/object-record/record-table/record-table-cell/hooks/useUpsertRecord';
import { MoveFocusDirection } from '@/object-record/record-table/types/MoveFocusDirection';
import { TableCellPosition } from '@/object-record/record-table/types/TableCellPosition';
@@ -32,7 +32,7 @@ export const RecordTableContextProvider = ({
objectNameSingular,
});
- const { upsertRecord } = useUpsertRecordV2({
+ const { upsertRecord } = useUpsertRecord({
objectNameSingular,
});
diff --git a/packages/twenty-front/src/modules/object-record/record-table/components/RecordTableWithWrappers.tsx b/packages/twenty-front/src/modules/object-record/record-table/components/RecordTableWithWrappers.tsx
index 2161a2fe0..7a6cabbdd 100644
--- a/packages/twenty-front/src/modules/object-record/record-table/components/RecordTableWithWrappers.tsx
+++ b/packages/twenty-front/src/modules/object-record/record-table/components/RecordTableWithWrappers.tsx
@@ -1,14 +1,11 @@
-import { useRef } from 'react';
import styled from '@emotion/styled';
-import { useRecoilCallback, useRecoilValue } from 'recoil';
+import { useRef } from 'react';
+import { useRecoilCallback } from 'recoil';
-import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
import { useDeleteOneRecord } from '@/object-record/hooks/useDeleteOneRecord';
import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata';
import { RecordTable } from '@/object-record/record-table/components/RecordTable';
-import { RecordTableEmptyState } from '@/object-record/record-table/components/RecordTableEmptyState';
import { EntityDeleteContext } from '@/object-record/record-table/contexts/EntityDeleteHookContext';
-import { useRecordTableStates } from '@/object-record/record-table/hooks/internal/useRecordTableStates';
import { ColumnDefinition } from '@/object-record/record-table/types/ColumnDefinition';
import { DragSelect } from '@/ui/utilities/drag-select/components/DragSelect';
import { ScrollWrapper } from '@/ui/utilities/scroll/components/ScrollWrapper';
@@ -31,6 +28,10 @@ const StyledTableContainer = styled.div`
position: relative;
`;
+const StyledTableInternalContainer = styled.div`
+ height: 100%;
+`;
+
type RecordTableWithWrappersProps = {
objectNameSingular: string;
recordTableId: string;
@@ -48,33 +49,14 @@ export const RecordTableWithWrappers = ({
}: RecordTableWithWrappersProps) => {
const tableBodyRef = useRef(null);
- const { isRecordTableInitialLoadingState, tableRowIdsState } =
- useRecordTableStates(recordTableId);
-
- const isRecordTableInitialLoading = useRecoilValue(
- isRecordTableInitialLoadingState,
- );
-
- const tableRowIds = useRecoilValue(tableRowIdsState);
-
const { resetTableRowSelection, setRowSelected } = useRecordTable({
recordTableId,
});
- const { objectMetadataItem: foundObjectMetadataItem } = useObjectMetadataItem(
- {
- objectNameSingular,
- },
- );
-
const { saveViewFields } = useSaveCurrentViewFields(viewBarId);
const { deleteOneRecord } = useDeleteOneRecord({ objectNameSingular });
- const objectLabel = foundObjectMetadataItem?.labelSingular;
-
- const isRemote = foundObjectMetadataItem?.isRemote ?? false;
-
const handleColumnsChange = useRecoilCallback(
() => (columns) => {
saveViewFields(
@@ -86,24 +68,13 @@ export const RecordTableWithWrappers = ({
[saveViewFields],
);
- if (!isRecordTableInitialLoading && tableRowIds.length === 0) {
- return (
-
- );
- }
-
return (
-
+
-
+
({
__esModule: true,
useCreateOneRecord: jest.fn(),
@@ -93,17 +94,25 @@ describe('useUpsertRecord', () => {
});
it('calls update record if there is no pending record', async () => {
- const { result } = renderHook(() => useUpsertRecord(), {
- wrapper: ({ children }) =>
- Wrapper({
- pendingRecordIdMockedValue: null,
- draftValueMockedValue: null,
- children,
- }),
- });
+ const { result } = renderHook(
+ () => useUpsertRecord({ objectNameSingular: 'person' }),
+ {
+ wrapper: ({ children }) =>
+ Wrapper({
+ pendingRecordIdMockedValue: null,
+ draftValueMockedValue: null,
+ children,
+ }),
+ },
+ );
await act(async () => {
- await result.current.upsertRecord(updateOneRecordMock);
+ await result.current.upsertRecord(
+ updateOneRecordMock,
+ 'entityId',
+ 'name',
+ 'recordTableId',
+ );
});
expect(createOneRecordMock).not.toHaveBeenCalled();
@@ -111,42 +120,28 @@ describe('useUpsertRecord', () => {
});
it('calls update record if pending record is empty', async () => {
- const { result } = renderHook(() => useUpsertRecord(), {
- wrapper: ({ children }) =>
- Wrapper({
- pendingRecordIdMockedValue: null,
- draftValueMockedValue: draftValue,
- children,
- }),
- });
+ const { result } = renderHook(
+ () => useUpsertRecord({ objectNameSingular: 'person' }),
+ {
+ wrapper: ({ children }) =>
+ Wrapper({
+ pendingRecordIdMockedValue: null,
+ draftValueMockedValue: draftValue,
+ children,
+ }),
+ },
+ );
await act(async () => {
- await result.current.upsertRecord(updateOneRecordMock);
+ await result.current.upsertRecord(
+ updateOneRecordMock,
+ 'entityId',
+ 'name',
+ 'recordTableId',
+ );
});
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/useUpsertRecord.ts b/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/useUpsertRecord.ts
index 230e4a5d3..553c6aa83 100644
--- 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
@@ -1,41 +1,65 @@
-import { useContext } from 'react';
-import { useRecoilValue } from 'recoil';
+import { useRecoilCallback } 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 { recordFieldInputDraftValueComponentSelector } from '@/object-record/record-field/states/selectors/recordFieldInputDraftValueComponentSelector';
+import { recordTablePendingRecordIdComponentState } from '@/object-record/record-table/states/recordTablePendingRecordIdComponentState';
+import { getScopeIdFromComponentId } from '@/ui/utilities/recoil-scope/utils/getScopeIdFromComponentId';
+import { getSnapshotValue } from '@/ui/utilities/recoil-scope/utils/getSnapshotValue';
+import { extractComponentSelector } from '@/ui/utilities/state/component-state/utils/extractComponentSelector';
+import { extractComponentState } from '@/ui/utilities/state/component-state/utils/extractComponentState';
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 ?? '';
+export const useUpsertRecord = ({
+ objectNameSingular,
+}: {
+ objectNameSingular: string;
+}) => {
const { createOneRecord } = useCreateOneRecord({
objectNameSingular,
});
- const upsertRecord = (persistField: () => void) => {
- if (isDefined(pendingRecordId) && isDefined(draftValue)) {
- createOneRecord({
- id: pendingRecordId,
- name: draftValue,
- position: 'first',
- });
- } else if (!pendingRecordId) {
- persistField();
- }
- };
+ const upsertRecord = useRecoilCallback(
+ ({ snapshot }) =>
+ (
+ persistField: () => void,
+ entityId: string,
+ fieldName: string,
+ recordTableId: string,
+ ) => {
+ const tableScopeId = getScopeIdFromComponentId(recordTableId);
+
+ const recordTablePendingRecordIdState = extractComponentState(
+ recordTablePendingRecordIdComponentState,
+ tableScopeId,
+ );
+
+ const recordTablePendingRecordId = getSnapshotValue(
+ snapshot,
+ recordTablePendingRecordIdState,
+ );
+ const fieldScopeId = getScopeIdFromComponentId(
+ `${entityId}-${fieldName}`,
+ );
+
+ const draftValueSelector = extractComponentSelector(
+ recordFieldInputDraftValueComponentSelector,
+ fieldScopeId,
+ );
+
+ const draftValue = getSnapshotValue(snapshot, draftValueSelector());
+
+ if (isDefined(recordTablePendingRecordId) && isDefined(draftValue)) {
+ createOneRecord({
+ id: recordTablePendingRecordId,
+ name: draftValue,
+ position: 'first',
+ });
+ } else if (!recordTablePendingRecordId) {
+ persistField();
+ }
+ },
+ [createOneRecord],
+ );
return { upsertRecord };
};
diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/useUpsertRecordV2.ts b/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/useUpsertRecordV2.ts
deleted file mode 100644
index c0b714ab2..000000000
--- a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/useUpsertRecordV2.ts
+++ /dev/null
@@ -1,65 +0,0 @@
-import { useRecoilCallback } from 'recoil';
-
-import { useCreateOneRecord } from '@/object-record/hooks/useCreateOneRecord';
-import { recordFieldInputDraftValueComponentSelector } from '@/object-record/record-field/states/selectors/recordFieldInputDraftValueComponentSelector';
-import { recordTablePendingRecordIdComponentState } from '@/object-record/record-table/states/recordTablePendingRecordIdComponentState';
-import { getScopeIdFromComponentId } from '@/ui/utilities/recoil-scope/utils/getScopeIdFromComponentId';
-import { getSnapshotValue } from '@/ui/utilities/recoil-scope/utils/getSnapshotValue';
-import { extractComponentSelector } from '@/ui/utilities/state/component-state/utils/extractComponentSelector';
-import { extractComponentState } from '@/ui/utilities/state/component-state/utils/extractComponentState';
-import { isDefined } from '~/utils/isDefined';
-
-export const useUpsertRecordV2 = ({
- objectNameSingular,
-}: {
- objectNameSingular: string;
-}) => {
- const { createOneRecord } = useCreateOneRecord({
- objectNameSingular,
- });
-
- const upsertRecord = useRecoilCallback(
- ({ snapshot }) =>
- (
- persistField: () => void,
- entityId: string,
- fieldName: string,
- recordTableId: string,
- ) => {
- const tableScopeId = getScopeIdFromComponentId(recordTableId);
-
- const recordTablePendingRecordIdState = extractComponentState(
- recordTablePendingRecordIdComponentState,
- tableScopeId,
- );
-
- const recordTablePendingRecordId = getSnapshotValue(
- snapshot,
- recordTablePendingRecordIdState,
- );
- const fieldScopeId = getScopeIdFromComponentId(
- `${entityId}-${fieldName}`,
- );
-
- const draftValueSelector = extractComponentSelector(
- recordFieldInputDraftValueComponentSelector,
- fieldScopeId,
- );
-
- const draftValue = getSnapshotValue(snapshot, draftValueSelector());
-
- if (isDefined(recordTablePendingRecordId) && isDefined(draftValue)) {
- createOneRecord({
- id: recordTablePendingRecordId,
- name: draftValue,
- position: 'first',
- });
- } else if (!recordTablePendingRecordId) {
- persistField();
- }
- },
- [createOneRecord],
- );
-
- return { upsertRecord };
-};
diff --git a/packages/twenty-front/src/pages/object-record/RecordIndexPage.tsx b/packages/twenty-front/src/pages/object-record/RecordIndexPage.tsx
index bcb3508dc..2e4a0268d 100644
--- a/packages/twenty-front/src/pages/object-record/RecordIndexPage.tsx
+++ b/packages/twenty-front/src/pages/object-record/RecordIndexPage.tsx
@@ -1,5 +1,5 @@
-import { useParams } from 'react-router-dom';
import styled from '@emotion/styled';
+import { useParams } from 'react-router-dom';
import { v4 } from 'uuid';
import { RecordIndexContainer } from '@/object-record/record-index/components/RecordIndexContainer';