Default address country 🗺️ & Phone prefix ☎️ (#8614)
# Default address 🗺️ country & Phone ☎️ country We add the ability to add a Default address country and a default Phone country for fields in the Data model. fix #8081 --------- Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
@ -0,0 +1,86 @@
|
||||
import { Controller, useFormContext } from 'react-hook-form';
|
||||
|
||||
import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
|
||||
import { addressSchema as addressFieldDefaultValueSchema } from '@/object-record/record-field/types/guards/isFieldAddressValue';
|
||||
import { SettingsOptionCardContentSelect } from '@/settings/components/SettingsOptions/SettingsOptionCardContentSelect';
|
||||
import { useCountries } from '@/ui/input/components/internal/hooks/useCountries';
|
||||
import { IconMap } from 'twenty-ui';
|
||||
import { z } from 'zod';
|
||||
import { applySimpleQuotesToString } from '~/utils/string/applySimpleQuotesToString';
|
||||
import { stripSimpleQuotesFromString } from '~/utils/string/stripSimpleQuotesFromString';
|
||||
|
||||
type SettingsDataModelFieldAddressFormProps = {
|
||||
disabled?: boolean;
|
||||
defaultCountry?: string;
|
||||
fieldMetadataItem: Pick<
|
||||
FieldMetadataItem,
|
||||
'icon' | 'label' | 'type' | 'defaultValue' | 'settings'
|
||||
>;
|
||||
};
|
||||
|
||||
export const settingsDataModelFieldAddressFormSchema = z.object({
|
||||
defaultValue: addressFieldDefaultValueSchema,
|
||||
});
|
||||
|
||||
export type SettingsDataModelFieldTextFormValues = z.infer<
|
||||
typeof settingsDataModelFieldAddressFormSchema
|
||||
>;
|
||||
|
||||
export const SettingsDataModelFieldAddressForm = ({
|
||||
disabled,
|
||||
fieldMetadataItem,
|
||||
}: SettingsDataModelFieldAddressFormProps) => {
|
||||
const { control } = useFormContext<SettingsDataModelFieldTextFormValues>();
|
||||
const countries = useCountries()
|
||||
.sort((a, b) => a.countryName.localeCompare(b.countryName))
|
||||
.map((country) => ({
|
||||
label: country.countryName,
|
||||
value: country.countryName,
|
||||
}));
|
||||
countries.unshift({
|
||||
label: 'No country',
|
||||
value: '',
|
||||
});
|
||||
const defaultDefaultValue = {
|
||||
addressStreet1: "''",
|
||||
addressStreet2: null,
|
||||
addressCity: null,
|
||||
addressState: null,
|
||||
addressPostcode: null,
|
||||
addressCountry: null,
|
||||
addressLat: null,
|
||||
addressLng: null,
|
||||
};
|
||||
|
||||
return (
|
||||
<Controller
|
||||
name="defaultValue"
|
||||
defaultValue={{
|
||||
...defaultDefaultValue,
|
||||
...fieldMetadataItem?.defaultValue,
|
||||
}}
|
||||
control={control}
|
||||
render={({ field: { onChange, value } }) => {
|
||||
const defaultCountry = value?.addressCountry || '';
|
||||
return (
|
||||
<SettingsOptionCardContentSelect<string>
|
||||
Icon={IconMap}
|
||||
dropdownId="selectDefaultCountry"
|
||||
title="Default Country"
|
||||
description="The default country for new addresses"
|
||||
value={stripSimpleQuotesFromString(defaultCountry)}
|
||||
onChange={(newCountry) =>
|
||||
onChange({
|
||||
...value,
|
||||
addressCountry: applySimpleQuotesToString(newCountry),
|
||||
})
|
||||
}
|
||||
disabled={disabled}
|
||||
options={countries}
|
||||
fullWidth={true}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@ -0,0 +1,45 @@
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
|
||||
|
||||
import { SettingsDataModelPreviewFormCard } from '@/settings/data-model/components/SettingsDataModelPreviewFormCard';
|
||||
import { SettingsDataModelFieldAddressForm } from '@/settings/data-model/fields/forms/address/components/SettingsDataModelFieldAddressForm';
|
||||
import {
|
||||
SettingsDataModelFieldPreviewCard,
|
||||
SettingsDataModelFieldPreviewCardProps,
|
||||
} from '@/settings/data-model/fields/preview/components/SettingsDataModelFieldPreviewCard';
|
||||
|
||||
type SettingsDataModelFieldAddressSettingsFormCardProps = {
|
||||
disabled?: boolean;
|
||||
fieldMetadataItem: Pick<
|
||||
FieldMetadataItem,
|
||||
'icon' | 'label' | 'type' | 'defaultValue'
|
||||
>;
|
||||
} & Pick<SettingsDataModelFieldPreviewCardProps, 'objectMetadataItem'>;
|
||||
|
||||
const StyledFieldPreviewCard = styled(SettingsDataModelFieldPreviewCard)`
|
||||
flex: 1 1 100%;
|
||||
`;
|
||||
|
||||
export const SettingsDataModelFieldAddressSettingsFormCard = ({
|
||||
disabled,
|
||||
fieldMetadataItem,
|
||||
objectMetadataItem,
|
||||
}: SettingsDataModelFieldAddressSettingsFormCardProps) => {
|
||||
return (
|
||||
<SettingsDataModelPreviewFormCard
|
||||
preview={
|
||||
<StyledFieldPreviewCard
|
||||
fieldMetadataItem={fieldMetadataItem}
|
||||
objectMetadataItem={objectMetadataItem}
|
||||
/>
|
||||
}
|
||||
form={
|
||||
<SettingsDataModelFieldAddressForm
|
||||
disabled={disabled}
|
||||
fieldMetadataItem={fieldMetadataItem}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@ -5,6 +5,8 @@ import { z } from 'zod';
|
||||
import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
|
||||
import { SettingsDataModelPreviewFormCard } from '@/settings/data-model/components/SettingsDataModelPreviewFormCard';
|
||||
import { SETTINGS_FIELD_TYPE_CONFIGS } from '@/settings/data-model/constants/SettingsFieldTypeConfigs';
|
||||
import { settingsDataModelFieldAddressFormSchema } from '@/settings/data-model/fields/forms/address/components/SettingsDataModelFieldAddressForm';
|
||||
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';
|
||||
@ -15,6 +17,8 @@ import { settingsDataModelFieldDateFormSchema } from '@/settings/data-model/fiel
|
||||
import { SettingsDataModelFieldDateSettingsFormCard } from '@/settings/data-model/fields/forms/date/components/SettingsDataModelFieldDateSettingsFormCard';
|
||||
import { settingsDataModelFieldNumberFormSchema } from '@/settings/data-model/fields/forms/number/components/SettingsDataModelFieldNumberForm';
|
||||
import { SettingsDataModelFieldNumberSettingsFormCard } from '@/settings/data-model/fields/forms/number/components/SettingsDataModelFieldNumberSettingsFormCard';
|
||||
import { settingsDataModelFieldPhonesFormSchema } from '@/settings/data-model/fields/forms/phones/components/SettingsDataModelFieldPhonesForm';
|
||||
import { SettingsDataModelFieldPhonesSettingsFormCard } from '@/settings/data-model/fields/forms/phones/components/SettingsDataModelFieldPhonesSettingsFormCard';
|
||||
import { settingsDataModelFieldRelationFormSchema } from '@/settings/data-model/fields/forms/relation/components/SettingsDataModelFieldRelationForm';
|
||||
import { SettingsDataModelFieldRelationSettingsFormCard } from '@/settings/data-model/fields/forms/relation/components/SettingsDataModelFieldRelationSettingsFormCard';
|
||||
import {
|
||||
@ -64,6 +68,14 @@ const textFieldFormSchema = z
|
||||
.object({ type: z.literal(FieldMetadataType.Text) })
|
||||
.merge(settingsDataModelFieldtextFormSchema);
|
||||
|
||||
const addressFieldFormSchema = z
|
||||
.object({ type: z.literal(FieldMetadataType.Address) })
|
||||
.merge(settingsDataModelFieldAddressFormSchema);
|
||||
|
||||
const phonesFieldFormSchema = z
|
||||
.object({ type: z.literal(FieldMetadataType.Phones) })
|
||||
.merge(settingsDataModelFieldPhonesFormSchema);
|
||||
|
||||
const otherFieldsFormSchema = z.object({
|
||||
type: z.enum(
|
||||
Object.keys(
|
||||
@ -76,6 +88,8 @@ const otherFieldsFormSchema = z.object({
|
||||
FieldMetadataType.Date,
|
||||
FieldMetadataType.DateTime,
|
||||
FieldMetadataType.Number,
|
||||
FieldMetadataType.Address,
|
||||
FieldMetadataType.Phones,
|
||||
FieldMetadataType.Text,
|
||||
]),
|
||||
) as [FieldMetadataType, ...FieldMetadataType[]],
|
||||
@ -94,6 +108,8 @@ export const settingsDataModelFieldSettingsFormSchema = z.discriminatedUnion(
|
||||
multiSelectFieldFormSchema,
|
||||
numberFieldFormSchema,
|
||||
textFieldFormSchema,
|
||||
addressFieldFormSchema,
|
||||
phonesFieldFormSchema,
|
||||
otherFieldsFormSchema,
|
||||
],
|
||||
);
|
||||
@ -195,6 +211,24 @@ export const SettingsDataModelFieldSettingsFormCard = ({
|
||||
);
|
||||
}
|
||||
|
||||
if (fieldMetadataItem.type === FieldMetadataType.Address) {
|
||||
return (
|
||||
<SettingsDataModelFieldAddressSettingsFormCard
|
||||
fieldMetadataItem={fieldMetadataItem}
|
||||
objectMetadataItem={objectMetadataItem}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
if (fieldMetadataItem.type === FieldMetadataType.Phones) {
|
||||
return (
|
||||
<SettingsDataModelFieldPhonesSettingsFormCard
|
||||
fieldMetadataItem={fieldMetadataItem}
|
||||
objectMetadataItem={objectMetadataItem}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
fieldMetadataItem.type === FieldMetadataType.Select ||
|
||||
fieldMetadataItem.type === FieldMetadataType.MultiSelect
|
||||
|
||||
@ -0,0 +1,80 @@
|
||||
import { Controller, useFormContext } from 'react-hook-form';
|
||||
|
||||
import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
|
||||
import { phonesSchema as phonesFieldDefaultValueSchema } from '@/object-record/record-field/types/guards/isFieldPhonesValue';
|
||||
import { SettingsOptionCardContentSelect } from '@/settings/components/SettingsOptions/SettingsOptionCardContentSelect';
|
||||
import { useCountries } from '@/ui/input/components/internal/hooks/useCountries';
|
||||
import { IconMap } from 'twenty-ui';
|
||||
import { z } from 'zod';
|
||||
import { applySimpleQuotesToString } from '~/utils/string/applySimpleQuotesToString';
|
||||
import { stripSimpleQuotesFromString } from '~/utils/string/stripSimpleQuotesFromString';
|
||||
|
||||
type SettingsDataModelFieldPhonesFormProps = {
|
||||
disabled?: boolean;
|
||||
defaultCountryCode?: string;
|
||||
fieldMetadataItem: Pick<
|
||||
FieldMetadataItem,
|
||||
'icon' | 'label' | 'type' | 'defaultValue' | 'settings'
|
||||
>;
|
||||
};
|
||||
|
||||
export const settingsDataModelFieldPhonesFormSchema = z.object({
|
||||
defaultValue: phonesFieldDefaultValueSchema,
|
||||
});
|
||||
|
||||
export type SettingsDataModelFieldTextFormValues = z.infer<
|
||||
typeof settingsDataModelFieldPhonesFormSchema
|
||||
>;
|
||||
|
||||
export const SettingsDataModelFieldPhonesForm = ({
|
||||
disabled,
|
||||
fieldMetadataItem,
|
||||
}: SettingsDataModelFieldPhonesFormProps) => {
|
||||
const { control } = useFormContext<SettingsDataModelFieldTextFormValues>();
|
||||
|
||||
const countries = useCountries()
|
||||
.sort((a, b) => a.countryName.localeCompare(b.countryName))
|
||||
.map((country) => ({
|
||||
label: `${country.countryName} (+${country.callingCode})`,
|
||||
value: `${country.callingCode}`,
|
||||
}));
|
||||
countries.unshift({ label: 'No country', value: '' });
|
||||
const defaultDefaultValue = {
|
||||
primaryPhoneNumber: "''",
|
||||
primaryPhoneCountryCode: "''",
|
||||
additionalPhones: null,
|
||||
};
|
||||
const fieldMetadataItemDefaultValue = fieldMetadataItem?.defaultValue;
|
||||
|
||||
return (
|
||||
<Controller
|
||||
name="defaultValue"
|
||||
defaultValue={{
|
||||
...defaultDefaultValue,
|
||||
...fieldMetadataItemDefaultValue,
|
||||
}}
|
||||
control={control}
|
||||
render={({ field: { onChange, value } }) => {
|
||||
return (
|
||||
<SettingsOptionCardContentSelect<string>
|
||||
Icon={IconMap}
|
||||
dropdownId="selectDefaultCountryCode"
|
||||
title="Default Country Code"
|
||||
description="The default country code for new phone numbers."
|
||||
value={stripSimpleQuotesFromString(value?.primaryPhoneCountryCode)}
|
||||
onChange={(newPhoneCountryCode) =>
|
||||
onChange({
|
||||
...value,
|
||||
primaryPhoneCountryCode:
|
||||
applySimpleQuotesToString(newPhoneCountryCode),
|
||||
})
|
||||
}
|
||||
disabled={disabled}
|
||||
options={countries}
|
||||
fullWidth={true}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@ -0,0 +1,46 @@
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
|
||||
|
||||
import { SettingsDataModelPreviewFormCard } from '@/settings/data-model/components/SettingsDataModelPreviewFormCard';
|
||||
|
||||
import { SettingsDataModelFieldPhonesForm } from '@/settings/data-model/fields/forms/phones/components/SettingsDataModelFieldPhonesForm';
|
||||
import {
|
||||
SettingsDataModelFieldPreviewCard,
|
||||
SettingsDataModelFieldPreviewCardProps,
|
||||
} from '@/settings/data-model/fields/preview/components/SettingsDataModelFieldPreviewCard';
|
||||
|
||||
type SettingsDataModelFieldPhonesSettingsFormCardProps = {
|
||||
disabled?: boolean;
|
||||
fieldMetadataItem: Pick<
|
||||
FieldMetadataItem,
|
||||
'icon' | 'label' | 'type' | 'defaultValue'
|
||||
>;
|
||||
} & Pick<SettingsDataModelFieldPreviewCardProps, 'objectMetadataItem'>;
|
||||
|
||||
const StyledFieldPreviewCard = styled(SettingsDataModelFieldPreviewCard)`
|
||||
flex: 1 1 100%;
|
||||
`;
|
||||
|
||||
export const SettingsDataModelFieldPhonesSettingsFormCard = ({
|
||||
disabled,
|
||||
fieldMetadataItem,
|
||||
objectMetadataItem,
|
||||
}: SettingsDataModelFieldPhonesSettingsFormCardProps) => {
|
||||
return (
|
||||
<SettingsDataModelPreviewFormCard
|
||||
preview={
|
||||
<StyledFieldPreviewCard
|
||||
fieldMetadataItem={fieldMetadataItem}
|
||||
objectMetadataItem={objectMetadataItem}
|
||||
/>
|
||||
}
|
||||
form={
|
||||
<SettingsDataModelFieldPhonesForm
|
||||
disabled={disabled}
|
||||
fieldMetadataItem={fieldMetadataItem}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@ -2,9 +2,11 @@ import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSi
|
||||
import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
|
||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { useRelationFieldPreviewValue } from '@/settings/data-model/fields/preview/hooks/useRelationFieldPreviewValue';
|
||||
import { getAddressFieldPreviewValue } from '@/settings/data-model/fields/preview/utils/getAddressFieldPreviewValue';
|
||||
import { getCurrencyFieldPreviewValue } from '@/settings/data-model/fields/preview/utils/getCurrencyFieldPreviewValue';
|
||||
import { getFieldPreviewValue } from '@/settings/data-model/fields/preview/utils/getFieldPreviewValue';
|
||||
import { getMultiSelectFieldPreviewValue } from '@/settings/data-model/fields/preview/utils/getMultiSelectFieldPreviewValue';
|
||||
import { getPhonesFieldPreviewValue } from '@/settings/data-model/fields/preview/utils/getPhonesFieldPreviewValue';
|
||||
import { getSelectFieldPreviewValue } from '@/settings/data-model/fields/preview/utils/getSelectFieldPreviewValue';
|
||||
import { FieldMetadataType } from '~/generated-metadata/graphql';
|
||||
|
||||
@ -45,6 +47,10 @@ export const useFieldPreviewValue = ({
|
||||
return getSelectFieldPreviewValue({ fieldMetadataItem });
|
||||
case FieldMetadataType.MultiSelect:
|
||||
return getMultiSelectFieldPreviewValue({ fieldMetadataItem });
|
||||
case FieldMetadataType.Address:
|
||||
return getAddressFieldPreviewValue({ fieldMetadataItem });
|
||||
case FieldMetadataType.Phones:
|
||||
return getPhonesFieldPreviewValue({ fieldMetadataItem });
|
||||
default:
|
||||
return getFieldPreviewValue({ fieldMetadataItem });
|
||||
}
|
||||
|
||||
@ -0,0 +1,34 @@
|
||||
import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
|
||||
import { FieldAddressValue } from '@/object-record/record-field/types/FieldMetadata';
|
||||
import { getSettingsFieldTypeConfig } from '@/settings/data-model/utils/getSettingsFieldTypeConfig';
|
||||
import { FieldMetadataType } from '~/generated-metadata/graphql';
|
||||
import { stripSimpleQuotesFromString } from '~/utils/string/stripSimpleQuotesFromString';
|
||||
|
||||
export const getAddressFieldPreviewValue = ({
|
||||
fieldMetadataItem,
|
||||
}: {
|
||||
fieldMetadataItem: Pick<
|
||||
FieldMetadataItem,
|
||||
'defaultValue' | 'options' | 'type'
|
||||
>;
|
||||
}): FieldAddressValue | null => {
|
||||
if (fieldMetadataItem.type !== FieldMetadataType.Address) return null;
|
||||
|
||||
const addressFieldTypeConfig = getSettingsFieldTypeConfig(
|
||||
FieldMetadataType.Address,
|
||||
);
|
||||
|
||||
const placeholderDefaultValue = addressFieldTypeConfig.exampleValue;
|
||||
|
||||
const addressCountry =
|
||||
fieldMetadataItem.defaultValue?.addressCountry &&
|
||||
fieldMetadataItem.defaultValue.addressCountry !== ''
|
||||
? stripSimpleQuotesFromString(
|
||||
fieldMetadataItem.defaultValue?.addressCountry,
|
||||
)
|
||||
: null;
|
||||
return {
|
||||
...placeholderDefaultValue,
|
||||
addressCountry: addressCountry,
|
||||
};
|
||||
};
|
||||
@ -0,0 +1,33 @@
|
||||
import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
|
||||
import { FieldPhonesValue } from '@/object-record/record-field/types/FieldMetadata';
|
||||
import { getSettingsFieldTypeConfig } from '@/settings/data-model/utils/getSettingsFieldTypeConfig';
|
||||
import { FieldMetadataType } from '~/generated-metadata/graphql';
|
||||
import { stripSimpleQuotesFromString } from '~/utils/string/stripSimpleQuotesFromString';
|
||||
|
||||
export const getPhonesFieldPreviewValue = ({
|
||||
fieldMetadataItem,
|
||||
}: {
|
||||
fieldMetadataItem: Pick<
|
||||
FieldMetadataItem,
|
||||
'defaultValue' | 'options' | 'type'
|
||||
>;
|
||||
}): FieldPhonesValue | null => {
|
||||
if (fieldMetadataItem.type !== FieldMetadataType.Phones) return null;
|
||||
|
||||
const phonesFieldTypeConfig = getSettingsFieldTypeConfig(
|
||||
FieldMetadataType.Phones,
|
||||
);
|
||||
|
||||
const placeholderDefaultValue = phonesFieldTypeConfig.exampleValue;
|
||||
const primaryPhoneCountryCode =
|
||||
fieldMetadataItem.defaultValue?.primaryPhoneCountryCode &&
|
||||
fieldMetadataItem.defaultValue.primaryPhoneCountryCode !== ''
|
||||
? `+${stripSimpleQuotesFromString(
|
||||
fieldMetadataItem.defaultValue?.primaryPhoneCountryCode,
|
||||
)}`
|
||||
: null;
|
||||
return {
|
||||
...placeholderDefaultValue,
|
||||
primaryPhoneCountryCode,
|
||||
};
|
||||
};
|
||||
Reference in New Issue
Block a user