Refactor board field definitions to use record fields (#14165)
This PR removes board field definitions usage in favor of record fields, like we've already done on table with table columns. This PR also introduces a first step in the refactoring of the RecordIndexContext as a new way to store in-memory a normalized cache for some states used pretty much everywhere like field metadata items or field definition (for now but it will soon be removed). Since most of those states do not change if we don't modify the metadata, then we can safely store them in a context, which has the fastest access time in React. Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
co-authored by
Charles Bochet
parent
74eeb98293
commit
db5897d4d0
+30
-21
@@ -11,10 +11,15 @@ import { ViewComponentInstanceContext } from '@/views/states/contexts/ViewCompon
|
||||
import { type MockedResponse } from '@apollo/client/testing';
|
||||
import gql from 'graphql-tag';
|
||||
import { getJestMetadataAndApolloMocksWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksWrapper';
|
||||
import { peopleQueryResult } from '~/testing/mock-data/people';
|
||||
import { JestRecordIndexContextProviderWrapper } from '~/testing/jest/JestRecordIndexContextProviderWrapper';
|
||||
import {
|
||||
getMockPersonObjectMetadataItem,
|
||||
peopleQueryResult,
|
||||
} from '~/testing/mock-data/people';
|
||||
|
||||
const recordTableId = 'people';
|
||||
const objectNameSingular = 'person';
|
||||
const mockPersonObjectMetadataItem = getMockPersonObjectMetadataItem();
|
||||
|
||||
const ObjectNamePluralSetter = ({ children }: { children: ReactNode }) => {
|
||||
return <>{children}</>;
|
||||
@@ -660,29 +665,33 @@ jest.mock('react-router-dom', () => ({
|
||||
const Wrapper = ({ children }: { children: ReactNode }) => {
|
||||
return (
|
||||
<HookMockWrapper>
|
||||
<RecordTableContextProvider
|
||||
objectNameSingular={objectNameSingular}
|
||||
recordTableId={recordTableId}
|
||||
viewBarId="instanceId"
|
||||
<ViewComponentInstanceContext.Provider
|
||||
value={{ instanceId: 'instanceId' }}
|
||||
>
|
||||
<ObjectNamePluralSetter>
|
||||
<ViewComponentInstanceContext.Provider
|
||||
value={{ instanceId: 'instanceId' }}
|
||||
<RecordComponentInstanceContextsWrapper
|
||||
componentInstanceId={recordTableId}
|
||||
>
|
||||
<JestRecordIndexContextProviderWrapper
|
||||
objectMetadataItem={mockPersonObjectMetadataItem}
|
||||
>
|
||||
<RecordComponentInstanceContextsWrapper
|
||||
componentInstanceId={recordTableId}
|
||||
<RecordTableContextProvider
|
||||
objectNameSingular={objectNameSingular}
|
||||
recordTableId={recordTableId}
|
||||
viewBarId="instanceId"
|
||||
>
|
||||
<RecordTableComponentInstance recordTableId={recordTableId}>
|
||||
<RecordGroupContext.Provider
|
||||
value={{ recordGroupId: 'default' }}
|
||||
>
|
||||
{children}
|
||||
</RecordGroupContext.Provider>
|
||||
</RecordTableComponentInstance>
|
||||
</RecordComponentInstanceContextsWrapper>
|
||||
</ViewComponentInstanceContext.Provider>
|
||||
</ObjectNamePluralSetter>
|
||||
</RecordTableContextProvider>
|
||||
<ObjectNamePluralSetter>
|
||||
<RecordTableComponentInstance recordTableId={recordTableId}>
|
||||
<RecordGroupContext.Provider
|
||||
value={{ recordGroupId: 'default' }}
|
||||
>
|
||||
{children}
|
||||
</RecordGroupContext.Provider>
|
||||
</RecordTableComponentInstance>
|
||||
</ObjectNamePluralSetter>
|
||||
</RecordTableContextProvider>
|
||||
</JestRecordIndexContextProviderWrapper>
|
||||
</RecordComponentInstanceContextsWrapper>
|
||||
</ViewComponentInstanceContext.Provider>
|
||||
</HookMockWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
+15
@@ -7,6 +7,7 @@ import { OBJECT_OPTIONS_DROPDOWN_ID } from '@/object-record/object-options-dropd
|
||||
import { ObjectOptionsDropdownContext } from '@/object-record/object-options-dropdown/states/contexts/ObjectOptionsDropdownContext';
|
||||
import { type ObjectOptionsContentId } from '@/object-record/object-options-dropdown/types/ObjectOptionsContentId';
|
||||
import { RecordIndexContextProvider } from '@/object-record/record-index/contexts/RecordIndexContext';
|
||||
import { useRecordIndexFieldMetadataDerivedStates } from '@/object-record/record-index/hooks/useRecordIndexFieldMetadataDerivedStates';
|
||||
import { RecordTableComponentInstanceContext } from '@/object-record/record-table/states/context/RecordTableComponentInstanceContext';
|
||||
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
|
||||
import { ViewComponentInstanceContext } from '@/views/states/contexts/ViewComponentInstanceContext';
|
||||
@@ -77,6 +78,16 @@ const createStory = (contentId: ObjectOptionsContentId | null): Story => ({
|
||||
(item) => item.nameSingular === 'company',
|
||||
)!;
|
||||
|
||||
const {
|
||||
fieldDefinitionByFieldMetadataItemId,
|
||||
fieldMetadataItemByFieldMetadataItemId,
|
||||
labelIdentifierFieldMetadataItem,
|
||||
recordFieldByFieldMetadataItemId,
|
||||
} = useRecordIndexFieldMetadataDerivedStates(
|
||||
companyObjectMetadataItem,
|
||||
instanceId,
|
||||
);
|
||||
|
||||
return (
|
||||
<RecordIndexContextProvider
|
||||
value={{
|
||||
@@ -87,6 +98,10 @@ const createStory = (contentId: ObjectOptionsContentId | null): Story => ({
|
||||
objectNameSingular: 'company',
|
||||
objectMetadataItem: companyObjectMetadataItem,
|
||||
recordIndexId: instanceId,
|
||||
fieldDefinitionByFieldMetadataItemId,
|
||||
fieldMetadataItemByFieldMetadataItemId,
|
||||
labelIdentifierFieldMetadataItem,
|
||||
recordFieldByFieldMetadataItemId,
|
||||
}}
|
||||
>
|
||||
<ObjectOptionsDropdownContext.Provider
|
||||
|
||||
+2
-13
@@ -1,13 +1,12 @@
|
||||
import { recordIndexActionMenuDropdownPositionComponentState } from '@/action-menu/states/recordIndexActionMenuDropdownPositionComponentState';
|
||||
import { getActionMenuDropdownIdFromActionMenuId } from '@/action-menu/utils/getActionMenuDropdownIdFromActionMenuId';
|
||||
import { getActionMenuIdFromRecordIndexId } from '@/action-menu/utils/getActionMenuIdFromRecordIndexId';
|
||||
import { useRecordDragState } from '@/object-record/record-drag/shared/hooks/useRecordDragState';
|
||||
import { RecordBoardCardContext } from '@/object-record/record-board/record-board-card/contexts/RecordBoardCardContext';
|
||||
import { isRecordBoardCardActiveComponentFamilyState } from '@/object-record/record-board/states/isRecordBoardCardActiveComponentFamilyState';
|
||||
import { isRecordBoardCardFocusedComponentFamilyState } from '@/object-record/record-board/states/isRecordBoardCardFocusedComponentFamilyState';
|
||||
import { isRecordBoardCardSelectedComponentFamilyState } from '@/object-record/record-board/states/isRecordBoardCardSelectedComponentFamilyState';
|
||||
import { isRecordBoardCompactModeActiveComponentState } from '@/object-record/record-board/states/isRecordBoardCompactModeActiveComponentState';
|
||||
import { recordBoardVisibleFieldDefinitionsComponentSelector } from '@/object-record/record-board/states/selectors/recordBoardVisibleFieldDefinitionsComponentSelector';
|
||||
import { useRecordDragState } from '@/object-record/record-drag/shared/hooks/useRecordDragState';
|
||||
|
||||
import { useActiveRecordBoardCard } from '@/object-record/record-board/hooks/useActiveRecordBoardCard';
|
||||
import { useFocusedRecordBoardCard } from '@/object-record/record-board/hooks/useFocusedRecordBoardCard';
|
||||
@@ -140,10 +139,6 @@ export const RecordBoardCard = () => {
|
||||
multiDragState.originalSelection.includes(recordId) &&
|
||||
recordId !== multiDragState.primaryDraggedRecordId;
|
||||
|
||||
const visibleFieldDefinitions = useRecoilComponentValue(
|
||||
recordBoardVisibleFieldDefinitionsComponentSelector,
|
||||
);
|
||||
|
||||
const isCompactModeActive = useRecoilComponentValue(
|
||||
isRecordBoardCompactModeActiveComponentState,
|
||||
);
|
||||
@@ -223,10 +218,6 @@ export const RecordBoardCard = () => {
|
||||
rootMargin: '1000px',
|
||||
});
|
||||
|
||||
const visibleFieldDefinitionsFiltered = visibleFieldDefinitions.filter(
|
||||
(boardField) => !boardField.isLabelIdentifier,
|
||||
);
|
||||
|
||||
return (
|
||||
<RecordBoardCardComponentInstanceContext.Provider
|
||||
value={{
|
||||
@@ -270,9 +261,7 @@ export const RecordBoardCard = () => {
|
||||
isOpen={isCardExpanded || !isCompactModeActive}
|
||||
initial={false}
|
||||
>
|
||||
<RecordBoardCardBody
|
||||
fieldDefinitions={visibleFieldDefinitionsFiltered}
|
||||
/>
|
||||
<RecordBoardCardBody />
|
||||
</AnimatedEaseInOut>
|
||||
</StyledBoardCard>
|
||||
</StyledCardContainer>
|
||||
|
||||
+58
-60
@@ -5,29 +5,30 @@ import { StopPropagationContainer } from '@/object-record/record-board/record-bo
|
||||
import { RECORD_BOARD_CARD_INPUT_ID_PREFIX } from '@/object-record/record-board/record-board-card/constants/RecordBoardCardInputIdPrefix';
|
||||
import { RecordBoardCardContext } from '@/object-record/record-board/record-board-card/contexts/RecordBoardCardContext';
|
||||
import { recordBoardCardHoverPositionComponentState } from '@/object-record/record-board/record-board-card/states/recordBoardCardHoverPositionComponentState';
|
||||
import { type RecordBoardFieldDefinition } from '@/object-record/record-board/types/RecordBoardFieldDefinition';
|
||||
import { visibleRecordFieldsComponentSelector } from '@/object-record/record-field/states/visibleRecordFieldsComponentSelector';
|
||||
import {
|
||||
FieldContext,
|
||||
type RecordUpdateHook,
|
||||
type RecordUpdateHookParams,
|
||||
} from '@/object-record/record-field/ui/contexts/FieldContext';
|
||||
import { RecordFieldComponentInstanceContext } from '@/object-record/record-field/ui/states/contexts/RecordFieldComponentInstanceContext';
|
||||
import { type FieldMetadata } from '@/object-record/record-field/ui/types/FieldMetadata';
|
||||
import { getFieldButtonIcon } from '@/object-record/record-field/ui/utils/getFieldButtonIcon';
|
||||
import { useRecordIndexContextOrThrow } from '@/object-record/record-index/contexts/RecordIndexContext';
|
||||
import { RecordInlineCell } from '@/object-record/record-inline-cell/components/RecordInlineCell';
|
||||
import { getRecordFieldInputInstanceId } from '@/object-record/utils/getRecordFieldInputId';
|
||||
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
|
||||
import { useSetRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentState';
|
||||
import { useContext } from 'react';
|
||||
|
||||
export const RecordBoardCardBody = ({
|
||||
fieldDefinitions,
|
||||
}: {
|
||||
fieldDefinitions: RecordBoardFieldDefinition<FieldMetadata>[];
|
||||
}) => {
|
||||
export const RecordBoardCardBody = () => {
|
||||
const { recordId, isRecordReadOnly } = useContext(RecordBoardCardContext);
|
||||
|
||||
const { updateOneRecord, objectPermissions } = useContext(RecordBoardContext);
|
||||
|
||||
const {
|
||||
labelIdentifierFieldMetadataItem,
|
||||
fieldDefinitionByFieldMetadataItemId,
|
||||
} = useRecordIndexContextOrThrow();
|
||||
|
||||
const useUpdateOneRecordHook: RecordUpdateHook = () => {
|
||||
const updateEntity = ({ variables }: RecordUpdateHookParams) => {
|
||||
updateOneRecord?.({
|
||||
@@ -39,18 +40,13 @@ export const RecordBoardCardBody = ({
|
||||
return [updateEntity, { loading: false }];
|
||||
};
|
||||
|
||||
const fieldDefinitionsWithReadOnly = fieldDefinitions.map(
|
||||
(fieldDefinition) => ({
|
||||
...fieldDefinition,
|
||||
isRecordFieldReadOnly: isRecordFieldReadOnly({
|
||||
isRecordReadOnly,
|
||||
objectPermissions,
|
||||
fieldMetadataItem: {
|
||||
id: fieldDefinition.fieldMetadataId,
|
||||
isUIReadOnly: fieldDefinition.metadata.isUIReadOnly ?? false,
|
||||
},
|
||||
}),
|
||||
}),
|
||||
const visibleRecordFields = useRecoilComponentValue(
|
||||
visibleRecordFieldsComponentSelector,
|
||||
);
|
||||
|
||||
const visibleRecordFieldsExceptLabelIdentifier = visibleRecordFields.filter(
|
||||
(recordField) =>
|
||||
recordField.fieldMetadataItemId !== labelIdentifierFieldMetadataItem?.id,
|
||||
);
|
||||
|
||||
const setRecordBoardCardHoverPosition = useSetRecoilComponentState(
|
||||
@@ -63,50 +59,52 @@ export const RecordBoardCardBody = ({
|
||||
|
||||
return (
|
||||
<RecordBoardCardBodyContainer>
|
||||
{fieldDefinitionsWithReadOnly.map((fieldDefinition, index) => (
|
||||
<StopPropagationContainer key={fieldDefinition.fieldMetadataId}>
|
||||
<FieldContext.Provider
|
||||
value={{
|
||||
recordId,
|
||||
maxWidth: 156,
|
||||
isLabelIdentifier: false,
|
||||
isRecordFieldReadOnly: fieldDefinition.isRecordFieldReadOnly,
|
||||
fieldDefinition: {
|
||||
disableTooltip: false,
|
||||
fieldMetadataId: fieldDefinition.fieldMetadataId,
|
||||
label: fieldDefinition.label,
|
||||
iconName: fieldDefinition.iconName,
|
||||
type: fieldDefinition.type,
|
||||
metadata: fieldDefinition.metadata,
|
||||
defaultValue: fieldDefinition.defaultValue,
|
||||
editButtonIcon: getFieldButtonIcon({
|
||||
metadata: fieldDefinition.metadata,
|
||||
type: fieldDefinition.type,
|
||||
}),
|
||||
},
|
||||
useUpdateRecord: useUpdateOneRecordHook,
|
||||
isDisplayModeFixHeight: true,
|
||||
triggerEvent: 'CLICK',
|
||||
anchorId: `${RECORD_BOARD_CARD_INPUT_ID_PREFIX}-${recordId}-${fieldDefinition.metadata.fieldName}`,
|
||||
onMouseEnter: () => handleMouseEnter(index),
|
||||
}}
|
||||
>
|
||||
<RecordFieldComponentInstanceContext.Provider
|
||||
{visibleRecordFieldsExceptLabelIdentifier.map((recordField, index) => {
|
||||
const correspondingFieldDefinition =
|
||||
fieldDefinitionByFieldMetadataItemId[recordField.fieldMetadataItemId];
|
||||
|
||||
return (
|
||||
<StopPropagationContainer key={recordField.fieldMetadataItemId}>
|
||||
<FieldContext.Provider
|
||||
value={{
|
||||
instanceId: getRecordFieldInputInstanceId({
|
||||
recordId,
|
||||
fieldName: fieldDefinition.metadata.fieldName,
|
||||
prefix: RECORD_BOARD_CARD_INPUT_ID_PREFIX,
|
||||
recordId,
|
||||
maxWidth: 156,
|
||||
isLabelIdentifier: false,
|
||||
isRecordFieldReadOnly: isRecordFieldReadOnly({
|
||||
isRecordReadOnly,
|
||||
objectPermissions,
|
||||
fieldMetadataItem: {
|
||||
id: recordField.fieldMetadataItemId,
|
||||
isUIReadOnly:
|
||||
correspondingFieldDefinition.metadata.isUIReadOnly ??
|
||||
false,
|
||||
},
|
||||
}),
|
||||
fieldDefinition: correspondingFieldDefinition,
|
||||
useUpdateRecord: useUpdateOneRecordHook,
|
||||
isDisplayModeFixHeight: true,
|
||||
triggerEvent: 'CLICK',
|
||||
anchorId: `${RECORD_BOARD_CARD_INPUT_ID_PREFIX}-${recordId}-${correspondingFieldDefinition.metadata.fieldName}`,
|
||||
onMouseEnter: () => handleMouseEnter(index),
|
||||
}}
|
||||
>
|
||||
<RecordInlineCell
|
||||
instanceIdPrefix={RECORD_BOARD_CARD_INPUT_ID_PREFIX}
|
||||
/>
|
||||
</RecordFieldComponentInstanceContext.Provider>
|
||||
</FieldContext.Provider>
|
||||
</StopPropagationContainer>
|
||||
))}
|
||||
<RecordFieldComponentInstanceContext.Provider
|
||||
value={{
|
||||
instanceId: getRecordFieldInputInstanceId({
|
||||
recordId,
|
||||
fieldName: correspondingFieldDefinition.metadata.fieldName,
|
||||
prefix: RECORD_BOARD_CARD_INPUT_ID_PREFIX,
|
||||
}),
|
||||
}}
|
||||
>
|
||||
<RecordInlineCell
|
||||
instanceIdPrefix={RECORD_BOARD_CARD_INPUT_ID_PREFIX}
|
||||
/>
|
||||
</RecordFieldComponentInstanceContext.Provider>
|
||||
</FieldContext.Provider>
|
||||
</StopPropagationContainer>
|
||||
);
|
||||
})}
|
||||
</RecordBoardCardBodyContainer>
|
||||
);
|
||||
};
|
||||
|
||||
+17
-13
@@ -1,7 +1,8 @@
|
||||
import { RecordBoardContext } from '@/object-record/record-board/contexts/RecordBoardContext';
|
||||
import { recordBoardCardEditModePositionComponentState } from '@/object-record/record-board/record-board-card/states/recordBoardCardEditModePositionComponentState';
|
||||
import { recordBoardCardHoverPositionComponentState } from '@/object-record/record-board/record-board-card/states/recordBoardCardHoverPositionComponentState';
|
||||
import { recordBoardVisibleFieldDefinitionsComponentSelector } from '@/object-record/record-board/states/selectors/recordBoardVisibleFieldDefinitionsComponentSelector';
|
||||
import { visibleRecordFieldsComponentSelector } from '@/object-record/record-field/states/visibleRecordFieldsComponentSelector';
|
||||
import { useRecordIndexContextOrThrow } from '@/object-record/record-index/contexts/RecordIndexContext';
|
||||
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
|
||||
import { useContext } from 'react';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
@@ -17,33 +18,36 @@ export const useRecordBoardCardMetadataFromPosition = () => {
|
||||
recordBoardCardEditModePositionComponentState,
|
||||
);
|
||||
|
||||
const visibleFieldDefinitions = useRecoilComponentValue(
|
||||
recordBoardVisibleFieldDefinitionsComponentSelector,
|
||||
const visibleRecordFields = useRecoilComponentValue(
|
||||
visibleRecordFieldsComponentSelector,
|
||||
);
|
||||
|
||||
const visibleFieldDefinitionsFiltered = visibleFieldDefinitions.filter(
|
||||
(boardField) => !boardField.isLabelIdentifier,
|
||||
const { labelIdentifierFieldMetadataItem } = useRecordIndexContextOrThrow();
|
||||
|
||||
const visibleRecordFieldsFiltered = visibleRecordFields.filter(
|
||||
(recordField) =>
|
||||
labelIdentifierFieldMetadataItem?.id !== recordField.fieldMetadataItemId,
|
||||
);
|
||||
|
||||
const hoveredFieldDefinition = isDefined(hoverPosition)
|
||||
? visibleFieldDefinitionsFiltered.at(hoverPosition)
|
||||
const hoveredRecordField = isDefined(hoverPosition)
|
||||
? visibleRecordFieldsFiltered.at(hoverPosition)
|
||||
: undefined;
|
||||
|
||||
const editedFieldDefinition = isDefined(editModePosition)
|
||||
? visibleFieldDefinitionsFiltered.at(editModePosition)
|
||||
const editedRecordField = isDefined(editModePosition)
|
||||
? visibleRecordFieldsFiltered.at(editModePosition)
|
||||
: undefined;
|
||||
|
||||
const hoveredFieldMetadataItem = isDefined(hoveredFieldDefinition)
|
||||
const hoveredFieldMetadataItem = isDefined(hoveredRecordField)
|
||||
? objectMetadataItem.fields.find(
|
||||
(fieldMetadataItem) =>
|
||||
fieldMetadataItem.id === hoveredFieldDefinition.fieldMetadataId,
|
||||
fieldMetadataItem.id === hoveredRecordField.fieldMetadataItemId,
|
||||
)
|
||||
: undefined;
|
||||
|
||||
const editedFieldMetadataItem = isDefined(editedFieldDefinition)
|
||||
const editedFieldMetadataItem = isDefined(editedRecordField)
|
||||
? objectMetadataItem.fields.find(
|
||||
(fieldMetadataItem) =>
|
||||
fieldMetadataItem.id === editedFieldDefinition.fieldMetadataId,
|
||||
fieldMetadataItem.id === editedRecordField.fieldMetadataItemId,
|
||||
)
|
||||
: undefined;
|
||||
|
||||
|
||||
+4
-4
@@ -10,7 +10,7 @@ import { RecordBoardColumnNewRecordButton } from '@/object-record/record-board/r
|
||||
import { RecordBoardColumnContext } from '@/object-record/record-board/record-board-column/contexts/RecordBoardColumnContext';
|
||||
import { getNumberOfCardsPerColumnForSkeletonLoading } from '@/object-record/record-board/record-board-column/utils/getNumberOfCardsPerColumnForSkeletonLoading';
|
||||
import { isRecordBoardCompactModeActiveComponentState } from '@/object-record/record-board/states/isRecordBoardCompactModeActiveComponentState';
|
||||
import { recordBoardVisibleFieldDefinitionsComponentSelector } from '@/object-record/record-board/states/selectors/recordBoardVisibleFieldDefinitionsComponentSelector';
|
||||
import { visibleRecordFieldsComponentSelector } from '@/object-record/record-field/states/visibleRecordFieldsComponentSelector';
|
||||
import { isRecordIndexBoardColumnLoadingFamilyState } from '@/object-record/states/isRecordBoardColumnLoadingFamilyState';
|
||||
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
|
||||
|
||||
@@ -52,11 +52,11 @@ export const RecordBoardColumnCardsContainer = ({
|
||||
isRecordIndexBoardColumnLoadingFamilyState(columnId),
|
||||
);
|
||||
|
||||
const visibleFieldDefinitions = useRecoilComponentValue(
|
||||
recordBoardVisibleFieldDefinitionsComponentSelector,
|
||||
const visibleRecordFields = useRecoilComponentValue(
|
||||
visibleRecordFieldsComponentSelector,
|
||||
);
|
||||
|
||||
const numberOfFields = visibleFieldDefinitions.length;
|
||||
const numberOfFields = visibleRecordFields.length;
|
||||
|
||||
const isCompactModeActive = useRecoilComponentValue(
|
||||
isRecordBoardCompactModeActiveComponentState,
|
||||
|
||||
-12
@@ -1,12 +0,0 @@
|
||||
import { RecordBoardComponentInstanceContext } from '@/object-record/record-board/states/contexts/RecordBoardComponentInstanceContext';
|
||||
import { type RecordBoardFieldDefinition } from '@/object-record/record-board/types/RecordBoardFieldDefinition';
|
||||
import { type FieldMetadata } from '@/object-record/record-field/ui/types/FieldMetadata';
|
||||
import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState';
|
||||
|
||||
export const recordBoardFieldDefinitionsComponentState = createComponentState<
|
||||
RecordBoardFieldDefinition<FieldMetadata>[]
|
||||
>({
|
||||
key: 'recordBoardFieldDefinitionsComponentState',
|
||||
defaultValue: [],
|
||||
componentInstanceContext: RecordBoardComponentInstanceContext,
|
||||
});
|
||||
-17
@@ -1,17 +0,0 @@
|
||||
import { RecordBoardComponentInstanceContext } from '@/object-record/record-board/states/contexts/RecordBoardComponentInstanceContext';
|
||||
import { recordBoardFieldDefinitionsComponentState } from '@/object-record/record-board/states/recordBoardFieldDefinitionsComponentState';
|
||||
import { createComponentSelector } from '@/ui/utilities/state/component-state/utils/createComponentSelector';
|
||||
|
||||
export const recordBoardVisibleFieldDefinitionsComponentSelector =
|
||||
createComponentSelector({
|
||||
key: 'recordBoardVisibleFieldDefinitionsComponentSelector',
|
||||
get:
|
||||
({ instanceId }) =>
|
||||
({ get }) =>
|
||||
get(
|
||||
recordBoardFieldDefinitionsComponentState.atomFamily({ instanceId }),
|
||||
)
|
||||
.filter((field) => field.isVisible)
|
||||
.sort((a, b) => a.position - b.position),
|
||||
componentInstanceContext: RecordBoardComponentInstanceContext,
|
||||
});
|
||||
+15
@@ -4,6 +4,7 @@ import { CoreObjectNamePlural } from '@/object-metadata/types/CoreObjectNamePlur
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { ChipFieldDisplay } from '@/object-record/record-field/ui/meta-types/display/components/ChipFieldDisplay';
|
||||
import { RecordIndexContextProvider } from '@/object-record/record-index/contexts/RecordIndexContext';
|
||||
import { useRecordIndexFieldMetadataDerivedStates } from '@/object-record/record-index/hooks/useRecordIndexFieldMetadataDerivedStates';
|
||||
import { RecordTableComponentInstanceContext } from '@/object-record/record-table/states/context/RecordTableComponentInstanceContext';
|
||||
import { ComponentDecorator } from 'twenty-ui/testing';
|
||||
import { ChipGeneratorsDecorator } from '~/testing/decorators/ChipGeneratorsDecorator';
|
||||
@@ -22,6 +23,16 @@ const meta: Meta = {
|
||||
(item) => item.nameSingular === CoreObjectNameSingular.Company,
|
||||
)!;
|
||||
|
||||
const {
|
||||
fieldDefinitionByFieldMetadataItemId,
|
||||
fieldMetadataItemByFieldMetadataItemId,
|
||||
labelIdentifierFieldMetadataItem,
|
||||
recordFieldByFieldMetadataItemId,
|
||||
} = useRecordIndexFieldMetadataDerivedStates(
|
||||
companyObjectMetadataItem,
|
||||
instanceId,
|
||||
);
|
||||
|
||||
return (
|
||||
<RecordTableComponentInstanceContext.Provider
|
||||
value={{
|
||||
@@ -37,6 +48,10 @@ const meta: Meta = {
|
||||
objectNameSingular: CoreObjectNameSingular.Company,
|
||||
objectMetadataItem: companyObjectMetadataItem,
|
||||
recordIndexId: instanceId,
|
||||
fieldDefinitionByFieldMetadataItemId,
|
||||
fieldMetadataItemByFieldMetadataItemId,
|
||||
labelIdentifierFieldMetadataItem,
|
||||
recordFieldByFieldMetadataItemId,
|
||||
}}
|
||||
>
|
||||
<Story />
|
||||
|
||||
-15
@@ -3,9 +3,7 @@ import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
|
||||
import { isRecordBoardCompactModeActiveComponentState } from '@/object-record/record-board/states/isRecordBoardCompactModeActiveComponentState';
|
||||
import { recordBoardFieldDefinitionsComponentState } from '@/object-record/record-board/states/recordBoardFieldDefinitionsComponentState';
|
||||
import { recordBoardSelectedRecordIdsComponentSelector } from '@/object-record/record-board/states/selectors/recordBoardSelectedRecordIdsComponentSelector';
|
||||
import { recordIndexFieldDefinitionsState } from '@/object-record/record-index/states/recordIndexFieldDefinitionsState';
|
||||
import { recordIndexIsCompactModeActiveState } from '@/object-record/record-index/states/recordIndexIsCompactModeActiveState';
|
||||
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
|
||||
import { useSetRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentState';
|
||||
@@ -17,19 +15,10 @@ type RecordIndexBoardDataLoaderEffectProps = {
|
||||
export const RecordIndexBoardDataLoaderEffect = ({
|
||||
recordBoardId,
|
||||
}: RecordIndexBoardDataLoaderEffectProps) => {
|
||||
const recordIndexFieldDefinitions = useRecoilValue(
|
||||
recordIndexFieldDefinitionsState,
|
||||
);
|
||||
|
||||
const recordIndexIsCompactModeActive = useRecoilValue(
|
||||
recordIndexIsCompactModeActiveState,
|
||||
);
|
||||
|
||||
const setRecordBoardFieldDefinitions = useSetRecoilComponentState(
|
||||
recordBoardFieldDefinitionsComponentState,
|
||||
recordBoardId,
|
||||
);
|
||||
|
||||
const selectedRecordIds = useRecoilComponentValue(
|
||||
recordBoardSelectedRecordIdsComponentSelector,
|
||||
recordBoardId,
|
||||
@@ -44,10 +33,6 @@ export const RecordIndexBoardDataLoaderEffect = ({
|
||||
setIsCompactModeActive(recordIndexIsCompactModeActive);
|
||||
}, [recordIndexIsCompactModeActive, setIsCompactModeActive]);
|
||||
|
||||
useEffect(() => {
|
||||
setRecordBoardFieldDefinitions(recordIndexFieldDefinitions);
|
||||
}, [recordIndexFieldDefinitions, setRecordBoardFieldDefinitions]);
|
||||
|
||||
const setContextStoreTargetedRecords = useSetRecoilComponentState(
|
||||
contextStoreTargetedRecordsRuleComponentState,
|
||||
);
|
||||
|
||||
+15
@@ -11,6 +11,7 @@ import { RecordIndexContainerContextStoreNumberOfSelectedRecordsEffect } from '@
|
||||
import { RecordIndexLoadBaseOnContextStoreEffect } from '@/object-record/record-index/components/RecordIndexLoadBaseOnContextStoreEffect';
|
||||
import { RecordIndexPageHeader } from '@/object-record/record-index/components/RecordIndexPageHeader';
|
||||
import { useHandleIndexIdentifierClick } from '@/object-record/record-index/hooks/useHandleIndexIdentifierClick';
|
||||
import { useRecordIndexFieldMetadataDerivedStates } from '@/object-record/record-index/hooks/useRecordIndexFieldMetadataDerivedStates';
|
||||
import { useRecordIndexIdFromCurrentContextStore } from '@/object-record/record-index/hooks/useRecordIndexIdFromCurrentContextStore';
|
||||
import { PageBody } from '@/ui/layout/page/components/PageBody';
|
||||
import { RECORD_INDEX_DRAG_SELECT_BOUNDARY_CLASS } from '@/ui/utilities/drag-select/constants/RecordIndecDragSelectBoundaryClass';
|
||||
@@ -52,6 +53,16 @@ export const RecordIndexContainerGater = () => {
|
||||
|
||||
const hasObjectReadPermissions = objectPermissions.canReadObjectRecords;
|
||||
|
||||
const {
|
||||
fieldDefinitionByFieldMetadataItemId,
|
||||
fieldMetadataItemByFieldMetadataItemId,
|
||||
labelIdentifierFieldMetadataItem,
|
||||
recordFieldByFieldMetadataItemId,
|
||||
} = useRecordIndexFieldMetadataDerivedStates(
|
||||
objectMetadataItem,
|
||||
recordIndexId,
|
||||
);
|
||||
|
||||
if (!hasObjectReadPermissions) {
|
||||
return <NotFound />;
|
||||
}
|
||||
@@ -67,6 +78,10 @@ export const RecordIndexContainerGater = () => {
|
||||
objectMetadataItem,
|
||||
onIndexRecordsLoaded: handleIndexRecordsLoaded,
|
||||
indexIdentifierUrl,
|
||||
recordFieldByFieldMetadataItemId,
|
||||
labelIdentifierFieldMetadataItem,
|
||||
fieldMetadataItemByFieldMetadataItemId,
|
||||
fieldDefinitionByFieldMetadataItemId,
|
||||
}}
|
||||
>
|
||||
<ViewComponentInstanceContext.Provider
|
||||
|
||||
+11
@@ -1,4 +1,8 @@
|
||||
import { type FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
|
||||
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { type RecordField } from '@/object-record/record-field/types/RecordField';
|
||||
import { type FieldMetadata } from '@/object-record/record-field/ui/types/FieldMetadata';
|
||||
import { type ColumnDefinition } from '@/object-record/record-table/types/ColumnDefinition';
|
||||
import { type ObjectPermissions } from 'twenty-shared/types';
|
||||
import { createRequiredContext } from '~/utils/createRequiredContext';
|
||||
|
||||
@@ -13,6 +17,13 @@ export type RecordIndexContextValue = {
|
||||
ObjectPermissions & { objectMetadataId: string }
|
||||
>;
|
||||
recordIndexId: string;
|
||||
recordFieldByFieldMetadataItemId: Record<string, RecordField>;
|
||||
labelIdentifierFieldMetadataItem: FieldMetadataItem | undefined;
|
||||
fieldMetadataItemByFieldMetadataItemId: Record<string, FieldMetadataItem>;
|
||||
fieldDefinitionByFieldMetadataItemId: Record<
|
||||
string,
|
||||
ColumnDefinition<FieldMetadata>
|
||||
>;
|
||||
};
|
||||
|
||||
export const [RecordIndexContextProvider, useRecordIndexContextOrThrow] =
|
||||
|
||||
+18
-5
@@ -3,8 +3,9 @@ import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSi
|
||||
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { generateDepthOneRecordGqlFields } from '@/object-record/graphql/utils/generateDepthOneRecordGqlFields';
|
||||
import { generateDepthOneWithoutRelationsRecordGqlFields } from '@/object-record/graphql/utils/generateDepthOneWithoutRelationsRecordGqlFields';
|
||||
import { recordBoardVisibleFieldDefinitionsComponentSelector } from '@/object-record/record-board/states/selectors/recordBoardVisibleFieldDefinitionsComponentSelector';
|
||||
import { visibleRecordFieldsComponentSelector } from '@/object-record/record-field/states/visibleRecordFieldsComponentSelector';
|
||||
import { recordGroupFieldMetadataComponentState } from '@/object-record/record-group/states/recordGroupFieldMetadataComponentState';
|
||||
import { useRecordIndexContextOrThrow } from '@/object-record/record-index/contexts/RecordIndexContext';
|
||||
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
@@ -15,11 +16,14 @@ export const useRecordBoardRecordGqlFields = ({
|
||||
recordBoardId: string;
|
||||
objectMetadataItem: ObjectMetadataItem;
|
||||
}) => {
|
||||
const visibleFieldDefinitions = useRecoilComponentValue(
|
||||
recordBoardVisibleFieldDefinitionsComponentSelector,
|
||||
const visibleRecordFields = useRecoilComponentValue(
|
||||
visibleRecordFieldsComponentSelector,
|
||||
recordBoardId,
|
||||
);
|
||||
|
||||
const { fieldMetadataItemByFieldMetadataItemId } =
|
||||
useRecordIndexContextOrThrow();
|
||||
|
||||
const recordGroupFieldMetadata = useRecoilComponentValue(
|
||||
recordGroupFieldMetadataComponentState,
|
||||
recordBoardId,
|
||||
@@ -40,11 +44,20 @@ export const useRecordBoardRecordGqlFields = ({
|
||||
objectMetadataItem,
|
||||
});
|
||||
|
||||
const visibleFieldMetadataItems = visibleRecordFields
|
||||
.map(
|
||||
(recordField) =>
|
||||
fieldMetadataItemByFieldMetadataItemId[
|
||||
recordField.fieldMetadataItemId
|
||||
] ?? null,
|
||||
)
|
||||
.filter(isDefined);
|
||||
|
||||
const recordGqlFields: Record<string, any> = {
|
||||
...allDepthOneWithoutRelationsRecordGqlFields,
|
||||
...Object.fromEntries(
|
||||
visibleFieldDefinitions.map((visibleFieldDefinition) => [
|
||||
visibleFieldDefinition.metadata.fieldName,
|
||||
visibleFieldMetadataItems.map((visibleFieldMetadataItem) => [
|
||||
visibleFieldMetadataItem.name,
|
||||
true,
|
||||
]),
|
||||
),
|
||||
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
import { labelIdentifierFieldMetadataItemSelector } from '@/object-metadata/states/labelIdentifierFieldMetadataItemSelector';
|
||||
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { formatFieldMetadataItemAsColumnDefinition } from '@/object-metadata/utils/formatFieldMetadataItemAsColumnDefinition';
|
||||
import { currentRecordFieldsComponentState } from '@/object-record/record-field/states/currentRecordFieldsComponentState';
|
||||
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
export const useRecordIndexFieldMetadataDerivedStates = (
|
||||
objectMetadataItem: ObjectMetadataItem | undefined,
|
||||
recordIndexId?: string | undefined,
|
||||
) => {
|
||||
const fieldMetadataItems = objectMetadataItem?.fields ?? [];
|
||||
|
||||
const fieldMetadataItemByFieldMetadataItemId = Object.fromEntries(
|
||||
fieldMetadataItems.map((fieldMetadataItem) => [
|
||||
fieldMetadataItem.id,
|
||||
fieldMetadataItem,
|
||||
]),
|
||||
);
|
||||
|
||||
const currentRecordFields = useRecoilComponentValue(
|
||||
currentRecordFieldsComponentState,
|
||||
recordIndexId,
|
||||
);
|
||||
|
||||
const recordFieldByFieldMetadataItemId = Object.fromEntries(
|
||||
currentRecordFields.map((recordField) => [
|
||||
recordField.fieldMetadataItemId,
|
||||
recordField,
|
||||
]),
|
||||
);
|
||||
|
||||
const fieldDefinitionByFieldMetadataItemId = isDefined(objectMetadataItem)
|
||||
? Object.fromEntries(
|
||||
fieldMetadataItems.map((fieldMetadataItem) => [
|
||||
fieldMetadataItem.id,
|
||||
formatFieldMetadataItemAsColumnDefinition({
|
||||
field: fieldMetadataItem,
|
||||
objectMetadataItem,
|
||||
position:
|
||||
recordFieldByFieldMetadataItemId[fieldMetadataItem.id]
|
||||
?.position ?? 0,
|
||||
labelWidth:
|
||||
recordFieldByFieldMetadataItemId[fieldMetadataItem.id]?.size ?? 0,
|
||||
}),
|
||||
]),
|
||||
)
|
||||
: {};
|
||||
|
||||
const labelIdentifierFieldMetadataItem = useRecoilValue(
|
||||
labelIdentifierFieldMetadataItemSelector({
|
||||
objectMetadataItemId: objectMetadataItem?.id ?? '',
|
||||
}),
|
||||
);
|
||||
|
||||
return {
|
||||
fieldMetadataItemByFieldMetadataItemId,
|
||||
labelIdentifierFieldMetadataItem,
|
||||
fieldDefinitionByFieldMetadataItemId,
|
||||
recordFieldByFieldMetadataItemId,
|
||||
};
|
||||
};
|
||||
+2
-2
@@ -4,7 +4,7 @@ import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataI
|
||||
import { generateDepthOneRecordGqlFields } from '@/object-record/graphql/utils/generateDepthOneRecordGqlFields';
|
||||
import { generateDepthOneWithoutRelationsRecordGqlFields } from '@/object-record/graphql/utils/generateDepthOneWithoutRelationsRecordGqlFields';
|
||||
import { visibleRecordFieldsComponentSelector } from '@/object-record/record-field/states/visibleRecordFieldsComponentSelector';
|
||||
import { useRecordTableContextOrThrow } from '@/object-record/record-table/contexts/RecordTableContext';
|
||||
import { useRecordIndexContextOrThrow } from '@/object-record/record-index/contexts/RecordIndexContext';
|
||||
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
|
||||
import { FieldMetadataType } from 'twenty-shared/types';
|
||||
import { computeMorphRelationFieldName, isDefined } from 'twenty-shared/utils';
|
||||
@@ -19,7 +19,7 @@ export const useRecordTableRecordGqlFields = ({
|
||||
);
|
||||
|
||||
const { fieldMetadataItemByFieldMetadataItemId } =
|
||||
useRecordTableContextOrThrow();
|
||||
useRecordIndexContextOrThrow();
|
||||
|
||||
const { objectMetadataItem: noteTargetObjectMetadataItem } =
|
||||
useObjectMetadataItem({
|
||||
|
||||
-49
@@ -3,13 +3,9 @@ import { type ReactNode } from 'react';
|
||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||
import { RecordTableContextProvider as RecordTableContextInternalProvider } from '@/object-record/record-table/contexts/RecordTableContext';
|
||||
|
||||
import { labelIdentifierFieldMetadataItemSelector } from '@/object-metadata/states/labelIdentifierFieldMetadataItemSelector';
|
||||
import { formatFieldMetadataItemAsColumnDefinition } from '@/object-metadata/utils/formatFieldMetadataItemAsColumnDefinition';
|
||||
import { useObjectPermissionsForObject } from '@/object-record/hooks/useObjectPermissionsForObject';
|
||||
import { currentRecordFieldsComponentState } from '@/object-record/record-field/states/currentRecordFieldsComponentState';
|
||||
import { visibleRecordFieldsComponentSelector } from '@/object-record/record-field/states/visibleRecordFieldsComponentSelector';
|
||||
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
type RecordTableContextProviderProps = {
|
||||
viewBarId: string;
|
||||
@@ -37,47 +33,6 @@ export const RecordTableContextProvider = ({
|
||||
recordTableId,
|
||||
);
|
||||
|
||||
const currentRecordFields = useRecoilComponentValue(
|
||||
currentRecordFieldsComponentState,
|
||||
recordTableId,
|
||||
);
|
||||
|
||||
const fieldMetadataItems = objectMetadataItem.fields;
|
||||
|
||||
const fieldMetadataItemByFieldMetadataItemId = Object.fromEntries(
|
||||
fieldMetadataItems.map((fieldMetadataItem) => [
|
||||
fieldMetadataItem.id,
|
||||
fieldMetadataItem,
|
||||
]),
|
||||
);
|
||||
|
||||
const recordFieldByFieldMetadataItemId = Object.fromEntries(
|
||||
currentRecordFields.map((recordField) => [
|
||||
recordField.fieldMetadataItemId,
|
||||
recordField,
|
||||
]),
|
||||
);
|
||||
|
||||
const fieldDefinitionByFieldMetadataItemId = Object.fromEntries(
|
||||
fieldMetadataItems.map((fieldMetadataItem) => [
|
||||
fieldMetadataItem.id,
|
||||
formatFieldMetadataItemAsColumnDefinition({
|
||||
field: fieldMetadataItem,
|
||||
objectMetadataItem,
|
||||
position:
|
||||
recordFieldByFieldMetadataItemId[fieldMetadataItem.id]?.position ?? 0,
|
||||
labelWidth:
|
||||
recordFieldByFieldMetadataItemId[fieldMetadataItem.id]?.size ?? 0,
|
||||
}),
|
||||
]),
|
||||
);
|
||||
|
||||
const labelIdentifierFieldMetadataItem = useRecoilValue(
|
||||
labelIdentifierFieldMetadataItemSelector({
|
||||
objectMetadataItemId: objectMetadataItem.id,
|
||||
}),
|
||||
);
|
||||
|
||||
return (
|
||||
<RecordTableContextInternalProvider
|
||||
value={{
|
||||
@@ -86,11 +41,7 @@ export const RecordTableContextProvider = ({
|
||||
recordTableId,
|
||||
objectNameSingular,
|
||||
objectPermissions,
|
||||
recordFieldByFieldMetadataItemId,
|
||||
labelIdentifierFieldMetadataItem,
|
||||
visibleRecordFields,
|
||||
fieldMetadataItemByFieldMetadataItemId,
|
||||
fieldDefinitionByFieldMetadataItemId,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
|
||||
+4
-4
@@ -142,6 +142,10 @@ const meta: Meta = {
|
||||
// TODO: update performance mocks with new data, and merge with common mocks if possible
|
||||
objectMetadataItem: mockPerformance.objectMetadataItem as any,
|
||||
recordIndexId: 'recordIndexId',
|
||||
fieldDefinitionByFieldMetadataItemId,
|
||||
fieldMetadataItemByFieldMetadataItemId,
|
||||
labelIdentifierFieldMetadataItem,
|
||||
recordFieldByFieldMetadataItemId,
|
||||
}}
|
||||
>
|
||||
<RecordComponentInstanceContextsWrapper componentInstanceId="recordTableId">
|
||||
@@ -156,10 +160,6 @@ const meta: Meta = {
|
||||
objectPermissions: {
|
||||
objectMetadataId: mockPerformance.objectMetadataItem.id,
|
||||
},
|
||||
fieldDefinitionByFieldMetadataItemId,
|
||||
fieldMetadataItemByFieldMetadataItemId,
|
||||
labelIdentifierFieldMetadataItem,
|
||||
recordFieldByFieldMetadataItemId,
|
||||
visibleRecordFields,
|
||||
}}
|
||||
>
|
||||
|
||||
-10
@@ -1,8 +1,5 @@
|
||||
import { type FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
|
||||
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { type RecordField } from '@/object-record/record-field/types/RecordField';
|
||||
import { type FieldMetadata } from '@/object-record/record-field/ui/types/FieldMetadata';
|
||||
import { type ColumnDefinition } from '@/object-record/record-table/types/ColumnDefinition';
|
||||
import { type ObjectPermission } from '~/generated/graphql';
|
||||
import { createRequiredContext } from '~/utils/createRequiredContext';
|
||||
|
||||
@@ -13,13 +10,6 @@ type RecordTableContextValue = {
|
||||
objectMetadataItem: ObjectMetadataItem;
|
||||
objectPermissions: ObjectPermission;
|
||||
visibleRecordFields: RecordField[];
|
||||
recordFieldByFieldMetadataItemId: Record<string, RecordField>;
|
||||
labelIdentifierFieldMetadataItem: FieldMetadataItem | undefined;
|
||||
fieldMetadataItemByFieldMetadataItemId: Record<string, FieldMetadataItem>;
|
||||
fieldDefinitionByFieldMetadataItemId: Record<
|
||||
string,
|
||||
ColumnDefinition<FieldMetadata>
|
||||
>;
|
||||
};
|
||||
|
||||
export const [RecordTableContextProvider, useRecordTableContextOrThrow] =
|
||||
|
||||
+6
-5
@@ -22,13 +22,14 @@ export const RecordTableCellFieldContextGeneric = ({
|
||||
}: RecordTableCellFieldContextGenericProps) => {
|
||||
const { recordId, isRecordReadOnly } = useRecordTableRowContextOrThrow();
|
||||
|
||||
const { objectMetadataItem, objectPermissions } =
|
||||
useRecordTableContextOrThrow();
|
||||
|
||||
const {
|
||||
objectMetadataItem,
|
||||
objectPermissions,
|
||||
indexIdentifierUrl,
|
||||
objectPermissionsByObjectMetadataId,
|
||||
fieldDefinitionByFieldMetadataItemId,
|
||||
} = useRecordTableContextOrThrow();
|
||||
const { indexIdentifierUrl, objectPermissionsByObjectMetadataId } =
|
||||
useRecordIndexContextOrThrow();
|
||||
} = useRecordIndexContextOrThrow();
|
||||
|
||||
const fieldDefinition =
|
||||
fieldDefinitionByFieldMetadataItemId[recordField.fieldMetadataItemId];
|
||||
|
||||
+1
-1
@@ -28,7 +28,7 @@ export const RecordTableCellFieldContextLabelIdentifier = ({
|
||||
useRecordIndexContextOrThrow();
|
||||
const { recordId, isRecordReadOnly } = useRecordTableRowContextOrThrow();
|
||||
const { fieldDefinitionByFieldMetadataItemId } =
|
||||
useRecordTableContextOrThrow();
|
||||
useRecordIndexContextOrThrow();
|
||||
|
||||
const { recordField } = useContext(RecordTableCellContext);
|
||||
const { objectMetadataItem, recordTableId } = useRecordTableContextOrThrow();
|
||||
|
||||
+8
-11
@@ -1,8 +1,7 @@
|
||||
import { isLabelIdentifierField } from '@/object-metadata/utils/isLabelIdentifierField';
|
||||
import { type RecordField } from '@/object-record/record-field/types/RecordField';
|
||||
import { RecordFieldComponentInstanceContext } from '@/object-record/record-field/ui/states/contexts/RecordFieldComponentInstanceContext';
|
||||
import { useRecordIndexContextOrThrow } from '@/object-record/record-index/contexts/RecordIndexContext';
|
||||
import { RECORD_TABLE_CELL_INPUT_ID_PREFIX } from '@/object-record/record-table/constants/RecordTableCellInputIdPrefix';
|
||||
import { useRecordTableContextOrThrow } from '@/object-record/record-table/contexts/RecordTableContext';
|
||||
import { useRecordTableRowContextOrThrow } from '@/object-record/record-table/contexts/RecordTableRowContext';
|
||||
import { RecordTableCellFieldContextGeneric } from '@/object-record/record-table/record-table-cell/components/RecordTableCellFieldContextGeneric';
|
||||
import { RecordTableCellFieldContextLabelIdentifier } from '@/object-record/record-table/record-table-cell/components/RecordTableCellFieldContextLabelIdentifier';
|
||||
@@ -19,8 +18,11 @@ export const RecordTableCellFieldContextWrapper = ({
|
||||
children,
|
||||
}: RecordTableCellFieldContextWrapperProps) => {
|
||||
const { recordId } = useRecordTableRowContextOrThrow();
|
||||
const { objectMetadataItem, fieldMetadataItemByFieldMetadataItemId } =
|
||||
useRecordTableContextOrThrow();
|
||||
|
||||
const {
|
||||
fieldMetadataItemByFieldMetadataItemId,
|
||||
labelIdentifierFieldMetadataItem,
|
||||
} = useRecordIndexContextOrThrow();
|
||||
|
||||
const fieldMetadataItem =
|
||||
fieldMetadataItemByFieldMetadataItemId[recordField.fieldMetadataItemId];
|
||||
@@ -31,13 +33,8 @@ export const RecordTableCellFieldContextWrapper = ({
|
||||
prefix: RECORD_TABLE_CELL_INPUT_ID_PREFIX,
|
||||
});
|
||||
|
||||
const isLabelIdentifier = isLabelIdentifierField({
|
||||
fieldMetadataItem: {
|
||||
id: recordField.fieldMetadataItemId,
|
||||
name: fieldMetadataItem.name,
|
||||
},
|
||||
objectMetadataItem,
|
||||
});
|
||||
const isLabelIdentifier =
|
||||
labelIdentifierFieldMetadataItem?.id === recordField.fieldMetadataItemId;
|
||||
|
||||
return (
|
||||
<RecordFieldComponentInstanceContext.Provider value={{ instanceId }}>
|
||||
|
||||
+2
-1
@@ -6,6 +6,7 @@ import { isObjectMetadataReadOnly } from '@/object-record/read-only/utils/isObje
|
||||
import { useUpdateRecordField } from '@/object-record/record-field/hooks/useUpdateRecordField';
|
||||
|
||||
import { type RecordField } from '@/object-record/record-field/types/RecordField';
|
||||
import { useRecordIndexContextOrThrow } from '@/object-record/record-index/contexts/RecordIndexContext';
|
||||
import { useRecordTableContextOrThrow } from '@/object-record/record-table/contexts/RecordTableContext';
|
||||
import { useCreateNewIndexRecord } from '@/object-record/record-table/hooks/useCreateNewIndexRecord';
|
||||
import { RecordTableColumnHeadWithDropdown } from '@/object-record/record-table/record-table-header/components/RecordTableColumnHeadWithDropdown';
|
||||
@@ -207,7 +208,7 @@ export const RecordTableHeaderCell = ({
|
||||
|
||||
const isMobile = useIsMobile();
|
||||
|
||||
const { labelIdentifierFieldMetadataItem } = useRecordTableContextOrThrow();
|
||||
const { labelIdentifierFieldMetadataItem } = useRecordIndexContextOrThrow();
|
||||
|
||||
const isLabelIdentifier =
|
||||
recordField.fieldMetadataItemId === labelIdentifierFieldMetadataItem?.id;
|
||||
|
||||
+15
@@ -7,6 +7,7 @@ import { ContextStoreComponentInstanceContext } from '@/context-store/states/con
|
||||
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
||||
import { RecordComponentInstanceContextsWrapper } from '@/object-record/components/RecordComponentInstanceContextsWrapper';
|
||||
import { RecordIndexContextProvider } from '@/object-record/record-index/contexts/RecordIndexContext';
|
||||
import { useRecordIndexFieldMetadataDerivedStates } from '@/object-record/record-index/hooks/useRecordIndexFieldMetadataDerivedStates';
|
||||
import { RecordTableWithWrappers } from '@/object-record/record-table/components/RecordTableWithWrappers';
|
||||
import { SignInBackgroundMockContainerEffect } from '@/sign-in-background-mock/components/SignInBackgroundMockContainerEffect';
|
||||
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
|
||||
@@ -39,6 +40,16 @@ export const SignInBackgroundMockContainer = () => {
|
||||
(objectMetadataItem) => objectMetadataItem.id === objectMetadataItemId,
|
||||
);
|
||||
|
||||
const {
|
||||
fieldDefinitionByFieldMetadataItemId,
|
||||
fieldMetadataItemByFieldMetadataItemId,
|
||||
labelIdentifierFieldMetadataItem,
|
||||
recordFieldByFieldMetadataItemId,
|
||||
} = useRecordIndexFieldMetadataDerivedStates(
|
||||
objectMetadataItem,
|
||||
recordIndexId,
|
||||
);
|
||||
|
||||
return (
|
||||
<StyledContainer>
|
||||
<RecordIndexContextProvider
|
||||
@@ -50,6 +61,10 @@ export const SignInBackgroundMockContainer = () => {
|
||||
objectMetadataItem: objectMetadataItem ?? objectMetadataItems[0],
|
||||
onIndexRecordsLoaded: () => {},
|
||||
indexIdentifierUrl: () => '',
|
||||
fieldDefinitionByFieldMetadataItemId,
|
||||
fieldMetadataItemByFieldMetadataItemId,
|
||||
labelIdentifierFieldMetadataItem,
|
||||
recordFieldByFieldMetadataItemId,
|
||||
}}
|
||||
>
|
||||
<ViewComponentInstanceContext.Provider
|
||||
|
||||
+15
@@ -16,6 +16,7 @@ import { MAIN_CONTEXT_STORE_INSTANCE_ID } from '@/context-store/constants/MainCo
|
||||
import { RecordComponentInstanceContextsWrapper } from '@/object-record/components/RecordComponentInstanceContextsWrapper';
|
||||
import { currentRecordFieldsComponentState } from '@/object-record/record-field/states/currentRecordFieldsComponentState';
|
||||
import { type RecordField } from '@/object-record/record-field/types/RecordField';
|
||||
import { useRecordIndexFieldMetadataDerivedStates } from '@/object-record/record-index/hooks/useRecordIndexFieldMetadataDerivedStates';
|
||||
import { VIEW_BAR_FILTER_DROPDOWN_ID } from '@/views/constants/ViewBarFilterDropdownId';
|
||||
import { coreViewsState } from '@/views/states/coreViewState';
|
||||
import { within } from '@storybook/test';
|
||||
@@ -79,6 +80,16 @@ const meta: Meta<typeof ViewBarFilterDropdown> = {
|
||||
|
||||
setCurrentRecordFields(columns);
|
||||
|
||||
const {
|
||||
fieldDefinitionByFieldMetadataItemId,
|
||||
fieldMetadataItemByFieldMetadataItemId,
|
||||
labelIdentifierFieldMetadataItem,
|
||||
recordFieldByFieldMetadataItemId,
|
||||
} = useRecordIndexFieldMetadataDerivedStates(
|
||||
companyObjectMetadataItem,
|
||||
instanceId,
|
||||
);
|
||||
|
||||
return (
|
||||
<RecordIndexContextProvider
|
||||
value={{
|
||||
@@ -89,6 +100,10 @@ const meta: Meta<typeof ViewBarFilterDropdown> = {
|
||||
objectNameSingular: CoreObjectNameSingular.Company,
|
||||
objectMetadataItem: companyObjectMetadataItem,
|
||||
recordIndexId: instanceId,
|
||||
labelIdentifierFieldMetadataItem,
|
||||
recordFieldByFieldMetadataItemId,
|
||||
fieldDefinitionByFieldMetadataItemId,
|
||||
fieldMetadataItemByFieldMetadataItemId,
|
||||
}}
|
||||
>
|
||||
<RecordComponentInstanceContextsWrapper
|
||||
|
||||
+16
-1
@@ -11,6 +11,7 @@ import { type RecordFilterGroup } from '@/object-record/record-filter-group/type
|
||||
import { RecordFiltersComponentInstanceContext } from '@/object-record/record-filter/states/context/RecordFiltersComponentInstanceContext';
|
||||
import { type RecordFilter } from '@/object-record/record-filter/types/RecordFilter';
|
||||
import { RecordIndexContextProvider } from '@/object-record/record-index/contexts/RecordIndexContext';
|
||||
import { useRecordIndexFieldMetadataDerivedStates } from '@/object-record/record-index/hooks/useRecordIndexFieldMetadataDerivedStates';
|
||||
import { InputLabel } from '@/ui/input/components/InputLabel';
|
||||
import { WorkflowStepBody } from '@/workflow/workflow-steps/components/WorkflowStepBody';
|
||||
import { WorkflowFindRecordsFilters } from '@/workflow/workflow-steps/workflow-actions/find-records-action/components/WorkflowFindRecordsFilters';
|
||||
@@ -70,6 +71,7 @@ export const WorkflowEditActionFindRecords = ({
|
||||
filter: action.settings.input.filter as FindRecordsActionFilter,
|
||||
});
|
||||
const isFormDisabled = actionOptions.readonly;
|
||||
const instanceId = `workflow-edit-action-record-find-records-${action.id}-${formData.objectNameSingular}`;
|
||||
|
||||
const selectedObjectMetadataItem = activeNonSystemObjectMetadataItems.find(
|
||||
(item) => item.nameSingular === formData.objectNameSingular,
|
||||
@@ -80,6 +82,16 @@ export const WorkflowEditActionFindRecords = ({
|
||||
|
||||
const { objectPermissionsByObjectMetadataId } = useObjectPermissions();
|
||||
|
||||
const {
|
||||
fieldDefinitionByFieldMetadataItemId,
|
||||
fieldMetadataItemByFieldMetadataItemId,
|
||||
labelIdentifierFieldMetadataItem,
|
||||
recordFieldByFieldMetadataItemId,
|
||||
} = useRecordIndexFieldMetadataDerivedStates(
|
||||
selectedObjectMetadataItem,
|
||||
instanceId,
|
||||
);
|
||||
|
||||
const saveAction = useDebouncedCallback(
|
||||
async (formData: FindRecordsFormData) => {
|
||||
if (actionOptions.readonly === true) {
|
||||
@@ -118,7 +130,6 @@ export const WorkflowEditActionFindRecords = ({
|
||||
action,
|
||||
defaultTitle: 'Search Records',
|
||||
});
|
||||
const instanceId = `workflow-edit-action-record-find-records-${action.id}-${formData.objectNameSingular}`;
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -174,6 +185,10 @@ export const WorkflowEditActionFindRecords = ({
|
||||
objectMetadataItem: selectedObjectMetadataItem,
|
||||
recordIndexId: instanceId,
|
||||
objectPermissionsByObjectMetadataId,
|
||||
labelIdentifierFieldMetadataItem,
|
||||
recordFieldByFieldMetadataItemId,
|
||||
fieldDefinitionByFieldMetadataItemId,
|
||||
fieldMetadataItemByFieldMetadataItemId,
|
||||
}}
|
||||
>
|
||||
<RecordFilterGroupsComponentInstanceContext.Provider
|
||||
|
||||
@@ -143,6 +143,10 @@ const InternalTableContextProviders = ({
|
||||
objectMetadataItem: objectMetadataItem,
|
||||
objectPermissionsByObjectMetadataId,
|
||||
recordIndexId: 'record-index',
|
||||
labelIdentifierFieldMetadataItem,
|
||||
recordFieldByFieldMetadataItemId,
|
||||
fieldDefinitionByFieldMetadataItemId,
|
||||
fieldMetadataItemByFieldMetadataItemId,
|
||||
}}
|
||||
>
|
||||
<RecordTableContextProvider
|
||||
@@ -155,11 +159,7 @@ const InternalTableContextProviders = ({
|
||||
objectPermissionsByObjectMetadataId,
|
||||
objectMetadataId: objectMetadataItem.id,
|
||||
}),
|
||||
labelIdentifierFieldMetadataItem,
|
||||
recordFieldByFieldMetadataItemId,
|
||||
visibleRecordFields,
|
||||
fieldDefinitionByFieldMetadataItemId,
|
||||
fieldMetadataItemByFieldMetadataItemId,
|
||||
}}
|
||||
>
|
||||
<RecordTableBodyContextProvider
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { RecordIndexContextProvider } from '@/object-record/record-index/contexts/RecordIndexContext';
|
||||
import { useRecordIndexFieldMetadataDerivedStates } from '@/object-record/record-index/hooks/useRecordIndexFieldMetadataDerivedStates';
|
||||
import { type PropsWithChildren } from 'react';
|
||||
|
||||
type JestRecordIndexContextProviderWrapperProps = {
|
||||
objectMetadataItem: ObjectMetadataItem;
|
||||
} & PropsWithChildren;
|
||||
|
||||
export const JestRecordIndexContextProviderWrapper = ({
|
||||
objectMetadataItem,
|
||||
children,
|
||||
}: JestRecordIndexContextProviderWrapperProps) => {
|
||||
const {
|
||||
fieldDefinitionByFieldMetadataItemId,
|
||||
fieldMetadataItemByFieldMetadataItemId,
|
||||
labelIdentifierFieldMetadataItem,
|
||||
recordFieldByFieldMetadataItemId,
|
||||
} = useRecordIndexFieldMetadataDerivedStates(objectMetadataItem);
|
||||
|
||||
return (
|
||||
<RecordIndexContextProvider
|
||||
value={{
|
||||
objectPermissionsByObjectMetadataId: {},
|
||||
indexIdentifierUrl: () => 'indexIdentifierUrl',
|
||||
onIndexRecordsLoaded: () => {},
|
||||
objectNamePlural: objectMetadataItem.namePlural,
|
||||
objectNameSingular: objectMetadataItem.nameSingular,
|
||||
objectMetadataItem: objectMetadataItem,
|
||||
recordIndexId: 'recordIndexId',
|
||||
labelIdentifierFieldMetadataItem,
|
||||
recordFieldByFieldMetadataItemId,
|
||||
fieldDefinitionByFieldMetadataItemId,
|
||||
fieldMetadataItemByFieldMetadataItemId,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</RecordIndexContextProvider>
|
||||
);
|
||||
};
|
||||
+25
-40
@@ -1,7 +1,5 @@
|
||||
import { ActionMenuComponentInstanceContext } from '@/action-menu/states/contexts/ActionMenuComponentInstanceContext';
|
||||
import { ContextStoreComponentInstanceContext } from '@/context-store/states/contexts/ContextStoreComponentInstanceContext';
|
||||
import { RecordComponentInstanceContextsWrapper } from '@/object-record/components/RecordComponentInstanceContextsWrapper';
|
||||
import { RecordIndexContextProvider } from '@/object-record/record-index/contexts/RecordIndexContext';
|
||||
import { type MockedResponse } from '@apollo/client/testing';
|
||||
import { type ReactNode } from 'react';
|
||||
import { type MutableSnapshot } from 'recoil';
|
||||
@@ -10,6 +8,7 @@ import {
|
||||
JestContextStoreSetter,
|
||||
type JestContextStoreSetterMocks,
|
||||
} from '~/testing/jest/JestContextStoreSetter';
|
||||
import { JestRecordIndexContextProviderWrapper } from '~/testing/jest/JestRecordIndexContextProviderWrapper';
|
||||
import { getJestMetadataAndApolloMocksWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksWrapper';
|
||||
import { getMockObjectMetadataItemOrThrow } from '~/testing/utils/getMockObjectMetadataItemOrThrow';
|
||||
|
||||
@@ -49,48 +48,34 @@ export const getJestMetadataAndApolloMocksAndActionMenuWrapper = ({
|
||||
|
||||
return ({ children }: { children: ReactNode }) => (
|
||||
<Wrapper>
|
||||
<RecordComponentInstanceContextsWrapper
|
||||
componentInstanceId={componentInstanceId}
|
||||
<ContextStoreComponentInstanceContext.Provider
|
||||
value={{ instanceId: componentInstanceId }}
|
||||
>
|
||||
<ContextStoreComponentInstanceContext.Provider
|
||||
value={{ instanceId: componentInstanceId }}
|
||||
<ActionMenuComponentInstanceContext.Provider
|
||||
value={{
|
||||
instanceId: componentInstanceId,
|
||||
}}
|
||||
>
|
||||
<ActionMenuComponentInstanceContext.Provider
|
||||
value={{
|
||||
instanceId: componentInstanceId,
|
||||
}}
|
||||
<JestRecordIndexContextProviderWrapper
|
||||
objectMetadataItem={mockObjectMetadataItem}
|
||||
>
|
||||
<RecordIndexContextProvider
|
||||
value={{
|
||||
objectPermissionsByObjectMetadataId: {},
|
||||
indexIdentifierUrl: () => 'indexIdentifierUrl',
|
||||
onIndexRecordsLoaded: () => {},
|
||||
objectNamePlural: mockObjectMetadataItem.namePlural,
|
||||
objectNameSingular: mockObjectMetadataItem.nameSingular,
|
||||
objectMetadataItem: mockObjectMetadataItem,
|
||||
recordIndexId: 'recordIndexId',
|
||||
}}
|
||||
<JestContextStoreSetter
|
||||
contextStoreCurrentViewId={contextStoreCurrentViewId}
|
||||
contextStoreFilters={contextStoreFilters}
|
||||
contextStoreTargetedRecordsRule={contextStoreTargetedRecordsRule}
|
||||
contextStoreNumberOfSelectedRecords={
|
||||
contextStoreNumberOfSelectedRecords
|
||||
}
|
||||
contextStoreCurrentObjectMetadataNameSingular={
|
||||
contextStoreCurrentObjectMetadataNameSingular
|
||||
}
|
||||
contextStoreCurrentViewType={contextStoreCurrentViewType}
|
||||
>
|
||||
<JestContextStoreSetter
|
||||
contextStoreCurrentViewId={contextStoreCurrentViewId}
|
||||
contextStoreFilters={contextStoreFilters}
|
||||
contextStoreTargetedRecordsRule={
|
||||
contextStoreTargetedRecordsRule
|
||||
}
|
||||
contextStoreNumberOfSelectedRecords={
|
||||
contextStoreNumberOfSelectedRecords
|
||||
}
|
||||
contextStoreCurrentObjectMetadataNameSingular={
|
||||
contextStoreCurrentObjectMetadataNameSingular
|
||||
}
|
||||
contextStoreCurrentViewType={contextStoreCurrentViewType}
|
||||
>
|
||||
{children}
|
||||
</JestContextStoreSetter>
|
||||
</RecordIndexContextProvider>
|
||||
</ActionMenuComponentInstanceContext.Provider>
|
||||
</ContextStoreComponentInstanceContext.Provider>
|
||||
</RecordComponentInstanceContextsWrapper>
|
||||
{children}
|
||||
</JestContextStoreSetter>
|
||||
</JestRecordIndexContextProviderWrapper>
|
||||
</ActionMenuComponentInstanceContext.Provider>
|
||||
</ContextStoreComponentInstanceContext.Provider>
|
||||
</Wrapper>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user