diff --git a/packages/twenty-front/src/modules/object-record/record-field/__mocks__/fieldDefinitions.ts b/packages/twenty-front/src/modules/object-record/record-field/__mocks__/fieldDefinitions.ts index 0fe2fc3caa..03e81bf66f 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/__mocks__/fieldDefinitions.ts +++ b/packages/twenty-front/src/modules/object-record/record-field/__mocks__/fieldDefinitions.ts @@ -108,5 +108,6 @@ export const actorFieldDefinition: FieldDefinition = { defaultValue: { source: 'MANUAL', name: '' }, metadata: { fieldName: 'actor', + objectMetadataNameSingular: 'person', }, }; diff --git a/packages/twenty-front/src/modules/object-record/record-field/hooks/__tests__/useIsFieldReadOnly.test.tsx b/packages/twenty-front/src/modules/object-record/record-field/hooks/__tests__/useIsFieldReadOnly.test.tsx deleted file mode 100644 index f51cf795f8..0000000000 --- a/packages/twenty-front/src/modules/object-record/record-field/hooks/__tests__/useIsFieldReadOnly.test.tsx +++ /dev/null @@ -1,50 +0,0 @@ -import { renderHook } from '@testing-library/react'; -import { ReactNode } from 'react'; -import { RecoilRoot } from 'recoil'; - -import { - actorFieldDefinition, - phonesFieldDefinition, -} from '@/object-record/record-field/__mocks__/fieldDefinitions'; -import { FieldContext } from '@/object-record/record-field/contexts/FieldContext'; -import { useIsFieldReadOnly } from '@/object-record/record-field/hooks/useIsFieldReadOnly'; -import { FieldDefinition } from '@/object-record/record-field/types/FieldDefinition'; -import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata'; - -const recordId = 'recordId'; - -const getWrapper = - (fieldDefinition: FieldDefinition) => - ({ children }: { children: ReactNode }) => ( - - {children} - - ); - -const ActorWrapper = getWrapper(actorFieldDefinition); -const PhoneWrapper = getWrapper(phonesFieldDefinition); - -describe('useIsFieldReadOnly', () => { - it('should return true', () => { - const { result } = renderHook(() => useIsFieldReadOnly(), { - wrapper: ActorWrapper, - }); - - expect(result.current).toBe(true); - }); - - it('should return false', () => { - const { result } = renderHook(() => useIsFieldReadOnly(), { - wrapper: PhoneWrapper, - }); - - expect(result.current).toBe(false); - }); -}); diff --git a/packages/twenty-front/src/modules/object-record/record-field/hooks/__tests__/useIsFieldValueReadOnly.test.tsx b/packages/twenty-front/src/modules/object-record/record-field/hooks/__tests__/useIsFieldValueReadOnly.test.tsx new file mode 100644 index 0000000000..a436b97535 --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-field/hooks/__tests__/useIsFieldValueReadOnly.test.tsx @@ -0,0 +1,74 @@ +import { renderHook } from '@testing-library/react'; +import { ReactNode } from 'react'; +import { RecoilRoot } from 'recoil'; + +import { + actorFieldDefinition, + phonesFieldDefinition, +} from '@/object-record/record-field/__mocks__/fieldDefinitions'; +import { FieldContext } from '@/object-record/record-field/contexts/FieldContext'; +import { FieldDefinition } from '@/object-record/record-field/types/FieldDefinition'; +import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata'; + +import { ObjectRecord } from '@/object-record/types/ObjectRecord'; +import { JestObjectMetadataItemSetter } from '~/testing/jest/JestObjectMetadataItemSetter'; +import { JestRecordStoreSetter } from '~/testing/jest/JestRecordStoreSetter'; + +import { useIsFieldValueReadOnly } from '../useIsFieldValueReadOnly'; + +const recordId = 'recordId'; + +const getWrapper = + (fieldDefinition: FieldDefinition, isRecordDeleted: boolean) => + ({ children }: { children: ReactNode }) => { + return ( + + + + + {children} + + + + + ); + }; + +describe('useIsFieldValueReadOnly', () => { + it('should take fieldDefinition into account', () => { + const { result } = renderHook(() => useIsFieldValueReadOnly(), { + wrapper: getWrapper(phonesFieldDefinition, false), + }); + + expect(result.current).toBe(false); + + const { result: result2 } = renderHook(() => useIsFieldValueReadOnly(), { + wrapper: getWrapper(actorFieldDefinition, false), + }); + + expect(result2.current).toBe(true); + }); + + it('should take isRecordDeleted into account', () => { + const { result } = renderHook(() => useIsFieldValueReadOnly(), { + wrapper: getWrapper(phonesFieldDefinition, true), + }); + + expect(result.current).toBe(true); + }); +}); diff --git a/packages/twenty-front/src/modules/object-record/record-field/hooks/useIsFieldReadOnly.ts b/packages/twenty-front/src/modules/object-record/record-field/hooks/useIsFieldReadOnly.ts deleted file mode 100644 index 48bb034244..0000000000 --- a/packages/twenty-front/src/modules/object-record/record-field/hooks/useIsFieldReadOnly.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { useContext } from 'react'; - -import { isFieldActor } from '@/object-record/record-field/types/guards/isFieldActor'; -import { isFieldRichText } from '@/object-record/record-field/types/guards/isFieldRichText'; -import { FieldContext } from '../contexts/FieldContext'; -import { isFieldMetadataReadOnly } from '../utils/isFieldMetadataReadOnly'; - -export const useIsFieldReadOnly = () => { - const { fieldDefinition } = useContext(FieldContext); - - const { metadata } = fieldDefinition; - - return ( - isFieldActor(fieldDefinition) || - isFieldRichText(fieldDefinition) || - isFieldMetadataReadOnly(metadata) - ); -}; diff --git a/packages/twenty-front/src/modules/object-record/record-field/hooks/useIsFieldValueReadOnly.ts b/packages/twenty-front/src/modules/object-record/record-field/hooks/useIsFieldValueReadOnly.ts new file mode 100644 index 0000000000..aa8ec3515a --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-field/hooks/useIsFieldValueReadOnly.ts @@ -0,0 +1,30 @@ +import { useContext } from 'react'; + +import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem'; +import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState'; +import { ObjectRecord } from '@/object-record/types/ObjectRecord'; +import { useRecoilValue } from 'recoil'; +import { FieldContext } from '../contexts/FieldContext'; +import { isFieldValueReadOnly } from '../utils/isFieldValueReadOnly'; + +export const useIsFieldValueReadOnly = () => { + const { fieldDefinition, recordId } = useContext(FieldContext); + + const { metadata, type } = fieldDefinition; + + const recordFromStore = useRecoilValue( + recordStoreFamilyState(recordId), + ); + + const { objectMetadataItem } = useObjectMetadataItem({ + objectNameSingular: metadata.objectMetadataNameSingular ?? '', + }); + + return isFieldValueReadOnly({ + objectNameSingular: metadata.objectMetadataNameSingular, + fieldName: metadata.fieldName, + fieldType: type, + isObjectRemote: objectMetadataItem.isRemote, + isRecordDeleted: recordFromStore?.deletedAt, + }); +}; diff --git a/packages/twenty-front/src/modules/object-record/record-field/utils/__tests__/isFieldValueReadOnly.test.ts b/packages/twenty-front/src/modules/object-record/record-field/utils/__tests__/isFieldValueReadOnly.test.ts new file mode 100644 index 0000000000..a5dff94f7a --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-field/utils/__tests__/isFieldValueReadOnly.test.ts @@ -0,0 +1,113 @@ +import { isFieldValueReadOnly } from '@/object-record/record-field/utils/isFieldValueReadOnly'; +import { FieldMetadataType } from '~/generated/graphql'; + +describe('isFieldValueReadOnly', () => { + it('should return true if fieldName is noteTargets or taskTargets', () => { + const result = isFieldValueReadOnly({ + fieldName: 'noteTargets', + }); + expect(result).toBe(true); + + const result2 = isFieldValueReadOnly({ + fieldName: 'taskTargets', + }); + + expect(result2).toBe(true); + }); + + it('should return false if fieldName is not noteTargets or taskTargets', () => { + const result = isFieldValueReadOnly({ + fieldName: 'test', + }); + + expect(result).toBe(false); + }); + + it('should return true if isObjectRemote is true', () => { + const result = isFieldValueReadOnly({ + isObjectRemote: true, + }); + + expect(result).toBe(true); + }); + + it('should return false if isObjectRemote is false', () => { + const result = isFieldValueReadOnly({ + isObjectRemote: false, + }); + + expect(result).toBe(false); + }); + + it('should return true if isRecordDeleted is true', () => { + const result = isFieldValueReadOnly({ + isRecordDeleted: true, + }); + + expect(result).toBe(true); + }); + + it('should return false if isRecordDeleted is false', () => { + const result = isFieldValueReadOnly({ + isRecordDeleted: false, + }); + + expect(result).toBe(false); + }); + + it('should return true if objectNameSingular is Workflow and fieldName is not name', () => { + const result = isFieldValueReadOnly({ + objectNameSingular: 'workflow', + fieldName: 'test', + }); + + expect(result).toBe(true); + }); + + it('should return false if objectNameSingular is Workflow and fieldName is name', () => { + const result = isFieldValueReadOnly({ + objectNameSingular: 'Workflow', + fieldName: 'name', + }); + + expect(result).toBe(false); + }); + + it('should return true if isWorkflowSubObjectMetadata is true', () => { + const result = isFieldValueReadOnly({ + objectNameSingular: 'workflowVersion', + }); + + expect(result).toBe(true); + }); + + it('should return true if fieldType is FieldMetadataType.Actor', () => { + const result = isFieldValueReadOnly({ + fieldType: FieldMetadataType.Actor, + }); + + expect(result).toBe(true); + }); + + it('should return true if fieldType is FieldMetadataType.RichText', () => { + const result = isFieldValueReadOnly({ + fieldType: FieldMetadataType.RichText, + }); + + expect(result).toBe(true); + }); + + it('should return false if fieldType is not FieldMetadataType.Actor or FieldMetadataType.RichText', () => { + const result = isFieldValueReadOnly({ + fieldType: FieldMetadataType.Text, + }); + + expect(result).toBe(false); + }); + + it('should return false if none of the conditions are met', () => { + const result = isFieldValueReadOnly({}); + + expect(result).toBe(false); + }); +}); diff --git a/packages/twenty-front/src/modules/object-record/record-field/utils/isFieldMetadataReadOnly.ts b/packages/twenty-front/src/modules/object-record/record-field/utils/isFieldMetadataReadOnly.ts deleted file mode 100644 index a24ed10035..0000000000 --- a/packages/twenty-front/src/modules/object-record/record-field/utils/isFieldMetadataReadOnly.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; -import { isWorkflowSubObjectMetadata } from '@/object-metadata/utils/isWorkflowSubObjectMetadata'; -import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata'; - -export const isFieldMetadataReadOnly = (fieldMetadata: FieldMetadata) => { - if ( - fieldMetadata.fieldName === 'noteTargets' || - fieldMetadata.fieldName === 'taskTargets' - ) { - return true; - } - - return ( - isWorkflowSubObjectMetadata(fieldMetadata.objectMetadataNameSingular) || - (fieldMetadata.objectMetadataNameSingular === - CoreObjectNameSingular.Workflow && - fieldMetadata.fieldName !== 'name') - ); -}; diff --git a/packages/twenty-front/src/modules/object-record/record-field/utils/isFieldValueReadOnly.ts b/packages/twenty-front/src/modules/object-record/record-field/utils/isFieldValueReadOnly.ts new file mode 100644 index 0000000000..e3e59574d3 --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-field/utils/isFieldValueReadOnly.ts @@ -0,0 +1,54 @@ +import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; +import { isWorkflowSubObjectMetadata } from '@/object-metadata/utils/isWorkflowSubObjectMetadata'; +import { isFieldActor } from '@/object-record/record-field/types/guards/isFieldActor'; +import { isFieldRichText } from '@/object-record/record-field/types/guards/isFieldRichText'; +import { isDefined } from 'twenty-ui'; +import { FieldMetadataType } from '~/generated-metadata/graphql'; + +type isFieldValueReadOnlyParams = { + objectNameSingular?: string; + fieldName?: string; + fieldType?: FieldMetadataType; + isObjectRemote?: boolean; + isRecordDeleted?: boolean; +}; + +export const isFieldValueReadOnly = ({ + objectNameSingular, + fieldName, + fieldType, + isObjectRemote = false, + isRecordDeleted = false, +}: isFieldValueReadOnlyParams) => { + if (fieldName === 'noteTargets' || fieldName === 'taskTargets') { + return true; + } + + if (isObjectRemote) { + return true; + } + + if (isRecordDeleted) { + return true; + } + + if (isWorkflowSubObjectMetadata(objectNameSingular)) { + return true; + } + + if ( + objectNameSingular === CoreObjectNameSingular.Workflow && + fieldName !== 'name' + ) { + return true; + } + + if ( + isDefined(fieldType) && + (isFieldActor({ type: fieldType }) || isFieldRichText({ type: fieldType })) + ) { + return true; + } + + return false; +}; diff --git a/packages/twenty-front/src/modules/object-record/record-index/hooks/useRecordBoardRecordGqlFields.ts b/packages/twenty-front/src/modules/object-record/record-index/hooks/useRecordBoardRecordGqlFields.ts index 64433aae26..aef238c2be 100644 --- a/packages/twenty-front/src/modules/object-record/record-index/hooks/useRecordBoardRecordGqlFields.ts +++ b/packages/twenty-front/src/modules/object-record/record-index/hooks/useRecordBoardRecordGqlFields.ts @@ -36,6 +36,7 @@ export const useRecordBoardRecordGqlFields = ({ const recordGqlFields: Record = { id: true, + deletedAt: true, ...Object.fromEntries( visibleFieldDefinitions.map((visibleFieldDefinition) => [ visibleFieldDefinition.metadata.fieldName, diff --git a/packages/twenty-front/src/modules/object-record/record-index/hooks/useRecordTableRecordGqlFields.ts b/packages/twenty-front/src/modules/object-record/record-index/hooks/useRecordTableRecordGqlFields.ts index ae07877447..a4588c6d5f 100644 --- a/packages/twenty-front/src/modules/object-record/record-index/hooks/useRecordTableRecordGqlFields.ts +++ b/packages/twenty-front/src/modules/object-record/record-index/hooks/useRecordTableRecordGqlFields.ts @@ -41,6 +41,7 @@ export const useRecordTableRecordGqlFields = ({ const recordGqlFields: Record = { id: true, + deletedAt: true, ...Object.fromEntries( visibleTableColumns.map((column) => [column.metadata.fieldName, true]), ), diff --git a/packages/twenty-front/src/modules/object-record/record-inline-cell/components/RecordInlineCell.tsx b/packages/twenty-front/src/modules/object-record/record-inline-cell/components/RecordInlineCell.tsx index 55f3ccbf75..8b58072b0d 100644 --- a/packages/twenty-front/src/modules/object-record/record-inline-cell/components/RecordInlineCell.tsx +++ b/packages/twenty-front/src/modules/object-record/record-inline-cell/components/RecordInlineCell.tsx @@ -13,7 +13,7 @@ import { RelationPickerHotkeyScope } from '@/object-record/relation-picker/types import { useInlineCell } from '../hooks/useInlineCell'; -import { useIsFieldReadOnly } from '@/object-record/record-field/hooks/useIsFieldReadOnly'; +import { useIsFieldValueReadOnly } from '@/object-record/record-field/hooks/useIsFieldValueReadOnly'; import { getRecordFieldInputId } from '@/object-record/utils/getRecordFieldInputId'; import { RecordInlineCellContainer } from './RecordInlineCellContainer'; import { @@ -26,21 +26,16 @@ type RecordInlineCellProps = { loading?: boolean; }; -export const RecordInlineCell = ({ - readonly, - loading, -}: RecordInlineCellProps) => { +export const RecordInlineCell = ({ loading }: RecordInlineCellProps) => { const { fieldDefinition, recordId, isCentered } = useContext(FieldContext); const buttonIcon = useGetButtonIcon(); const isFieldInputOnly = useIsFieldInputOnly(); - const isFieldReadOnly = useIsFieldReadOnly(); + const isFieldReadOnly = useIsFieldValueReadOnly(); const { closeInlineCell } = useInlineCell(); - const cellIsReadOnly = readonly || isFieldReadOnly; - const handleEnter: FieldInputEvent = (persistField) => { persistField(); closeInlineCell(); @@ -77,7 +72,7 @@ export const RecordInlineCell = ({ const { getIcon } = useIcons(); const RecordInlineCellContextValue: RecordInlineCellContextProps = { - readonly: cellIsReadOnly, + readonly: isFieldReadOnly, buttonIcon: buttonIcon, customEditHotkeyScope: isFieldRelation(fieldDefinition) ? { scope: RelationPickerHotkeyScope.RelationPicker } @@ -102,7 +97,7 @@ export const RecordInlineCell = ({ onTab={handleTab} onShiftTab={handleShiftTab} onClickOutside={handleClickOutside} - isReadOnly={cellIsReadOnly} + isReadOnly={isFieldReadOnly} /> ), displayModeContent: , diff --git a/packages/twenty-front/src/modules/object-record/record-show/components/FieldsCard.tsx b/packages/twenty-front/src/modules/object-record/record-show/components/FieldsCard.tsx index 1e2d3504b9..3dfc2550fc 100644 --- a/packages/twenty-front/src/modules/object-record/record-show/components/FieldsCard.tsx +++ b/packages/twenty-front/src/modules/object-record/record-show/components/FieldsCard.tsx @@ -84,7 +84,6 @@ export const FieldsCard = ({ fieldMetadataItem.name === 'taskTargets') ), ); - const isReadOnly = objectMetadataItem.isRemote; return ( <> @@ -149,10 +148,7 @@ export const FieldsCard = ({ hotkeyScope: InlineCellHotkeyScope.InlineCell, }} > - + ))} diff --git a/packages/twenty-front/src/modules/object-record/record-show/components/SummaryCard.tsx b/packages/twenty-front/src/modules/object-record/record-show/components/SummaryCard.tsx index 493854b129..d681cf1991 100644 --- a/packages/twenty-front/src/modules/object-record/record-show/components/SummaryCard.tsx +++ b/packages/twenty-front/src/modules/object-record/record-show/components/SummaryCard.tsx @@ -1,6 +1,7 @@ import { useGetStandardObjectIcon } from '@/object-metadata/hooks/useGetStandardObjectIcon'; import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; import { FieldContext } from '@/object-record/record-field/contexts/FieldContext'; +import { isFieldValueReadOnly } from '@/object-record/record-field/utils/isFieldValueReadOnly'; import { RecordInlineCell } from '@/object-record/record-inline-cell/components/RecordInlineCell'; import { InlineCellHotkeyScope } from '@/object-record/record-inline-cell/types/InlineCellHotkeyScope'; import { RightDrawerTitleRecordInlineCell } from '@/object-record/record-right-drawer/components/RightDrawerTitleRecordInlineCell'; @@ -29,7 +30,6 @@ export const SummaryCard = ({ const { recordFromStore, recordLoading, - objectMetadataItem, labelIdentifierFieldMetadataItem, isPrefetchLoading, recordIdentifier, @@ -47,7 +47,11 @@ export const SummaryCard = ({ const { Icon, IconColor } = useGetStandardObjectIcon(objectNameSingular); const isMobile = useIsMobile() || isInRightDrawer; - const isReadOnly = objectMetadataItem.isRemote; + + const isReadOnly = isFieldValueReadOnly({ + objectNameSingular, + isRecordDeleted: recordFromStore?.isDeleted, + }); if (isNewRightDrawerItemLoading || !isDefined(recordFromStore)) { return ; diff --git a/packages/twenty-front/src/modules/object-record/record-show/record-detail-section/components/RecordDetailRelationRecordsListItem.tsx b/packages/twenty-front/src/modules/object-record/record-show/record-detail-section/components/RecordDetailRelationRecordsListItem.tsx index 3d51e9d382..6c9c2ed81d 100644 --- a/packages/twenty-front/src/modules/object-record/record-show/record-detail-section/components/RecordDetailRelationRecordsListItem.tsx +++ b/packages/twenty-front/src/modules/object-record/record-show/record-detail-section/components/RecordDetailRelationRecordsListItem.tsx @@ -25,9 +25,9 @@ import { RecordUpdateHook, RecordUpdateHookParams, } from '@/object-record/record-field/contexts/FieldContext'; +import { useIsFieldValueReadOnly } from '@/object-record/record-field/hooks/useIsFieldValueReadOnly'; import { usePersistField } from '@/object-record/record-field/hooks/usePersistField'; import { FieldRelationMetadata } from '@/object-record/record-field/types/FieldMetadata'; -import { isFieldMetadataReadOnly } from '@/object-record/record-field/utils/isFieldMetadataReadOnly'; import { RecordInlineCell } from '@/object-record/record-inline-cell/components/RecordInlineCell'; import { PropertyBox } from '@/object-record/record-inline-cell/property-box/components/PropertyBox'; import { InlineCellHotkeyScope } from '@/object-record/record-inline-cell/types/InlineCellHotkeyScope'; @@ -189,7 +189,7 @@ export const RecordDetailRelationRecordsListItem = ({ [isExpanded], ); - const canEdit = !isFieldMetadataReadOnly(fieldDefinition.metadata); + const isReadOnly = useIsFieldValueReadOnly(); return ( <> @@ -206,7 +206,7 @@ export const RecordDetailRelationRecordsListItem = ({ accent="tertiary" /> - {canEdit && ( + {!isReadOnly && ( 0} rightAdornment={ - canEdit && ( + !isReadOnly && ( { useContext(RecordTableContext); const { recordId, fieldDefinition } = useContext(FieldContext); - const isFieldReadOnly = useIsFieldReadOnly(); + const isFieldReadOnly = useIsFieldValueReadOnly(); const handleEnter: FieldInputEvent = (persistField) => { onUpsertRecord({ diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/components/RecordTableCellSoftFocusMode.tsx b/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/components/RecordTableCellSoftFocusMode.tsx index b401149862..d9c47b8268 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/components/RecordTableCellSoftFocusMode.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/components/RecordTableCellSoftFocusMode.tsx @@ -10,7 +10,6 @@ import { useIsFieldEmpty } from '@/object-record/record-field/hooks/useIsFieldEm import { useIsFieldInputOnly } from '@/object-record/record-field/hooks/useIsFieldInputOnly'; import { useToggleEditOnlyInput } from '@/object-record/record-field/hooks/useToggleEditOnlyInput'; import { RecordTableCellContext } from '@/object-record/record-table/contexts/RecordTableCellContext'; -import { RecordTableRowContext } from '@/object-record/record-table/contexts/RecordTableRowContext'; import { useCloseCurrentTableCellInEditMode } from '@/object-record/record-table/hooks/internal/useCloseCurrentTableCellInEditMode'; import { RecordTableCellButton } from '@/object-record/record-table/record-table-cell/components/RecordTableCellButton'; import { useOpenRecordTableCellFromCell } from '@/object-record/record-table/record-table-cell/hooks/useOpenRecordTableCellFromCell'; @@ -22,7 +21,7 @@ import { isDefined } from '~/utils/isDefined'; import { TableHotkeyScope } from '../../types/TableHotkeyScope'; -import { useIsFieldReadOnly } from '@/object-record/record-field/hooks/useIsFieldReadOnly'; +import { useIsFieldValueReadOnly } from '@/object-record/record-field/hooks/useIsFieldValueReadOnly'; import { RecordTableCellDisplayContainer } from './RecordTableCellDisplayContainer'; type RecordTableCellSoftFocusModeProps = { @@ -36,11 +35,8 @@ export const RecordTableCellSoftFocusMode = ({ }: RecordTableCellSoftFocusModeProps) => { const { columnIndex } = useContext(RecordTableCellContext); const closeCurrentTableCell = useCloseCurrentTableCellInEditMode(); - const { isReadOnly: isRowReadOnly } = useContext(RecordTableRowContext); - const isFieldReadOnly = useIsFieldReadOnly(); - - const isCellReadOnly = isFieldReadOnly || isRowReadOnly; + const isFieldReadOnly = useIsFieldValueReadOnly(); const { openTableCell } = useOpenRecordTableCellFromCell(); @@ -78,7 +74,7 @@ export const RecordTableCellSoftFocusMode = ({ useScopedHotkeys( Key.Enter, () => { - if (isCellReadOnly) { + if (isFieldReadOnly) { return; } @@ -95,7 +91,7 @@ export const RecordTableCellSoftFocusMode = ({ useScopedHotkeys( '*', (keyboardEvent) => { - if (isCellReadOnly) { + if (isFieldReadOnly) { return; } @@ -124,7 +120,7 @@ export const RecordTableCellSoftFocusMode = ({ ); const handleClick = () => { - if (!isFieldInputOnly && !isCellReadOnly) { + if (!isFieldInputOnly && !isFieldReadOnly) { openTableCell(); } }; @@ -156,9 +152,9 @@ export const RecordTableCellSoftFocusMode = ({ isDefined(buttonIcon) && !editModeContentOnly && (!isFirstColumn || !isEmpty) && - !isCellReadOnly; + !isFieldReadOnly; - const dontShowContent = isEmpty && isCellReadOnly; + const dontShowContent = isEmpty && isFieldReadOnly; return ( <> diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/__mocks__/cell.ts b/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/__mocks__/cell.ts index 9bbaeaec89..068a0ba42b 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/__mocks__/cell.ts +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/__mocks__/cell.ts @@ -8,7 +8,6 @@ export const recordTableRow: RecordTableRowContextProps = { recordId: 'recordId', pathToShowPage: '/', objectNameSingular: 'objectNameSingular', - isReadOnly: false, dragHandleProps: {} as any, isDragging: false, inView: true, diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/useOpenRecordTableCellFromCell.ts b/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/useOpenRecordTableCellFromCell.ts index 002d638cf2..67583b2f94 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/useOpenRecordTableCellFromCell.ts +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/useOpenRecordTableCellFromCell.ts @@ -1,7 +1,7 @@ import { useContext } from 'react'; import { FieldContext } from '@/object-record/record-field/contexts/FieldContext'; -import { useIsFieldReadOnly } from '@/object-record/record-field/hooks/useIsFieldReadOnly'; +import { useIsFieldValueReadOnly } from '@/object-record/record-field/hooks/useIsFieldValueReadOnly'; import { FieldDefinition } from '@/object-record/record-field/types/FieldDefinition'; import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata'; import { CellHotkeyScopeContext } from '@/object-record/record-table/contexts/CellHotkeyScopeContext'; @@ -32,11 +32,10 @@ export const useOpenRecordTableCellFromCell = () => { const cellPosition = useCurrentTableCellPosition(); const customCellHotkeyScope = useContext(CellHotkeyScopeContext); const { recordId, fieldDefinition } = useContext(FieldContext); - const { isReadOnly, pathToShowPage, objectNameSingular } = useContext( + const { pathToShowPage, objectNameSingular } = useContext( RecordTableRowContext, ); - const isFieldReadOnly = useIsFieldReadOnly(); - const cellIsReadOnly = isReadOnly || isFieldReadOnly; + const isFieldReadOnly = useIsFieldValueReadOnly(); const openTableCell = ( initialValue?: string, @@ -47,7 +46,7 @@ export const useOpenRecordTableCellFromCell = () => { customCellHotkeyScope, recordId, fieldDefinition, - isReadOnly: cellIsReadOnly, + isReadOnly: isFieldReadOnly, pathToShowPage, objectNameSingular, initialValue, diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-row/components/RecordTableRowWrapper.tsx b/packages/twenty-front/src/modules/object-record/record-table/record-table-row/components/RecordTableRowWrapper.tsx index 11d1ae1bb8..6c7def1b07 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-row/components/RecordTableRowWrapper.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-row/components/RecordTableRowWrapper.tsx @@ -12,7 +12,7 @@ import { isRowSelectedComponentFamilyState } from '@/object-record/record-table/ import { tableCellWidthsComponentState } from '@/object-record/record-table/states/tableCellWidthsComponentState'; import { RecordTableWithWrappersScrollWrapperContext } from '@/ui/utilities/scroll/contexts/ScrollWrapperContexts'; import { useRecoilComponentFamilyValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyValueV2'; -import { useRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentStateV2'; +import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2'; export const RecordTableRowWrapper = ({ recordId, @@ -48,7 +48,7 @@ export const RecordTableRowWrapper = ({ rootMargin: '1000px', }); - const [, setTableCellWidths] = useRecoilComponentStateV2( + const setTableCellWidths = useSetRecoilComponentStateV2( tableCellWidthsComponentState, ); @@ -108,7 +108,6 @@ export const RecordTableRowWrapper = ({ }) + recordId, objectNameSingular: objectMetadataItem.nameSingular, isSelected: currentRowSelected, - isReadOnly: objectMetadataItem.isRemote ?? false, isPendingRow, isDragging: draggableSnapshot.isDragging, dragHandleProps: draggableProvided.dragHandleProps, diff --git a/packages/twenty-front/src/testing/jest/JestRecordStoreSetter.tsx b/packages/twenty-front/src/testing/jest/JestRecordStoreSetter.tsx new file mode 100644 index 0000000000..844b11d1c5 --- /dev/null +++ b/packages/twenty-front/src/testing/jest/JestRecordStoreSetter.tsx @@ -0,0 +1,20 @@ +import { ReactNode, useEffect } from 'react'; + +import { useUpsertRecordsInStore } from '@/object-record/record-store/hooks/useUpsertRecordsInStore'; +import { ObjectRecord } from '@/object-record/types/ObjectRecord'; + +export const JestRecordStoreSetter = ({ + children, + records, +}: { + children: ReactNode; + records: ObjectRecord[]; +}) => { + const { upsertRecords } = useUpsertRecordsInStore(); + + useEffect(() => { + upsertRecords(records); + }); + + return <>{children}; +};