@ -88,6 +88,7 @@ const mocks = [
|
||||
phones {
|
||||
primaryPhoneNumber
|
||||
primaryPhoneCountryCode
|
||||
primaryPhoneCallingCode
|
||||
additionalPhones
|
||||
}
|
||||
position
|
||||
@ -95,6 +96,7 @@ const mocks = [
|
||||
whatsapp {
|
||||
primaryPhoneNumber
|
||||
primaryPhoneCountryCode
|
||||
primaryPhoneCallingCode
|
||||
additionalPhones
|
||||
}
|
||||
workPreference
|
||||
@ -246,6 +248,7 @@ const mocks = [
|
||||
phones {
|
||||
primaryPhoneNumber
|
||||
primaryPhoneCountryCode
|
||||
primaryPhoneCallingCode
|
||||
additionalPhones
|
||||
}
|
||||
position
|
||||
@ -253,6 +256,7 @@ const mocks = [
|
||||
whatsapp {
|
||||
primaryPhoneNumber
|
||||
primaryPhoneCountryCode
|
||||
primaryPhoneCallingCode
|
||||
additionalPhones
|
||||
}
|
||||
workPreference
|
||||
|
||||
@ -246,6 +246,7 @@ mutation UpdateOneFavorite(
|
||||
phones {
|
||||
primaryPhoneNumber
|
||||
primaryPhoneCountryCode
|
||||
primaryPhoneCallingCode
|
||||
additionalPhones
|
||||
}
|
||||
position
|
||||
@ -253,6 +254,7 @@ mutation UpdateOneFavorite(
|
||||
whatsapp {
|
||||
primaryPhoneNumber
|
||||
primaryPhoneCountryCode
|
||||
primaryPhoneCallingCode
|
||||
additionalPhones
|
||||
}
|
||||
workPreference
|
||||
@ -532,6 +534,7 @@ export const mocks = [
|
||||
phones {
|
||||
primaryPhoneNumber
|
||||
primaryPhoneCountryCode
|
||||
primaryPhoneCallingCode
|
||||
additionalPhones
|
||||
}
|
||||
position
|
||||
@ -539,6 +542,7 @@ export const mocks = [
|
||||
whatsapp {
|
||||
primaryPhoneNumber
|
||||
primaryPhoneCountryCode
|
||||
primaryPhoneCallingCode
|
||||
additionalPhones
|
||||
}
|
||||
workPreference
|
||||
|
||||
@ -198,6 +198,7 @@ phone
|
||||
{
|
||||
primaryPhoneNumber
|
||||
primaryPhoneCountryCode
|
||||
primaryPhoneCallingCode
|
||||
}
|
||||
linkedinLink
|
||||
{
|
||||
|
||||
@ -48,6 +48,7 @@ describe('mapObjectMetadataToGraphQLQuery', () => {
|
||||
{
|
||||
primaryPhoneNumber
|
||||
primaryPhoneCountryCode
|
||||
primaryPhoneCallingCode
|
||||
}
|
||||
createdAt
|
||||
avatarUrl
|
||||
|
||||
@ -157,6 +157,7 @@ ${mapObjectMetadataToGraphQLQuery({
|
||||
{
|
||||
primaryPhoneNumber
|
||||
primaryPhoneCountryCode
|
||||
primaryPhoneCallingCode
|
||||
additionalPhones
|
||||
}`;
|
||||
}
|
||||
|
||||
@ -30,6 +30,7 @@ export const PERSON_FRAGMENT_WITH_DEPTH_ZERO_RELATIONS = `
|
||||
phones {
|
||||
primaryPhoneNumber
|
||||
primaryPhoneCountryCode
|
||||
primaryPhoneCallingCode
|
||||
additionalPhones
|
||||
}
|
||||
position
|
||||
@ -37,6 +38,7 @@ export const PERSON_FRAGMENT_WITH_DEPTH_ZERO_RELATIONS = `
|
||||
whatsapp {
|
||||
primaryPhoneNumber
|
||||
primaryPhoneCountryCode
|
||||
primaryPhoneCallingCode
|
||||
additionalPhones
|
||||
}
|
||||
workPreference
|
||||
@ -229,6 +231,7 @@ export const PERSON_FRAGMENT_WITH_DEPTH_ONE_RELATIONS = `
|
||||
phones {
|
||||
primaryPhoneNumber
|
||||
primaryPhoneCountryCode
|
||||
primaryPhoneCallingCode
|
||||
additionalPhones
|
||||
}
|
||||
pointOfContactForOpportunities {
|
||||
@ -305,6 +308,7 @@ export const PERSON_FRAGMENT_WITH_DEPTH_ONE_RELATIONS = `
|
||||
whatsapp {
|
||||
primaryPhoneNumber
|
||||
primaryPhoneCountryCode
|
||||
primaryPhoneCallingCode
|
||||
additionalPhones
|
||||
}
|
||||
workPreference
|
||||
|
||||
@ -38,6 +38,7 @@ export const responseData = {
|
||||
},
|
||||
phones: {
|
||||
primaryPhoneCountryCode: '',
|
||||
primaryPhoneCallingCode: '',
|
||||
primaryPhoneNumber: '',
|
||||
},
|
||||
linkedinLink: {
|
||||
|
||||
@ -43,6 +43,7 @@ export const responseData = {
|
||||
},
|
||||
phones: {
|
||||
primaryPhoneCountryCode: '',
|
||||
primaryPhoneCallingCode: '',
|
||||
primaryPhoneNumber: '',
|
||||
},
|
||||
linkedinLink: {
|
||||
|
||||
@ -178,6 +178,7 @@ const mocks: MockedResponse[] = [
|
||||
phones {
|
||||
primaryPhoneNumber
|
||||
primaryPhoneCountryCode
|
||||
primaryPhoneCallingCode
|
||||
additionalPhones
|
||||
}
|
||||
position
|
||||
@ -185,6 +186,7 @@ const mocks: MockedResponse[] = [
|
||||
whatsapp {
|
||||
primaryPhoneNumber
|
||||
primaryPhoneCountryCode
|
||||
primaryPhoneCallingCode
|
||||
additionalPhones
|
||||
}
|
||||
workPreference
|
||||
@ -332,6 +334,7 @@ const mocks: MockedResponse[] = [
|
||||
phones {
|
||||
primaryPhoneNumber
|
||||
primaryPhoneCountryCode
|
||||
primaryPhoneCallingCode
|
||||
additionalPhones
|
||||
}
|
||||
position
|
||||
@ -339,6 +342,7 @@ const mocks: MockedResponse[] = [
|
||||
whatsapp {
|
||||
primaryPhoneNumber
|
||||
primaryPhoneCountryCode
|
||||
primaryPhoneCallingCode
|
||||
additionalPhones
|
||||
}
|
||||
workPreference
|
||||
|
||||
@ -39,7 +39,8 @@ const mocks: MockedResponse[] = [
|
||||
input: {
|
||||
phones: {
|
||||
primaryPhoneNumber: '123 456',
|
||||
primaryPhoneCountryCode: '+1',
|
||||
primaryPhoneCountryCode: 'US',
|
||||
primaryPhoneCallingCode: '+1',
|
||||
additionalPhones: [],
|
||||
},
|
||||
},
|
||||
@ -134,7 +135,8 @@ describe('usePersistField', () => {
|
||||
act(() => {
|
||||
result.current.persistField({
|
||||
primaryPhoneNumber: '123 456',
|
||||
primaryPhoneCountryCode: '+1',
|
||||
primaryPhoneCountryCode: 'US',
|
||||
primaryPhoneCallingCode: '+1',
|
||||
additionalPhones: [],
|
||||
});
|
||||
});
|
||||
|
||||
@ -208,6 +208,7 @@ const mocks: MockedResponse[] = [
|
||||
phones {
|
||||
primaryPhoneNumber
|
||||
primaryPhoneCountryCode
|
||||
primaryPhoneCallingCode
|
||||
additionalPhones
|
||||
}
|
||||
position
|
||||
@ -215,6 +216,7 @@ const mocks: MockedResponse[] = [
|
||||
whatsapp {
|
||||
primaryPhoneNumber
|
||||
primaryPhoneCountryCode
|
||||
primaryPhoneCallingCode
|
||||
additionalPhones
|
||||
}
|
||||
workPreference
|
||||
|
||||
@ -9,12 +9,11 @@ import { TEXT_INPUT_STYLE } from 'twenty-ui';
|
||||
import { MultiItemFieldInput } from './MultiItemFieldInput';
|
||||
|
||||
import { createPhonesFromFieldValue } from '@/object-record/record-field/meta-types/input/utils/phonesUtils';
|
||||
import { useCountries } from '@/ui/input/components/internal/hooks/useCountries';
|
||||
import { PhoneCountryPickerDropdownButton } from '@/ui/input/components/internal/phone/components/PhoneCountryPickerDropdownButton';
|
||||
import { FieldMetadataType } from '~/generated-metadata/graphql';
|
||||
import { stripSimpleQuotesFromString } from '~/utils/string/stripSimpleQuotesFromString';
|
||||
|
||||
export const DEFAULT_PHONE_COUNTRY_CODE = '1';
|
||||
export const DEFAULT_PHONE_CALLING_CODE = '1';
|
||||
|
||||
const StyledCustomPhoneInput = styled(ReactPhoneNumberInput)`
|
||||
font-family: ${({ theme }) => theme.font.family};
|
||||
@ -60,22 +59,22 @@ export const PhonesFieldInput = ({
|
||||
|
||||
const phones = createPhonesFromFieldValue(fieldValue);
|
||||
|
||||
const defaultCallingCode =
|
||||
stripSimpleQuotesFromString(
|
||||
fieldDefinition?.defaultValue?.primaryPhoneCountryCode,
|
||||
) ?? DEFAULT_PHONE_COUNTRY_CODE;
|
||||
// TODO : improve once we store the real country code
|
||||
const defaultCountry = useCountries().find(
|
||||
(obj) => `+${obj.callingCode}` === defaultCallingCode,
|
||||
)?.countryCode;
|
||||
const defaultCountry = stripSimpleQuotesFromString(
|
||||
fieldDefinition?.defaultValue?.primaryPhoneCountryCode,
|
||||
);
|
||||
|
||||
const handlePersistPhones = (
|
||||
updatedPhones: { number: string; callingCode: string }[],
|
||||
updatedPhones: {
|
||||
number: string;
|
||||
countryCode: string;
|
||||
callingCode: string;
|
||||
}[],
|
||||
) => {
|
||||
const [nextPrimaryPhone, ...nextAdditionalPhones] = updatedPhones;
|
||||
persistPhonesField({
|
||||
primaryPhoneNumber: nextPrimaryPhone?.number ?? '',
|
||||
primaryPhoneCountryCode: nextPrimaryPhone?.callingCode ?? '',
|
||||
primaryPhoneCountryCode: nextPrimaryPhone?.countryCode ?? '',
|
||||
primaryPhoneCallingCode: nextPrimaryPhone?.callingCode ?? '',
|
||||
additionalPhones: nextAdditionalPhones,
|
||||
});
|
||||
};
|
||||
@ -96,11 +95,13 @@ export const PhonesFieldInput = ({
|
||||
return {
|
||||
number: phone.nationalNumber,
|
||||
callingCode: `+${phone.countryCallingCode}`,
|
||||
countryCode: phone.country as string,
|
||||
};
|
||||
}
|
||||
return {
|
||||
number: '',
|
||||
callingCode: '',
|
||||
countryCode: '',
|
||||
};
|
||||
}}
|
||||
renderItem={({
|
||||
|
||||
@ -19,7 +19,8 @@ describe('createPhonesFromFieldValue test suite', () => {
|
||||
it('should return an array with primary phone number if it is defined', () => {
|
||||
const fieldValue: FieldPhonesValue = {
|
||||
primaryPhoneNumber: '123456789',
|
||||
primaryPhoneCountryCode: '+1',
|
||||
primaryPhoneCountryCode: 'US',
|
||||
primaryPhoneCallingCode: '+1',
|
||||
additionalPhones: [],
|
||||
};
|
||||
const result = createPhonesFromFieldValue(fieldValue);
|
||||
@ -27,6 +28,24 @@ describe('createPhonesFromFieldValue test suite', () => {
|
||||
{
|
||||
number: '123456789',
|
||||
callingCode: '+1',
|
||||
countryCode: 'US',
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should return an array with primary phone number if it is defined, even with incorrect callingCode', () => {
|
||||
const fieldValue: FieldPhonesValue = {
|
||||
primaryPhoneNumber: '123456789',
|
||||
primaryPhoneCountryCode: 'US',
|
||||
primaryPhoneCallingCode: '+33',
|
||||
additionalPhones: [],
|
||||
};
|
||||
const result = createPhonesFromFieldValue(fieldValue);
|
||||
expect(result).toEqual([
|
||||
{
|
||||
number: '123456789',
|
||||
callingCode: '+33',
|
||||
countryCode: 'US',
|
||||
},
|
||||
]);
|
||||
});
|
||||
@ -34,10 +53,11 @@ describe('createPhonesFromFieldValue test suite', () => {
|
||||
it('should return an array with both primary and additional phones if they are defined', () => {
|
||||
const fieldValue: FieldPhonesValue = {
|
||||
primaryPhoneNumber: '123456789',
|
||||
primaryPhoneCountryCode: '+1',
|
||||
primaryPhoneCountryCode: 'US',
|
||||
primaryPhoneCallingCode: '+1',
|
||||
additionalPhones: [
|
||||
{ number: '987654321', callingCode: '+44' },
|
||||
{ number: '555555555', callingCode: '+33' },
|
||||
{ number: '987654321', callingCode: '+44', countryCode: 'GB' },
|
||||
{ number: '555555555', callingCode: '+33', countryCode: 'FR' },
|
||||
],
|
||||
};
|
||||
const result = createPhonesFromFieldValue(fieldValue);
|
||||
@ -45,9 +65,10 @@ describe('createPhonesFromFieldValue test suite', () => {
|
||||
{
|
||||
number: '123456789',
|
||||
callingCode: '+1',
|
||||
countryCode: 'US',
|
||||
},
|
||||
{ number: '987654321', callingCode: '+44' },
|
||||
{ number: '555555555', callingCode: '+33' },
|
||||
{ number: '987654321', callingCode: '+44', countryCode: 'GB' },
|
||||
{ number: '555555555', callingCode: '+33', countryCode: 'FR' },
|
||||
]);
|
||||
});
|
||||
|
||||
@ -56,14 +77,14 @@ describe('createPhonesFromFieldValue test suite', () => {
|
||||
primaryPhoneNumber: '',
|
||||
primaryPhoneCountryCode: '',
|
||||
additionalPhones: [
|
||||
{ number: '987654321', callingCode: '+44' },
|
||||
{ number: '555555555', callingCode: '+33' },
|
||||
{ number: '987654321', callingCode: '+44', countryCode: 'GB' },
|
||||
{ number: '555555555', callingCode: '+33', countryCode: 'FR' },
|
||||
],
|
||||
};
|
||||
const result = createPhonesFromFieldValue(fieldValue);
|
||||
expect(result).toEqual([
|
||||
{ number: '987654321', callingCode: '+44' },
|
||||
{ number: '555555555', callingCode: '+33' },
|
||||
{ number: '987654321', callingCode: '+44', countryCode: 'GB' },
|
||||
{ number: '555555555', callingCode: '+33', countryCode: 'FR' },
|
||||
]);
|
||||
});
|
||||
|
||||
@ -72,22 +93,34 @@ describe('createPhonesFromFieldValue test suite', () => {
|
||||
primaryPhoneNumber: ' ',
|
||||
primaryPhoneCountryCode: '',
|
||||
additionalPhones: [
|
||||
{ number: '987654321', callingCode: '+44' },
|
||||
{ number: '555555555', callingCode: '+33' },
|
||||
{ number: '987654321', callingCode: '+44', countryCode: 'GB' },
|
||||
{ number: '555555555', callingCode: '+33', countryCode: 'FR' },
|
||||
],
|
||||
};
|
||||
const result = createPhonesFromFieldValue(fieldValue);
|
||||
expect(result).toEqual([
|
||||
{ number: ' ', callingCode: '' },
|
||||
{ number: '987654321', callingCode: '+44' },
|
||||
{ number: '555555555', callingCode: '+33' },
|
||||
{ number: ' ', callingCode: '', countryCode: '' },
|
||||
{ number: '987654321', callingCode: '+44', countryCode: 'GB' },
|
||||
{ number: '555555555', callingCode: '+33', countryCode: 'FR' },
|
||||
]);
|
||||
});
|
||||
|
||||
it('should return an empty array if only country code is defined', () => {
|
||||
it('should return an empty array if only country and calling code are defined', () => {
|
||||
const fieldValue: FieldPhonesValue = {
|
||||
primaryPhoneNumber: '',
|
||||
primaryPhoneCountryCode: '+33',
|
||||
primaryPhoneCountryCode: 'FR',
|
||||
primaryPhoneCallingCode: '+33',
|
||||
additionalPhones: [],
|
||||
};
|
||||
const result = createPhonesFromFieldValue(fieldValue);
|
||||
expect(result).toEqual([]);
|
||||
});
|
||||
|
||||
it('should return an empty array if only calling code is defined', () => {
|
||||
const fieldValue: FieldPhonesValue = {
|
||||
primaryPhoneNumber: '',
|
||||
primaryPhoneCallingCode: '+33',
|
||||
primaryPhoneCountryCode: '',
|
||||
additionalPhones: [],
|
||||
};
|
||||
const result = createPhonesFromFieldValue(fieldValue);
|
||||
|
||||
@ -8,7 +8,10 @@ export const createPhonesFromFieldValue = (fieldValue: FieldPhonesValue) => {
|
||||
fieldValue.primaryPhoneNumber
|
||||
? {
|
||||
number: fieldValue.primaryPhoneNumber,
|
||||
callingCode: fieldValue.primaryPhoneCountryCode,
|
||||
callingCode: fieldValue.primaryPhoneCallingCode
|
||||
? fieldValue.primaryPhoneCallingCode
|
||||
: fieldValue.primaryPhoneCountryCode,
|
||||
countryCode: fieldValue.primaryPhoneCountryCode,
|
||||
}
|
||||
: null,
|
||||
...(fieldValue.additionalPhones ?? []),
|
||||
|
||||
@ -27,6 +27,7 @@ export type FieldDateTimeDraftValue = string;
|
||||
export type FieldPhonesDraftValue = {
|
||||
primaryPhoneNumber: string;
|
||||
primaryPhoneCountryCode: string;
|
||||
primaryPhoneCallingCode: string;
|
||||
additionalPhones?: PhoneRecord[] | null;
|
||||
};
|
||||
export type FieldEmailsDraftValue = {
|
||||
|
||||
@ -265,10 +265,15 @@ export type FieldActorValue = {
|
||||
|
||||
export type FieldArrayValue = string[];
|
||||
|
||||
export type PhoneRecord = { number: string; callingCode: string };
|
||||
export type PhoneRecord = {
|
||||
number: string;
|
||||
callingCode: string;
|
||||
countryCode: string;
|
||||
};
|
||||
|
||||
export type FieldPhonesValue = {
|
||||
primaryPhoneNumber: string;
|
||||
primaryPhoneCountryCode: string;
|
||||
primaryPhoneCallingCode?: string;
|
||||
additionalPhones?: PhoneRecord[] | null;
|
||||
};
|
||||
|
||||
@ -5,8 +5,15 @@ import { FieldPhonesValue } from '../FieldMetadata';
|
||||
export const phonesSchema = z.object({
|
||||
primaryPhoneNumber: z.string(),
|
||||
primaryPhoneCountryCode: z.string(),
|
||||
primaryPhoneCallingCode: z.string(),
|
||||
additionalPhones: z
|
||||
.array(z.object({ number: z.string(), callingCode: z.string() }))
|
||||
.array(
|
||||
z.object({
|
||||
number: z.string(),
|
||||
callingCode: z.string(),
|
||||
countryCode: z.string(),
|
||||
}),
|
||||
)
|
||||
.nullable(),
|
||||
}) satisfies z.ZodType<FieldPhonesValue>;
|
||||
|
||||
|
||||
@ -71,6 +71,9 @@ export const computeDraftValueFromFieldValue = <FieldValue>({
|
||||
primaryPhoneCountryCode: stripSimpleQuotesFromString(
|
||||
fieldDefinition?.defaultValue?.primaryPhoneCountryCode,
|
||||
),
|
||||
primaryPhoneCallingCode: stripSimpleQuotesFromString(
|
||||
fieldDefinition?.defaultValue?.primaryPhoneCallingCode,
|
||||
),
|
||||
} as unknown as FieldInputDraftValue<FieldValue>;
|
||||
}
|
||||
|
||||
|
||||
@ -21,6 +21,7 @@ const mockPerson = {
|
||||
whatsapp: {
|
||||
primaryPhoneNumber: '+1',
|
||||
primaryPhoneCountryCode: '234-567-890',
|
||||
primaryPhoneCallingCode: '+33',
|
||||
additionalPhones: [],
|
||||
},
|
||||
linkedinLink: {
|
||||
|
||||
@ -663,7 +663,8 @@ export const mockPerformance = {
|
||||
id: '20202020-2d40-4e49-8df4-9c6a049191df',
|
||||
email: 'lorie.vladim@google.com',
|
||||
phones: {
|
||||
primaryPhoneCountryCode: '+33',
|
||||
primaryPhoneCountryCode: 'FR',
|
||||
primaryPhoneCallingCode: '+33',
|
||||
primaryPhoneNumber: '788901235',
|
||||
},
|
||||
linkedinLink: {
|
||||
|
||||
@ -207,6 +207,7 @@ const companyMocks = [
|
||||
phones {
|
||||
primaryPhoneNumber
|
||||
primaryPhoneCountryCode
|
||||
primaryPhoneCallingCode
|
||||
additionalPhones
|
||||
}
|
||||
position
|
||||
@ -214,6 +215,7 @@ const companyMocks = [
|
||||
whatsapp {
|
||||
primaryPhoneNumber
|
||||
primaryPhoneCountryCode
|
||||
primaryPhoneCallingCode
|
||||
additionalPhones
|
||||
}
|
||||
workPreference
|
||||
|
||||
@ -95,6 +95,7 @@ export const generateEmptyFieldValue = (
|
||||
return {
|
||||
primaryPhoneNumber: '',
|
||||
primaryPhoneCountryCode: '',
|
||||
primaryPhoneCallingCode: '',
|
||||
additionalPhones: null,
|
||||
};
|
||||
}
|
||||
|
||||
@ -91,7 +91,9 @@ export const SETTINGS_COMPOSITE_FIELD_TYPE_CONFIGS = {
|
||||
exampleValue: {
|
||||
primaryPhoneNumber: '234-567-890',
|
||||
primaryPhoneCountryCode: '+1',
|
||||
additionalPhones: [{ number: '234-567-890', callingCode: '+1' }],
|
||||
additionalPhones: [
|
||||
{ number: '234-567-890', callingCode: '+1', countryCode: 'US' },
|
||||
],
|
||||
},
|
||||
subFields: [
|
||||
'primaryPhoneNumber',
|
||||
@ -102,6 +104,7 @@ export const SETTINGS_COMPOSITE_FIELD_TYPE_CONFIGS = {
|
||||
labelBySubField: {
|
||||
primaryPhoneNumber: 'Primary Phone Number',
|
||||
primaryPhoneCountryCode: 'Primary Phone Country Code',
|
||||
primaryPhoneCallingCode: 'Primary Phone Calling Code',
|
||||
additionalPhones: 'Additional Phones',
|
||||
},
|
||||
category: 'Basic',
|
||||
|
||||
@ -3,8 +3,10 @@ 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 { countryCodeToCallingCode } from '@/settings/data-model/fields/preview/utils/getPhonesFieldPreviewValue';
|
||||
import { useCountries } from '@/ui/input/components/internal/hooks/useCountries';
|
||||
import { Select } from '@/ui/input/components/Select';
|
||||
import { CountryCode } from 'libphonenumber-js';
|
||||
import { IconMap } from 'twenty-ui';
|
||||
import { z } from 'zod';
|
||||
import { applySimpleQuotesToString } from '~/utils/string/applySimpleQuotesToString';
|
||||
@ -27,22 +29,27 @@ export type SettingsDataModelFieldTextFormValues = z.infer<
|
||||
typeof settingsDataModelFieldPhonesFormSchema
|
||||
>;
|
||||
|
||||
export type CountryCodeOrEmpty = CountryCode | '';
|
||||
|
||||
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 countries = [
|
||||
{ label: 'No country', value: '' },
|
||||
...useCountries()
|
||||
.sort((a, b) => a.countryName.localeCompare(b.countryName))
|
||||
.map((country) => ({
|
||||
label: `${country.countryName} (+${country.callingCode})`,
|
||||
value: country.countryCode as CountryCodeOrEmpty,
|
||||
})),
|
||||
];
|
||||
const defaultDefaultValue = {
|
||||
primaryPhoneNumber: "''",
|
||||
primaryPhoneCountryCode: "''",
|
||||
primaryPhoneCallingCode: "''",
|
||||
additionalPhones: null,
|
||||
};
|
||||
const fieldMetadataItemDefaultValue = fieldMetadataItem?.defaultValue;
|
||||
@ -73,6 +80,9 @@ export const SettingsDataModelFieldPhonesForm = ({
|
||||
...value,
|
||||
primaryPhoneCountryCode:
|
||||
applySimpleQuotesToString(newPhoneCountryCode),
|
||||
primaryPhoneCallingCode: applySimpleQuotesToString(
|
||||
countryCodeToCallingCode(newPhoneCountryCode),
|
||||
),
|
||||
})
|
||||
}
|
||||
disabled={disabled}
|
||||
|
||||
@ -1,9 +1,29 @@
|
||||
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 {
|
||||
CountryCode,
|
||||
getCountries,
|
||||
getCountryCallingCode,
|
||||
} from 'libphonenumber-js';
|
||||
import { FieldMetadataType } from '~/generated-metadata/graphql';
|
||||
import { stripSimpleQuotesFromString } from '~/utils/string/stripSimpleQuotesFromString';
|
||||
|
||||
const isStrCountryCodeGuard = (str: string): str is CountryCode => {
|
||||
return getCountries().includes(str as CountryCode);
|
||||
};
|
||||
|
||||
export const countryCodeToCallingCode = (countryCode: string): string => {
|
||||
if (!countryCode || !isStrCountryCodeGuard(countryCode)) {
|
||||
return `+${DEFAULT_PHONE_CALLING_CODE}`;
|
||||
}
|
||||
|
||||
const callingCode = getCountryCallingCode(countryCode);
|
||||
|
||||
return callingCode ? `+${callingCode}` : `+${DEFAULT_PHONE_CALLING_CODE}`;
|
||||
};
|
||||
|
||||
export const getPhonesFieldPreviewValue = ({
|
||||
fieldMetadataItem,
|
||||
}: {
|
||||
@ -26,8 +46,16 @@ export const getPhonesFieldPreviewValue = ({
|
||||
fieldMetadataItem.defaultValue?.primaryPhoneCountryCode,
|
||||
)
|
||||
: null;
|
||||
const primaryPhoneCallingCode =
|
||||
fieldMetadataItem.defaultValue?.primaryPhoneCallingCode &&
|
||||
fieldMetadataItem.defaultValue.primaryPhoneCallingCode !== ''
|
||||
? stripSimpleQuotesFromString(
|
||||
fieldMetadataItem.defaultValue?.primaryPhoneCallingCode,
|
||||
)
|
||||
: null;
|
||||
return {
|
||||
...placeholderDefaultValue,
|
||||
primaryPhoneCountryCode,
|
||||
primaryPhoneCallingCode,
|
||||
};
|
||||
};
|
||||
|
||||
@ -5,6 +5,7 @@ import { RoundedLink, THEME_COMMON } from 'twenty-ui';
|
||||
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 { parsePhoneNumber } from 'libphonenumber-js';
|
||||
import { isDefined } from '~/utils/isDefined';
|
||||
import { logError } from '~/utils/logError';
|
||||
@ -36,7 +37,10 @@ export const PhonesDisplay = ({ value, isFocused }: PhonesDisplayProps) => {
|
||||
value?.primaryPhoneNumber
|
||||
? {
|
||||
number: value.primaryPhoneNumber,
|
||||
callingCode: value.primaryPhoneCountryCode,
|
||||
callingCode:
|
||||
value.primaryPhoneCallingCode ||
|
||||
value.primaryPhoneCountryCode ||
|
||||
`+${DEFAULT_PHONE_CALLING_CODE}`,
|
||||
}
|
||||
: null,
|
||||
...parseAdditionalPhones(value?.additionalPhones),
|
||||
@ -50,11 +54,11 @@ export const PhonesDisplay = ({ value, isFocused }: PhonesDisplayProps) => {
|
||||
}),
|
||||
[
|
||||
value?.primaryPhoneNumber,
|
||||
value?.primaryPhoneCallingCode,
|
||||
value?.primaryPhoneCountryCode,
|
||||
value?.additionalPhones,
|
||||
],
|
||||
);
|
||||
|
||||
const parsePhoneNumberOrReturnInvalidValue = (number: string) => {
|
||||
try {
|
||||
return { parsedPhone: parsePhoneNumber(number) };
|
||||
|
||||
@ -19461,7 +19461,8 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery =
|
||||
"defaultValue": {
|
||||
"additionalPhones": null,
|
||||
"primaryPhoneNumber": "''",
|
||||
"primaryPhoneCountryCode": "''"
|
||||
"primaryPhoneCountryCode": "''",
|
||||
"primaryPhoneCallingCode": "''"
|
||||
},
|
||||
"options": null,
|
||||
"isLabelSyncedWithName": false,
|
||||
@ -19740,7 +19741,8 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery =
|
||||
{
|
||||
"additionalPhones": {},
|
||||
"primaryPhoneNumber": "",
|
||||
"primaryPhoneCountryCode": ""
|
||||
"primaryPhoneCountryCode": "",
|
||||
"primaryPhoneCallingCode": ""
|
||||
}
|
||||
],
|
||||
"options": null,
|
||||
|
||||
@ -47,7 +47,8 @@ export const peopleQueryResult: { people: RecordGqlConnection } = {
|
||||
city: 'ASd',
|
||||
phones: {
|
||||
primaryPhoneNumber: '781234562',
|
||||
primaryPhoneCountryCode: '+33',
|
||||
primaryPhoneCountryCode: 'FR',
|
||||
primaryPhoneCallingCode: '+33',
|
||||
},
|
||||
id: 'da3c2c4b-da01-4b81-9734-226069eb4cd0',
|
||||
jobTitle: '',
|
||||
@ -177,7 +178,8 @@ export const peopleQueryResult: { people: RecordGqlConnection } = {
|
||||
city: 'Seattle',
|
||||
phones: {
|
||||
primaryPhoneNumber: '781234562',
|
||||
primaryPhoneCountryCode: '+33',
|
||||
primaryPhoneCountryCode: 'FR',
|
||||
primaryPhoneCallingCode: '+33',
|
||||
},
|
||||
id: '20202020-1c0e-494c-a1b6-85b1c6fefaa5',
|
||||
jobTitle: '',
|
||||
@ -307,7 +309,8 @@ export const peopleQueryResult: { people: RecordGqlConnection } = {
|
||||
city: 'Los Angeles',
|
||||
phones: {
|
||||
primaryPhoneNumber: '781234576',
|
||||
primaryPhoneCountryCode: '+33',
|
||||
primaryPhoneCountryCode: 'FR',
|
||||
primaryPhoneCallingCode: '+33',
|
||||
},
|
||||
id: '20202020-ac73-4797-824e-87a1f5aea9e0',
|
||||
jobTitle: '',
|
||||
@ -406,7 +409,8 @@ export const peopleQueryResult: { people: RecordGqlConnection } = {
|
||||
city: 'Seattle',
|
||||
phones: {
|
||||
primaryPhoneNumber: '781234545',
|
||||
primaryPhoneCountryCode: '+33',
|
||||
primaryPhoneCountryCode: 'FR',
|
||||
primaryPhoneCallingCode: '+33',
|
||||
},
|
||||
id: '20202020-f517-42fd-80ae-14173b3b70ae',
|
||||
jobTitle: '',
|
||||
@ -505,7 +509,8 @@ export const peopleQueryResult: { people: RecordGqlConnection } = {
|
||||
city: 'Los Angeles',
|
||||
phones: {
|
||||
primaryPhoneNumber: '781234587',
|
||||
primaryPhoneCountryCode: '+33',
|
||||
primaryPhoneCountryCode: 'FR',
|
||||
primaryPhoneCallingCode: '+33',
|
||||
},
|
||||
id: '20202020-eee1-4690-ad2c-8619e5b56a2e',
|
||||
jobTitle: '',
|
||||
@ -604,7 +609,8 @@ export const peopleQueryResult: { people: RecordGqlConnection } = {
|
||||
city: 'Seattle',
|
||||
phones: {
|
||||
primaryPhoneNumber: '781234599',
|
||||
primaryPhoneCountryCode: '+33',
|
||||
primaryPhoneCountryCode: 'FR',
|
||||
primaryPhoneCallingCode: '+33',
|
||||
},
|
||||
id: '20202020-6784-4449-afdf-dc62cb8702f2',
|
||||
jobTitle: '',
|
||||
@ -703,7 +709,8 @@ export const peopleQueryResult: { people: RecordGqlConnection } = {
|
||||
city: 'New York',
|
||||
phones: {
|
||||
primaryPhoneNumber: '781234572',
|
||||
primaryPhoneCountryCode: '+33',
|
||||
primaryPhoneCountryCode: 'FR',
|
||||
primaryPhoneCallingCode: '+33',
|
||||
},
|
||||
id: '20202020-490f-4466-8391-733cfd66a0c8',
|
||||
jobTitle: '',
|
||||
@ -802,7 +809,8 @@ export const peopleQueryResult: { people: RecordGqlConnection } = {
|
||||
city: 'Seattle',
|
||||
phones: {
|
||||
primaryPhoneNumber: '781234582',
|
||||
primaryPhoneCountryCode: '+33',
|
||||
primaryPhoneCountryCode: 'FR',
|
||||
primaryPhoneCallingCode: '+33',
|
||||
},
|
||||
id: '20202020-80f1-4dff-b570-a74942528de3',
|
||||
jobTitle: '',
|
||||
@ -901,7 +909,8 @@ export const peopleQueryResult: { people: RecordGqlConnection } = {
|
||||
city: 'New York',
|
||||
phones: {
|
||||
primaryPhoneNumber: '781234569',
|
||||
primaryPhoneCountryCode: '+33',
|
||||
primaryPhoneCountryCode: 'FR',
|
||||
primaryPhoneCallingCode: '+33',
|
||||
},
|
||||
id: '20202020-338b-46df-8811-fa08c7d19d35',
|
||||
jobTitle: '',
|
||||
@ -1000,7 +1009,8 @@ export const peopleQueryResult: { people: RecordGqlConnection } = {
|
||||
city: 'San Francisco',
|
||||
phones: {
|
||||
primaryPhoneNumber: '781234962',
|
||||
primaryPhoneCountryCode: '+33',
|
||||
primaryPhoneCountryCode: 'FR',
|
||||
primaryPhoneCallingCode: '+33',
|
||||
},
|
||||
id: '20202020-64ad-4b0e-bbfd-e9fd795b7016',
|
||||
jobTitle: '',
|
||||
@ -1099,7 +1109,8 @@ export const peopleQueryResult: { people: RecordGqlConnection } = {
|
||||
city: 'New York',
|
||||
phones: {
|
||||
primaryPhoneNumber: '781234502',
|
||||
primaryPhoneCountryCode: '+33',
|
||||
primaryPhoneCountryCode: 'FR',
|
||||
primaryPhoneCallingCode: '+33',
|
||||
},
|
||||
id: '20202020-5d54-41b7-ba36-f0d20e1417ae',
|
||||
jobTitle: '',
|
||||
@ -1198,7 +1209,8 @@ export const peopleQueryResult: { people: RecordGqlConnection } = {
|
||||
city: 'Los Angeles',
|
||||
phones: {
|
||||
primaryPhoneNumber: '781234563',
|
||||
primaryPhoneCountryCode: '+33',
|
||||
primaryPhoneCountryCode: 'FR',
|
||||
primaryPhoneCallingCode: '+33',
|
||||
},
|
||||
id: '20202020-623d-41fe-92e7-dd45b7c568e1',
|
||||
jobTitle: '',
|
||||
@ -1297,7 +1309,8 @@ export const peopleQueryResult: { people: RecordGqlConnection } = {
|
||||
city: 'Seattle',
|
||||
phones: {
|
||||
primaryPhoneNumber: '781234542',
|
||||
primaryPhoneCountryCode: '+33',
|
||||
primaryPhoneCountryCode: 'FR',
|
||||
primaryPhoneCallingCode: '+33',
|
||||
},
|
||||
id: '20202020-2d40-4e49-8df4-9c6a049190ef',
|
||||
jobTitle: '',
|
||||
@ -1396,7 +1409,8 @@ export const peopleQueryResult: { people: RecordGqlConnection } = {
|
||||
city: 'Seattle',
|
||||
phones: {
|
||||
primaryPhoneNumber: '782234562',
|
||||
primaryPhoneCountryCode: '+33',
|
||||
primaryPhoneCountryCode: 'FR',
|
||||
primaryPhoneCallingCode: '+33',
|
||||
},
|
||||
id: '20202020-2d40-4e49-8df4-9c6a049190df',
|
||||
jobTitle: '',
|
||||
@ -1495,7 +1509,8 @@ export const peopleQueryResult: { people: RecordGqlConnection } = {
|
||||
city: 'Seattle',
|
||||
phones: {
|
||||
primaryPhoneNumber: '781274562',
|
||||
primaryPhoneCountryCode: '+33',
|
||||
primaryPhoneCountryCode: 'FR',
|
||||
primaryPhoneCallingCode: '+33',
|
||||
},
|
||||
id: '20202020-2d40-4e49-8df4-9c6a049191de',
|
||||
jobTitle: '',
|
||||
@ -1594,7 +1609,8 @@ export const peopleQueryResult: { people: RecordGqlConnection } = {
|
||||
city: 'Seattle',
|
||||
phones: {
|
||||
primaryPhoneNumber: '781239562',
|
||||
primaryPhoneCountryCode: '+33',
|
||||
primaryPhoneCountryCode: 'FR',
|
||||
primaryPhoneCallingCode: '+33',
|
||||
},
|
||||
id: '20202020-2d40-4e49-8df4-9c6a049191df',
|
||||
jobTitle: '',
|
||||
|
||||
@ -1,5 +1,3 @@
|
||||
import { Logger } from '@nestjs/common';
|
||||
|
||||
import chalk from 'chalk';
|
||||
import { Option } from 'nest-commander';
|
||||
import { Repository } from 'typeorm';
|
||||
@ -20,11 +18,8 @@ export type ActiveWorkspacesCommandOptions = BaseCommandOptions & {
|
||||
export abstract class ActiveWorkspacesCommandRunner extends BaseCommandRunner {
|
||||
private workspaceIds: string[] = [];
|
||||
|
||||
protected readonly logger: Logger;
|
||||
|
||||
constructor(protected readonly workspaceRepository: Repository<Workspace>) {
|
||||
super();
|
||||
this.logger = new Logger(this.constructor.name);
|
||||
}
|
||||
|
||||
@Option({
|
||||
|
||||
@ -3,6 +3,7 @@ import { Logger } from '@nestjs/common';
|
||||
import chalk from 'chalk';
|
||||
import { CommandRunner, Option } from 'nest-commander';
|
||||
|
||||
import { CommandLogger } from './logger';
|
||||
export type BaseCommandOptions = {
|
||||
workspaceId?: string;
|
||||
dryRun?: boolean;
|
||||
@ -10,11 +11,13 @@ export type BaseCommandOptions = {
|
||||
};
|
||||
|
||||
export abstract class BaseCommandRunner extends CommandRunner {
|
||||
protected readonly logger: Logger;
|
||||
|
||||
protected logger: CommandLogger | Logger;
|
||||
constructor() {
|
||||
super();
|
||||
this.logger = new Logger(this.constructor.name);
|
||||
this.logger = new CommandLogger({
|
||||
verbose: false,
|
||||
constructorName: this.constructor.name,
|
||||
});
|
||||
}
|
||||
|
||||
@Option({
|
||||
@ -27,10 +30,11 @@ export abstract class BaseCommandRunner extends CommandRunner {
|
||||
}
|
||||
|
||||
@Option({
|
||||
flags: '--verbose',
|
||||
flags: '-v, --verbose',
|
||||
description: 'Verbose output',
|
||||
required: false,
|
||||
})
|
||||
parseVerbose() {
|
||||
parseVerbose(): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -38,6 +42,13 @@ export abstract class BaseCommandRunner extends CommandRunner {
|
||||
passedParams: string[],
|
||||
options: BaseCommandOptions,
|
||||
): Promise<void> {
|
||||
if (options.verbose) {
|
||||
this.logger = new CommandLogger({
|
||||
verbose: true,
|
||||
constructorName: this.constructor.name,
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
await this.executeBaseCommand(passedParams, options);
|
||||
} catch (error) {
|
||||
|
||||
@ -52,8 +52,8 @@ import { WorkspaceSyncMetadataModule } from 'src/engine/workspace-manager/worksp
|
||||
UpgradeTo0_32CommandModule,
|
||||
UpgradeTo0_33CommandModule,
|
||||
UpgradeTo0_34CommandModule,
|
||||
FeatureFlagModule,
|
||||
UpgradeTo0_40CommandModule,
|
||||
FeatureFlagModule,
|
||||
],
|
||||
providers: [
|
||||
DataSeedWorkspaceCommand,
|
||||
|
||||
46
packages/twenty-server/src/database/commands/logger.ts
Normal file
46
packages/twenty-server/src/database/commands/logger.ts
Normal file
@ -0,0 +1,46 @@
|
||||
import { Logger } from '@nestjs/common';
|
||||
|
||||
interface CommandLoggerOptions {
|
||||
verbose?: boolean;
|
||||
constructorName: string;
|
||||
}
|
||||
|
||||
export class CommandLogger {
|
||||
private logger: Logger;
|
||||
private verbose: boolean;
|
||||
|
||||
constructor(options: CommandLoggerOptions) {
|
||||
this.logger = new Logger(options.constructorName);
|
||||
this.verbose = options.verbose ?? true;
|
||||
}
|
||||
|
||||
log(message: string, context?: string) {
|
||||
if (this.verbose) {
|
||||
this.logger.log(message, context);
|
||||
}
|
||||
}
|
||||
|
||||
error(message: string, stack?: string, context?: string) {
|
||||
if (this.verbose) {
|
||||
this.logger.error(message, stack, context);
|
||||
}
|
||||
}
|
||||
|
||||
warn(message: string, context?: string) {
|
||||
if (this.verbose) {
|
||||
this.logger.warn(message, context);
|
||||
}
|
||||
}
|
||||
|
||||
debug(message: string, context?: string) {
|
||||
if (this.verbose) {
|
||||
this.logger.debug(message, context);
|
||||
}
|
||||
}
|
||||
|
||||
verboseLog(message: string, context?: string) {
|
||||
if (this.verbose) {
|
||||
this.logger.verbose(message, context);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,144 @@
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
|
||||
import chalk from 'chalk';
|
||||
import { Command } from 'nest-commander';
|
||||
import { Repository } from 'typeorm';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
import {
|
||||
ActiveWorkspacesCommandOptions,
|
||||
ActiveWorkspacesCommandRunner,
|
||||
} from 'src/database/commands/active-workspaces.command';
|
||||
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
import {
|
||||
FieldMetadataEntity,
|
||||
FieldMetadataType,
|
||||
} from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { WorkspaceMetadataVersionService } from 'src/engine/metadata-modules/workspace-metadata-version/services/workspace-metadata-version.service';
|
||||
import { generateMigrationName } from 'src/engine/metadata-modules/workspace-migration/utils/generate-migration-name.util';
|
||||
import {
|
||||
WorkspaceMigrationColumnActionType,
|
||||
WorkspaceMigrationTableAction,
|
||||
WorkspaceMigrationTableActionType,
|
||||
} from 'src/engine/metadata-modules/workspace-migration/workspace-migration.entity';
|
||||
import { WorkspaceMigrationFactory } from 'src/engine/metadata-modules/workspace-migration/workspace-migration.factory';
|
||||
import { WorkspaceMigrationService } from 'src/engine/metadata-modules/workspace-migration/workspace-migration.service';
|
||||
import { computeObjectTargetTable } from 'src/engine/utils/compute-object-target-table.util';
|
||||
import { WorkspaceMigrationRunnerService } from 'src/engine/workspace-manager/workspace-migration-runner/workspace-migration-runner.service';
|
||||
import { isDefined } from 'src/utils/is-defined';
|
||||
|
||||
@Command({
|
||||
name: 'upgrade-0.40:phone-calling-code-create-column',
|
||||
description: 'Create the callingCode column',
|
||||
})
|
||||
export class PhoneCallingCodeCreateColumnCommand extends ActiveWorkspacesCommandRunner {
|
||||
constructor(
|
||||
@InjectRepository(Workspace, 'core')
|
||||
protected readonly workspaceRepository: Repository<Workspace>,
|
||||
@InjectRepository(FieldMetadataEntity, 'metadata')
|
||||
private readonly fieldMetadataRepository: Repository<FieldMetadataEntity>,
|
||||
@InjectRepository(ObjectMetadataEntity, 'metadata')
|
||||
private readonly objectMetadataRepository: Repository<ObjectMetadataEntity>,
|
||||
private readonly workspaceMigrationService: WorkspaceMigrationService,
|
||||
private readonly workspaceMigrationFactory: WorkspaceMigrationFactory,
|
||||
private readonly workspaceMigrationRunnerService: WorkspaceMigrationRunnerService,
|
||||
private readonly workspaceMetadataVersionService: WorkspaceMetadataVersionService,
|
||||
) {
|
||||
super(workspaceRepository);
|
||||
}
|
||||
|
||||
async executeActiveWorkspacesCommand(
|
||||
_passedParam: string[],
|
||||
options: ActiveWorkspacesCommandOptions,
|
||||
workspaceIds: string[],
|
||||
): Promise<void> {
|
||||
this.logger.log(
|
||||
'Running command to add calling code and change country code with default one',
|
||||
);
|
||||
|
||||
this.logger.log(`Part 1 - Workspace`);
|
||||
let workspaceIterator = 1;
|
||||
|
||||
for (const workspaceId of workspaceIds) {
|
||||
this.logger.log(
|
||||
`Running command for workspace ${workspaceId} ${workspaceIterator}/${workspaceIds.length}`,
|
||||
);
|
||||
|
||||
this.logger.log(
|
||||
`P1 Step 1 - let's find all the fieldsMetadata that have the PHONES type, and extract the objectMetadataId`,
|
||||
);
|
||||
|
||||
try {
|
||||
const phonesFieldMetadata = await this.fieldMetadataRepository.find({
|
||||
where: {
|
||||
workspaceId,
|
||||
type: FieldMetadataType.PHONES,
|
||||
},
|
||||
relations: ['object'],
|
||||
});
|
||||
|
||||
for (const phoneFieldMetadata of phonesFieldMetadata) {
|
||||
if (
|
||||
isDefined(phoneFieldMetadata?.name && phoneFieldMetadata.object)
|
||||
) {
|
||||
this.logger.log(
|
||||
`P1 Step 1 - Let's find the "nameSingular" of this objectMetadata: ${phoneFieldMetadata.object.nameSingular || 'not found'}`,
|
||||
);
|
||||
|
||||
if (!phoneFieldMetadata.object?.nameSingular) continue;
|
||||
|
||||
this.logger.log(
|
||||
`P1 Step 1 - Create migration for field ${phoneFieldMetadata.name}`,
|
||||
);
|
||||
|
||||
if (options.dryRun === true) {
|
||||
continue;
|
||||
}
|
||||
await this.workspaceMigrationService.createCustomMigration(
|
||||
generateMigrationName(
|
||||
`create-${phoneFieldMetadata.object.nameSingular}PrimaryPhoneCallingCode-for-field-${phoneFieldMetadata.name}`,
|
||||
),
|
||||
workspaceId,
|
||||
[
|
||||
{
|
||||
name: computeObjectTargetTable(phoneFieldMetadata.object),
|
||||
action: WorkspaceMigrationTableActionType.ALTER,
|
||||
columns: this.workspaceMigrationFactory.createColumnActions(
|
||||
WorkspaceMigrationColumnActionType.CREATE,
|
||||
{
|
||||
id: v4(),
|
||||
type: FieldMetadataType.TEXT,
|
||||
name: `${phoneFieldMetadata.name}PrimaryPhoneCallingCode`,
|
||||
label: `${phoneFieldMetadata.name}PrimaryPhoneCallingCode`,
|
||||
objectMetadataId: phoneFieldMetadata.object.id,
|
||||
workspaceId: workspaceId,
|
||||
isNullable: true,
|
||||
defaultValue: "''",
|
||||
} satisfies Partial<FieldMetadataEntity>,
|
||||
),
|
||||
} satisfies WorkspaceMigrationTableAction,
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
this.logger.log(
|
||||
`P1 Step 1 - RUN migration to create callingCodes for ${workspaceId.slice(0, 5)}`,
|
||||
);
|
||||
await this.workspaceMigrationRunnerService.executeMigrationFromPendingMigrations(
|
||||
workspaceId,
|
||||
);
|
||||
|
||||
await this.workspaceMetadataVersionService.incrementMetadataVersion(
|
||||
workspaceId,
|
||||
);
|
||||
} catch (error) {
|
||||
console.log(`Error in workspace ${workspaceId} : ${error}`);
|
||||
}
|
||||
workspaceIterator++;
|
||||
}
|
||||
|
||||
this.logger.log(chalk.green(`Command completed!`));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,302 @@
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
|
||||
import chalk from 'chalk';
|
||||
import { getCountries, getCountryCallingCode } from 'libphonenumber-js';
|
||||
import { Command } from 'nest-commander';
|
||||
import { Repository } from 'typeorm';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
import {
|
||||
ActiveWorkspacesCommandOptions,
|
||||
ActiveWorkspacesCommandRunner,
|
||||
} from 'src/database/commands/active-workspaces.command';
|
||||
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
import {
|
||||
FieldMetadataEntity,
|
||||
FieldMetadataType,
|
||||
} from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { WorkspaceMetadataVersionService } from 'src/engine/metadata-modules/workspace-metadata-version/services/workspace-metadata-version.service';
|
||||
import { generateMigrationName } from 'src/engine/metadata-modules/workspace-migration/utils/generate-migration-name.util';
|
||||
import {
|
||||
WorkspaceMigrationColumnActionType,
|
||||
WorkspaceMigrationTableAction,
|
||||
WorkspaceMigrationTableActionType,
|
||||
} from 'src/engine/metadata-modules/workspace-migration/workspace-migration.entity';
|
||||
import { WorkspaceMigrationFactory } from 'src/engine/metadata-modules/workspace-migration/workspace-migration.factory';
|
||||
import { WorkspaceMigrationService } from 'src/engine/metadata-modules/workspace-migration/workspace-migration.service';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
import { computeObjectTargetTable } from 'src/engine/utils/compute-object-target-table.util';
|
||||
import { WorkspaceMigrationRunnerService } from 'src/engine/workspace-manager/workspace-migration-runner/workspace-migration-runner.service';
|
||||
import { isDefined } from 'src/utils/is-defined';
|
||||
|
||||
const callingCodeToCountryCode = (callingCode: string): string => {
|
||||
if (!callingCode) {
|
||||
return '';
|
||||
}
|
||||
let callingCodeSanitized = callingCode;
|
||||
|
||||
if (callingCode.startsWith('+')) {
|
||||
callingCodeSanitized = callingCode.slice(1);
|
||||
}
|
||||
|
||||
return (
|
||||
getCountries().find(
|
||||
(countryCode) =>
|
||||
getCountryCallingCode(countryCode) === callingCodeSanitized,
|
||||
) || ''
|
||||
);
|
||||
};
|
||||
|
||||
const isCallingCode = (callingCode: string): boolean => {
|
||||
return callingCodeToCountryCode(callingCode) !== '';
|
||||
};
|
||||
|
||||
@Command({
|
||||
name: 'upgrade-0.40:phone-calling-code-migrate-data',
|
||||
description: 'Add calling code and change country code with default one',
|
||||
})
|
||||
export class PhoneCallingCodeMigrateDataCommand extends ActiveWorkspacesCommandRunner {
|
||||
constructor(
|
||||
@InjectRepository(Workspace, 'core')
|
||||
protected readonly workspaceRepository: Repository<Workspace>,
|
||||
@InjectRepository(FieldMetadataEntity, 'metadata')
|
||||
private readonly fieldMetadataRepository: Repository<FieldMetadataEntity>,
|
||||
@InjectRepository(ObjectMetadataEntity, 'metadata')
|
||||
private readonly objectMetadataRepository: Repository<ObjectMetadataEntity>,
|
||||
private readonly twentyORMGlobalManager: TwentyORMGlobalManager,
|
||||
private readonly workspaceMigrationService: WorkspaceMigrationService,
|
||||
private readonly workspaceMigrationFactory: WorkspaceMigrationFactory,
|
||||
private readonly workspaceMigrationRunnerService: WorkspaceMigrationRunnerService,
|
||||
private readonly workspaceMetadataVersionService: WorkspaceMetadataVersionService,
|
||||
) {
|
||||
super(workspaceRepository);
|
||||
}
|
||||
|
||||
async executeActiveWorkspacesCommand(
|
||||
_passedParam: string[],
|
||||
options: ActiveWorkspacesCommandOptions,
|
||||
workspaceIds: string[],
|
||||
): Promise<void> {
|
||||
this.logger.log(
|
||||
'Running command to add calling code and change country code with default one',
|
||||
);
|
||||
|
||||
this.logger.log(`Part 1 - Workspace`);
|
||||
|
||||
let workspaceIterator = 1;
|
||||
|
||||
for (const workspaceId of workspaceIds) {
|
||||
this.logger.log(
|
||||
`Running command for workspace ${workspaceId} ${workspaceIterator}/${workspaceIds.length}`,
|
||||
);
|
||||
|
||||
this.logger.log(
|
||||
`P1 Step 1 - let's find all the fieldsMetadata that have the PHONES type, and extract the objectMetadataId`,
|
||||
);
|
||||
|
||||
try {
|
||||
const phonesFieldMetadata = await this.fieldMetadataRepository.find({
|
||||
where: {
|
||||
workspaceId,
|
||||
type: FieldMetadataType.PHONES,
|
||||
},
|
||||
relations: ['object'],
|
||||
});
|
||||
|
||||
for (const phoneFieldMetadata of phonesFieldMetadata) {
|
||||
if (
|
||||
isDefined(phoneFieldMetadata?.name) &&
|
||||
isDefined(phoneFieldMetadata.object)
|
||||
) {
|
||||
this.logger.log(
|
||||
`P1 Step 1 - Let's find the "nameSingular" of this objectMetadata: ${phoneFieldMetadata.object.nameSingular || 'not found'}`,
|
||||
);
|
||||
|
||||
if (!phoneFieldMetadata.object?.nameSingular) continue;
|
||||
|
||||
this.logger.log(
|
||||
`P1 Step 1 - Create migration for field ${phoneFieldMetadata.name}`,
|
||||
);
|
||||
if (options.dryRun === false) {
|
||||
await this.workspaceMigrationService.createCustomMigration(
|
||||
generateMigrationName(
|
||||
`create-${phoneFieldMetadata.object.nameSingular}PrimaryPhoneCallingCode-for-field-${phoneFieldMetadata.name}`,
|
||||
),
|
||||
workspaceId,
|
||||
[
|
||||
{
|
||||
name: computeObjectTargetTable(phoneFieldMetadata.object),
|
||||
action: WorkspaceMigrationTableActionType.ALTER,
|
||||
columns: this.workspaceMigrationFactory.createColumnActions(
|
||||
WorkspaceMigrationColumnActionType.CREATE,
|
||||
{
|
||||
id: v4(),
|
||||
type: FieldMetadataType.TEXT,
|
||||
name: `${phoneFieldMetadata.name}PrimaryPhoneCallingCode`,
|
||||
label: `${phoneFieldMetadata.name}PrimaryPhoneCallingCode`,
|
||||
objectMetadataId: phoneFieldMetadata.object.id,
|
||||
workspaceId: workspaceId,
|
||||
isNullable: true,
|
||||
defaultValue: "''",
|
||||
},
|
||||
),
|
||||
} satisfies WorkspaceMigrationTableAction,
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.logger.log(
|
||||
`P1 Step 1 - RUN migration to create callingCodes for ${workspaceId.slice(0, 5)}`,
|
||||
);
|
||||
await this.workspaceMigrationRunnerService.executeMigrationFromPendingMigrations(
|
||||
workspaceId,
|
||||
);
|
||||
|
||||
await this.workspaceMetadataVersionService.incrementMetadataVersion(
|
||||
workspaceId,
|
||||
);
|
||||
|
||||
this.logger.log(
|
||||
`P1 Step 2 - Migrations for callingCode must be first. Now can use twentyORMGlobalManager to update countryCode`,
|
||||
);
|
||||
|
||||
this.logger.log(
|
||||
`P1 Step 3 (same time) - update CountryCode to letters: +33 => FR || +1 => US (if mulitple, first one)`,
|
||||
);
|
||||
|
||||
this.logger.log(
|
||||
`P1 Step 4 (same time) - update all additioanl phones to add a country code following the same logic`,
|
||||
);
|
||||
|
||||
for (const phoneFieldMetadata of phonesFieldMetadata) {
|
||||
this.logger.log(`P1 Step 2 - for ${phoneFieldMetadata.name}`);
|
||||
if (
|
||||
isDefined(phoneFieldMetadata) &&
|
||||
isDefined(phoneFieldMetadata.name)
|
||||
) {
|
||||
const [objectMetadata] = await this.objectMetadataRepository.find({
|
||||
where: {
|
||||
id: phoneFieldMetadata?.objectMetadataId,
|
||||
},
|
||||
});
|
||||
|
||||
const repository =
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace(
|
||||
workspaceId,
|
||||
objectMetadata.nameSingular,
|
||||
);
|
||||
const records = await repository.find();
|
||||
|
||||
for (const record of records) {
|
||||
if (
|
||||
record?.[phoneFieldMetadata.name]?.primaryPhoneCountryCode &&
|
||||
isCallingCode(
|
||||
record[phoneFieldMetadata.name].primaryPhoneCountryCode,
|
||||
)
|
||||
) {
|
||||
let additionalPhones = null;
|
||||
|
||||
if (record[phoneFieldMetadata.name].additionalPhones) {
|
||||
additionalPhones = record[
|
||||
phoneFieldMetadata.name
|
||||
].additionalPhones.map((phone) => {
|
||||
return {
|
||||
...phone,
|
||||
countryCode: callingCodeToCountryCode(phone.callingCode),
|
||||
};
|
||||
});
|
||||
}
|
||||
if (options.dryRun === false) {
|
||||
await repository.update(record.id, {
|
||||
[`${phoneFieldMetadata.name}PrimaryPhoneCallingCode`]:
|
||||
record[phoneFieldMetadata.name].primaryPhoneCountryCode,
|
||||
[`${phoneFieldMetadata.name}PrimaryPhoneCountryCode`]:
|
||||
callingCodeToCountryCode(
|
||||
record[phoneFieldMetadata.name].primaryPhoneCountryCode,
|
||||
),
|
||||
[`${phoneFieldMetadata.name}AdditionalPhones`]:
|
||||
additionalPhones,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(`Error in workspace ${workspaceId} : ${error}`);
|
||||
}
|
||||
workspaceIterator++;
|
||||
}
|
||||
|
||||
this.logger.log(`
|
||||
|
||||
Part 2 - FieldMetadata`);
|
||||
|
||||
workspaceIterator = 1;
|
||||
for (const workspaceId of workspaceIds) {
|
||||
this.logger.log(
|
||||
`Running command for workspace ${workspaceId} ${workspaceIterator}/${workspaceIds.length}`,
|
||||
);
|
||||
|
||||
this.logger.log(
|
||||
`P2 Step 1 - let's find all the fieldsMetadata that have the PHONES type, and extract the objectMetadataId`,
|
||||
);
|
||||
|
||||
try {
|
||||
const phonesFieldMetadata = await this.fieldMetadataRepository.find({
|
||||
where: {
|
||||
workspaceId,
|
||||
type: FieldMetadataType.PHONES,
|
||||
},
|
||||
});
|
||||
|
||||
for (const phoneFieldMetadata of phonesFieldMetadata) {
|
||||
if (
|
||||
!isDefined(phoneFieldMetadata) ||
|
||||
!isDefined(phoneFieldMetadata.defaultValue)
|
||||
)
|
||||
continue;
|
||||
let defaultValue = phoneFieldMetadata.defaultValue;
|
||||
|
||||
// some cases look like it's an array. let's flatten it (not sure the case is supposed to happen but I saw it in my local db)
|
||||
if (Array.isArray(defaultValue) && isDefined(defaultValue[0]))
|
||||
defaultValue = phoneFieldMetadata.defaultValue[0];
|
||||
|
||||
if (!isDefined(defaultValue)) continue;
|
||||
if (typeof defaultValue !== 'object') continue;
|
||||
if (!('primaryPhoneCountryCode' in defaultValue)) continue;
|
||||
if (!defaultValue.primaryPhoneCountryCode) continue;
|
||||
|
||||
const primaryPhoneCountryCode = defaultValue.primaryPhoneCountryCode;
|
||||
|
||||
const countryCode = callingCodeToCountryCode(
|
||||
primaryPhoneCountryCode.replace(/["']/g, ''),
|
||||
);
|
||||
|
||||
if (options.dryRun === false) {
|
||||
await this.fieldMetadataRepository.update(phoneFieldMetadata.id, {
|
||||
defaultValue: {
|
||||
...defaultValue,
|
||||
primaryPhoneCountryCode: countryCode
|
||||
? `'${countryCode}'`
|
||||
: "''",
|
||||
primaryPhoneCallingCode: isCallingCode(
|
||||
primaryPhoneCountryCode.replace(/["']/g, ''),
|
||||
)
|
||||
? primaryPhoneCountryCode
|
||||
: "''",
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(`Error in workspace ${workspaceId} : ${error}`);
|
||||
}
|
||||
workspaceIterator++;
|
||||
}
|
||||
this.logger.log(chalk.green(`Command completed!`));
|
||||
}
|
||||
}
|
||||
@ -5,6 +5,8 @@ import { Repository } from 'typeorm';
|
||||
|
||||
import { ActiveWorkspacesCommandRunner } from 'src/database/commands/active-workspaces.command';
|
||||
import { BaseCommandOptions } from 'src/database/commands/base.command';
|
||||
import { PhoneCallingCodeCreateColumnCommand } from 'src/database/commands/upgrade-version/0-40/0-40-phone-calling-code-create-column.command';
|
||||
import { PhoneCallingCodeMigrateDataCommand } from 'src/database/commands/upgrade-version/0-40/0-40-phone-calling-code-migrate-data.command';
|
||||
import { RecordPositionBackfillCommand } from 'src/database/commands/upgrade-version/0-40/0-40-record-position-backfill.command';
|
||||
import { ViewGroupNoValueBackfillCommand } from 'src/database/commands/upgrade-version/0-40/0-40-view-group-no-value-backfill.command';
|
||||
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
@ -18,9 +20,11 @@ export class UpgradeTo0_40Command extends ActiveWorkspacesCommandRunner {
|
||||
constructor(
|
||||
@InjectRepository(Workspace, 'core')
|
||||
protected readonly workspaceRepository: Repository<Workspace>,
|
||||
private readonly recordPositionBackfillCommand: RecordPositionBackfillCommand,
|
||||
private readonly viewGroupNoValueBackfillCommand: ViewGroupNoValueBackfillCommand,
|
||||
private readonly syncWorkspaceMetadataCommand: SyncWorkspaceMetadataCommand,
|
||||
private readonly phoneCallingCodeMigrateDataCommand: PhoneCallingCodeMigrateDataCommand,
|
||||
private readonly phoneCallingCodeCreateColumnCommand: PhoneCallingCodeCreateColumnCommand,
|
||||
private readonly recordPositionBackfillCommand: RecordPositionBackfillCommand,
|
||||
) {
|
||||
super(workspaceRepository);
|
||||
}
|
||||
@ -30,6 +34,22 @@ export class UpgradeTo0_40Command extends ActiveWorkspacesCommandRunner {
|
||||
options: BaseCommandOptions,
|
||||
workspaceIds: string[],
|
||||
): Promise<void> {
|
||||
this.logger.log(
|
||||
'Running command to upgrade to 0.40: must start with phone calling code otherwise SyncMetadata will fail',
|
||||
);
|
||||
|
||||
await this.phoneCallingCodeCreateColumnCommand.executeActiveWorkspacesCommand(
|
||||
passedParam,
|
||||
options,
|
||||
workspaceIds,
|
||||
);
|
||||
|
||||
await this.phoneCallingCodeMigrateDataCommand.executeActiveWorkspacesCommand(
|
||||
passedParam,
|
||||
options,
|
||||
workspaceIds,
|
||||
);
|
||||
|
||||
await this.recordPositionBackfillCommand.executeActiveWorkspacesCommand(
|
||||
passedParam,
|
||||
options,
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
|
||||
import { PhoneCallingCodeCreateColumnCommand } from 'src/database/commands/upgrade-version/0-40/0-40-phone-calling-code-create-column.command';
|
||||
import { PhoneCallingCodeMigrateDataCommand } from 'src/database/commands/upgrade-version/0-40/0-40-phone-calling-code-migrate-data.command';
|
||||
import { RecordPositionBackfillCommand } from 'src/database/commands/upgrade-version/0-40/0-40-record-position-backfill.command';
|
||||
import { UpgradeTo0_40Command } from 'src/database/commands/upgrade-version/0-40/0-40-upgrade-version.command';
|
||||
import { ViewGroupNoValueBackfillCommand } from 'src/database/commands/upgrade-version/0-40/0-40-view-group-no-value-backfill.command';
|
||||
@ -8,18 +10,35 @@ import { RecordPositionBackfillModule } from 'src/engine/api/graphql/workspace-q
|
||||
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import { FieldMetadataModule } from 'src/engine/metadata-modules/field-metadata/field-metadata.module';
|
||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { SearchModule } from 'src/engine/metadata-modules/search/search.module';
|
||||
import { WorkspaceMetadataVersionModule } from 'src/engine/metadata-modules/workspace-metadata-version/workspace-metadata-version.module';
|
||||
import { WorkspaceMigrationFactory } from 'src/engine/metadata-modules/workspace-migration/workspace-migration.factory';
|
||||
import { WorkspaceMigrationModule } from 'src/engine/metadata-modules/workspace-migration/workspace-migration.module';
|
||||
import { WorkspaceMigrationRunnerModule } from 'src/engine/workspace-manager/workspace-migration-runner/workspace-migration-runner.module';
|
||||
import { WorkspaceSyncMetadataCommandsModule } from 'src/engine/workspace-manager/workspace-sync-metadata/commands/workspace-sync-metadata-commands.module';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
TypeOrmModule.forFeature([Workspace], 'core'),
|
||||
TypeOrmModule.forFeature(
|
||||
[ObjectMetadataEntity, FieldMetadataEntity],
|
||||
'metadata',
|
||||
),
|
||||
TypeOrmModule.forFeature([FieldMetadataEntity], 'metadata'),
|
||||
WorkspaceSyncMetadataCommandsModule,
|
||||
SearchModule,
|
||||
WorkspaceMigrationRunnerModule,
|
||||
WorkspaceMetadataVersionModule,
|
||||
WorkspaceMigrationModule,
|
||||
RecordPositionBackfillModule,
|
||||
FieldMetadataModule,
|
||||
],
|
||||
providers: [
|
||||
UpgradeTo0_40Command,
|
||||
PhoneCallingCodeMigrateDataCommand,
|
||||
PhoneCallingCodeCreateColumnCommand,
|
||||
WorkspaceMigrationFactory,
|
||||
RecordPositionBackfillCommand,
|
||||
ViewGroupNoValueBackfillCommand,
|
||||
],
|
||||
|
||||
@ -106,13 +106,12 @@ export const getDevSeedPeopleCustomFields = (
|
||||
isActive: true,
|
||||
isNullable: false,
|
||||
isUnique: false,
|
||||
defaultValue: [
|
||||
{
|
||||
primaryPhoneNumber: '',
|
||||
primaryPhoneCountryCode: '',
|
||||
additionalPhones: {},
|
||||
},
|
||||
],
|
||||
defaultValue: {
|
||||
primaryPhoneNumber: "''",
|
||||
primaryPhoneCountryCode: "'FR'",
|
||||
primaryPhoneCallingCode: "'+33'",
|
||||
additionalPhones: null,
|
||||
},
|
||||
objectMetadataId,
|
||||
},
|
||||
{
|
||||
|
||||
@ -35,12 +35,14 @@ export const seedPeople = async (
|
||||
'nameFirstName',
|
||||
'nameLastName',
|
||||
'phonesPrimaryPhoneCountryCode',
|
||||
'phonesPrimaryPhoneCallingCode',
|
||||
'phonesPrimaryPhoneNumber',
|
||||
'city',
|
||||
'companyId',
|
||||
'emailsPrimaryEmail',
|
||||
'position',
|
||||
'whatsappPrimaryPhoneCountryCode',
|
||||
'whatsappPrimaryPhoneCallingCode',
|
||||
'whatsappPrimaryPhoneNumber',
|
||||
'createdBySource',
|
||||
'createdByWorkspaceMemberId',
|
||||
@ -52,13 +54,15 @@ export const seedPeople = async (
|
||||
id: DEV_SEED_PERSON_IDS.CHRISTOPH,
|
||||
nameFirstName: 'Christoph',
|
||||
nameLastName: 'Callisto',
|
||||
phonePrimaryPhoneCountryCode: '+33',
|
||||
phonePrimaryPhoneNumber: '789012345',
|
||||
phonesPrimaryPhoneCountryCode: 'FR',
|
||||
phonesPrimaryPhoneCallingCode: '+33',
|
||||
phonesPrimaryPhoneNumber: '789012345',
|
||||
city: 'Seattle',
|
||||
companyId: DEV_SEED_COMPANY_IDS.LINKEDIN,
|
||||
emailsPrimaryEmail: 'christoph.calisto@linkedin.com',
|
||||
position: 1,
|
||||
whatsappPrimaryPhoneCountryCode: '+33',
|
||||
whatsappPrimaryPhoneCountryCode: 'FR',
|
||||
whatsappPrimaryPhoneCallingCode: '+33',
|
||||
whatsappPrimaryPhoneNumber: '789012345',
|
||||
createdBySource: 'MANUAL',
|
||||
createdByWorkspaceMemberId: DEV_SEED_WORKSPACE_MEMBER_IDS.TIM,
|
||||
@ -68,13 +72,15 @@ export const seedPeople = async (
|
||||
id: DEV_SEED_PERSON_IDS.SYLVIE,
|
||||
nameFirstName: 'Sylvie',
|
||||
nameLastName: 'Palmer',
|
||||
phonePrimaryPhoneCountryCode: '+33',
|
||||
phonePrimaryPhoneNumber: '780123456',
|
||||
phonesPrimaryPhoneCountryCode: 'FR',
|
||||
phonesPrimaryPhoneCallingCode: '+33',
|
||||
phonesPrimaryPhoneNumber: '780123456',
|
||||
city: 'Los Angeles',
|
||||
companyId: DEV_SEED_COMPANY_IDS.LINKEDIN,
|
||||
emailsPrimaryEmail: 'sylvie.palmer@linkedin.com',
|
||||
position: 2,
|
||||
whatsappPrimaryPhoneCountryCode: '+33',
|
||||
whatsappPrimaryPhoneCountryCode: 'FR',
|
||||
whatsappPrimaryPhoneCallingCode: '+33',
|
||||
whatsappPrimaryPhoneNumber: '780123456',
|
||||
createdBySource: 'MANUAL',
|
||||
createdByWorkspaceMemberId: DEV_SEED_WORKSPACE_MEMBER_IDS.TIM,
|
||||
@ -84,13 +90,15 @@ export const seedPeople = async (
|
||||
id: DEV_SEED_PERSON_IDS.CHRISTOPHER_G,
|
||||
nameFirstName: 'Christopher',
|
||||
nameLastName: 'Gonzalez',
|
||||
phonePrimaryPhoneCountryCode: '+33',
|
||||
phonePrimaryPhoneNumber: '789012345',
|
||||
phonesPrimaryPhoneCountryCode: 'FR',
|
||||
phonesPrimaryPhoneCallingCode: '+33',
|
||||
phonesPrimaryPhoneNumber: '789012345',
|
||||
city: 'Seattle',
|
||||
companyId: DEV_SEED_COMPANY_IDS.QONTO,
|
||||
emailsPrimaryEmail: 'christopher.gonzalez@qonto.com',
|
||||
position: 3,
|
||||
whatsappPrimaryPhoneCountryCode: '+33',
|
||||
whatsappPrimaryPhoneCountryCode: 'FR',
|
||||
whatsappPrimaryPhoneCallingCode: '+33',
|
||||
whatsappPrimaryPhoneNumber: '789012345',
|
||||
createdBySource: 'MANUAL',
|
||||
createdByWorkspaceMemberId: DEV_SEED_WORKSPACE_MEMBER_IDS.TIM,
|
||||
@ -100,13 +108,15 @@ export const seedPeople = async (
|
||||
id: DEV_SEED_PERSON_IDS.ASHLEY,
|
||||
nameFirstName: 'Ashley',
|
||||
nameLastName: 'Parker',
|
||||
phonePrimaryPhoneCountryCode: '+33',
|
||||
phonePrimaryPhoneNumber: '780123456',
|
||||
phonesPrimaryPhoneCountryCode: 'FR',
|
||||
phonesPrimaryPhoneCallingCode: '+33',
|
||||
phonesPrimaryPhoneNumber: '780123456',
|
||||
city: 'Los Angeles',
|
||||
companyId: DEV_SEED_COMPANY_IDS.QONTO,
|
||||
emailsPrimaryEmail: 'ashley.parker@qonto.com',
|
||||
position: 4,
|
||||
whatsappPrimaryPhoneCountryCode: '+33',
|
||||
whatsappPrimaryPhoneCountryCode: 'FR',
|
||||
whatsappPrimaryPhoneCallingCode: '+33',
|
||||
whatsappPrimaryPhoneNumber: '780123456',
|
||||
createdBySource: 'MANUAL',
|
||||
createdByWorkspaceMemberId: DEV_SEED_WORKSPACE_MEMBER_IDS.TIM,
|
||||
@ -116,13 +126,15 @@ export const seedPeople = async (
|
||||
id: DEV_SEED_PERSON_IDS.NICHOLAS,
|
||||
nameFirstName: 'Nicholas',
|
||||
nameLastName: 'Wright',
|
||||
phonePrimaryPhoneCountryCode: '+33',
|
||||
phonePrimaryPhoneNumber: '781234567',
|
||||
phonesPrimaryPhoneCountryCode: 'FR',
|
||||
phonesPrimaryPhoneCallingCode: '+33',
|
||||
phonesPrimaryPhoneNumber: '781234567',
|
||||
city: 'Seattle',
|
||||
companyId: DEV_SEED_COMPANY_IDS.MICROSOFT,
|
||||
emailsPrimaryEmail: 'nicholas.wright@microsoft.com',
|
||||
position: 5,
|
||||
whatsappPrimaryPhoneCountryCode: '+33',
|
||||
whatsappPrimaryPhoneCountryCode: 'FR',
|
||||
whatsappPrimaryPhoneCallingCode: '+33',
|
||||
whatsappPrimaryPhoneNumber: '781234567',
|
||||
createdBySource: 'MANUAL',
|
||||
createdByWorkspaceMemberId: DEV_SEED_WORKSPACE_MEMBER_IDS.TIM,
|
||||
@ -132,13 +144,15 @@ export const seedPeople = async (
|
||||
id: DEV_SEED_PERSON_IDS.ISABELLA,
|
||||
nameFirstName: 'Isabella',
|
||||
nameLastName: 'Scott',
|
||||
phonePrimaryPhoneCountryCode: '+33',
|
||||
phonePrimaryPhoneNumber: '782345678',
|
||||
phonesPrimaryPhoneCountryCode: 'FR',
|
||||
phonesPrimaryPhoneCallingCode: '+33',
|
||||
phonesPrimaryPhoneNumber: '782345678',
|
||||
city: 'New York',
|
||||
companyId: DEV_SEED_COMPANY_IDS.MICROSOFT,
|
||||
emailsPrimaryEmail: 'isabella.scott@microsoft.com',
|
||||
position: 6,
|
||||
whatsappPrimaryPhoneCountryCode: '+33',
|
||||
whatsappPrimaryPhoneCountryCode: 'FR',
|
||||
whatsappPrimaryPhoneCallingCode: '+33',
|
||||
whatsappPrimaryPhoneNumber: '782345678',
|
||||
createdBySource: 'MANUAL',
|
||||
createdByWorkspaceMemberId: DEV_SEED_WORKSPACE_MEMBER_IDS.TIM,
|
||||
@ -148,13 +162,15 @@ export const seedPeople = async (
|
||||
id: DEV_SEED_PERSON_IDS.MATTHEW,
|
||||
nameFirstName: 'Matthew',
|
||||
nameLastName: 'Green',
|
||||
phonePrimaryPhoneCountryCode: '+33',
|
||||
phonePrimaryPhoneNumber: '783456789',
|
||||
phonesPrimaryPhoneCountryCode: 'FR',
|
||||
phonesPrimaryPhoneCallingCode: '+33',
|
||||
phonesPrimaryPhoneNumber: '783456789',
|
||||
city: 'Seattle',
|
||||
companyId: DEV_SEED_COMPANY_IDS.MICROSOFT,
|
||||
emailsPrimaryEmail: 'matthew.green@microsoft.com',
|
||||
position: 7,
|
||||
whatsappPrimaryPhoneCountryCode: '+33',
|
||||
whatsappPrimaryPhoneCountryCode: 'FR',
|
||||
whatsappPrimaryPhoneCallingCode: '+33',
|
||||
whatsappPrimaryPhoneNumber: '783456789',
|
||||
createdBySource: 'MANUAL',
|
||||
createdByWorkspaceMemberId: DEV_SEED_WORKSPACE_MEMBER_IDS.TIM,
|
||||
@ -164,13 +180,15 @@ export const seedPeople = async (
|
||||
id: DEV_SEED_PERSON_IDS.ELIZABETH,
|
||||
nameFirstName: 'Elizabeth',
|
||||
nameLastName: 'Baker',
|
||||
phonePrimaryPhoneCountryCode: '+33',
|
||||
phonePrimaryPhoneNumber: '784567890',
|
||||
phonesPrimaryPhoneCountryCode: 'FR',
|
||||
phonesPrimaryPhoneCallingCode: '+33',
|
||||
phonesPrimaryPhoneNumber: '784567890',
|
||||
city: 'New York',
|
||||
companyId: DEV_SEED_COMPANY_IDS.AIRBNB,
|
||||
emailsPrimaryEmail: 'elizabeth.baker@airbnb.com',
|
||||
position: 8,
|
||||
whatsappPrimaryPhoneCountryCode: '+33',
|
||||
whatsappPrimaryPhoneCountryCode: 'FR',
|
||||
whatsappPrimaryPhoneCallingCode: '+33',
|
||||
whatsappPrimaryPhoneNumber: '784567890',
|
||||
createdBySource: 'MANUAL',
|
||||
createdByWorkspaceMemberId: DEV_SEED_WORKSPACE_MEMBER_IDS.TIM,
|
||||
@ -180,13 +198,15 @@ export const seedPeople = async (
|
||||
id: DEV_SEED_PERSON_IDS.CHRISTOPHER_N,
|
||||
nameFirstName: 'Christopher',
|
||||
nameLastName: 'Nelson',
|
||||
phonePrimaryPhoneCountryCode: '+33',
|
||||
phonePrimaryPhoneNumber: '785678901',
|
||||
phonesPrimaryPhoneCountryCode: 'FR',
|
||||
phonesPrimaryPhoneCallingCode: '+33',
|
||||
phonesPrimaryPhoneNumber: '785678901',
|
||||
city: 'San Francisco',
|
||||
companyId: DEV_SEED_COMPANY_IDS.AIRBNB,
|
||||
emailsPrimaryEmail: 'christopher.nelson@airbnb.com',
|
||||
position: 9,
|
||||
whatsappPrimaryPhoneCountryCode: '+33',
|
||||
whatsappPrimaryPhoneCountryCode: 'FR',
|
||||
whatsappPrimaryPhoneCallingCode: '+33',
|
||||
whatsappPrimaryPhoneNumber: '785678901',
|
||||
createdBySource: 'MANUAL',
|
||||
createdByWorkspaceMemberId: DEV_SEED_WORKSPACE_MEMBER_IDS.TIM,
|
||||
@ -196,13 +216,15 @@ export const seedPeople = async (
|
||||
id: DEV_SEED_PERSON_IDS.AVERY,
|
||||
nameFirstName: 'Avery',
|
||||
nameLastName: 'Carter',
|
||||
phonePrimaryPhoneCountryCode: '+33',
|
||||
phonePrimaryPhoneNumber: '786789012',
|
||||
phonesPrimaryPhoneCountryCode: 'FR',
|
||||
phonesPrimaryPhoneCallingCode: '+33',
|
||||
phonesPrimaryPhoneNumber: '786789012',
|
||||
city: 'New York',
|
||||
companyId: DEV_SEED_COMPANY_IDS.AIRBNB,
|
||||
emailsPrimaryEmail: 'avery.carter@airbnb.com',
|
||||
position: 10,
|
||||
whatsappPrimaryPhoneCountryCode: '+33',
|
||||
whatsappPrimaryPhoneCountryCode: 'FR',
|
||||
whatsappPrimaryPhoneCallingCode: '+33',
|
||||
whatsappPrimaryPhoneNumber: '786789012',
|
||||
createdBySource: 'MANUAL',
|
||||
createdByWorkspaceMemberId: DEV_SEED_WORKSPACE_MEMBER_IDS.TIM,
|
||||
@ -212,13 +234,15 @@ export const seedPeople = async (
|
||||
id: DEV_SEED_PERSON_IDS.ETHAN,
|
||||
nameFirstName: 'Ethan',
|
||||
nameLastName: 'Mitchell',
|
||||
phonePrimaryPhoneCountryCode: '+33',
|
||||
phonePrimaryPhoneNumber: '787890123',
|
||||
phonesPrimaryPhoneCountryCode: 'FR',
|
||||
phonesPrimaryPhoneCallingCode: '+33',
|
||||
phonesPrimaryPhoneNumber: '787890123',
|
||||
city: 'Los Angeles',
|
||||
companyId: DEV_SEED_COMPANY_IDS.GOOGLE,
|
||||
emailsPrimaryEmail: 'ethan.mitchell@google.com',
|
||||
position: 11,
|
||||
whatsappPrimaryPhoneCountryCode: '+33',
|
||||
whatsappPrimaryPhoneCountryCode: 'FR',
|
||||
whatsappPrimaryPhoneCallingCode: '+33',
|
||||
whatsappPrimaryPhoneNumber: '787890123',
|
||||
createdBySource: 'MANUAL',
|
||||
createdByWorkspaceMemberId: DEV_SEED_WORKSPACE_MEMBER_IDS.TIM,
|
||||
@ -228,13 +252,15 @@ export const seedPeople = async (
|
||||
id: DEV_SEED_PERSON_IDS.MADISON,
|
||||
nameFirstName: 'Madison',
|
||||
nameLastName: 'Perez',
|
||||
phonePrimaryPhoneCountryCode: '+33',
|
||||
phonePrimaryPhoneNumber: '788901234',
|
||||
phonesPrimaryPhoneCountryCode: 'FR',
|
||||
phonesPrimaryPhoneCallingCode: '+33',
|
||||
phonesPrimaryPhoneNumber: '788901234',
|
||||
city: 'Seattle',
|
||||
companyId: DEV_SEED_COMPANY_IDS.GOOGLE,
|
||||
emailsPrimaryEmail: 'madison.perez@google.com',
|
||||
position: 12,
|
||||
whatsappPrimaryPhoneCountryCode: '+33',
|
||||
whatsappPrimaryPhoneCountryCode: 'FR',
|
||||
whatsappPrimaryPhoneCallingCode: '+33',
|
||||
whatsappPrimaryPhoneNumber: '788901234',
|
||||
createdBySource: 'MANUAL',
|
||||
createdByWorkspaceMemberId: DEV_SEED_WORKSPACE_MEMBER_IDS.TIM,
|
||||
@ -244,13 +270,15 @@ export const seedPeople = async (
|
||||
id: DEV_SEED_PERSON_IDS.BERTRAND,
|
||||
nameFirstName: 'Bertrand',
|
||||
nameLastName: 'Voulzy',
|
||||
phonePrimaryPhoneCountryCode: '+33',
|
||||
phonePrimaryPhoneNumber: '788901234',
|
||||
phonesPrimaryPhoneCountryCode: 'FR',
|
||||
phonesPrimaryPhoneCallingCode: '+33',
|
||||
phonesPrimaryPhoneNumber: '788901234',
|
||||
city: 'Seattle',
|
||||
companyId: DEV_SEED_COMPANY_IDS.GOOGLE,
|
||||
emailsPrimaryEmail: 'bertrand.voulzy@google.com',
|
||||
position: 13,
|
||||
whatsappPrimaryPhoneCountryCode: '+33',
|
||||
whatsappPrimaryPhoneCountryCode: 'FR',
|
||||
whatsappPrimaryPhoneCallingCode: '+33',
|
||||
whatsappPrimaryPhoneNumber: '788901234',
|
||||
createdBySource: 'MANUAL',
|
||||
createdByWorkspaceMemberId: DEV_SEED_WORKSPACE_MEMBER_IDS.TIM,
|
||||
@ -260,13 +288,15 @@ export const seedPeople = async (
|
||||
id: DEV_SEED_PERSON_IDS.LOUIS,
|
||||
nameFirstName: 'Louis',
|
||||
nameLastName: 'Duss',
|
||||
phonePrimaryPhoneCountryCode: '+33',
|
||||
phonePrimaryPhoneNumber: '789012345',
|
||||
phonesPrimaryPhoneCountryCode: 'FR',
|
||||
phonesPrimaryPhoneCallingCode: '+33',
|
||||
phonesPrimaryPhoneNumber: '789012345',
|
||||
city: 'Seattle',
|
||||
companyId: DEV_SEED_COMPANY_IDS.GOOGLE,
|
||||
emailsPrimaryEmail: 'louis.duss@google.com',
|
||||
position: 14,
|
||||
whatsappPrimaryPhoneCountryCode: '+33',
|
||||
whatsappPrimaryPhoneCountryCode: 'FR',
|
||||
whatsappPrimaryPhoneCallingCode: '+33',
|
||||
whatsappPrimaryPhoneNumber: '789012345',
|
||||
createdBySource: 'MANUAL',
|
||||
createdByWorkspaceMemberId: DEV_SEED_WORKSPACE_MEMBER_IDS.TIM,
|
||||
@ -276,13 +306,15 @@ export const seedPeople = async (
|
||||
id: DEV_SEED_PERSON_IDS.LORIE,
|
||||
nameFirstName: 'Lorie',
|
||||
nameLastName: 'Vladim',
|
||||
phonePrimaryPhoneCountryCode: '+33',
|
||||
phonePrimaryPhoneNumber: '788901235',
|
||||
phonesPrimaryPhoneCountryCode: 'FR',
|
||||
phonesPrimaryPhoneCallingCode: '+33',
|
||||
phonesPrimaryPhoneNumber: '788901235',
|
||||
city: 'Seattle',
|
||||
companyId: DEV_SEED_COMPANY_IDS.GOOGLE,
|
||||
emailsPrimaryEmail: 'lorie.vladim@google.com',
|
||||
position: 15,
|
||||
whatsappPrimaryPhoneCountryCode: '+33',
|
||||
whatsappPrimaryPhoneCountryCode: 'FR',
|
||||
whatsappPrimaryPhoneCallingCode: '+33',
|
||||
whatsappPrimaryPhoneNumber: '788901235',
|
||||
createdBySource: 'MANUAL',
|
||||
createdByWorkspaceMemberId: null,
|
||||
|
||||
@ -231,6 +231,7 @@ const fieldPhonesMock = {
|
||||
{
|
||||
primaryPhoneNumber: '',
|
||||
primaryPhoneCountryCode: '',
|
||||
primaryPhoneCallingCode: '',
|
||||
additionalPhones: {},
|
||||
},
|
||||
],
|
||||
|
||||
@ -150,6 +150,7 @@ export const mapFieldMetadataToGraphqlQuery = (
|
||||
{
|
||||
primaryPhoneNumber
|
||||
primaryPhoneCountryCode
|
||||
primaryPhoneCallingCode
|
||||
additionalPhones
|
||||
}
|
||||
`;
|
||||
|
||||
@ -31,6 +31,9 @@ describe('computeSchemaComponents', () => {
|
||||
primaryPhoneCountryCode: {
|
||||
type: 'string',
|
||||
},
|
||||
primaryPhoneCallingCode: {
|
||||
type: 'string',
|
||||
},
|
||||
primaryPhoneNumber: {
|
||||
type: 'string',
|
||||
},
|
||||
@ -216,6 +219,9 @@ describe('computeSchemaComponents', () => {
|
||||
primaryPhoneCountryCode: {
|
||||
type: 'string',
|
||||
},
|
||||
primaryPhoneCallingCode: {
|
||||
type: 'string',
|
||||
},
|
||||
primaryPhoneNumber: {
|
||||
type: 'string',
|
||||
},
|
||||
@ -400,6 +406,9 @@ describe('computeSchemaComponents', () => {
|
||||
primaryPhoneCountryCode: {
|
||||
type: 'string',
|
||||
},
|
||||
primaryPhoneCallingCode: {
|
||||
type: 'string',
|
||||
},
|
||||
primaryPhoneNumber: {
|
||||
type: 'string',
|
||||
},
|
||||
|
||||
@ -259,6 +259,9 @@ const getSchemaComponentsProperties = ({
|
||||
primaryPhoneCountryCode: {
|
||||
type: 'string',
|
||||
},
|
||||
primaryPhoneCallingCode: {
|
||||
type: 'string',
|
||||
},
|
||||
primaryPhoneNumber: {
|
||||
type: 'string',
|
||||
},
|
||||
|
||||
@ -18,6 +18,12 @@ export const phonesCompositeType: CompositeType = {
|
||||
hidden: false,
|
||||
isRequired: false,
|
||||
},
|
||||
{
|
||||
name: 'primaryPhoneCallingCode',
|
||||
type: FieldMetadataType.TEXT,
|
||||
hidden: false,
|
||||
isRequired: false,
|
||||
},
|
||||
{
|
||||
name: 'additionalPhones',
|
||||
type: FieldMetadataType.RAW_JSON,
|
||||
@ -30,5 +36,6 @@ export const phonesCompositeType: CompositeType = {
|
||||
export type PhonesMetadata = {
|
||||
primaryPhoneNumber: string;
|
||||
primaryPhoneCountryCode: string;
|
||||
primaryPhoneCallingCode: string;
|
||||
additionalPhones: object | null;
|
||||
};
|
||||
|
||||
@ -193,6 +193,10 @@ export class FieldMetadataDefaultValuePhones {
|
||||
@IsQuotedString()
|
||||
primaryPhoneCountryCode: string | null;
|
||||
|
||||
@ValidateIf((_object, value) => value !== null)
|
||||
@IsQuotedString()
|
||||
primaryPhoneCallingCode: string | null;
|
||||
|
||||
@ValidateIf((_object, value) => value !== null)
|
||||
@IsObject()
|
||||
additionalPhones: object | null;
|
||||
|
||||
@ -44,6 +44,7 @@ export function generateDefaultValue(
|
||||
return {
|
||||
primaryPhoneNumber: "''",
|
||||
primaryPhoneCountryCode: "''",
|
||||
primaryPhoneCallingCode: "''",
|
||||
additionalPhones: null,
|
||||
};
|
||||
default:
|
||||
|
||||
@ -60,8 +60,11 @@ describe('creates.create_company', () => {
|
||||
name: { firstName: 'John', lastName: 'Doe' },
|
||||
phones: {
|
||||
primaryPhoneNumber: '610203040',
|
||||
primaryPhoneCountryCode: '+33',
|
||||
additionalPhones: ['{number: "610203041", countryCode: "+33"}'],
|
||||
primaryPhoneCountryCode: 'FR',
|
||||
primaryPhoneCallingCode: '+33',
|
||||
additionalPhones: [
|
||||
'{number: "610203041", countryCode: "FR", callingCode: "+33"}',
|
||||
],
|
||||
},
|
||||
city: 'Paris',
|
||||
});
|
||||
|
||||
@ -25,8 +25,11 @@ describe('utils.handleQueryParams', () => {
|
||||
},
|
||||
phones: {
|
||||
primaryPhoneNumber: '322110011',
|
||||
primaryPhoneCountryCode: '+33',
|
||||
additionalPhones: ["{ phoneNumber: '322110012', countryCode: '+33' }"],
|
||||
primaryPhoneCountryCode: 'FR',
|
||||
primaryPhoneCallingCode: '+33',
|
||||
additionalPhones: [
|
||||
"{ phoneNumber: '322110012', countryCode: 'FR', callingCode: '+33' }",
|
||||
],
|
||||
},
|
||||
xUrl__url: '/x_url',
|
||||
xUrl__label: 'Test xUrl',
|
||||
@ -42,7 +45,7 @@ describe('utils.handleQueryParams', () => {
|
||||
'linkedinUrl: {url: "/linkedin_url", label: "Test linkedinUrl"}, ' +
|
||||
'whatsapp: {primaryLinkUrl: "/whatsapp_url", primaryLinkLabel: "Whatsapp Link", secondaryLinks: [{url: \'/secondary_whatsapp_url\',label: \'Secondary Whatsapp Link\'}]}, ' +
|
||||
'emails: {primaryEmail: "primary@email.com", additionalEmails: ["secondary@email.com"]}, ' +
|
||||
'phones: {primaryPhoneNumber: "322110011", primaryPhoneCountryCode: "+33", additionalPhones: [{ phoneNumber: \'322110012\', countryCode: \'+33\' }]}, ' +
|
||||
'phones: {primaryPhoneNumber: "322110011", primaryPhoneCountryCode: "FR", primaryPhoneCallingCode: "+33", additionalPhones: [{ phoneNumber: \'322110012\', countryCode: \'+33\' }]}, ' +
|
||||
'xUrl: {url: "/x_url", label: "Test xUrl"}, ' +
|
||||
'annualRecurringRevenue: 100000, ' +
|
||||
'idealCustomerProfile: true, ' +
|
||||
|
||||
Reference in New Issue
Block a user