diff --git a/packages/twenty-front/src/modules/activities/calendar/components/CalendarEventDetails.tsx b/packages/twenty-front/src/modules/activities/calendar/components/CalendarEventDetails.tsx index aada7f11fc..003accf780 100644 --- a/packages/twenty-front/src/modules/activities/calendar/components/CalendarEventDetails.tsx +++ b/packages/twenty-front/src/modules/activities/calendar/components/CalendarEventDetails.tsx @@ -9,6 +9,7 @@ import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSi import { type FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem'; import { formatFieldMetadataItemAsFieldDefinition } from '@/object-metadata/utils/formatFieldMetadataItemAsFieldDefinition'; import { useIsRecordReadOnly } from '@/object-record/read-only/hooks/useIsRecordReadOnly'; +import { RecordFieldsScopeContextProvider } from '@/object-record/record-field-list/contexts/RecordFieldsScopeContext'; import { useFieldListFieldMetadataItems } from '@/object-record/record-field-list/hooks/useFieldListFieldMetadataItems'; import { FieldContext } from '@/object-record/record-field/ui/contexts/FieldContext'; import { RecordFieldComponentInstanceContext } from '@/object-record/record-field/ui/states/contexts/RecordFieldComponentInstanceContext'; @@ -143,43 +144,47 @@ export const CalendarEventDetails = ({ }), }} > - + ); return ( - - } - label={t`Event`} - /> - - - {calendarEvent.title} - - - {t`Created`}{' '} - {beautifyPastDateRelativeToNow( - new Date(calendarEvent.externalCreatedAt), + + + } + label={t`Event`} + /> + + + {calendarEvent.title} + + + {t`Created`}{' '} + {beautifyPastDateRelativeToNow( + new Date(calendarEvent.externalCreatedAt), + )} + + + + {standardFields.slice(0, 2).map(renderField)} + {calendarEventParticipants && ( + )} - - - - {standardFields.slice(0, 2).map(renderField)} - {calendarEventParticipants && ( - - )} - {standardFields.slice(2).map(renderField)} - {customFields.map(renderField)} - - + {standardFields.slice(2).map(renderField)} + {customFields.map(renderField)} + + + ); }; diff --git a/packages/twenty-front/src/modules/activities/inline-cell/components/ActivityTargetsInlineCell.tsx b/packages/twenty-front/src/modules/activities/inline-cell/components/ActivityTargetsInlineCell.tsx index 6ca1ca5a96..693833a481 100644 --- a/packages/twenty-front/src/modules/activities/inline-cell/components/ActivityTargetsInlineCell.tsx +++ b/packages/twenty-front/src/modules/activities/inline-cell/components/ActivityTargetsInlineCell.tsx @@ -1,11 +1,12 @@ -import { useContext } from 'react'; import { t } from '@lingui/core/macro'; +import { useContext } from 'react'; import { ActivityTargetChips } from '@/activities/components/ActivityTargetChips'; import { useActivityTargetObjectRecords } from '@/activities/hooks/useActivityTargetObjectRecords'; import { useOpenActivityTargetCellEditMode } from '@/activities/inline-cell/hooks/useOpenActivityTargetCellEditMode'; import { useUpdateActivityTargetFromCell } from '@/activities/inline-cell/hooks/useUpdateActivityTargetFromCell'; import { type CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; +import { RecordFieldsScopeContextProvider } from '@/object-record/record-field-list/contexts/RecordFieldsScopeContext'; import { FieldContextProvider } from '@/object-record/record-field/ui/components/FieldContextProvider'; import { FieldContext } from '@/object-record/record-field/ui/contexts/FieldContext'; import { FieldFocusContextProvider } from '@/object-record/record-field/ui/contexts/FieldFocusContextProvider'; @@ -54,67 +55,71 @@ export const ActivityTargetsInlineCell = ({ }); return ( - - - - { - closeInlineCell(); - }} - onChange={(morphItem) => { - updateActivityTargetFromCell({ - recordPickerInstanceId: componentInstanceId, - morphItem, - activityTargetWithTargetRecords: - activityTargetObjectRecords, - }); - }} - onSubmit={() => { - closeInlineCell(); - }} - /> - ), - label: t`Relations`, - displayModeContent: ( - - ), - onOpenEditMode: () => { - openActivityTargetCellEditMode({ - recordPickerInstanceId: componentInstanceId, - activityTargetObjectRecords, - }); - }, - }} + + + - - - - - + { + closeInlineCell(); + }} + onChange={(morphItem) => { + updateActivityTargetFromCell({ + recordPickerInstanceId: componentInstanceId, + morphItem, + activityTargetWithTargetRecords: + activityTargetObjectRecords, + }); + }} + onSubmit={() => { + closeInlineCell(); + }} + /> + ), + label: t`Relations`, + displayModeContent: ( + + ), + onOpenEditMode: () => { + openActivityTargetCellEditMode({ + recordPickerInstanceId: componentInstanceId, + activityTargetObjectRecords, + }); + }, + }} + > + + + + + + ); }; diff --git a/packages/twenty-front/src/modules/activities/notes/components/NoteTile.tsx b/packages/twenty-front/src/modules/activities/notes/components/NoteTile.tsx index 13ad2be6ee..55b08f9b53 100644 --- a/packages/twenty-front/src/modules/activities/notes/components/NoteTile.tsx +++ b/packages/twenty-front/src/modules/activities/notes/components/NoteTile.tsx @@ -7,6 +7,7 @@ import { type Note } from '@/activities/types/Note'; import { getActivityPreview } from '@/activities/utils/getActivityPreview'; import { useOpenRecordInCommandMenu } from '@/command-menu/hooks/useOpenRecordInCommandMenu'; import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; +import { RecordFieldsScopeContextProvider } from '@/object-record/record-field-list/contexts/RecordFieldsScopeContext'; import { FieldContextProvider } from '@/object-record/record-field/ui/components/FieldContextProvider'; const StyledCard = styled.div<{ isSingleNote: boolean }>` @@ -99,11 +100,17 @@ export const NoteTile = ({ fieldMetadataName="noteTargets" fieldPosition={0} > - + + + diff --git a/packages/twenty-front/src/modules/activities/tasks/components/TaskRow.tsx b/packages/twenty-front/src/modules/activities/tasks/components/TaskRow.tsx index 59c9aae5cd..12e136f1df 100644 --- a/packages/twenty-front/src/modules/activities/tasks/components/TaskRow.tsx +++ b/packages/twenty-front/src/modules/activities/tasks/components/TaskRow.tsx @@ -12,6 +12,7 @@ import { type Task } from '@/activities/types/Task'; import { useOpenRecordInCommandMenu } from '@/command-menu/hooks/useOpenRecordInCommandMenu'; import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; import { StopPropagationContainer } from '@/object-record/record-board/record-board-card/components/StopPropagationContainer'; +import { RecordFieldsScopeContextProvider } from '@/object-record/record-field-list/contexts/RecordFieldsScopeContext'; import { FieldContextProvider } from '@/object-record/record-field/ui/components/FieldContextProvider'; import { IconCalendar, OverflowingTextWithTooltip } from 'twenty-ui/display'; import { Checkbox, CheckboxShape } from 'twenty-ui/input'; @@ -138,15 +139,21 @@ export const TaskRow = ({ task }: { task: Task }) => { fieldMetadataName="taskTargets" fieldPosition={0} > - - - + + + + + } diff --git a/packages/twenty-front/src/modules/object-record/record-board/record-board-card/components/RecordBoardCard.tsx b/packages/twenty-front/src/modules/object-record/record-board/record-board-card/components/RecordBoardCard.tsx index 021063b39b..0ad245f90a 100644 --- a/packages/twenty-front/src/modules/object-record/record-board/record-board-card/components/RecordBoardCard.tsx +++ b/packages/twenty-front/src/modules/object-record/record-board/record-board-card/components/RecordBoardCard.tsx @@ -14,12 +14,14 @@ import { RecordBoardCardBody } from '@/object-record/record-board/record-board-c import { RecordBoardCardHeader } from '@/object-record/record-board/record-board-card/components/RecordBoardCardHeader'; import { RecordBoardCardMultiDragStack } from '@/object-record/record-board/record-board-card/components/RecordBoardCardMultiDragStack'; import { RECORD_BOARD_CARD_CLICK_OUTSIDE_ID } from '@/object-record/record-board/record-board-card/constants/RecordBoardCardClickOutsideId'; +import { RECORD_BOARD_CARD_INPUT_ID_PREFIX } from '@/object-record/record-board/record-board-card/constants/RecordBoardCardInputIdPrefix'; import { RecordBoardCardComponentInstanceContext } from '@/object-record/record-board/record-board-card/states/contexts/RecordBoardCardComponentInstanceContext'; import { recordBoardCardIsExpandedComponentState } from '@/object-record/record-board/record-board-card/states/recordBoardCardIsExpandedComponentState'; import { RecordBoardComponentInstanceContext } from '@/object-record/record-board/states/contexts/RecordBoardComponentInstanceContext'; import { RecordCard } from '@/object-record/record-card/components/RecordCard'; import { isRecordIdPrimaryDragMultipleComponentFamilyState } from '@/object-record/record-drag/states/isRecordIdPrimaryDragMultipleComponentFamilyState'; import { isRecordIdSecondaryDragMultipleComponentFamilyState } from '@/object-record/record-drag/states/isRecordIdSecondaryDragMultipleComponentFamilyState'; +import { RecordFieldsScopeContextProvider } from '@/object-record/record-field-list/contexts/RecordFieldsScopeContext'; import { useOpenRecordFromIndexView } from '@/object-record/record-index/hooks/useOpenRecordFromIndexView'; import { useOpenDropdown } from '@/ui/layout/dropdown/hooks/useOpenDropdown'; import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow'; @@ -151,34 +153,40 @@ export const RecordBoardCard = () => { instanceId: `record-board-card-${recordId}`, }} > - - - {isRecordIdPrimaryDragMultiple && } - + - - } + - - - - - - - + + + + + + + + + + ); }; diff --git a/packages/twenty-front/src/modules/object-record/record-calendar/record-calendar-card/components/RecordCalendarCard.tsx b/packages/twenty-front/src/modules/object-record/record-calendar/record-calendar-card/components/RecordCalendarCard.tsx index db5c459600..0555744bb4 100644 --- a/packages/twenty-front/src/modules/object-record/record-calendar/record-calendar-card/components/RecordCalendarCard.tsx +++ b/packages/twenty-front/src/modules/object-record/record-calendar/record-calendar-card/components/RecordCalendarCard.tsx @@ -6,10 +6,12 @@ import { RecordCalendarCardCellHoveredPortal } from '@/object-record/record-cale import { RecordCalendarCardBody } from '@/object-record/record-calendar/record-calendar-card/components/RecordCalendarCardBody'; import { RecordCalendarCardHeader } from '@/object-record/record-calendar/record-calendar-card/components/RecordCalendarCardHeader'; import { RECORD_CALENDAR_CARD_CLICK_OUTSIDE_ID } from '@/object-record/record-calendar/record-calendar-card/constants/RecordCalendarCardClickOutsideId'; +import { RECORD_CALENDAR_CARD_INPUT_ID_PREFIX } from '@/object-record/record-calendar/record-calendar-card/constants/RecordCalendarCardInputIdPrefix'; import { RecordCalendarCardComponentInstanceContext } from '@/object-record/record-calendar/record-calendar-card/states/contexts/RecordCalendarCardComponentInstanceContext'; import { isRecordCalendarCardSelectedComponentFamilyState } from '@/object-record/record-calendar/record-calendar-card/states/isRecordCalendarCardSelectedComponentFamilyState'; import { RecordCalendarComponentInstanceContext } from '@/object-record/record-calendar/states/contexts/RecordCalendarComponentInstanceContext'; import { RecordCard } from '@/object-record/record-card/components/RecordCard'; +import { RecordFieldsScopeContextProvider } from '@/object-record/record-field-list/contexts/RecordFieldsScopeContext'; import { useOpenDropdown } from '@/ui/layout/dropdown/hooks/useOpenDropdown'; import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow'; import { useRecoilComponentFamilyState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyState'; @@ -78,22 +80,26 @@ export const RecordCalendarCard = ({ recordId }: RecordCalendarCardProps) => { instanceId: recordId, }} > - - - - - - - - - - + + + + + + + + + + + + ); }; diff --git a/packages/twenty-front/src/modules/object-record/record-field-list/contexts/RecordFieldsScopeContext.ts b/packages/twenty-front/src/modules/object-record/record-field-list/contexts/RecordFieldsScopeContext.ts new file mode 100644 index 0000000000..5c075e2aad --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-field-list/contexts/RecordFieldsScopeContext.ts @@ -0,0 +1,12 @@ +import { createRequiredContext } from '~/utils/createRequiredContext'; + +export type RecordFieldsScopeContextValue = { + scopeInstanceId: string; +}; + +export const [ + RecordFieldsScopeContextProvider, + useRecordFieldsScopeContextOrThrow, +] = createRequiredContext( + 'RecordFieldsScopeContext', +); diff --git a/packages/twenty-front/src/modules/object-record/record-field-list/record-detail-section/relation/components/RecordDetailMorphRelationSection.tsx b/packages/twenty-front/src/modules/object-record/record-field-list/record-detail-section/relation/components/RecordDetailMorphRelationSection.tsx index 4a94a1595b..a5ec5d2fcd 100644 --- a/packages/twenty-front/src/modules/object-record/record-field-list/record-detail-section/relation/components/RecordDetailMorphRelationSection.tsx +++ b/packages/twenty-front/src/modules/object-record/record-field-list/record-detail-section/relation/components/RecordDetailMorphRelationSection.tsx @@ -1,6 +1,7 @@ import { useContext } from 'react'; import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems'; +import { useRecordFieldsScopeContextOrThrow } from '@/object-record/record-field-list/contexts/RecordFieldsScopeContext'; import { RecordDetailSectionContainer } from '@/object-record/record-field-list/record-detail-section/components/RecordDetailSectionContainer'; import { FieldContext } from '@/object-record/record-field/ui/contexts/FieldContext'; import { @@ -26,6 +27,7 @@ type RecordDetailMorphRelationSectionProps = { export const RecordDetailMorphRelationSection = ({ loading, }: RecordDetailMorphRelationSectionProps) => { + const { scopeInstanceId } = useRecordFieldsScopeContextOrThrow(); const { recordId, fieldDefinition } = useContext(FieldContext); const metadata = fieldDefinition.metadata as FieldMorphRelationMetadata; @@ -44,6 +46,7 @@ export const RecordDetailMorphRelationSection = ({ const dropdownId = getRecordFieldCardRelationPickerDropdownId({ fieldDefinition, recordId, + instanceId: scopeInstanceId, }); const isDropdownOpen = useRecoilComponentValue( diff --git a/packages/twenty-front/src/modules/object-record/record-field-list/record-detail-section/relation/components/RecordDetailMorphRelationSectionDropdownManyToOne.tsx b/packages/twenty-front/src/modules/object-record/record-field-list/record-detail-section/relation/components/RecordDetailMorphRelationSectionDropdownManyToOne.tsx index e85419c327..9ef0a2f944 100644 --- a/packages/twenty-front/src/modules/object-record/record-field-list/record-detail-section/relation/components/RecordDetailMorphRelationSectionDropdownManyToOne.tsx +++ b/packages/twenty-front/src/modules/object-record/record-field-list/record-detail-section/relation/components/RecordDetailMorphRelationSectionDropdownManyToOne.tsx @@ -1,6 +1,7 @@ import { type ReactNode, useCallback, useContext } from 'react'; import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems'; +import { useRecordFieldsScopeContextOrThrow } from '@/object-record/record-field-list/contexts/RecordFieldsScopeContext'; import { useGetMorphRelationRelatedRecordsWithObjectNameSingular } from '@/object-record/record-field-list/record-detail-section/relation/components/hooks/useGetMorphRelationRelatedRecordsWithObjectNameSingular'; import { FieldContext } from '@/object-record/record-field/ui/contexts/FieldContext'; import { FieldInputEventContext } from '@/object-record/record-field/ui/contexts/FieldInputEventContext'; @@ -29,6 +30,7 @@ type RecordDetailMorphRelationSectionDropdownManyToOneProps = { export const RecordDetailMorphRelationSectionDropdownManyToOne = ({ dropdownTriggerClickableComponent, }: RecordDetailMorphRelationSectionDropdownManyToOneProps) => { + const { scopeInstanceId } = useRecordFieldsScopeContextOrThrow(); const { recordId, fieldDefinition } = useContext(FieldContext); assertFieldMetadata( @@ -58,6 +60,7 @@ export const RecordDetailMorphRelationSectionDropdownManyToOne = ({ const dropdownId = getRecordFieldCardRelationPickerDropdownId({ fieldDefinition, recordId, + instanceId: scopeInstanceId, }); const { closeDropdown } = useCloseDropdown(); diff --git a/packages/twenty-front/src/modules/object-record/record-field-list/record-detail-section/relation/components/RecordDetailMorphRelationSectionDropdownOneToMany.tsx b/packages/twenty-front/src/modules/object-record/record-field-list/record-detail-section/relation/components/RecordDetailMorphRelationSectionDropdownOneToMany.tsx index ec461d056e..e0b2413058 100644 --- a/packages/twenty-front/src/modules/object-record/record-field-list/record-detail-section/relation/components/RecordDetailMorphRelationSectionDropdownOneToMany.tsx +++ b/packages/twenty-front/src/modules/object-record/record-field-list/record-detail-section/relation/components/RecordDetailMorphRelationSectionDropdownOneToMany.tsx @@ -1,6 +1,7 @@ import { type ReactNode, useCallback, useContext } from 'react'; import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems'; +import { useRecordFieldsScopeContextOrThrow } from '@/object-record/record-field-list/contexts/RecordFieldsScopeContext'; import { useGetMorphRelationRelatedRecordsWithObjectNameSingular } from '@/object-record/record-field-list/record-detail-section/relation/components/hooks/useGetMorphRelationRelatedRecordsWithObjectNameSingular'; import { FieldContext } from '@/object-record/record-field/ui/contexts/FieldContext'; import { useUpdateMorphRelationOneToManyFieldInput } from '@/object-record/record-field/ui/meta-types/input/hooks/useUpdateMorphRelationOneToManyFieldInput'; @@ -31,6 +32,7 @@ type RecordDetailMorphRelationSectionDropdownOneToManyProps = { export const RecordDetailMorphRelationSectionDropdownOneToMany = ({ dropdownTriggerClickableComponent, }: RecordDetailMorphRelationSectionDropdownOneToManyProps) => { + const { scopeInstanceId } = useRecordFieldsScopeContextOrThrow(); const { recordId, fieldDefinition } = useContext(FieldContext); assertFieldMetadata( @@ -61,6 +63,7 @@ export const RecordDetailMorphRelationSectionDropdownOneToMany = ({ const dropdownId = getRecordFieldCardRelationPickerDropdownId({ fieldDefinition, recordId, + instanceId: scopeInstanceId, }); const { closeDropdown } = useCloseDropdown(); diff --git a/packages/twenty-front/src/modules/object-record/record-field-list/record-detail-section/relation/components/RecordDetailRelationRecordsListItem.tsx b/packages/twenty-front/src/modules/object-record/record-field-list/record-detail-section/relation/components/RecordDetailRelationRecordsListItem.tsx index 0c9d0edb7b..698850d8b8 100644 --- a/packages/twenty-front/src/modules/object-record/record-field-list/record-detail-section/relation/components/RecordDetailRelationRecordsListItem.tsx +++ b/packages/twenty-front/src/modules/object-record/record-field-list/record-detail-section/relation/components/RecordDetailRelationRecordsListItem.tsx @@ -12,6 +12,10 @@ import { useDeleteOneRecord } from '@/object-record/hooks/useDeleteOneRecord'; import { useObjectPermissionsForObject } from '@/object-record/hooks/useObjectPermissionsForObject'; import { useUpdateOneRecord } from '@/object-record/hooks/useUpdateOneRecord'; import { RecordFieldList } from '@/object-record/record-field-list/components/RecordFieldList'; +import { + RecordFieldsScopeContextProvider, + useRecordFieldsScopeContextOrThrow, +} from '@/object-record/record-field-list/contexts/RecordFieldsScopeContext'; import { RecordDetailRecordsListItemContainer } from '@/object-record/record-field-list/record-detail-section/components/RecordDetailRecordsListItemContainer'; import { FieldContext } from '@/object-record/record-field/ui/contexts/FieldContext'; import { FieldInputEventContext } from '@/object-record/record-field/ui/contexts/FieldInputEventContext'; @@ -30,13 +34,13 @@ import { useModal } from '@/ui/layout/modal/hooks/useModal'; import { useIsInRightDrawerOrThrow } from '@/ui/layout/right-drawer/contexts/RightDrawerContext'; import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; import { useSetRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentState'; +import { t } from '@lingui/core/macro'; +import { Trans } from '@lingui/react/macro'; import { createPortal } from 'react-dom'; import { computeMorphRelationFieldName, CustomError, } from 'twenty-shared/utils'; -import { Trans } from '@lingui/react/macro'; -import { t } from '@lingui/core/macro'; import { IconChevronDown, IconDotsVertical, @@ -99,6 +103,7 @@ export const RecordDetailRelationRecordsListItem = ({ relationObjectMetadataNameSingular, relationFieldMetadataId, }: RecordDetailRelationRecordsListItemProps) => { + const { scopeInstanceId } = useRecordFieldsScopeContextOrThrow(); const { fieldDefinition, recordId, @@ -162,6 +167,7 @@ export const RecordDetailRelationRecordsListItem = ({ const dropdownId = getRecordFieldCardRelationPickerDropdownId({ fieldDefinition, recordId, + instanceId: scopeInstanceId, }); const setSingleRecordPickerSelectedId = useSetRecoilComponentState( singleRecordPickerSelectedIdComponentState, @@ -284,15 +290,21 @@ export const RecordDetailRelationRecordsListItem = ({ )} - + + + {createPortal( { const { t } = useLingui(); + const { scopeInstanceId } = useRecordFieldsScopeContextOrThrow(); const { recordId, fieldDefinition } = useContext(FieldContext); const { @@ -95,6 +97,7 @@ export const RecordDetailRelationSection = ({ const dropdownId = getRecordFieldCardRelationPickerDropdownId({ fieldDefinition, recordId, + instanceId: scopeInstanceId, }); const isDropdownOpen = useRecoilComponentValue( diff --git a/packages/twenty-front/src/modules/object-record/record-field-list/record-detail-section/relation/components/RecordDetailRelationSectionDropdownToMany.tsx b/packages/twenty-front/src/modules/object-record/record-field-list/record-detail-section/relation/components/RecordDetailRelationSectionDropdownToMany.tsx index 34393dd2ce..45216d9d5b 100644 --- a/packages/twenty-front/src/modules/object-record/record-field-list/record-detail-section/relation/components/RecordDetailRelationSectionDropdownToMany.tsx +++ b/packages/twenty-front/src/modules/object-record/record-field-list/record-detail-section/relation/components/RecordDetailRelationSectionDropdownToMany.tsx @@ -4,6 +4,7 @@ import { useRecoilValue } from 'recoil'; import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem'; import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems'; import { getFieldMetadataItemById } from '@/object-metadata/utils/getFieldMetadataItemById'; +import { useRecordFieldsScopeContextOrThrow } from '@/object-record/record-field-list/contexts/RecordFieldsScopeContext'; import { FieldContext } from '@/object-record/record-field/ui/contexts/FieldContext'; import { useAddNewRecordAndOpenRightDrawer } from '@/object-record/record-field/ui/meta-types/input/hooks/useAddNewRecordAndOpenRightDrawer'; import { useUpdateRelationOneToManyFieldInput } from '@/object-record/record-field/ui/meta-types/input/hooks/useUpdateRelationOneToManyFieldInput'; @@ -33,6 +34,7 @@ type RecordDetailRelationSectionDropdownToManyProps = { export const RecordDetailRelationSectionDropdownToMany = ({ dropdownTriggerClickableComponent, }: RecordDetailRelationSectionDropdownToManyProps) => { + const { scopeInstanceId } = useRecordFieldsScopeContextOrThrow(); const { recordId, fieldDefinition } = useContext(FieldContext); const { fieldMetadataId } = fieldDefinition; const { @@ -78,6 +80,7 @@ export const RecordDetailRelationSectionDropdownToMany = ({ const dropdownId = getRecordFieldCardRelationPickerDropdownId({ fieldDefinition, recordId, + instanceId: scopeInstanceId, }); const { closeDropdown } = useCloseDropdown(); diff --git a/packages/twenty-front/src/modules/object-record/record-field-list/record-detail-section/relation/components/RecordDetailRelationSectionDropdownToOne.tsx b/packages/twenty-front/src/modules/object-record/record-field-list/record-detail-section/relation/components/RecordDetailRelationSectionDropdownToOne.tsx index 1de7b3c495..1d071e0095 100644 --- a/packages/twenty-front/src/modules/object-record/record-field-list/record-detail-section/relation/components/RecordDetailRelationSectionDropdownToOne.tsx +++ b/packages/twenty-front/src/modules/object-record/record-field-list/record-detail-section/relation/components/RecordDetailRelationSectionDropdownToOne.tsx @@ -3,6 +3,7 @@ import { useRecoilValue } from 'recoil'; import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem'; import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; +import { useRecordFieldsScopeContextOrThrow } from '@/object-record/record-field-list/contexts/RecordFieldsScopeContext'; import { FieldContext } from '@/object-record/record-field/ui/contexts/FieldContext'; import { FieldInputEventContext } from '@/object-record/record-field/ui/contexts/FieldInputEventContext'; import { useAddNewRecordAndOpenRightDrawer } from '@/object-record/record-field/ui/meta-types/input/hooks/useAddNewRecordAndOpenRightDrawer'; @@ -36,6 +37,7 @@ type RecordDetailRelationSectionDropdownToOneProps = { export const RecordDetailRelationSectionDropdownToOne = ({ dropdownTriggerClickableComponent, }: RecordDetailRelationSectionDropdownToOneProps) => { + const { scopeInstanceId } = useRecordFieldsScopeContextOrThrow(); const { recordId, fieldDefinition } = useContext(FieldContext); assertFieldMetadata( FieldMetadataType.RELATION, @@ -89,6 +91,7 @@ export const RecordDetailRelationSectionDropdownToOne = ({ const dropdownId = getRecordFieldCardRelationPickerDropdownId({ fieldDefinition, recordId, + instanceId: scopeInstanceId, }); const { closeDropdown } = useCloseDropdown(); diff --git a/packages/twenty-front/src/modules/object-record/record-field-list/record-detail-section/relation/components/__stories__/RecordDetailRelationSection.stories.tsx b/packages/twenty-front/src/modules/object-record/record-field-list/record-detail-section/relation/components/__stories__/RecordDetailRelationSection.stories.tsx index d6b555ffde..0bc442439e 100644 --- a/packages/twenty-front/src/modules/object-record/record-field-list/record-detail-section/relation/components/__stories__/RecordDetailRelationSection.stories.tsx +++ b/packages/twenty-front/src/modules/object-record/record-field-list/record-detail-section/relation/components/__stories__/RecordDetailRelationSection.stories.tsx @@ -10,6 +10,7 @@ import { graphqlMocks } from '~/testing/graphqlMocks'; import { getCompaniesMock } from '~/testing/mock-data/companies'; import { ContextStoreComponentInstanceContext } from '@/context-store/states/contexts/ContextStoreComponentInstanceContext'; +import { RecordFieldsScopeContextProvider } from '@/object-record/record-field-list/contexts/RecordFieldsScopeContext'; import { ComponentDecorator } from 'twenty-ui/testing'; import { I18nFrontDecorator } from '~/testing/decorators/I18nFrontDecorator'; import { RightDrawerDecorator } from '~/testing/decorators/RightDrawerDecorator'; @@ -49,7 +50,11 @@ const meta: Meta = { isRecordFieldReadOnly: false, }} > - + + + ), diff --git a/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/input/components/__stories__/DateTimeFieldInput.stories.tsx b/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/input/components/__stories__/DateTimeFieldInput.stories.tsx index feddce92da..0798cf158d 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/input/components/__stories__/DateTimeFieldInput.stories.tsx +++ b/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/input/components/__stories__/DateTimeFieldInput.stories.tsx @@ -5,6 +5,7 @@ import { useEffect } from 'react'; import { usePushFocusItemToFocusStack } from '@/ui/utilities/focus/hooks/usePushFocusItemToFocusStack'; import { FieldMetadataType } from '~/generated-metadata/graphql'; +import { RecordFieldsScopeContextProvider } from '@/object-record/record-field-list/contexts/RecordFieldsScopeContext'; import { FieldContext } from '@/object-record/record-field/ui/contexts/FieldContext'; import { getFieldInputEventContextProviderWithJestMocks } from '@/object-record/record-field/ui/meta-types/input/components/__stories__/utils/getFieldInputEventContextProviderWithJestMocks'; import { RecordFieldComponentInstanceContext } from '@/object-record/record-field/ui/states/contexts/RecordFieldComponentInstanceContext'; @@ -90,11 +91,15 @@ const DateFieldInputWithContext = ({ isRecordFieldReadOnly: false, }} > - - - - - + + + + + + +
diff --git a/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/input/components/__stories__/NumberFieldInput.stories.tsx b/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/input/components/__stories__/NumberFieldInput.stories.tsx index e50987597e..9ebb6c0e1e 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/input/components/__stories__/NumberFieldInput.stories.tsx +++ b/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/input/components/__stories__/NumberFieldInput.stories.tsx @@ -6,6 +6,7 @@ import { usePushFocusItemToFocusStack } from '@/ui/utilities/focus/hooks/usePush import { FieldMetadataType } from '~/generated-metadata/graphql'; import { SnackBarDecorator } from '~/testing/decorators/SnackBarDecorator'; +import { RecordFieldsScopeContextProvider } from '@/object-record/record-field-list/contexts/RecordFieldsScopeContext'; import { FieldContext } from '@/object-record/record-field/ui/contexts/FieldContext'; import { getFieldInputEventContextProviderWithJestMocks } from '@/object-record/record-field/ui/meta-types/input/components/__stories__/utils/getFieldInputEventContextProviderWithJestMocks'; import { RecordFieldComponentInstanceContext } from '@/object-record/record-field/ui/states/contexts/RecordFieldComponentInstanceContext'; @@ -96,11 +97,15 @@ const NumberFieldInputWithContext = ({ isRecordFieldReadOnly: false, }} > - - {isReady && } - - - + + + {isReady && } + + + + {isReady &&
}
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 3d7b2f9fbf..0854003248 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 @@ -9,6 +9,7 @@ import { useGetButtonIcon } from '@/object-record/record-field/ui/hooks/useGetBu import { useIsFieldInputOnly } from '@/object-record/record-field/ui/hooks/useIsFieldInputOnly'; import { useOpenFieldInputEditMode } from '@/object-record/record-field/ui/hooks/useOpenFieldInputEditMode'; +import { useRecordFieldsScopeContextOrThrow } from '@/object-record/record-field-list/contexts/RecordFieldsScopeContext'; import { FieldInputEventContext, type FieldInputClickOutsideEvent, @@ -44,6 +45,7 @@ export const RecordInlineCell = ({ onCloseEditMode: onCloseEditModeFromContext, isRecordFieldReadOnly: isReadOnly, } = useContext(FieldContext); + const { scopeInstanceId } = useRecordFieldsScopeContextOrThrow(); const { openFieldInput, closeFieldInput } = useOpenFieldInputEditMode(); @@ -142,11 +144,12 @@ export const RecordInlineCell = ({ .getLoadable(activeDropdownFocusIdState) .getValue(); - const expectedDropdownFocusId = getDropdownFocusIdForRecordField( + const expectedDropdownFocusId = getDropdownFocusIdForRecordField({ recordId, - fieldDefinition.fieldMetadataId, - 'inline-cell', - ); + fieldMetadataId: fieldDefinition.fieldMetadataId, + componentType: 'inline-cell', + instanceId: scopeInstanceId, + }); if (currentDropdownFocusId !== expectedDropdownFocusId) { return; @@ -162,9 +165,10 @@ export const RecordInlineCell = ({ closeInlineCell(); }, [ - closeInlineCell, recordId, fieldDefinition.fieldMetadataId, + scopeInstanceId, + closeInlineCell, persistFieldFromFieldInputContext, ], ); diff --git a/packages/twenty-front/src/modules/object-record/record-inline-cell/components/RecordInlineCellAnchoredPortal.tsx b/packages/twenty-front/src/modules/object-record/record-inline-cell/components/RecordInlineCellAnchoredPortal.tsx index 5489771cc1..a2afe362db 100644 --- a/packages/twenty-front/src/modules/object-record/record-inline-cell/components/RecordInlineCellAnchoredPortal.tsx +++ b/packages/twenty-front/src/modules/object-record/record-inline-cell/components/RecordInlineCellAnchoredPortal.tsx @@ -101,8 +101,9 @@ export const RecordInlineCellAnchoredPortal = ({ > {children} + + - , anchorElement, )} diff --git a/packages/twenty-front/src/modules/object-record/record-inline-cell/hooks/useInlineCell.ts b/packages/twenty-front/src/modules/object-record/record-inline-cell/hooks/useInlineCell.ts index 596324d6f2..dc24e22ce2 100644 --- a/packages/twenty-front/src/modules/object-record/record-inline-cell/hooks/useInlineCell.ts +++ b/packages/twenty-front/src/modules/object-record/record-inline-cell/hooks/useInlineCell.ts @@ -2,6 +2,7 @@ import { useContext } from 'react'; import { FieldContext } from '@/object-record/record-field/ui/contexts/FieldContext'; +import { useRecordFieldsScopeContextOrThrow } from '@/object-record/record-field-list/contexts/RecordFieldsScopeContext'; import { useInitDraftValue } from '@/object-record/record-field/ui/hooks/useInitDraftValue'; import { RecordFieldComponentInstanceContext } from '@/object-record/record-field/ui/states/contexts/RecordFieldComponentInstanceContext'; import { useRecordInlineCellContext } from '@/object-record/record-inline-cell/components/RecordInlineCellContext'; @@ -21,6 +22,7 @@ export const useInlineCell = ( ); const { onOpenEditMode, onCloseEditMode } = useRecordInlineCellContext(); + const { scopeInstanceId } = useRecordFieldsScopeContextOrThrow(); const { setActiveDropdownFocusIdAndMemorizePrevious } = useSetActiveDropdownFocusIdAndMemorizePrevious(); @@ -43,13 +45,14 @@ export const useInlineCell = ( fieldComponentInstanceId: recordFieldComponentInstanceId, }); - setActiveDropdownFocusIdAndMemorizePrevious( - getDropdownFocusIdForRecordField( - recordId, - fieldDefinition.fieldMetadataId, - 'inline-cell', - ), - ); + const dropdownId = getDropdownFocusIdForRecordField({ + recordId, + fieldMetadataId: fieldDefinition.fieldMetadataId, + componentType: 'inline-cell', + instanceId: scopeInstanceId, + }); + + setActiveDropdownFocusIdAndMemorizePrevious(dropdownId); }; return { 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 61e66b3498..1d78990c31 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 @@ -1,4 +1,5 @@ import { RecordFieldList } from '@/object-record/record-field-list/components/RecordFieldList'; +import { RecordFieldsScopeContextProvider } from '@/object-record/record-field-list/contexts/RecordFieldsScopeContext'; import { type FieldCardConfiguration } from '@/object-record/record-show/types/CardConfiguration'; import { useLayoutRenderingContext } from '@/ui/layout/contexts/LayoutRenderingContext'; import { useTargetRecord } from '@/ui/layout/contexts/useTargetRecord'; @@ -11,12 +12,16 @@ export const FieldsCard = ({ configuration }: FieldsCardProps) => { const targetRecord = useTargetRecord(); const { isInRightDrawer } = useLayoutRenderingContext(); + const instanceId = `fields-card-${targetRecord.id}${isInRightDrawer ? '-right-drawer' : ''}`; + return ( - + + + ); }; diff --git a/packages/twenty-front/src/modules/object-record/record-show/utils/getRecordFieldCardRelationPickerDropdownId.ts b/packages/twenty-front/src/modules/object-record/record-show/utils/getRecordFieldCardRelationPickerDropdownId.ts index 5d7125a236..1169327501 100644 --- a/packages/twenty-front/src/modules/object-record/record-show/utils/getRecordFieldCardRelationPickerDropdownId.ts +++ b/packages/twenty-front/src/modules/object-record/record-show/utils/getRecordFieldCardRelationPickerDropdownId.ts @@ -4,9 +4,11 @@ import { type FieldMetadata } from '@/object-record/record-field/ui/types/FieldM type GetRecordFieldCardRelationPickerDropdownIdArgs = { fieldDefinition: FieldDefinition; recordId: string; + instanceId: string; }; export const getRecordFieldCardRelationPickerDropdownId = ({ fieldDefinition, recordId, + instanceId, }: GetRecordFieldCardRelationPickerDropdownIdArgs) => - `record-field-card-relation-picker-${fieldDefinition.fieldMetadataId}-${recordId}`; + `record-field-card-relation-picker:${instanceId}:${fieldDefinition.fieldMetadataId}:${recordId}`; 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 e7c993ee59..57adf61798 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 @@ -4,8 +4,10 @@ import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadata import { RecordTableContextProvider as RecordTableContextInternalProvider } from '@/object-record/record-table/contexts/RecordTableContext'; import { useObjectPermissionsForObject } from '@/object-record/hooks/useObjectPermissionsForObject'; +import { RecordFieldsScopeContextProvider } from '@/object-record/record-field-list/contexts/RecordFieldsScopeContext'; import { visibleRecordFieldsComponentSelector } from '@/object-record/record-field/states/visibleRecordFieldsComponentSelector'; import { recordIndexOpenRecordInState } from '@/object-record/record-index/states/recordIndexOpenRecordInState'; +import { RECORD_TABLE_CELL_INPUT_ID_PREFIX } from '@/object-record/record-table/constants/RecordTableCellInputIdPrefix'; import { RECORD_TABLE_COLUMN_MIN_WIDTH } from '@/object-record/record-table/constants/RecordTableColumnMinWidth'; import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; import { ViewOpenRecordInType } from '@/views/types/ViewOpenRecordInType'; @@ -46,22 +48,26 @@ export const RecordTableContextProvider = ({ : 'MOUSE_DOWN'; return ( - ({ - ...field, - size: Math.max(field.size, RECORD_TABLE_COLUMN_MIN_WIDTH), - })), - onRecordIdentifierClick, - triggerEvent, - }} + - {children} - + ({ + ...field, + size: Math.max(field.size, RECORD_TABLE_COLUMN_MIN_WIDTH), + })), + onRecordIdentifierClick, + triggerEvent, + }} + > + {children} + + ); }; diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/useOpenRecordTableCell.ts b/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/useOpenRecordTableCell.ts index 5eabe4fd99..4347a40944 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/useOpenRecordTableCell.ts +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/useOpenRecordTableCell.ts @@ -21,6 +21,7 @@ import { getDropdownFocusIdForRecordField } from '@/object-record/utils/getDropd import { getRecordFieldInputInstanceId } from '@/object-record/utils/getRecordFieldInputId'; import { useSetActiveDropdownFocusIdAndMemorizePrevious } from '@/ui/layout/dropdown/hooks/useSetFocusedDropdownIdAndMemorizePrevious'; +import { useRecordFieldsScopeContextOrThrow } from '@/object-record/record-field-list/contexts/RecordFieldsScopeContext'; import { useOpenRecordFromIndexView } from '@/object-record/record-index/hooks/useOpenRecordFromIndexView'; import { useActiveRecordTableRow } from '@/object-record/record-table/hooks/useActiveRecordTableRow'; import { useFocusedRecordTableRow } from '@/object-record/record-table/hooks/useFocusedRecordTableRow'; @@ -41,6 +42,8 @@ export type OpenTableCellArgs = { }; export const useOpenRecordTableCell = (recordTableId: string) => { + const { scopeInstanceId } = useRecordFieldsScopeContextOrThrow(); + const clickOutsideListenerIsActivatedState = useRecoilComponentCallbackState( clickOutsideListenerIsActivatedComponentState, RECORD_TABLE_CLICK_OUTSIDE_LISTENER_ID, @@ -157,11 +160,12 @@ export const useOpenRecordTableCell = (recordTableId: string) => { toggleClickOutside(false); setActiveDropdownFocusIdAndMemorizePrevious( - getDropdownFocusIdForRecordField( + getDropdownFocusIdForRecordField({ recordId, - fieldDefinition.fieldMetadataId, - 'table-cell', - ), + fieldMetadataId: fieldDefinition.fieldMetadataId, + componentType: 'table-cell', + instanceId: scopeInstanceId, + }), ); }, [ @@ -175,10 +179,11 @@ export const useOpenRecordTableCell = (recordTableId: string) => { initDraftValue, toggleClickOutside, setActiveDropdownFocusIdAndMemorizePrevious, + scopeInstanceId, leaveTableFocus, + openRecordFromIndexView, activateRecordTableRow, unfocusRecordTableRow, - openRecordFromIndexView, ], ); diff --git a/packages/twenty-front/src/modules/object-record/utils/getDropdownFocusIdForRecordField.ts b/packages/twenty-front/src/modules/object-record/utils/getDropdownFocusIdForRecordField.ts index b5298f9d54..b7d99da731 100644 --- a/packages/twenty-front/src/modules/object-record/utils/getDropdownFocusIdForRecordField.ts +++ b/packages/twenty-front/src/modules/object-record/utils/getDropdownFocusIdForRecordField.ts @@ -1,7 +1,13 @@ -export const getDropdownFocusIdForRecordField = ( - recordId: string, - fieldMetadataId: string, - componentType: 'table-cell' | 'inline-cell', -) => { - return `dropdown-${componentType}-record-${recordId}-field-${fieldMetadataId}`; +export const getDropdownFocusIdForRecordField = ({ + recordId, + fieldMetadataId, + componentType, + instanceId, +}: { + recordId: string; + fieldMetadataId: string; + componentType: 'table-cell' | 'inline-cell'; + instanceId: string; +}) => { + return `dropdown-${instanceId}-${componentType}-record-${recordId}-field-${fieldMetadataId}`; }; diff --git a/packages/twenty-front/src/modules/page-layout/constants/DefaultCompanyRecordPageLayout.ts b/packages/twenty-front/src/modules/page-layout/constants/DefaultCompanyRecordPageLayout.ts index c1e3cf2474..1e17068ef0 100644 --- a/packages/twenty-front/src/modules/page-layout/constants/DefaultCompanyRecordPageLayout.ts +++ b/packages/twenty-front/src/modules/page-layout/constants/DefaultCompanyRecordPageLayout.ts @@ -43,6 +43,25 @@ export const DEFAULT_COMPANY_RECORD_PAGE_LAYOUT: PageLayout = { updatedAt: new Date().toISOString(), deletedAt: null, }, + { + __typename: 'PageLayoutWidget', + id: 'company-widget-fields-2', + pageLayoutTabId: 'company-tab-fields', + title: 'Fields', + type: WidgetType.FIELDS, + objectMetadataId: null, + gridPosition: { + __typename: 'GridPosition', + row: 3, + column: 0, + rowSpan: 12, + columnSpan: 12, + }, + configuration: null, + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + deletedAt: null, + }, { __typename: 'PageLayoutWidget', id: 'company-widget-account-owner', @@ -112,6 +131,29 @@ export const DEFAULT_COMPANY_RECORD_PAGE_LAYOUT: PageLayout = { updatedAt: new Date().toISOString(), deletedAt: null, }, + { + __typename: 'PageLayoutWidget', + id: 'company-widget-people-2', + pageLayoutTabId: 'company-tab-fields', + title: 'People', + type: WidgetType.FIELD, + objectMetadataId: null, + gridPosition: { + __typename: 'GridPosition', + row: 2, + column: 0, + rowSpan: 1, + columnSpan: 12, + }, + configuration: { + __typename: 'FieldConfiguration', + fieldMetadataId: 'people', + layout: 'FIELD', + }, + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + deletedAt: null, + }, { __typename: 'PageLayoutWidget', id: 'company-widget-name', @@ -135,6 +177,29 @@ export const DEFAULT_COMPANY_RECORD_PAGE_LAYOUT: PageLayout = { updatedAt: new Date().toISOString(), deletedAt: null, }, + { + __typename: 'PageLayoutWidget', + id: 'company-widget-name-2', + pageLayoutTabId: 'company-tab-fields', + title: 'Name', + type: WidgetType.FIELD, + objectMetadataId: null, + gridPosition: { + __typename: 'GridPosition', + row: 2, + column: 0, + rowSpan: 1, + columnSpan: 12, + }, + configuration: { + __typename: 'FieldConfiguration', + fieldMetadataId: 'name', + layout: 'FIELD', + }, + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + deletedAt: null, + }, { __typename: 'PageLayoutWidget', id: 'company-widget-employees', diff --git a/packages/twenty-front/src/modules/page-layout/widgets/components/WidgetActionFieldEdit.tsx b/packages/twenty-front/src/modules/page-layout/widgets/components/WidgetActionFieldEdit.tsx index adf53c1d06..f3b94a3891 100644 --- a/packages/twenty-front/src/modules/page-layout/widgets/components/WidgetActionFieldEdit.tsx +++ b/packages/twenty-front/src/modules/page-layout/widgets/components/WidgetActionFieldEdit.tsx @@ -4,6 +4,7 @@ import { formatFieldMetadataItemAsColumnDefinition } from '@/object-metadata/uti import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions'; import { useIsRecordReadOnly } from '@/object-record/read-only/hooks/useIsRecordReadOnly'; import { isRecordFieldReadOnly } from '@/object-record/read-only/utils/isRecordFieldReadOnly'; +import { RecordFieldsScopeContextProvider } from '@/object-record/record-field-list/contexts/RecordFieldsScopeContext'; import { FieldContext, type GenericFieldContextType, @@ -70,21 +71,24 @@ export const WidgetActionFieldEdit = () => { const isRelationField = isFieldRelation(fieldDefinition) || isFieldMorphRelation(fieldDefinition); - if (isRelationField) { - return ( - - ); - } - const instanceId = getFieldWidgetInstanceId({ + widgetId: widget.id, recordId: targetRecord.id, fieldName: fieldMetadataItem.name, isInRightDrawer, }); + if (isRelationField) { + return ( + + + + ); + } + const recordFieldInputInstanceId = getRecordFieldInputInstanceId({ recordId: targetRecord.id, fieldName: fieldMetadataItem.name, @@ -113,14 +117,16 @@ export const WidgetActionFieldEdit = () => { } satisfies GenericFieldContextType; return ( - - - - - + + + + + + + ); }; diff --git a/packages/twenty-front/src/modules/page-layout/widgets/field/components/FieldWidgetCellEditModePortal.tsx b/packages/twenty-front/src/modules/page-layout/widgets/field/components/FieldWidgetCellEditModePortal.tsx index dfcb645003..a935edff30 100644 --- a/packages/twenty-front/src/modules/page-layout/widgets/field/components/FieldWidgetCellEditModePortal.tsx +++ b/packages/twenty-front/src/modules/page-layout/widgets/field/components/FieldWidgetCellEditModePortal.tsx @@ -1,5 +1,6 @@ import { type FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem'; import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; +import { useRecordFieldsScopeContextOrThrow } from '@/object-record/record-field-list/contexts/RecordFieldsScopeContext'; import { FieldInput } from '@/object-record/record-field/ui/components/FieldInput'; import { RecordInlineCellAnchoredPortal } from '@/object-record/record-inline-cell/components/RecordInlineCellAnchoredPortal'; import { RecordInlineCellEditMode } from '@/object-record/record-inline-cell/components/RecordInlineCellEditMode'; @@ -23,12 +24,14 @@ export const FieldWidgetCellEditModePortal = ({ recordId, instanceId, }: FieldWidgetCellEditModePortalProps) => { + const { scopeInstanceId } = useRecordFieldsScopeContextOrThrow(); const activeDropdownFocusId = useRecoilValue(activeDropdownFocusIdState); - const expectedDropdownFocusId = getDropdownFocusIdForRecordField( + const expectedDropdownFocusId = getDropdownFocusIdForRecordField({ recordId, - fieldMetadataItem.id, - 'inline-cell', - ); + fieldMetadataId: fieldMetadataItem.id, + componentType: 'inline-cell', + instanceId: scopeInstanceId, + }); const isEditing = activeDropdownFocusId === expectedDropdownFocusId; const setIsHovered = useSetRecoilComponentState( diff --git a/packages/twenty-front/src/modules/page-layout/widgets/field/components/FieldWidgetCellHoveredPortal.tsx b/packages/twenty-front/src/modules/page-layout/widgets/field/components/FieldWidgetCellHoveredPortal.tsx index 87342ac7ba..edc05e5b82 100644 --- a/packages/twenty-front/src/modules/page-layout/widgets/field/components/FieldWidgetCellHoveredPortal.tsx +++ b/packages/twenty-front/src/modules/page-layout/widgets/field/components/FieldWidgetCellHoveredPortal.tsx @@ -1,5 +1,6 @@ import { type FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem'; import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; +import { useRecordFieldsScopeContextOrThrow } from '@/object-record/record-field-list/contexts/RecordFieldsScopeContext'; import { RecordInlineCellAnchoredPortal } from '@/object-record/record-inline-cell/components/RecordInlineCellAnchoredPortal'; import { getDropdownFocusIdForRecordField } from '@/object-record/utils/getDropdownFocusIdForRecordField'; import { FieldWidgetCellHoveredContent } from '@/page-layout/widgets/field/components/FieldWidgetCellHoveredContent'; @@ -23,12 +24,14 @@ export const FieldWidgetCellHoveredPortal = ({ isHovered, onMouseLeave, }: FieldWidgetCellHoveredPortalProps) => { + const { scopeInstanceId } = useRecordFieldsScopeContextOrThrow(); const activeDropdownFocusId = useRecoilValue(activeDropdownFocusIdState); - const expectedDropdownFocusId = getDropdownFocusIdForRecordField( + const expectedDropdownFocusId = getDropdownFocusIdForRecordField({ recordId, - fieldMetadataItem.id, - 'inline-cell', - ); + fieldMetadataId: fieldMetadataItem.id, + componentType: 'inline-cell', + instanceId: scopeInstanceId, + }); const isEditing = activeDropdownFocusId === expectedDropdownFocusId; if (!isHovered || isEditing) { diff --git a/packages/twenty-front/src/modules/page-layout/widgets/field/components/FieldWidgetDisplay.tsx b/packages/twenty-front/src/modules/page-layout/widgets/field/components/FieldWidgetDisplay.tsx index 54724bd751..d9b2ea17ac 100644 --- a/packages/twenty-front/src/modules/page-layout/widgets/field/components/FieldWidgetDisplay.tsx +++ b/packages/twenty-front/src/modules/page-layout/widgets/field/components/FieldWidgetDisplay.tsx @@ -3,6 +3,7 @@ import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataI import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions'; import { useIsRecordReadOnly } from '@/object-record/read-only/hooks/useIsRecordReadOnly'; import { isRecordFieldReadOnly } from '@/object-record/read-only/utils/isRecordFieldReadOnly'; +import { RecordFieldsScopeContextProvider } from '@/object-record/record-field-list/contexts/RecordFieldsScopeContext'; import { FieldContext } from '@/object-record/record-field/ui/contexts/FieldContext'; import { RecordFieldComponentInstanceContext } from '@/object-record/record-field/ui/states/contexts/RecordFieldComponentInstanceContext'; import { type FieldDefinition } from '@/object-record/record-field/ui/types/FieldDefinition'; @@ -13,6 +14,7 @@ import { FieldWidgetCellHoveredPortal } from '@/page-layout/widgets/field/compon import { FieldWidgetInlineCell } from '@/page-layout/widgets/field/components/FieldWidgetInlineCell'; import { fieldWidgetHoverComponentState } from '@/page-layout/widgets/field/states/fieldWidgetHoverComponentState'; import { getFieldWidgetInstanceId } from '@/page-layout/widgets/field/utils/getFieldWidgetInstanceId'; +import { useCurrentWidget } from '@/page-layout/widgets/hooks/useCurrentWidget'; import { getObjectPermissionsFromMapByObjectMetadataId } from '@/settings/roles/role-permissions/objects-permissions/utils/getObjectPermissionsFromMapByObjectMetadataId'; import { RightDrawerProvider } from '@/ui/layout/right-drawer/contexts/RightDrawerContext'; import { useRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentState'; @@ -38,11 +40,14 @@ export const FieldWidgetDisplay = ({ recordId, isInRightDrawer, }: FieldWidgetDisplayProps) => { + const widget = useCurrentWidget(); + const [isHovered, setIsHovered] = useRecoilComponentState( fieldWidgetHoverComponentState, ); const instanceId = getFieldWidgetInstanceId({ + widgetId: widget.id, recordId, fieldName: fieldMetadataItem.name, isInRightDrawer, @@ -64,64 +69,66 @@ export const FieldWidgetDisplay = ({ const handleMouseLeave = () => setIsHovered(false); return ( - - - - + + + + - - - + maxWidth: 200, + isLabelIdentifier: false, + fieldDefinition, + useUpdateRecord: useUpdateOneObjectRecordMutation, + isDisplayModeFixHeight: false, + isRecordFieldReadOnly: isRecordFieldReadOnly({ + isRecordReadOnly, + objectPermissions: + getObjectPermissionsFromMapByObjectMetadataId({ + objectPermissionsByObjectMetadataId, + objectMetadataId: objectMetadataItem.id, + }), + fieldMetadataItem: { + id: fieldMetadataItem.id, + isUIReadOnly: fieldMetadataItem.isUIReadOnly ?? false, + }, + }), + onMouseEnter: handleMouseEnter, + anchorId: getRecordFieldInputInstanceId({ + recordId, + fieldName: fieldMetadataItem.name, + prefix: instanceId, + }), + }} + > + + + - - - - + + + + + ); }; diff --git a/packages/twenty-front/src/modules/page-layout/widgets/field/components/FieldWidgetInlineCell.tsx b/packages/twenty-front/src/modules/page-layout/widgets/field/components/FieldWidgetInlineCell.tsx index a4c3aad46e..8d906cab2d 100644 --- a/packages/twenty-front/src/modules/page-layout/widgets/field/components/FieldWidgetInlineCell.tsx +++ b/packages/twenty-front/src/modules/page-layout/widgets/field/components/FieldWidgetInlineCell.tsx @@ -9,6 +9,7 @@ import { useGetButtonIcon } from '@/object-record/record-field/ui/hooks/useGetBu import { useIsFieldInputOnly } from '@/object-record/record-field/ui/hooks/useIsFieldInputOnly'; import { useOpenFieldInputEditMode } from '@/object-record/record-field/ui/hooks/useOpenFieldInputEditMode'; +import { useRecordFieldsScopeContextOrThrow } from '@/object-record/record-field-list/contexts/RecordFieldsScopeContext'; import { FieldInputEventContext, type FieldInputClickOutsideEvent, @@ -34,6 +35,7 @@ export const FieldWidgetInlineCell = ({ loading, instanceIdPrefix, }: FieldWidgetInlineCellProps) => { + const { scopeInstanceId } = useRecordFieldsScopeContextOrThrow(); const { fieldDefinition, recordId, @@ -141,11 +143,12 @@ export const FieldWidgetInlineCell = ({ .getLoadable(activeDropdownFocusIdState) .getValue(); - const expectedDropdownFocusId = getDropdownFocusIdForRecordField( + const expectedDropdownFocusId = getDropdownFocusIdForRecordField({ recordId, - fieldDefinition.fieldMetadataId, - 'inline-cell', - ); + fieldMetadataId: fieldDefinition.fieldMetadataId, + componentType: 'inline-cell', + instanceId: scopeInstanceId, + }); if (currentDropdownFocusId !== expectedDropdownFocusId) { return; @@ -165,6 +168,7 @@ export const FieldWidgetInlineCell = ({ recordId, fieldDefinition.fieldMetadataId, persistFieldFromFieldInputContext, + scopeInstanceId, ], ); diff --git a/packages/twenty-front/src/modules/page-layout/widgets/field/components/FieldWidgetInlineCellContainer.tsx b/packages/twenty-front/src/modules/page-layout/widgets/field/components/FieldWidgetInlineCellContainer.tsx index da1d1ca508..65e8b2fec2 100644 --- a/packages/twenty-front/src/modules/page-layout/widgets/field/components/FieldWidgetInlineCellContainer.tsx +++ b/packages/twenty-front/src/modules/page-layout/widgets/field/components/FieldWidgetInlineCellContainer.tsx @@ -4,7 +4,7 @@ import { useContext } from 'react'; import { FieldContext } from '@/object-record/record-field/ui/contexts/FieldContext'; import { useFieldFocus } from '@/object-record/record-field/ui/hooks/useFieldFocus'; import { useRecordInlineCellContext } from '@/object-record/record-inline-cell/components/RecordInlineCellContext'; -import { FieldWidgetInlineCellValue } from '@/page-layout/widgets/field/components/FieldWidgetInlineCellValue'; +import { RecordInlineCellValue } from '@/object-record/record-inline-cell/components/RecordInlineCellValue'; const StyledValueContainer = styled.div<{ readonly: boolean }>` display: flex; @@ -52,7 +52,7 @@ export const FieldWidgetInlineCellContainer = () => { onMouseLeave={handleContainerMouseLeave} > - + ); diff --git a/packages/twenty-front/src/modules/page-layout/widgets/field/components/FieldWidgetInlineCellValue.tsx b/packages/twenty-front/src/modules/page-layout/widgets/field/components/FieldWidgetInlineCellValue.tsx deleted file mode 100644 index 3985ddfd6f..0000000000 --- a/packages/twenty-front/src/modules/page-layout/widgets/field/components/FieldWidgetInlineCellValue.tsx +++ /dev/null @@ -1,45 +0,0 @@ -import { css } from '@emotion/react'; -import styled from '@emotion/styled'; - -import { FieldDisplay } from '@/object-record/record-field/ui/components/FieldDisplay'; -import { useRecordInlineCellContext } from '@/object-record/record-inline-cell/components/RecordInlineCellContext'; -import { RecordInlineCellDisplayMode } from '@/object-record/record-inline-cell/components/RecordInlineCellDisplayMode'; -import { RecordInlineCellSkeletonLoader } from '@/object-record/record-inline-cell/components/RecordInlineCellSkeletonLoader'; - -const StyledClickableContainer = styled.div<{ - readonly?: boolean; - isCentered?: boolean; -}>` - align-items: center; - display: flex; - gap: ${({ theme }) => theme.spacing(1)}; - width: 100%; - - ${({ isCentered }) => - isCentered === true && - ` - justify-content: center; - `}; - - ${({ readonly }) => - !readonly && - css` - cursor: pointer; - `}; -`; - -export const FieldWidgetInlineCellValue = () => { - const { readonly, loading, isCentered } = useRecordInlineCellContext(); - - if (loading === true) { - return ; - } - - return ( - - - - - - ); -}; diff --git a/packages/twenty-front/src/modules/page-layout/widgets/field/components/FieldWidgetRelationEditAction.tsx b/packages/twenty-front/src/modules/page-layout/widgets/field/components/FieldWidgetRelationEditAction.tsx index e8668df6dd..4641ee04e1 100644 --- a/packages/twenty-front/src/modules/page-layout/widgets/field/components/FieldWidgetRelationEditAction.tsx +++ b/packages/twenty-front/src/modules/page-layout/widgets/field/components/FieldWidgetRelationEditAction.tsx @@ -1,4 +1,5 @@ import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems'; +import { useRecordFieldsScopeContextOrThrow } from '@/object-record/record-field-list/contexts/RecordFieldsScopeContext'; import { RecordDetailMorphRelationSectionDropdown } from '@/object-record/record-field-list/record-detail-section/relation/components/RecordDetailMorphRelationSectionDropdown'; import { RecordDetailRelationSectionDropdown } from '@/object-record/record-field-list/record-detail-section/relation/components/RecordDetailRelationSectionDropdown'; import { @@ -51,6 +52,8 @@ export const FieldWidgetRelationEditAction = ({ fieldDefinition, recordId, }: FieldWidgetRelationEditActionProps) => { + const { scopeInstanceId } = useRecordFieldsScopeContextOrThrow(); + const { objectMetadataItems } = useObjectMetadataItems(); const objectMetadataItem = objectMetadataItems.find( (item) => @@ -89,6 +92,7 @@ export const FieldWidgetRelationEditAction = ({ getRecordFieldCardRelationPickerDropdownId({ fieldDefinition, recordId, + instanceId: scopeInstanceId, }); const isRelationSelectionDropdownOpen = useRecoilComponentValue( diff --git a/packages/twenty-front/src/modules/page-layout/widgets/field/utils/getFieldWidgetInstanceId.ts b/packages/twenty-front/src/modules/page-layout/widgets/field/utils/getFieldWidgetInstanceId.ts index 2b7f66f760..54d30e8780 100644 --- a/packages/twenty-front/src/modules/page-layout/widgets/field/utils/getFieldWidgetInstanceId.ts +++ b/packages/twenty-front/src/modules/page-layout/widgets/field/utils/getFieldWidgetInstanceId.ts @@ -1,11 +1,13 @@ export const getFieldWidgetInstanceId = ({ + widgetId, recordId, fieldName, isInRightDrawer, }: { + widgetId: string; recordId: string; fieldName: string; isInRightDrawer: boolean; }): string => { - return `field-widget-${recordId}-${fieldName}${isInRightDrawer ? '-right-drawer' : ''}`; + return `${widgetId}-field-widget-${recordId}-${fieldName}${isInRightDrawer ? '-right-drawer' : ''}`; }; diff --git a/packages/twenty-front/src/modules/page-layout/widgets/fields/components/FieldsWidget.tsx b/packages/twenty-front/src/modules/page-layout/widgets/fields/components/FieldsWidget.tsx index 3e27152972..c5d97e2568 100644 --- a/packages/twenty-front/src/modules/page-layout/widgets/fields/components/FieldsWidget.tsx +++ b/packages/twenty-front/src/modules/page-layout/widgets/fields/components/FieldsWidget.tsx @@ -3,6 +3,7 @@ import { formatFieldMetadataItemAsColumnDefinition } from '@/object-metadata/uti import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions'; import { useIsRecordReadOnly } from '@/object-record/read-only/hooks/useIsRecordReadOnly'; import { isRecordFieldReadOnly } from '@/object-record/read-only/utils/isRecordFieldReadOnly'; +import { RecordFieldsScopeContextProvider } from '@/object-record/record-field-list/contexts/RecordFieldsScopeContext'; import { RecordDetailSectionContainer } from '@/object-record/record-field-list/record-detail-section/components/RecordDetailSectionContainer'; import { RecordFieldListComponentInstanceContext } from '@/object-record/record-field-list/states/contexts/RecordFieldListComponentInstanceContext'; import { recordFieldListHoverPositionComponentState } from '@/object-record/record-field-list/states/recordFieldListHoverPositionComponentState'; @@ -44,11 +45,11 @@ type FieldsWidgetProps = { widget: PageLayoutWidget; }; -export const FieldsWidget = ({ widget: _widget }: FieldsWidgetProps) => { +export const FieldsWidget = ({ widget }: FieldsWidgetProps) => { const targetRecord = useTargetRecord(); const { isInRightDrawer } = useLayoutRenderingContext(); - const instanceId = `fields-widget-${targetRecord.id}-${isInRightDrawer ? 'right-drawer' : ''}`; + const instanceId = `fields-widget-${widget.id}-${targetRecord.id}${isInRightDrawer ? '-right-drawer' : ''}`; const { recordLoading, isPrefetchLoading } = useRecordShowContainerData({ objectRecordId: targetRecord.id, @@ -103,93 +104,97 @@ export const FieldsWidget = ({ widget: _widget }: FieldsWidgetProps) => { } return ( - - - {sectionsWithFieldIndices.map((section) => ( - - - {isPrefetchLoading ? ( - - ) : ( - <> - {section.fields.map( - ({ field: fieldMetadataItem, globalIndex }) => { - return ( - - setRecordFieldListHoverPosition(globalIndex), - anchorId: `${getRecordFieldInputInstanceId({ - recordId: targetRecord.id, - fieldName: fieldMetadataItem.name, - prefix: instanceId, - })}`, - }} - > - + + + {sectionsWithFieldIndices.map((section) => ( + + + {isPrefetchLoading ? ( + + ) : ( + <> + {section.fields.map( + ({ field: fieldMetadataItem, globalIndex }) => { + return ( + + setRecordFieldListHoverPosition(globalIndex), + anchorId: `${getRecordFieldInputInstanceId({ recordId: targetRecord.id, fieldName: fieldMetadataItem.name, prefix: instanceId, - }), + })}`, }} > - - - - ); - }, - )} - - )} - - - ))} + + + + + ); + }, + )} + + )} + + + ))} - - - - + + + + + ); }; diff --git a/packages/twenty-front/src/testing/components/StorybookFieldInputDropdownFocusIdSetterEffect.tsx b/packages/twenty-front/src/testing/components/StorybookFieldInputDropdownFocusIdSetterEffect.tsx index 6f6f9b8e2d..eef392b32a 100644 --- a/packages/twenty-front/src/testing/components/StorybookFieldInputDropdownFocusIdSetterEffect.tsx +++ b/packages/twenty-front/src/testing/components/StorybookFieldInputDropdownFocusIdSetterEffect.tsx @@ -1,3 +1,4 @@ +import { useRecordFieldsScopeContextOrThrow } from '@/object-record/record-field-list/contexts/RecordFieldsScopeContext'; import { FieldContext } from '@/object-record/record-field/ui/contexts/FieldContext'; import { getDropdownFocusIdForRecordField } from '@/object-record/utils/getDropdownFocusIdForRecordField'; import { useSetActiveDropdownFocusIdAndMemorizePrevious } from '@/ui/layout/dropdown/hooks/useSetFocusedDropdownIdAndMemorizePrevious'; @@ -5,15 +6,17 @@ import { useContext, useEffect } from 'react'; export const StorybookFieldInputDropdownFocusIdSetterEffect = () => { const { recordId, fieldDefinition } = useContext(FieldContext); + const { scopeInstanceId } = useRecordFieldsScopeContextOrThrow(); const { setActiveDropdownFocusIdAndMemorizePrevious } = useSetActiveDropdownFocusIdAndMemorizePrevious(); - const fieldDropdownFocusIdTableCell = getDropdownFocusIdForRecordField( + const fieldDropdownFocusIdTableCell = getDropdownFocusIdForRecordField({ recordId, - fieldDefinition.fieldMetadataId, - 'table-cell', - ); + fieldMetadataId: fieldDefinition.fieldMetadataId, + componentType: 'table-cell', + instanceId: scopeInstanceId, + }); useEffect(() => { setActiveDropdownFocusIdAndMemorizePrevious(fieldDropdownFocusIdTableCell);