Support for multiple values in the Phone field (#6882)
### Description - This is the first PR on Phones field; - We are introducing new field type(Phones) - We are Forbidding creation of Phone field - We Added support for filtering and sorting on Phones field - We are using the same display mode as used on the Links field type (chips), check the Domain field of the Company object - We are also using the same logic of the link when editing the field **How to Test** 1. Checkout to TWNTY-6260 branch 2. Reset database using "npx nx database:reset twenty-server" command 3. Add custom field of type Phones in settings/data-model **Loom Video:**\ <https://www.loom.com/share/3c981260be254dcf851256d020a20ab0?sid=58507361-3a3b-452c-9de8-b5b1abda70ac> ### Refs #6260 Co-authored-by: gitstart-twenty <gitstart-twenty@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
91187dcf82
commit
846953b0f4
@ -7,6 +7,7 @@ export const FIELD_CURRENCY_MOCK_NAME = 'fieldCurrency';
|
||||
export const FIELD_ADDRESS_MOCK_NAME = 'fieldAddress';
|
||||
export const FIELD_ACTOR_MOCK_NAME = 'fieldActor';
|
||||
export const FIELD_FULL_NAME_MOCK_NAME = 'fieldFullName';
|
||||
export const FIELD_PHONES_MOCK_NAME = 'fieldPhones';
|
||||
|
||||
export const fieldNumberMock = {
|
||||
name: 'fieldNumber',
|
||||
@ -221,6 +222,7 @@ const fieldActorMock = {
|
||||
name: '',
|
||||
},
|
||||
};
|
||||
|
||||
const fieldEmailsMock = {
|
||||
name: 'fieldEmails',
|
||||
type: FieldMetadataType.EMAILS,
|
||||
@ -228,10 +230,24 @@ const fieldEmailsMock = {
|
||||
defaultValue: [{ primaryEmail: '', additionalEmails: {} }],
|
||||
};
|
||||
|
||||
const fieldPhonesMock = {
|
||||
name: FIELD_PHONES_MOCK_NAME,
|
||||
type: FieldMetadataType.PHONES,
|
||||
isNullable: false,
|
||||
defaultValue: [
|
||||
{
|
||||
primaryPhoneNumber: '',
|
||||
primaryPhoneCountryCode: '',
|
||||
additionalPhones: {},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export const fields = [
|
||||
fieldUuidMock,
|
||||
fieldTextMock,
|
||||
fieldPhoneMock,
|
||||
fieldPhonesMock,
|
||||
fieldEmailMock,
|
||||
fieldEmailsMock,
|
||||
fieldDateTimeMock,
|
||||
|
||||
@ -152,5 +152,14 @@ export const mapFieldMetadataToGraphqlQuery = (
|
||||
additionalEmails
|
||||
}
|
||||
`;
|
||||
} else if (fieldType === FieldMetadataType.PHONES) {
|
||||
return `
|
||||
${field.name}
|
||||
{
|
||||
primaryPhoneNumber
|
||||
primaryPhoneCountryCode
|
||||
additionalPhones
|
||||
}
|
||||
`;
|
||||
}
|
||||
};
|
||||
|
||||
@ -33,6 +33,20 @@ describe('computeSchemaComponents', () => {
|
||||
fieldPhone: {
|
||||
type: 'string',
|
||||
},
|
||||
fieldPhones: {
|
||||
properties: {
|
||||
additionalPhones: {
|
||||
type: 'object',
|
||||
},
|
||||
primaryPhoneCountryCode: {
|
||||
type: 'string',
|
||||
},
|
||||
primaryPhoneNumber: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
type: 'object',
|
||||
},
|
||||
fieldEmail: {
|
||||
type: 'string',
|
||||
format: 'email',
|
||||
@ -195,6 +209,20 @@ describe('computeSchemaComponents', () => {
|
||||
fieldPhone: {
|
||||
type: 'string',
|
||||
},
|
||||
fieldPhones: {
|
||||
properties: {
|
||||
additionalPhones: {
|
||||
type: 'object',
|
||||
},
|
||||
primaryPhoneCountryCode: {
|
||||
type: 'string',
|
||||
},
|
||||
primaryPhoneNumber: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
type: 'object',
|
||||
},
|
||||
fieldEmail: {
|
||||
type: 'string',
|
||||
format: 'email',
|
||||
@ -356,6 +384,20 @@ describe('computeSchemaComponents', () => {
|
||||
fieldPhone: {
|
||||
type: 'string',
|
||||
},
|
||||
fieldPhones: {
|
||||
properties: {
|
||||
additionalPhones: {
|
||||
type: 'object',
|
||||
},
|
||||
primaryPhoneCountryCode: {
|
||||
type: 'string',
|
||||
},
|
||||
primaryPhoneNumber: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
type: 'object',
|
||||
},
|
||||
fieldEmail: {
|
||||
type: 'string',
|
||||
format: 'email',
|
||||
|
||||
@ -137,6 +137,7 @@ const getSchemaComponentsProperties = ({
|
||||
case FieldMetadataType.ADDRESS:
|
||||
case FieldMetadataType.ACTOR:
|
||||
case FieldMetadataType.EMAILS:
|
||||
case FieldMetadataType.PHONES:
|
||||
itemProperty = {
|
||||
type: 'object',
|
||||
properties: compositeTypeDefinitions
|
||||
|
||||
@ -7,6 +7,7 @@ import { emailsCompositeType } from 'src/engine/metadata-modules/field-metadata/
|
||||
import { fullNameCompositeType } from 'src/engine/metadata-modules/field-metadata/composite-types/full-name.composite-type';
|
||||
import { linkCompositeType } from 'src/engine/metadata-modules/field-metadata/composite-types/link.composite-type';
|
||||
import { linksCompositeType } from 'src/engine/metadata-modules/field-metadata/composite-types/links.composite-type';
|
||||
import { phonesCompositeType } from 'src/engine/metadata-modules/field-metadata/composite-types/phones.composite-type';
|
||||
import { FieldMetadataType } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
|
||||
export const compositeTypeDefinitions = new Map<
|
||||
@ -20,4 +21,5 @@ export const compositeTypeDefinitions = new Map<
|
||||
[FieldMetadataType.ADDRESS, addressCompositeType],
|
||||
[FieldMetadataType.ACTOR, actorCompositeType],
|
||||
[FieldMetadataType.EMAILS, emailsCompositeType],
|
||||
[FieldMetadataType.PHONES, phonesCompositeType],
|
||||
]);
|
||||
|
||||
@ -0,0 +1,33 @@
|
||||
import { CompositeType } from 'src/engine/metadata-modules/field-metadata/interfaces/composite-type.interface';
|
||||
|
||||
import { FieldMetadataType } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
|
||||
export const phonesCompositeType: CompositeType = {
|
||||
type: FieldMetadataType.PHONES,
|
||||
properties: [
|
||||
{
|
||||
name: 'primaryPhoneNumber',
|
||||
type: FieldMetadataType.TEXT,
|
||||
hidden: false,
|
||||
isRequired: false,
|
||||
},
|
||||
{
|
||||
name: 'primaryPhoneCountryCode',
|
||||
type: FieldMetadataType.TEXT,
|
||||
hidden: false,
|
||||
isRequired: false,
|
||||
},
|
||||
{
|
||||
name: 'additionalPhones',
|
||||
type: FieldMetadataType.RAW_JSON,
|
||||
hidden: false,
|
||||
isRequired: false,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export type PhonesMetadata = {
|
||||
primaryPhoneNumber: string;
|
||||
primaryPhoneCountryCode: string;
|
||||
additionalPhones: object | null;
|
||||
};
|
||||
@ -185,3 +185,17 @@ export class FieldMetadataDefaultValueEmails {
|
||||
@IsObject()
|
||||
additionalEmails: string[] | null;
|
||||
}
|
||||
|
||||
export class FieldMetadataDefaultValuePhones {
|
||||
@ValidateIf((_object, value) => value !== null)
|
||||
@IsQuotedString()
|
||||
primaryPhoneNumber: string | null;
|
||||
|
||||
@ValidateIf((_object, value) => value !== null)
|
||||
@IsQuotedString()
|
||||
primaryPhoneCountryCode: string | null;
|
||||
|
||||
@ValidateIf((_object, value) => value !== null)
|
||||
@IsObject()
|
||||
additionalPhones: object | null;
|
||||
}
|
||||
|
||||
@ -25,6 +25,7 @@ export enum FieldMetadataType {
|
||||
UUID = 'UUID',
|
||||
TEXT = 'TEXT',
|
||||
PHONE = 'PHONE',
|
||||
PHONES = 'PHONES',
|
||||
EMAIL = 'EMAIL',
|
||||
EMAILS = 'EMAILS',
|
||||
DATE_TIME = 'DATE_TIME',
|
||||
|
||||
@ -10,6 +10,7 @@ import {
|
||||
FieldMetadataDefaultValueLinks,
|
||||
FieldMetadataDefaultValueNowFunction,
|
||||
FieldMetadataDefaultValueNumber,
|
||||
FieldMetadataDefaultValuePhones,
|
||||
FieldMetadataDefaultValueRawJson,
|
||||
FieldMetadataDefaultValueRichText,
|
||||
FieldMetadataDefaultValueString,
|
||||
@ -27,6 +28,7 @@ type FieldMetadataDefaultValueMapping = {
|
||||
| FieldMetadataDefaultValueUuidFunction;
|
||||
[FieldMetadataType.TEXT]: FieldMetadataDefaultValueString;
|
||||
[FieldMetadataType.PHONE]: FieldMetadataDefaultValueString;
|
||||
[FieldMetadataType.PHONES]: FieldMetadataDefaultValuePhones;
|
||||
[FieldMetadataType.EMAIL]: FieldMetadataDefaultValueString;
|
||||
[FieldMetadataType.EMAILS]: FieldMetadataDefaultValueEmails;
|
||||
[FieldMetadataType.DATE_TIME]:
|
||||
|
||||
@ -47,6 +47,12 @@ export function generateDefaultValue(
|
||||
primaryLinkUrl: "''",
|
||||
secondaryLinks: null,
|
||||
};
|
||||
case FieldMetadataType.PHONES:
|
||||
return {
|
||||
primaryPhoneNumber: "''",
|
||||
primaryPhoneCountryCode: "''",
|
||||
additionalPhones: null,
|
||||
};
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -9,7 +9,8 @@ export const isCompositeFieldMetadataType = (
|
||||
| FieldMetadataType.ADDRESS
|
||||
| FieldMetadataType.LINKS
|
||||
| FieldMetadataType.ACTOR
|
||||
| FieldMetadataType.EMAILS => {
|
||||
| FieldMetadataType.EMAILS
|
||||
| FieldMetadataType.PHONES => {
|
||||
return [
|
||||
FieldMetadataType.LINK,
|
||||
FieldMetadataType.CURRENCY,
|
||||
@ -18,5 +19,6 @@ export const isCompositeFieldMetadataType = (
|
||||
FieldMetadataType.LINKS,
|
||||
FieldMetadataType.ACTOR,
|
||||
FieldMetadataType.EMAILS,
|
||||
FieldMetadataType.PHONES,
|
||||
].includes(type);
|
||||
};
|
||||
|
||||
@ -19,6 +19,7 @@ import {
|
||||
FieldMetadataDefaultValueLinks,
|
||||
FieldMetadataDefaultValueNowFunction,
|
||||
FieldMetadataDefaultValueNumber,
|
||||
FieldMetadataDefaultValuePhones,
|
||||
FieldMetadataDefaultValueRawJson,
|
||||
FieldMetadataDefaultValueString,
|
||||
FieldMetadataDefaultValueStringArray,
|
||||
@ -55,6 +56,7 @@ export const defaultValueValidatorsMap = {
|
||||
[FieldMetadataType.LINKS]: [FieldMetadataDefaultValueLinks],
|
||||
[FieldMetadataType.ACTOR]: [FieldMetadataDefaultActor],
|
||||
[FieldMetadataType.EMAILS]: [FieldMetadataDefaultValueEmails],
|
||||
[FieldMetadataType.PHONES]: [FieldMetadataDefaultValuePhones],
|
||||
};
|
||||
|
||||
type ValidationResult = {
|
||||
|
||||
@ -24,7 +24,8 @@ export type CompositeFieldMetadataType =
|
||||
| FieldMetadataType.FULL_NAME
|
||||
| FieldMetadataType.LINK
|
||||
| FieldMetadataType.LINKS
|
||||
| FieldMetadataType.EMAILS;
|
||||
| FieldMetadataType.EMAILS
|
||||
| FieldMetadataType.PHONES;
|
||||
|
||||
@Injectable()
|
||||
export class CompositeColumnActionFactory extends ColumnActionAbstractFactory<CompositeFieldMetadataType> {
|
||||
|
||||
@ -101,6 +101,10 @@ export class WorkspaceMigrationFactory {
|
||||
FieldMetadataType.EMAILS,
|
||||
{ factory: this.compositeColumnActionFactory },
|
||||
],
|
||||
[
|
||||
FieldMetadataType.PHONES,
|
||||
{ factory: this.compositeColumnActionFactory },
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user