mirror of
https://github.com/twentyhq/twenty.git
synced 2024-09-11 23:37:21 +03:00
Fix display empty value if boolean instead of false on show page (#4468)
* default value boolean fixed * fixed creation, fixed updating a value to false * fixed default value for default value if boolean * fixed tests --------- Co-authored-by: Félix Malfait <felix.malfait@gmail.com>
This commit is contained in:
parent
1d351a29b8
commit
da8f1b0a66
@ -17,7 +17,7 @@ export const useFieldMetadataItem = () => {
|
||||
const { deleteOneFieldMetadataItem } = useDeleteOneFieldMetadataItem();
|
||||
|
||||
const createMetadataField = (
|
||||
input: Pick<Field, 'label' | 'icon' | 'description'> & {
|
||||
input: Pick<Field, 'label' | 'icon' | 'description' | 'defaultValue'> & {
|
||||
defaultValue?: unknown;
|
||||
objectMetadataId: string;
|
||||
options?: Omit<FieldMetadataOption, 'id'>[];
|
||||
@ -26,7 +26,9 @@ export const useFieldMetadataItem = () => {
|
||||
) => {
|
||||
const formatedInput = formatFieldMetadataItemInput(input);
|
||||
const defaultValue = input.defaultValue
|
||||
? `'${input.defaultValue}'`
|
||||
? typeof input.defaultValue == 'string'
|
||||
? `'${input.defaultValue}'`
|
||||
: input.defaultValue
|
||||
: formatedInput.defaultValue ?? undefined;
|
||||
|
||||
return createOneFieldMetadataItem({
|
||||
@ -38,14 +40,25 @@ export const useFieldMetadataItem = () => {
|
||||
};
|
||||
|
||||
const editMetadataField = (
|
||||
input: Pick<Field, 'id' | 'label' | 'icon' | 'description'> & {
|
||||
input: Pick<
|
||||
Field,
|
||||
'id' | 'label' | 'icon' | 'description' | 'defaultValue'
|
||||
> & {
|
||||
options?: FieldMetadataOption[];
|
||||
},
|
||||
) =>
|
||||
updateOneFieldMetadataItem({
|
||||
) => {
|
||||
const formatedInput = formatFieldMetadataItemInput(input);
|
||||
const defaultValue = input.defaultValue
|
||||
? typeof input.defaultValue == 'string'
|
||||
? `'${input.defaultValue}'`
|
||||
: input.defaultValue
|
||||
: formatedInput.defaultValue ?? undefined;
|
||||
|
||||
return updateOneFieldMetadataItem({
|
||||
fieldMetadataIdToUpdate: input.id,
|
||||
updatePayload: formatFieldMetadataItemInput({
|
||||
...input,
|
||||
defaultValue,
|
||||
// In Edit mode, all options need an id,
|
||||
// so we generate an id for newly created options.
|
||||
options: input.options?.map((option) =>
|
||||
@ -53,6 +66,7 @@ export const useFieldMetadataItem = () => {
|
||||
),
|
||||
}),
|
||||
});
|
||||
};
|
||||
|
||||
const activateMetadataField = (metadataField: FieldMetadataItem) =>
|
||||
updateOneFieldMetadataItem({
|
||||
|
@ -45,7 +45,7 @@ export const formatFieldMetadataItemInput = (
|
||||
return {
|
||||
defaultValue: defaultOption
|
||||
? `'${getOptionValueFromLabel(defaultOption.label)}'`
|
||||
: undefined,
|
||||
: input.defaultValue,
|
||||
description: input.description?.trim() ?? null,
|
||||
icon: input.icon,
|
||||
label: input.label.trim(),
|
||||
|
@ -0,0 +1,60 @@
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
import { IconCheck, IconX } from '@/ui/display/icon';
|
||||
import { Select } from '@/ui/input/components/Select';
|
||||
import { CardContent } from '@/ui/layout/card/components/CardContent';
|
||||
|
||||
type SettingsDataModelDefaultValueFormProps = {
|
||||
className?: string;
|
||||
disabled?: boolean;
|
||||
onChange?: (defaultValue: SettingsDataModelDefaultValue) => void;
|
||||
value?: SettingsDataModelDefaultValue;
|
||||
};
|
||||
|
||||
export type SettingsDataModelDefaultValue = any;
|
||||
|
||||
const StyledContainer = styled(CardContent)`
|
||||
padding-bottom: ${({ theme }) => theme.spacing(3.5)};
|
||||
`;
|
||||
|
||||
const StyledLabel = styled.span`
|
||||
color: ${({ theme }) => theme.font.color.light};
|
||||
display: block;
|
||||
font-size: ${({ theme }) => theme.font.size.xs};
|
||||
font-weight: ${({ theme }) => theme.font.weight.semiBold};
|
||||
margin-bottom: 6px;
|
||||
margin-top: ${({ theme }) => theme.spacing(1)};
|
||||
`;
|
||||
|
||||
export const SettingsDataModelDefaultValueForm = ({
|
||||
className,
|
||||
disabled,
|
||||
onChange,
|
||||
value,
|
||||
}: SettingsDataModelDefaultValueFormProps) => {
|
||||
return (
|
||||
<StyledContainer>
|
||||
<StyledLabel>Default Value</StyledLabel>
|
||||
<Select
|
||||
className={className}
|
||||
fullWidth
|
||||
disabled={disabled}
|
||||
dropdownId="object-field-default-value-select"
|
||||
value={value}
|
||||
onChange={(value) => onChange?.(value)}
|
||||
options={[
|
||||
{
|
||||
value: true,
|
||||
label: 'True',
|
||||
Icon: IconCheck,
|
||||
},
|
||||
{
|
||||
value: false,
|
||||
label: 'False',
|
||||
Icon: IconX,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</StyledContainer>
|
||||
);
|
||||
};
|
@ -2,6 +2,7 @@ import styled from '@emotion/styled';
|
||||
|
||||
import { useObjectMetadataItemForSettings } from '@/object-metadata/hooks/useObjectMetadataItemForSettings';
|
||||
import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
|
||||
import { SettingsDataModelDefaultValueForm } from '@/settings/data-model/components/SettingsDataModelDefaultValue';
|
||||
import { SettingsDataModelPreviewFormCard } from '@/settings/data-model/components/SettingsDataModelPreviewFormCard';
|
||||
import {
|
||||
SettingsObjectFieldCurrencyForm,
|
||||
@ -26,6 +27,7 @@ export type SettingsDataModelFieldSettingsFormValues = {
|
||||
currency: SettingsObjectFieldCurrencyFormValues;
|
||||
relation: SettingsObjectFieldRelationFormValues;
|
||||
select: SettingsObjectFieldSelectFormValues;
|
||||
defaultValue: any;
|
||||
};
|
||||
|
||||
type SettingsDataModelFieldSettingsFormCardProps = {
|
||||
@ -152,6 +154,13 @@ export const SettingsDataModelFieldSettingsFormCard = ({
|
||||
onChange({ select: nextSelectValues })
|
||||
}
|
||||
/>
|
||||
) : fieldMetadataItem.type === FieldMetadataType.Boolean ? (
|
||||
<SettingsDataModelDefaultValueForm
|
||||
value={values.defaultValue}
|
||||
onChange={(nextValueDefaultValue) =>
|
||||
onChange({ defaultValue: nextValueDefaultValue })
|
||||
}
|
||||
/>
|
||||
) : undefined
|
||||
}
|
||||
/>
|
||||
|
@ -6,12 +6,19 @@ import {
|
||||
} from '@/settings/data-model/constants/SettingsFieldTypeConfigs';
|
||||
import { SettingsSupportedFieldType } from '@/settings/data-model/types/SettingsSupportedFieldType';
|
||||
import { Select, SelectOption } from '@/ui/input/components/Select';
|
||||
import { FieldMetadataType } from '~/generated-metadata/graphql';
|
||||
|
||||
type SettingsDataModelFieldTypeSelectProps = {
|
||||
className?: string;
|
||||
disabled?: boolean;
|
||||
excludedFieldTypes?: SettingsSupportedFieldType[];
|
||||
onChange?: ({ type }: { type: SettingsSupportedFieldType }) => void;
|
||||
onChange?: ({
|
||||
type,
|
||||
defaultValue,
|
||||
}: {
|
||||
type: SettingsSupportedFieldType;
|
||||
defaultValue: any;
|
||||
}) => void;
|
||||
value?: SettingsSupportedFieldType;
|
||||
};
|
||||
|
||||
@ -41,7 +48,12 @@ export const SettingsDataModelFieldTypeSelect = ({
|
||||
disabled={disabled}
|
||||
dropdownId="object-field-type-select"
|
||||
value={value}
|
||||
onChange={(value) => onChange?.({ type: value })}
|
||||
onChange={(value) =>
|
||||
onChange?.({
|
||||
type: value,
|
||||
defaultValue: value === FieldMetadataType.Boolean ? false : undefined,
|
||||
})
|
||||
}
|
||||
options={fieldTypeOptions}
|
||||
/>
|
||||
);
|
||||
|
@ -26,6 +26,7 @@ const defaultValues = {
|
||||
currency: fieldMetadataFormDefaultValues.currency,
|
||||
relation: fieldMetadataFormDefaultValues.relation,
|
||||
select: fieldMetadataFormDefaultValues.select,
|
||||
defaultValue: fieldMetadataFormDefaultValues.defaultValue,
|
||||
};
|
||||
|
||||
const meta: Meta<typeof SettingsDataModelFieldSettingsFormCard> = {
|
||||
@ -110,6 +111,7 @@ export const WithSelectForm: Story = {
|
||||
},
|
||||
{ color: 'green', label: '🌏 NGO', value: 'NGO' },
|
||||
],
|
||||
defaultValue: undefined,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
@ -23,6 +23,7 @@ describe('useFieldMetadataForm', () => {
|
||||
objectMetadataId: '',
|
||||
field: { label: '' },
|
||||
},
|
||||
defaultValue: null,
|
||||
select: [
|
||||
{ color: 'green', label: 'Option 1', value: expect.any(String) },
|
||||
],
|
||||
|
@ -18,6 +18,7 @@ type FormValues = {
|
||||
description?: string;
|
||||
icon: string;
|
||||
label: string;
|
||||
defaultValue: any;
|
||||
type: SettingsSupportedFieldType;
|
||||
} & SettingsDataModelFieldSettingsFormValues;
|
||||
|
||||
@ -31,6 +32,7 @@ export const fieldMetadataFormDefaultValues: FormValues = {
|
||||
objectMetadataId: '',
|
||||
field: { label: '' },
|
||||
},
|
||||
defaultValue: null,
|
||||
select: [{ color: 'green', label: 'Option 1', value: v4() }],
|
||||
};
|
||||
|
||||
@ -38,6 +40,7 @@ const fieldSchema = z.object({
|
||||
description: z.string().optional(),
|
||||
icon: z.string().startsWith('Icon'),
|
||||
label: z.string().min(1),
|
||||
defaultValue: z.any(),
|
||||
});
|
||||
|
||||
const currencySchema = fieldSchema.merge(
|
||||
@ -124,6 +127,8 @@ export const useFieldMetadataForm = () => {
|
||||
const [hasCurrencyFormChanged, setHasCurrencyFormChanged] = useState(false);
|
||||
const [hasRelationFormChanged, setHasRelationFormChanged] = useState(false);
|
||||
const [hasSelectFormChanged, setHasSelectFormChanged] = useState(false);
|
||||
const [hasDefaultValueChanged, setHasDefaultValueFormChanged] =
|
||||
useState(false);
|
||||
const [validationResult, setValidationResult] = useState(
|
||||
schema.safeParse(formValues),
|
||||
);
|
||||
@ -169,12 +174,14 @@ export const useFieldMetadataForm = () => {
|
||||
currency: initialCurrencyFormValues,
|
||||
relation: initialRelationFormValues,
|
||||
select: initialSelectFormValues,
|
||||
defaultValue: initalDefaultValue,
|
||||
...initialFieldFormValues
|
||||
} = initialFormValues;
|
||||
const {
|
||||
currency: nextCurrencyFormValues,
|
||||
relation: nextRelationFormValues,
|
||||
select: nextSelectFormValues,
|
||||
defaultValue: nextDefaultValue,
|
||||
...nextFieldFormValues
|
||||
} = nextFormValues;
|
||||
|
||||
@ -193,6 +200,10 @@ export const useFieldMetadataForm = () => {
|
||||
nextFieldFormValues.type === FieldMetadataType.Select &&
|
||||
!isDeeplyEqual(initialSelectFormValues, nextSelectFormValues),
|
||||
);
|
||||
setHasDefaultValueFormChanged(
|
||||
nextFieldFormValues.type === FieldMetadataType.Boolean &&
|
||||
!isDeeplyEqual(initalDefaultValue, nextDefaultValue),
|
||||
);
|
||||
};
|
||||
|
||||
return {
|
||||
@ -203,9 +214,11 @@ export const useFieldMetadataForm = () => {
|
||||
hasFieldFormChanged ||
|
||||
hasCurrencyFormChanged ||
|
||||
hasRelationFormChanged ||
|
||||
hasSelectFormChanged,
|
||||
hasSelectFormChanged ||
|
||||
hasDefaultValueChanged,
|
||||
hasRelationFormChanged,
|
||||
hasSelectFormChanged,
|
||||
hasDefaultValueChanged,
|
||||
initForm,
|
||||
isInitialized,
|
||||
isValid: validationResult.success,
|
||||
|
@ -83,6 +83,7 @@ export const SettingsObjectFieldEdit = () => {
|
||||
formValues,
|
||||
handleFormChange,
|
||||
hasFieldFormChanged,
|
||||
hasDefaultValueChanged,
|
||||
hasFormChanged,
|
||||
hasRelationFormChanged,
|
||||
hasSelectFormChanged,
|
||||
@ -132,6 +133,7 @@ export const SettingsObjectFieldEdit = () => {
|
||||
objectMetadataId: relationObjectMetadataItem?.id || '',
|
||||
type: relationType || RelationMetadataType.OneToMany,
|
||||
},
|
||||
defaultValue: activeMetadataField.defaultValue,
|
||||
...(selectOptions?.length ? { select: selectOptions } : {}),
|
||||
});
|
||||
}, [
|
||||
@ -170,13 +172,17 @@ export const SettingsObjectFieldEdit = () => {
|
||||
label: validatedFormValues.relation.field.label,
|
||||
});
|
||||
}
|
||||
|
||||
if (hasFieldFormChanged || hasSelectFormChanged) {
|
||||
if (
|
||||
hasFieldFormChanged ||
|
||||
hasSelectFormChanged ||
|
||||
hasDefaultValueChanged
|
||||
) {
|
||||
await editMetadataField({
|
||||
description: validatedFormValues.description,
|
||||
icon: validatedFormValues.icon,
|
||||
id: activeMetadataField.id,
|
||||
label: validatedFormValues.label,
|
||||
defaultValue: validatedFormValues.defaultValue,
|
||||
options:
|
||||
validatedFormValues.type === FieldMetadataType.Select
|
||||
? validatedFormValues.select
|
||||
@ -255,6 +261,7 @@ export const SettingsObjectFieldEdit = () => {
|
||||
currency: formValues.currency,
|
||||
relation: formValues.relation,
|
||||
select: formValues.select,
|
||||
defaultValue: formValues.defaultValue,
|
||||
}}
|
||||
/>
|
||||
</Section>
|
||||
|
@ -213,7 +213,7 @@ export const SettingsObjectNewFieldStep2 = () => {
|
||||
amountMicros: null,
|
||||
currencyCode: validatedFormValues.currency.currencyCode,
|
||||
}
|
||||
: undefined,
|
||||
: validatedFormValues.defaultValue,
|
||||
description: validatedFormValues.description,
|
||||
icon: validatedFormValues.icon,
|
||||
label: validatedFormValues.label ?? '',
|
||||
@ -320,6 +320,7 @@ export const SettingsObjectNewFieldStep2 = () => {
|
||||
currency: formValues.currency,
|
||||
relation: formValues.relation,
|
||||
select: formValues.select,
|
||||
defaultValue: formValues.defaultValue,
|
||||
}}
|
||||
/>
|
||||
</Section>
|
||||
|
@ -300,10 +300,12 @@ export class FieldMetadataService extends TypeOrmQueryService<FieldMetadataEntit
|
||||
await fieldMetadataRepository.update(id, {
|
||||
...updatableFieldInput,
|
||||
defaultValue:
|
||||
// Todo: we need to handle default value for all field types. Right now we are only allowing update for SELECt
|
||||
existingFieldMetadata.type !== FieldMetadataType.SELECT
|
||||
// Todo: we handle default value for all field types.
|
||||
![FieldMetadataType.SELECT, FieldMetadataType.BOOLEAN].includes(
|
||||
existingFieldMetadata.type,
|
||||
)
|
||||
? existingFieldMetadata.defaultValue
|
||||
: updatableFieldInput.defaultValue
|
||||
: updatableFieldInput.defaultValue !== null
|
||||
? updatableFieldInput.defaultValue
|
||||
: null,
|
||||
// If the name is updated, the targetColumnMap should be updated as well
|
||||
|
Loading…
Reference in New Issue
Block a user