Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactored and improved seeds #8695

Open
wants to merge 29 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
5077f95
OK
lucasbordeau Nov 21, 2024
9bfd3aa
Ok
lucasbordeau Nov 22, 2024
afa0c9c
Fix
lucasbordeau Nov 22, 2024
50c3df5
Fix seeds
lucasbordeau Nov 22, 2024
047f44a
Fix
lucasbordeau Nov 25, 2024
dad62c4
wip
lucasbordeau Nov 25, 2024
5c8b606
Merge branch 'main' into feat/new-seeds
lucasbordeau Nov 26, 2024
6c3426c
Refactor naming
lucasbordeau Nov 26, 2024
253da19
Minor fixes
lucasbordeau Nov 26, 2024
4bfbb0b
Fix readonly for new rich text field
lucasbordeau Nov 26, 2024
214d0d8
Fixmin height
lucasbordeau Nov 26, 2024
8ae3386
Fix
lucasbordeau Nov 26, 2024
3fce159
Fix
lucasbordeau Nov 26, 2024
1247412
Merge branch 'main' into feat/new-seeds
lucasbordeau Nov 28, 2024
61ed304
Fixed circular dependency problem
lucasbordeau Nov 28, 2024
61ef4eb
Merge branch 'main' into feat/new-seeds
lucasbordeau Dec 17, 2024
c4e121a
WIP
lucasbordeau Dec 18, 2024
a29acc0
Fixes
lucasbordeau Dec 18, 2024
b1433d0
Merge branch 'main' into feat/new-seeds
lucasbordeau Dec 18, 2024
894a794
Removed unused field types
lucasbordeau Dec 18, 2024
378e390
Removed fix from main
lucasbordeau Dec 18, 2024
4fc18fb
Put back changes from main
lucasbordeau Dec 18, 2024
7bfcb47
Fixed rich text editor field input
lucasbordeau Dec 18, 2024
59a48b2
Merge branch 'main' into feat/new-seeds
lucasbordeau Dec 19, 2024
1f53d0f
Put back seed dev for now
lucasbordeau Dec 19, 2024
7da7f1a
Use dropdown focus id for block editor click outside conflicts
lucasbordeau Dec 19, 2024
5d9afc2
Fixed
lucasbordeau Dec 19, 2024
4a210bc
Fix lint
lucasbordeau Dec 19, 2024
59478a0
Merge branch 'main' into feat/new-seeds
lucasbordeau Dec 20, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,7 @@
"nx",
"run",
"twenty-server:command",
"my-command",
"--my-parameter value",
"workspace:seed:demo",
lucasbordeau marked this conversation as resolved.
Show resolved Hide resolved
],
"outputCapture": "std",
"internalConsoleOptions": "openOnSessionStart",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import { isFieldRelationToOneObject } from '@/object-record/record-field/types/g
import { getScopeIdFromComponentId } from '@/ui/utilities/recoil-scope/utils/getScopeIdFromComponentId';

