Skip to content

Commit

Permalink
Fix variable dropdown (#8521)
Browse files Browse the repository at this point in the history
  • Loading branch information
martmull authored Nov 15, 2024
1 parent 54b28ff commit 9b2853b
Show file tree
Hide file tree
Showing 15 changed files with 189 additions and 79 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ const StyledEndIcon = styled.div`
`;

const StyledChildrenWrapper = styled.span`
overflow: hidden;
padding: 0 ${({ theme }) => theme.spacing(1)};
`;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,8 @@ import { forwardRef, InputHTMLAttributes } from 'react';
import { TEXT_INPUT_STYLE } from 'twenty-ui';

const StyledDropdownMenuSearchInputContainer = styled.div`
--vertical-padding: ${({ theme }) => theme.spacing(1)};
align-items: center;
--vertical-padding: ${({ theme }) => theme.spacing(2)};
display: flex;
flex-direction: row;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ const SearchVariablesDropdown = ({

return (
<Dropdown
dropdownMenuWidth={320}
dropdownId={dropdownId}
dropdownHotkeyScope={{
scope: dropdownId,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
import {
OverflowingTextWithTooltip,
IconChevronLeft,
MenuItemSelect,
useIcons,
} from 'twenty-ui';
import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader';
import { StepOutputSchema } from '@/workflow/search-variables/types/StepOutputSchema';
import { isObject } from '@sniptt/guards';
import {
OutputSchema,
StepOutputSchema,
} from '@/workflow/search-variables/types/StepOutputSchema';
import { useState } from 'react';
import { IconChevronLeft, MenuItemSelect } from 'twenty-ui';
import { DropdownMenuSearchInput } from '@/ui/layout/dropdown/components/DropdownMenuSearchInput';

type SearchVariablesDropdownStepSubItemProps = {
Expand All @@ -18,19 +25,20 @@ const SearchVariablesDropdownStepSubItem = ({
}: SearchVariablesDropdownStepSubItemProps) => {
const [currentPath, setCurrentPath] = useState<string[]>([]);
const [searchInputValue, setSearchInputValue] = useState('');
const { getIcon } = useIcons();

const getSelectedObject = () => {
const getSelectedObject = (): OutputSchema => {
let selected = step.outputSchema;
for (const key of currentPath) {
selected = selected[key];
selected = selected[key]?.value;
}
return selected;
};

const handleSelect = (key: string) => {
const selectedObject = getSelectedObject();

if (isObject(selectedObject[key])) {
if (!selectedObject[key]?.isLeaf) {
setCurrentPath([...currentPath, key]);
setSearchInputValue('');
} else {
Expand Down Expand Up @@ -59,7 +67,7 @@ const SearchVariablesDropdownStepSubItem = ({
return (
<>
<DropdownMenuHeader StartIcon={IconChevronLeft} onClick={goBack}>
{headerLabel}
<OverflowingTextWithTooltip text={headerLabel} />
</DropdownMenuHeader>
<DropdownMenuSearchInput
autoFocus
Expand All @@ -73,8 +81,8 @@ const SearchVariablesDropdownStepSubItem = ({
hovered={false}
onClick={() => handleSelect(key)}
text={key}
hasSubMenu={isObject(value)}
LeftIcon={undefined}
hasSubMenu={!value.isLeaf}
LeftIcon={value.icon ? getIcon(value.icon) : undefined}
/>
))}
</>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,22 @@
import { InputSchemaPropertyType } from '@/workflow/types/InputSchema';

type Leaf = {
isLeaf: true;
type?: InputSchemaPropertyType;
icon?: string;
value: any;
};

type Node = {
isLeaf: false;
icon?: string;
value: OutputSchema;
};

export type OutputSchema = Record<string, Leaf | Node>;

export type StepOutputSchema = {
id: string;
name: string;
outputSchema: Record<string, any>;
outputSchema: OutputSchema;
};
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
type InputSchemaPropertyType =
import { FieldMetadataType } from '~/generated/graphql';

export type InputSchemaPropertyType =
| 'string'
| 'number'
| 'boolean'
| 'object'
| 'array'
| 'unknown';
| 'unknown'
| FieldMetadataType;

type InputSchemaProperty = {
type: InputSchemaPropertyType;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { AuthWorkspace } from 'src/engine/decorators/auth/auth-workspace.decorat
import { UserAuthGuard } from 'src/engine/guards/user-auth.guard';
import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard';
import { WorkflowBuilderWorkspaceService } from 'src/modules/workflow/workflow-builder/workflow-builder.workspace-service';
import { OutputSchema } from 'src/modules/workflow/workflow-executor/workflow-actions/types/workflow-action-settings.type';
import { OutputSchema } from 'src/modules/workflow/workflow-builder/types/output-schema.type';

@Resolver()
@UseGuards(WorkspaceAuthGuard, UserAuthGuard)
Expand Down
10 changes: 9 additions & 1 deletion packages/twenty-server/src/engine/utils/generate-fake-value.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
export const generateFakeValue = (valueType: string): any => {
type FakeValueTypes =
| string
| number
| boolean
| Date
| FakeValueTypes[]
| { [key: string]: FakeValueTypes };

export const generateFakeValue = (valueType: string): FakeValueTypes => {
if (valueType === 'string') {
return 'generated-string-value';
} else if (valueType === 'number') {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
type InputSchemaPropertyType =
import { FieldMetadataType } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';

export type InputSchemaPropertyType =
| 'string'
| 'number'
| 'boolean'
| 'object'
| 'array'
| 'unknown';
| 'unknown'
| FieldMetadataType;

export type InputSchemaProperty = {
type: InputSchemaPropertyType;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { InputSchemaPropertyType } from 'src/modules/code-introspection/types/input-schema.type';

type Leaf = {
isLeaf: true;
icon?: string;
type?: InputSchemaPropertyType;
value: any;
};

type Node = {
isLeaf: false;
icon?: string;
value: OutputSchema;
};

export type OutputSchema = Record<string, Leaf | Node>;
Original file line number Diff line number Diff line change
@@ -1,76 +1,88 @@
import { v4 } from 'uuid';

import { DatabaseEventAction } from 'src/engine/api/graphql/graphql-query-runner/enums/database-event-action';
import { ObjectRecordCreateEvent } from 'src/engine/core-modules/event-emitter/types/object-record-create.event';
import { ObjectRecordDeleteEvent } from 'src/engine/core-modules/event-emitter/types/object-record-delete.event';
import { ObjectRecordDestroyEvent } from 'src/engine/core-modules/event-emitter/types/object-record-destroy.event';
import { ObjectRecordUpdateEvent } from 'src/engine/core-modules/event-emitter/types/object-record-update.event';
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
import { generateFakeObjectRecord } from 'src/modules/workflow/workflow-builder/utils/generate-fake-object-record';
import { OutputSchema } from 'src/modules/workflow/workflow-builder/types/output-schema.type';

export const generateFakeObjectRecordEvent = <Entity>(
export const generateFakeObjectRecordEvent = (
objectMetadataEntity: ObjectMetadataEntity,
action: DatabaseEventAction,
):
| ObjectRecordCreateEvent<Entity>
| ObjectRecordUpdateEvent<Entity>
| ObjectRecordDeleteEvent<Entity>
| ObjectRecordDestroyEvent<Entity> => {
): OutputSchema => {
const recordId = v4();
const userId = v4();
const workspaceMemberId = v4();

const after = generateFakeObjectRecord<Entity>(objectMetadataEntity);
const after = generateFakeObjectRecord(objectMetadataEntity);
const formattedObjectMetadataEntity = Object.entries(
objectMetadataEntity,
).reduce((acc: OutputSchema, [key, value]) => {
acc[key] = { isLeaf: true, value };

return acc;
}, {});

const baseResult: OutputSchema = {
recordId: { isLeaf: true, type: 'string', value: recordId },
userId: { isLeaf: true, type: 'string', value: userId },
workspaceMemberId: {
isLeaf: true,
type: 'string',
value: workspaceMemberId,
},
objectMetadata: {
isLeaf: false,
value: formattedObjectMetadataEntity,
},
};

if (action === DatabaseEventAction.CREATED) {
return {
recordId,
userId,
workspaceMemberId,
objectMetadata: objectMetadataEntity,
...baseResult,
properties: {
after,
isLeaf: false,
value: { after: { isLeaf: false, value: after } },
},
} satisfies ObjectRecordCreateEvent<Entity>;
};
}

const before = generateFakeObjectRecord<Entity>(objectMetadataEntity);
const before = generateFakeObjectRecord(objectMetadataEntity);

if (action === DatabaseEventAction.UPDATED) {
return {
recordId,
userId,
workspaceMemberId,
objectMetadata: objectMetadataEntity,
...baseResult,
properties: {
before,
after,
isLeaf: false,
value: {
before: { isLeaf: false, value: before },
after: { isLeaf: false, value: after },
},
},
} satisfies ObjectRecordUpdateEvent<Entity>;
};
}

if (action === DatabaseEventAction.DELETED) {
return {
recordId,
userId,
workspaceMemberId,
objectMetadata: objectMetadataEntity,
...baseResult,
properties: {
before,
isLeaf: false,
value: {
before: { isLeaf: false, value: before },
},
},
} satisfies ObjectRecordDeleteEvent<Entity>;
};
}

if (action === DatabaseEventAction.DESTROYED) {
return {
recordId,
userId,
workspaceMemberId,
objectMetadata: objectMetadataEntity,
...baseResult,
properties: {
before,
isLeaf: false,
value: {
before: { isLeaf: false, value: before },
},
},
} satisfies ObjectRecordDestroyEvent<Entity>;
};
}

throw new Error(`Unknown action '${action}'`);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,40 @@
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
import { generateFakeValue } from 'src/engine/utils/generate-fake-value';
import { shouldGenerateFieldFakeValue } from 'src/modules/workflow/workflow-builder/utils/should-generate-field-fake-value';
import { OutputSchema } from 'src/modules/workflow/workflow-builder/types/output-schema.type';
import { compositeTypeDefinitions } from 'src/engine/metadata-modules/field-metadata/composite-types';

export const generateFakeObjectRecord = <Entity>(
export const generateFakeObjectRecord = (
objectMetadataEntity: ObjectMetadataEntity,
): Entity =>
objectMetadataEntity.fields.reduce((acc, field) => {
): OutputSchema =>
objectMetadataEntity.fields.reduce((acc: OutputSchema, field) => {
if (!shouldGenerateFieldFakeValue(field)) {
return acc;
}
const compositeType = compositeTypeDefinitions.get(field.type);

acc[field.name] = generateFakeValue(field.type);
if (!compositeType) {
acc[field.name] = {
isLeaf: true,
type: field.type,
icon: field.icon,
value: generateFakeValue(field.type),
};
} else {
acc[field.name] = {
isLeaf: false,
icon: field.icon,
value: compositeType.properties.reduce((acc, property) => {
acc[property.name] = {
isLeaf: true,
type: property.type,
value: generateFakeValue(property.type),
};

return acc;
}, {}),
};
}

return acc;
}, {} as Entity);
}, {});
Loading

0 comments on commit 9b2853b

Please sign in to comment.