Add shortcut metadata to data models & CommandMenu (#7977)

Resolves https://github.com/twentyhq/twenty/issues/7503

---------

Co-authored-by: Félix Malfait <felix@twenty.com>
This commit is contained in:
Florian Liebig
2024-10-25 11:38:30 +02:00
committed by GitHub
parent 7edfa61571
commit bf2ba25a6e
21 changed files with 80 additions and 13 deletions
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -27,11 +27,17 @@ export const GotoHotkeysEffectsProvider = () => {
),
});
return nonSystemActiveObjectMetadataItems.map((objectMetadataItem) => (
<GoToHotkeyItemEffect
key={`go-to-hokey-item-${objectMetadataItem.id}`}
hotkey={objectMetadataItem.namePlural[0]}
pathToNavigateTo={`/objects/${objectMetadataItem.namePlural}`}
/>
));
return nonSystemActiveObjectMetadataItems.map((objectMetadataItem) => {
if (!objectMetadataItem.shortcut) {
return null;
}
return (
<GoToHotkeyItemEffect
key={`go-to-hokey-item-${objectMetadataItem.id}`}
hotkey={objectMetadataItem.shortcut}
pathToNavigateTo={`/objects/${objectMetadataItem.namePlural}`}
/>
);
});
};
@@ -125,6 +125,7 @@ describe('useCommandMenu', () => {
labelSingular: 'Task',
labelPlural: 'Tasks',
shouldSyncLabelAndName: true,
shortcut: 'T',
description: 'A task',
icon: 'IconCheckbox',
isCustom: false,
@@ -83,8 +83,8 @@ export const useCommandMenu = () => {
to: `/objects/${item.namePlural}`,
label: `Go to ${item.labelPlural}`,
type: CommandType.Navigate,
firstHotKey: 'G',
secondHotKey: item.labelPlural[0],
firstHotKey: item.shortcut ? 'G' : undefined,
secondHotKey: item.shortcut,
Icon: ALL_ICONS[
(item?.icon as keyof typeof ALL_ICONS) ?? 'IconArrowUpRight'
],
@@ -24,6 +24,7 @@ export const FIND_MANY_OBJECT_METADATA_ITEMS = gql`
updatedAt
labelIdentifierFieldMetadataId
imageIdentifierFieldMetadataId
shortcut
shouldSyncLabelAndName
indexMetadatas(paging: { first: 100 }) {
edges {
@@ -24,6 +24,8 @@ export const query = gql`
updatedAt
labelIdentifierFieldMetadataId
imageIdentifierFieldMetadataId
shortcut
shouldSyncLabelAndName
fields(paging: { first: 1000 }, filter: $fieldFilter) {
edges {
node {
@@ -26,5 +26,6 @@ export const objectMetadataItemSchema = z.object({
namePlural: camelCaseStringSchema,
nameSingular: camelCaseStringSchema,
updatedAt: z.string().datetime(),
shortcut: z.string().nullable().optional(),
shouldSyncLabelAndName: z.boolean(),
}) satisfies z.ZodType<ObjectMetadataItem>;
@@ -0,0 +1,24 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
export class AddObjectShortcut1729676165199 implements MigrationInterface {
name = 'AddObjectShortcut1729676165199';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "metadata"."objectMetadata" ADD "shortcut" character varying`,
);
await queryRunner.query(
`CREATE UNIQUE INDEX "IDX_objectMetadata_shortcut_upper_workspace" ON "metadata"."objectMetadata" (UPPER("shortcut"), "workspaceId")`,
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`DROP INDEX "metadata"."IDX_objectMetadata_shortcut_upper_workspace"`,
);
await queryRunner.query(
`ALTER TABLE "metadata"."objectMetadata" DROP COLUMN "shortcut"`,
);
}
}
@@ -51,6 +51,11 @@ export class CreateObjectInput {
@Field({ nullable: true })
icon?: string;
@IsString()
@IsOptional()
@Field({ nullable: true })
shortcut?: string;
@HideField()
dataSourceId: string;
@@ -53,6 +53,9 @@ export class ObjectMetadataDTO {
@Field({ nullable: true })
icon: string;
@Field({ nullable: true })
shortcut: string;
@FilterableField()
isCustom: boolean;
@@ -47,6 +47,11 @@ export class UpdateObjectPayload {
@Field({ nullable: true })
icon?: string;
@IsString()
@IsOptional()
@Field({ nullable: true })
shortcut?: string;
@IsBoolean()
@IsOptional()
@Field({ nullable: true })
@@ -69,6 +69,9 @@ export class ObjectMetadataEntity implements ObjectMetadataInterface {
@Column({ default: true })
isAuditLogged: boolean;
@Column({ nullable: true })
shortcut: string;
@Column({ nullable: true, type: 'uuid' })
labelIdentifierFieldMetadataId?: string | null;
@@ -10,6 +10,7 @@ interface WorkspaceEntityOptions {
labelPlural: string;
description?: string;
icon?: string;
shortcut?: string;
labelIdentifierStandardId?: string;
imageIdentifierStandardId?: string;
}
@@ -44,6 +45,7 @@ export function WorkspaceEntity(
options.labelIdentifierStandardId ?? BASE_OBJECT_STANDARD_FIELD_IDS.id,
imageIdentifierStandardId: options.imageIdentifierStandardId ?? null,
icon: options.icon,
shortcut: options.shortcut,
isAuditLogged,
isSystem,
gate,
@@ -36,6 +36,11 @@ export interface WorkspaceEntityMetadataArgs {
*/
readonly icon?: string;
/**
* Entity shortcut.
*/
readonly shortcut?: string;
/**
* Is audit logged.
*/
@@ -54,6 +54,7 @@ export const SEARCH_FIELDS_FOR_COMPANY: FieldTypeAndNameMetadata[] = [
labelPlural: 'Companies',
description: 'A company',
icon: 'IconBuildingSkyscraper',
shortcut: 'C',
labelIdentifierStandardId: COMPANY_STANDARD_FIELD_IDS.name,
})
export class CompanyWorkspaceEntity extends BaseWorkspaceEntity {
@@ -44,6 +44,7 @@ export const SEARCH_FIELDS_FOR_NOTES: FieldTypeAndNameMetadata[] = [
labelPlural: 'Notes',
description: 'A note',
icon: 'IconNotes',
shortcut: 'N',
labelIdentifierStandardId: NOTE_STANDARD_FIELD_IDS.title,
})
export class NoteWorkspaceEntity extends BaseWorkspaceEntity {
@@ -50,6 +50,7 @@ export const SEARCH_FIELDS_FOR_OPPORTUNITY: FieldTypeAndNameMetadata[] = [
labelPlural: 'Opportunities',
description: 'An opportunity',
icon: 'IconTargetArrow',
shortcut: 'O',
labelIdentifierStandardId: OPPORTUNITY_STANDARD_FIELD_IDS.name,
})
@WorkspaceIsNotAuditLogged()
@@ -59,6 +59,7 @@ export const SEARCH_FIELDS_FOR_PERSON: FieldTypeAndNameMetadata[] = [
labelPlural: 'People',
description: 'A person',
icon: 'IconUser',
shortcut: 'P',
labelIdentifierStandardId: PERSON_STANDARD_FIELD_IDS.name,
imageIdentifierStandardId: PERSON_STANDARD_FIELD_IDS.avatarUrl,
})
@@ -31,6 +31,7 @@ import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/sta
labelPlural: 'Tasks',
description: 'A task',
icon: 'IconCheckbox',
shortcut: 'T',
labelIdentifierStandardId: TASK_STANDARD_FIELD_IDS.title,
})
export class TaskWorkspaceEntity extends BaseWorkspaceEntity {
@@ -55,6 +55,7 @@ const WorkflowStatusOptions = [
labelPlural: 'Workflows',
description: 'A workflow',
icon: 'IconSettingsAutomation',
shortcut: 'W',
labelIdentifierStandardId: WORKFLOW_STANDARD_FIELD_IDS.name,
})
@WorkspaceGate({