fix: fix several field bugs (#5339)

After discussing with @charlesBochet, several fixes are needed on
fields:
- [x] Disable Boolean field `defaultValue` edition for now (On
`defaultValue` update, newly created records are not taking the updated
`defaultValue` into account. Setting the `defaultValue` on creation is
fine.)
- [x] Disable Phone field creation for now
- [x] For the Person object, display the "Phone" field as a field of
type Phone (right now its type is Text; later we'll migrate it to a
proper Phone field).
- [x] Fix RawJson field display (displaying `[object Object]` in Record
Table cells).
- [x] In Settings/Data Model, on Relation field creation/edition,
"Object destination" select is not working properly if an object was not
manually selected (displays Companies by default but creates a relation
to another random object than Companies).
This commit is contained in:
Thaïs
2024-05-09 01:56:15 +02:00
committed by GitHub
parent 005045c596
commit 7728c09dba
23 changed files with 332 additions and 167 deletions

View File

@ -6,6 +6,7 @@ import { z } from 'zod';
import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
import { Select } from '@/ui/input/components/Select';
import { CardContent } from '@/ui/layout/card/components/CardContent';
import { isDefined } from '~/utils/isDefined';
// TODO: rename to SettingsDataModelFieldBooleanForm and move to settings/data-model/fields/forms/components
@ -41,6 +42,7 @@ export const SettingsDataModelFieldBooleanForm = ({
}: SettingsDataModelFieldBooleanFormProps) => {
const { control } = useFormContext<SettingsDataModelFieldBooleanFormValues>();
const isEditMode = isDefined(fieldMetadataItem?.defaultValue);
const initialValue = fieldMetadataItem?.defaultValue ?? true;
return (
@ -54,6 +56,9 @@ export const SettingsDataModelFieldBooleanForm = ({
<Select
className={className}
fullWidth
// TODO: temporary fix - disabling edition because after editing the defaultValue,
// newly created records are not taking into account the updated defaultValue properly.
disabled={isEditMode}
dropdownId="object-field-default-value-select"
value={value}
onChange={onChange}

View File

@ -1,18 +1,16 @@
import { useMemo } from 'react';
import { Controller, useFormContext } from 'react-hook-form';
import styled from '@emotion/styled';
import { useIcons } from 'twenty-ui';
import { z } from 'zod';
import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems';
import { useGetRelationMetadata } from '@/object-metadata/hooks/useGetRelationMetadata';
import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
import { isObjectMetadataAvailableForRelation } from '@/object-metadata/utils/isObjectMetadataAvailableForRelation';
import { fieldMetadataItemSchema } from '@/object-metadata/validation-schemas/fieldMetadataItemSchema';
import { useRelationSettingsFormInitialValues } from '@/settings/data-model/fields/forms/hooks/useRelationSettingsFormInitialValues';
import { IconPicker } from '@/ui/input/components/IconPicker';
import { Select } from '@/ui/input/components/Select';
import { TextInput } from '@/ui/input/components/TextInput';
import { RelationMetadataType } from '~/generated-metadata/graphql';
import { RELATION_TYPES } from '../constants/RelationTypes';
import { RelationType } from '../types/RelationType';
@ -32,7 +30,7 @@ export const settingsDataModelFieldRelationFormSchema = z.object({
}),
});
type SettingsDataModelFieldRelationFormValues = z.infer<
export type SettingsDataModelFieldRelationFormValues = z.infer<
typeof settingsDataModelFieldRelationFormSchema
>;
@ -79,30 +77,23 @@ const RELATION_TYPE_OPTIONS = Object.entries(RELATION_TYPES)
export const SettingsDataModelFieldRelationForm = ({
fieldMetadataItem,
}: SettingsDataModelFieldRelationFormProps) => {
const { control } =
const { control, watch: watchFormValue } =
useFormContext<SettingsDataModelFieldRelationFormValues>();
const { getIcon } = useIcons();
const { objectMetadataItems } = useFilteredObjectMetadataItems();
const { objectMetadataItems, findObjectMetadataItemById } =
useFilteredObjectMetadataItems();
const getRelationMetadata = useGetRelationMetadata();
const {
relationFieldMetadataItem,
relationType,
relationObjectMetadataItem,
} =
useMemo(
() =>
fieldMetadataItem ? getRelationMetadata({ fieldMetadataItem }) : null,
[fieldMetadataItem, getRelationMetadata],
) ?? {};
disableFieldEdition,
disableRelationEdition,
initialRelationFieldMetadataItem,
initialRelationObjectMetadataItem,
initialRelationType,
} = useRelationSettingsFormInitialValues({ fieldMetadataItem });
const disableFieldEdition =
relationFieldMetadataItem && !relationFieldMetadataItem.isCustom;
const disableRelationEdition = !!relationFieldMetadataItem;
const selectedObjectMetadataItem =
relationObjectMetadataItem ?? objectMetadataItems[0];
const selectedObjectMetadataItem = findObjectMetadataItemById(
watchFormValue('relation.objectMetadataId'),
);
return (
<StyledContainer>
@ -110,7 +101,7 @@ export const SettingsDataModelFieldRelationForm = ({
<Controller
name="relation.type"
control={control}
defaultValue={relationType ?? RelationMetadataType.OneToMany}
defaultValue={initialRelationType}
render={({ field: { onChange, value } }) => (
<Select
label="Relation type"
@ -126,7 +117,7 @@ export const SettingsDataModelFieldRelationForm = ({
<Controller
name="relation.objectMetadataId"
control={control}
defaultValue={selectedObjectMetadataItem?.id}
defaultValue={initialRelationObjectMetadataItem.id}
render={({ field: { onChange, value } }) => (
<Select
label="Object destination"
@ -153,11 +144,7 @@ export const SettingsDataModelFieldRelationForm = ({
<Controller
name="relation.field.icon"
control={control}
defaultValue={
relationFieldMetadataItem?.icon ??
relationObjectMetadataItem?.icon ??
'IconUsers'
}
defaultValue={initialRelationFieldMetadataItem.icon}
render={({ field: { onChange, value } }) => (
<IconPicker
disabled={disableFieldEdition}
@ -171,7 +158,7 @@ export const SettingsDataModelFieldRelationForm = ({
<Controller
name="relation.field.label"
control={control}
defaultValue={relationFieldMetadataItem?.label}
defaultValue={initialRelationFieldMetadataItem.label}
render={({ field: { onChange, value } }) => (
<TextInput
disabled={disableFieldEdition}

View File

@ -0,0 +1,109 @@
import { useFormContext } from 'react-hook-form';
import styled from '@emotion/styled';
import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems';
import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
import { SettingsDataModelPreviewFormCard } from '@/settings/data-model/components/SettingsDataModelPreviewFormCard';
import {
SettingsDataModelFieldRelationForm,
SettingsDataModelFieldRelationFormValues,
} from '@/settings/data-model/components/SettingsObjectFieldRelationForm';
import { RELATION_TYPES } from '@/settings/data-model/constants/RelationTypes';
import { useRelationSettingsFormInitialValues } from '@/settings/data-model/fields/forms/hooks/useRelationSettingsFormInitialValues';
import {
SettingsDataModelFieldPreviewCard,
SettingsDataModelFieldPreviewCardProps,
} from '@/settings/data-model/fields/preview/components/SettingsDataModelFieldPreviewCard';
import { FieldMetadataType } from '~/generated-metadata/graphql';
type SettingsDataModelFieldRelationSettingsFormCardProps = {
fieldMetadataItem: Pick<FieldMetadataItem, 'icon' | 'label' | 'type'> &
Partial<Omit<FieldMetadataItem, 'icon' | 'label' | 'type'>>;
relationFieldMetadataItem?: FieldMetadataItem;
} & Pick<SettingsDataModelFieldPreviewCardProps, 'objectMetadataItem'>;
const StyledFieldPreviewCard = styled(SettingsDataModelFieldPreviewCard)`
display: grid;
flex: 1 1 100%;
`;
const StyledPreviewContent = styled.div`
display: flex;
gap: 6px;
`;
const StyledRelationImage = styled.img<{ flip?: boolean }>`
transform: ${({ flip }) => (flip ? 'scaleX(-1)' : 'none')};
width: 54px;
`;
export const SettingsDataModelFieldRelationSettingsFormCard = ({
fieldMetadataItem,
objectMetadataItem,
}: SettingsDataModelFieldRelationSettingsFormCardProps) => {
const { watch: watchFormValue } =
useFormContext<SettingsDataModelFieldRelationFormValues>();
const { findObjectMetadataItemById } = useFilteredObjectMetadataItems();
const {
initialRelationObjectMetadataItem,
initialRelationType,
initialRelationFieldMetadataItem,
} = useRelationSettingsFormInitialValues({ fieldMetadataItem });
const relationObjectMetadataId = watchFormValue(
'relation.objectMetadataId',
initialRelationObjectMetadataItem?.id,
);
const relationObjectMetadataItem = findObjectMetadataItemById(
relationObjectMetadataId,
);
if (!relationObjectMetadataItem) return null;
const relationType = watchFormValue('relation.type', initialRelationType);
const relationTypeConfig = RELATION_TYPES[relationType];
return (
<SettingsDataModelPreviewFormCard
preview={
<StyledPreviewContent>
<StyledFieldPreviewCard
fieldMetadataItem={fieldMetadataItem}
shrink
objectMetadataItem={objectMetadataItem}
relationObjectMetadataItem={relationObjectMetadataItem}
/>
<StyledRelationImage
src={relationTypeConfig.imageSrc}
flip={relationTypeConfig.isImageFlipped}
alt={relationTypeConfig.label}
/>
<StyledFieldPreviewCard
fieldMetadataItem={{
...initialRelationFieldMetadataItem,
icon: watchFormValue(
'relation.field.icon',
initialRelationFieldMetadataItem.icon,
),
label:
watchFormValue(
'relation.field.label',
initialRelationFieldMetadataItem.label,
) || 'Field name',
type: FieldMetadataType.Relation,
}}
shrink
objectMetadataItem={relationObjectMetadataItem}
relationObjectMetadataItem={objectMetadataItem}
/>
</StyledPreviewContent>
}
form={
<SettingsDataModelFieldRelationForm
fieldMetadataItem={fieldMetadataItem}
/>
}
/>
);
};

View File

@ -3,7 +3,6 @@ import styled from '@emotion/styled';
import omit from 'lodash.omit';
import { z } from 'zod';
import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems';
import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
import {
SettingsDataModelFieldBooleanForm,
@ -14,16 +13,13 @@ import {
SettingsDataModelFieldCurrencyForm,
settingsDataModelFieldCurrencyFormSchema,
} from '@/settings/data-model/components/SettingsObjectFieldCurrencyForm';
import {
SettingsDataModelFieldRelationForm,
settingsDataModelFieldRelationFormSchema,
} from '@/settings/data-model/components/SettingsObjectFieldRelationForm';
import { settingsDataModelFieldRelationFormSchema } from '@/settings/data-model/components/SettingsObjectFieldRelationForm';
import {
SettingsDataModelFieldSelectForm,
settingsDataModelFieldSelectFormSchema,
} from '@/settings/data-model/components/SettingsObjectFieldSelectForm';
import { RELATION_TYPES } from '@/settings/data-model/constants/RelationTypes';
import { SETTINGS_FIELD_TYPE_CONFIGS } from '@/settings/data-model/constants/SettingsFieldTypeConfigs';
import { SettingsDataModelFieldRelationSettingsFormCard } from '@/settings/data-model/fields/forms/components/SettingsDataModelFieldRelationSettingsFormCard';
import {
SettingsDataModelFieldPreviewCard,
SettingsDataModelFieldPreviewCardProps,
@ -81,7 +77,6 @@ type SettingsDataModelFieldSettingsFormCardProps = {
disableCurrencyForm?: boolean;
fieldMetadataItem: Pick<FieldMetadataItem, 'icon' | 'label' | 'type'> &
Partial<Omit<FieldMetadataItem, 'icon' | 'label' | 'type'>>;
relationFieldMetadataItem?: FieldMetadataItem;
} & Pick<SettingsDataModelFieldPreviewCardProps, 'objectMetadataItem'>;
const StyledFieldPreviewCard = styled(SettingsDataModelFieldPreviewCard)`
@ -94,11 +89,6 @@ const StyledPreviewContent = styled.div`
gap: 6px;
`;
const StyledRelationImage = styled.img<{ flip?: boolean }>`
transform: ${({ flip }) => (flip ? 'scaleX(-1)' : 'none')};
width: 54px;
`;
const previewableTypes = [
FieldMetadataType.Boolean,
FieldMetadataType.Currency,
@ -121,23 +111,20 @@ export const SettingsDataModelFieldSettingsFormCard = ({
disableCurrencyForm,
fieldMetadataItem,
objectMetadataItem,
relationFieldMetadataItem,
}: SettingsDataModelFieldSettingsFormCardProps) => {
const { watch: watchFormValue } =
useFormContext<SettingsDataModelFieldSettingsFormValues>();
const { findObjectMetadataItemById } = useFilteredObjectMetadataItems();
if (!previewableTypes.includes(fieldMetadataItem.type)) return null;
const relationObjectMetadataId = watchFormValue('relation.objectMetadataId');
const relationObjectMetadataItem = relationObjectMetadataId
? findObjectMetadataItemById(relationObjectMetadataId)
: undefined;
const relationType = watchFormValue('relation.type');
const relationTypeConfig = relationType
? RELATION_TYPES[relationType]
: undefined;
if (fieldMetadataItem.type === FieldMetadataType.Relation) {
return (
<SettingsDataModelFieldRelationSettingsFormCard
fieldMetadataItem={fieldMetadataItem}
objectMetadataItem={objectMetadataItem}
/>
);
}
return (
<SettingsDataModelPreviewFormCard
@ -145,34 +132,9 @@ export const SettingsDataModelFieldSettingsFormCard = ({
<StyledPreviewContent>
<StyledFieldPreviewCard
fieldMetadataItem={fieldMetadataItem}
shrink={fieldMetadataItem.type === FieldMetadataType.Relation}
objectMetadataItem={objectMetadataItem}
relationObjectMetadataItem={relationObjectMetadataItem}
selectOptions={watchFormValue('options')}
/>
{fieldMetadataItem.type === FieldMetadataType.Relation &&
!!relationObjectMetadataItem &&
!!relationTypeConfig && (
<>
<StyledRelationImage
src={relationTypeConfig.imageSrc}
flip={relationTypeConfig.isImageFlipped}
alt={relationTypeConfig.label}
/>
<StyledFieldPreviewCard
fieldMetadataItem={{
...relationFieldMetadataItem,
icon: watchFormValue('relation.field.icon'),
label:
watchFormValue('relation.field.label') || 'Field name',
type: FieldMetadataType.Relation,
}}
shrink
objectMetadataItem={relationObjectMetadataItem}
relationObjectMetadataItem={objectMetadataItem}
/>
</>
)}
</StyledPreviewContent>
}
form={
@ -185,10 +147,6 @@ export const SettingsDataModelFieldSettingsFormCard = ({
disabled={disableCurrencyForm}
fieldMetadataItem={fieldMetadataItem}
/>
) : fieldMetadataItem.type === FieldMetadataType.Relation ? (
<SettingsDataModelFieldRelationForm
fieldMetadataItem={fieldMetadataItem}
/>
) : fieldMetadataItem.type === FieldMetadataType.Select ||
fieldMetadataItem.type === FieldMetadataType.MultiSelect ? (
<SettingsDataModelFieldSelectForm

View File

@ -7,10 +7,7 @@ import { MemoryRouterDecorator } from '~/testing/decorators/MemoryRouterDecorato
import { ObjectMetadataItemsDecorator } from '~/testing/decorators/ObjectMetadataItemsDecorator';
import { SnackBarDecorator } from '~/testing/decorators/SnackBarDecorator';
import { graphqlMocks } from '~/testing/graphqlMocks';
import {
mockedCompanyObjectMetadataItem,
mockedPersonObjectMetadataItem,
} from '~/testing/mock-data/metadata';
import { mockedCompanyObjectMetadataItem } from '~/testing/mock-data/metadata';
import { SettingsDataModelFieldSettingsFormCard } from '../SettingsDataModelFieldSettingsFormCard';
@ -49,9 +46,6 @@ export const WithRelationForm: Story = {
fieldMetadataItem: mockedCompanyObjectMetadataItem.fields.find(
({ name }) => name === 'people',
),
relationFieldMetadataItem: mockedPersonObjectMetadataItem.fields.find(
({ name }) => name === 'company',
)!,
},
};

View File

@ -0,0 +1,55 @@
import { useMemo } from 'react';
import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems';
import { useGetRelationMetadata } from '@/object-metadata/hooks/useGetRelationMetadata';
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
import { isObjectMetadataAvailableForRelation } from '@/object-metadata/utils/isObjectMetadataAvailableForRelation';
import { RelationMetadataType } from '~/generated-metadata/graphql';
export const useRelationSettingsFormInitialValues = ({
fieldMetadataItem,
}: {
fieldMetadataItem?: Pick<
FieldMetadataItem,
'fromRelationMetadata' | 'toRelationMetadata' | 'type'
>;
}) => {
const { objectMetadataItems } = useFilteredObjectMetadataItems();
const getRelationMetadata = useGetRelationMetadata();
const {
relationFieldMetadataItem,
relationObjectMetadataItem: relationObjectMetadataItemFromFieldMetadata,
relationType: relationTypeFromFieldMetadata,
} = useMemo(
() =>
fieldMetadataItem ? getRelationMetadata({ fieldMetadataItem }) : null,
[fieldMetadataItem, getRelationMetadata],
) ?? {};
const initialRelationObjectMetadataItem = useMemo(
() =>
relationObjectMetadataItemFromFieldMetadata ??
objectMetadataItems.find(
({ nameSingular }) => nameSingular === CoreObjectNameSingular.Person,
) ??
objectMetadataItems.filter(isObjectMetadataAvailableForRelation)[0],
[objectMetadataItems, relationObjectMetadataItemFromFieldMetadata],
);
const initialRelationType =
relationTypeFromFieldMetadata ?? RelationMetadataType.OneToMany;
return {
disableFieldEdition:
relationFieldMetadataItem && !relationFieldMetadataItem.isCustom,
disableRelationEdition: !!relationFieldMetadataItem,
initialRelationFieldMetadataItem: relationFieldMetadataItem ?? {
icon: initialRelationObjectMetadataItem.icon ?? 'IconUsers',
label: '',
},
initialRelationObjectMetadataItem,
initialRelationType,
};
};