import { ArrayFieldInput } from '@/object-record/record-field/meta-types/input/components/ArrayFieldInput';
import { RichTextFieldInput } from '@/object-record/record-field/meta-types/input/components/RichTextFieldInput';
import { isFieldAddress } from '@/object-record/record-field/types/guards/isFieldAddress';
import { isFieldArray } from '@/object-record/record-field/types/guards/isFieldArray';
import { isFieldBoolean } from '@/object-record/record-field/types/guards/isFieldBoolean';
Expand All @@ -31,7 +30,6 @@ import { isFieldMultiSelect } from '@/object-record/record-field/types/guards/is
import { isFieldNumber } from '@/object-record/record-field/types/guards/isFieldNumber';
import { isFieldRating } from '@/object-record/record-field/types/guards/isFieldRating';
import { isFieldRawJson } from '@/object-record/record-field/types/guards/isFieldRawJson';
import { isFieldRichText } from '@/object-record/record-field/types/guards/isFieldRichText';
import { isFieldSelect } from '@/object-record/record-field/types/guards/isFieldSelect';
import { FieldContext } from '../contexts/FieldContext';
import { BooleanFieldInput } from '../meta-types/input/components/BooleanFieldInput';
Expand Down Expand Up @@ -167,8 +165,6 @@ export const FieldInput = ({
onTab={onTab}
onShiftTab={onShiftTab}
/>
) : isFieldRichText(fieldDefinition) ? (
<RichTextFieldInput />
) : isFieldArray(fieldDefinition) ? (
<ArrayFieldInput
onCancel={onCancel}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ import { RecordForSelect } from '@/object-record/relation-picker/types/RecordFor

import { isFieldArray } from '@/object-record/record-field/types/guards/isFieldArray';
import { isFieldArrayValue } from '@/object-record/record-field/types/guards/isFieldArrayValue';
import { isFieldRichText } from '@/object-record/record-field/types/guards/isFieldRichText';
import { isFieldRichTextValue } from '@/object-record/record-field/types/guards/isFieldRichTextValue';
import { FieldContext } from '../contexts/FieldContext';
import { isFieldBoolean } from '../types/guards/isFieldBoolean';
import { isFieldBooleanValue } from '../types/guards/isFieldBooleanValue';
Expand Down Expand Up @@ -111,6 +113,10 @@ export const usePersistField = () => {
isFieldRawJson(fieldDefinition) &&
isFieldRawJsonValue(valueToPersist);

const fieldIsRichText =
isFieldRichText(fieldDefinition) &&
isFieldRichTextValue(valueToPersist);

const fieldIsArray =
isFieldArray(fieldDefinition) && isFieldArrayValue(valueToPersist);

Expand All @@ -131,7 +137,8 @@ export const usePersistField = () => {
fieldIsMultiSelect ||
fieldIsAddress ||
fieldIsRawJson ||
fieldIsArray;
fieldIsArray ||
fieldIsRichText;

if (isValuePersistable) {
const fieldName = fieldDefinition.metadata.fieldName;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { useTextFieldDisplay } from '@/object-record/record-field/meta-types/hooks/useTextFieldDisplay';
import { useRichTextFieldDisplay } from '@/object-record/record-field/meta-types/hooks/useRichTextFieldDisplay';
import { getFirstNonEmptyLineOfRichText } from '@/ui/input/editor/utils/getFirstNonEmptyLineOfRichText';
import { PartialBlock } from '@blocknote/core';

export const RichTextFieldDisplay = () => {
const { fieldValue } = useTextFieldDisplay();
const parsedField =
fieldValue === '' ? null : (JSON.parse(fieldValue) as PartialBlock[]);
const { fieldValue } = useRichTextFieldDisplay();

return <>{getFirstNonEmptyLineOfRichText(parsedField)}</>;
return (
<div>
<span>{getFirstNonEmptyLineOfRichText(fieldValue)}</span>
</div>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { useContext } from 'react';
import { useRecoilState, useRecoilValue } from 'recoil';

import { useRecordFieldInput } from '@/object-record/record-field/hooks/useRecordFieldInput';
import { FieldRichTextValue } from '@/object-record/record-field/types/FieldMetadata';
import { recordStoreFamilySelector } from '@/object-record/record-store/states/selectors/recordStoreFamilySelector';
import { FieldMetadataType } from '~/generated-metadata/graphql';

import { usePersistField } from '@/object-record/record-field/hooks/usePersistField';
import { isFieldRichText } from '@/object-record/record-field/types/guards/isFieldRichText';
import { isFieldRichTextValue } from '@/object-record/record-field/types/guards/isFieldRichTextValue';
import { PartialBlock } from '@blocknote/core';
import { isNonEmptyString } from '@sniptt/guards';
import { FieldContext } from '../../contexts/FieldContext';
import { assertFieldMetadata } from '../../types/guards/assertFieldMetadata';

export const useRichTextField = () => {
const { recordId, fieldDefinition, hotkeyScope, maxWidth } =
useContext(FieldContext);

assertFieldMetadata(
FieldMetadataType.RichText,
isFieldRichText,
fieldDefinition,
);

const fieldName = fieldDefinition.metadata.fieldName;

const [fieldValue, setFieldValue] = useRecoilState<FieldRichTextValue>(
recordStoreFamilySelector({
recordId,
fieldName: fieldName,
}),
);
const fieldRichTextValue = isFieldRichTextValue(fieldValue) ? fieldValue : '';
lucasbordeau marked this conversation as resolved.
Show resolved Hide resolved

const { setDraftValue, getDraftValueSelector } =
useRecordFieldInput<FieldRichTextValue>(`${recordId}-${fieldName}`);

const draftValue = useRecoilValue(getDraftValueSelector());

const draftValueParsed: PartialBlock[] = isNonEmptyString(draftValue)
? JSON.parse(draftValue)
: draftValue;
lucasbordeau marked this conversation as resolved.
Show resolved Hide resolved

const persistField = usePersistField();

const persistRichTextField = (nextValue: PartialBlock[]) => {
if (!nextValue) {
persistField(null);
} else {
const parsedValueToPersist = JSON.stringify(nextValue);

persistField(parsedValueToPersist);
}
};

return {
draftValue: draftValueParsed,
setDraftValue,
maxWidth,
fieldDefinition,
fieldValue: fieldRichTextValue,
setFieldValue,
hotkeyScope,
persistRichTextField,
};
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { useContext } from 'react';

import { useRecordFieldValue } from '@/object-record/record-store/contexts/RecordFieldValueSelectorContext';

import { FieldRichTextValue } from '@/object-record/record-field/types/FieldMetadata';
import { assertFieldMetadata } from '@/object-record/record-field/types/guards/assertFieldMetadata';
import { isFieldRichText } from '@/object-record/record-field/types/guards/isFieldRichText';
import { PartialBlock } from '@blocknote/core';
import { FieldMetadataType } from '~/generated-metadata/graphql';
import { parseJson } from '~/utils/parseJson';
import { FieldContext } from '../../contexts/FieldContext';

export const useRichTextFieldDisplay = () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: hook uses FieldContext but doesn't handle case where context is undefined

const { recordId, fieldDefinition, hotkeyScope } = useContext(FieldContext);

assertFieldMetadata(
FieldMetadataType.RichText,
isFieldRichText,
fieldDefinition,
);

const fieldName = fieldDefinition.metadata.fieldName;

const fieldValue = useRecordFieldValue<FieldRichTextValue | undefined>(
recordId,
fieldName,
);

const fieldValueParsed = parseJson<PartialBlock[]>(fieldValue);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: parseJson could return undefined/null - need to handle this case explicitly to avoid runtime errors when using fieldValueParsed


return {
fieldDefinition,
fieldValue: fieldValueParsed,
hotkeyScope,
};
};
Original file line number Diff line number Diff line change
@@ -1,5 +1,59 @@
import { RichTextFieldDisplay } from '@/object-record/record-field/meta-types/display/components/RichTextFieldDisplay';
import { BLOCK_SCHEMA } from '@/activities/blocks/constants/Schema';
import { FieldContext } from '@/object-record/record-field/contexts/FieldContext';
import { useRichTextField } from '@/object-record/record-field/meta-types/hooks/useRichTextField';
import { FieldInputClickOutsideEvent } from '@/object-record/record-field/meta-types/input/components/DateTimeFieldInput';
import { useRegisterInputEvents } from '@/object-record/record-field/meta-types/input/hooks/useRegisterInputEvents';
import { BlockEditor } from '@/ui/input/editor/components/BlockEditor';
import { BlockEditorComponentInstanceContext } from '@/ui/input/editor/contexts/BlockEditorCompoponeInstanceContext';
import { PartialBlock } from '@blocknote/core';
import { useCreateBlockNote } from '@blocknote/react';
import styled from '@emotion/styled';

export const RichTextFieldInput = () => {
return <RichTextFieldDisplay />;
import { useContext, useRef } from 'react';

const StyledRichTextContainer = styled.div`
height: 400px;
width: 500px;

overflow: auto;
`;

export type RichTextFieldInputProps = {
onClickOutside?: FieldInputClickOutsideEvent;
};

export const RichTextFieldInput = ({
onClickOutside,
}: RichTextFieldInputProps) => {
const containerRef = useRef<HTMLDivElement>(null);
const { recordId } = useContext(FieldContext);
const { draftValue, hotkeyScope, persistRichTextField, fieldDefinition } =
useRichTextField();

const editor = useCreateBlockNote({
initialContent: draftValue,
domAttributes: { editor: { class: 'editor' } },
schema: BLOCK_SCHEMA,
});

lucasbordeau marked this conversation as resolved.
Show resolved Hide resolved
const handleClickOutside = (event: MouseEvent | TouchEvent) => {
onClickOutside?.(() => persistRichTextField(editor.document), event);
};

useRegisterInputEvents<PartialBlock[]>({
inputRef: containerRef,
inputValue: draftValue,
onClickOutside: handleClickOutside,
hotkeyScope,
});

return (
<StyledRichTextContainer ref={containerRef}>
<BlockEditorComponentInstanceContext.Provider
value={{ instanceId: `${recordId}-${fieldDefinition.fieldMetadataId}` }}
>
<BlockEditor editor={editor} />
</BlockEditorComponentInstanceContext.Provider>
</StyledRichTextContainer>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,16 @@ export type FieldDateMetadata = {
};
};

export type FieldNumberVariant = 'number' | 'percentage';

export type FieldNumberMetadata = {
objectMetadataNameSingular?: string;
fieldName: string;
placeHolder: string;
isPositive?: boolean;
settings?: {
decimals?: number;
type?: 'percentage' | 'number';
type?: FieldNumberVariant;
};
};

Expand Down Expand Up @@ -209,6 +211,7 @@ export type FieldMetadata =
| FieldActorMetadata
| FieldArrayMetadata
| FieldTsVectorMetadata;

export type FieldTextValue = string;
export type FieldUUidValue = string; // TODO: can we replace with a template literal type, or maybe overkill ?
export type FieldDateTimeValue = string | null;
Expand Down Expand Up @@ -255,7 +258,7 @@ export type FieldRelationValue<
export type Json = ZodHelperLiteral | { [key: string]: Json } | Json[];
export type FieldJsonValue = Record<string, Json> | Json[] | null;

export type FieldRichTextValue = Record<string, Json> | Json[] | null;
export type FieldRichTextValue = null | string;

export type FieldActorValue = {
source: string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {
FieldRatingMetadata,
FieldRawJsonMetadata,
FieldRelationMetadata,
FieldRichTextMetadata,
FieldSelectMetadata,
FieldTextMetadata,
FieldUuidMetadata,
Expand Down Expand Up @@ -68,7 +69,7 @@ type AssertFieldMetadataFunction = <
: E extends 'RAW_JSON'
? FieldRawJsonMetadata
: E extends 'RICH_TEXT'
? FieldTextMetadata
? FieldRichTextMetadata
: E extends 'ACTOR'
? FieldActorMetadata
: E extends 'ARRAY'
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { z } from 'zod';
import { FieldRichTextValue } from '../FieldMetadata';

export const richTextSchema: z.ZodType<FieldRichTextValue> = z.union([
z.null(), // Exclude literal values other than null
z.string(),
]);
Comment on lines +4 to +7
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: Schema only allows null or string, but rich text content is typically stored as JSON string of PartialBlock[] based on usage in useRichTextField.ts


export const isFieldRichTextValue = (
fieldValue: unknown,
): fieldValue is FieldRichTextValue =>
richTextSchema.safeParse(fieldValue).success;
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { RecordTableCellWrapper } from '@/object-record/record-table/record-tabl
import { RecordTableTd } from '@/object-record/record-table/record-table-cell/components/RecordTableTd';
import { visibleTableColumnsComponentSelector } from '@/object-record/record-table/states/selectors/visibleTableColumnsComponentSelector';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { isNonEmptyArray } from '~/utils/isNonEmptyArray';

export const RecordTableCellsVisible = () => {
const { isSelected } = useRecordTableRowContextOrThrow();
Expand All @@ -15,6 +16,10 @@ export const RecordTableCellsVisible = () => {
visibleTableColumnsComponentSelector,
);

if (!isNonEmptyArray(visibleTableColumns)) {
return null;
}

const tableColumnsAfterFirst = visibleTableColumns.slice(1);

return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export const SettingsOptionIconCustomizer = ({
<StyledIconCustomizer zoom={zoom} rotate={rotate}>
<Icon
size={theme.icon.size.lg}
color={theme.IllustrationIcon.color.grey}
color={theme.IllustrationIcon.color.gray}
stroke={theme.icon.stroke.md}
/>
</StyledIconCustomizer>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ export const SETTINGS_NON_COMPOSITE_FIELD_TYPE_CONFIGS: SettingsNonCompositeFiel
[FieldMetadataType.RichText]: {
label: 'Rich Text',
Icon: IllustrationIconSetting,
exampleValue: { key: 'value' },
exampleValue: "{ key: 'value' }",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: The RichText example value should be an object, not a string. This will cause type errors with FieldRichTextValue. Keep it as { key: 'value' } without quotes.

category: 'Basic',
} as const satisfies SettingsFieldTypeConfig<FieldRichTextValue>,
[FieldMetadataType.Array]: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,17 @@ interface BlockEditorProps {
onBlur?: () => void;
onPaste?: (event: ClipboardEvent) => void;
onChange?: () => void;
readonly?: boolean;
}

const StyledEditor = styled.div`
width: 100%;

& .editor {
background: ${({ theme }) => theme.background.primary};
font-size: 13px;
color: ${({ theme }) => theme.font.color.primary};
min-height: 400px;
}
& .editor [class^='_inlineContent']:before {
color: ${({ theme }) => theme.font.color.tertiary};
Expand Down Expand Up @@ -124,6 +127,7 @@ export const BlockEditor = ({
onBlur,
onChange,
onPaste,
readonly,
}: BlockEditorProps) => {
const theme = useTheme();
const blockNoteTheme = theme.name === 'light' ? 'light' : 'dark';
Expand Down Expand Up @@ -155,6 +159,7 @@ export const BlockEditor = ({
theme={blockNoteTheme}
slashMenu={false}
sideMenu={false}
editable={!readonly}
>
<CustomSideMenu editor={editor} />
lucasbordeau marked this conversation as resolved.
Show resolved Hide resolved
<SuggestionMenuController
Expand Down
Loading
Loading