Unique fields - add unicity toggle on data model settings (#13592)
To review after https://github.com/twentyhq/twenty/pull/13539 closes https://github.com/twentyhq/core-team-issues/issues/1224 --------- Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
co-authored by
Charles Bochet
parent
6a492b0732
commit
e06fc34cbe
@@ -34,6 +34,7 @@ export const CREATE_ONE_FIELD_METADATA_ITEM = gql`
|
||||
icon
|
||||
isCustom
|
||||
isActive
|
||||
isUnique
|
||||
isNullable
|
||||
createdAt
|
||||
updatedAt
|
||||
@@ -59,6 +60,7 @@ export const UPDATE_ONE_FIELD_METADATA_ITEM = gql`
|
||||
icon
|
||||
isCustom
|
||||
isActive
|
||||
isUnique
|
||||
isNullable
|
||||
createdAt
|
||||
updatedAt
|
||||
@@ -128,6 +130,7 @@ export const DELETE_ONE_FIELD_METADATA_ITEM = gql`
|
||||
icon
|
||||
isCustom
|
||||
isActive
|
||||
isUnique
|
||||
isNullable
|
||||
createdAt
|
||||
updatedAt
|
||||
|
||||
+3
@@ -17,6 +17,7 @@ export const queries = {
|
||||
icon
|
||||
isCustom
|
||||
isActive
|
||||
isUnique
|
||||
isNullable
|
||||
createdAt
|
||||
updatedAt
|
||||
@@ -99,6 +100,7 @@ export const queries = {
|
||||
icon
|
||||
isCustom
|
||||
isActive
|
||||
isUnique
|
||||
isNullable
|
||||
createdAt
|
||||
updatedAt
|
||||
@@ -131,6 +133,7 @@ export const variables = {
|
||||
options: undefined,
|
||||
settings: undefined,
|
||||
isLabelSyncedWithName: true,
|
||||
isUnique: undefined,
|
||||
objectMetadataId,
|
||||
type: 'TEXT',
|
||||
},
|
||||
|
||||
+2
@@ -13,6 +13,7 @@ export const formatFieldMetadataItemInput = (
|
||||
| 'options'
|
||||
| 'settings'
|
||||
| 'isLabelSyncedWithName'
|
||||
| 'isUnique'
|
||||
>
|
||||
>,
|
||||
) => {
|
||||
@@ -25,5 +26,6 @@ export const formatFieldMetadataItemInput = (
|
||||
options: input.options,
|
||||
settings: input.settings,
|
||||
isLabelSyncedWithName: input.isLabelSyncedWithName,
|
||||
isUnique: input.isUnique,
|
||||
};
|
||||
};
|
||||
|
||||
-2
@@ -18,8 +18,6 @@ import { TEXT_INPUT_STYLE } from 'twenty-ui/theme';
|
||||
import { FieldMetadataType } from '~/generated-metadata/graphql';
|
||||
import { stripSimpleQuotesFromString } from '~/utils/string/stripSimpleQuotesFromString';
|
||||
|
||||
export const DEFAULT_PHONE_CALLING_CODE = '1';
|
||||
|
||||
const StyledCustomPhoneInputContainer = styled.div<{
|
||||
hasItem: boolean;
|
||||
hasError?: boolean;
|
||||
|
||||
-1
@@ -18,7 +18,6 @@ type SettingsOptionCardContentSelectProps = {
|
||||
|
||||
const StyledSelectContainer = styled.div`
|
||||
margin-left: auto;
|
||||
width: 120px;
|
||||
`;
|
||||
|
||||
export const SettingsOptionCardContentSelect = ({
|
||||
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
import { Controller, useFormContext } from 'react-hook-form';
|
||||
|
||||
import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
|
||||
|
||||
import { Separator } from '@/settings/components/Separator';
|
||||
import { SettingsOptionCardContentSelect } from '@/settings/components/SettingsOptions/SettingsOptionCardContentSelect';
|
||||
import { canBeUnique } from '@/settings/data-model/fields/forms/utils/canBeUnique.util';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { IconKey } from 'twenty-ui/display';
|
||||
import { Toggle } from 'twenty-ui/input';
|
||||
|
||||
type SettingsDataModelFieldIsUniqueFormValues = {
|
||||
isUnique: boolean;
|
||||
};
|
||||
|
||||
type SettingsDataModelFieldIsUniqueFormProps = {
|
||||
fieldMetadataItem: Pick<FieldMetadataItem, 'isUnique' | 'type' | 'isCustom'>;
|
||||
};
|
||||
|
||||
export const SettingsDataModelFieldIsUniqueForm = ({
|
||||
fieldMetadataItem,
|
||||
}: SettingsDataModelFieldIsUniqueFormProps) => {
|
||||
const { control } =
|
||||
useFormContext<SettingsDataModelFieldIsUniqueFormValues>();
|
||||
|
||||
if (!canBeUnique(fieldMetadataItem)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Controller
|
||||
name="isUnique"
|
||||
defaultValue={fieldMetadataItem?.isUnique || false}
|
||||
control={control}
|
||||
render={({ field: { onChange, value } }) => {
|
||||
const isUnique = value ?? false;
|
||||
|
||||
return (
|
||||
<>
|
||||
<Separator />
|
||||
<SettingsOptionCardContentSelect
|
||||
Icon={IconKey}
|
||||
title={t`Unique`}
|
||||
description={t`Prevent users from assigining the same value to different records`}
|
||||
>
|
||||
<Toggle
|
||||
toggleSize="small"
|
||||
value={isUnique}
|
||||
onChange={(value) => onChange(value)}
|
||||
/>
|
||||
</SettingsOptionCardContentSelect>
|
||||
</>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
+43
-25
@@ -9,7 +9,8 @@ import { settingsDataModelFieldAddressFormSchema } from '@/settings/data-model/f
|
||||
import { SettingsDataModelFieldAddressSettingsFormCard } from '@/settings/data-model/fields/forms/address/components/SettingsDataModelFieldAddressSettingsFormCard';
|
||||
import { settingsDataModelFieldBooleanFormSchema } from '@/settings/data-model/fields/forms/boolean/components/SettingsDataModelFieldBooleanForm';
|
||||
import { SettingsDataModelFieldBooleanSettingsFormCard } from '@/settings/data-model/fields/forms/boolean/components/SettingsDataModelFieldBooleanSettingsFormCard';
|
||||
import { settingsDataModelFieldtextFormSchema } from '@/settings/data-model/fields/forms/components/text/SettingsDataModelFieldTextForm';
|
||||
import { SettingsDataModelFieldIsUniqueForm } from '@/settings/data-model/fields/forms/components/SettingsDataModelFieldIsUniqueForm';
|
||||
import { settingsDataModelFieldTextFormSchema } from '@/settings/data-model/fields/forms/components/text/SettingsDataModelFieldTextForm';
|
||||
import { SettingsDataModelFieldTextSettingsFormCard } from '@/settings/data-model/fields/forms/components/text/SettingsDataModelFieldTextSettingsFormCard';
|
||||
import { settingsDataModelFieldCurrencyFormSchema } from '@/settings/data-model/fields/forms/currency/components/SettingsDataModelFieldCurrencyForm';
|
||||
import { SettingsDataModelFieldCurrencySettingsFormCard } from '@/settings/data-model/fields/forms/currency/components/SettingsDataModelFieldCurrencySettingsFormCard';
|
||||
@@ -32,6 +33,10 @@ import {
|
||||
} from '@/settings/data-model/fields/preview/components/SettingsDataModelFieldPreviewCard';
|
||||
import { FieldMetadataType } from '~/generated-metadata/graphql';
|
||||
|
||||
const isUniqueFieldFormSchema = z.object({
|
||||
isUnique: z.boolean().nullable().optional(),
|
||||
});
|
||||
|
||||
const booleanFieldFormSchema = z
|
||||
.object({ type: z.literal(FieldMetadataType.BOOLEAN) })
|
||||
.merge(settingsDataModelFieldBooleanFormSchema);
|
||||
@@ -42,11 +47,13 @@ const currencyFieldFormSchema = z
|
||||
|
||||
const dateFieldFormSchema = z
|
||||
.object({ type: z.literal(FieldMetadataType.DATE) })
|
||||
.merge(settingsDataModelFieldDateFormSchema);
|
||||
.merge(settingsDataModelFieldDateFormSchema)
|
||||
.merge(isUniqueFieldFormSchema);
|
||||
|
||||
const dateTimeFieldFormSchema = z
|
||||
.object({ type: z.literal(FieldMetadataType.DATE_TIME) })
|
||||
.merge(settingsDataModelFieldDateFormSchema);
|
||||
.merge(settingsDataModelFieldDateFormSchema)
|
||||
.merge(isUniqueFieldFormSchema);
|
||||
|
||||
const relationFieldFormSchema = z
|
||||
.object({ type: z.literal(FieldMetadataType.RELATION) })
|
||||
@@ -62,11 +69,13 @@ const multiSelectFieldFormSchema = z
|
||||
|
||||
const numberFieldFormSchema = z
|
||||
.object({ type: z.literal(FieldMetadataType.NUMBER) })
|
||||
.merge(settingsDataModelFieldNumberFormSchema);
|
||||
.merge(settingsDataModelFieldNumberFormSchema)
|
||||
.merge(isUniqueFieldFormSchema);
|
||||
|
||||
const textFieldFormSchema = z
|
||||
.object({ type: z.literal(FieldMetadataType.TEXT) })
|
||||
.merge(settingsDataModelFieldtextFormSchema);
|
||||
.merge(settingsDataModelFieldTextFormSchema)
|
||||
.merge(isUniqueFieldFormSchema);
|
||||
|
||||
const addressFieldFormSchema = z
|
||||
.object({ type: z.literal(FieldMetadataType.ADDRESS) })
|
||||
@@ -74,27 +83,30 @@ const addressFieldFormSchema = z
|
||||
|
||||
const phonesFieldFormSchema = z
|
||||
.object({ type: z.literal(FieldMetadataType.PHONES) })
|
||||
.merge(settingsDataModelFieldPhonesFormSchema);
|
||||
.merge(settingsDataModelFieldPhonesFormSchema)
|
||||
.merge(isUniqueFieldFormSchema);
|
||||
|
||||
const otherFieldsFormSchema = z.object({
|
||||
type: z.enum(
|
||||
Object.keys(
|
||||
omit(SETTINGS_FIELD_TYPE_CONFIGS, [
|
||||
FieldMetadataType.BOOLEAN,
|
||||
FieldMetadataType.CURRENCY,
|
||||
FieldMetadataType.RELATION,
|
||||
FieldMetadataType.SELECT,
|
||||
FieldMetadataType.MULTI_SELECT,
|
||||
FieldMetadataType.DATE,
|
||||
FieldMetadataType.DATE_TIME,
|
||||
FieldMetadataType.NUMBER,
|
||||
FieldMetadataType.ADDRESS,
|
||||
FieldMetadataType.PHONES,
|
||||
FieldMetadataType.TEXT,
|
||||
]),
|
||||
) as [FieldMetadataType, ...FieldMetadataType[]],
|
||||
),
|
||||
});
|
||||
const otherFieldsFormSchema = z
|
||||
.object({
|
||||
type: z.enum(
|
||||
Object.keys(
|
||||
omit(SETTINGS_FIELD_TYPE_CONFIGS, [
|
||||
FieldMetadataType.BOOLEAN,
|
||||
FieldMetadataType.CURRENCY,
|
||||
FieldMetadataType.RELATION,
|
||||
FieldMetadataType.SELECT,
|
||||
FieldMetadataType.MULTI_SELECT,
|
||||
FieldMetadataType.DATE,
|
||||
FieldMetadataType.DATE_TIME,
|
||||
FieldMetadataType.NUMBER,
|
||||
FieldMetadataType.ADDRESS,
|
||||
FieldMetadataType.PHONES,
|
||||
FieldMetadataType.TEXT,
|
||||
]),
|
||||
) as [FieldMetadataType, ...FieldMetadataType[]],
|
||||
),
|
||||
})
|
||||
.merge(isUniqueFieldFormSchema);
|
||||
|
||||
export const settingsDataModelFieldSettingsFormSchema = z.discriminatedUnion(
|
||||
'type',
|
||||
@@ -144,6 +156,7 @@ const previewableTypes = [
|
||||
FieldMetadataType.RELATION,
|
||||
FieldMetadataType.SELECT,
|
||||
FieldMetadataType.TEXT,
|
||||
FieldMetadataType.UUID,
|
||||
];
|
||||
|
||||
export const SettingsDataModelFieldSettingsFormCard = ({
|
||||
@@ -249,6 +262,11 @@ export const SettingsDataModelFieldSettingsFormCard = ({
|
||||
objectMetadataItem={objectMetadataItem}
|
||||
/>
|
||||
}
|
||||
form={
|
||||
<SettingsDataModelFieldIsUniqueForm
|
||||
fieldMetadataItem={fieldMetadataItem}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
+27
-23
@@ -12,20 +12,26 @@ type SettingsDataModelFieldTextFormProps = {
|
||||
disabled?: boolean;
|
||||
fieldMetadataItem: Pick<
|
||||
FieldMetadataItem,
|
||||
'icon' | 'label' | 'type' | 'defaultValue' | 'settings'
|
||||
| 'icon'
|
||||
| 'label'
|
||||
| 'type'
|
||||
| 'defaultValue'
|
||||
| 'settings'
|
||||
| 'isUnique'
|
||||
| 'isCustom'
|
||||
>;
|
||||
};
|
||||
|
||||
export const textFieldDefaultValueSchema = z.object({
|
||||
const textFieldDefaultValueSchema = z.object({
|
||||
displayedMaxRows: z.number().nullable(),
|
||||
});
|
||||
|
||||
export const settingsDataModelFieldtextFormSchema = z.object({
|
||||
export const settingsDataModelFieldTextFormSchema = z.object({
|
||||
settings: textFieldDefaultValueSchema,
|
||||
});
|
||||
|
||||
export type SettingsDataModelFieldTextFormValues = z.infer<
|
||||
typeof settingsDataModelFieldtextFormSchema
|
||||
typeof settingsDataModelFieldTextFormSchema
|
||||
>;
|
||||
|
||||
export const SettingsDataModelFieldTextForm = ({
|
||||
@@ -46,25 +52,23 @@ export const SettingsDataModelFieldTextForm = ({
|
||||
const displayedMaxRows = value?.displayedMaxRows ?? 0;
|
||||
|
||||
return (
|
||||
<>
|
||||
<SettingsOptionCardContentSelect
|
||||
Icon={IconTextWrap}
|
||||
title={t`Wrap on record pages`}
|
||||
description={t`Display text on multiple lines`}
|
||||
>
|
||||
<Select<number>
|
||||
dropdownId="text-wrap"
|
||||
value={displayedMaxRows}
|
||||
onChange={(value) => onChange({ displayedMaxRows: value })}
|
||||
disabled={disabled}
|
||||
options={TEXT_DATA_MODEL_SELECT_OPTIONS.map((option) => ({
|
||||
...option,
|
||||
label: t(option.label),
|
||||
}))}
|
||||
selectSizeVariant="small"
|
||||
/>
|
||||
</SettingsOptionCardContentSelect>
|
||||
</>
|
||||
<SettingsOptionCardContentSelect
|
||||
Icon={IconTextWrap}
|
||||
title={t`Wrap on record pages`}
|
||||
description={t`Display text on multiple lines`}
|
||||
>
|
||||
<Select<number>
|
||||
dropdownId="text-wrap"
|
||||
value={displayedMaxRows}
|
||||
onChange={(value) => onChange({ displayedMaxRows: value })}
|
||||
disabled={disabled}
|
||||
options={TEXT_DATA_MODEL_SELECT_OPTIONS.map((option) => ({
|
||||
...option,
|
||||
label: t(option.label),
|
||||
}))}
|
||||
selectSizeVariant="small"
|
||||
/>
|
||||
</SettingsOptionCardContentSelect>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
|
||||
+11
-5
@@ -3,6 +3,7 @@ import styled from '@emotion/styled';
|
||||
import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
|
||||
import { SettingsDataModelPreviewFormCard } from '@/settings/data-model/components/SettingsDataModelPreviewFormCard';
|
||||
|
||||
import { SettingsDataModelFieldIsUniqueForm } from '@/settings/data-model/fields/forms/components/SettingsDataModelFieldIsUniqueForm';
|
||||
import { SettingsDataModelFieldTextForm } from '@/settings/data-model/fields/forms/components/text/SettingsDataModelFieldTextForm';
|
||||
import {
|
||||
SettingsDataModelFieldPreviewCard,
|
||||
@@ -14,7 +15,7 @@ type SettingsDataModelFieldTextSettingsFormCardProps = {
|
||||
disabled?: boolean;
|
||||
fieldMetadataItem: Pick<
|
||||
FieldMetadataItem,
|
||||
'icon' | 'label' | 'type' | 'defaultValue'
|
||||
'icon' | 'label' | 'type' | 'defaultValue' | 'isUnique' | 'isCustom'
|
||||
>;
|
||||
} & Pick<SettingsDataModelFieldPreviewCardProps, 'objectMetadataItem'>;
|
||||
|
||||
@@ -41,10 +42,15 @@ export const SettingsDataModelFieldTextSettingsFormCard = ({
|
||||
/>
|
||||
}
|
||||
form={
|
||||
<SettingsDataModelFieldTextForm
|
||||
disabled={disabled}
|
||||
fieldMetadataItem={fieldMetadataItem}
|
||||
/>
|
||||
<>
|
||||
<SettingsDataModelFieldTextForm
|
||||
disabled={disabled}
|
||||
fieldMetadataItem={fieldMetadataItem}
|
||||
/>
|
||||
<SettingsDataModelFieldIsUniqueForm
|
||||
fieldMetadataItem={fieldMetadataItem}
|
||||
/>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
);
|
||||
|
||||
+11
-5
@@ -3,6 +3,7 @@ import { useFormContext } from 'react-hook-form';
|
||||
|
||||
import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
|
||||
import { SettingsDataModelPreviewFormCard } from '@/settings/data-model/components/SettingsDataModelPreviewFormCard';
|
||||
import { SettingsDataModelFieldIsUniqueForm } from '@/settings/data-model/fields/forms/components/SettingsDataModelFieldIsUniqueForm';
|
||||
import {
|
||||
SettingsDataModelFieldDateForm,
|
||||
SettingsDataModelFieldDateFormValues,
|
||||
@@ -17,7 +18,7 @@ type SettingsDataModelFieldDateSettingsFormCardProps = {
|
||||
disabled?: boolean;
|
||||
fieldMetadataItem: Pick<
|
||||
FieldMetadataItem,
|
||||
'icon' | 'label' | 'type' | 'settings'
|
||||
'icon' | 'label' | 'type' | 'isUnique' | 'isCustom' | 'settings'
|
||||
>;
|
||||
} & Pick<SettingsDataModelFieldPreviewCardProps, 'objectMetadataItem'>;
|
||||
|
||||
@@ -60,10 +61,15 @@ export const SettingsDataModelFieldDateSettingsFormCard = ({
|
||||
/>
|
||||
}
|
||||
form={
|
||||
<SettingsDataModelFieldDateForm
|
||||
disabled={disabled}
|
||||
fieldMetadataItem={fieldMetadataItem}
|
||||
/>
|
||||
<>
|
||||
<SettingsDataModelFieldDateForm
|
||||
disabled={disabled}
|
||||
fieldMetadataItem={fieldMetadataItem}
|
||||
/>
|
||||
<SettingsDataModelFieldIsUniqueForm
|
||||
fieldMetadataItem={fieldMetadataItem}
|
||||
/>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
);
|
||||
|
||||
+12
-5
@@ -2,6 +2,7 @@ import styled from '@emotion/styled';
|
||||
|
||||
import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
|
||||
import { SettingsDataModelPreviewFormCard } from '@/settings/data-model/components/SettingsDataModelPreviewFormCard';
|
||||
import { SettingsDataModelFieldIsUniqueForm } from '@/settings/data-model/fields/forms/components/SettingsDataModelFieldIsUniqueForm';
|
||||
import { SettingsDataModelFieldNumberForm } from '@/settings/data-model/fields/forms/number/components/SettingsDataModelFieldNumberForm';
|
||||
import {
|
||||
SettingsDataModelFieldPreviewCard,
|
||||
@@ -13,7 +14,7 @@ type SettingsDataModelFieldNumberSettingsFormCardProps = {
|
||||
disabled?: boolean;
|
||||
fieldMetadataItem: Pick<
|
||||
FieldMetadataItem,
|
||||
'icon' | 'label' | 'type' | 'defaultValue' | 'settings'
|
||||
'icon' | 'label' | 'type' | 'defaultValue' | 'isUnique' | 'isCustom'
|
||||
>;
|
||||
} & Pick<SettingsDataModelFieldPreviewCardProps, 'objectMetadataItem'>;
|
||||
|
||||
@@ -43,10 +44,16 @@ export const SettingsDataModelFieldNumberSettingsFormCard = ({
|
||||
/>
|
||||
}
|
||||
form={
|
||||
<SettingsDataModelFieldNumberForm
|
||||
disabled={disabled}
|
||||
fieldMetadataItem={fieldMetadataItem}
|
||||
/>
|
||||
<>
|
||||
<SettingsDataModelFieldNumberForm
|
||||
disabled={disabled}
|
||||
fieldMetadataItem={fieldMetadataItem}
|
||||
/>
|
||||
|
||||
<SettingsDataModelFieldIsUniqueForm
|
||||
fieldMetadataItem={fieldMetadataItem}
|
||||
/>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
);
|
||||
|
||||
+11
-5
@@ -4,6 +4,7 @@ import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
|
||||
|
||||
import { SettingsDataModelPreviewFormCard } from '@/settings/data-model/components/SettingsDataModelPreviewFormCard';
|
||||
|
||||
import { SettingsDataModelFieldIsUniqueForm } from '@/settings/data-model/fields/forms/components/SettingsDataModelFieldIsUniqueForm';
|
||||
import { SettingsDataModelFieldPhonesForm } from '@/settings/data-model/fields/forms/phones/components/SettingsDataModelFieldPhonesForm';
|
||||
import {
|
||||
SettingsDataModelFieldPreviewCard,
|
||||
@@ -14,7 +15,7 @@ type SettingsDataModelFieldPhonesSettingsFormCardProps = {
|
||||
disabled?: boolean;
|
||||
fieldMetadataItem: Pick<
|
||||
FieldMetadataItem,
|
||||
'icon' | 'label' | 'type' | 'defaultValue'
|
||||
'icon' | 'label' | 'type' | 'defaultValue' | 'isUnique' | 'isCustom'
|
||||
>;
|
||||
} & Pick<SettingsDataModelFieldPreviewCardProps, 'objectMetadataItem'>;
|
||||
|
||||
@@ -36,10 +37,15 @@ export const SettingsDataModelFieldPhonesSettingsFormCard = ({
|
||||
/>
|
||||
}
|
||||
form={
|
||||
<SettingsDataModelFieldPhonesForm
|
||||
disabled={disabled}
|
||||
fieldMetadataItem={fieldMetadataItem}
|
||||
/>
|
||||
<>
|
||||
<SettingsDataModelFieldPhonesForm
|
||||
disabled={disabled}
|
||||
fieldMetadataItem={fieldMetadataItem}
|
||||
/>
|
||||
<SettingsDataModelFieldIsUniqueForm
|
||||
fieldMetadataItem={fieldMetadataItem}
|
||||
/>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
);
|
||||
|
||||
+16
-4
@@ -4,6 +4,7 @@ import { z } from 'zod';
|
||||
|
||||
import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
|
||||
import { SettingsDataModelPreviewFormCard } from '@/settings/data-model/components/SettingsDataModelPreviewFormCard';
|
||||
import { SettingsDataModelFieldIsUniqueForm } from '@/settings/data-model/fields/forms/components/SettingsDataModelFieldIsUniqueForm';
|
||||
import {
|
||||
settingsDataModelFieldMultiSelectFormSchema,
|
||||
SettingsDataModelFieldSelectForm,
|
||||
@@ -23,7 +24,13 @@ type SettingsDataModelFieldSelectOrMultiSelectFormValues = z.infer<
|
||||
type SettingsDataModelFieldSelectSettingsFormCardProps = {
|
||||
fieldMetadataItem: Pick<
|
||||
FieldMetadataItem,
|
||||
'icon' | 'label' | 'type' | 'defaultValue' | 'options'
|
||||
| 'icon'
|
||||
| 'label'
|
||||
| 'type'
|
||||
| 'defaultValue'
|
||||
| 'options'
|
||||
| 'isUnique'
|
||||
| 'isCustom'
|
||||
>;
|
||||
} & Pick<SettingsDataModelFieldPreviewCardProps, 'objectMetadataItem'>;
|
||||
|
||||
@@ -57,9 +64,14 @@ export const SettingsDataModelFieldSelectSettingsFormCard = ({
|
||||
/>
|
||||
}
|
||||
form={
|
||||
<SettingsDataModelFieldSelectForm
|
||||
fieldMetadataItem={fieldMetadataItem}
|
||||
/>
|
||||
<>
|
||||
<SettingsDataModelFieldSelectForm
|
||||
fieldMetadataItem={fieldMetadataItem}
|
||||
/>
|
||||
<SettingsDataModelFieldIsUniqueForm
|
||||
fieldMetadataItem={fieldMetadataItem}
|
||||
/>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
);
|
||||
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
|
||||
import { isCompositeFieldType } from '@/object-record/object-filter-dropdown/utils/isCompositeFieldType';
|
||||
import { SETTINGS_COMPOSITE_FIELD_TYPE_CONFIGS } from '@/settings/data-model/constants/SettingsCompositeFieldTypeConfigs';
|
||||
import { FieldMetadataType } from 'twenty-shared/types';
|
||||
|
||||
export const canBeUnique = (
|
||||
field: Pick<FieldMetadataItem, 'type' | 'isCustom'>,
|
||||
) => {
|
||||
if (
|
||||
[FieldMetadataType.MORPH_RELATION, FieldMetadataType.RELATION].includes(
|
||||
field.type,
|
||||
) ||
|
||||
(isCompositeFieldType(field.type) &&
|
||||
SETTINGS_COMPOSITE_FIELD_TYPE_CONFIGS[field.type].subFields.every(
|
||||
(subField) => !subField.isIncludedInUniqueConstraint,
|
||||
))
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
+2
-3
@@ -1,5 +1,4 @@
|
||||
import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
|
||||
import { DEFAULT_PHONE_CALLING_CODE } from '@/object-record/record-field/meta-types/input/components/PhonesFieldInput';
|
||||
import { FieldPhonesValue } from '@/object-record/record-field/types/FieldMetadata';
|
||||
import { getSettingsFieldTypeConfig } from '@/settings/data-model/utils/getSettingsFieldTypeConfig';
|
||||
import {
|
||||
@@ -16,12 +15,12 @@ const isStrCountryCodeGuard = (str: string): str is CountryCode => {
|
||||
|
||||
export const countryCodeToCallingCode = (countryCode: string): string => {
|
||||
if (!countryCode || !isStrCountryCodeGuard(countryCode)) {
|
||||
return `+${DEFAULT_PHONE_CALLING_CODE}`;
|
||||
return '';
|
||||
}
|
||||
|
||||
const callingCode = getCountryCallingCode(countryCode);
|
||||
|
||||
return callingCode ? `+${callingCode}` : `+${DEFAULT_PHONE_CALLING_CODE}`;
|
||||
return callingCode ? `+${callingCode}` : '';
|
||||
};
|
||||
|
||||
export const getPhonesFieldPreviewValue = ({
|
||||
|
||||
@@ -3,7 +3,6 @@ import React, { useMemo } from 'react';
|
||||
import { FieldPhonesValue } from '@/object-record/record-field/types/FieldMetadata';
|
||||
import { ExpandableList } from '@/ui/layout/expandable-list/components/ExpandableList';
|
||||
|
||||
import { DEFAULT_PHONE_CALLING_CODE } from '@/object-record/record-field/meta-types/input/components/PhonesFieldInput';
|
||||
import { styled } from '@linaria/react';
|
||||
import { parsePhoneNumber } from 'libphonenumber-js';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
@@ -49,7 +48,7 @@ export const PhonesDisplay = ({
|
||||
callingCode:
|
||||
value.primaryPhoneCallingCode ||
|
||||
value.primaryPhoneCountryCode ||
|
||||
`+${DEFAULT_PHONE_CALLING_CODE}`,
|
||||
'',
|
||||
}
|
||||
: null,
|
||||
...parseAdditionalPhones(value?.additionalPhones),
|
||||
|
||||
-4
@@ -8,7 +8,6 @@ describe('validateCanCreateUniqueIndex', () => {
|
||||
const field = {
|
||||
name: 'testField',
|
||||
type: FieldMetadataType.MORPH_RELATION,
|
||||
isCustom: true,
|
||||
};
|
||||
|
||||
expect(() => validateCanCreateUniqueIndex(field)).toThrow(
|
||||
@@ -23,7 +22,6 @@ describe('validateCanCreateUniqueIndex', () => {
|
||||
const field = {
|
||||
name: 'testField',
|
||||
type: FieldMetadataType.RELATION,
|
||||
isCustom: true,
|
||||
};
|
||||
|
||||
expect(() => validateCanCreateUniqueIndex(field)).toThrow(
|
||||
@@ -38,7 +36,6 @@ describe('validateCanCreateUniqueIndex', () => {
|
||||
const field = {
|
||||
name: 'testField',
|
||||
type: FieldMetadataType.FULL_NAME,
|
||||
isCustom: true,
|
||||
};
|
||||
|
||||
expect(() => validateCanCreateUniqueIndex(field)).toThrow(
|
||||
@@ -53,7 +50,6 @@ describe('validateCanCreateUniqueIndex', () => {
|
||||
const field = {
|
||||
name: 'testField',
|
||||
type: FieldMetadataType.ADDRESS,
|
||||
isCustom: true,
|
||||
};
|
||||
|
||||
expect(() => validateCanCreateUniqueIndex(field)).toThrow(
|
||||
|
||||
+1
-10
@@ -10,17 +10,8 @@ import {
|
||||
import { isCompositeFieldMetadataType } from 'src/engine/metadata-modules/field-metadata/utils/is-composite-field-metadata-type.util';
|
||||
|
||||
export const validateCanCreateUniqueIndex = (
|
||||
field: Pick<FieldMetadataEntity, 'type' | 'name' | 'isCustom'>,
|
||||
field: Pick<FieldMetadataEntity, 'type' | 'name'>,
|
||||
) => {
|
||||
if (field.isCustom === false)
|
||||
throw new FieldMetadataException(
|
||||
`Unique index cannot be created on standard field`,
|
||||
FieldMetadataExceptionCode.INVALID_FIELD_INPUT,
|
||||
{
|
||||
userFriendlyMessage: t`Standard fields cannot be unique.`,
|
||||
},
|
||||
);
|
||||
|
||||
const isCompositeFieldWithNonIncludedUniqueConstraint =
|
||||
isCompositeFieldMetadataType(field.type) &&
|
||||
!compositeTypeDefinitions
|
||||
|
||||
Reference in New Issue
Block a user