feat: rename RICH_TEXT_V2 → RICH_TEXT in codebase (keep DB value) (#18628)

## Summary

- Renames the `FieldMetadataType` enum key from `RICH_TEXT_V2` to
`RICH_TEXT` across the entire codebase, while keeping the underlying
string value as `'RICH_TEXT_V2'` to maintain PostgreSQL database
compatibility
- Renames all related types, guards, hooks, components, and files from
`*RichTextV2*` / `*rich-text-v2*` to `*RichText*` / `*rich-text*` (e.g.
`FormRichTextV2FieldInput` → `FormRichTextFieldInput`,
`isFieldRichTextV2` → `isFieldRichText`)
- Updates generated files (GraphQL schema, SDK types) to use the new key
while preserving the `RICH_TEXT_V2` string value for DB/API layer
- Updates i18n locale files, test snapshots, and integration tests to
reflect the rename

## Context

The legacy `RICH_TEXT` (V1) field type was deprecated and migrated to
`TEXT` in a previous PR (#18623). With V1 gone, the `RICH_TEXT_V2`
naming is no longer necessary — `RICH_TEXT` is now the canonical name.
The DB enum value stays `'RICH_TEXT_V2'` to avoid confusion with the
just-deprecated V1 type and to prevent a database migration.

## Test plan

- [x] `twenty-server` typecheck passes
- [x] `twenty-front` typecheck passes (only pre-existing Apollo client
errors remain)
- [x] `twenty-server` lint passes
- [x] `twenty-front` lint passes
- [x] `twenty-shared` build passes
- [ ] CI passes


Made with [Cursor](https://cursor.com)
This commit is contained in:
Charles Bochet
2026-03-13 19:07:55 +01:00
committed by GitHub
parent 3054679411
commit d9eb317bb5
129 changed files with 452 additions and 401 deletions
@@ -30,7 +30,7 @@ type AnalysisResult = {
commitments: Commitment[]; commitments: Commitment[];
}; };
type RichTextV2Data = { type RichTextData = {
markdown: string; markdown: string;
blocknote: null; blocknote: null;
}; };
@@ -123,7 +123,7 @@ const createNoteInTwenty = async (
bodyV2: { bodyV2: {
markdown: noteBodyMarkdown, markdown: noteBodyMarkdown,
blocknote: null, blocknote: null,
} satisfies RichTextV2Data, } satisfies RichTextData,
}; };
try { try {
@@ -159,7 +159,7 @@ const createTaskInTwenty = async (
const taskData: { const taskData: {
title: string; title: string;
bodyV2: RichTextV2Data; bodyV2: RichTextData;
dueAt?: string; dueAt?: string;
} = { } = {
title: actionItem.title, title: actionItem.title,
@@ -32,7 +32,7 @@ type AnalysisResult = {
commitments: Commitment[]; commitments: Commitment[];
}; };
type RichTextV2Data = { type RichTextData = {
markdown: string; markdown: string;
blocknote: null; blocknote: null;
}; };
@@ -362,7 +362,7 @@ const createNoteInTwenty = async (
bodyV2: { bodyV2: {
markdown: noteBodyMarkdown, markdown: noteBodyMarkdown,
blocknote: null, blocknote: null,
} satisfies RichTextV2Data, } satisfies RichTextData,
}; };
try { try {
@@ -451,7 +451,7 @@ const createTaskInTwenty = async (
const taskData: { const taskData: {
title: string; title: string;
bodyV2: RichTextV2Data; bodyV2: RichTextData;
dueAt?: string; dueAt?: string;
assigneeId?: string; assigneeId?: string;
} = { } = {
@@ -81,7 +81,7 @@ export default defineObject({
}, },
{ {
universalIdentifier: TRANSCRIPT_FIELD_UNIVERSAL_IDENTIFIER, universalIdentifier: TRANSCRIPT_FIELD_UNIVERSAL_IDENTIFIER,
type: FieldType.RICH_TEXT_V2, type: FieldType.RICH_TEXT,
name: 'transcript', name: 'transcript',
label: 'Transcript', label: 'Transcript',
description: 'Human-readable transcript of the call', description: 'Human-readable transcript of the call',
@@ -114,7 +114,7 @@ export default defineObject({
}, },
{ {
universalIdentifier: SUMMARY_FIELD_UNIVERSAL_IDENTIFIER, universalIdentifier: SUMMARY_FIELD_UNIVERSAL_IDENTIFIER,
type: FieldType.RICH_TEXT_V2, type: FieldType.RICH_TEXT,
name: 'summary', name: 'summary',
label: 'Summary', label: 'Summary',
description: 'AI-generated summary of the call', description: 'AI-generated summary of the call',
@@ -16,7 +16,7 @@ Use this skill when a user asks you to summarize, analyze, or extract insights f
## How to Access the Data ## How to Access the Data
1. Use \`find_one_callRecording\` to fetch the call recording by its ID. 1. Use \`find_one_callRecording\` to fetch the call recording by its ID.
2. Read the \`transcript\` field (RICH_TEXT_V2, markdown format) which contains the full conversation. 2. Read the \`transcript\` field (RICH_TEXT, markdown format) which contains the full conversation.
3. The transcript uses the format: **Speaker Name:** spoken text 3. The transcript uses the format: **Speaker Name:** spoken text
## What to Produce ## What to Produce
File diff suppressed because one or more lines are too long
@@ -96,12 +96,12 @@ describe('filterSortableFieldMetadataItems', () => {
expect(filterSortableFieldMetadataItems(field)).toBe(false); expect(filterSortableFieldMetadataItems(field)).toBe(false);
}); });
it('should NOT allow unsortable field types like RICH_TEXT_V2', () => { it('should NOT allow unsortable field types like RICH_TEXT', () => {
const field = { const field = {
type: FieldMetadataType.RICH_TEXT_V2, type: FieldMetadataType.RICH_TEXT,
isSystem: false, isSystem: false,
isActive: true, isActive: true,
name: 'richTextV2', name: 'richText',
}; };
expect(filterSortableFieldMetadataItems(field)).toBe(false); expect(filterSortableFieldMetadataItems(field)).toBe(false);
@@ -310,7 +310,7 @@ ${mapObjectMetadataToGraphQLQuery({
}`; }`;
} }
if (fieldType === FieldMetadataType.RICH_TEXT_V2) { if (fieldType === FieldMetadataType.RICH_TEXT) {
return `${gqlField} return `${gqlField}
{ {
blocknote blocknote
@@ -65,7 +65,7 @@ export const useFieldListFieldMetadataItems = ({
) )
.filter( .filter(
(fieldMetadataItem) => (fieldMetadataItem) =>
fieldMetadataItem.type !== FieldMetadataType.RICH_TEXT_V2, fieldMetadataItem.type !== FieldMetadataType.RICH_TEXT,
), ),
(fieldMetadataItem) => (fieldMetadataItem) =>
fieldMetadataItem.type === FieldMetadataType.RELATION || fieldMetadataItem.type === FieldMetadataType.RELATION ||
@@ -10,7 +10,7 @@ import { LinksFieldDisplay } from '@/object-record/record-field/ui/meta-types/di
import { PhonesFieldDisplay } from '@/object-record/record-field/ui/meta-types/display/components/PhonesFieldDisplay'; import { PhonesFieldDisplay } from '@/object-record/record-field/ui/meta-types/display/components/PhonesFieldDisplay';
import { RatingFieldDisplay } from '@/object-record/record-field/ui/meta-types/display/components/RatingFieldDisplay'; import { RatingFieldDisplay } from '@/object-record/record-field/ui/meta-types/display/components/RatingFieldDisplay';
import { RelationFromManyFieldDisplay } from '@/object-record/record-field/ui/meta-types/display/components/RelationFromManyFieldDisplay'; import { RelationFromManyFieldDisplay } from '@/object-record/record-field/ui/meta-types/display/components/RelationFromManyFieldDisplay';
import { RichTextV2FieldDisplay } from '@/object-record/record-field/ui/meta-types/display/components/RichTextV2FieldDisplay'; import { RichTextFieldDisplay } from '@/object-record/record-field/ui/meta-types/display/components/RichTextFieldDisplay';
import { isFieldIdentifierDisplay } from '@/object-record/record-field/ui/meta-types/display/utils/isFieldIdentifierDisplay'; import { isFieldIdentifierDisplay } from '@/object-record/record-field/ui/meta-types/display/utils/isFieldIdentifierDisplay';
import { isFieldActor } from '@/object-record/record-field/ui/types/guards/isFieldActor'; import { isFieldActor } from '@/object-record/record-field/ui/types/guards/isFieldActor';
import { isFieldArray } from '@/object-record/record-field/ui/types/guards/isFieldArray'; import { isFieldArray } from '@/object-record/record-field/ui/types/guards/isFieldArray';
@@ -20,7 +20,7 @@ import { isFieldFiles } from '@/object-record/record-field/ui/types/guards/isFie
import { isFieldLinks } from '@/object-record/record-field/ui/types/guards/isFieldLinks'; import { isFieldLinks } from '@/object-record/record-field/ui/types/guards/isFieldLinks';
import { isFieldPhones } from '@/object-record/record-field/ui/types/guards/isFieldPhones'; import { isFieldPhones } from '@/object-record/record-field/ui/types/guards/isFieldPhones';
import { isFieldRating } from '@/object-record/record-field/ui/types/guards/isFieldRating'; import { isFieldRating } from '@/object-record/record-field/ui/types/guards/isFieldRating';
import { isFieldRichTextV2 } from '@/object-record/record-field/ui/types/guards/isFieldRichTextV2'; import { isFieldRichText } from '@/object-record/record-field/ui/types/guards/isFieldRichText';
import { MorphRelationManyToOneFieldDisplay } from '@/object-record/record-field/ui/meta-types/display/components/MorphRelationManyToOneFieldDisplay'; import { MorphRelationManyToOneFieldDisplay } from '@/object-record/record-field/ui/meta-types/display/components/MorphRelationManyToOneFieldDisplay';
import { MorphRelationOneToManyFieldDisplay } from '@/object-record/record-field/ui/meta-types/display/components/MorphRelationOneToManyFieldDisplay'; import { MorphRelationOneToManyFieldDisplay } from '@/object-record/record-field/ui/meta-types/display/components/MorphRelationOneToManyFieldDisplay';
@@ -110,8 +110,8 @@ export const FieldDisplay = () => {
<BooleanFieldDisplay /> <BooleanFieldDisplay />
) : isFieldRating(fieldDefinition) ? ( ) : isFieldRating(fieldDefinition) ? (
<RatingFieldDisplay readonly={isRecordFieldReadOnly} /> <RatingFieldDisplay readonly={isRecordFieldReadOnly} />
) : isFieldRichTextV2(fieldDefinition) ? ( ) : isFieldRichText(fieldDefinition) ? (
<RichTextV2FieldDisplay /> <RichTextFieldDisplay />
) : isFieldActor(fieldDefinition) ? ( ) : isFieldActor(fieldDefinition) ? (
<ActorFieldDisplay /> <ActorFieldDisplay />
) : isFieldArray(fieldDefinition) ? ( ) : isFieldArray(fieldDefinition) ? (
@@ -36,7 +36,7 @@ import { isFieldRating } from '@/object-record/record-field/ui/types/guards/isFi
import { isFieldRawJson } from '@/object-record/record-field/ui/types/guards/isFieldRawJson'; import { isFieldRawJson } from '@/object-record/record-field/ui/types/guards/isFieldRawJson';
import { isFieldRelationManyToOne } from '@/object-record/record-field/ui/types/guards/isFieldRelationManyToOne'; import { isFieldRelationManyToOne } from '@/object-record/record-field/ui/types/guards/isFieldRelationManyToOne';
import { isFieldRelationOneToMany } from '@/object-record/record-field/ui/types/guards/isFieldRelationOneToMany'; import { isFieldRelationOneToMany } from '@/object-record/record-field/ui/types/guards/isFieldRelationOneToMany';
import { isFieldRichTextV2 } from '@/object-record/record-field/ui/types/guards/isFieldRichTextV2'; import { isFieldRichText } from '@/object-record/record-field/ui/types/guards/isFieldRichText';
import { isFieldSelect } from '@/object-record/record-field/ui/types/guards/isFieldSelect'; import { isFieldSelect } from '@/object-record/record-field/ui/types/guards/isFieldSelect';
import { FieldContext } from '@/object-record/record-field/ui/contexts/FieldContext'; import { FieldContext } from '@/object-record/record-field/ui/contexts/FieldContext';
import { BooleanFieldInput } from '@/object-record/record-field/ui/meta-types/input/components/BooleanFieldInput'; import { BooleanFieldInput } from '@/object-record/record-field/ui/meta-types/input/components/BooleanFieldInput';
@@ -94,7 +94,7 @@ export const FieldInput = () => {
<RawJsonFieldInput /> <RawJsonFieldInput />
) : isFieldArray(fieldDefinition) ? ( ) : isFieldArray(fieldDefinition) ? (
<ArrayFieldInput /> <ArrayFieldInput />
) : isFieldRichTextV2(fieldDefinition) ? ( ) : isFieldRichText(fieldDefinition) ? (
<RichTextFieldInput /> <RichTextFieldInput />
) : ( ) : (
<></> <></>
@@ -13,7 +13,7 @@ import { FormNumberFieldInput } from '@/object-record/record-field/ui/form-types
import { FormPhoneFieldInput } from '@/object-record/record-field/ui/form-types/components/FormPhoneFieldInput'; import { FormPhoneFieldInput } from '@/object-record/record-field/ui/form-types/components/FormPhoneFieldInput';
import { FormRawJsonFieldInput } from '@/object-record/record-field/ui/form-types/components/FormRawJsonFieldInput'; import { FormRawJsonFieldInput } from '@/object-record/record-field/ui/form-types/components/FormRawJsonFieldInput';
import { FormRelationToOneFieldInput } from '@/object-record/record-field/ui/form-types/components/FormRelationToOneFieldInput'; import { FormRelationToOneFieldInput } from '@/object-record/record-field/ui/form-types/components/FormRelationToOneFieldInput';
import { FormRichTextV2FieldInput } from '@/object-record/record-field/ui/form-types/components/FormRichTextV2FieldInput'; import { FormRichTextFieldInput } from '@/object-record/record-field/ui/form-types/components/FormRichTextFieldInput';
import { FormSelectFieldInput } from '@/object-record/record-field/ui/form-types/components/FormSelectFieldInput'; import { FormSelectFieldInput } from '@/object-record/record-field/ui/form-types/components/FormSelectFieldInput';
import { FormTextFieldInput } from '@/object-record/record-field/ui/form-types/components/FormTextFieldInput'; import { FormTextFieldInput } from '@/object-record/record-field/ui/form-types/components/FormTextFieldInput';
import { FormUuidFieldInput } from '@/object-record/record-field/ui/form-types/components/FormUuidFieldInput'; import { FormUuidFieldInput } from '@/object-record/record-field/ui/form-types/components/FormUuidFieldInput';
@@ -30,7 +30,7 @@ import {
type FieldPhonesValue, type FieldPhonesValue,
type FieldRelationToOneValue, type FieldRelationToOneValue,
type FieldRelationValue, type FieldRelationValue,
type FieldRichTextV2Value, type FieldRichTextValue,
type FormFieldCurrencyValue, type FormFieldCurrencyValue,
} from '@/object-record/record-field/ui/types/FieldMetadata'; } from '@/object-record/record-field/ui/types/FieldMetadata';
import { isFieldAddress } from '@/object-record/record-field/ui/types/guards/isFieldAddress'; import { isFieldAddress } from '@/object-record/record-field/ui/types/guards/isFieldAddress';
@@ -48,7 +48,7 @@ import { isFieldNumber } from '@/object-record/record-field/ui/types/guards/isFi
import { isFieldPhones } from '@/object-record/record-field/ui/types/guards/isFieldPhones'; import { isFieldPhones } from '@/object-record/record-field/ui/types/guards/isFieldPhones';
import { isFieldRawJson } from '@/object-record/record-field/ui/types/guards/isFieldRawJson'; import { isFieldRawJson } from '@/object-record/record-field/ui/types/guards/isFieldRawJson';
import { isFieldRelationManyToOne } from '@/object-record/record-field/ui/types/guards/isFieldRelationManyToOne'; import { isFieldRelationManyToOne } from '@/object-record/record-field/ui/types/guards/isFieldRelationManyToOne';
import { isFieldRichTextV2 } from '@/object-record/record-field/ui/types/guards/isFieldRichTextV2'; import { isFieldRichText } from '@/object-record/record-field/ui/types/guards/isFieldRichText';
import { isFieldSelect } from '@/object-record/record-field/ui/types/guards/isFieldSelect'; import { isFieldSelect } from '@/object-record/record-field/ui/types/guards/isFieldSelect';
import { isFieldText } from '@/object-record/record-field/ui/types/guards/isFieldText'; import { isFieldText } from '@/object-record/record-field/ui/types/guards/isFieldText';
import { isFieldUuid } from '@/object-record/record-field/ui/types/guards/isFieldUuid'; import { isFieldUuid } from '@/object-record/record-field/ui/types/guards/isFieldUuid';
@@ -220,10 +220,10 @@ export const FormFieldInput = ({
VariablePicker={VariablePicker} VariablePicker={VariablePicker}
readonly={readonly} readonly={readonly}
/> />
) : isFieldRichTextV2(field) ? ( ) : isFieldRichText(field) ? (
<FormRichTextV2FieldInput <FormRichTextFieldInput
label={field.label} label={field.label}
defaultValue={defaultValue as FieldRichTextV2Value | undefined} defaultValue={defaultValue as FieldRichTextValue | undefined}
onChange={onChange} onChange={onChange}
VariablePicker={VariablePicker} VariablePicker={VariablePicker}
readonly={readonly} readonly={readonly}
@@ -1,22 +1,22 @@
import { FormAdvancedTextFieldInput } from '@/object-record/record-field/ui/form-types/components/FormAdvancedTextFieldInput'; import { FormAdvancedTextFieldInput } from '@/object-record/record-field/ui/form-types/components/FormAdvancedTextFieldInput';
import { type VariablePickerComponent } from '@/object-record/record-field/ui/form-types/types/VariablePickerComponent'; import { type VariablePickerComponent } from '@/object-record/record-field/ui/form-types/types/VariablePickerComponent';
import { type FieldRichTextV2Value } from '@/object-record/record-field/ui/types/FieldMetadata'; import { type FieldRichTextValue } from '@/object-record/record-field/ui/types/FieldMetadata';
type FormRichTextV2FieldInputProps = { type FormRichTextFieldInputProps = {
label?: string; label?: string;
error?: string; error?: string;
hint?: string; hint?: string;
defaultValue: FieldRichTextV2Value | undefined; defaultValue: FieldRichTextValue | undefined;
onChange: (value: FieldRichTextV2Value) => void; onChange: (value: FieldRichTextValue) => void;
onBlur?: () => void; onBlur?: () => void;
readonly?: boolean; readonly?: boolean;
placeholder?: string; placeholder?: string;
VariablePicker?: VariablePickerComponent; VariablePicker?: VariablePickerComponent;
}; };
const RICH_TEXT_V2_EDITOR_MIN_HEIGHT = 340; const RICH_TEXT_EDITOR_MIN_HEIGHT = 340;
const RICH_TEXT_V2_EDITOR_MAX_WIDTH = 600; const RICH_TEXT_EDITOR_MAX_WIDTH = 600;
const mapTipTapToBlockNote = (tiptapJson: string): string => { const mapTipTapToBlockNote = (tiptapJson: string): string => {
try { try {
@@ -30,7 +30,7 @@ const mapTipTapToBlockNote = (tiptapJson: string): string => {
} }
}; };
export const FormRichTextV2FieldInput = ({ export const FormRichTextFieldInput = ({
label, label,
error, error,
hint, hint,
@@ -39,7 +39,7 @@ export const FormRichTextV2FieldInput = ({
onChange, onChange,
readonly, readonly,
VariablePicker, VariablePicker,
}: FormRichTextV2FieldInputProps) => { }: FormRichTextFieldInputProps) => {
const handleChange = (value: string) => { const handleChange = (value: string) => {
onChange({ onChange({
blocknote: mapTipTapToBlockNote(value), blocknote: mapTipTapToBlockNote(value),
@@ -57,8 +57,8 @@ export const FormRichTextV2FieldInput = ({
onChange={handleChange} onChange={handleChange}
readonly={readonly} readonly={readonly}
VariablePicker={VariablePicker} VariablePicker={VariablePicker}
minHeight={RICH_TEXT_V2_EDITOR_MIN_HEIGHT} minHeight={RICH_TEXT_EDITOR_MIN_HEIGHT}
maxWidth={RICH_TEXT_V2_EDITOR_MAX_WIDTH} maxWidth={RICH_TEXT_EDITOR_MAX_WIDTH}
/> />
); );
}; };
@@ -1,4 +1,4 @@
import { FormRichTextV2FieldInput } from '@/object-record/record-field/ui/form-types/components/FormRichTextV2FieldInput'; import { FormRichTextFieldInput } from '@/object-record/record-field/ui/form-types/components/FormRichTextFieldInput';
import { type Meta, type StoryObj } from '@storybook/react-vite'; import { type Meta, type StoryObj } from '@storybook/react-vite';
import { import {
expect, expect,
@@ -12,9 +12,9 @@ import { getUserDevice } from 'twenty-ui/utilities';
import { WorkflowStepDecorator } from '~/testing/decorators/WorkflowStepDecorator'; import { WorkflowStepDecorator } from '~/testing/decorators/WorkflowStepDecorator';
import { MOCKED_STEP_ID } from '~/testing/mock-data/workflow'; import { MOCKED_STEP_ID } from '~/testing/mock-data/workflow';
const meta: Meta<typeof FormRichTextV2FieldInput> = { const meta: Meta<typeof FormRichTextFieldInput> = {
title: 'UI/Data/Field/Form/Input/FormRichTextV2FieldInput', title: 'UI/Data/Field/Form/Input/FormRichTextFieldInput',
component: FormRichTextV2FieldInput, component: FormRichTextFieldInput,
args: {}, args: {},
argTypes: {}, argTypes: {},
decorators: [WorkflowStepDecorator], decorators: [WorkflowStepDecorator],
@@ -22,7 +22,7 @@ const meta: Meta<typeof FormRichTextV2FieldInput> = {
export default meta; export default meta;
type Story = StoryObj<typeof FormRichTextV2FieldInput>; type Story = StoryObj<typeof FormRichTextFieldInput>;
export const Default: Story = { export const Default: Story = {
args: { args: {
@@ -49,8 +49,8 @@ import { isFieldRating } from '@/object-record/record-field/ui/types/guards/isFi
import { isFieldRatingValue } from '@/object-record/record-field/ui/types/guards/isFieldRatingValue'; import { isFieldRatingValue } from '@/object-record/record-field/ui/types/guards/isFieldRatingValue';
import { isFieldRelationManyToOne } from '@/object-record/record-field/ui/types/guards/isFieldRelationManyToOne'; import { isFieldRelationManyToOne } from '@/object-record/record-field/ui/types/guards/isFieldRelationManyToOne';
import { isFieldRelationManyToOneValue } from '@/object-record/record-field/ui/types/guards/isFieldRelationManyToOneValue'; import { isFieldRelationManyToOneValue } from '@/object-record/record-field/ui/types/guards/isFieldRelationManyToOneValue';
import { isFieldRichTextV2 } from '@/object-record/record-field/ui/types/guards/isFieldRichTextV2'; import { isFieldRichText } from '@/object-record/record-field/ui/types/guards/isFieldRichText';
import { isFieldRichTextV2Value } from '@/object-record/record-field/ui/types/guards/isFieldRichTextValueV2'; import { isFieldRichTextValue } from '@/object-record/record-field/ui/types/guards/isFieldRichTextValue';
import { isFieldText } from '@/object-record/record-field/ui/types/guards/isFieldText'; import { isFieldText } from '@/object-record/record-field/ui/types/guards/isFieldText';
import { isFieldTextValue } from '@/object-record/record-field/ui/types/guards/isFieldTextValue'; import { isFieldTextValue } from '@/object-record/record-field/ui/types/guards/isFieldTextValue';
import { useUpsertRecordsInStore } from '@/object-record/record-store/hooks/useUpsertRecordsInStore'; import { useUpsertRecordsInStore } from '@/object-record/record-store/hooks/useUpsertRecordsInStore';
@@ -143,9 +143,9 @@ export const usePersistField = ({
const fieldIsRawJson = const fieldIsRawJson =
isFieldRawJson(fieldDefinition) && isFieldRawJsonValue(valueToPersist); isFieldRawJson(fieldDefinition) && isFieldRawJsonValue(valueToPersist);
const fieldIsRichTextV2 = const fieldIsRichText =
isFieldRichTextV2(fieldDefinition) && isFieldRichText(fieldDefinition) &&
isFieldRichTextV2Value(valueToPersist); isFieldRichTextValue(valueToPersist);
const fieldIsArray = const fieldIsArray =
isFieldArray(fieldDefinition) && isFieldArrayValue(valueToPersist); isFieldArray(fieldDefinition) && isFieldArrayValue(valueToPersist);
@@ -179,7 +179,7 @@ export const usePersistField = ({
fieldIsRawJson || fieldIsRawJson ||
fieldIsArray || fieldIsArray ||
fieldIsFiles || fieldIsFiles ||
fieldIsRichTextV2; fieldIsRichText;
if (isValuePersistable) { if (isValuePersistable) {
const fieldName = fieldDefinition.metadata.fieldName; const fieldName = fieldDefinition.metadata.fieldName;
@@ -1,11 +1,11 @@
import { useRichTextV2FieldDisplay } from '@/object-record/record-field/ui/meta-types/hooks/useRichTextV2FieldDisplay'; import { useRichTextFieldDisplay } from '@/object-record/record-field/ui/meta-types/hooks/useRichTextFieldDisplay';
import { getFirstNonEmptyLineOfRichText } from '@/blocknote-editor/utils/getFirstNonEmptyLineOfRichText'; import { getFirstNonEmptyLineOfRichText } from '@/blocknote-editor/utils/getFirstNonEmptyLineOfRichText';
import type { PartialBlock } from '@blocknote/core'; import type { PartialBlock } from '@blocknote/core';
import { isNonEmptyString } from '@sniptt/guards'; import { isNonEmptyString } from '@sniptt/guards';
import { isDefined, parseJson } from 'twenty-shared/utils'; import { isDefined, parseJson } from 'twenty-shared/utils';
export const RichTextV2FieldDisplay = () => { export const RichTextFieldDisplay = () => {
const { fieldValue } = useRichTextV2FieldDisplay(); const { fieldValue } = useRichTextFieldDisplay();
const blocks = const blocks =
isDefined(fieldValue) && isNonEmptyString(fieldValue.blocknote) isDefined(fieldValue) && isNonEmptyString(fieldValue.blocknote)
@@ -1,24 +1,24 @@
import { useContext } from 'react'; import { useContext } from 'react';
import { type FieldRichTextV2Value } from '@/object-record/record-field/ui/types/FieldMetadata'; import { type FieldRichTextValue } from '@/object-record/record-field/ui/types/FieldMetadata';
import { assertFieldMetadata } from '@/object-record/record-field/ui/types/guards/assertFieldMetadata'; import { assertFieldMetadata } from '@/object-record/record-field/ui/types/guards/assertFieldMetadata';
import { isFieldRichTextV2 } from '@/object-record/record-field/ui/types/guards/isFieldRichTextV2'; import { isFieldRichText } from '@/object-record/record-field/ui/types/guards/isFieldRichText';
import { useRecordFieldValue } from '@/object-record/record-store/hooks/useRecordFieldValue'; import { useRecordFieldValue } from '@/object-record/record-store/hooks/useRecordFieldValue';
import { FieldMetadataType } from '~/generated-metadata/graphql'; import { FieldMetadataType } from '~/generated-metadata/graphql';
import { FieldContext } from '@/object-record/record-field/ui/contexts/FieldContext'; import { FieldContext } from '@/object-record/record-field/ui/contexts/FieldContext';
export const useRichTextV2FieldDisplay = () => { export const useRichTextFieldDisplay = () => {
const { recordId, fieldDefinition } = useContext(FieldContext); const { recordId, fieldDefinition } = useContext(FieldContext);
assertFieldMetadata( assertFieldMetadata(
FieldMetadataType.RICH_TEXT_V2, FieldMetadataType.RICH_TEXT,
isFieldRichTextV2, isFieldRichText,
fieldDefinition, fieldDefinition,
); );
const fieldName = fieldDefinition.metadata.fieldName; const fieldName = fieldDefinition.metadata.fieldName;
const fieldValue = useRecordFieldValue<FieldRichTextV2Value | undefined>( const fieldValue = useRecordFieldValue<FieldRichTextValue | undefined>(
recordId, recordId,
fieldName, fieldName,
fieldDefinition, fieldDefinition,
@@ -7,7 +7,7 @@ import { RecordFieldComponentInstanceContext } from '@/object-record/record-fiel
import { FieldContext } from '@/object-record/record-field/ui/contexts/FieldContext'; import { FieldContext } from '@/object-record/record-field/ui/contexts/FieldContext';
import { FieldInputEventContext } from '@/object-record/record-field/ui/contexts/FieldInputEventContext'; import { FieldInputEventContext } from '@/object-record/record-field/ui/contexts/FieldInputEventContext';
import { type FieldRichTextV2Metadata } from '@/object-record/record-field/ui/types/FieldMetadata'; import { type FieldRichTextMetadata } from '@/object-record/record-field/ui/types/FieldMetadata';
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow'; import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
import { styled } from '@linaria/react'; import { styled } from '@linaria/react';
import { Suspense, lazy, useContext, useRef } from 'react'; import { Suspense, lazy, useContext, useRef } from 'react';
@@ -59,7 +59,7 @@ export const RichTextFieldInput = () => {
id: recordId, id: recordId,
targetObjectNameSingular: ( targetObjectNameSingular: (
fieldDefinition as { fieldDefinition as {
metadata: FieldRichTextV2Metadata; metadata: FieldRichTextMetadata;
} }
).metadata.objectMetadataNameSingular as ).metadata.objectMetadataNameSingular as
| CoreObjectNameSingular.Note | CoreObjectNameSingular.Note
@@ -65,7 +65,7 @@ const RichTextFieldInputWithContext = () => {
fieldDefinition: { fieldDefinition: {
fieldMetadataId: 'richText', fieldMetadataId: 'richText',
label: 'Rich Text', label: 'Rich Text',
type: FieldMetadataType.RICH_TEXT_V2, type: FieldMetadataType.RICH_TEXT,
iconName: 'IconRichText', iconName: 'IconRichText',
metadata: { metadata: {
fieldName: 'richText', fieldName: 'richText',
@@ -131,7 +131,7 @@ export type FieldRawJsonMetadata = BaseFieldMetadata & {
settings?: null; settings?: null;
}; };
export type FieldRichTextV2Metadata = BaseFieldMetadata & { export type FieldRichTextMetadata = BaseFieldMetadata & {
settings?: null; settings?: null;
}; };
@@ -225,7 +225,7 @@ export type FieldMetadata =
| FieldArrayMetadata | FieldArrayMetadata
| FieldTsVectorMetadata | FieldTsVectorMetadata
| FieldRawJsonMetadata | FieldRawJsonMetadata
| FieldRichTextV2Metadata; | FieldRichTextMetadata;
export type FieldTextValue = string; export type FieldTextValue = string;
export type FieldUUidValue = string; // TODO: can we replace with a template literal type, or maybe overkill ? export type FieldUUidValue = string; // TODO: can we replace with a template literal type, or maybe overkill ?
@@ -279,7 +279,7 @@ export type FieldRelationValue<
export type Json = ZodHelperLiteral | { [key: string]: Json } | Json[]; export type Json = ZodHelperLiteral | { [key: string]: Json } | Json[];
export type FieldJsonValue = Record<string, Json> | Json[] | null; export type FieldJsonValue = Record<string, Json> | Json[] | null;
export type FieldRichTextV2Value = { export type FieldRichTextValue = {
blocknote: string | null; blocknote: string | null;
markdown: string | null; markdown: string | null;
}; };
@@ -24,7 +24,7 @@ import {
type FieldRatingMetadata, type FieldRatingMetadata,
type FieldRawJsonMetadata, type FieldRawJsonMetadata,
type FieldRelationMetadata, type FieldRelationMetadata,
type FieldRichTextV2Metadata, type FieldRichTextMetadata,
type FieldSelectMetadata, type FieldSelectMetadata,
type FieldTextMetadata, type FieldTextMetadata,
type FieldUuidMetadata, type FieldUuidMetadata,
@@ -74,8 +74,8 @@ type AssertFieldMetadataFunction = <
? FieldAddressMetadata ? FieldAddressMetadata
: E extends 'RAW_JSON' : E extends 'RAW_JSON'
? FieldRawJsonMetadata ? FieldRawJsonMetadata
: E extends 'RICH_TEXT_V2' : E extends 'RICH_TEXT'
? FieldRichTextV2Metadata ? FieldRichTextMetadata
: E extends 'ACTOR' : E extends 'ACTOR'
? FieldActorMetadata ? FieldActorMetadata
: E extends 'ARRAY' : E extends 'ARRAY'
@@ -3,10 +3,10 @@ import { FieldMetadataType } from '~/generated-metadata/graphql';
import { type FieldDefinition } from '@/object-record/record-field/ui/types/FieldDefinition'; import { type FieldDefinition } from '@/object-record/record-field/ui/types/FieldDefinition';
import { import {
type FieldMetadata, type FieldMetadata,
type FieldRichTextV2Metadata, type FieldRichTextMetadata,
} from '@/object-record/record-field/ui/types/FieldMetadata'; } from '@/object-record/record-field/ui/types/FieldMetadata';
export const isFieldRichTextV2 = ( export const isFieldRichText = (
field: Pick<FieldDefinition<FieldMetadata>, 'type'>, field: Pick<FieldDefinition<FieldMetadata>, 'type'>,
): field is FieldDefinition<FieldRichTextV2Metadata> => ): field is FieldDefinition<FieldRichTextMetadata> =>
field.type === FieldMetadataType.RICH_TEXT_V2; field.type === FieldMetadataType.RICH_TEXT;
@@ -0,0 +1,12 @@
import { type FieldRichTextValue } from '@/object-record/record-field/ui/types/FieldMetadata';
import { z } from 'zod';
export const richTextSchema: z.ZodType<FieldRichTextValue> = z.object({
blocknote: z.string().nullable(),
markdown: z.string().nullable(),
});
export const isFieldRichTextValue = (
fieldValue: unknown,
): fieldValue is FieldRichTextValue =>
richTextSchema.safeParse(fieldValue).success;
@@ -1,12 +0,0 @@
import { type FieldRichTextV2Value } from '@/object-record/record-field/ui/types/FieldMetadata';
import { z } from 'zod';
export const richTextV2Schema: z.ZodType<FieldRichTextV2Value> = z.object({
blocknote: z.string().nullable(),
markdown: z.string().nullable(),
});
export const isFieldRichTextV2Value = (
fieldValue: unknown,
): fieldValue is FieldRichTextV2Value =>
richTextV2Schema.safeParse(fieldValue).success;
@@ -32,8 +32,8 @@ import { isFieldPosition } from '@/object-record/record-field/ui/types/guards/is
import { isFieldRating } from '@/object-record/record-field/ui/types/guards/isFieldRating'; import { isFieldRating } from '@/object-record/record-field/ui/types/guards/isFieldRating';
import { isFieldRawJson } from '@/object-record/record-field/ui/types/guards/isFieldRawJson'; import { isFieldRawJson } from '@/object-record/record-field/ui/types/guards/isFieldRawJson';
import { isFieldRelation } from '@/object-record/record-field/ui/types/guards/isFieldRelation'; import { isFieldRelation } from '@/object-record/record-field/ui/types/guards/isFieldRelation';
import { isFieldRichTextV2 } from '@/object-record/record-field/ui/types/guards/isFieldRichTextV2'; import { isFieldRichText } from '@/object-record/record-field/ui/types/guards/isFieldRichText';
import { isFieldRichTextV2Value } from '@/object-record/record-field/ui/types/guards/isFieldRichTextValueV2'; import { isFieldRichTextValue } from '@/object-record/record-field/ui/types/guards/isFieldRichTextValue';
import { isFieldSelect } from '@/object-record/record-field/ui/types/guards/isFieldSelect'; import { isFieldSelect } from '@/object-record/record-field/ui/types/guards/isFieldSelect';
import { isFieldSelectValue } from '@/object-record/record-field/ui/types/guards/isFieldSelectValue'; import { isFieldSelectValue } from '@/object-record/record-field/ui/types/guards/isFieldSelectValue';
import { isFieldText } from '@/object-record/record-field/ui/types/guards/isFieldText'; import { isFieldText } from '@/object-record/record-field/ui/types/guards/isFieldText';
@@ -177,9 +177,9 @@ export const isFieldValueEmpty = ({
return false; return false;
} }
if (isFieldRichTextV2(fieldDefinition)) { if (isFieldRichText(fieldDefinition)) {
return ( return (
!isFieldRichTextV2Value(fieldValue) || isValueEmpty(fieldValue?.markdown) !isFieldRichTextValue(fieldValue) || isValueEmpty(fieldValue?.markdown)
); );
} }
@@ -22,7 +22,7 @@ import {
type RatingFilter, type RatingFilter,
type RawJsonFilter, type RawJsonFilter,
type RecordGqlOperationFilter, type RecordGqlOperationFilter,
type RichTextV2Filter, type RichTextFilter,
type SelectFilter, type SelectFilter,
type StringFilter, type StringFilter,
type TSVectorFilter, type TSVectorFilter,
@@ -40,7 +40,7 @@ import {
isMatchingMultiSelectFilter, isMatchingMultiSelectFilter,
isMatchingRatingFilter, isMatchingRatingFilter,
isMatchingRawJsonFilter, isMatchingRawJsonFilter,
isMatchingRichTextV2Filter, isMatchingRichTextFilter,
isMatchingSelectFilter, isMatchingSelectFilter,
isMatchingStringFilter, isMatchingStringFilter,
isMatchingTSVectorFilter, isMatchingTSVectorFilter,
@@ -235,9 +235,9 @@ export const isRecordMatchingFilter = ({
value: record[filterKey], value: record[filterKey],
}); });
} }
case FieldMetadataType.RICH_TEXT_V2: { case FieldMetadataType.RICH_TEXT: {
return isMatchingRichTextV2Filter({ return isMatchingRichTextFilter({
richTextV2Filter: filterValue as RichTextV2Filter, richTextFilter: filterValue as RichTextFilter,
value: record[filterKey], value: record[filterKey],
}); });
} }
@@ -375,7 +375,7 @@ const SUPPORTED_COMPOSITE_FIELD_TYPES: FieldMetadataType[] = [
FieldMetadataType.EMAILS, FieldMetadataType.EMAILS,
FieldMetadataType.PHONES, FieldMetadataType.PHONES,
FieldMetadataType.ACTOR, FieldMetadataType.ACTOR,
FieldMetadataType.RICH_TEXT_V2, FieldMetadataType.RICH_TEXT,
]; ];
const computeValueFromSubFieldType = ({ const computeValueFromSubFieldType = ({
@@ -48,7 +48,7 @@ export const useBuildSpreadsheetImportFields = () => {
case FieldMetadataType.FULL_NAME: case FieldMetadataType.FULL_NAME:
case FieldMetadataType.LINKS: case FieldMetadataType.LINKS:
case FieldMetadataType.PHONES: case FieldMetadataType.PHONES:
case FieldMetadataType.RICH_TEXT_V2: case FieldMetadataType.RICH_TEXT:
return handleCompositeFields({ return handleCompositeFields({
fieldMetadataItem, fieldMetadataItem,
fieldType: fieldMetadataItem.type, fieldType: fieldMetadataItem.type,
@@ -251,7 +251,7 @@ describe('buildRecordFromImportedStructuredRow', () => {
universalIdentifier: '16', universalIdentifier: '16',
name: 'richTextField', name: 'richTextField',
label: 'Rich Text Field', label: 'Rich Text Field',
type: FieldMetadataType.RICH_TEXT_V2, type: FieldMetadataType.RICH_TEXT,
isNullable: true, isNullable: true,
isActive: true, isActive: true,
isCustom: false, isCustom: false,
@@ -196,7 +196,7 @@ export const buildRecordFromImportedStructuredRow = ({
additionalPhones: phoneArrayJSONSchema.parse, additionalPhones: phoneArrayJSONSchema.parse,
}, },
[FieldMetadataType.RICH_TEXT_V2]: { [FieldMetadataType.RICH_TEXT]: {
blocknote: castToString, blocknote: castToString,
markdown: castToString, markdown: castToString,
}, },
@@ -222,7 +222,7 @@ export const buildRecordFromImportedStructuredRow = ({
case FieldMetadataType.CURRENCY: case FieldMetadataType.CURRENCY:
case FieldMetadataType.ADDRESS: case FieldMetadataType.ADDRESS:
case FieldMetadataType.LINKS: case FieldMetadataType.LINKS:
case FieldMetadataType.RICH_TEXT_V2: case FieldMetadataType.RICH_TEXT:
case FieldMetadataType.EMAILS: case FieldMetadataType.EMAILS:
case FieldMetadataType.FULL_NAME: { case FieldMetadataType.FULL_NAME: {
const compositeData = buildCompositeFieldRecord( const compositeData = buildCompositeFieldRecord(
@@ -95,7 +95,7 @@ export const generateEmptyFieldValue = ({
case FieldMetadataType.RAW_JSON: { case FieldMetadataType.RAW_JSON: {
return null; return null;
} }
case FieldMetadataType.RICH_TEXT_V2: { case FieldMetadataType.RICH_TEXT: {
return { return {
blocknote: null, blocknote: null,
markdown: null, markdown: null,
@@ -17,7 +17,7 @@ import { isDefined } from 'twenty-shared/utils';
import { v4 as uuidv4 } from 'uuid'; import { v4 as uuidv4 } from 'uuid';
import { import {
type PageLayoutWidget, type PageLayoutWidget,
type RichTextV2Body, type RichTextBody,
WidgetType, WidgetType,
} from '~/generated-metadata/graphql'; } from '~/generated-metadata/graphql';
@@ -48,7 +48,7 @@ export const useCreatePageLayoutStandaloneRichTextWidget = (
); );
const createPageLayoutStandaloneRichTextWidget = useCallback( const createPageLayoutStandaloneRichTextWidget = useCallback(
(body: RichTextV2Body): PageLayoutWidget => { (body: RichTextBody): PageLayoutWidget => {
const activeTabId = store.get( const activeTabId = store.get(
activeTabIdComponentState.atomFamily({ activeTabIdComponentState.atomFamily({
instanceId: tabListInstanceId, instanceId: tabListInstanceId,
@@ -2,7 +2,7 @@ import {
type GridPosition, type GridPosition,
PageLayoutTabLayoutMode, PageLayoutTabLayoutMode,
type PageLayoutWidget, type PageLayoutWidget,
type RichTextV2Body, type RichTextBody,
WidgetConfigurationType, WidgetConfigurationType,
WidgetType, WidgetType,
} from '~/generated-metadata/graphql'; } from '~/generated-metadata/graphql';
@@ -10,7 +10,7 @@ import {
export const createDefaultStandaloneRichTextWidget = ( export const createDefaultStandaloneRichTextWidget = (
id: string, id: string,
pageLayoutTabId: string, pageLayoutTabId: string,
body: RichTextV2Body, body: RichTextBody,
gridPosition: GridPosition, gridPosition: GridPosition,
objectMetadataId?: string | null, objectMetadataId?: string | null,
): PageLayoutWidget => { ): PageLayoutWidget => {
@@ -43,7 +43,7 @@ export const COMPOSITE_FIELD_SUB_FIELD_LABELS: {
workspaceMemberId: 'Workspace Member', workspaceMemberId: 'Workspace Member',
context: 'Context', context: 'Context',
}, },
[FieldMetadataType.RICH_TEXT_V2]: { [FieldMetadataType.RICH_TEXT]: {
blocknote: 'BlockNote', blocknote: 'BlockNote',
markdown: 'Markdown', markdown: 'Markdown',
}, },
@@ -6,7 +6,7 @@ import {
type FieldFullNameValue, type FieldFullNameValue,
type FieldLinksValue, type FieldLinksValue,
type FieldPhonesValue, type FieldPhonesValue,
type FieldRichTextV2Value, type FieldRichTextValue,
} from '@/object-record/record-field/ui/types/FieldMetadata'; } from '@/object-record/record-field/ui/types/FieldMetadata';
import { COMPOSITE_FIELD_SUB_FIELD_LABELS } from '@/settings/data-model/constants/CompositeFieldSubFieldLabel'; import { COMPOSITE_FIELD_SUB_FIELD_LABELS } from '@/settings/data-model/constants/CompositeFieldSubFieldLabel';
import { type SettingsFieldTypeConfig } from '@/settings/data-model/constants/SettingsNonCompositeFieldTypeConfigs'; import { type SettingsFieldTypeConfig } from '@/settings/data-model/constants/SettingsNonCompositeFieldTypeConfigs';
@@ -499,17 +499,17 @@ export const SETTINGS_COMPOSITE_FIELD_TYPE_CONFIGS = {
}, },
], ],
} as const satisfies SettingsCompositeFieldTypeConfig<FieldActorValue>, } as const satisfies SettingsCompositeFieldTypeConfig<FieldActorValue>,
[FieldMetadataType.RICH_TEXT_V2]: { [FieldMetadataType.RICH_TEXT]: {
label: 'Rich Text', label: 'Rich Text',
Icon: IllustrationIconText, Icon: IllustrationIconText,
category: 'Basic', category: 'Basic',
subFields: [ subFields: [
{ {
subFieldName: subFieldName:
COMPOSITE_FIELD_TYPE_SUB_FIELDS_NAMES[FieldMetadataType.RICH_TEXT_V2] COMPOSITE_FIELD_TYPE_SUB_FIELDS_NAMES[FieldMetadataType.RICH_TEXT]
.blocknote, .blocknote,
subFieldLabel: subFieldLabel:
COMPOSITE_FIELD_SUB_FIELD_LABELS[FieldMetadataType.RICH_TEXT_V2] COMPOSITE_FIELD_SUB_FIELD_LABELS[FieldMetadataType.RICH_TEXT]
.blocknote, .blocknote,
isImportable: false, isImportable: false,
isFilterable: false, isFilterable: false,
@@ -517,10 +517,10 @@ export const SETTINGS_COMPOSITE_FIELD_TYPE_CONFIGS = {
}, },
{ {
subFieldName: subFieldName:
COMPOSITE_FIELD_TYPE_SUB_FIELDS_NAMES[FieldMetadataType.RICH_TEXT_V2] COMPOSITE_FIELD_TYPE_SUB_FIELDS_NAMES[FieldMetadataType.RICH_TEXT]
.markdown, .markdown,
subFieldLabel: subFieldLabel:
COMPOSITE_FIELD_SUB_FIELD_LABELS[FieldMetadataType.RICH_TEXT_V2] COMPOSITE_FIELD_SUB_FIELD_LABELS[FieldMetadataType.RICH_TEXT]
.markdown, .markdown,
isImportable: true, isImportable: true,
isFilterable: false, isFilterable: false,
@@ -541,5 +541,5 @@ export const SETTINGS_COMPOSITE_FIELD_TYPE_CONFIGS = {
markdown: '# Hello Again', markdown: '# Hello Again',
}, },
], ],
} as const satisfies SettingsCompositeFieldTypeConfig<FieldRichTextV2Value>, } as const satisfies SettingsCompositeFieldTypeConfig<FieldRichTextValue>,
} as const satisfies SettingsCompositeFieldTypeConfigArray; } as const satisfies SettingsCompositeFieldTypeConfigArray;
@@ -10,7 +10,7 @@ export const COMPOSITE_FIELD_TYPES = [
'PHONES', 'PHONES',
'FULL_NAME', 'FULL_NAME',
'ACTOR', 'ACTOR',
'RICH_TEXT_V2', 'RICH_TEXT',
] as const; ] as const;
type CompositeFieldTypeBaseLiteral = (typeof COMPOSITE_FIELD_TYPES)[number]; type CompositeFieldTypeBaseLiteral = (typeof COMPOSITE_FIELD_TYPES)[number];
@@ -3,5 +3,5 @@ import { type PickLiteral } from '~/types/PickLiteral';
export type SettingsExcludedFieldType = PickLiteral< export type SettingsExcludedFieldType = PickLiteral<
FieldType, FieldType,
'POSITION' | 'TS_VECTOR' | 'RICH_TEXT_V2' | 'NUMERIC' 'POSITION' | 'TS_VECTOR' | 'RICH_TEXT' | 'NUMERIC'
>; >;
@@ -51,10 +51,10 @@ describe('getSortIconForFieldType', () => {
).toBe(IconSortDescendingLetters); ).toBe(IconSortDescendingLetters);
}); });
it('should return IconSortAscendingLetters for RICH_TEXT_V2 field ascending', () => { it('should return IconSortAscendingLetters for RICH_TEXT field ascending', () => {
expect( expect(
getSortIconForFieldType({ getSortIconForFieldType({
fieldType: FieldMetadataType.RICH_TEXT_V2, fieldType: FieldMetadataType.RICH_TEXT,
orderBy: GraphOrderBy.FIELD_ASC, orderBy: GraphOrderBy.FIELD_ASC,
}), }),
).toBe(IconSortAscendingLetters); ).toBe(IconSortAscendingLetters);
@@ -41,7 +41,7 @@ export const useDownloadFakeRecords = () => {
break; break;
} }
case FieldMetadataType.RICH_TEXT_V2: case FieldMetadataType.RICH_TEXT:
case FieldMetadataType.ACTOR: case FieldMetadataType.ACTOR:
case FieldMetadataType.EMAILS: case FieldMetadataType.EMAILS:
case FieldMetadataType.CURRENCY: case FieldMetadataType.CURRENCY:
@@ -22,7 +22,7 @@ type WorkflowStepFilterFieldSelectProps = {
}; };
const NON_SELECTABLE_FIELD_TYPES = [ const NON_SELECTABLE_FIELD_TYPES = [
FieldMetadataType.RICH_TEXT_V2, FieldMetadataType.RICH_TEXT,
FieldMetadataType.RATING, FieldMetadataType.RATING,
]; ];
@@ -59,7 +59,7 @@ const isFilterableFieldType = (
FieldMetadataType.SELECT, FieldMetadataType.SELECT,
FieldMetadataType.MULTI_SELECT, FieldMetadataType.MULTI_SELECT,
FieldMetadataType.RAW_JSON, FieldMetadataType.RAW_JSON,
FieldMetadataType.RICH_TEXT_V2, FieldMetadataType.RICH_TEXT,
FieldMetadataType.ARRAY, FieldMetadataType.ARRAY,
FieldMetadataType.UUID, FieldMetadataType.UUID,
FieldMetadataType.RELATION, FieldMetadataType.RELATION,
@@ -22,7 +22,7 @@ const SUPPORTED_FORM_FIELD_TYPES = [
FieldMetadataType.UUID, FieldMetadataType.UUID,
FieldMetadataType.ARRAY, FieldMetadataType.ARRAY,
FieldMetadataType.RELATION, FieldMetadataType.RELATION,
FieldMetadataType.RICH_TEXT_V2, FieldMetadataType.RICH_TEXT,
]; ];
export const shouldDisplayFormField = ({ export const shouldDisplayFormField = ({
@@ -24,6 +24,6 @@ export const DEFAULT_ICONS_BY_FIELD_TYPE: Record<FieldMetadataType, string> = {
[FieldMetadataType.ACTOR]: 'IconUsers', [FieldMetadataType.ACTOR]: 'IconUsers',
[FieldMetadataType.NUMERIC]: 'IconUsers', [FieldMetadataType.NUMERIC]: 'IconUsers',
[FieldMetadataType.POSITION]: 'IconUsers', [FieldMetadataType.POSITION]: 'IconUsers',
[FieldMetadataType.RICH_TEXT_V2]: 'IconUsers', [FieldMetadataType.RICH_TEXT]: 'IconUsers',
[FieldMetadataType.TS_VECTOR]: 'IconUsers', [FieldMetadataType.TS_VECTOR]: 'IconUsers',
}; };
@@ -47,7 +47,7 @@ export const SettingsObjectNewFieldSelect = () => {
const excludedFieldTypes: FieldType[] = ( const excludedFieldTypes: FieldType[] = (
[ [
FieldMetadataType.NUMERIC, FieldMetadataType.NUMERIC,
FieldMetadataType.RICH_TEXT_V2, FieldMetadataType.RICH_TEXT,
FieldMetadataType.ACTOR, FieldMetadataType.ACTOR,
FieldMetadataType.UUID, FieldMetadataType.UUID,
] as const ] as const
@@ -23,7 +23,7 @@ export const mockedNoteRecords: ObjectRecord[] =
] ]
}, },
"bodyV2": { "bodyV2": {
"__typename": "RichTextV2", "__typename": "RichText",
"blocknote": "[{\"id\":\"block-1\",\"type\":\"paragraph\",\"props\":{\"textColor\":\"default\",\"backgroundColor\":\"default\",\"textAlignment\":\"left\"},\"content\":[{\"type\":\"text\",\"text\":\"Great conversation today about potential collaboration opportunities. Next steps discussed include proposal review and timeline planning.\",\"styles\":{}}],\"children\":[]}]", "blocknote": "[{\"id\":\"block-1\",\"type\":\"paragraph\",\"props\":{\"textColor\":\"default\",\"backgroundColor\":\"default\",\"textAlignment\":\"left\"},\"content\":[{\"type\":\"text\",\"text\":\"Great conversation today about potential collaboration opportunities. Next steps discussed include proposal review and timeline planning.\",\"styles\":{}}],\"children\":[]}]",
"markdown": "Great conversation today about potential collaboration opportunities. Next steps discussed include proposal review and timeline planning." "markdown": "Great conversation today about potential collaboration opportunities. Next steps discussed include proposal review and timeline planning."
}, },
@@ -106,7 +106,7 @@ export const mockedNoteRecords: ObjectRecord[] =
] ]
}, },
"bodyV2": { "bodyV2": {
"__typename": "RichTextV2", "__typename": "RichText",
"blocknote": "[{\"id\":\"block-2\",\"type\":\"paragraph\",\"props\":{\"textColor\":\"default\",\"backgroundColor\":\"default\",\"textAlignment\":\"left\"},\"content\":[{\"type\":\"text\",\"text\":\"Reviewed current project status and identified key deliverables for the upcoming quarter. Timeline adjustments may be needed.\",\"styles\":{}}],\"children\":[]}]", "blocknote": "[{\"id\":\"block-2\",\"type\":\"paragraph\",\"props\":{\"textColor\":\"default\",\"backgroundColor\":\"default\",\"textAlignment\":\"left\"},\"content\":[{\"type\":\"text\",\"text\":\"Reviewed current project status and identified key deliverables for the upcoming quarter. Timeline adjustments may be needed.\",\"styles\":{}}],\"children\":[]}]",
"markdown": "Reviewed current project status and identified key deliverables for the upcoming quarter. Timeline adjustments may be needed." "markdown": "Reviewed current project status and identified key deliverables for the upcoming quarter. Timeline adjustments may be needed."
}, },
@@ -189,7 +189,7 @@ export const mockedNoteRecords: ObjectRecord[] =
] ]
}, },
"bodyV2": { "bodyV2": {
"__typename": "RichTextV2", "__typename": "RichText",
"blocknote": "[{\"id\":\"block-3\",\"type\":\"paragraph\",\"props\":{\"textColor\":\"default\",\"backgroundColor\":\"default\",\"textAlignment\":\"left\"},\"content\":[{\"type\":\"text\",\"text\":\"Impressive background in technology and leadership. Strong potential for senior roles in upcoming projects.\",\"styles\":{}}],\"children\":[]}]", "blocknote": "[{\"id\":\"block-3\",\"type\":\"paragraph\",\"props\":{\"textColor\":\"default\",\"backgroundColor\":\"default\",\"textAlignment\":\"left\"},\"content\":[{\"type\":\"text\",\"text\":\"Impressive background in technology and leadership. Strong potential for senior roles in upcoming projects.\",\"styles\":{}}],\"children\":[]}]",
"markdown": "Impressive background in technology and leadership. Strong potential for senior roles in upcoming projects." "markdown": "Impressive background in technology and leadership. Strong potential for senior roles in upcoming projects."
}, },
@@ -272,7 +272,7 @@ export const mockedNoteRecords: ObjectRecord[] =
] ]
}, },
"bodyV2": { "bodyV2": {
"__typename": "RichTextV2", "__typename": "RichText",
"blocknote": "[{\"id\":\"block-4\",\"type\":\"paragraph\",\"props\":{\"textColor\":\"default\",\"backgroundColor\":\"default\",\"textAlignment\":\"left\"},\"content\":[{\"type\":\"text\",\"text\":\"Made connection at industry conference. Shared interests in digital transformation and innovation strategies.\",\"styles\":{}}],\"children\":[]}]", "blocknote": "[{\"id\":\"block-4\",\"type\":\"paragraph\",\"props\":{\"textColor\":\"default\",\"backgroundColor\":\"default\",\"textAlignment\":\"left\"},\"content\":[{\"type\":\"text\",\"text\":\"Made connection at industry conference. Shared interests in digital transformation and innovation strategies.\",\"styles\":{}}],\"children\":[]}]",
"markdown": "Made connection at industry conference. Shared interests in digital transformation and innovation strategies." "markdown": "Made connection at industry conference. Shared interests in digital transformation and innovation strategies."
}, },
@@ -355,7 +355,7 @@ export const mockedNoteRecords: ObjectRecord[] =
] ]
}, },
"bodyV2": { "bodyV2": {
"__typename": "RichTextV2", "__typename": "RichText",
"blocknote": "[{\"id\":\"block-5\",\"type\":\"paragraph\",\"props\":{\"textColor\":\"default\",\"backgroundColor\":\"default\",\"textAlignment\":\"left\"},\"content\":[{\"type\":\"text\",\"text\":\"Strong candidate with relevant experience. Technical skills align well with team requirements. Positive cultural fit assessment.\",\"styles\":{}}],\"children\":[]}]", "blocknote": "[{\"id\":\"block-5\",\"type\":\"paragraph\",\"props\":{\"textColor\":\"default\",\"backgroundColor\":\"default\",\"textAlignment\":\"left\"},\"content\":[{\"type\":\"text\",\"text\":\"Strong candidate with relevant experience. Technical skills align well with team requirements. Positive cultural fit assessment.\",\"styles\":{}}],\"children\":[]}]",
"markdown": "Strong candidate with relevant experience. Technical skills align well with team requirements. Positive cultural fit assessment." "markdown": "Strong candidate with relevant experience. Technical skills align well with team requirements. Positive cultural fit assessment."
}, },
@@ -438,7 +438,7 @@ export const mockedNoteRecords: ObjectRecord[] =
] ]
}, },
"bodyV2": { "bodyV2": {
"__typename": "RichTextV2", "__typename": "RichText",
"blocknote": "[{\"id\":\"block-6\",\"type\":\"paragraph\",\"props\":{\"textColor\":\"default\",\"backgroundColor\":\"default\",\"textAlignment\":\"left\"},\"content\":[{\"type\":\"text\",\"text\":\"Quarterly review completed. Exceeded targets in key areas. Discussed career development opportunities and growth plans.\",\"styles\":{}}],\"children\":[]}]", "blocknote": "[{\"id\":\"block-6\",\"type\":\"paragraph\",\"props\":{\"textColor\":\"default\",\"backgroundColor\":\"default\",\"textAlignment\":\"left\"},\"content\":[{\"type\":\"text\",\"text\":\"Quarterly review completed. Exceeded targets in key areas. Discussed career development opportunities and growth plans.\",\"styles\":{}}],\"children\":[]}]",
"markdown": "Quarterly review completed. Exceeded targets in key areas. Discussed career development opportunities and growth plans." "markdown": "Quarterly review completed. Exceeded targets in key areas. Discussed career development opportunities and growth plans."
}, },
@@ -521,7 +521,7 @@ export const mockedNoteRecords: ObjectRecord[] =
] ]
}, },
"bodyV2": { "bodyV2": {
"__typename": "RichTextV2", "__typename": "RichText",
"blocknote": "[{\"id\":\"block-7\",\"type\":\"paragraph\",\"props\":{\"textColor\":\"default\",\"backgroundColor\":\"default\",\"textAlignment\":\"left\"},\"content\":[{\"type\":\"text\",\"text\":\"Completed certification program successfully. Ready to take on expanded responsibilities in the next phase.\",\"styles\":{}}],\"children\":[]}]", "blocknote": "[{\"id\":\"block-7\",\"type\":\"paragraph\",\"props\":{\"textColor\":\"default\",\"backgroundColor\":\"default\",\"textAlignment\":\"left\"},\"content\":[{\"type\":\"text\",\"text\":\"Completed certification program successfully. Ready to take on expanded responsibilities in the next phase.\",\"styles\":{}}],\"children\":[]}]",
"markdown": "Completed certification program successfully. Ready to take on expanded responsibilities in the next phase." "markdown": "Completed certification program successfully. Ready to take on expanded responsibilities in the next phase."
}, },
@@ -604,7 +604,7 @@ export const mockedNoteRecords: ObjectRecord[] =
] ]
}, },
"bodyV2": { "bodyV2": {
"__typename": "RichTextV2", "__typename": "RichText",
"blocknote": "[{\"id\":\"block-8\",\"type\":\"paragraph\",\"props\":{\"textColor\":\"default\",\"backgroundColor\":\"default\",\"textAlignment\":\"left\"},\"content\":[{\"type\":\"text\",\"text\":\"Long-standing professional relationship. Reliable partner for complex initiatives. High satisfaction ratings.\",\"styles\":{}}],\"children\":[]}]", "blocknote": "[{\"id\":\"block-8\",\"type\":\"paragraph\",\"props\":{\"textColor\":\"default\",\"backgroundColor\":\"default\",\"textAlignment\":\"left\"},\"content\":[{\"type\":\"text\",\"text\":\"Long-standing professional relationship. Reliable partner for complex initiatives. High satisfaction ratings.\",\"styles\":{}}],\"children\":[]}]",
"markdown": "Long-standing professional relationship. Reliable partner for complex initiatives. High satisfaction ratings." "markdown": "Long-standing professional relationship. Reliable partner for complex initiatives. High satisfaction ratings."
}, },
@@ -687,7 +687,7 @@ export const mockedNoteRecords: ObjectRecord[] =
] ]
}, },
"bodyV2": { "bodyV2": {
"__typename": "RichTextV2", "__typename": "RichText",
"blocknote": "[{\"id\":\"block-9\",\"type\":\"paragraph\",\"props\":{\"textColor\":\"default\",\"backgroundColor\":\"default\",\"textAlignment\":\"left\"},\"content\":[{\"type\":\"text\",\"text\":\"Great conversation today about potential collaboration opportunities. Next steps discussed include proposal review and timeline planning.\",\"styles\":{}}],\"children\":[]}]", "blocknote": "[{\"id\":\"block-9\",\"type\":\"paragraph\",\"props\":{\"textColor\":\"default\",\"backgroundColor\":\"default\",\"textAlignment\":\"left\"},\"content\":[{\"type\":\"text\",\"text\":\"Great conversation today about potential collaboration opportunities. Next steps discussed include proposal review and timeline planning.\",\"styles\":{}}],\"children\":[]}]",
"markdown": "Great conversation today about potential collaboration opportunities. Next steps discussed include proposal review and timeline planning." "markdown": "Great conversation today about potential collaboration opportunities. Next steps discussed include proposal review and timeline planning."
}, },
@@ -770,7 +770,7 @@ export const mockedNoteRecords: ObjectRecord[] =
] ]
}, },
"bodyV2": { "bodyV2": {
"__typename": "RichTextV2", "__typename": "RichText",
"blocknote": "[{\"id\":\"block-10\",\"type\":\"paragraph\",\"props\":{\"textColor\":\"default\",\"backgroundColor\":\"default\",\"textAlignment\":\"left\"},\"content\":[{\"type\":\"text\",\"text\":\"Reviewed current project status and identified key deliverables for the upcoming quarter. Timeline adjustments may be needed.\",\"styles\":{}}],\"children\":[]}]", "blocknote": "[{\"id\":\"block-10\",\"type\":\"paragraph\",\"props\":{\"textColor\":\"default\",\"backgroundColor\":\"default\",\"textAlignment\":\"left\"},\"content\":[{\"type\":\"text\",\"text\":\"Reviewed current project status and identified key deliverables for the upcoming quarter. Timeline adjustments may be needed.\",\"styles\":{}}],\"children\":[]}]",
"markdown": "Reviewed current project status and identified key deliverables for the upcoming quarter. Timeline adjustments may be needed." "markdown": "Reviewed current project status and identified key deliverables for the upcoming quarter. Timeline adjustments may be needed."
}, },
@@ -33,7 +33,7 @@ export const mockedTaskRecords: ObjectRecord[] =
] ]
}, },
"bodyV2": { "bodyV2": {
"__typename": "RichTextV2", "__typename": "RichText",
"blocknote": "[{\"id\":\"block-1\",\"type\":\"paragraph\",\"props\":{\"textColor\":\"default\",\"backgroundColor\":\"default\",\"textAlignment\":\"left\"},\"content\":[{\"type\":\"text\",\"text\":\"Arrange a follow-up call to discuss project details and next steps.\",\"styles\":{}}],\"children\":[]}]", "blocknote": "[{\"id\":\"block-1\",\"type\":\"paragraph\",\"props\":{\"textColor\":\"default\",\"backgroundColor\":\"default\",\"textAlignment\":\"left\"},\"content\":[{\"type\":\"text\",\"text\":\"Arrange a follow-up call to discuss project details and next steps.\",\"styles\":{}}],\"children\":[]}]",
"markdown": "Arrange a follow-up call to discuss project details and next steps." "markdown": "Arrange a follow-up call to discuss project details and next steps."
}, },
@@ -128,7 +128,7 @@ export const mockedTaskRecords: ObjectRecord[] =
] ]
}, },
"bodyV2": { "bodyV2": {
"__typename": "RichTextV2", "__typename": "RichText",
"blocknote": "[{\"id\":\"block-2\",\"type\":\"paragraph\",\"props\":{\"textColor\":\"default\",\"backgroundColor\":\"default\",\"textAlignment\":\"left\"},\"content\":[{\"type\":\"text\",\"text\":\"Prepare and send the project proposal document with timeline and deliverables.\",\"styles\":{}}],\"children\":[]}]", "blocknote": "[{\"id\":\"block-2\",\"type\":\"paragraph\",\"props\":{\"textColor\":\"default\",\"backgroundColor\":\"default\",\"textAlignment\":\"left\"},\"content\":[{\"type\":\"text\",\"text\":\"Prepare and send the project proposal document with timeline and deliverables.\",\"styles\":{}}],\"children\":[]}]",
"markdown": "Prepare and send the project proposal document with timeline and deliverables." "markdown": "Prepare and send the project proposal document with timeline and deliverables."
}, },
@@ -223,7 +223,7 @@ export const mockedTaskRecords: ObjectRecord[] =
] ]
}, },
"bodyV2": { "bodyV2": {
"__typename": "RichTextV2", "__typename": "RichText",
"blocknote": "[{\"id\":\"block-3\",\"type\":\"paragraph\",\"props\":{\"textColor\":\"default\",\"backgroundColor\":\"default\",\"textAlignment\":\"left\"},\"content\":[{\"type\":\"text\",\"text\":\"Review the contract terms and conditions before final approval.\",\"styles\":{}}],\"children\":[]}]", "blocknote": "[{\"id\":\"block-3\",\"type\":\"paragraph\",\"props\":{\"textColor\":\"default\",\"backgroundColor\":\"default\",\"textAlignment\":\"left\"},\"content\":[{\"type\":\"text\",\"text\":\"Review the contract terms and conditions before final approval.\",\"styles\":{}}],\"children\":[]}]",
"markdown": "Review the contract terms and conditions before final approval." "markdown": "Review the contract terms and conditions before final approval."
}, },
@@ -318,7 +318,7 @@ export const mockedTaskRecords: ObjectRecord[] =
] ]
}, },
"bodyV2": { "bodyV2": {
"__typename": "RichTextV2", "__typename": "RichText",
"blocknote": "[{\"id\":\"block-4\",\"type\":\"paragraph\",\"props\":{\"textColor\":\"default\",\"backgroundColor\":\"default\",\"textAlignment\":\"left\"},\"content\":[{\"type\":\"text\",\"text\":\"Create detailed agenda for upcoming strategy meeting.\",\"styles\":{}}],\"children\":[]}]", "blocknote": "[{\"id\":\"block-4\",\"type\":\"paragraph\",\"props\":{\"textColor\":\"default\",\"backgroundColor\":\"default\",\"textAlignment\":\"left\"},\"content\":[{\"type\":\"text\",\"text\":\"Create detailed agenda for upcoming strategy meeting.\",\"styles\":{}}],\"children\":[]}]",
"markdown": "Create detailed agenda for upcoming strategy meeting." "markdown": "Create detailed agenda for upcoming strategy meeting."
}, },
@@ -413,7 +413,7 @@ export const mockedTaskRecords: ObjectRecord[] =
] ]
}, },
"bodyV2": { "bodyV2": {
"__typename": "RichTextV2", "__typename": "RichText",
"blocknote": "[{\"id\":\"block-5\",\"type\":\"paragraph\",\"props\":{\"textColor\":\"default\",\"backgroundColor\":\"default\",\"textAlignment\":\"left\"},\"content\":[{\"type\":\"text\",\"text\":\"Verify and update contact details in the system.\",\"styles\":{}}],\"children\":[]}]", "blocknote": "[{\"id\":\"block-5\",\"type\":\"paragraph\",\"props\":{\"textColor\":\"default\",\"backgroundColor\":\"default\",\"textAlignment\":\"left\"},\"content\":[{\"type\":\"text\",\"text\":\"Verify and update contact details in the system.\",\"styles\":{}}],\"children\":[]}]",
"markdown": "Verify and update contact details in the system." "markdown": "Verify and update contact details in the system."
}, },
@@ -508,7 +508,7 @@ export const mockedTaskRecords: ObjectRecord[] =
] ]
}, },
"bodyV2": { "bodyV2": {
"__typename": "RichTextV2", "__typename": "RichText",
"blocknote": "[{\"id\":\"block-6\",\"type\":\"paragraph\",\"props\":{\"textColor\":\"default\",\"backgroundColor\":\"default\",\"textAlignment\":\"left\"},\"content\":[{\"type\":\"text\",\"text\":\"Complete reference verification for background check process.\",\"styles\":{}}],\"children\":[]}]", "blocknote": "[{\"id\":\"block-6\",\"type\":\"paragraph\",\"props\":{\"textColor\":\"default\",\"backgroundColor\":\"default\",\"textAlignment\":\"left\"},\"content\":[{\"type\":\"text\",\"text\":\"Complete reference verification for background check process.\",\"styles\":{}}],\"children\":[]}]",
"markdown": "Complete reference verification for background check process." "markdown": "Complete reference verification for background check process."
}, },
@@ -603,7 +603,7 @@ export const mockedTaskRecords: ObjectRecord[] =
] ]
}, },
"bodyV2": { "bodyV2": {
"__typename": "RichTextV2", "__typename": "RichText",
"blocknote": "[{\"id\":\"block-7\",\"type\":\"paragraph\",\"props\":{\"textColor\":\"default\",\"backgroundColor\":\"default\",\"textAlignment\":\"left\"},\"content\":[{\"type\":\"text\",\"text\":\"Send portfolio examples and case studies for review.\",\"styles\":{}}],\"children\":[]}]", "blocknote": "[{\"id\":\"block-7\",\"type\":\"paragraph\",\"props\":{\"textColor\":\"default\",\"backgroundColor\":\"default\",\"textAlignment\":\"left\"},\"content\":[{\"type\":\"text\",\"text\":\"Send portfolio examples and case studies for review.\",\"styles\":{}}],\"children\":[]}]",
"markdown": "Send portfolio examples and case studies for review." "markdown": "Send portfolio examples and case studies for review."
}, },
@@ -698,7 +698,7 @@ export const mockedTaskRecords: ObjectRecord[] =
] ]
}, },
"bodyV2": { "bodyV2": {
"__typename": "RichTextV2", "__typename": "RichText",
"blocknote": "[{\"id\":\"block-8\",\"type\":\"paragraph\",\"props\":{\"textColor\":\"default\",\"backgroundColor\":\"default\",\"textAlignment\":\"left\"},\"content\":[{\"type\":\"text\",\"text\":\"Prepare onboarding materials and schedule orientation session.\",\"styles\":{}}],\"children\":[]}]", "blocknote": "[{\"id\":\"block-8\",\"type\":\"paragraph\",\"props\":{\"textColor\":\"default\",\"backgroundColor\":\"default\",\"textAlignment\":\"left\"},\"content\":[{\"type\":\"text\",\"text\":\"Prepare onboarding materials and schedule orientation session.\",\"styles\":{}}],\"children\":[]}]",
"markdown": "Prepare onboarding materials and schedule orientation session." "markdown": "Prepare onboarding materials and schedule orientation session."
}, },
@@ -793,7 +793,7 @@ export const mockedTaskRecords: ObjectRecord[] =
] ]
}, },
"bodyV2": { "bodyV2": {
"__typename": "RichTextV2", "__typename": "RichText",
"blocknote": "[{\"id\":\"block-9\",\"type\":\"paragraph\",\"props\":{\"textColor\":\"default\",\"backgroundColor\":\"default\",\"textAlignment\":\"left\"},\"content\":[{\"type\":\"text\",\"text\":\"Arrange a follow-up call to discuss project details and next steps.\",\"styles\":{}}],\"children\":[]}]", "blocknote": "[{\"id\":\"block-9\",\"type\":\"paragraph\",\"props\":{\"textColor\":\"default\",\"backgroundColor\":\"default\",\"textAlignment\":\"left\"},\"content\":[{\"type\":\"text\",\"text\":\"Arrange a follow-up call to discuss project details and next steps.\",\"styles\":{}}],\"children\":[]}]",
"markdown": "Arrange a follow-up call to discuss project details and next steps." "markdown": "Arrange a follow-up call to discuss project details and next steps."
}, },
@@ -888,7 +888,7 @@ export const mockedTaskRecords: ObjectRecord[] =
] ]
}, },
"bodyV2": { "bodyV2": {
"__typename": "RichTextV2", "__typename": "RichText",
"blocknote": "[{\"id\":\"block-10\",\"type\":\"paragraph\",\"props\":{\"textColor\":\"default\",\"backgroundColor\":\"default\",\"textAlignment\":\"left\"},\"content\":[{\"type\":\"text\",\"text\":\"Prepare and send the project proposal document with timeline and deliverables.\",\"styles\":{}}],\"children\":[]}]", "blocknote": "[{\"id\":\"block-10\",\"type\":\"paragraph\",\"props\":{\"textColor\":\"default\",\"backgroundColor\":\"default\",\"textAlignment\":\"left\"},\"content\":[{\"type\":\"text\",\"text\":\"Prepare and send the project proposal document with timeline and deliverables.\",\"styles\":{}}],\"children\":[]}]",
"markdown": "Prepare and send the project proposal document with timeline and deliverables." "markdown": "Prepare and send the project proposal document with timeline and deliverables."
}, },
@@ -8638,7 +8638,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery =
"__typename": "Field", "__typename": "Field",
"id": "15cffcf2-ed38-4ee1-956a-d8bc9d2bef16", "id": "15cffcf2-ed38-4ee1-956a-d8bc9d2bef16",
"universalIdentifier": "20202020-4aa0-4ae8-898d-7df0afd47ab1", "universalIdentifier": "20202020-4aa0-4ae8-898d-7df0afd47ab1",
"type": "RICH_TEXT_V2", "type": "RICH_TEXT",
"name": "bodyV2", "name": "bodyV2",
"label": "Body", "label": "Body",
"description": "Task body", "description": "Task body",
@@ -23733,7 +23733,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery =
"__typename": "Field", "__typename": "Field",
"id": "71961f86-5386-4cc9-b23a-39fc5c1c7c5e", "id": "71961f86-5386-4cc9-b23a-39fc5c1c7c5e",
"universalIdentifier": "20202020-a7bb-4d94-be51-8f25181502c8", "universalIdentifier": "20202020-a7bb-4d94-be51-8f25181502c8",
"type": "RICH_TEXT_V2", "type": "RICH_TEXT",
"name": "bodyV2", "name": "bodyV2",
"label": "Body", "label": "Body",
"description": "Note body", "description": "Note body",
@@ -374,7 +374,7 @@ enum FieldMetadataType {
RATING RATING
RAW_JSON RAW_JSON
RELATION RELATION
RICH_TEXT_V2 RICH_TEXT
SELECT SELECT
TEXT TEXT
TS_VECTOR TS_VECTOR
@@ -873,7 +873,7 @@ type RatioAggregateConfig {
optionValue: String! optionValue: String!
} }
type RichTextV2Body { type RichTextBody {
blocknote: String blocknote: String
markdown: String markdown: String
} }
@@ -990,7 +990,7 @@ enum WidgetConfigurationType {
type StandaloneRichTextConfiguration { type StandaloneRichTextConfiguration {
configurationType: WidgetConfigurationType! configurationType: WidgetConfigurationType!
body: RichTextV2Body! body: RichTextBody!
} }
type PieChartConfiguration { type PieChartConfiguration {
@@ -1606,6 +1606,7 @@ enum FeatureFlagKey {
IS_DATE_TIME_WHOLE_DAY_FILTER_ENABLED IS_DATE_TIME_WHOLE_DAY_FILTER_ENABLED
IS_NAVIGATION_MENU_ITEM_EDITING_ENABLED IS_NAVIGATION_MENU_ITEM_EDITING_ENABLED
IS_DRAFT_EMAIL_ENABLED IS_DRAFT_EMAIL_ENABLED
IS_RICH_TEXT_V1_MIGRATED
} }
type SSOIdentityProvider { type SSOIdentityProvider {
@@ -309,7 +309,7 @@ export interface Field {
/** Type of the field */ /** Type of the field */
export type FieldMetadataType = 'ACTOR' | 'ADDRESS' | 'ARRAY' | 'BOOLEAN' | 'CURRENCY' | 'DATE' | 'DATE_TIME' | 'EMAILS' | 'FILES' | 'FULL_NAME' | 'LINKS' | 'MORPH_RELATION' | 'MULTI_SELECT' | 'NUMBER' | 'NUMERIC' | 'PHONES' | 'POSITION' | 'RATING' | 'RAW_JSON' | 'RELATION' | 'RICH_TEXT_V2' | 'SELECT' | 'TEXT' | 'TS_VECTOR' | 'UUID' export type FieldMetadataType = 'ACTOR' | 'ADDRESS' | 'ARRAY' | 'BOOLEAN' | 'CURRENCY' | 'DATE' | 'DATE_TIME' | 'EMAILS' | 'FILES' | 'FULL_NAME' | 'LINKS' | 'MORPH_RELATION' | 'MULTI_SELECT' | 'NUMBER' | 'NUMERIC' | 'PHONES' | 'POSITION' | 'RATING' | 'RAW_JSON' | 'RELATION' | 'RICH_TEXT' | 'SELECT' | 'TEXT' | 'TS_VECTOR' | 'UUID'
export interface IndexField { export interface IndexField {
id: Scalars['UUID'] id: Scalars['UUID']
@@ -646,10 +646,10 @@ export interface RatioAggregateConfig {
__typename: 'RatioAggregateConfig' __typename: 'RatioAggregateConfig'
} }
export interface RichTextV2Body { export interface RichTextBody {
blocknote?: Scalars['String'] blocknote?: Scalars['String']
markdown?: Scalars['String'] markdown?: Scalars['String']
__typename: 'RichTextV2Body' __typename: 'RichTextBody'
} }
export interface GridPosition { export interface GridPosition {
@@ -726,7 +726,7 @@ export type WidgetConfigurationType = 'AGGREGATE_CHART' | 'GAUGE_CHART' | 'PIE_C
export interface StandaloneRichTextConfiguration { export interface StandaloneRichTextConfiguration {
configurationType: WidgetConfigurationType configurationType: WidgetConfigurationType
body: RichTextV2Body body: RichTextBody
__typename: 'StandaloneRichTextConfiguration' __typename: 'StandaloneRichTextConfiguration'
} }
@@ -1310,7 +1310,7 @@ export interface FeatureFlag {
__typename: 'FeatureFlag' __typename: 'FeatureFlag'
} }
export type FeatureFlagKey = 'IS_UNIQUE_INDEXES_ENABLED' | 'IS_JSON_FILTER_ENABLED' | 'IS_AI_ENABLED' | 'IS_APPLICATION_ENABLED' | 'IS_MARKETPLACE_ENABLED' | 'IS_RECORD_PAGE_LAYOUT_EDITING_ENABLED' | 'IS_PUBLIC_DOMAIN_ENABLED' | 'IS_EMAILING_DOMAIN_ENABLED' | 'IS_DASHBOARD_V2_ENABLED' | 'IS_ATTACHMENT_MIGRATED' | 'IS_NOTE_TARGET_MIGRATED' | 'IS_TASK_TARGET_MIGRATED' | 'IS_ROW_LEVEL_PERMISSION_PREDICATES_ENABLED' | 'IS_JUNCTION_RELATIONS_ENABLED' | 'IS_COMMAND_MENU_ITEM_ENABLED' | 'IS_NAVIGATION_MENU_ITEM_ENABLED' | 'IS_DATE_TIME_WHOLE_DAY_FILTER_ENABLED' | 'IS_NAVIGATION_MENU_ITEM_EDITING_ENABLED' | 'IS_DRAFT_EMAIL_ENABLED' export type FeatureFlagKey = 'IS_UNIQUE_INDEXES_ENABLED' | 'IS_JSON_FILTER_ENABLED' | 'IS_AI_ENABLED' | 'IS_APPLICATION_ENABLED' | 'IS_MARKETPLACE_ENABLED' | 'IS_RECORD_PAGE_LAYOUT_EDITING_ENABLED' | 'IS_PUBLIC_DOMAIN_ENABLED' | 'IS_EMAILING_DOMAIN_ENABLED' | 'IS_DASHBOARD_V2_ENABLED' | 'IS_ATTACHMENT_MIGRATED' | 'IS_NOTE_TARGET_MIGRATED' | 'IS_TASK_TARGET_MIGRATED' | 'IS_ROW_LEVEL_PERMISSION_PREDICATES_ENABLED' | 'IS_JUNCTION_RELATIONS_ENABLED' | 'IS_COMMAND_MENU_ITEM_ENABLED' | 'IS_NAVIGATION_MENU_ITEM_ENABLED' | 'IS_DATE_TIME_WHOLE_DAY_FILTER_ENABLED' | 'IS_NAVIGATION_MENU_ITEM_EDITING_ENABLED' | 'IS_DRAFT_EMAIL_ENABLED' | 'IS_RICH_TEXT_V1_MIGRATED'
export interface SSOIdentityProvider { export interface SSOIdentityProvider {
id: Scalars['UUID'] id: Scalars['UUID']
@@ -3512,7 +3512,7 @@ export interface RatioAggregateConfigGenqlSelection{
__scalar?: boolean | number __scalar?: boolean | number
} }
export interface RichTextV2BodyGenqlSelection{ export interface RichTextBodyGenqlSelection{
blocknote?: boolean | number blocknote?: boolean | number
markdown?: boolean | number markdown?: boolean | number
__typename?: boolean | number __typename?: boolean | number
@@ -3621,7 +3621,7 @@ export interface AggregateChartConfigurationGenqlSelection{
export interface StandaloneRichTextConfigurationGenqlSelection{ export interface StandaloneRichTextConfigurationGenqlSelection{
configurationType?: boolean | number configurationType?: boolean | number
body?: RichTextV2BodyGenqlSelection body?: RichTextBodyGenqlSelection
__typename?: boolean | number __typename?: boolean | number
__scalar?: boolean | number __scalar?: boolean | number
} }
@@ -6495,10 +6495,10 @@ export interface LogicFunctionLogsInput {applicationId?: (Scalars['UUID'] | null
const RichTextV2Body_possibleTypes: string[] = ['RichTextV2Body'] const RichTextBody_possibleTypes: string[] = ['RichTextBody']
export const isRichTextV2Body = (obj?: { __typename?: any } | null): obj is RichTextV2Body => { export const isRichTextBody = (obj?: { __typename?: any } | null): obj is RichTextBody => {
if (!obj?.__typename) throw new Error('__typename is missing in "isRichTextV2Body"') if (!obj?.__typename) throw new Error('__typename is missing in "isRichTextBody"')
return RichTextV2Body_possibleTypes.includes(obj.__typename) return RichTextBody_possibleTypes.includes(obj.__typename)
} }
@@ -8290,7 +8290,7 @@ export const enumFieldMetadataType = {
RATING: 'RATING' as const, RATING: 'RATING' as const,
RAW_JSON: 'RAW_JSON' as const, RAW_JSON: 'RAW_JSON' as const,
RELATION: 'RELATION' as const, RELATION: 'RELATION' as const,
RICH_TEXT_V2: 'RICH_TEXT_V2' as const, RICH_TEXT: 'RICH_TEXT' as const,
SELECT: 'SELECT' as const, SELECT: 'SELECT' as const,
TEXT: 'TEXT' as const, TEXT: 'TEXT' as const,
TS_VECTOR: 'TS_VECTOR' as const, TS_VECTOR: 'TS_VECTOR' as const,
@@ -8570,7 +8570,8 @@ export const enumFeatureFlagKey = {
IS_NAVIGATION_MENU_ITEM_ENABLED: 'IS_NAVIGATION_MENU_ITEM_ENABLED' as const, IS_NAVIGATION_MENU_ITEM_ENABLED: 'IS_NAVIGATION_MENU_ITEM_ENABLED' as const,
IS_DATE_TIME_WHOLE_DAY_FILTER_ENABLED: 'IS_DATE_TIME_WHOLE_DAY_FILTER_ENABLED' as const, IS_DATE_TIME_WHOLE_DAY_FILTER_ENABLED: 'IS_DATE_TIME_WHOLE_DAY_FILTER_ENABLED' as const,
IS_NAVIGATION_MENU_ITEM_EDITING_ENABLED: 'IS_NAVIGATION_MENU_ITEM_EDITING_ENABLED' as const, IS_NAVIGATION_MENU_ITEM_EDITING_ENABLED: 'IS_NAVIGATION_MENU_ITEM_EDITING_ENABLED' as const,
IS_DRAFT_EMAIL_ENABLED: 'IS_DRAFT_EMAIL_ENABLED' as const IS_DRAFT_EMAIL_ENABLED: 'IS_DRAFT_EMAIL_ENABLED' as const,
IS_RICH_TEXT_V1_MIGRATED: 'IS_RICH_TEXT_V1_MIGRATED' as const
} }
export const enumRelationType = { export const enumRelationType = {
@@ -1822,7 +1822,7 @@ export default {
1 1
] ]
}, },
"RichTextV2Body": { "RichTextBody": {
"blocknote": [ "blocknote": [
1 1
], ],
@@ -6,5 +6,5 @@ export type {
FullNameMetadata as FullNameField, FullNameMetadata as FullNameField,
LinksMetadata as LinksField, LinksMetadata as LinksField,
PhonesMetadata as PhonesField, PhonesMetadata as PhonesField,
RichTextV2Metadata as RichTextField, RichTextMetadata as RichTextField,
} from 'twenty-shared/types'; } from 'twenty-shared/types';
@@ -1,10 +1,12 @@
import { InjectDataSource, InjectRepository } from '@nestjs/typeorm'; import { InjectDataSource, InjectRepository } from '@nestjs/typeorm';
import { Command } from 'nest-commander'; import { Command } from 'nest-commander';
import { FeatureFlagKey } from 'twenty-shared/types';
import { DataSource, Repository } from 'typeorm'; import { DataSource, Repository } from 'typeorm';
import { ActiveOrSuspendedWorkspacesMigrationCommandRunner } from 'src/database/commands/command-runners/active-or-suspended-workspaces-migration.command-runner'; import { ActiveOrSuspendedWorkspacesMigrationCommandRunner } from 'src/database/commands/command-runners/active-or-suspended-workspaces-migration.command-runner';
import { RunOnWorkspaceArgs } from 'src/database/commands/command-runners/workspaces-migration.command-runner'; import { RunOnWorkspaceArgs } from 'src/database/commands/command-runners/workspaces-migration.command-runner';
import { FeatureFlagService } from 'src/engine/core-modules/feature-flag/services/feature-flag.service';
import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity'; import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service'; import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service';
import { getMetadataFlatEntityMapsKey } from 'src/engine/metadata-modules/flat-entity/utils/get-metadata-flat-entity-maps-key.util'; import { getMetadataFlatEntityMapsKey } from 'src/engine/metadata-modules/flat-entity/utils/get-metadata-flat-entity-maps-key.util';
@@ -18,7 +20,7 @@ import { type WorkspaceCacheKeyName } from 'src/engine/workspace-cache/types/wor
@Command({ @Command({
name: 'upgrade:1-20:migrate-rich-text-to-text', name: 'upgrade:1-20:migrate-rich-text-to-text',
description: description:
'Migrate deprecated RICH_TEXT field metadata type to TEXT. The underlying column type is already text, so only the metadata needs updating.', 'Migrate deprecated RICH_TEXT (V1) to TEXT and rename RICH_TEXT_V2 to RICH_TEXT. The underlying column type is already text, so only the metadata needs updating.',
}) })
export class MigrateRichTextToTextCommand extends ActiveOrSuspendedWorkspacesMigrationCommandRunner { export class MigrateRichTextToTextCommand extends ActiveOrSuspendedWorkspacesMigrationCommandRunner {
constructor( constructor(
@@ -28,6 +30,7 @@ export class MigrateRichTextToTextCommand extends ActiveOrSuspendedWorkspacesMig
private readonly coreDataSource: DataSource, private readonly coreDataSource: DataSource,
protected readonly twentyORMGlobalManager: GlobalWorkspaceOrmManager, protected readonly twentyORMGlobalManager: GlobalWorkspaceOrmManager,
protected readonly dataSourceService: DataSourceService, protected readonly dataSourceService: DataSourceService,
private readonly featureFlagService: FeatureFlagService,
private readonly workspaceCacheService: WorkspaceCacheService, private readonly workspaceCacheService: WorkspaceCacheService,
private readonly workspaceCacheStorageService: WorkspaceCacheStorageService, private readonly workspaceCacheStorageService: WorkspaceCacheStorageService,
private readonly workspaceMetadataVersionService: WorkspaceMetadataVersionService, private readonly workspaceMetadataVersionService: WorkspaceMetadataVersionService,
@@ -39,26 +42,38 @@ export class MigrateRichTextToTextCommand extends ActiveOrSuspendedWorkspacesMig
workspaceId, workspaceId,
options, options,
}: RunOnWorkspaceArgs): Promise<void> { }: RunOnWorkspaceArgs): Promise<void> {
const dryRun = options?.dryRun ?? false; const isMigrated = await this.featureFlagService.isFeatureEnabled(
FeatureFlagKey.IS_RICH_TEXT_V1_MIGRATED,
this.logger.log( workspaceId,
`${dryRun ? '[DRY RUN] ' : ''}Migrating RICH_TEXT fields to TEXT in workspace ${workspaceId}`,
); );
if (dryRun) { if (isMigrated) {
this.logger.log( this.logger.log(
`[DRY RUN] Would update RICH_TEXT -> TEXT in core.fieldMetadata for workspace ${workspaceId}. Skipping.`, `Rich text migration already completed for workspace ${workspaceId}. Skipping.`,
); );
return; return;
} }
const dryRun = options?.dryRun ?? false;
if (dryRun) {
this.logger.log(
`[DRY RUN] Would update RICH_TEXT -> TEXT and RICH_TEXT_V2 -> RICH_TEXT in core.fieldMetadata for workspace ${workspaceId}. Skipping.`,
);
return;
}
this.logger.log(`Migrating RICH_TEXT fields in workspace ${workspaceId}`);
const queryRunner = this.coreDataSource.createQueryRunner(); const queryRunner = this.coreDataSource.createQueryRunner();
await queryRunner.connect(); await queryRunner.connect();
await queryRunner.startTransaction();
try { try {
const result = await queryRunner.query( const v1Result = await queryRunner.query(
`UPDATE core."fieldMetadata" `UPDATE core."fieldMetadata"
SET "type" = 'TEXT' SET "type" = 'TEXT'
WHERE "workspaceId" = $1 WHERE "workspaceId" = $1
@@ -67,18 +82,48 @@ export class MigrateRichTextToTextCommand extends ActiveOrSuspendedWorkspacesMig
[workspaceId], [workspaceId],
); );
const updatedCount = result.length; const v1Count = v1Result.length;
if (updatedCount > 0) { const renameResult = await queryRunner.query(
`UPDATE core."fieldMetadata"
SET "type" = 'RICH_TEXT'
WHERE "workspaceId" = $1
AND "type" = 'RICH_TEXT_V2'
RETURNING "id"`,
[workspaceId],
);
const renameCount = renameResult.length;
await queryRunner.commitTransaction();
if (v1Count > 0) {
this.logger.log( this.logger.log(
`Migrated ${updatedCount} RICH_TEXT field(s) to TEXT in workspace ${workspaceId}`, `Migrated ${v1Count} RICH_TEXT (V1) field(s) to TEXT in workspace ${workspaceId}`,
); );
}
if (renameCount > 0) {
this.logger.log(
`Renamed ${renameCount} RICH_TEXT_V2 field(s) to RICH_TEXT in workspace ${workspaceId}`,
);
}
await this.featureFlagService.enableFeatureFlags(
[FeatureFlagKey.IS_RICH_TEXT_V1_MIGRATED],
workspaceId,
);
if (v1Count > 0 || renameCount > 0) {
await this.invalidateCaches(workspaceId); await this.invalidateCaches(workspaceId);
} else { } else {
this.logger.log( this.logger.log(
`No RICH_TEXT fields found in workspace ${workspaceId}`, `No RICH_TEXT or RICH_TEXT_V2 fields found in workspace ${workspaceId}`,
); );
} }
} catch (error) {
await queryRunner.rollbackTransaction();
throw error;
} finally { } finally {
await queryRunner.release(); await queryRunner.release();
} }
@@ -94,6 +139,7 @@ export class MigrateRichTextToTextCommand extends ActiveOrSuspendedWorkspacesMig
.map(getMetadataFlatEntityMapsKey), .map(getMetadataFlatEntityMapsKey),
), ),
'ORMEntityMetadatas', 'ORMEntityMetadatas',
'featureFlagsMap',
]; ];
await this.workspaceCacheService.invalidateAndRecompute( await this.workspaceCacheService.invalidateAndRecompute(
@@ -172,11 +172,11 @@ exports[`DataArgProcessorService failing inputs validation RELATION should throw
exports[`DataArgProcessorService failing inputs validation RELATION should throw for invalid input #5: "non-uuid" 1`] = `"Invalid UUID value 'non-uuid' for field "manyToOneRelationFieldId""`; exports[`DataArgProcessorService failing inputs validation RELATION should throw for invalid input #5: "non-uuid" 1`] = `"Invalid UUID value 'non-uuid' for field "manyToOneRelationFieldId""`;
exports[`DataArgProcessorService failing inputs validation RICH_TEXT_V2 should throw for invalid input #1: "not-a-rich-text" 1`] = `"Invalid rich text v2 value 'not-a-rich-text' for field "richTextV2Field" - Should be an object"`; exports[`DataArgProcessorService failing inputs validation RICH_TEXT should throw for invalid input #1: "not-a-rich-text" 1`] = `"Invalid rich text v2 value 'not-a-rich-text' for field "richTextField" - Should be an object"`;
exports[`DataArgProcessorService failing inputs validation RICH_TEXT_V2 should throw for invalid input #2: 1 1`] = `"Invalid rich text v2 value 1 for field "richTextV2Field" - Should be an object"`; exports[`DataArgProcessorService failing inputs validation RICH_TEXT should throw for invalid input #2: 1 1`] = `"Invalid rich text v2 value 1 for field "richTextField" - Should be an object"`;
exports[`DataArgProcessorService failing inputs validation RICH_TEXT_V2 should throw for invalid input #3: true 1`] = `"Invalid rich text v2 value true for field "richTextV2Field" - Should be an object"`; exports[`DataArgProcessorService failing inputs validation RICH_TEXT should throw for invalid input #3: true 1`] = `"Invalid rich text v2 value true for field "richTextField" - Should be an object"`;
exports[`DataArgProcessorService failing inputs validation SELECT should throw for invalid input #1: "not-a-select-option" 1`] = `"Invalid value 'not-a-select-option' for field "selectField""`; exports[`DataArgProcessorService failing inputs validation SELECT should throw for invalid input #1: "not-a-select-option" 1`] = `"Invalid value 'not-a-select-option' for field "selectField""`;
@@ -150,10 +150,10 @@ export const failingInputsByFieldMetadataType: {
{ input: { linksField: 1 } }, { input: { linksField: 1 } },
{ input: { linksField: true } }, { input: { linksField: true } },
], ],
[FieldMetadataType.RICH_TEXT_V2]: [ [FieldMetadataType.RICH_TEXT]: [
{ input: { richTextV2Field: 'not-a-rich-text' } }, { input: { richTextField: 'not-a-rich-text' } },
{ input: { richTextV2Field: 1 } }, { input: { richTextField: 1 } },
{ input: { richTextV2Field: true } }, { input: { richTextField: true } },
], ],
[FieldMetadataType.POSITION]: [ [FieldMetadataType.POSITION]: [
{ input: { position: 'not-a-position' } }, { input: { position: 'not-a-position' } },
@@ -134,9 +134,9 @@ export const fieldMetadataConfigByFieldName: Record<
type: FieldMetadataType.LINKS, type: FieldMetadataType.LINKS,
isNullable: true, isNullable: true,
}, },
richTextV2Field: { richTextField: {
name: 'richTextV2Field', name: 'richTextField',
type: FieldMetadataType.RICH_TEXT_V2, type: FieldMetadataType.RICH_TEXT,
isNullable: true, isNullable: true,
}, },
position: { position: {
@@ -383,16 +383,16 @@ export const successfulInputsByFieldMetadataType: {
}, },
{ input: { linksField: null }, expectedOutput: { linksField: null } }, { input: { linksField: null }, expectedOutput: { linksField: null } },
], ],
[FieldMetadataType.RICH_TEXT_V2]: [ [FieldMetadataType.RICH_TEXT]: [
{ {
input: { richTextV2Field: { blocknote: 'test', markdown: 'test' } }, input: { richTextField: { blocknote: 'test', markdown: 'test' } },
expectedOutput: { expectedOutput: {
richTextV2Field: { blocknote: 'test', markdown: 'test' }, richTextField: { blocknote: 'test', markdown: 'test' },
}, },
}, },
{ {
input: { richTextV2Field: null }, input: { richTextField: null },
expectedOutput: { richTextV2Field: null }, expectedOutput: { richTextField: null },
}, },
], ],
[FieldMetadataType.POSITION]: [ [FieldMetadataType.POSITION]: [
@@ -16,9 +16,9 @@ import { successfulInputsByFieldMetadataType } from './constants/successful-inpu
// Mock the rich text v2 transformation to avoid BlockNote module issues // Mock the rich text v2 transformation to avoid BlockNote module issues
jest.mock( jest.mock(
'src/engine/core-modules/record-transformer/utils/transform-rich-text-v2.util', 'src/engine/core-modules/record-transformer/utils/transform-rich-text.util',
() => ({ () => ({
transformRichTextV2Value: jest.fn().mockImplementation((value) => value), transformRichTextValue: jest.fn().mockImplementation((value) => value),
}), }),
); );
@@ -44,7 +44,7 @@ const DEFAULT_ACTOR_FIELD_NULL_EQUIVALENT_VALUE = {
name: DEFAULT_TEXT_FIELD_NULL_EQUIVALENT_VALUE, name: DEFAULT_TEXT_FIELD_NULL_EQUIVALENT_VALUE,
}; };
const DEFAULT_RICH_TEXT_V2_FIELD_NULL_EQUIVALENT_VALUE = { const DEFAULT_RICH_TEXT_FIELD_NULL_EQUIVALENT_VALUE = {
markdown: DEFAULT_TEXT_FIELD_NULL_EQUIVALENT_VALUE, markdown: DEFAULT_TEXT_FIELD_NULL_EQUIVALENT_VALUE,
}; };
@@ -57,6 +57,5 @@ export const DEFAULT_COMPOSITE_FIELDS_NULL_EQUIVALENT_VALUE: Partial<
[FieldMetadataType.PHONES]: DEFAULT_PHONES_FIELD_NULL_EQUIVALENT_VALUE, [FieldMetadataType.PHONES]: DEFAULT_PHONES_FIELD_NULL_EQUIVALENT_VALUE,
[FieldMetadataType.FULL_NAME]: DEFAULT_FULL_NAME_FIELD_NULL_EQUIVALENT_VALUE, [FieldMetadataType.FULL_NAME]: DEFAULT_FULL_NAME_FIELD_NULL_EQUIVALENT_VALUE,
[FieldMetadataType.ACTOR]: DEFAULT_ACTOR_FIELD_NULL_EQUIVALENT_VALUE, [FieldMetadataType.ACTOR]: DEFAULT_ACTOR_FIELD_NULL_EQUIVALENT_VALUE,
[FieldMetadataType.RICH_TEXT_V2]: [FieldMetadataType.RICH_TEXT]: DEFAULT_RICH_TEXT_FIELD_NULL_EQUIVALENT_VALUE,
DEFAULT_RICH_TEXT_V2_FIELD_NULL_EQUIVALENT_VALUE,
}; };
@@ -41,7 +41,7 @@ import { validateOverriddenPositionFieldOrThrow } from 'src/engine/api/common/co
import { validatePhonesFieldOrThrow } from 'src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-phones-field-or-throw.util'; import { validatePhonesFieldOrThrow } from 'src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-phones-field-or-throw.util';
import { validateRatingAndSelectFieldOrThrow } from 'src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-rating-and-select-field-or-throw.util'; import { validateRatingAndSelectFieldOrThrow } from 'src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-rating-and-select-field-or-throw.util';
import { validateRawJsonFieldOrThrow } from 'src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-raw-json-field-or-throw.util'; import { validateRawJsonFieldOrThrow } from 'src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-raw-json-field-or-throw.util';
import { validateRichTextV2FieldOrThrow } from 'src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-rich-text-v2-field-or-throw.util'; import { validateRichTextFieldOrThrow } from 'src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-rich-text-field-or-throw.util';
import { validateTextFieldOrThrow } from 'src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-text-field-or-throw.util'; import { validateTextFieldOrThrow } from 'src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-text-field-or-throw.util';
import { validateUUIDFieldOrThrow } from 'src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-uuid-field-or-throw.util'; import { validateUUIDFieldOrThrow } from 'src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-uuid-field-or-throw.util';
import { import {
@@ -54,7 +54,7 @@ import { RecordPositionService } from 'src/engine/core-modules/record-position/s
import { transformEmailsValue } from 'src/engine/core-modules/record-transformer/utils/transform-emails-value.util'; import { transformEmailsValue } from 'src/engine/core-modules/record-transformer/utils/transform-emails-value.util';
import { transformLinksValue } from 'src/engine/core-modules/record-transformer/utils/transform-links-value.util'; import { transformLinksValue } from 'src/engine/core-modules/record-transformer/utils/transform-links-value.util';
import { transformPhonesValue } from 'src/engine/core-modules/record-transformer/utils/transform-phones-value.util'; import { transformPhonesValue } from 'src/engine/core-modules/record-transformer/utils/transform-phones-value.util';
import { transformRichTextV2Value } from 'src/engine/core-modules/record-transformer/utils/transform-rich-text-v2.util'; import { transformRichTextValue } from 'src/engine/core-modules/record-transformer/utils/transform-rich-text.util';
import { WorkspaceNotFoundDefaultError } from 'src/engine/core-modules/workspace/workspace.exception'; import { WorkspaceNotFoundDefaultError } from 'src/engine/core-modules/workspace/workspace.exception';
import { FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type'; import { FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type';
import { findFlatEntityByIdInFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps.util'; import { findFlatEntityByIdInFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps.util';
@@ -302,10 +302,10 @@ export class DataArgProcessorService {
return transformActorField(validatedValue); return transformActorField(validatedValue);
} }
case FieldMetadataType.RICH_TEXT_V2: { case FieldMetadataType.RICH_TEXT: {
const validatedValue = validateRichTextV2FieldOrThrow(value, key); const validatedValue = validateRichTextFieldOrThrow(value, key);
return await transformRichTextV2Value(validatedValue); return await transformRichTextValue(validatedValue);
} }
case FieldMetadataType.LINKS: { case FieldMetadataType.LINKS: {
const validatedValue = validateLinksFieldOrThrow(value, key); const validatedValue = validateLinksFieldOrThrow(value, key);
@@ -171,12 +171,12 @@ describe('findPostgresDefaultNullEquivalentValue', () => {
}); });
}); });
describe('RICH_TEXT_V2', () => { describe('RICH_TEXT', () => {
it('should return json default for blocknote', () => { it('should return json default for blocknote', () => {
expect( expect(
findPostgresDefaultNullEquivalentValue( findPostgresDefaultNullEquivalentValue(
{}, {},
FieldMetadataType.RICH_TEXT_V2, FieldMetadataType.RICH_TEXT,
'blocknote', 'blocknote',
), ),
).toBe(undefined); ).toBe(undefined);
@@ -186,7 +186,7 @@ describe('findPostgresDefaultNullEquivalentValue', () => {
expect( expect(
findPostgresDefaultNullEquivalentValue( findPostgresDefaultNullEquivalentValue(
'', '',
FieldMetadataType.RICH_TEXT_V2, FieldMetadataType.RICH_TEXT,
'markdown', 'markdown',
), ),
).toBe(POSTGRES_DEFAULT_TEXT_FIELD_NULL_EQUIVALENT_VALUE); ).toBe(POSTGRES_DEFAULT_TEXT_FIELD_NULL_EQUIVALENT_VALUE);
@@ -117,7 +117,7 @@ export const findPostgresDefaultNullEquivalentValue = (
return undefined; return undefined;
} }
} }
case FieldMetadataType.RICH_TEXT_V2: { case FieldMetadataType.RICH_TEXT: {
switch (key) { switch (key) {
case 'markdown': case 'markdown':
return isNullEquivalentTextFieldValue(value) || value === 'NULL' return isNullEquivalentTextFieldValue(value) || value === 'NULL'
@@ -1,16 +1,16 @@
import { validateRichTextV2FieldOrThrow } from 'src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-rich-text-v2-field-or-throw.util'; import { validateRichTextFieldOrThrow } from 'src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-rich-text-field-or-throw.util';
import { CommonQueryRunnerException } from 'src/engine/api/common/common-query-runners/errors/common-query-runner.exception'; import { CommonQueryRunnerException } from 'src/engine/api/common/common-query-runners/errors/common-query-runner.exception';
describe('validateRichTextV2FieldOrThrow', () => { describe('validateRichTextFieldOrThrow', () => {
describe('valid inputs', () => { describe('valid inputs', () => {
it('should return null when value is null', () => { it('should return null when value is null', () => {
const result = validateRichTextV2FieldOrThrow(null, 'testField'); const result = validateRichTextFieldOrThrow(null, 'testField');
expect(result).toBeNull(); expect(result).toBeNull();
}); });
it('should return null when value is an empty object', () => { it('should return null when value is an empty object', () => {
const result = validateRichTextV2FieldOrThrow({}, 'testField'); const result = validateRichTextFieldOrThrow({}, 'testField');
expect(result).toBeNull(); expect(result).toBeNull();
}); });
@@ -20,7 +20,7 @@ describe('validateRichTextV2FieldOrThrow', () => {
blocknote: 'some blocknote content', blocknote: 'some blocknote content',
markdown: '# Heading\nContent', markdown: '# Heading\nContent',
}; };
const result = validateRichTextV2FieldOrThrow(value, 'testField'); const result = validateRichTextFieldOrThrow(value, 'testField');
expect(result).toEqual(value); expect(result).toEqual(value);
}); });
@@ -29,23 +29,23 @@ describe('validateRichTextV2FieldOrThrow', () => {
describe('invalid inputs', () => { describe('invalid inputs', () => {
it('should throw when value is undefined', () => { it('should throw when value is undefined', () => {
expect(() => expect(() =>
validateRichTextV2FieldOrThrow(undefined, 'testField'), validateRichTextFieldOrThrow(undefined, 'testField'),
).toThrow(CommonQueryRunnerException); ).toThrow(CommonQueryRunnerException);
}); });
it('should throw when value is a string', () => { it('should throw when value is a string', () => {
expect(() => expect(() =>
validateRichTextV2FieldOrThrow('not an object', 'testField'), validateRichTextFieldOrThrow('not an object', 'testField'),
).toThrow(CommonQueryRunnerException); ).toThrow(CommonQueryRunnerException);
}); });
it('should throw when value has invalid subfields', () => { it('should throw when value has invalid subfields', () => {
const value = { invalidField: 'value' }; const value = { invalidField: 'value' };
expect(() => validateRichTextV2FieldOrThrow(value, 'testField')).toThrow( expect(() => validateRichTextFieldOrThrow(value, 'testField')).toThrow(
CommonQueryRunnerException, CommonQueryRunnerException,
); );
expect(() => validateRichTextV2FieldOrThrow(value, 'testField')).toThrow( expect(() => validateRichTextFieldOrThrow(value, 'testField')).toThrow(
/Should have only blocknote, markdown subfields/, /Should have only blocknote, markdown subfields/,
); );
}); });
@@ -12,7 +12,7 @@ import {
CommonQueryRunnerExceptionCode, CommonQueryRunnerExceptionCode,
} from 'src/engine/api/common/common-query-runners/errors/common-query-runner.exception'; } from 'src/engine/api/common/common-query-runners/errors/common-query-runner.exception';
export const validateRichTextV2FieldOrThrow = ( export const validateRichTextFieldOrThrow = (
value: unknown, value: unknown,
fieldName: string, fieldName: string,
): { ): {
@@ -29,16 +29,16 @@ export const validateRichTextV2FieldOrThrow = (
if (Object.keys(parsedValue).length === 0) return null; if (Object.keys(parsedValue).length === 0) return null;
const subfields = Object.keys(parsedValue); const subfields = Object.keys(parsedValue);
const richTextV2Subfields = compositeTypeDefinitions const richTextSubfields = compositeTypeDefinitions
.get(FieldMetadataType.RICH_TEXT_V2) .get(FieldMetadataType.RICH_TEXT)
?.properties.filter( ?.properties.filter(
(prop) => prop.hidden !== true && prop.hidden !== 'input', (prop) => prop.hidden !== true && prop.hidden !== 'input',
) )
.map((prop) => prop.name); .map((prop) => prop.name);
if (!subfields.every((subfield) => richTextV2Subfields?.includes(subfield))) if (!subfields.every((subfield) => richTextSubfields?.includes(subfield)))
throw new Error( throw new Error(
`Should have only ${richTextV2Subfields?.join(', ')} subfields`, `Should have only ${richTextSubfields?.join(', ')} subfields`,
); );
return value as { return value as {
@@ -140,9 +140,9 @@ exports[`FilterArgProcessorService failing filter inputs validation RELATION sho
exports[`FilterArgProcessorService failing filter inputs validation RELATION should throw for invalid filter #3: {"manyToOneRelationFieldId":{}} 1`] = `"Filter for field "manyToOneRelationFieldId" must have exactly one operator"`; exports[`FilterArgProcessorService failing filter inputs validation RELATION should throw for invalid filter #3: {"manyToOneRelationFieldId":{}} 1`] = `"Filter for field "manyToOneRelationFieldId" must have exactly one operator"`;
exports[`FilterArgProcessorService failing filter inputs validation RICH_TEXT_V2 should throw for invalid filter #1: {"richTextV2Field":{"invalidOperator":"test"}} 1`] = `"Sub field "invalidOperator" not found for composite type: RICH_TEXT_V2"`; exports[`FilterArgProcessorService failing filter inputs validation RICH_TEXT should throw for invalid filter #1: {"richTextField":{"invalidOperator":"test"}} 1`] = `"Sub field "invalidOperator" not found for composite type: RICH_TEXT"`;
exports[`FilterArgProcessorService failing filter inputs validation RICH_TEXT_V2 should throw for invalid filter #2: {"richTextV2Field":{"markdown":{"invalidOperator":"test"}}} 1`] = `"Operator "invalidOperator" is not valid for field "richTextV2Field.markdown" of type TEXT - Allowed operators: eq, neq, gt, gte, lt, lte, in, is, like, ilike, startsWith, endsWith"`; exports[`FilterArgProcessorService failing filter inputs validation RICH_TEXT should throw for invalid filter #2: {"richTextField":{"markdown":{"invalidOperator":"test"}}} 1`] = `"Operator "invalidOperator" is not valid for field "richTextField.markdown" of type TEXT - Allowed operators: eq, neq, gt, gte, lt, lte, in, is, like, ilike, startsWith, endsWith"`;
exports[`FilterArgProcessorService failing filter inputs validation SELECT should throw for invalid filter #1: {"selectField":{"invalidOperator":"OPTION_1"}} 1`] = `"Operator "invalidOperator" is not valid for field "selectField" of type SELECT - Allowed operators: eq, neq, in, containsAny, is, isEmptyArray"`; exports[`FilterArgProcessorService failing filter inputs validation SELECT should throw for invalid filter #1: {"selectField":{"invalidOperator":"OPTION_1"}} 1`] = `"Operator "invalidOperator" is not valid for field "selectField" of type SELECT - Allowed operators: eq, neq, in, containsAny, is, isEmptyArray"`;
@@ -116,9 +116,9 @@ export const failingFilterInputsByFieldMetadataType: {
{ filter: { filesField: { invalidOperator: 'test' } } }, { filter: { filesField: { invalidOperator: 'test' } } },
{ filter: { filesField: {} } }, { filter: { filesField: {} } },
], ],
[FieldMetadataType.RICH_TEXT_V2]: [ [FieldMetadataType.RICH_TEXT]: [
{ filter: { richTextV2Field: { invalidOperator: 'test' } } }, { filter: { richTextField: { invalidOperator: 'test' } } },
{ filter: { richTextV2Field: { markdown: { invalidOperator: 'test' } } } }, { filter: { richTextField: { markdown: { invalidOperator: 'test' } } } },
], ],
[FieldMetadataType.ADDRESS]: [ [FieldMetadataType.ADDRESS]: [
{ filter: { addressField: { invalidSubField: { eq: 'test' } } } }, { filter: { addressField: { invalidSubField: { eq: 'test' } } } },
@@ -190,8 +190,8 @@ export const successfulFilterInputsByFieldMetadataType: {
{ filter: { filesField: { is: 'NOT_NULL' } } }, { filter: { filesField: { is: 'NOT_NULL' } } },
{ filter: { filesField: { like: '%test%' } } }, { filter: { filesField: { like: '%test%' } } },
], ],
[FieldMetadataType.RICH_TEXT_V2]: [ [FieldMetadataType.RICH_TEXT]: [
{ filter: { richTextV2Field: { markdown: { ilike: '%test%' } } } }, { filter: { richTextField: { markdown: { ilike: '%test%' } } } },
], ],
[FieldMetadataType.ADDRESS]: [ [FieldMetadataType.ADDRESS]: [
{ filter: { addressField: { addressCity: { eq: 'Paris' } } } }, { filter: { addressField: { addressCity: { eq: 'Paris' } } } },
@@ -73,4 +73,4 @@ export const ENUM_FILTER_OPERATORS: FilterOperator[] = [
export const RAW_JSON_FILTER_OPERATORS: FilterOperator[] = ['is', 'like']; export const RAW_JSON_FILTER_OPERATORS: FilterOperator[] = ['is', 'like'];
export const RICH_TEXT_V2_FILTER_OPERATORS: FilterOperator[] = ['ilike']; export const RICH_TEXT_FILTER_OPERATORS: FilterOperator[] = ['ilike'];
@@ -9,7 +9,7 @@ import {
MULTI_SELECT_FILTER_OPERATORS, MULTI_SELECT_FILTER_OPERATORS,
NUMBER_FILTER_OPERATORS, NUMBER_FILTER_OPERATORS,
RAW_JSON_FILTER_OPERATORS, RAW_JSON_FILTER_OPERATORS,
RICH_TEXT_V2_FILTER_OPERATORS, RICH_TEXT_FILTER_OPERATORS,
STRING_FILTER_OPERATORS, STRING_FILTER_OPERATORS,
UUID_FILTER_OPERATORS, UUID_FILTER_OPERATORS,
} from 'src/engine/api/common/common-args-processors/filter-arg-processor/constants/filter-operators.constant'; } from 'src/engine/api/common/common-args-processors/filter-arg-processor/constants/filter-operators.constant';
@@ -53,8 +53,8 @@ export const getOperatorsForFieldType = (
case FieldMetadataType.FILES: case FieldMetadataType.FILES:
return RAW_JSON_FILTER_OPERATORS; return RAW_JSON_FILTER_OPERATORS;
case FieldMetadataType.RICH_TEXT_V2: case FieldMetadataType.RICH_TEXT:
return RICH_TEXT_V2_FILTER_OPERATORS; return RICH_TEXT_FILTER_OPERATORS;
case FieldMetadataType.TS_VECTOR: case FieldMetadataType.TS_VECTOR:
case FieldMetadataType.ACTOR: case FieldMetadataType.ACTOR:
@@ -11,7 +11,7 @@ import { type QueryResultFieldValue } from 'src/engine/api/graphql/workspace-que
import { type QueryResultGetterHandlerInterface } from 'src/engine/api/graphql/workspace-query-runner/factories/query-result-getters/interfaces/query-result-getter-handler.interface'; import { type QueryResultGetterHandlerInterface } from 'src/engine/api/graphql/workspace-query-runner/factories/query-result-getters/interfaces/query-result-getter-handler.interface';
import { FilesFieldQueryResultGetterHandler } from 'src/engine/api/common/common-result-getters/handlers/field-handlers/files-field-query-result-getter.handler'; import { FilesFieldQueryResultGetterHandler } from 'src/engine/api/common/common-result-getters/handlers/field-handlers/files-field-query-result-getter.handler';
import { RichTextV2FieldQueryResultGetterHandler } from 'src/engine/api/common/common-result-getters/handlers/field-handlers/rich-text-v2-field-query-result-getter.handler'; import { RichTextFieldQueryResultGetterHandler } from 'src/engine/api/common/common-result-getters/handlers/field-handlers/rich-text-field-query-result-getter.handler';
import { AttachmentQueryResultGetterHandler } from 'src/engine/api/graphql/workspace-query-runner/factories/query-result-getters/handlers/attachment-query-result-getter.handler'; import { AttachmentQueryResultGetterHandler } from 'src/engine/api/graphql/workspace-query-runner/factories/query-result-getters/handlers/attachment-query-result-getter.handler';
import { PersonQueryResultGetterHandler } from 'src/engine/api/graphql/workspace-query-runner/factories/query-result-getters/handlers/person-query-result-getter.handler'; import { PersonQueryResultGetterHandler } from 'src/engine/api/graphql/workspace-query-runner/factories/query-result-getters/handlers/person-query-result-getter.handler';
import { WorkspaceMemberQueryResultGetterHandler } from 'src/engine/api/graphql/workspace-query-runner/factories/query-result-getters/handlers/workspace-member-query-result-getter.handler'; import { WorkspaceMemberQueryResultGetterHandler } from 'src/engine/api/graphql/workspace-query-runner/factories/query-result-getters/handlers/workspace-member-query-result-getter.handler';
@@ -68,8 +68,8 @@ export class CommonResultGettersService {
new FilesFieldQueryResultGetterHandler(this.fileUrlService), new FilesFieldQueryResultGetterHandler(this.fileUrlService),
], ],
[ [
FieldMetadataType.RICH_TEXT_V2, FieldMetadataType.RICH_TEXT,
new RichTextV2FieldQueryResultGetterHandler(this.fileUrlService), new RichTextFieldQueryResultGetterHandler(this.fileUrlService),
], ],
]); ]);
} }
@@ -1,6 +1,6 @@
import { FieldMetadataType, type ObjectRecord } from 'twenty-shared/types'; import { FieldMetadataType, type ObjectRecord } from 'twenty-shared/types';
import { RichTextV2FieldQueryResultGetterHandler } from 'src/engine/api/common/common-result-getters/handlers/field-handlers/rich-text-v2-field-query-result-getter.handler'; import { RichTextFieldQueryResultGetterHandler } from 'src/engine/api/common/common-result-getters/handlers/field-handlers/rich-text-field-query-result-getter.handler';
import { type FileUrlService } from 'src/engine/core-modules/file/file-url/file-url.service'; import { type FileUrlService } from 'src/engine/core-modules/file/file-url/file-url.service';
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type'; import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
@@ -13,7 +13,7 @@ const baseRecord: ObjectRecord = {
const richTextFieldMetadata = [ const richTextFieldMetadata = [
{ {
type: FieldMetadataType.RICH_TEXT_V2, type: FieldMetadataType.RICH_TEXT,
name: 'bodyV2', name: 'bodyV2',
}, },
] as FlatFieldMetadata[]; ] as FlatFieldMetadata[];
@@ -23,12 +23,12 @@ const mockFileUrlService = {
signFileByIdUrl: jest.fn().mockReturnValue('signed-path'), signFileByIdUrl: jest.fn().mockReturnValue('signed-path'),
} as unknown as FileUrlService; } as unknown as FileUrlService;
describe('RichTextV2FieldQueryResultGetterHandler', () => { describe('RichTextFieldQueryResultGetterHandler', () => {
let handler: RichTextV2FieldQueryResultGetterHandler; let handler: RichTextFieldQueryResultGetterHandler;
beforeEach(() => { beforeEach(() => {
process.env.SERVER_URL = 'https://my-domain.twenty.com'; process.env.SERVER_URL = 'https://my-domain.twenty.com';
handler = new RichTextV2FieldQueryResultGetterHandler(mockFileUrlService); handler = new RichTextFieldQueryResultGetterHandler(mockFileUrlService);
}); });
afterEach(() => { afterEach(() => {
@@ -37,7 +37,7 @@ describe('RichTextV2FieldQueryResultGetterHandler', () => {
}); });
describe('should return record unchanged', () => { describe('should return record unchanged', () => {
it('when no RICH_TEXT_V2 field metadata is present', async () => { it('when no RICH_TEXT field metadata is present', async () => {
const record = { const record = {
...baseRecord, ...baseRecord,
bodyV2: { blocknote: '[]', markdown: null }, bodyV2: { blocknote: '[]', markdown: null },
@@ -156,11 +156,11 @@ describe('RichTextV2FieldQueryResultGetterHandler', () => {
}); });
}); });
describe('should handle multiple RICH_TEXT_V2 fields', () => { describe('should handle multiple RICH_TEXT fields', () => {
it('when record has multiple rich text fields', async () => { it('when record has multiple rich text fields', async () => {
const multiFieldMetadata = [ const multiFieldMetadata = [
{ type: FieldMetadataType.RICH_TEXT_V2, name: 'bodyV2' }, { type: FieldMetadataType.RICH_TEXT, name: 'bodyV2' },
{ type: FieldMetadataType.RICH_TEXT_V2, name: 'description' }, { type: FieldMetadataType.RICH_TEXT, name: 'description' },
] as FlatFieldMetadata[]; ] as FlatFieldMetadata[];
const record = { const record = {
@@ -30,7 +30,7 @@ const parseBlocknoteJsonSafely = (
} }
}; };
export class RichTextV2FieldQueryResultGetterHandler export class RichTextFieldQueryResultGetterHandler
implements QueryResultGetterHandlerInterface implements QueryResultGetterHandlerInterface
{ {
constructor(private readonly fileUrlService: FileUrlService) {} constructor(private readonly fileUrlService: FileUrlService) {}
@@ -40,15 +40,15 @@ export class RichTextV2FieldQueryResultGetterHandler
workspaceId: string, workspaceId: string,
flatFieldMetadata: FlatFieldMetadata[], flatFieldMetadata: FlatFieldMetadata[],
): Promise<ObjectRecord> { ): Promise<ObjectRecord> {
const richTextV2Fields = flatFieldMetadata.filter( const richTextFields = flatFieldMetadata.filter(
(field) => field.type === FieldMetadataType.RICH_TEXT_V2, (field) => field.type === FieldMetadataType.RICH_TEXT,
); );
if (richTextV2Fields.length === 0) { if (richTextFields.length === 0) {
return record; return record;
} }
for (const field of richTextV2Fields) { for (const field of richTextFields) {
const fieldValue = record[field.name]; const fieldValue = record[field.name];
const blocknoteJson = fieldValue?.blocknote; const blocknoteJson = fieldValue?.blocknote;
@@ -1,7 +1,7 @@
import { GraphQLInputObjectType, GraphQLString } from 'graphql'; import { GraphQLInputObjectType, GraphQLString } from 'graphql';
const richTextV2LeafFilter = new GraphQLInputObjectType({ const richTextLeafFilter = new GraphQLInputObjectType({
name: 'RichTextV2LeafFilter', name: 'RichTextLeafFilter',
fields: { fields: {
ilike: { ilike: {
type: GraphQLString, type: GraphQLString,
@@ -10,10 +10,10 @@ const richTextV2LeafFilter = new GraphQLInputObjectType({
}, },
}); });
export const RichTextV2FilterType = new GraphQLInputObjectType({ export const RichTextFilterType = new GraphQLInputObjectType({
name: 'RichTextV2Filter', name: 'RichTextFilter',
fields: { fields: {
blocknote: { type: richTextV2LeafFilter }, blocknote: { type: richTextLeafFilter },
markdown: { type: richTextV2LeafFilter }, markdown: { type: richTextLeafFilter },
}, },
}); });
@@ -33,7 +33,7 @@ import {
} from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/input'; } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/input';
import { FilesInputType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/input/files.input-type'; import { FilesInputType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/input/files.input-type';
import { MultiSelectFilterType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/input/multi-select-filter.input-type'; import { MultiSelectFilterType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/input/multi-select-filter.input-type';
import { RichTextV2FilterType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/input/rich-text.input-type'; import { RichTextFilterType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/input/rich-text.input-type';
import { SelectFilterType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/input/select-filter.input-type'; import { SelectFilterType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/input/select-filter.input-type';
import { TSVectorFilterType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/input/ts-vector-filter.input-type'; import { TSVectorFilterType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/input/ts-vector-filter.input-type';
import { UUIDFilterType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/input/uuid-filter.input-type'; import { UUIDFilterType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/input/uuid-filter.input-type';
@@ -178,7 +178,7 @@ export class TypeMapperService {
[FieldMetadataType.POSITION, FloatFilterType], [FieldMetadataType.POSITION, FloatFilterType],
[FieldMetadataType.FILES, RawJsonFilterType], [FieldMetadataType.FILES, RawJsonFilterType],
[FieldMetadataType.RAW_JSON, RawJsonFilterType], [FieldMetadataType.RAW_JSON, RawJsonFilterType],
[FieldMetadataType.RICH_TEXT_V2, RichTextV2FilterType], [FieldMetadataType.RICH_TEXT, RichTextFilterType],
[FieldMetadataType.ARRAY, ArrayFilterType], [FieldMetadataType.ARRAY, ArrayFilterType],
[FieldMetadataType.MULTI_SELECT, MultiSelectFilterType], [FieldMetadataType.MULTI_SELECT, MultiSelectFilterType],
[FieldMetadataType.SELECT, SelectFilterType], [FieldMetadataType.SELECT, SelectFilterType],
@@ -124,7 +124,7 @@ export const generateRandomFieldValue = ({
return {}; return {};
} }
case FieldMetadataType.RICH_TEXT_V2: { case FieldMetadataType.RICH_TEXT: {
return ''; return '';
} }
@@ -28,7 +28,7 @@ export const generateFieldFilterZodSchema = (
.describe(`Filter by ${field.name} (UUID field)`); .describe(`Filter by ${field.name} (UUID field)`);
case FieldMetadataType.TEXT: case FieldMetadataType.TEXT:
case FieldMetadataType.RICH_TEXT_V2: case FieldMetadataType.RICH_TEXT:
return z return z
.object({ .object({
eq: z.string().optional().describe('Equals'), eq: z.string().optional().describe('Equals'),
@@ -238,7 +238,7 @@ export const generateRecordPropertiesZodSchema = (
}); });
break; break;
case FieldMetadataType.RICH_TEXT_V2: case FieldMetadataType.RICH_TEXT:
fieldSchema = z.object({ fieldSchema = z.object({
markdown: z.string().optional(), markdown: z.string().optional(),
blocknote: z.string().optional(), blocknote: z.string().optional(),
@@ -10,7 +10,7 @@ import { isDefined } from 'twenty-shared/utils';
import { transformEmailsValue } from 'src/engine/core-modules/record-transformer/utils/transform-emails-value.util'; import { transformEmailsValue } from 'src/engine/core-modules/record-transformer/utils/transform-emails-value.util';
import { transformLinksValue } from 'src/engine/core-modules/record-transformer/utils/transform-links-value.util'; import { transformLinksValue } from 'src/engine/core-modules/record-transformer/utils/transform-links-value.util';
import { transformPhonesValue } from 'src/engine/core-modules/record-transformer/utils/transform-phones-value.util'; import { transformPhonesValue } from 'src/engine/core-modules/record-transformer/utils/transform-phones-value.util';
import { transformRichTextV2Value } from 'src/engine/core-modules/record-transformer/utils/transform-rich-text-v2.util'; import { transformRichTextValue } from 'src/engine/core-modules/record-transformer/utils/transform-rich-text.util';
import { FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type'; import { FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type';
import { findFlatEntityByIdInFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps.util'; import { findFlatEntityByIdInFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps.util';
import { FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type'; import { FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
@@ -78,8 +78,8 @@ export class RecordInputTransformerService {
return value || null; return value || null;
case FieldMetadataType.NUMBER: case FieldMetadataType.NUMBER:
return value === null ? null : Number(value); return value === null ? null : Number(value);
case FieldMetadataType.RICH_TEXT_V2: case FieldMetadataType.RICH_TEXT:
return await transformRichTextV2Value(value); return await transformRichTextValue(value);
case FieldMetadataType.LINKS: case FieldMetadataType.LINKS:
return transformLinksValue(value); return transformLinksValue(value);
case FieldMetadataType.EMAILS: case FieldMetadataType.EMAILS:
@@ -1,7 +1,7 @@
import { isNonEmptyString } from '@sniptt/guards'; import { isNonEmptyString } from '@sniptt/guards';
import { import {
type RichTextV2Metadata, type RichTextMetadata,
richTextV2ValueSchema, richTextValueSchema,
} from 'twenty-shared/types'; } from 'twenty-shared/types';
import { isDefined } from 'twenty-shared/utils'; import { isDefined } from 'twenty-shared/utils';
@@ -29,12 +29,12 @@ const getServerBlockNoteEditor = async (): Promise<ServerBlockNoteEditor> => {
return editor; return editor;
}; };
export const transformRichTextV2Value = async ( export const transformRichTextValue = async (
// oxlint-disable-next-line @typescripttypescript/no-explicit-any // oxlint-disable-next-line @typescripttypescript/no-explicit-any
richTextValue: any, richTextValue: any,
): Promise<RichTextV2Metadata> => { ): Promise<RichTextMetadata> => {
const parsedValue = isNonEmptyString(richTextValue) const parsedValue = isNonEmptyString(richTextValue)
? richTextV2ValueSchema.parse(richTextValue) ? richTextValueSchema.parse(richTextValue)
: richTextValue; : richTextValue;
const serverBlockNoteEditor = await getServerBlockNoteEditor(); const serverBlockNoteEditor = await getServerBlockNoteEditor();
@@ -7,7 +7,7 @@ const compositeFieldTypes = [
FieldMetadataType.LINKS, FieldMetadataType.LINKS,
FieldMetadataType.EMAILS, FieldMetadataType.EMAILS,
FieldMetadataType.PHONES, FieldMetadataType.PHONES,
FieldMetadataType.RICH_TEXT_V2, FieldMetadataType.RICH_TEXT,
FieldMetadataType.ACTOR, FieldMetadataType.ACTOR,
] as const; ] as const;
@@ -6,7 +6,7 @@ describe('computeCompositeColumnName', () => {
it('should compute composite column name for rich text v2 field', () => { it('should compute composite column name for rich text v2 field', () => {
const fieldMetadata = { const fieldMetadata = {
name: 'bodyV2', name: 'bodyV2',
type: FieldMetadataType.RICH_TEXT_V2, type: FieldMetadataType.RICH_TEXT,
}; };
const property = { const property = {
@@ -9,7 +9,7 @@ export const isCompositeFieldMetadataType = (
| FieldMetadataType.ACTOR | FieldMetadataType.ACTOR
| FieldMetadataType.EMAILS | FieldMetadataType.EMAILS
| FieldMetadataType.PHONES | FieldMetadataType.PHONES
| FieldMetadataType.RICH_TEXT_V2 => { | FieldMetadataType.RICH_TEXT => {
return [ return [
FieldMetadataType.CURRENCY, FieldMetadataType.CURRENCY,
FieldMetadataType.FULL_NAME, FieldMetadataType.FULL_NAME,
@@ -18,6 +18,6 @@ export const isCompositeFieldMetadataType = (
FieldMetadataType.ACTOR, FieldMetadataType.ACTOR,
FieldMetadataType.EMAILS, FieldMetadataType.EMAILS,
FieldMetadataType.PHONES, FieldMetadataType.PHONES,
FieldMetadataType.RICH_TEXT_V2, FieldMetadataType.RICH_TEXT,
].includes(type); ].includes(type);
}; };
@@ -55,7 +55,7 @@ export const NOTE_FLAT_FIELDS_MOCK = {
bodyV2: getFlatFieldMetadataMock({ bodyV2: getFlatFieldMetadataMock({
id: 'acb2973c-7e46-4296-ac79-8b8d163b6d85', id: 'acb2973c-7e46-4296-ac79-8b8d163b6d85',
objectMetadataId: '1253e3e5-5b00-4a34-93b8-02f3dc6e2b7c', objectMetadataId: '1253e3e5-5b00-4a34-93b8-02f3dc6e2b7c',
type: FieldMetadataType.RICH_TEXT_V2, type: FieldMetadataType.RICH_TEXT,
name: 'bodyV2', name: 'bodyV2',
label: 'Body', label: 'Body',
defaultValue: null, defaultValue: null,
@@ -59,7 +59,7 @@ export const TASK_FLAT_FIELDS_MOCK = {
bodyV2: getFlatFieldMetadataMock({ bodyV2: getFlatFieldMetadataMock({
id: '161570ed-7fcd-4172-a5d1-c773cd9afe76', id: '161570ed-7fcd-4172-a5d1-c773cd9afe76',
objectMetadataId: '3186920d-2a15-4b5f-96c7-2bf6567024b0', objectMetadataId: '3186920d-2a15-4b5f-96c7-2bf6567024b0',
type: FieldMetadataType.RICH_TEXT_V2, type: FieldMetadataType.RICH_TEXT,
name: 'bodyV2', name: 'bodyV2',
label: 'Body', label: 'Body',
defaultValue: null, defaultValue: null,
@@ -71,7 +71,7 @@ export class FlatFieldMetadataTypeValidatorService {
PHONES: DEFAULT_NO_VALIDATION, PHONES: DEFAULT_NO_VALIDATION,
POSITION: validatePositionFlatFieldMetadata, POSITION: validatePositionFlatFieldMetadata,
RAW_JSON: DEFAULT_NO_VALIDATION, RAW_JSON: DEFAULT_NO_VALIDATION,
RICH_TEXT_V2: DEFAULT_NO_VALIDATION, RICH_TEXT: DEFAULT_NO_VALIDATION,
TEXT: DEFAULT_NO_VALIDATION, TEXT: DEFAULT_NO_VALIDATION,
TS_VECTOR: validateTsVectorFlatFieldMetadata, TS_VECTOR: validateTsVectorFlatFieldMetadata,
UUID: DEFAULT_NO_VALIDATION, UUID: DEFAULT_NO_VALIDATION,
@@ -105,7 +105,7 @@ const _assertion: Record<string, AbstractFlatFieldMetadata> = {
// JSON/Array types // JSON/Array types
rawJson: {} as FlatFieldMetadata<FieldMetadataType.RAW_JSON>, rawJson: {} as FlatFieldMetadata<FieldMetadataType.RAW_JSON>,
array: {} as FlatFieldMetadata<FieldMetadataType.ARRAY>, array: {} as FlatFieldMetadata<FieldMetadataType.ARRAY>,
richTextV2: {} as FlatFieldMetadata<FieldMetadataType.RICH_TEXT_V2>, richText: {} as FlatFieldMetadata<FieldMetadataType.RICH_TEXT>,
// Relation types // Relation types
relation: {} as FlatFieldMetadata<FieldMetadataType.RELATION>, relation: {} as FlatFieldMetadata<FieldMetadataType.RELATION>,
@@ -183,7 +183,7 @@ export const fromCreateFieldInputToFlatFieldMetadatasToCreate = async ({
case FieldMetadataType.POSITION: case FieldMetadataType.POSITION:
case FieldMetadataType.ADDRESS: case FieldMetadataType.ADDRESS:
case FieldMetadataType.RAW_JSON: case FieldMetadataType.RAW_JSON:
case FieldMetadataType.RICH_TEXT_V2: case FieldMetadataType.RICH_TEXT:
case FieldMetadataType.ACTOR: case FieldMetadataType.ACTOR:
case FieldMetadataType.ARRAY: { case FieldMetadataType.ARRAY: {
const indexMetadatas: UniversalFlatIndexMetadata[] = []; const indexMetadatas: UniversalFlatIndexMetadata[] = [];
@@ -1,10 +1,10 @@
import { Field, ObjectType } from '@nestjs/graphql'; import { Field, ObjectType } from '@nestjs/graphql';
import { IsOptional, IsString } from 'class-validator'; import { IsOptional, IsString } from 'class-validator';
import { type RichTextV2Metadata } from 'twenty-shared/types'; import { type RichTextMetadata } from 'twenty-shared/types';
@ObjectType('RichTextV2Body') @ObjectType('RichTextBody')
export class RichTextV2BodyDTO implements RichTextV2Metadata { export class RichTextBodyDTO implements RichTextMetadata {
@Field(() => String, { nullable: true }) @Field(() => String, { nullable: true })
@IsString() @IsString()
@IsOptional() @IsOptional()
@@ -4,7 +4,7 @@ import { Type } from 'class-transformer';
import { IsIn, IsNotEmpty, ValidateNested } from 'class-validator'; import { IsIn, IsNotEmpty, ValidateNested } from 'class-validator';
import { type StandaloneRichTextConfiguration } from 'twenty-shared/types'; import { type StandaloneRichTextConfiguration } from 'twenty-shared/types';
import { RichTextV2BodyDTO } from 'src/engine/metadata-modules/page-layout-widget/dtos/rich-text-v2-body.dto'; import { RichTextBodyDTO } from 'src/engine/metadata-modules/page-layout-widget/dtos/rich-text-body.dto';
import { WidgetConfigurationType } from 'src/engine/metadata-modules/page-layout-widget/enums/widget-configuration-type.type'; import { WidgetConfigurationType } from 'src/engine/metadata-modules/page-layout-widget/enums/widget-configuration-type.type';
@ObjectType('StandaloneRichTextConfiguration') @ObjectType('StandaloneRichTextConfiguration')
@@ -16,9 +16,9 @@ export class StandaloneRichTextConfigurationDTO
@IsNotEmpty() @IsNotEmpty()
configurationType: WidgetConfigurationType.STANDALONE_RICH_TEXT; configurationType: WidgetConfigurationType.STANDALONE_RICH_TEXT;
@Field(() => RichTextV2BodyDTO) @Field(() => RichTextBodyDTO)
@ValidateNested() @ValidateNested()
@Type(() => RichTextV2BodyDTO) @Type(() => RichTextBodyDTO)
@IsNotEmpty() @IsNotEmpty()
body: RichTextV2BodyDTO; body: RichTextBodyDTO;
} }
@@ -3,7 +3,7 @@ import { Injectable } from '@nestjs/common';
import { isDefined } from 'twenty-shared/utils'; import { isDefined } from 'twenty-shared/utils';
import { ApplicationService } from 'src/engine/core-modules/application/application.service'; import { ApplicationService } from 'src/engine/core-modules/application/application.service';
import { transformRichTextV2Value } from 'src/engine/core-modules/record-transformer/utils/transform-rich-text-v2.util'; import { transformRichTextValue } from 'src/engine/core-modules/record-transformer/utils/transform-rich-text.util';
import { WorkspaceManyOrAllFlatEntityMapsCacheService } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.service'; import { WorkspaceManyOrAllFlatEntityMapsCacheService } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.service';
import { findFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps-or-throw.util'; import { findFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps-or-throw.util';
import { findFlatEntityByIdInFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps.util'; import { findFlatEntityByIdInFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps.util';
@@ -115,7 +115,7 @@ export class PageLayoutWidgetService {
try { try {
return { return {
...configuration, ...configuration,
body: await transformRichTextV2Value(configuration.body), body: await transformRichTextValue(configuration.body),
}; };
} catch { } catch {
return configuration; return configuration;
@@ -245,6 +245,7 @@ describe('WorkspaceEntityManager', () => {
IS_MARKETPLACE_ENABLED: false, IS_MARKETPLACE_ENABLED: false,
IS_DATE_TIME_WHOLE_DAY_FILTER_ENABLED: false, IS_DATE_TIME_WHOLE_DAY_FILTER_ENABLED: false,
IS_DRAFT_EMAIL_ENABLED: false, IS_DRAFT_EMAIL_ENABLED: false,
IS_RICH_TEXT_V1_MIGRATED: false,
}, },
userWorkspaceRoleMap: {}, userWorkspaceRoleMap: {},
eventEmitterService: { eventEmitterService: {
@@ -36,7 +36,7 @@ export const getSubfieldsForAggregateOperation = (
'primaryPhoneCountryCode', 'primaryPhoneCountryCode',
'primaryPhoneCallingCode', 'primaryPhoneCallingCode',
]; ];
case FieldMetadataType.RICH_TEXT_V2: case FieldMetadataType.RICH_TEXT:
return ['blocknote', 'markdown']; return ['blocknote', 'markdown'];
default: default:
throw new Error(`Unsupported composite field type: ${fieldType}`); throw new Error(`Unsupported composite field type: ${fieldType}`);
@@ -23,7 +23,7 @@ import {
type RatingFilter, type RatingFilter,
type RawJsonFilter, type RawJsonFilter,
type RecordGqlOperationFilter, type RecordGqlOperationFilter,
type RichTextV2Filter, type RichTextFilter,
type SelectFilter, type SelectFilter,
type StringFilter, type StringFilter,
type TSVectorFilter, type TSVectorFilter,
@@ -40,7 +40,7 @@ import {
isMatchingMultiSelectFilter, isMatchingMultiSelectFilter,
isMatchingRatingFilter, isMatchingRatingFilter,
isMatchingRawJsonFilter, isMatchingRawJsonFilter,
isMatchingRichTextV2Filter, isMatchingRichTextFilter,
isMatchingSelectFilter, isMatchingSelectFilter,
isMatchingStringFilter, isMatchingStringFilter,
isMatchingTSVectorFilter, isMatchingTSVectorFilter,
@@ -243,9 +243,9 @@ export const isRecordMatchingRLSRowLevelPermissionPredicate = ({
value: recordFieldValue, value: recordFieldValue,
}); });
} }
case FieldMetadataType.RICH_TEXT_V2: { case FieldMetadataType.RICH_TEXT: {
return isMatchingRichTextV2Filter({ return isMatchingRichTextFilter({
richTextV2Filter: filterValue as RichTextV2Filter, richTextFilter: filterValue as RichTextFilter,
value: recordFieldValue, value: recordFieldValue,
}); });
} }
@@ -324,7 +324,7 @@ export const convertObjectMetadataToSchemaProperties = ({
type: 'object', type: 'object',
}; };
break; break;
case FieldMetadataType.RICH_TEXT_V2: case FieldMetadataType.RICH_TEXT:
itemProperty = { itemProperty = {
type: 'object', type: 'object',
properties: { properties: {
@@ -151,7 +151,7 @@ export const buildNoteStandardFlatFieldMetadatas = ({
workspaceId, workspaceId,
context: { context: {
fieldName: 'bodyV2', fieldName: 'bodyV2',
type: FieldMetadataType.RICH_TEXT_V2, type: FieldMetadataType.RICH_TEXT,
label: 'Body', label: 'Body',
description: 'Note body', description: 'Note body',
icon: 'IconFilePencil', icon: 'IconFilePencil',
@@ -152,7 +152,7 @@ export const buildTaskStandardFlatFieldMetadatas = ({
workspaceId, workspaceId,
context: { context: {
fieldName: 'bodyV2', fieldName: 'bodyV2',
type: FieldMetadataType.RICH_TEXT_V2, type: FieldMetadataType.RICH_TEXT,
label: 'Body', label: 'Body',
description: 'Task body', description: 'Task body',
icon: 'IconFilePencil', icon: 'IconFilePencil',
@@ -367,7 +367,7 @@ You help users manage their workspace data model by creating, updating, and orga
- **CURRENCY**: Monetary values - **CURRENCY**: Monetary values
- **RATING**: Star ratings - **RATING**: Star ratings
- **RELATION**: Links to other objects - **RELATION**: Links to other objects
- **RICH_TEXT_V2**: Formatted rich text content - **RICH_TEXT**: Formatted rich text content
## Best Practices ## Best Practices
@@ -63,7 +63,7 @@ describe('getTsVectorColumnExpressionFromFields', () => {
it('should handle rich text v2 fields', () => { it('should handle rich text v2 fields', () => {
const fields = [ const fields = [
{ name: 'bodyV2', type: FieldMetadataType.RICH_TEXT_V2 }, { name: 'bodyV2', type: FieldMetadataType.RICH_TEXT },
] as FieldTypeAndNameMetadata[]; ] as FieldTypeAndNameMetadata[];
const result = getTsVectorColumnExpressionFromFields(fields); const result = getTsVectorColumnExpressionFromFields(fields);
@@ -6,7 +6,7 @@ const SEARCHABLE_FIELD_TYPES = [
FieldMetadataType.ADDRESS, FieldMetadataType.ADDRESS,
FieldMetadataType.LINKS, FieldMetadataType.LINKS,
FieldMetadataType.PHONES, FieldMetadataType.PHONES,
FieldMetadataType.RICH_TEXT_V2, FieldMetadataType.RICH_TEXT,
FieldMetadataType.UUID, FieldMetadataType.UUID,
] as const; ] as const;
@@ -9,7 +9,7 @@ export const isSearchableSubfield = (
} }
switch (compositeFieldMetadataType) { switch (compositeFieldMetadataType) {
case FieldMetadataType.RICH_TEXT_V2: case FieldMetadataType.RICH_TEXT:
return ['markdown'].includes(subFieldName); return ['markdown'].includes(subFieldName);
case FieldMetadataType.PHONES: case FieldMetadataType.PHONES:
return ['primaryPhoneNumber', 'primaryPhoneCallingCode'].includes( return ['primaryPhoneNumber', 'primaryPhoneCallingCode'].includes(
@@ -1,11 +1,12 @@
import { FieldMetadataType } from 'twenty-shared/types'; import { FieldMetadataType } from 'twenty-shared/types';
// 'RICH_TEXT' kept as a raw string for pre-upgrade workspaces that haven't // Raw strings kept for pre-upgrade workspaces that haven't yet run
// yet run the 1.20 migrate-rich-text-to-text command. // the 1.20 migrate-rich-text-to-text / rename-rich-text-v2 commands.
export const isTextColumnType = (type: FieldMetadataType) => { export const isTextColumnType = (type: FieldMetadataType) => {
return ( return (
type === FieldMetadataType.TEXT || type === FieldMetadataType.TEXT ||
type === FieldMetadataType.ARRAY || type === FieldMetadataType.ARRAY ||
(type as string) === 'RICH_TEXT_V2' ||
(type as string) === 'RICH_TEXT' (type as string) === 'RICH_TEXT'
); );
}; };
@@ -1,7 +1,7 @@
import { import {
type ActorMetadata, type ActorMetadata,
FieldMetadataType, FieldMetadataType,
type RichTextV2Metadata, type RichTextMetadata,
} from 'twenty-shared/types'; } from 'twenty-shared/types';
import { BaseWorkspaceEntity } from 'src/engine/twenty-orm/base.workspace-entity'; import { BaseWorkspaceEntity } from 'src/engine/twenty-orm/base.workspace-entity';
@@ -17,13 +17,13 @@ const BODY_V2_FIELD_NAME = 'bodyV2';
export const SEARCH_FIELDS_FOR_NOTES: FieldTypeAndNameMetadata[] = [ export const SEARCH_FIELDS_FOR_NOTES: FieldTypeAndNameMetadata[] = [
{ name: TITLE_FIELD_NAME, type: FieldMetadataType.TEXT }, { name: TITLE_FIELD_NAME, type: FieldMetadataType.TEXT },
{ name: BODY_V2_FIELD_NAME, type: FieldMetadataType.RICH_TEXT_V2 }, { name: BODY_V2_FIELD_NAME, type: FieldMetadataType.RICH_TEXT },
]; ];
export class NoteWorkspaceEntity extends BaseWorkspaceEntity { export class NoteWorkspaceEntity extends BaseWorkspaceEntity {
position: number; position: number;
title: string; title: string;
bodyV2: RichTextV2Metadata | null; bodyV2: RichTextMetadata | null;
createdBy: ActorMetadata; createdBy: ActorMetadata;
updatedBy: ActorMetadata; updatedBy: ActorMetadata;
noteTargets: EntityRelation<NoteTargetWorkspaceEntity[]>; noteTargets: EntityRelation<NoteTargetWorkspaceEntity[]>;

Some files were not shown because too many files have changed in this diff Show More