Add composite Emails field and forbid creation of Email field type (#6689)
### Description
1.
- We are introducing new field type(Emails)
- We are Forbiding creation of Email field
- We Added support for filtering and sorting on Emails 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\
Follow the below steps for testing locally:\
1. Checkout to TWENTY-6261\
2. Reset database using "npx nx database:reset twenty-server" command\
3. Run both the backend and frontend app\
4. Go to Settings/Data model and choose one of the standard objects like
people\
5. Click on Add Field button and choose Emails as the field type
\
### Refs
#6261\
\
### Demo
\
<https://www.loom.com/share/22979acac8134ed390fef93cc56fe07c?sid=adafba94-840d-4f01-872c-dc9ec256d987>
Co-authored-by: gitstart-twenty <gitstart-twenty@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
c87ccfa3c7
commit
7a9a43b85c
@ -205,12 +205,19 @@ const fieldActorMock = {
|
||||
name: '',
|
||||
},
|
||||
};
|
||||
const fieldEmailsMock = {
|
||||
name: 'fieldEmails',
|
||||
type: FieldMetadataType.EMAILS,
|
||||
isNullable: false,
|
||||
defaultValue: [{ primaryEmail: '', additionalEmails: {} }],
|
||||
};
|
||||
|
||||
export const fields = [
|
||||
fieldUuidMock,
|
||||
fieldTextMock,
|
||||
fieldPhoneMock,
|
||||
fieldEmailMock,
|
||||
fieldEmailsMock,
|
||||
fieldDateTimeMock,
|
||||
fieldDateMock,
|
||||
fieldBooleanMock,
|
||||
|
||||
@ -144,5 +144,13 @@ export const mapFieldMetadataToGraphqlQuery = (
|
||||
name
|
||||
}
|
||||
`;
|
||||
} else if (fieldType === FieldMetadataType.EMAILS) {
|
||||
return `
|
||||
${field.name}
|
||||
{
|
||||
primaryEmail
|
||||
additionalEmails
|
||||
}
|
||||
`;
|
||||
}
|
||||
};
|
||||
|
||||
@ -147,6 +147,17 @@ describe('computeSchemaComponents', () => {
|
||||
},
|
||||
type: 'object',
|
||||
},
|
||||
fieldEmails: {
|
||||
properties: {
|
||||
primaryEmail: {
|
||||
type: 'string',
|
||||
},
|
||||
additionalEmails: {
|
||||
type: 'object',
|
||||
},
|
||||
},
|
||||
type: 'object',
|
||||
},
|
||||
},
|
||||
},
|
||||
'ObjectName with Relations': {
|
||||
|
||||
@ -72,6 +72,7 @@ const getSchemaComponentsProperties = (
|
||||
case FieldMetadataType.FULL_NAME:
|
||||
case FieldMetadataType.ADDRESS:
|
||||
case FieldMetadataType.ACTOR:
|
||||
case FieldMetadataType.EMAILS:
|
||||
itemProperty = {
|
||||
type: 'object',
|
||||
properties: compositeTypeDefinitions
|
||||
|
||||
@ -0,0 +1,26 @@
|
||||
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 emailsCompositeType: CompositeType = {
|
||||
type: FieldMetadataType.EMAILS,
|
||||
properties: [
|
||||
{
|
||||
name: 'primaryEmail',
|
||||
type: FieldMetadataType.TEXT,
|
||||
hidden: false,
|
||||
isRequired: false,
|
||||
},
|
||||
{
|
||||
name: 'additionalEmails',
|
||||
type: FieldMetadataType.RAW_JSON,
|
||||
hidden: false,
|
||||
isRequired: false,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export type EmailsMetadata = {
|
||||
primaryEmail: string;
|
||||
additionalEmails: string[] | null;
|
||||
};
|
||||
@ -4,6 +4,7 @@ import { FieldMetadataInterface } from 'src/engine/metadata-modules/field-metada
|
||||
import { actorCompositeType } from 'src/engine/metadata-modules/field-metadata/composite-types/actor.composite-type';
|
||||
import { addressCompositeType } from 'src/engine/metadata-modules/field-metadata/composite-types/address.composite-type';
|
||||
import { currencyCompositeType } from 'src/engine/metadata-modules/field-metadata/composite-types/currency.composite-type';
|
||||
import { emailsCompositeType } from 'src/engine/metadata-modules/field-metadata/composite-types/emails.composite-type';
|
||||
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';
|
||||
@ -23,4 +24,5 @@ export const compositeTypeDefinitions = new Map<
|
||||
[FieldMetadataType.FULL_NAME, fullNameCompositeType],
|
||||
[FieldMetadataType.ADDRESS, addressCompositeType],
|
||||
[FieldMetadataType.ACTOR, actorCompositeType],
|
||||
[FieldMetadataType.EMAILS, emailsCompositeType],
|
||||
]);
|
||||
|
||||
@ -175,3 +175,13 @@ export class FieldMetadataDefaultActor {
|
||||
@IsString()
|
||||
name: string;
|
||||
}
|
||||
|
||||
export class FieldMetadataDefaultValueEmails {
|
||||
@ValidateIf((_object, value) => value !== null)
|
||||
@IsQuotedString()
|
||||
primaryEmail: string | null;
|
||||
|
||||
@ValidateIf((_object, value) => value !== null)
|
||||
@IsObject()
|
||||
additionalEmails: string[] | null;
|
||||
}
|
||||
|
||||
@ -26,6 +26,7 @@ export enum FieldMetadataType {
|
||||
TEXT = 'TEXT',
|
||||
PHONE = 'PHONE',
|
||||
EMAIL = 'EMAIL',
|
||||
EMAILS = 'EMAILS',
|
||||
DATE_TIME = 'DATE_TIME',
|
||||
DATE = 'DATE',
|
||||
BOOLEAN = 'BOOLEAN',
|
||||
|
||||
@ -143,6 +143,13 @@ export class FieldMetadataService extends TypeOrmQueryService<FieldMetadataEntit
|
||||
);
|
||||
}
|
||||
|
||||
if (fieldMetadataInput.type === FieldMetadataType.EMAIL) {
|
||||
throw new FieldMetadataException(
|
||||
'"Email" field types are being deprecated, please use Emails type instead',
|
||||
FieldMetadataExceptionCode.INVALID_FIELD_INPUT,
|
||||
);
|
||||
}
|
||||
|
||||
this.validateFieldMetadataInput<CreateFieldInput>(
|
||||
fieldMetadataInput,
|
||||
objectMetadata,
|
||||
|
||||
@ -4,6 +4,7 @@ import {
|
||||
FieldMetadataDefaultValueBoolean,
|
||||
FieldMetadataDefaultValueCurrency,
|
||||
FieldMetadataDefaultValueDateTime,
|
||||
FieldMetadataDefaultValueEmails,
|
||||
FieldMetadataDefaultValueFullName,
|
||||
FieldMetadataDefaultValueLink,
|
||||
FieldMetadataDefaultValueLinks,
|
||||
@ -27,6 +28,7 @@ type FieldMetadataDefaultValueMapping = {
|
||||
[FieldMetadataType.TEXT]: FieldMetadataDefaultValueString;
|
||||
[FieldMetadataType.PHONE]: FieldMetadataDefaultValueString;
|
||||
[FieldMetadataType.EMAIL]: FieldMetadataDefaultValueString;
|
||||
[FieldMetadataType.EMAILS]: FieldMetadataDefaultValueEmails;
|
||||
[FieldMetadataType.DATE_TIME]:
|
||||
| FieldMetadataDefaultValueDateTime
|
||||
| FieldMetadataDefaultValueNowFunction;
|
||||
|
||||
@ -10,6 +10,11 @@ export function generateDefaultValue(
|
||||
case FieldMetadataType.PHONE:
|
||||
case FieldMetadataType.EMAIL:
|
||||
return "''";
|
||||
case FieldMetadataType.EMAILS:
|
||||
return {
|
||||
primaryEmail: "''",
|
||||
additionalEmails: null,
|
||||
};
|
||||
case FieldMetadataType.FULL_NAME:
|
||||
return {
|
||||
firstName: "''",
|
||||
|
||||
@ -8,7 +8,8 @@ export const isCompositeFieldMetadataType = (
|
||||
| FieldMetadataType.FULL_NAME
|
||||
| FieldMetadataType.ADDRESS
|
||||
| FieldMetadataType.LINKS
|
||||
| FieldMetadataType.ACTOR => {
|
||||
| FieldMetadataType.ACTOR
|
||||
| FieldMetadataType.EMAILS => {
|
||||
return [
|
||||
FieldMetadataType.LINK,
|
||||
FieldMetadataType.CURRENCY,
|
||||
@ -16,5 +17,6 @@ export const isCompositeFieldMetadataType = (
|
||||
FieldMetadataType.ADDRESS,
|
||||
FieldMetadataType.LINKS,
|
||||
FieldMetadataType.ACTOR,
|
||||
FieldMetadataType.EMAILS,
|
||||
].includes(type);
|
||||
};
|
||||
|
||||
@ -13,6 +13,7 @@ import {
|
||||
FieldMetadataDefaultValueCurrency,
|
||||
FieldMetadataDefaultValueDate,
|
||||
FieldMetadataDefaultValueDateTime,
|
||||
FieldMetadataDefaultValueEmails,
|
||||
FieldMetadataDefaultValueFullName,
|
||||
FieldMetadataDefaultValueLink,
|
||||
FieldMetadataDefaultValueLinks,
|
||||
@ -53,6 +54,7 @@ export const defaultValueValidatorsMap = {
|
||||
[FieldMetadataType.RAW_JSON]: [FieldMetadataDefaultValueRawJson],
|
||||
[FieldMetadataType.LINKS]: [FieldMetadataDefaultValueLinks],
|
||||
[FieldMetadataType.ACTOR]: [FieldMetadataDefaultActor],
|
||||
[FieldMetadataType.EMAILS]: [FieldMetadataDefaultValueEmails],
|
||||
};
|
||||
|
||||
type ValidationResult = {
|
||||
|
||||
@ -23,7 +23,8 @@ export type CompositeFieldMetadataType =
|
||||
| FieldMetadataType.CURRENCY
|
||||
| FieldMetadataType.FULL_NAME
|
||||
| FieldMetadataType.LINK
|
||||
| FieldMetadataType.LINKS;
|
||||
| FieldMetadataType.LINKS
|
||||
| FieldMetadataType.EMAILS;
|
||||
|
||||
@Injectable()
|
||||
export class CompositeColumnActionFactory extends ColumnActionAbstractFactory<CompositeFieldMetadataType> {
|
||||
|
||||
@ -97,6 +97,10 @@ export class WorkspaceMigrationFactory {
|
||||
],
|
||||
[FieldMetadataType.LINKS, { factory: this.compositeColumnActionFactory }],
|
||||
[FieldMetadataType.ACTOR, { factory: this.compositeColumnActionFactory }],
|
||||
[
|
||||
FieldMetadataType.EMAILS,
|
||||
{ factory: this.compositeColumnActionFactory },
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user