From 7da845007589bfbfc941bd1d6566b56b012275a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Bosi?= <71827178+bosiraphael@users.noreply.github.com> Date: Fri, 20 Feb 2026 15:14:42 +0100 Subject: [PATCH] [FRONT COMPONENTS] Headless components (#18096) ## Description - Add `isHeadless` field to `FrontComponent` entity so front components can run without rendering UI in the command menu - Introduce headless front component mounting logic: `HeadlessFrontComponentMountRoot` at the application root, `useMountHeadlessFrontComponent`, and `useUnmountHeadlessFrontComponent` hooks to mount/unmount headless components - Expand the SDK with new action components (`Action`, `ActionLink`, `ActionOpenSidePanelPage`) and host communication functions (`openSidePanelPage`, `unmountFrontComponent`) - Move `CommandMenuPages` type to twenty-shared so the SDK can reference it for side panel navigation ## Video QA https://github.com/user-attachments/assets/4f9e3bb1-fcd1-42be-b3f4-a97e80c2add2 --- .../src/generated-metadata/graphql.ts | 9 ++-- .../components/ActionOpenSidePanelPage.tsx | 2 +- .../constants/RecordAgnosticActionsConfig.tsx | 2 +- .../app/components/AppRouterProviders.tsx | 2 + .../effect-components/PageChangeEffect.tsx | 3 +- .../fragments/commandMenuItemFragment.ts | 1 + ...seCommandMenuItemFrontComponentActions.tsx | 30 ++++++++--- .../components/CommandMenuContextChip.tsx | 2 +- .../components/CommandMenuPageInfo.tsx | 2 +- .../CommandMenuPageLayoutInfoContent.tsx | 2 +- .../components/CommandMenuTopBar.tsx | 2 +- .../CommandMenuTopBarInputFocusEffect.tsx | 2 +- .../CommandMenuTopBarRightCornerIcon.tsx | 2 +- .../CommandMenuWorkflowStepInfo.tsx | 2 +- .../__stories__/CommandMenu.stories.tsx | 2 +- .../hooks/usePageLayoutHeaderInfo.ts | 2 +- .../constants/CommandMenuPagesConfig.tsx | 2 +- .../hooks/__tests__/useCommandMenu.test.tsx | 2 +- ...MenuCloseAnimationCompleteCleanup.test.tsx | 2 +- .../__tests__/useCommandMenuHistory.test.tsx | 2 +- .../__tests__/useNavigateCommandMenu.test.tsx | 2 +- .../useOpenAskAIPageInCommandMenu.test.tsx | 6 +-- ...useOpenCalendarEventInCommandMenu.test.tsx | 2 +- .../useOpenEmailThreadInCommandMenu.test.tsx | 2 +- .../useOpenRecordInCommandMenu.test.tsx | 2 +- .../useUpdateCommandMenuPageInfo.test.tsx | 2 +- .../__tests__/useWorkflowCommandMenu.test.tsx | 4 +- .../command-menu/hooks/useCommandMenu.ts | 4 +- ...ommandMenuCloseAnimationCompleteCleanup.ts | 2 +- .../hooks/useCommandMenuContextChips.tsx | 2 +- .../hooks/useCommandMenuHotKeys.ts | 2 +- .../hooks/useNavigateCommandMenu.ts | 2 +- .../hooks/useOpenAskAIPageInCommandMenu.ts | 2 +- .../useOpenCalendarEventInCommandMenu.ts | 4 +- .../hooks/useOpenEmailThreadInCommandMenu.ts | 4 +- .../useOpenFrontComponentInCommandMenu.ts | 2 +- .../useOpenMergeRecordsPageInCommandMenu.tsx | 2 +- .../hooks/useOpenRecordInCommandMenu.ts | 2 +- .../useOpenRecordsSearchPageInCommandMenu.ts | 4 +- ...UpdateMultipleRecordsPageInCommandMenu.tsx | 2 +- .../hooks/useRichTextCommandMenu.ts | 4 +- .../hooks/useWorkflowCommandMenu.ts | 2 +- ...seNavigationMenuItemEditOrganizeActions.ts | 4 +- .../CommandMenuPageLayoutFieldsSettings.tsx | 2 +- .../CommandMenuPageLayoutWidgetTypeSelect.tsx | 2 +- .../chart-settings/ChartSettingItem.tsx | 2 +- .../types/PageLayoutCommandMenuPage.ts | 2 +- .../page-layout/utils/getPageLayoutIcon.ts | 2 +- .../utils/getPageLayoutPageTitle.ts | 2 +- .../states/commandMenuNavigationStackState.ts | 4 +- .../states/commandMenuPageState.ts | 2 +- .../components/FrontComponentRenderer.tsx | 2 +- .../HeadlessFrontComponentMountRoot.tsx | 31 +++++++++++ .../graphql/queries/findOneFrontComponent.ts | 1 + .../useFrontComponentExecutionContext.ts | 36 ++++++++++++- .../hooks/useMountHeadlessFrontComponent.ts | 17 ++++++ .../hooks/useUnmountHeadlessFrontComponent.ts | 19 +++++++ .../mountedHeadlessFrontComponentIdsState.ts | 6 +++ .../components/NavigationMenuEditModeBar.tsx | 2 +- .../WorkspaceNavigationMenuItems.tsx | 2 +- .../useOpenNavigationMenuItemInCommandMenu.ts | 2 +- .../components/PageLayoutRendererContent.tsx | 2 +- .../components/PageLayoutTabList.tsx | 2 +- ...youtTabListReorderableOverflowDropdown.tsx | 2 +- .../useCreateWidgetFromClick.test.tsx | 8 +-- .../useEndPageLayoutDragSelection.test.tsx | 12 ++--- .../hooks/useCreateWidgetFromClick.ts | 2 +- .../hooks/useDuplicatePageLayoutTab.ts | 2 +- .../hooks/useEditPageLayoutWidget.ts | 2 +- .../hooks/useEndPageLayoutDragSelection.ts | 6 +-- .../components/DashboardWidgetPlaceholder.tsx | 2 +- .../app-dev/expected-manifest.ts | 4 ++ .../app-dev/expected-manifest.ts | 1 + .../build/manifest/manifest-build.ts | 1 + .../components/FrontComponentWorkerEffect.tsx | 24 ++++----- .../remote/worker/remote-worker.ts | 8 ++- .../FrontComponentHostCommunicationApi.ts | 15 +++--- packages/twenty-sdk/src/sdk/action/Action.tsx | 31 +++++++++++ .../twenty-sdk/src/sdk/action/ActionLink.tsx | 42 +++++++++++++++ .../sdk/action/ActionOpenSidePanelPage.tsx | 52 +++++++++++++++++++ packages/twenty-sdk/src/sdk/action/index.ts | 6 +++ .../front-component-api/functions/navigate.ts | 33 ++++-------- .../functions/openSidePanelPage.ts | 17 ++++++ .../functions/unmountFrontComponent.ts | 17 ++++++ .../frontComponentHostCommunicationApi.ts | 37 +++++++++++++ .../src/sdk/front-component-api/index.ts | 6 ++- packages/twenty-sdk/src/sdk/index.ts | 12 +++++ ...8665-add-is-headless-to-front-component.ts | 19 +++++++ ...-to-universal-flat-front-component.util.ts | 1 + ...ompare-and-stringify.constant.spec.ts.snap | 1 + ...configuration-by-metadata-name.constant.ts | 5 ++ ...-component-editable-properties.constant.ts | 1 + ...-to-flat-front-component-to-create.util.ts | 1 + ...t-component-to-front-component-dto.util.ts | 1 + ...ent-entity-to-flat-front-component.util.ts | 1 + .../dtos/front-component.dto.ts | 5 ++ .../entities/front-component.entity.ts | 3 ++ .../application/frontComponentManifestType.ts | 1 + .../src}/types/CommandMenuPages.ts | 0 packages/twenty-shared/src/types/index.ts | 1 + 100 files changed, 518 insertions(+), 143 deletions(-) create mode 100644 packages/twenty-front/src/modules/front-components/components/HeadlessFrontComponentMountRoot.tsx create mode 100644 packages/twenty-front/src/modules/front-components/hooks/useMountHeadlessFrontComponent.ts create mode 100644 packages/twenty-front/src/modules/front-components/hooks/useUnmountHeadlessFrontComponent.ts create mode 100644 packages/twenty-front/src/modules/front-components/states/mountedHeadlessFrontComponentIdsState.ts create mode 100644 packages/twenty-sdk/src/sdk/action/Action.tsx create mode 100644 packages/twenty-sdk/src/sdk/action/ActionLink.tsx create mode 100644 packages/twenty-sdk/src/sdk/action/ActionOpenSidePanelPage.tsx create mode 100644 packages/twenty-sdk/src/sdk/action/index.ts create mode 100644 packages/twenty-sdk/src/sdk/front-component-api/functions/openSidePanelPage.ts create mode 100644 packages/twenty-sdk/src/sdk/front-component-api/functions/unmountFrontComponent.ts create mode 100644 packages/twenty-sdk/src/sdk/front-component-api/globals/frontComponentHostCommunicationApi.ts create mode 100644 packages/twenty-server/src/database/typeorm/core/migrations/common/1771509478665-add-is-headless-to-front-component.ts rename packages/{twenty-front/src/modules/command-menu => twenty-shared/src}/types/CommandMenuPages.ts (100%) diff --git a/packages/twenty-front/src/generated-metadata/graphql.ts b/packages/twenty-front/src/generated-metadata/graphql.ts index 75bf1889ea..c8aa14be5d 100644 --- a/packages/twenty-front/src/generated-metadata/graphql.ts +++ b/packages/twenty-front/src/generated-metadata/graphql.ts @@ -1675,6 +1675,7 @@ export type FrontComponent = { createdAt: Scalars['DateTime']; description?: Maybe; id: Scalars['UUID']; + isHeadless: Scalars['Boolean']; name: Scalars['String']; sourceComponentPath: Scalars['String']; universalIdentifier?: Maybe; @@ -5744,12 +5745,12 @@ export type ListPlansQueryVariables = Exact<{ [key: string]: never; }>; export type ListPlansQuery = { __typename?: 'Query', listPlans: Array<{ __typename?: 'BillingPlanOutput', planKey: BillingPlanKey, licensedProducts: Array<{ __typename?: 'BillingLicensedProduct', name: string, description: string, images?: Array | null, prices?: Array<{ __typename?: 'BillingPriceLicensed', stripePriceId: string, unitAmount: number, recurringInterval: SubscriptionInterval, priceUsageType: BillingUsageType }> | null, metadata: { __typename?: 'BillingProductMetadata', productKey: BillingProductKey, planKey: BillingPlanKey, priceUsageBased: BillingUsageType } }>, meteredProducts: Array<{ __typename?: 'BillingMeteredProduct', name: string, description: string, images?: Array | null, prices?: Array<{ __typename?: 'BillingPriceMetered', priceUsageType: BillingUsageType, recurringInterval: SubscriptionInterval, stripePriceId: string, tiers: Array<{ __typename?: 'BillingPriceTier', flatAmount?: number | null, unitAmount?: number | null, upTo?: number | null }> }> | null, metadata: { __typename?: 'BillingProductMetadata', productKey: BillingProductKey, planKey: BillingPlanKey, priceUsageBased: BillingUsageType } }> }> }; -export type CommandMenuItemFieldsFragment = { __typename?: 'CommandMenuItem', id: string, workflowVersionId?: string | null, frontComponentId?: string | null, label: string, icon?: string | null, isPinned: boolean, availabilityType: CommandMenuItemAvailabilityType, availabilityObjectMetadataId?: string | null, frontComponent?: { __typename?: 'FrontComponent', id: string, name: string } | null }; +export type CommandMenuItemFieldsFragment = { __typename?: 'CommandMenuItem', id: string, workflowVersionId?: string | null, frontComponentId?: string | null, label: string, icon?: string | null, isPinned: boolean, availabilityType: CommandMenuItemAvailabilityType, availabilityObjectMetadataId?: string | null, frontComponent?: { __typename?: 'FrontComponent', id: string, name: string, isHeadless: boolean } | null }; export type FindManyCommandMenuItemsQueryVariables = Exact<{ [key: string]: never; }>; -export type FindManyCommandMenuItemsQuery = { __typename?: 'Query', commandMenuItems: Array<{ __typename?: 'CommandMenuItem', id: string, workflowVersionId?: string | null, frontComponentId?: string | null, label: string, icon?: string | null, isPinned: boolean, availabilityType: CommandMenuItemAvailabilityType, availabilityObjectMetadataId?: string | null, frontComponent?: { __typename?: 'FrontComponent', id: string, name: string } | null }> }; +export type FindManyCommandMenuItemsQuery = { __typename?: 'Query', commandMenuItems: Array<{ __typename?: 'CommandMenuItem', id: string, workflowVersionId?: string | null, frontComponentId?: string | null, label: string, icon?: string | null, isPinned: boolean, availabilityType: CommandMenuItemAvailabilityType, availabilityObjectMetadataId?: string | null, frontComponent?: { __typename?: 'FrontComponent', id: string, name: string, isHeadless: boolean } | null }> }; export type PageLayoutFragmentFragment = { __typename?: 'PageLayout', id: string, name: string, objectMetadataId?: string | null, type: PageLayoutType, defaultTabToFocusOnMobileAndSidePanelId?: string | null, createdAt: string, updatedAt: string, tabs?: Array<{ __typename?: 'PageLayoutTab', id: string, applicationId: string, title: string, icon?: string | null, position: number, layoutMode?: PageLayoutTabLayoutMode | null, pageLayoutId: string, createdAt: string, updatedAt: string, widgets?: Array<{ __typename?: 'PageLayoutWidget', id: string, title: string, type: WidgetType, objectMetadataId?: string | null, createdAt: string, updatedAt: string, deletedAt?: string | null, pageLayoutTabId: string, gridPosition: { __typename?: 'GridPosition', column: number, columnSpan: number, row: number, rowSpan: number }, position?: { __typename?: 'PageLayoutWidgetCanvasPosition', layoutMode: PageLayoutTabLayoutMode } | { __typename?: 'PageLayoutWidgetGridPosition', layoutMode: PageLayoutTabLayoutMode, row: number, column: number, rowSpan: number, columnSpan: number } | { __typename?: 'PageLayoutWidgetVerticalListPosition', layoutMode: PageLayoutTabLayoutMode, index: number } | null, configuration: { __typename?: 'AggregateChartConfiguration', configurationType: WidgetConfigurationType, aggregateFieldMetadataId: string, aggregateOperation: AggregateOperations, label?: string | null, displayDataLabel?: boolean | null, format?: string | null, description?: string | null, filter?: any | null, prefix?: string | null, suffix?: string | null, timezone?: string | null, firstDayOfTheWeek?: number | null, ratioAggregateConfig?: { __typename?: 'RatioAggregateConfig', fieldMetadataId: string, optionValue: string } | null } | { __typename?: 'BarChartConfiguration', configurationType: WidgetConfigurationType, aggregateFieldMetadataId: string, aggregateOperation: AggregateOperations, primaryAxisGroupByFieldMetadataId: string, primaryAxisGroupBySubFieldName?: string | null, primaryAxisDateGranularity?: ObjectRecordGroupByDateGranularity | null, primaryAxisOrderBy?: GraphOrderBy | null, primaryAxisManualSortOrder?: Array | null, secondaryAxisGroupByFieldMetadataId?: string | null, secondaryAxisGroupBySubFieldName?: string | null, secondaryAxisGroupByDateGranularity?: ObjectRecordGroupByDateGranularity | null, secondaryAxisOrderBy?: GraphOrderBy | null, secondaryAxisManualSortOrder?: Array | null, omitNullValues?: boolean | null, axisNameDisplay?: AxisNameDisplay | null, displayDataLabel?: boolean | null, displayLegend?: boolean | null, rangeMin?: number | null, rangeMax?: number | null, color?: string | null, description?: string | null, filter?: any | null, groupMode?: BarChartGroupMode | null, layout: BarChartLayout, isCumulative?: boolean | null, splitMultiValueFields?: boolean | null, timezone?: string | null, firstDayOfTheWeek?: number | null } | { __typename?: 'CalendarConfiguration', configurationType: WidgetConfigurationType } | { __typename?: 'EmailsConfiguration', configurationType: WidgetConfigurationType } | { __typename?: 'FieldConfiguration', configurationType: WidgetConfigurationType } | { __typename?: 'FieldRichTextConfiguration', configurationType: WidgetConfigurationType } | { __typename?: 'FieldsConfiguration', configurationType: WidgetConfigurationType, viewId?: string | null } | { __typename?: 'FilesConfiguration', configurationType: WidgetConfigurationType } | { __typename?: 'FrontComponentConfiguration', configurationType: WidgetConfigurationType, frontComponentId: string } | { __typename?: 'GaugeChartConfiguration', configurationType: WidgetConfigurationType, aggregateFieldMetadataId: string, aggregateOperation: AggregateOperations, displayDataLabel?: boolean | null, color?: string | null, description?: string | null, filter?: any | null, timezone?: string | null, firstDayOfTheWeek?: number | null } | { __typename?: 'IframeConfiguration', configurationType: WidgetConfigurationType, url?: string | null } | { __typename?: 'LineChartConfiguration', configurationType: WidgetConfigurationType, aggregateFieldMetadataId: string, aggregateOperation: AggregateOperations, primaryAxisGroupByFieldMetadataId: string, primaryAxisGroupBySubFieldName?: string | null, primaryAxisDateGranularity?: ObjectRecordGroupByDateGranularity | null, primaryAxisOrderBy?: GraphOrderBy | null, primaryAxisManualSortOrder?: Array | null, secondaryAxisGroupByFieldMetadataId?: string | null, secondaryAxisGroupBySubFieldName?: string | null, secondaryAxisGroupByDateGranularity?: ObjectRecordGroupByDateGranularity | null, secondaryAxisOrderBy?: GraphOrderBy | null, secondaryAxisManualSortOrder?: Array | null, omitNullValues?: boolean | null, axisNameDisplay?: AxisNameDisplay | null, displayDataLabel?: boolean | null, displayLegend?: boolean | null, rangeMin?: number | null, rangeMax?: number | null, color?: string | null, description?: string | null, filter?: any | null, isStacked?: boolean | null, isCumulative?: boolean | null, splitMultiValueFields?: boolean | null, timezone?: string | null, firstDayOfTheWeek?: number | null } | { __typename?: 'NotesConfiguration', configurationType: WidgetConfigurationType } | { __typename?: 'PieChartConfiguration', configurationType: WidgetConfigurationType, groupByFieldMetadataId: string, aggregateFieldMetadataId: string, aggregateOperation: AggregateOperations, groupBySubFieldName?: string | null, dateGranularity?: ObjectRecordGroupByDateGranularity | null, orderBy?: GraphOrderBy | null, manualSortOrder?: Array | null, displayDataLabel?: boolean | null, showCenterMetric?: boolean | null, displayLegend?: boolean | null, hideEmptyCategory?: boolean | null, splitMultiValueFields?: boolean | null, color?: string | null, description?: string | null, filter?: any | null, timezone?: string | null, firstDayOfTheWeek?: number | null } | { __typename?: 'StandaloneRichTextConfiguration', configurationType: WidgetConfigurationType, body: { __typename?: 'RichTextV2Body', blocknote?: string | null, markdown?: string | null } } | { __typename?: 'TasksConfiguration', configurationType: WidgetConfigurationType } | { __typename?: 'TimelineConfiguration', configurationType: WidgetConfigurationType } | { __typename?: 'ViewConfiguration', configurationType: WidgetConfigurationType } | { __typename?: 'WorkflowConfiguration', configurationType: WidgetConfigurationType } | { __typename?: 'WorkflowRunConfiguration', configurationType: WidgetConfigurationType } | { __typename?: 'WorkflowVersionConfiguration', configurationType: WidgetConfigurationType } }> | null }> | null }; @@ -5808,7 +5809,7 @@ export type FindOneFrontComponentQueryVariables = Exact<{ }>; -export type FindOneFrontComponentQuery = { __typename?: 'Query', frontComponent?: { __typename?: 'FrontComponent', id: string, name: string, applicationId: string, builtComponentChecksum: string, applicationTokenPair?: { __typename?: 'ApplicationTokenPair', applicationAccessToken: { __typename?: 'AuthToken', token: string, expiresAt: string }, applicationRefreshToken: { __typename?: 'AuthToken', token: string, expiresAt: string } } | null } | null }; +export type FindOneFrontComponentQuery = { __typename?: 'Query', frontComponent?: { __typename?: 'FrontComponent', id: string, name: string, applicationId: string, builtComponentChecksum: string, isHeadless: boolean, applicationTokenPair?: { __typename?: 'ApplicationTokenPair', applicationAccessToken: { __typename?: 'AuthToken', token: string, expiresAt: string }, applicationRefreshToken: { __typename?: 'AuthToken', token: string, expiresAt: string } } | null } | null }; export type LogicFunctionFieldsFragment = { __typename?: 'LogicFunction', id: string, name: string, description?: string | null, runtime: string, timeoutSeconds: number, sourceHandlerPath: string, handlerName: string, toolInputSchema?: any | null, isTool: boolean, applicationId?: string | null, createdAt: string, updatedAt: string }; @@ -7121,6 +7122,7 @@ export const CommandMenuItemFieldsFragmentDoc = gql` frontComponent { id name + isHeadless } label icon @@ -10662,6 +10664,7 @@ export const FindOneFrontComponentDocument = gql` name applicationId builtComponentChecksum + isHeadless applicationTokenPair { applicationAccessToken { token diff --git a/packages/twenty-front/src/modules/action-menu/actions/components/ActionOpenSidePanelPage.tsx b/packages/twenty-front/src/modules/action-menu/actions/components/ActionOpenSidePanelPage.tsx index 6482d421f4..e301a6559f 100644 --- a/packages/twenty-front/src/modules/action-menu/actions/components/ActionOpenSidePanelPage.tsx +++ b/packages/twenty-front/src/modules/action-menu/actions/components/ActionOpenSidePanelPage.tsx @@ -2,11 +2,11 @@ import { ActionDisplay } from '@/action-menu/actions/display/components/ActionDi import { ActionConfigContext } from '@/action-menu/contexts/ActionConfigContext'; import { useNavigateCommandMenu } from '@/command-menu/hooks/useNavigateCommandMenu'; import { commandMenuSearchState } from '@/command-menu/states/commandMenuSearchState'; -import { type CommandMenuPages } from '@/command-menu/types/CommandMenuPages'; import { type MessageDescriptor } from '@lingui/core'; import { t } from '@lingui/core/macro'; import { useContext } from 'react'; import { useSetRecoilState } from 'recoil'; +import { type CommandMenuPages } from 'twenty-shared/types'; import { type IconComponent } from 'twenty-ui/display'; export const ActionOpenSidePanelPage = ({ diff --git a/packages/twenty-front/src/modules/action-menu/actions/record-agnostic-actions/constants/RecordAgnosticActionsConfig.tsx b/packages/twenty-front/src/modules/action-menu/actions/record-agnostic-actions/constants/RecordAgnosticActionsConfig.tsx index c157861fb2..042ad9db16 100644 --- a/packages/twenty-front/src/modules/action-menu/actions/record-agnostic-actions/constants/RecordAgnosticActionsConfig.tsx +++ b/packages/twenty-front/src/modules/action-menu/actions/record-agnostic-actions/constants/RecordAgnosticActionsConfig.tsx @@ -4,8 +4,8 @@ import { type ActionConfig } from '@/action-menu/actions/types/ActionConfig'; import { ActionScope } from '@/action-menu/actions/types/ActionScope'; import { ActionType } from '@/action-menu/actions/types/ActionType'; import { ActionViewType } from '@/action-menu/actions/types/ActionViewType'; -import { CommandMenuPages } from '@/command-menu/types/CommandMenuPages'; import { msg } from '@lingui/core/macro'; +import { CommandMenuPages } from 'twenty-shared/types'; import { IconHistory, IconSearch, IconSparkles } from 'twenty-ui/display'; export const RECORD_AGNOSTIC_ACTIONS_CONFIG: Record = { diff --git a/packages/twenty-front/src/modules/app/components/AppRouterProviders.tsx b/packages/twenty-front/src/modules/app/components/AppRouterProviders.tsx index 7ea8faf5b4..304a8d4859 100644 --- a/packages/twenty-front/src/modules/app/components/AppRouterProviders.tsx +++ b/packages/twenty-front/src/modules/app/components/AppRouterProviders.tsx @@ -11,6 +11,7 @@ import { ClientConfigProviderEffect } from '@/client-config/components/ClientCon import { MainContextStoreProvider } from '@/context-store/components/MainContextStoreProvider'; import { ErrorMessageEffect } from '@/error-handler/components/ErrorMessageEffect'; import { PromiseRejectionEffect } from '@/error-handler/components/PromiseRejectionEffect'; +import { HeadlessFrontComponentMountRoot } from '@/front-components/components/HeadlessFrontComponentMountRoot'; import { ApolloCoreProvider } from '@/object-metadata/components/ApolloCoreProvider'; import { ObjectMetadataItemsLoadEffect } from '@/object-metadata/components/ObjectMetadataItemsLoadEffect'; import { ObjectMetadataItemsProvider } from '@/object-metadata/components/ObjectMetadataItemsProvider'; @@ -68,6 +69,7 @@ export const AppRouterProviders = () => { + diff --git a/packages/twenty-front/src/modules/app/effect-components/PageChangeEffect.tsx b/packages/twenty-front/src/modules/app/effect-components/PageChangeEffect.tsx index 6cd97dc19f..ab3b69989e 100644 --- a/packages/twenty-front/src/modules/app/effect-components/PageChangeEffect.tsx +++ b/packages/twenty-front/src/modules/app/effect-components/PageChangeEffect.tsx @@ -18,7 +18,6 @@ import { isCaptchaScriptLoadedState } from '@/captcha/states/isCaptchaScriptLoad import { isCaptchaRequiredForPath } from '@/captcha/utils/isCaptchaRequiredForPath'; import { useCommandMenu } from '@/command-menu/hooks/useCommandMenu'; import { commandMenuPageState } from '@/command-menu/states/commandMenuPageState'; -import { CommandMenuPages } from '@/command-menu/types/CommandMenuPages'; import { MAIN_CONTEXT_STORE_INSTANCE_ID } from '@/context-store/constants/MainContextStoreInstanceId'; import { contextStoreCurrentViewIdComponentState } from '@/context-store/states/contextStoreCurrentViewIdComponentState'; import { contextStoreCurrentViewTypeComponentState } from '@/context-store/states/contextStoreCurrentViewTypeComponentState'; @@ -37,7 +36,7 @@ import { PageFocusId } from '@/types/PageFocusId'; import { useResetFocusStackToFocusItem } from '@/ui/utilities/focus/hooks/useResetFocusStackToFocusItem'; import { FocusComponentType } from '@/ui/utilities/focus/types/FocusComponentType'; import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; -import { AppBasePath, AppPath } from 'twenty-shared/types'; +import { AppBasePath, AppPath, CommandMenuPages } from 'twenty-shared/types'; import { isDefined } from 'twenty-shared/utils'; import { AnalyticsType } from '~/generated-metadata/graphql'; import { usePageChangeEffectNavigateLocation } from '~/hooks/usePageChangeEffectNavigateLocation'; diff --git a/packages/twenty-front/src/modules/command-menu-item/graphql/fragments/commandMenuItemFragment.ts b/packages/twenty-front/src/modules/command-menu-item/graphql/fragments/commandMenuItemFragment.ts index 08ee2ff4d6..790af60caf 100644 --- a/packages/twenty-front/src/modules/command-menu-item/graphql/fragments/commandMenuItemFragment.ts +++ b/packages/twenty-front/src/modules/command-menu-item/graphql/fragments/commandMenuItemFragment.ts @@ -8,6 +8,7 @@ export const COMMAND_MENU_ITEM_FRAGMENT = gql` frontComponent { id name + isHeadless } label icon diff --git a/packages/twenty-front/src/modules/command-menu-item/hooks/useCommandMenuItemFrontComponentActions.tsx b/packages/twenty-front/src/modules/command-menu-item/hooks/useCommandMenuItemFrontComponentActions.tsx index 60b806b098..d411a0e803 100644 --- a/packages/twenty-front/src/modules/command-menu-item/hooks/useCommandMenuItemFrontComponentActions.tsx +++ b/packages/twenty-front/src/modules/command-menu-item/hooks/useCommandMenuItemFrontComponentActions.tsx @@ -6,6 +6,7 @@ import { useOpenFrontComponentInCommandMenu } from '@/command-menu/hooks/useOpen import { contextStoreCurrentObjectMetadataItemIdComponentState } from '@/context-store/states/contextStoreCurrentObjectMetadataItemIdComponentState'; import { contextStoreIsPageInEditModeComponentState } from '@/context-store/states/contextStoreIsPageInEditModeComponentState'; import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState'; +import { useMountHeadlessFrontComponent } from '@/front-components/hooks/useMountHeadlessFrontComponent'; import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled'; import { useContext } from 'react'; @@ -35,6 +36,7 @@ type BuildActionFromItemParams = { pageTitle: string; pageIcon: IconComponent; }) => void; + mountHeadlessFrontComponent: (frontComponentId: string) => void; }; const buildActionFromItem = ({ @@ -44,11 +46,26 @@ const buildActionFromItem = ({ isPinned, getIcon, openFrontComponentInCommandMenu, + mountHeadlessFrontComponent, }: BuildActionFromItemParams) => { const displayLabel = item.label; const Icon = getIcon(item.icon, COMMAND_MENU_DEFAULT_ICON); + const isHeadless = item.frontComponent?.isHeadless === true; + + const handleClick = () => { + if (isHeadless) { + mountHeadlessFrontComponent(item.frontComponentId); + } else { + openFrontComponentInCommandMenu({ + frontComponentId: item.frontComponentId, + pageTitle: displayLabel, + pageIcon: Icon, + }); + } + }; + return { type: ActionType.FrontComponent, key: `command-menu-item-front-component-${item.id}`, @@ -61,14 +78,8 @@ const buildActionFromItem = ({ shouldBeRegistered: () => true, component: ( - openFrontComponentInCommandMenu({ - frontComponentId: item.frontComponentId, - pageTitle: displayLabel, - pageIcon: Icon, - }) - } - closeSidePanelOnCommandMenuListActionExecution={false} + onClick={handleClick} + closeSidePanelOnCommandMenuListActionExecution={isHeadless} /> ), }; @@ -78,6 +89,7 @@ export const useCommandMenuItemFrontComponentActions = () => { const { getIcon } = useIcons(); const { openFrontComponentInCommandMenu } = useOpenFrontComponentInCommandMenu(); + const mountHeadlessFrontComponent = useMountHeadlessFrontComponent(); const isPageInEditMode = useRecoilComponentValue( contextStoreIsPageInEditModeComponentState, @@ -140,6 +152,7 @@ export const useCommandMenuItemFrontComponentActions = () => { isPinned: !isPageInEditMode && item.isPinned, getIcon, openFrontComponentInCommandMenu, + mountHeadlessFrontComponent, }), ); @@ -151,6 +164,7 @@ export const useCommandMenuItemFrontComponentActions = () => { isPinned: !isPageInEditMode && item.isPinned, getIcon, openFrontComponentInCommandMenu, + mountHeadlessFrontComponent, }), ); diff --git a/packages/twenty-front/src/modules/command-menu/components/CommandMenuContextChip.tsx b/packages/twenty-front/src/modules/command-menu/components/CommandMenuContextChip.tsx index 93ce490673..bd101ddbae 100644 --- a/packages/twenty-front/src/modules/command-menu/components/CommandMenuContextChip.tsx +++ b/packages/twenty-front/src/modules/command-menu/components/CommandMenuContextChip.tsx @@ -1,8 +1,8 @@ -import { type CommandMenuPages } from '@/command-menu/types/CommandMenuPages'; import styled from '@emotion/styled'; import { t } from '@lingui/core/macro'; import { isNonEmptyString } from '@sniptt/guards'; import { Fragment } from 'react/jsx-runtime'; +import { type CommandMenuPages } from 'twenty-shared/types'; import { isDefined } from 'twenty-shared/utils'; import { OverflowingTextWithTooltip } from 'twenty-ui/display'; diff --git a/packages/twenty-front/src/modules/command-menu/components/CommandMenuPageInfo.tsx b/packages/twenty-front/src/modules/command-menu/components/CommandMenuPageInfo.tsx index ec1ff21251..87bb1d21c0 100644 --- a/packages/twenty-front/src/modules/command-menu/components/CommandMenuPageInfo.tsx +++ b/packages/twenty-front/src/modules/command-menu/components/CommandMenuPageInfo.tsx @@ -10,11 +10,11 @@ import { CommandMenuObjectViewRecordInfo } from '@/command-menu/components/Comma import { CommandMenuPageLayoutInfo } from '@/command-menu/components/CommandMenuPageLayoutInfo'; import { CommandMenuRecordInfo } from '@/command-menu/components/CommandMenuRecordInfo'; import { CommandMenuWorkflowStepInfo } from '@/command-menu/components/CommandMenuWorkflowStepInfo'; -import { CommandMenuPages } from '@/command-menu/types/CommandMenuPages'; import { NavigationMenuItemType } from '@/navigation-menu-item/constants/NavigationMenuItemType'; import { useWorkspaceSectionItems } from '@/navigation-menu-item/hooks/useWorkspaceSectionItems'; import { selectedNavigationMenuItemInEditModeStateV2 } from '@/navigation-menu-item/states/selectedNavigationMenuItemInEditModeStateV2'; import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2'; +import { CommandMenuPages } from 'twenty-shared/types'; import { type CommandMenuContextChipProps } from './CommandMenuContextChip'; diff --git a/packages/twenty-front/src/modules/command-menu/components/CommandMenuPageLayoutInfoContent.tsx b/packages/twenty-front/src/modules/command-menu/components/CommandMenuPageLayoutInfoContent.tsx index 569f4bc4a5..ed38acedac 100644 --- a/packages/twenty-front/src/modules/command-menu/components/CommandMenuPageLayoutInfoContent.tsx +++ b/packages/twenty-front/src/modules/command-menu/components/CommandMenuPageLayoutInfoContent.tsx @@ -3,7 +3,6 @@ import { useUpdateCommandMenuPageInfo } from '@/command-menu/hooks/useUpdateComm import { commandMenuPageInfoState } from '@/command-menu/states/commandMenuPageInfoState'; import { commandMenuPageState } from '@/command-menu/states/commandMenuPageState'; import { commandMenuShouldFocusTitleInputComponentState } from '@/command-menu/states/commandMenuShouldFocusTitleInputComponentState'; -import { CommandMenuPages } from '@/command-menu/types/CommandMenuPages'; import { useUpdatePageLayoutTab } from '@/page-layout/hooks/useUpdatePageLayoutTab'; import { useUpdatePageLayoutWidget } from '@/page-layout/hooks/useUpdatePageLayoutWidget'; import { pageLayoutDraftComponentState } from '@/page-layout/states/pageLayoutDraftComponentState'; @@ -16,6 +15,7 @@ import { useTheme } from '@emotion/react'; import { isNonEmptyString } from '@sniptt/guards'; import { useState } from 'react'; import { useRecoilValue } from 'recoil'; +import { CommandMenuPages } from 'twenty-shared/types'; import { isDefined } from 'twenty-shared/utils'; import { useIcons } from 'twenty-ui/display'; import { CommandMenuPageInfoLayout } from './CommandMenuPageInfoLayout'; diff --git a/packages/twenty-front/src/modules/command-menu/components/CommandMenuTopBar.tsx b/packages/twenty-front/src/modules/command-menu/components/CommandMenuTopBar.tsx index f8cdeabe8d..5bb58a0978 100644 --- a/packages/twenty-front/src/modules/command-menu/components/CommandMenuTopBar.tsx +++ b/packages/twenty-front/src/modules/command-menu/components/CommandMenuTopBar.tsx @@ -11,7 +11,6 @@ import { useCommandMenuContextChips } from '@/command-menu/hooks/useCommandMenuC import { commandMenuNavigationStackState } from '@/command-menu/states/commandMenuNavigationStackState'; import { commandMenuPageState } from '@/command-menu/states/commandMenuPageState'; import { commandMenuSearchState } from '@/command-menu/states/commandMenuSearchState'; -import { CommandMenuPages } from '@/command-menu/types/CommandMenuPages'; import { usePushFocusItemToFocusStack } from '@/ui/utilities/focus/hooks/usePushFocusItemToFocusStack'; import { useRemoveFocusItemFromFocusStackById } from '@/ui/utilities/focus/hooks/useRemoveFocusItemFromFocusStackById'; import { FocusComponentType } from '@/ui/utilities/focus/types/FocusComponentType'; @@ -21,6 +20,7 @@ import { useLingui } from '@lingui/react/macro'; import { AnimatePresence, motion } from 'framer-motion'; import { useRef } from 'react'; import { useRecoilState, useRecoilValue } from 'recoil'; +import { CommandMenuPages } from 'twenty-shared/types'; import { IconX } from 'twenty-ui/display'; import { IconButton } from 'twenty-ui/input'; import { useIsMobile } from 'twenty-ui/utilities'; diff --git a/packages/twenty-front/src/modules/command-menu/components/CommandMenuTopBarInputFocusEffect.tsx b/packages/twenty-front/src/modules/command-menu/components/CommandMenuTopBarInputFocusEffect.tsx index 74da53d428..fec745d997 100644 --- a/packages/twenty-front/src/modules/command-menu/components/CommandMenuTopBarInputFocusEffect.tsx +++ b/packages/twenty-front/src/modules/command-menu/components/CommandMenuTopBarInputFocusEffect.tsx @@ -1,8 +1,8 @@ import { useRecoilValue } from 'recoil'; import { commandMenuPageState } from '@/command-menu/states/commandMenuPageState'; -import { CommandMenuPages } from '@/command-menu/types/CommandMenuPages'; import { useEffect } from 'react'; +import { CommandMenuPages } from 'twenty-shared/types'; type CommandMenuTopBarInputFocusEffectProps = { inputRef: React.RefObject; diff --git a/packages/twenty-front/src/modules/command-menu/components/CommandMenuTopBarRightCornerIcon.tsx b/packages/twenty-front/src/modules/command-menu/components/CommandMenuTopBarRightCornerIcon.tsx index 68759dee45..2968339909 100644 --- a/packages/twenty-front/src/modules/command-menu/components/CommandMenuTopBarRightCornerIcon.tsx +++ b/packages/twenty-front/src/modules/command-menu/components/CommandMenuTopBarRightCornerIcon.tsx @@ -1,10 +1,10 @@ import { useOpenAskAIPageInCommandMenu } from '@/command-menu/hooks/useOpenAskAIPageInCommandMenu'; import { commandMenuPageState } from '@/command-menu/states/commandMenuPageState'; -import { CommandMenuPages } from '@/command-menu/types/CommandMenuPages'; import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled'; import styled from '@emotion/styled'; import { t } from '@lingui/core/macro'; import { useRecoilValue } from 'recoil'; +import { CommandMenuPages } from 'twenty-shared/types'; import { IconEdit, IconSparkles } from 'twenty-ui/display'; import { IconButton } from 'twenty-ui/input'; import { useIsMobile } from 'twenty-ui/utilities'; diff --git a/packages/twenty-front/src/modules/command-menu/components/CommandMenuWorkflowStepInfo.tsx b/packages/twenty-front/src/modules/command-menu/components/CommandMenuWorkflowStepInfo.tsx index e9ed92830b..a0bb489b91 100644 --- a/packages/twenty-front/src/modules/command-menu/components/CommandMenuWorkflowStepInfo.tsx +++ b/packages/twenty-front/src/modules/command-menu/components/CommandMenuWorkflowStepInfo.tsx @@ -2,7 +2,6 @@ import { useUpdateCommandMenuPageInfo } from '@/command-menu/hooks/useUpdateComm import { useCommandMenuWorkflowIdOrThrow } from '@/command-menu/pages/workflow/hooks/useCommandMenuWorkflowIdOrThrow'; import { commandMenuWorkflowStepIdComponentState } from '@/command-menu/pages/workflow/states/commandMenuWorkflowStepIdComponentState'; import { commandMenuPageState } from '@/command-menu/states/commandMenuPageState'; -import { CommandMenuPages } from '@/command-menu/types/CommandMenuPages'; import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; import { useUpdateOneRecord } from '@/object-record/hooks/useUpdateOneRecord'; import { TitleInput } from '@/ui/input/components/TitleInput'; @@ -22,6 +21,7 @@ import { useTheme } from '@emotion/react'; import { t } from '@lingui/core/macro'; import { useState } from 'react'; import { useRecoilValue } from 'recoil'; +import { CommandMenuPages } from 'twenty-shared/types'; import { isDefined } from 'twenty-shared/utils'; import { TRIGGER_STEP_ID } from 'twenty-shared/workflow'; import { useIcons } from 'twenty-ui/display'; diff --git a/packages/twenty-front/src/modules/command-menu/components/__stories__/CommandMenu.stories.tsx b/packages/twenty-front/src/modules/command-menu/components/__stories__/CommandMenu.stories.tsx index a5056915db..65dc4ae5cf 100644 --- a/packages/twenty-front/src/modules/command-menu/components/__stories__/CommandMenu.stories.tsx +++ b/packages/twenty-front/src/modules/command-menu/components/__stories__/CommandMenu.stories.tsx @@ -28,12 +28,12 @@ import { COMMAND_MENU_COMPONENT_INSTANCE_ID } from '@/command-menu/constants/Com import { SIDE_PANEL_FOCUS_ID } from '@/command-menu/constants/SidePanelFocusId'; import { commandMenuNavigationStackState } from '@/command-menu/states/commandMenuNavigationStackState'; import { isCommandMenuOpenedState } from '@/command-menu/states/isCommandMenuOpenedState'; -import { CommandMenuPages } from '@/command-menu/types/CommandMenuPages'; import { ContextStoreComponentInstanceContext } from '@/context-store/states/contexts/ContextStoreComponentInstanceContext'; import { ContextStoreViewType } from '@/context-store/types/ContextStoreViewType'; import { RecordComponentInstanceContextsWrapper } from '@/object-record/components/RecordComponentInstanceContextsWrapper'; import { ViewComponentInstanceContext } from '@/views/states/contexts/ViewComponentInstanceContext'; import { HttpResponse, graphql } from 'msw'; +import { CommandMenuPages } from 'twenty-shared/types'; import { IconDotsVertical } from 'twenty-ui/display'; import { JestContextStoreSetter } from '~/testing/jest/JestContextStoreSetter'; diff --git a/packages/twenty-front/src/modules/command-menu/components/hooks/usePageLayoutHeaderInfo.ts b/packages/twenty-front/src/modules/command-menu/components/hooks/usePageLayoutHeaderInfo.ts index 3a8df0a0a1..1a5c44269c 100644 --- a/packages/twenty-front/src/modules/command-menu/components/hooks/usePageLayoutHeaderInfo.ts +++ b/packages/twenty-front/src/modules/command-menu/components/hooks/usePageLayoutHeaderInfo.ts @@ -1,11 +1,11 @@ import { GRAPH_TYPE_INFORMATION } from '@/command-menu/pages/page-layout/constants/GraphTypeInformation'; import { getCurrentGraphTypeFromConfig } from '@/command-menu/pages/page-layout/utils/getCurrentGraphTypeFromConfig'; import { isWidgetConfigurationOfTypeGraph } from '@/command-menu/pages/page-layout/utils/isWidgetConfigurationOfTypeGraph'; -import { CommandMenuPages } from '@/command-menu/types/CommandMenuPages'; import { type PageLayoutTab } from '@/page-layout/types/PageLayoutTab'; import { type PageLayoutWidget } from '@/page-layout/types/PageLayoutWidget'; import { useTheme } from '@emotion/react'; import { t } from '@lingui/core/macro'; +import { CommandMenuPages } from 'twenty-shared/types'; import { isDefined } from 'twenty-shared/utils'; import { IconAppWindow, diff --git a/packages/twenty-front/src/modules/command-menu/constants/CommandMenuPagesConfig.tsx b/packages/twenty-front/src/modules/command-menu/constants/CommandMenuPagesConfig.tsx index 272810176d..dc30d3e717 100644 --- a/packages/twenty-front/src/modules/command-menu/constants/CommandMenuPagesConfig.tsx +++ b/packages/twenty-front/src/modules/command-menu/constants/CommandMenuPagesConfig.tsx @@ -24,7 +24,7 @@ import { CommandMenuWorkflowEditStepType } from '@/command-menu/pages/workflow/s import { CommandMenuWorkflowRunViewStep } from '@/command-menu/pages/workflow/step/view-run/components/CommandMenuWorkflowRunViewStep'; import { CommandMenuWorkflowViewStep } from '@/command-menu/pages/workflow/step/view/components/CommandMenuWorkflowViewStep'; import { CommandMenuWorkflowSelectTriggerType } from '@/command-menu/pages/workflow/trigger-type/components/CommandMenuWorkflowSelectTriggerType'; -import { CommandMenuPages } from '@/command-menu/types/CommandMenuPages'; +import { CommandMenuPages } from 'twenty-shared/types'; export const COMMAND_MENU_PAGES_CONFIG = new Map< CommandMenuPages, diff --git a/packages/twenty-front/src/modules/command-menu/hooks/__tests__/useCommandMenu.test.tsx b/packages/twenty-front/src/modules/command-menu/hooks/__tests__/useCommandMenu.test.tsx index bb674f3276..3aaa5e2ab8 100644 --- a/packages/twenty-front/src/modules/command-menu/hooks/__tests__/useCommandMenu.test.tsx +++ b/packages/twenty-front/src/modules/command-menu/hooks/__tests__/useCommandMenu.test.tsx @@ -8,7 +8,7 @@ import { commandMenuNavigationStackState } from '@/command-menu/states/commandMe import { commandMenuPageInfoState } from '@/command-menu/states/commandMenuPageInfoState'; import { commandMenuPageState } from '@/command-menu/states/commandMenuPageState'; import { isCommandMenuOpenedState } from '@/command-menu/states/isCommandMenuOpenedState'; -import { CommandMenuPages } from '@/command-menu/types/CommandMenuPages'; +import { CommandMenuPages } from 'twenty-shared/types'; import { IconDotsVertical } from 'twenty-ui/display'; const Wrapper = ({ children }: { children: React.ReactNode }) => ( diff --git a/packages/twenty-front/src/modules/command-menu/hooks/__tests__/useCommandMenuCloseAnimationCompleteCleanup.test.tsx b/packages/twenty-front/src/modules/command-menu/hooks/__tests__/useCommandMenuCloseAnimationCompleteCleanup.test.tsx index 095e20884c..cca8a59ea5 100644 --- a/packages/twenty-front/src/modules/command-menu/hooks/__tests__/useCommandMenuCloseAnimationCompleteCleanup.test.tsx +++ b/packages/twenty-front/src/modules/command-menu/hooks/__tests__/useCommandMenuCloseAnimationCompleteCleanup.test.tsx @@ -15,8 +15,8 @@ import { commandMenuSearchState } from '@/command-menu/states/commandMenuSearchS import { hasUserSelectedCommandState } from '@/command-menu/states/hasUserSelectedCommandState'; import { isCommandMenuClosingState } from '@/command-menu/states/isCommandMenuClosingState'; import { isCommandMenuOpenedState } from '@/command-menu/states/isCommandMenuOpenedState'; -import { CommandMenuPages } from '@/command-menu/types/CommandMenuPages'; import { viewableRecordIdState } from '@/object-record/record-right-drawer/states/viewableRecordIdState'; +import { CommandMenuPages } from 'twenty-shared/types'; import { IconList } from 'twenty-ui/display'; const mockCloseDropdown = jest.fn(); diff --git a/packages/twenty-front/src/modules/command-menu/hooks/__tests__/useCommandMenuHistory.test.tsx b/packages/twenty-front/src/modules/command-menu/hooks/__tests__/useCommandMenuHistory.test.tsx index efef9a91a8..74d7dd5442 100644 --- a/packages/twenty-front/src/modules/command-menu/hooks/__tests__/useCommandMenuHistory.test.tsx +++ b/packages/twenty-front/src/modules/command-menu/hooks/__tests__/useCommandMenuHistory.test.tsx @@ -10,7 +10,7 @@ import { commandMenuNavigationStackState } from '@/command-menu/states/commandMe import { commandMenuPageInfoState } from '@/command-menu/states/commandMenuPageInfoState'; import { commandMenuPageState } from '@/command-menu/states/commandMenuPageState'; import { isCommandMenuOpenedState } from '@/command-menu/states/isCommandMenuOpenedState'; -import { CommandMenuPages } from '@/command-menu/types/CommandMenuPages'; +import { CommandMenuPages } from 'twenty-shared/types'; import { IconList, IconSearch } from 'twenty-ui/display'; const Wrapper = ({ children }: { children: React.ReactNode }) => ( diff --git a/packages/twenty-front/src/modules/command-menu/hooks/__tests__/useNavigateCommandMenu.test.tsx b/packages/twenty-front/src/modules/command-menu/hooks/__tests__/useNavigateCommandMenu.test.tsx index baf558c6b2..bad5eb1fa0 100644 --- a/packages/twenty-front/src/modules/command-menu/hooks/__tests__/useNavigateCommandMenu.test.tsx +++ b/packages/twenty-front/src/modules/command-menu/hooks/__tests__/useNavigateCommandMenu.test.tsx @@ -7,8 +7,8 @@ import { useNavigateCommandMenu } from '@/command-menu/hooks/useNavigateCommandM import { commandMenuNavigationStackState } from '@/command-menu/states/commandMenuNavigationStackState'; import { commandMenuPageInfoState } from '@/command-menu/states/commandMenuPageInfoState'; import { commandMenuPageState } from '@/command-menu/states/commandMenuPageState'; -import { CommandMenuPages } from '@/command-menu/types/CommandMenuPages'; import { ContextStoreViewType } from '@/context-store/types/ContextStoreViewType'; +import { CommandMenuPages } from 'twenty-shared/types'; import { Icon123, useIcons } from 'twenty-ui/display'; import { getJestMetadataAndApolloMocksAndActionMenuWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksAndActionMenuWrapper'; import { generatedMockObjectMetadataItems } from '~/testing/utils/generatedMockObjectMetadataItems'; diff --git a/packages/twenty-front/src/modules/command-menu/hooks/__tests__/useOpenAskAIPageInCommandMenu.test.tsx b/packages/twenty-front/src/modules/command-menu/hooks/__tests__/useOpenAskAIPageInCommandMenu.test.tsx index 514e110f90..41223c0f64 100644 --- a/packages/twenty-front/src/modules/command-menu/hooks/__tests__/useOpenAskAIPageInCommandMenu.test.tsx +++ b/packages/twenty-front/src/modules/command-menu/hooks/__tests__/useOpenAskAIPageInCommandMenu.test.tsx @@ -1,12 +1,12 @@ -import { renderHook, act } from '@testing-library/react'; +import { act, renderHook } from '@testing-library/react'; import { Provider as JotaiProvider } from 'jotai'; import { type ReactNode } from 'react'; import { RecoilRoot } from 'recoil'; -import { isCommandMenuOpenedStateV2 } from '@/command-menu/states/isCommandMenuOpenedStateV2'; -import { CommandMenuPages } from '@/command-menu/types/CommandMenuPages'; import { useOpenAskAIPageInCommandMenu } from '@/command-menu/hooks/useOpenAskAIPageInCommandMenu'; +import { isCommandMenuOpenedStateV2 } from '@/command-menu/states/isCommandMenuOpenedStateV2'; import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore'; +import { CommandMenuPages } from 'twenty-shared/types'; import { IconSparkles } from 'twenty-ui/display'; const navigateCommandMenuMock = jest.fn(); diff --git a/packages/twenty-front/src/modules/command-menu/hooks/__tests__/useOpenCalendarEventInCommandMenu.test.tsx b/packages/twenty-front/src/modules/command-menu/hooks/__tests__/useOpenCalendarEventInCommandMenu.test.tsx index dc7b79af3a..0be5b688ea 100644 --- a/packages/twenty-front/src/modules/command-menu/hooks/__tests__/useOpenCalendarEventInCommandMenu.test.tsx +++ b/packages/twenty-front/src/modules/command-menu/hooks/__tests__/useOpenCalendarEventInCommandMenu.test.tsx @@ -4,9 +4,9 @@ import { act } from 'react'; import { COMMAND_MENU_COMPONENT_INSTANCE_ID } from '@/command-menu/constants/CommandMenuComponentInstanceId'; import { useOpenCalendarEventInCommandMenu } from '@/command-menu/hooks/useOpenCalendarEventInCommandMenu'; import { viewableRecordIdComponentState } from '@/command-menu/pages/record-page/states/viewableRecordIdComponentState'; -import { CommandMenuPages } from '@/command-menu/types/CommandMenuPages'; import { ContextStoreViewType } from '@/context-store/types/ContextStoreViewType'; import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; +import { CommandMenuPages } from 'twenty-shared/types'; import { IconCalendarEvent } from 'twenty-ui/display'; import { getJestMetadataAndApolloMocksAndActionMenuWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksAndActionMenuWrapper'; import { generatedMockObjectMetadataItems } from '~/testing/utils/generatedMockObjectMetadataItems'; diff --git a/packages/twenty-front/src/modules/command-menu/hooks/__tests__/useOpenEmailThreadInCommandMenu.test.tsx b/packages/twenty-front/src/modules/command-menu/hooks/__tests__/useOpenEmailThreadInCommandMenu.test.tsx index ffa4cad18f..8597d04a8a 100644 --- a/packages/twenty-front/src/modules/command-menu/hooks/__tests__/useOpenEmailThreadInCommandMenu.test.tsx +++ b/packages/twenty-front/src/modules/command-menu/hooks/__tests__/useOpenEmailThreadInCommandMenu.test.tsx @@ -4,9 +4,9 @@ import { act } from 'react'; import { COMMAND_MENU_COMPONENT_INSTANCE_ID } from '@/command-menu/constants/CommandMenuComponentInstanceId'; import { useOpenEmailThreadInCommandMenu } from '@/command-menu/hooks/useOpenEmailThreadInCommandMenu'; import { viewableRecordIdComponentState } from '@/command-menu/pages/record-page/states/viewableRecordIdComponentState'; -import { CommandMenuPages } from '@/command-menu/types/CommandMenuPages'; import { ContextStoreViewType } from '@/context-store/types/ContextStoreViewType'; import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; +import { CommandMenuPages } from 'twenty-shared/types'; import { IconMail } from 'twenty-ui/display'; import { getJestMetadataAndApolloMocksAndActionMenuWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksAndActionMenuWrapper'; import { generatedMockObjectMetadataItems } from '~/testing/utils/generatedMockObjectMetadataItems'; diff --git a/packages/twenty-front/src/modules/command-menu/hooks/__tests__/useOpenRecordInCommandMenu.test.tsx b/packages/twenty-front/src/modules/command-menu/hooks/__tests__/useOpenRecordInCommandMenu.test.tsx index afe012bf6e..13d927f87e 100644 --- a/packages/twenty-front/src/modules/command-menu/hooks/__tests__/useOpenRecordInCommandMenu.test.tsx +++ b/packages/twenty-front/src/modules/command-menu/hooks/__tests__/useOpenRecordInCommandMenu.test.tsx @@ -8,13 +8,13 @@ import { viewableRecordIdComponentState } from '@/command-menu/pages/record-page import { viewableRecordNameSingularComponentState } from '@/command-menu/pages/record-page/states/viewableRecordNameSingularComponentState'; import { commandMenuNavigationMorphItemsByPageState } from '@/command-menu/states/commandMenuNavigationMorphItemsByPageState'; import { commandMenuPageState } from '@/command-menu/states/commandMenuPageState'; -import { CommandMenuPages } from '@/command-menu/types/CommandMenuPages'; import { contextStoreCurrentObjectMetadataItemIdComponentState } from '@/context-store/states/contextStoreCurrentObjectMetadataItemIdComponentState'; import { contextStoreCurrentViewTypeComponentState } from '@/context-store/states/contextStoreCurrentViewTypeComponentState'; import { contextStoreNumberOfSelectedRecordsComponentState } from '@/context-store/states/contextStoreNumberOfSelectedRecordsComponentState'; import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState'; import { ContextStoreViewType } from '@/context-store/types/ContextStoreViewType'; import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; +import { CommandMenuPages } from 'twenty-shared/types'; import { useIcons } from 'twenty-ui/display'; import { getJestMetadataAndApolloMocksAndActionMenuWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksAndActionMenuWrapper'; import { generatedMockObjectMetadataItems } from '~/testing/utils/generatedMockObjectMetadataItems'; diff --git a/packages/twenty-front/src/modules/command-menu/hooks/__tests__/useUpdateCommandMenuPageInfo.test.tsx b/packages/twenty-front/src/modules/command-menu/hooks/__tests__/useUpdateCommandMenuPageInfo.test.tsx index 25be05a3d5..5d723946bb 100644 --- a/packages/twenty-front/src/modules/command-menu/hooks/__tests__/useUpdateCommandMenuPageInfo.test.tsx +++ b/packages/twenty-front/src/modules/command-menu/hooks/__tests__/useUpdateCommandMenuPageInfo.test.tsx @@ -1,10 +1,10 @@ import { useUpdateCommandMenuPageInfo } from '@/command-menu/hooks/useUpdateCommandMenuPageInfo'; import { commandMenuNavigationStackState } from '@/command-menu/states/commandMenuNavigationStackState'; import { commandMenuPageInfoState } from '@/command-menu/states/commandMenuPageInfoState'; -import { CommandMenuPages } from '@/command-menu/types/CommandMenuPages'; import { renderHook } from '@testing-library/react'; import { act } from 'react'; import { RecoilRoot, useRecoilValue } from 'recoil'; +import { CommandMenuPages } from 'twenty-shared/types'; import { IconArrowDown, IconDotsVertical } from 'twenty-ui/display'; const mockedPageInfo = { diff --git a/packages/twenty-front/src/modules/command-menu/hooks/__tests__/useWorkflowCommandMenu.test.tsx b/packages/twenty-front/src/modules/command-menu/hooks/__tests__/useWorkflowCommandMenu.test.tsx index b4aae2f0e8..cf6707e93b 100644 --- a/packages/twenty-front/src/modules/command-menu/hooks/__tests__/useWorkflowCommandMenu.test.tsx +++ b/packages/twenty-front/src/modules/command-menu/hooks/__tests__/useWorkflowCommandMenu.test.tsx @@ -2,13 +2,13 @@ import { renderHook } from '@testing-library/react'; import { useRecoilValue } from 'recoil'; import { COMMAND_MENU_COMPONENT_INSTANCE_ID } from '@/command-menu/constants/CommandMenuComponentInstanceId'; +import { useWorkflowCommandMenu } from '@/command-menu/hooks/useWorkflowCommandMenu'; import { viewableRecordIdComponentState } from '@/command-menu/pages/record-page/states/viewableRecordIdComponentState'; import { viewableRecordNameSingularComponentState } from '@/command-menu/pages/record-page/states/viewableRecordNameSingularComponentState'; import { commandMenuWorkflowIdComponentState } from '@/command-menu/pages/workflow/states/commandMenuWorkflowIdComponentState'; import { commandMenuWorkflowVersionIdComponentState } from '@/command-menu/pages/workflow/states/commandMenuWorkflowVersionIdComponentState'; import { commandMenuNavigationMorphItemsByPageState } from '@/command-menu/states/commandMenuNavigationMorphItemsByPageState'; import { commandMenuPageState } from '@/command-menu/states/commandMenuPageState'; -import { CommandMenuPages } from '@/command-menu/types/CommandMenuPages'; import { contextStoreCurrentObjectMetadataItemIdComponentState } from '@/context-store/states/contextStoreCurrentObjectMetadataItemIdComponentState'; import { contextStoreCurrentViewTypeComponentState } from '@/context-store/states/contextStoreCurrentViewTypeComponentState'; import { contextStoreNumberOfSelectedRecordsComponentState } from '@/context-store/states/contextStoreNumberOfSelectedRecordsComponentState'; @@ -17,10 +17,10 @@ import { ContextStoreViewType } from '@/context-store/types/ContextStoreViewType import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; import { t } from '@lingui/core/macro'; import { act } from 'react'; +import { CommandMenuPages } from 'twenty-shared/types'; import { IconBolt, IconSettingsAutomation, useIcons } from 'twenty-ui/display'; import { getJestMetadataAndApolloMocksAndActionMenuWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksAndActionMenuWrapper'; import { generatedMockObjectMetadataItems } from '~/testing/utils/generatedMockObjectMetadataItems'; -import { useWorkflowCommandMenu } from '@/command-menu/hooks/useWorkflowCommandMenu'; jest.mock('uuid', () => ({ v4: jest.fn().mockReturnValue('mocked-uuid'), diff --git a/packages/twenty-front/src/modules/command-menu/hooks/useCommandMenu.ts b/packages/twenty-front/src/modules/command-menu/hooks/useCommandMenu.ts index caf418c819..c495ace928 100644 --- a/packages/twenty-front/src/modules/command-menu/hooks/useCommandMenu.ts +++ b/packages/twenty-front/src/modules/command-menu/hooks/useCommandMenu.ts @@ -6,14 +6,14 @@ import { commandMenuSearchState } from '@/command-menu/states/commandMenuSearchS import { isCommandMenuClosingState } from '@/command-menu/states/isCommandMenuClosingState'; import { isCommandMenuOpenedState } from '@/command-menu/states/isCommandMenuOpenedState'; import { isCommandMenuOpenedStateV2 } from '@/command-menu/states/isCommandMenuOpenedStateV2'; -import { CommandMenuPages } from '@/command-menu/types/CommandMenuPages'; import { addToNavPayloadRegistryStateV2 } from '@/navigation-menu-item/states/addToNavPayloadRegistryStateV2'; import { useCloseAnyOpenDropdown } from '@/ui/layout/dropdown/hooks/useCloseAnyOpenDropdown'; import { emitSidePanelOpenEvent } from '@/ui/layout/right-drawer/utils/emitSidePanelOpenEvent'; -import { useStore } from 'jotai'; import { useRemoveFocusItemFromFocusStackById } from '@/ui/utilities/focus/hooks/useRemoveFocusItemFromFocusStackById'; import { t } from '@lingui/core/macro'; +import { useStore } from 'jotai'; import { useCallback } from 'react'; +import { CommandMenuPages } from 'twenty-shared/types'; import { IconDotsVertical } from 'twenty-ui/display'; export const useCommandMenu = () => { diff --git a/packages/twenty-front/src/modules/command-menu/hooks/useCommandMenuCloseAnimationCompleteCleanup.ts b/packages/twenty-front/src/modules/command-menu/hooks/useCommandMenuCloseAnimationCompleteCleanup.ts index 8926d4f13b..9a3bf3b798 100644 --- a/packages/twenty-front/src/modules/command-menu/hooks/useCommandMenuCloseAnimationCompleteCleanup.ts +++ b/packages/twenty-front/src/modules/command-menu/hooks/useCommandMenuCloseAnimationCompleteCleanup.ts @@ -12,7 +12,6 @@ import { hasUserSelectedCommandState } from '@/command-menu/states/hasUserSelect import { isCommandMenuClosingState } from '@/command-menu/states/isCommandMenuClosingState'; import { isCommandMenuOpenedState } from '@/command-menu/states/isCommandMenuOpenedState'; import { isCommandMenuOpenedStateV2 } from '@/command-menu/states/isCommandMenuOpenedStateV2'; -import { CommandMenuPages } from '@/command-menu/types/CommandMenuPages'; import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState'; import { viewableRecordIdState } from '@/object-record/record-right-drawer/states/viewableRecordIdState'; import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState'; @@ -28,6 +27,7 @@ import { WORKFLOW_LOGIC_FUNCTION_TAB_LIST_COMPONENT_ID } from '@/workflow/workfl import { WorkflowLogicFunctionTabId } from '@/workflow/workflow-steps/workflow-actions/code-action/types/WorkflowLogicFunctionTabId'; import { useStore } from 'jotai'; import { useRecoilCallback } from 'recoil'; +import { CommandMenuPages } from 'twenty-shared/types'; import { isDefined } from 'twenty-shared/utils'; export const useCommandMenuCloseAnimationCompleteCleanup = () => { diff --git a/packages/twenty-front/src/modules/command-menu/hooks/useCommandMenuContextChips.tsx b/packages/twenty-front/src/modules/command-menu/hooks/useCommandMenuContextChips.tsx index f0597f598e..883fcdff18 100644 --- a/packages/twenty-front/src/modules/command-menu/hooks/useCommandMenuContextChips.tsx +++ b/packages/twenty-front/src/modules/command-menu/hooks/useCommandMenuContextChips.tsx @@ -2,7 +2,7 @@ import { CommandMenuContextRecordChipAvatars } from '@/command-menu/components/C import { useCommandMenuHistory } from '@/command-menu/hooks/useCommandMenuHistory'; import { commandMenuNavigationMorphItemsByPageState } from '@/command-menu/states/commandMenuNavigationMorphItemsByPageState'; import { commandMenuNavigationStackState } from '@/command-menu/states/commandMenuNavigationStackState'; -import { CommandMenuPages } from '@/command-menu/types/CommandMenuPages'; +import { CommandMenuPages } from 'twenty-shared/types'; import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState'; import { recordStoreIdentifiersFamilySelector } from '@/object-record/record-store/states/selectors/recordStoreIdentifiersSelector'; import { recordStoreRecordsSelector } from '@/object-record/record-store/states/selectors/recordStoreRecordsSelector'; diff --git a/packages/twenty-front/src/modules/command-menu/hooks/useCommandMenuHotKeys.ts b/packages/twenty-front/src/modules/command-menu/hooks/useCommandMenuHotKeys.ts index 5b42cf9870..e0cd2dfc09 100644 --- a/packages/twenty-front/src/modules/command-menu/hooks/useCommandMenuHotKeys.ts +++ b/packages/twenty-front/src/modules/command-menu/hooks/useCommandMenuHotKeys.ts @@ -7,7 +7,6 @@ import { useOpenRecordsSearchPageInCommandMenu } from '@/command-menu/hooks/useO import { useSetGlobalCommandMenuContext } from '@/command-menu/hooks/useSetGlobalCommandMenuContext'; import { commandMenuPageState } from '@/command-menu/states/commandMenuPageState'; import { commandMenuSearchState } from '@/command-menu/states/commandMenuSearchState'; -import { CommandMenuPages } from '@/command-menu/types/CommandMenuPages'; import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState'; import { useKeyboardShortcutMenu } from '@/keyboard-shortcut-menu/hooks/useKeyboardShortcutMenu'; import { useGlobalHotkeys } from '@/ui/utilities/hotkey/hooks/useGlobalHotkeys'; @@ -17,6 +16,7 @@ import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled'; import { isNonEmptyString } from '@sniptt/guards'; import { useRecoilValue } from 'recoil'; import { Key } from 'ts-key-enum'; +import { CommandMenuPages } from 'twenty-shared/types'; import { FeatureFlagKey } from '~/generated-metadata/graphql'; export const useCommandMenuHotKeys = () => { diff --git a/packages/twenty-front/src/modules/command-menu/hooks/useNavigateCommandMenu.ts b/packages/twenty-front/src/modules/command-menu/hooks/useNavigateCommandMenu.ts index f21e784b1e..e49d738724 100644 --- a/packages/twenty-front/src/modules/command-menu/hooks/useNavigateCommandMenu.ts +++ b/packages/twenty-front/src/modules/command-menu/hooks/useNavigateCommandMenu.ts @@ -11,12 +11,12 @@ import { hasUserSelectedCommandState } from '@/command-menu/states/hasUserSelect import { isCommandMenuClosingState } from '@/command-menu/states/isCommandMenuClosingState'; import { isCommandMenuOpenedState } from '@/command-menu/states/isCommandMenuOpenedState'; import { isCommandMenuOpenedStateV2 } from '@/command-menu/states/isCommandMenuOpenedStateV2'; -import { type CommandMenuPages } from '@/command-menu/types/CommandMenuPages'; import { MAIN_CONTEXT_STORE_INSTANCE_ID } from '@/context-store/constants/MainContextStoreInstanceId'; import { usePushFocusItemToFocusStack } from '@/ui/utilities/focus/hooks/usePushFocusItemToFocusStack'; import { FocusComponentType } from '@/ui/utilities/focus/types/FocusComponentType'; import { useStore } from 'jotai'; import { useRecoilCallback } from 'recoil'; +import { type CommandMenuPages } from 'twenty-shared/types'; import { type IconComponent } from 'twenty-ui/display'; import { v4 } from 'uuid'; diff --git a/packages/twenty-front/src/modules/command-menu/hooks/useOpenAskAIPageInCommandMenu.ts b/packages/twenty-front/src/modules/command-menu/hooks/useOpenAskAIPageInCommandMenu.ts index 1f64488de7..90b564f352 100644 --- a/packages/twenty-front/src/modules/command-menu/hooks/useOpenAskAIPageInCommandMenu.ts +++ b/packages/twenty-front/src/modules/command-menu/hooks/useOpenAskAIPageInCommandMenu.ts @@ -1,9 +1,9 @@ import { useCommandMenu } from '@/command-menu/hooks/useCommandMenu'; import { isCommandMenuOpenedStateV2 } from '@/command-menu/states/isCommandMenuOpenedStateV2'; -import { CommandMenuPages } from '@/command-menu/types/CommandMenuPages'; import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2'; import { t } from '@lingui/core/macro'; import { useCallback } from 'react'; +import { CommandMenuPages } from 'twenty-shared/types'; import { IconSparkles } from 'twenty-ui/display'; import { v4 } from 'uuid'; diff --git a/packages/twenty-front/src/modules/command-menu/hooks/useOpenCalendarEventInCommandMenu.ts b/packages/twenty-front/src/modules/command-menu/hooks/useOpenCalendarEventInCommandMenu.ts index 08e63f7d96..184178d19d 100644 --- a/packages/twenty-front/src/modules/command-menu/hooks/useOpenCalendarEventInCommandMenu.ts +++ b/packages/twenty-front/src/modules/command-menu/hooks/useOpenCalendarEventInCommandMenu.ts @@ -1,10 +1,10 @@ import { useNavigateCommandMenu } from '@/command-menu/hooks/useNavigateCommandMenu'; import { viewableRecordIdComponentState } from '@/command-menu/pages/record-page/states/viewableRecordIdComponentState'; -import { CommandMenuPages } from '@/command-menu/types/CommandMenuPages'; import { t } from '@lingui/core/macro'; import { useRecoilCallback } from 'recoil'; -import { v4 } from 'uuid'; +import { CommandMenuPages } from 'twenty-shared/types'; import { IconCalendarEvent } from 'twenty-ui/display'; +import { v4 } from 'uuid'; export const useOpenCalendarEventInCommandMenu = () => { const { navigateCommandMenu } = useNavigateCommandMenu(); diff --git a/packages/twenty-front/src/modules/command-menu/hooks/useOpenEmailThreadInCommandMenu.ts b/packages/twenty-front/src/modules/command-menu/hooks/useOpenEmailThreadInCommandMenu.ts index 1720d75d7a..565415bf08 100644 --- a/packages/twenty-front/src/modules/command-menu/hooks/useOpenEmailThreadInCommandMenu.ts +++ b/packages/twenty-front/src/modules/command-menu/hooks/useOpenEmailThreadInCommandMenu.ts @@ -1,10 +1,10 @@ import { useNavigateCommandMenu } from '@/command-menu/hooks/useNavigateCommandMenu'; import { viewableRecordIdComponentState } from '@/command-menu/pages/record-page/states/viewableRecordIdComponentState'; -import { CommandMenuPages } from '@/command-menu/types/CommandMenuPages'; import { t } from '@lingui/core/macro'; import { useRecoilCallback } from 'recoil'; -import { v4 } from 'uuid'; +import { CommandMenuPages } from 'twenty-shared/types'; import { IconMail } from 'twenty-ui/display'; +import { v4 } from 'uuid'; export const useOpenEmailThreadInCommandMenu = () => { const { navigateCommandMenu } = useNavigateCommandMenu(); diff --git a/packages/twenty-front/src/modules/command-menu/hooks/useOpenFrontComponentInCommandMenu.ts b/packages/twenty-front/src/modules/command-menu/hooks/useOpenFrontComponentInCommandMenu.ts index 13ef505509..22ccc914cf 100644 --- a/packages/twenty-front/src/modules/command-menu/hooks/useOpenFrontComponentInCommandMenu.ts +++ b/packages/twenty-front/src/modules/command-menu/hooks/useOpenFrontComponentInCommandMenu.ts @@ -1,7 +1,7 @@ import { useCommandMenu } from '@/command-menu/hooks/useCommandMenu'; import { viewableFrontComponentIdComponentState } from '@/command-menu/pages/front-component/states/viewableFrontComponentIdComponentState'; -import { CommandMenuPages } from '@/command-menu/types/CommandMenuPages'; import { useRecoilCallback } from 'recoil'; +import { CommandMenuPages } from 'twenty-shared/types'; import { type IconComponent } from 'twenty-ui/display'; import { v4 } from 'uuid'; diff --git a/packages/twenty-front/src/modules/command-menu/hooks/useOpenMergeRecordsPageInCommandMenu.tsx b/packages/twenty-front/src/modules/command-menu/hooks/useOpenMergeRecordsPageInCommandMenu.tsx index 009e385f33..cf939c86b5 100644 --- a/packages/twenty-front/src/modules/command-menu/hooks/useOpenMergeRecordsPageInCommandMenu.tsx +++ b/packages/twenty-front/src/modules/command-menu/hooks/useOpenMergeRecordsPageInCommandMenu.tsx @@ -1,10 +1,10 @@ import { useCommandMenuUpdateNavigationMorphItemsByPage } from '@/command-menu/hooks/useCommandMenuUpdateNavigationMorphItemsByPage'; import { useNavigateCommandMenu } from '@/command-menu/hooks/useNavigateCommandMenu'; -import { CommandMenuPages } from '@/command-menu/types/CommandMenuPages'; import { contextStoreCurrentObjectMetadataItemIdComponentState } from '@/context-store/states/contextStoreCurrentObjectMetadataItemIdComponentState'; import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem'; import { useLazyFindManyRecords } from '@/object-record/hooks/useLazyFindManyRecords'; import { useUpsertRecordsInStore } from '@/object-record/record-store/hooks/useUpsertRecordsInStore'; +import { CommandMenuPages } from 'twenty-shared/types'; import { msg, t } from '@lingui/core/macro'; import { useRecoilCallback } from 'recoil'; diff --git a/packages/twenty-front/src/modules/command-menu/hooks/useOpenRecordInCommandMenu.ts b/packages/twenty-front/src/modules/command-menu/hooks/useOpenRecordInCommandMenu.ts index c881ba9e2e..5f5135d492 100644 --- a/packages/twenty-front/src/modules/command-menu/hooks/useOpenRecordInCommandMenu.ts +++ b/packages/twenty-front/src/modules/command-menu/hooks/useOpenRecordInCommandMenu.ts @@ -3,7 +3,6 @@ import { viewableRecordIdComponentState } from '@/command-menu/pages/record-page import { viewableRecordNameSingularComponentState } from '@/command-menu/pages/record-page/states/viewableRecordNameSingularComponentState'; import { commandMenuNavigationMorphItemsByPageState } from '@/command-menu/states/commandMenuNavigationMorphItemsByPageState'; import { commandMenuNavigationStackState } from '@/command-menu/states/commandMenuNavigationStackState'; -import { CommandMenuPages } from '@/command-menu/types/CommandMenuPages'; import { MAIN_CONTEXT_STORE_INSTANCE_ID } from '@/context-store/constants/MainContextStoreInstanceId'; import { contextStoreCurrentObjectMetadataItemIdComponentState } from '@/context-store/states/contextStoreCurrentObjectMetadataItemIdComponentState'; import { contextStoreCurrentViewIdComponentState } from '@/context-store/states/contextStoreCurrentViewIdComponentState'; @@ -17,6 +16,7 @@ import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSi import { getIconColorForObjectType } from '@/object-metadata/utils/getIconColorForObjectType'; import { viewableRecordIdState } from '@/object-record/record-right-drawer/states/viewableRecordIdState'; import { getSnapshotValue } from '@/ui/utilities/state/utils/getSnapshotValue'; +import { CommandMenuPages } from 'twenty-shared/types'; import { useRunWorkflowRunOpeningInCommandMenuSideEffects } from '@/workflow/hooks/useRunWorkflowRunOpeningInCommandMenuSideEffects'; import { useTheme } from '@emotion/react'; diff --git a/packages/twenty-front/src/modules/command-menu/hooks/useOpenRecordsSearchPageInCommandMenu.ts b/packages/twenty-front/src/modules/command-menu/hooks/useOpenRecordsSearchPageInCommandMenu.ts index f0051db1d9..f6d8be0f1b 100644 --- a/packages/twenty-front/src/modules/command-menu/hooks/useOpenRecordsSearchPageInCommandMenu.ts +++ b/packages/twenty-front/src/modules/command-menu/hooks/useOpenRecordsSearchPageInCommandMenu.ts @@ -1,10 +1,10 @@ import { useCommandMenu } from '@/command-menu/hooks/useCommandMenu'; import { isCommandMenuOpenedStateV2 } from '@/command-menu/states/isCommandMenuOpenedStateV2'; -import { CommandMenuPages } from '@/command-menu/types/CommandMenuPages'; import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2'; import { t } from '@lingui/core/macro'; -import { v4 } from 'uuid'; +import { CommandMenuPages } from 'twenty-shared/types'; import { IconSearch } from 'twenty-ui/display'; +import { v4 } from 'uuid'; export const useOpenRecordsSearchPageInCommandMenu = () => { const { navigateCommandMenu } = useCommandMenu(); diff --git a/packages/twenty-front/src/modules/command-menu/hooks/useOpenUpdateMultipleRecordsPageInCommandMenu.tsx b/packages/twenty-front/src/modules/command-menu/hooks/useOpenUpdateMultipleRecordsPageInCommandMenu.tsx index c59daadf8b..bfbfaa9a8d 100644 --- a/packages/twenty-front/src/modules/command-menu/hooks/useOpenUpdateMultipleRecordsPageInCommandMenu.tsx +++ b/packages/twenty-front/src/modules/command-menu/hooks/useOpenUpdateMultipleRecordsPageInCommandMenu.tsx @@ -1,5 +1,5 @@ import { useNavigateCommandMenu } from '@/command-menu/hooks/useNavigateCommandMenu'; -import { CommandMenuPages } from '@/command-menu/types/CommandMenuPages'; +import { CommandMenuPages } from 'twenty-shared/types'; import { msg, t } from '@lingui/core/macro'; import { useRecoilCallback } from 'recoil'; diff --git a/packages/twenty-front/src/modules/command-menu/hooks/useRichTextCommandMenu.ts b/packages/twenty-front/src/modules/command-menu/hooks/useRichTextCommandMenu.ts index 42b0b5c43c..87b0629d8e 100644 --- a/packages/twenty-front/src/modules/command-menu/hooks/useRichTextCommandMenu.ts +++ b/packages/twenty-front/src/modules/command-menu/hooks/useRichTextCommandMenu.ts @@ -1,9 +1,9 @@ import { useCommandMenu } from '@/command-menu/hooks/useCommandMenu'; import { viewableRichTextComponentStateV2 } from '@/command-menu/pages/rich-text-page/states/viewableRichTextComponentStateV2'; -import { CommandMenuPages } from '@/command-menu/types/CommandMenuPages'; -import { useStore } from 'jotai'; import { t } from '@lingui/core/macro'; +import { useStore } from 'jotai'; import { useCallback } from 'react'; +import { CommandMenuPages } from 'twenty-shared/types'; import { IconPencil } from 'twenty-ui/display'; export const useRichTextCommandMenu = () => { diff --git a/packages/twenty-front/src/modules/command-menu/hooks/useWorkflowCommandMenu.ts b/packages/twenty-front/src/modules/command-menu/hooks/useWorkflowCommandMenu.ts index 397d88e813..c3d1f27a24 100644 --- a/packages/twenty-front/src/modules/command-menu/hooks/useWorkflowCommandMenu.ts +++ b/packages/twenty-front/src/modules/command-menu/hooks/useWorkflowCommandMenu.ts @@ -3,12 +3,12 @@ import { commandMenuWorkflowIdComponentState } from '@/command-menu/pages/workfl import { commandMenuWorkflowRunIdComponentState } from '@/command-menu/pages/workflow/states/commandMenuWorkflowRunIdComponentState'; import { commandMenuWorkflowStepIdComponentState } from '@/command-menu/pages/workflow/states/commandMenuWorkflowStepIdComponentState'; import { commandMenuWorkflowVersionIdComponentState } from '@/command-menu/pages/workflow/states/commandMenuWorkflowVersionIdComponentState'; -import { CommandMenuPages } from '@/command-menu/types/CommandMenuPages'; import { type WorkflowRunStepStatus } from '@/workflow/types/Workflow'; import { useSetInitialWorkflowRunRightDrawerTab } from '@/workflow/workflow-diagram/hooks/useSetInitialWorkflowRunRightDrawerTab'; import { workflowSelectedNodeComponentState } from '@/workflow/workflow-diagram/states/workflowSelectedNodeComponentState'; import { t } from '@lingui/core/macro'; import { useRecoilCallback } from 'recoil'; +import { CommandMenuPages } from 'twenty-shared/types'; import { isDefined } from 'twenty-shared/utils'; import { IconBolt, diff --git a/packages/twenty-front/src/modules/command-menu/pages/navigation-menu-item/hooks/useNavigationMenuItemEditOrganizeActions.ts b/packages/twenty-front/src/modules/command-menu/pages/navigation-menu-item/hooks/useNavigationMenuItemEditOrganizeActions.ts index cc8f47c04b..d5a8405988 100644 --- a/packages/twenty-front/src/modules/command-menu/pages/navigation-menu-item/hooks/useNavigationMenuItemEditOrganizeActions.ts +++ b/packages/twenty-front/src/modules/command-menu/pages/navigation-menu-item/hooks/useNavigationMenuItemEditOrganizeActions.ts @@ -5,15 +5,15 @@ import { IconPlus } from 'twenty-ui/display'; import { useCommandMenu } from '@/command-menu/hooks/useCommandMenu'; import { useNavigateCommandMenu } from '@/command-menu/hooks/useNavigateCommandMenu'; import { type OrganizeActionsProps } from '@/command-menu/pages/navigation-menu-item/components/CommandMenuEditOrganizeActions'; -import { CommandMenuPages } from '@/command-menu/types/CommandMenuPages'; import { useNavigationMenuItemMoveRemove } from '@/navigation-menu-item/hooks/useNavigationMenuItemMoveRemove'; import { useNavigationMenuItemsDraftState } from '@/navigation-menu-item/hooks/useNavigationMenuItemsDraftState'; import { useWorkspaceSectionItems } from '@/navigation-menu-item/hooks/useWorkspaceSectionItems'; import { addMenuItemInsertionContextStateV2 } from '@/navigation-menu-item/states/addMenuItemInsertionContextStateV2'; import { selectedNavigationMenuItemInEditModeStateV2 } from '@/navigation-menu-item/states/selectedNavigationMenuItemInEditModeStateV2'; +import { type AddMenuItemInsertionContext } from '@/navigation-menu-item/types/AddMenuItemInsertionContext'; import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2'; import { useSetRecoilStateV2 } from '@/ui/utilities/state/jotai/hooks/useSetRecoilStateV2'; -import { type AddMenuItemInsertionContext } from '@/navigation-menu-item/types/AddMenuItemInsertionContext'; +import { CommandMenuPages } from 'twenty-shared/types'; const getAddMenuItemInsertionContext = ( selectedItem: { id: string; folderId?: string | null }, diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/CommandMenuPageLayoutFieldsSettings.tsx b/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/CommandMenuPageLayoutFieldsSettings.tsx index fbe96d4951..5cde68619a 100644 --- a/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/CommandMenuPageLayoutFieldsSettings.tsx +++ b/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/CommandMenuPageLayoutFieldsSettings.tsx @@ -5,11 +5,11 @@ import { WidgetSettingsFooter } from '@/command-menu/pages/page-layout/component import { useNavigatePageLayoutCommandMenu } from '@/command-menu/pages/page-layout/hooks/useNavigatePageLayoutCommandMenu'; import { usePageLayoutIdForRecordPageLayoutFromContextStoreTargetedRecord } from '@/command-menu/pages/page-layout/hooks/usePageLayoutIdForRecordPageLayoutFromContextStoreTargetedRecord'; import { useWidgetInEditMode } from '@/command-menu/pages/page-layout/hooks/useWidgetInEditMode'; -import { CommandMenuPages } from '@/command-menu/types/CommandMenuPages'; import { useFieldsWidgetGroups } from '@/page-layout/widgets/fields/hooks/useFieldsWidgetGroups'; import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem'; import styled from '@emotion/styled'; import { useLingui } from '@lingui/react/macro'; +import { CommandMenuPages } from 'twenty-shared/types'; import { isDefined } from 'twenty-shared/utils'; import { IconLayoutSidebarRight } from 'twenty-ui/display'; import { type FieldsConfiguration } from '~/generated-metadata/graphql'; diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/CommandMenuPageLayoutWidgetTypeSelect.tsx b/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/CommandMenuPageLayoutWidgetTypeSelect.tsx index daa26ae9e7..d47b65d5b7 100644 --- a/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/CommandMenuPageLayoutWidgetTypeSelect.tsx +++ b/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/CommandMenuPageLayoutWidgetTypeSelect.tsx @@ -6,7 +6,6 @@ import { useNavigatePageLayoutCommandMenu } from '@/command-menu/pages/page-layo import { usePageLayoutIdFromContextStoreTargetedRecord } from '@/command-menu/pages/page-layout/hooks/usePageLayoutFromContextStoreTargetedRecord'; import { getFrontComponentWidgetTypeSelectItemId } from '@/command-menu/pages/page-layout/utils/getFrontComponentWidgetTypeSelectItemId'; import { isExistingWidgetMissingOrDifferentType } from '@/command-menu/pages/page-layout/utils/isExistingWidgetMissingOrDifferentType'; -import { CommandMenuPages } from '@/command-menu/types/CommandMenuPages'; import { FIND_MANY_FRONT_COMPONENTS } from '@/front-components/graphql/queries/findManyFrontComponents'; import { useCreatePageLayoutFrontComponentWidget } from '@/page-layout/hooks/useCreatePageLayoutFrontComponentWidget'; import { useCreatePageLayoutGraphWidget } from '@/page-layout/hooks/useCreatePageLayoutGraphWidget'; @@ -22,6 +21,7 @@ import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/ho import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled'; import { useQuery } from '@apollo/client'; import { t } from '@lingui/core/macro'; +import { CommandMenuPages } from 'twenty-shared/types'; import { isDefined } from 'twenty-shared/utils'; import { IconAlignBoxLeftTop, diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/chart-settings/ChartSettingItem.tsx b/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/chart-settings/ChartSettingItem.tsx index d519ac951f..90e8a49805 100644 --- a/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/chart-settings/ChartSettingItem.tsx +++ b/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/chart-settings/ChartSettingItem.tsx @@ -14,7 +14,6 @@ import { type ChartConfiguration } from '@/command-menu/pages/page-layout/types/ import { CHART_CONFIGURATION_SETTING_IDS } from '@/command-menu/pages/page-layout/types/ChartConfigurationSettingIds'; import { type ChartSettingsItem } from '@/command-menu/pages/page-layout/types/ChartSettingsGroup'; import { isMinMaxRangeValid } from '@/command-menu/pages/page-layout/utils/isMinMaxRangeValid'; -import { CommandMenuPages } from '@/command-menu/types/CommandMenuPages'; import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent'; import { useCloseAnyOpenDropdown } from '@/ui/layout/dropdown/hooks/useCloseAnyOpenDropdown'; import { useOpenDropdown } from '@/ui/layout/dropdown/hooks/useOpenDropdown'; @@ -22,6 +21,7 @@ import { SelectableListItem } from '@/ui/layout/selectable-list/components/Selec import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList'; import { t } from '@lingui/core/macro'; import { isString } from '@sniptt/guards'; +import { CommandMenuPages } from 'twenty-shared/types'; import { isDefined } from 'twenty-shared/utils'; type ChartSettingItemProps = { diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/types/PageLayoutCommandMenuPage.ts b/packages/twenty-front/src/modules/command-menu/pages/page-layout/types/PageLayoutCommandMenuPage.ts index c0c0c5e7d3..02c64c6a61 100644 --- a/packages/twenty-front/src/modules/command-menu/pages/page-layout/types/PageLayoutCommandMenuPage.ts +++ b/packages/twenty-front/src/modules/command-menu/pages/page-layout/types/PageLayoutCommandMenuPage.ts @@ -1,4 +1,4 @@ -import { type CommandMenuPages } from '@/command-menu/types/CommandMenuPages'; +import { type CommandMenuPages } from 'twenty-shared/types'; export type PageLayoutCommandMenuPage = | CommandMenuPages.PageLayoutWidgetTypeSelect diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/getPageLayoutIcon.ts b/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/getPageLayoutIcon.ts index 73789d114e..53478c30fc 100644 --- a/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/getPageLayoutIcon.ts +++ b/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/getPageLayoutIcon.ts @@ -1,5 +1,5 @@ import { type PageLayoutCommandMenuPage } from '@/command-menu/pages/page-layout/types/PageLayoutCommandMenuPage'; -import { CommandMenuPages } from '@/command-menu/types/CommandMenuPages'; +import { CommandMenuPages } from 'twenty-shared/types'; import { assertUnreachable } from 'twenty-shared/utils'; import { IconAppWindow, diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/getPageLayoutPageTitle.ts b/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/getPageLayoutPageTitle.ts index 97d9d21852..a9c07bd1b1 100644 --- a/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/getPageLayoutPageTitle.ts +++ b/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/getPageLayoutPageTitle.ts @@ -1,6 +1,6 @@ import { type PageLayoutCommandMenuPage } from '@/command-menu/pages/page-layout/types/PageLayoutCommandMenuPage'; -import { CommandMenuPages } from '@/command-menu/types/CommandMenuPages'; import { t } from '@lingui/core/macro'; +import { CommandMenuPages } from 'twenty-shared/types'; import { assertUnreachable } from 'twenty-shared/utils'; export const getPageLayoutPageTitle = (page: PageLayoutCommandMenuPage) => { diff --git a/packages/twenty-front/src/modules/command-menu/states/commandMenuNavigationStackState.ts b/packages/twenty-front/src/modules/command-menu/states/commandMenuNavigationStackState.ts index baef93b465..312e979e29 100644 --- a/packages/twenty-front/src/modules/command-menu/states/commandMenuNavigationStackState.ts +++ b/packages/twenty-front/src/modules/command-menu/states/commandMenuNavigationStackState.ts @@ -1,6 +1,6 @@ -import { type CommandMenuPages } from '@/command-menu/types/CommandMenuPages'; -import { type IconComponent } from 'twenty-ui/display'; import { createState } from '@/ui/utilities/state/utils/createState'; +import { type CommandMenuPages } from 'twenty-shared/types'; +import { type IconComponent } from 'twenty-ui/display'; export type CommandMenuNavigationStackItem = { page: CommandMenuPages; diff --git a/packages/twenty-front/src/modules/command-menu/states/commandMenuPageState.ts b/packages/twenty-front/src/modules/command-menu/states/commandMenuPageState.ts index e76205f308..bdf20a5a69 100644 --- a/packages/twenty-front/src/modules/command-menu/states/commandMenuPageState.ts +++ b/packages/twenty-front/src/modules/command-menu/states/commandMenuPageState.ts @@ -1,5 +1,5 @@ -import { CommandMenuPages } from '@/command-menu/types/CommandMenuPages'; import { createState } from '@/ui/utilities/state/utils/createState'; +import { CommandMenuPages } from 'twenty-shared/types'; export const commandMenuPageState = createState({ key: 'command-menu/commandMenuPageState', diff --git a/packages/twenty-front/src/modules/front-components/components/FrontComponentRenderer.tsx b/packages/twenty-front/src/modules/front-components/components/FrontComponentRenderer.tsx index f9e852bf3b..8b5fd2eb92 100644 --- a/packages/twenty-front/src/modules/front-components/components/FrontComponentRenderer.tsx +++ b/packages/twenty-front/src/modules/front-components/components/FrontComponentRenderer.tsx @@ -20,7 +20,7 @@ export const FrontComponentRenderer = ({ const theme = useTheme(); const { enqueueErrorSnackBar } = useSnackBar(); const { executionContext, frontComponentHostCommunicationApi } = - useFrontComponentExecutionContext(); + useFrontComponentExecutionContext({ frontComponentId }); const handleError = useCallback( (error?: Error) => { diff --git a/packages/twenty-front/src/modules/front-components/components/HeadlessFrontComponentMountRoot.tsx b/packages/twenty-front/src/modules/front-components/components/HeadlessFrontComponentMountRoot.tsx new file mode 100644 index 0000000000..211e3ef4fb --- /dev/null +++ b/packages/twenty-front/src/modules/front-components/components/HeadlessFrontComponentMountRoot.tsx @@ -0,0 +1,31 @@ +import { Suspense, lazy } from 'react'; + +import { useRecoilValue } from 'recoil'; + +import { mountedHeadlessFrontComponentIdsState } from '@/front-components/states/mountedHeadlessFrontComponentIdsState'; + +const FrontComponentRenderer = lazy(() => + import('@/front-components/components/FrontComponentRenderer').then( + (module) => ({ default: module.FrontComponentRenderer }), + ), +); + +export const HeadlessFrontComponentMountRoot = () => { + const mountedHeadlessFrontComponentIds = useRecoilValue( + mountedHeadlessFrontComponentIdsState, + ); + + if (mountedHeadlessFrontComponentIds.size === 0) { + return null; + } + + return ( + <> + {[...mountedHeadlessFrontComponentIds].map((frontComponentId) => ( + + + + ))} + + ); +}; diff --git a/packages/twenty-front/src/modules/front-components/graphql/queries/findOneFrontComponent.ts b/packages/twenty-front/src/modules/front-components/graphql/queries/findOneFrontComponent.ts index af80db411d..99d530bc88 100644 --- a/packages/twenty-front/src/modules/front-components/graphql/queries/findOneFrontComponent.ts +++ b/packages/twenty-front/src/modules/front-components/graphql/queries/findOneFrontComponent.ts @@ -7,6 +7,7 @@ export const FIND_ONE_FRONT_COMPONENT = gql` name applicationId builtComponentChecksum + isHeadless applicationTokenPair { applicationAccessToken { token diff --git a/packages/twenty-front/src/modules/front-components/hooks/useFrontComponentExecutionContext.ts b/packages/twenty-front/src/modules/front-components/hooks/useFrontComponentExecutionContext.ts index 77e12464a7..78a3cd3bdd 100644 --- a/packages/twenty-front/src/modules/front-components/hooks/useFrontComponentExecutionContext.ts +++ b/packages/twenty-front/src/modules/front-components/hooks/useFrontComponentExecutionContext.ts @@ -1,4 +1,4 @@ -import { useRecoilValue } from 'recoil'; +import { useRecoilValue, useSetRecoilState } from 'recoil'; import { type FrontComponentExecutionContext, type FrontComponentHostCommunicationApi, @@ -6,14 +6,26 @@ import { import { type AppPath } from 'twenty-shared/types'; import { currentUserState } from '@/auth/states/currentUserState'; +import { useNavigateCommandMenu } from '@/command-menu/hooks/useNavigateCommandMenu'; +import { commandMenuSearchState } from '@/command-menu/states/commandMenuSearchState'; +import { useUnmountHeadlessFrontComponent } from '@/front-components/hooks/useUnmountHeadlessFrontComponent'; +import { useIcons } from 'twenty-ui/display'; import { useNavigateApp } from '~/hooks/useNavigateApp'; -export const useFrontComponentExecutionContext = (): { +export const useFrontComponentExecutionContext = ({ + frontComponentId, +}: { + frontComponentId: string; +}): { executionContext: FrontComponentExecutionContext; frontComponentHostCommunicationApi: FrontComponentHostCommunicationApi; } => { const currentUser = useRecoilValue(currentUserState); const navigateApp = useNavigateApp(); + const { navigateCommandMenu } = useNavigateCommandMenu(); + const setCommandMenuSearchState = useSetRecoilState(commandMenuSearchState); + const { getIcon } = useIcons(); + const unmountHeadlessFrontComponent = useUnmountHeadlessFrontComponent(); const navigate: FrontComponentHostCommunicationApi['navigate'] = async ( to, @@ -29,13 +41,33 @@ export const useFrontComponentExecutionContext = (): { ); }; + const openSidePanelPage: FrontComponentHostCommunicationApi['openSidePanelPage'] = + async ({ page, pageTitle, pageIcon, shouldResetSearchState }) => { + navigateCommandMenu({ + page, + pageTitle, + pageIcon: getIcon(pageIcon), + }); + + if (shouldResetSearchState === true) { + setCommandMenuSearchState(''); + } + }; + const executionContext: FrontComponentExecutionContext = { userId: currentUser?.id ?? null, }; + const unmountFrontComponent: FrontComponentHostCommunicationApi['unmountFrontComponent'] = + async () => { + unmountHeadlessFrontComponent(frontComponentId); + }; + const frontComponentHostCommunicationApi: FrontComponentHostCommunicationApi = { navigate, + openSidePanelPage, + unmountFrontComponent, }; return { diff --git a/packages/twenty-front/src/modules/front-components/hooks/useMountHeadlessFrontComponent.ts b/packages/twenty-front/src/modules/front-components/hooks/useMountHeadlessFrontComponent.ts new file mode 100644 index 0000000000..6f419afac7 --- /dev/null +++ b/packages/twenty-front/src/modules/front-components/hooks/useMountHeadlessFrontComponent.ts @@ -0,0 +1,17 @@ +import { useRecoilCallback } from 'recoil'; + +import { mountedHeadlessFrontComponentIdsState } from '@/front-components/states/mountedHeadlessFrontComponentIdsState'; + +export const useMountHeadlessFrontComponent = () => { + const mountHeadlessFrontComponent = useRecoilCallback( + ({ set }) => + (frontComponentId: string) => { + set(mountedHeadlessFrontComponentIdsState, (previousIds) => + new Set(previousIds).add(frontComponentId), + ); + }, + [], + ); + + return mountHeadlessFrontComponent; +}; diff --git a/packages/twenty-front/src/modules/front-components/hooks/useUnmountHeadlessFrontComponent.ts b/packages/twenty-front/src/modules/front-components/hooks/useUnmountHeadlessFrontComponent.ts new file mode 100644 index 0000000000..121909f012 --- /dev/null +++ b/packages/twenty-front/src/modules/front-components/hooks/useUnmountHeadlessFrontComponent.ts @@ -0,0 +1,19 @@ +import { useRecoilCallback } from 'recoil'; + +import { mountedHeadlessFrontComponentIdsState } from '@/front-components/states/mountedHeadlessFrontComponentIdsState'; + +export const useUnmountHeadlessFrontComponent = () => { + const unmountHeadlessFrontComponent = useRecoilCallback( + ({ set }) => + (frontComponentId: string) => { + set(mountedHeadlessFrontComponentIdsState, (previousIds) => { + const next = new Set(previousIds); + next.delete(frontComponentId); + return next; + }); + }, + [], + ); + + return unmountHeadlessFrontComponent; +}; diff --git a/packages/twenty-front/src/modules/front-components/states/mountedHeadlessFrontComponentIdsState.ts b/packages/twenty-front/src/modules/front-components/states/mountedHeadlessFrontComponentIdsState.ts new file mode 100644 index 0000000000..4b7604afd6 --- /dev/null +++ b/packages/twenty-front/src/modules/front-components/states/mountedHeadlessFrontComponentIdsState.ts @@ -0,0 +1,6 @@ +import { atom } from 'recoil'; + +export const mountedHeadlessFrontComponentIdsState = atom>({ + key: 'mountedHeadlessFrontComponentIdsState', + default: new Set(), +}); diff --git a/packages/twenty-front/src/modules/navigation-menu-item/components/NavigationMenuEditModeBar.tsx b/packages/twenty-front/src/modules/navigation-menu-item/components/NavigationMenuEditModeBar.tsx index c5e8f81a2a..b388c13d46 100644 --- a/packages/twenty-front/src/modules/navigation-menu-item/components/NavigationMenuEditModeBar.tsx +++ b/packages/twenty-front/src/modules/navigation-menu-item/components/NavigationMenuEditModeBar.tsx @@ -1,6 +1,5 @@ import { useCommandMenu } from '@/command-menu/hooks/useCommandMenu'; import { commandMenuPageState } from '@/command-menu/states/commandMenuPageState'; -import { CommandMenuPages } from '@/command-menu/types/CommandMenuPages'; import { useNavigationMenuItemsDraftState } from '@/navigation-menu-item/hooks/useNavigationMenuItemsDraftState'; import { useSaveNavigationMenuItemsDraft } from '@/navigation-menu-item/hooks/useSaveNavigationMenuItemsDraft'; import { isNavigationMenuInEditModeStateV2 } from '@/navigation-menu-item/states/isNavigationMenuInEditModeStateV2'; @@ -16,6 +15,7 @@ import styled from '@emotion/styled'; import { useLingui } from '@lingui/react/macro'; import { useState } from 'react'; import { useRecoilValue } from 'recoil'; +import { CommandMenuPages } from 'twenty-shared/types'; import { IconCheck, useIcons } from 'twenty-ui/display'; import { FeatureFlagKey } from '~/generated-metadata/graphql'; diff --git a/packages/twenty-front/src/modules/navigation-menu-item/components/WorkspaceNavigationMenuItems.tsx b/packages/twenty-front/src/modules/navigation-menu-item/components/WorkspaceNavigationMenuItems.tsx index 1791827d9a..62031e5bc0 100644 --- a/packages/twenty-front/src/modules/navigation-menu-item/components/WorkspaceNavigationMenuItems.tsx +++ b/packages/twenty-front/src/modules/navigation-menu-item/components/WorkspaceNavigationMenuItems.tsx @@ -7,7 +7,6 @@ import { LightIconButton } from 'twenty-ui/input'; import { FeatureFlagKey } from '~/generated-metadata/graphql'; import { useNavigateCommandMenu } from '@/command-menu/hooks/useNavigateCommandMenu'; -import { CommandMenuPages } from '@/command-menu/types/CommandMenuPages'; import { FOLDER_ICON_DEFAULT } from '@/navigation-menu-item/constants/FolderIconDefault'; import { NavigationMenuItemType } from '@/navigation-menu-item/constants/NavigationMenuItemType'; import { useOpenNavigationMenuItemInCommandMenu } from '@/navigation-menu-item/hooks/useOpenNavigationMenuItemInCommandMenu'; @@ -31,6 +30,7 @@ import { useSetRecoilStateV2 } from '@/ui/utilities/state/jotai/hooks/useSetReco import { useStore } from 'jotai'; import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled'; import { isNonEmptyString } from '@sniptt/guards'; +import { CommandMenuPages } from 'twenty-shared/types'; import { isDefined } from 'twenty-shared/utils'; const StyledRightIconsContainer = styled.div` diff --git a/packages/twenty-front/src/modules/navigation-menu-item/hooks/useOpenNavigationMenuItemInCommandMenu.ts b/packages/twenty-front/src/modules/navigation-menu-item/hooks/useOpenNavigationMenuItemInCommandMenu.ts index fef054d522..513b8b118a 100644 --- a/packages/twenty-front/src/modules/navigation-menu-item/hooks/useOpenNavigationMenuItemInCommandMenu.ts +++ b/packages/twenty-front/src/modules/navigation-menu-item/hooks/useOpenNavigationMenuItemInCommandMenu.ts @@ -1,5 +1,5 @@ import { useNavigateCommandMenu } from '@/command-menu/hooks/useNavigateCommandMenu'; -import { CommandMenuPages } from '@/command-menu/types/CommandMenuPages'; +import { CommandMenuPages } from 'twenty-shared/types'; import type { IconComponent } from 'twenty-ui/display'; export const useOpenNavigationMenuItemInCommandMenu = () => { diff --git a/packages/twenty-front/src/modules/page-layout/components/PageLayoutRendererContent.tsx b/packages/twenty-front/src/modules/page-layout/components/PageLayoutRendererContent.tsx index 8a82f5bf99..81145e8359 100644 --- a/packages/twenty-front/src/modules/page-layout/components/PageLayoutRendererContent.tsx +++ b/packages/twenty-front/src/modules/page-layout/components/PageLayoutRendererContent.tsx @@ -1,5 +1,4 @@ import { useNavigatePageLayoutCommandMenu } from '@/command-menu/pages/page-layout/hooks/useNavigatePageLayoutCommandMenu'; -import { CommandMenuPages } from '@/command-menu/types/CommandMenuPages'; import { PageLayoutLeftPanel } from '@/page-layout/components/PageLayoutLeftPanel'; import { PageLayoutTabList } from '@/page-layout/components/PageLayoutTabList'; import { PageLayoutTabListEffect } from '@/page-layout/components/PageLayoutTabListEffect'; @@ -24,6 +23,7 @@ import { useSetRecoilComponentState } from '@/ui/utilities/state/component-state import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2'; import styled from '@emotion/styled'; import { t } from '@lingui/core/macro'; +import { CommandMenuPages } from 'twenty-shared/types'; import { isDefined } from 'twenty-shared/utils'; import { useIsMobile } from 'twenty-ui/utilities'; diff --git a/packages/twenty-front/src/modules/page-layout/components/PageLayoutTabList.tsx b/packages/twenty-front/src/modules/page-layout/components/PageLayoutTabList.tsx index d688940f12..7848fc7147 100644 --- a/packages/twenty-front/src/modules/page-layout/components/PageLayoutTabList.tsx +++ b/packages/twenty-front/src/modules/page-layout/components/PageLayoutTabList.tsx @@ -28,7 +28,6 @@ import { useSetRecoilComponentState } from '@/ui/utilities/state/component-state import { useRecoilComponentStateV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentStateV2'; import { useNavigatePageLayoutCommandMenu } from '@/command-menu/pages/page-layout/hooks/useNavigatePageLayoutCommandMenu'; -import { CommandMenuPages } from '@/command-menu/types/CommandMenuPages'; import { PAGE_LAYOUT_TAB_LIST_DROPPABLE_IDS } from '@/page-layout/components/PageLayoutTabListDroppableIds'; import { PageLayoutTabListReorderableOverflowDropdown } from '@/page-layout/components/PageLayoutTabListReorderableOverflowDropdown'; import { PageLayoutTabListStaticOverflowDropdown } from '@/page-layout/components/PageLayoutTabListStaticOverflowDropdown'; @@ -45,6 +44,7 @@ import { TabListFromUrlOptionalEffect } from '@/ui/layout/tab-list/components/Ta import { type SingleTabProps } from '@/ui/layout/tab-list/types/SingleTabProps'; import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow'; import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; +import { CommandMenuPages } from 'twenty-shared/types'; import { isDefined } from 'twenty-shared/utils'; import { type PageLayoutType } from '~/generated-metadata/graphql'; diff --git a/packages/twenty-front/src/modules/page-layout/components/PageLayoutTabListReorderableOverflowDropdown.tsx b/packages/twenty-front/src/modules/page-layout/components/PageLayoutTabListReorderableOverflowDropdown.tsx index 81799dc596..315cd456e3 100644 --- a/packages/twenty-front/src/modules/page-layout/components/PageLayoutTabListReorderableOverflowDropdown.tsx +++ b/packages/twenty-front/src/modules/page-layout/components/PageLayoutTabListReorderableOverflowDropdown.tsx @@ -9,7 +9,6 @@ import { } from '@hello-pangea/dnd'; import { useNavigatePageLayoutCommandMenu } from '@/command-menu/pages/page-layout/hooks/useNavigatePageLayoutCommandMenu'; -import { CommandMenuPages } from '@/command-menu/types/CommandMenuPages'; import { PAGE_LAYOUT_TAB_LIST_DROPPABLE_IDS } from '@/page-layout/components/PageLayoutTabListDroppableIds'; import { PageLayoutTabListDroppableMoreButton } from '@/page-layout/components/PageLayoutTabListDroppableMoreButton'; import { PageLayoutTabMenuItemSelectAvatar } from '@/page-layout/components/PageLayoutTabMenuItemSelectAvatar'; @@ -29,6 +28,7 @@ import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/com import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; import { useSetRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentState'; import { useContext } from 'react'; +import { CommandMenuPages } from 'twenty-shared/types'; import { type PageLayoutType } from '~/generated-metadata/graphql'; const StyledOverflowDropdownListDraggableWrapper = styled.div` diff --git a/packages/twenty-front/src/modules/page-layout/hooks/__tests__/useCreateWidgetFromClick.test.tsx b/packages/twenty-front/src/modules/page-layout/hooks/__tests__/useCreateWidgetFromClick.test.tsx index c23c899245..27a7f2cfb5 100644 --- a/packages/twenty-front/src/modules/page-layout/hooks/__tests__/useCreateWidgetFromClick.test.tsx +++ b/packages/twenty-front/src/modules/page-layout/hooks/__tests__/useCreateWidgetFromClick.test.tsx @@ -1,11 +1,11 @@ import { useNavigatePageLayoutCommandMenu } from '@/command-menu/pages/page-layout/hooks/useNavigatePageLayoutCommandMenu'; -import { CommandMenuPages } from '@/command-menu/types/CommandMenuPages'; +import { useCreateWidgetFromClick } from '@/page-layout/hooks/useCreateWidgetFromClick'; +import { pageLayoutDraggedAreaComponentState } from '@/page-layout/states/pageLayoutDraggedAreaComponentState'; +import { pageLayoutEditingWidgetIdComponentState } from '@/page-layout/states/pageLayoutEditingWidgetIdComponentState'; import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; import { act, renderHook } from '@testing-library/react'; import { type ReactNode } from 'react'; -import { pageLayoutDraggedAreaComponentState } from '@/page-layout/states/pageLayoutDraggedAreaComponentState'; -import { pageLayoutEditingWidgetIdComponentState } from '@/page-layout/states/pageLayoutEditingWidgetIdComponentState'; -import { useCreateWidgetFromClick } from '@/page-layout/hooks/useCreateWidgetFromClick'; +import { CommandMenuPages } from 'twenty-shared/types'; import { PAGE_LAYOUT_TEST_INSTANCE_ID, PageLayoutTestWrapper, diff --git a/packages/twenty-front/src/modules/page-layout/hooks/__tests__/useEndPageLayoutDragSelection.test.tsx b/packages/twenty-front/src/modules/page-layout/hooks/__tests__/useEndPageLayoutDragSelection.test.tsx index 665bcaca05..3a8cc94c0b 100644 --- a/packages/twenty-front/src/modules/page-layout/hooks/__tests__/useEndPageLayoutDragSelection.test.tsx +++ b/packages/twenty-front/src/modules/page-layout/hooks/__tests__/useEndPageLayoutDragSelection.test.tsx @@ -1,13 +1,13 @@ import { useNavigatePageLayoutCommandMenu } from '@/command-menu/pages/page-layout/hooks/useNavigatePageLayoutCommandMenu'; -import { CommandMenuPages } from '@/command-menu/types/CommandMenuPages'; -import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; -import { act, renderHook } from '@testing-library/react'; -import { type ReactNode } from 'react'; -import { isDefined } from 'twenty-shared/utils'; +import { useEndPageLayoutDragSelection } from '@/page-layout/hooks/useEndPageLayoutDragSelection'; import { pageLayoutDraggedAreaComponentState } from '@/page-layout/states/pageLayoutDraggedAreaComponentState'; import { pageLayoutSelectedCellsComponentState } from '@/page-layout/states/pageLayoutSelectedCellsComponentState'; import { calculateGridBoundsFromSelectedCells } from '@/page-layout/utils/calculateGridBoundsFromSelectedCells'; -import { useEndPageLayoutDragSelection } from '@/page-layout/hooks/useEndPageLayoutDragSelection'; +import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; +import { act, renderHook } from '@testing-library/react'; +import { type ReactNode } from 'react'; +import { CommandMenuPages } from 'twenty-shared/types'; +import { isDefined } from 'twenty-shared/utils'; import { PAGE_LAYOUT_TEST_INSTANCE_ID, PageLayoutTestWrapper, diff --git a/packages/twenty-front/src/modules/page-layout/hooks/useCreateWidgetFromClick.ts b/packages/twenty-front/src/modules/page-layout/hooks/useCreateWidgetFromClick.ts index 3effad3324..d8135f5643 100644 --- a/packages/twenty-front/src/modules/page-layout/hooks/useCreateWidgetFromClick.ts +++ b/packages/twenty-front/src/modules/page-layout/hooks/useCreateWidgetFromClick.ts @@ -1,10 +1,10 @@ import { useNavigatePageLayoutCommandMenu } from '@/command-menu/pages/page-layout/hooks/useNavigatePageLayoutCommandMenu'; -import { CommandMenuPages } from '@/command-menu/types/CommandMenuPages'; import { pageLayoutDraggedAreaComponentState } from '@/page-layout/states/pageLayoutDraggedAreaComponentState'; import { pageLayoutEditingWidgetIdComponentState } from '@/page-layout/states/pageLayoutEditingWidgetIdComponentState'; import { parseCellIdToCoordinates } from '@/page-layout/utils/parseCellIdToCoordinates'; import { useRecoilComponentCallbackState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackState'; import { useRecoilCallback } from 'recoil'; +import { CommandMenuPages } from 'twenty-shared/types'; export const useCreateWidgetFromClick = () => { const pageLayoutDraggedAreaState = useRecoilComponentCallbackState( diff --git a/packages/twenty-front/src/modules/page-layout/hooks/useDuplicatePageLayoutTab.ts b/packages/twenty-front/src/modules/page-layout/hooks/useDuplicatePageLayoutTab.ts index 1e47cc6e73..5574b52ee5 100644 --- a/packages/twenty-front/src/modules/page-layout/hooks/useDuplicatePageLayoutTab.ts +++ b/packages/twenty-front/src/modules/page-layout/hooks/useDuplicatePageLayoutTab.ts @@ -1,6 +1,6 @@ import { useCommandMenu } from '@/command-menu/hooks/useCommandMenu'; import { useNavigatePageLayoutCommandMenu } from '@/command-menu/pages/page-layout/hooks/useNavigatePageLayoutCommandMenu'; -import { CommandMenuPages } from '@/command-menu/types/CommandMenuPages'; +import { CommandMenuPages } from 'twenty-shared/types'; import { PageLayoutComponentInstanceContext } from '@/page-layout/states/contexts/PageLayoutComponentInstanceContext'; import { pageLayoutCurrentLayoutsComponentState } from '@/page-layout/states/pageLayoutCurrentLayoutsComponentState'; import { pageLayoutDraftComponentState } from '@/page-layout/states/pageLayoutDraftComponentState'; diff --git a/packages/twenty-front/src/modules/page-layout/hooks/useEditPageLayoutWidget.ts b/packages/twenty-front/src/modules/page-layout/hooks/useEditPageLayoutWidget.ts index bae3496ef2..70a2a4041f 100644 --- a/packages/twenty-front/src/modules/page-layout/hooks/useEditPageLayoutWidget.ts +++ b/packages/twenty-front/src/modules/page-layout/hooks/useEditPageLayoutWidget.ts @@ -3,8 +3,8 @@ import { useSetRecoilState } from 'recoil'; import { useCommandMenu } from '@/command-menu/hooks/useCommandMenu'; import { commandMenuPageState } from '@/command-menu/states/commandMenuPageState'; -import { CommandMenuPages } from '@/command-menu/types/CommandMenuPages'; import { useSetRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentState'; +import { CommandMenuPages } from 'twenty-shared/types'; import { useNavigatePageLayoutCommandMenu } from '@/command-menu/pages/page-layout/hooks/useNavigatePageLayoutCommandMenu'; import { PageLayoutComponentInstanceContext } from '@/page-layout/states/contexts/PageLayoutComponentInstanceContext'; diff --git a/packages/twenty-front/src/modules/page-layout/hooks/useEndPageLayoutDragSelection.ts b/packages/twenty-front/src/modules/page-layout/hooks/useEndPageLayoutDragSelection.ts index 6ea20b3227..2a202206e6 100644 --- a/packages/twenty-front/src/modules/page-layout/hooks/useEndPageLayoutDragSelection.ts +++ b/packages/twenty-front/src/modules/page-layout/hooks/useEndPageLayoutDragSelection.ts @@ -1,14 +1,14 @@ import { useNavigatePageLayoutCommandMenu } from '@/command-menu/pages/page-layout/hooks/useNavigatePageLayoutCommandMenu'; -import { CommandMenuPages } from '@/command-menu/types/CommandMenuPages'; import { PageLayoutComponentInstanceContext } from '@/page-layout/states/contexts/PageLayoutComponentInstanceContext'; import { pageLayoutDraggedAreaComponentState } from '@/page-layout/states/pageLayoutDraggedAreaComponentState'; import { pageLayoutEditingWidgetIdComponentState } from '@/page-layout/states/pageLayoutEditingWidgetIdComponentState'; +import { pageLayoutSelectedCellsComponentState } from '@/page-layout/states/pageLayoutSelectedCellsComponentState'; +import { calculateGridBoundsFromSelectedCells } from '@/page-layout/utils/calculateGridBoundsFromSelectedCells'; import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow'; import { useRecoilComponentCallbackState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackState'; import { useRecoilCallback } from 'recoil'; +import { CommandMenuPages } from 'twenty-shared/types'; import { isDefined } from 'twenty-shared/utils'; -import { pageLayoutSelectedCellsComponentState } from '@/page-layout/states/pageLayoutSelectedCellsComponentState'; -import { calculateGridBoundsFromSelectedCells } from '@/page-layout/utils/calculateGridBoundsFromSelectedCells'; export const useEndPageLayoutDragSelection = ( pageLayoutIdFromProps?: string, diff --git a/packages/twenty-front/src/modules/page-layout/widgets/components/DashboardWidgetPlaceholder.tsx b/packages/twenty-front/src/modules/page-layout/widgets/components/DashboardWidgetPlaceholder.tsx index d7f49c461b..0655a177ee 100644 --- a/packages/twenty-front/src/modules/page-layout/widgets/components/DashboardWidgetPlaceholder.tsx +++ b/packages/twenty-front/src/modules/page-layout/widgets/components/DashboardWidgetPlaceholder.tsx @@ -1,5 +1,4 @@ import { useNavigatePageLayoutCommandMenu } from '@/command-menu/pages/page-layout/hooks/useNavigatePageLayoutCommandMenu'; -import { CommandMenuPages } from '@/command-menu/types/CommandMenuPages'; import { useSetIsPageLayoutInEditMode } from '@/page-layout/hooks/useSetIsPageLayoutInEditMode'; import { PageLayoutComponentInstanceContext } from '@/page-layout/states/contexts/PageLayoutComponentInstanceContext'; import { isPageLayoutInEditModeComponentState } from '@/page-layout/states/isPageLayoutInEditModeComponentState'; @@ -9,6 +8,7 @@ import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/com import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; import { t } from '@lingui/core/macro'; import { Trans } from '@lingui/react/macro'; +import { CommandMenuPages } from 'twenty-shared/types'; import { AnimatedPlaceholder, AnimatedPlaceholderEmptyContainer, diff --git a/packages/twenty-sdk/src/cli/__tests__/apps/rich-app/__integration__/app-dev/expected-manifest.ts b/packages/twenty-sdk/src/cli/__tests__/apps/rich-app/__integration__/app-dev/expected-manifest.ts index dcbba27124..db7d46bd8f 100644 --- a/packages/twenty-sdk/src/cli/__tests__/apps/rich-app/__integration__/app-dev/expected-manifest.ts +++ b/packages/twenty-sdk/src/cli/__tests__/apps/rich-app/__integration__/app-dev/expected-manifest.ts @@ -46,6 +46,7 @@ export const EXPECTED_MANIFEST: Manifest = { name: 'root-component', sourceComponentPath: 'src/root.front-component.tsx', universalIdentifier: 'a0a1a2a3-a4a5-4000-8000-000000000001', + isHeadless: false, }, { builtComponentPath: 'src/components/card.front-component.mjs', @@ -55,6 +56,7 @@ export const EXPECTED_MANIFEST: Manifest = { name: 'card-component', sourceComponentPath: 'src/components/card.front-component.tsx', universalIdentifier: '88c15ae2-5f87-4a6b-b48f-1974bbe62eb7', + isHeadless: false, }, { builtComponentPath: 'src/components/greeting.front-component.mjs', @@ -64,6 +66,7 @@ export const EXPECTED_MANIFEST: Manifest = { name: 'greeting-component', sourceComponentPath: 'src/components/greeting.front-component.tsx', universalIdentifier: '370ae182-743f-4ecb-b625-7ac48e21f0e5', + isHeadless: false, }, { builtComponentPath: 'src/components/test.front-component.mjs', @@ -73,6 +76,7 @@ export const EXPECTED_MANIFEST: Manifest = { name: 'test-component', sourceComponentPath: 'src/components/test.front-component.tsx', universalIdentifier: 'f1234567-abcd-4000-8000-000000000001', + isHeadless: false, }, ], diff --git a/packages/twenty-sdk/src/cli/__tests__/apps/root-app/__integration__/app-dev/expected-manifest.ts b/packages/twenty-sdk/src/cli/__tests__/apps/root-app/__integration__/app-dev/expected-manifest.ts index eb17241186..aa9efd3245 100644 --- a/packages/twenty-sdk/src/cli/__tests__/apps/root-app/__integration__/app-dev/expected-manifest.ts +++ b/packages/twenty-sdk/src/cli/__tests__/apps/root-app/__integration__/app-dev/expected-manifest.ts @@ -343,6 +343,7 @@ export const EXPECTED_MANIFEST: Manifest = { sourceComponentPath: 'my.front-component.tsx', builtComponentPath: 'my.front-component.mjs', builtComponentChecksum: '[checksum]', + isHeadless: false, }, ], views: [], diff --git a/packages/twenty-sdk/src/cli/utilities/build/manifest/manifest-build.ts b/packages/twenty-sdk/src/cli/utilities/build/manifest/manifest-build.ts index 5163115c82..1ce70842b7 100644 --- a/packages/twenty-sdk/src/cli/utilities/build/manifest/manifest-build.ts +++ b/packages/twenty-sdk/src/cli/utilities/build/manifest/manifest-build.ts @@ -227,6 +227,7 @@ export const buildManifest = async ( sourceComponentPath: relativeFilePath, builtComponentPath: relativeFilePath.replace(/\.tsx?$/, '.mjs'), builtComponentChecksum: '', + isHeadless: rest.isHeadless ?? false, }; frontComponents.push(config); diff --git a/packages/twenty-sdk/src/front-component-renderer/remote/components/FrontComponentWorkerEffect.tsx b/packages/twenty-sdk/src/front-component-renderer/remote/components/FrontComponentWorkerEffect.tsx index 2f2e2c00d3..16fe1efdcb 100644 --- a/packages/twenty-sdk/src/front-component-renderer/remote/components/FrontComponentWorkerEffect.tsx +++ b/packages/twenty-sdk/src/front-component-renderer/remote/components/FrontComponentWorkerEffect.tsx @@ -29,38 +29,32 @@ export const FrontComponentWorkerEffect = ({ setThread, setError, }: FrontComponentWorkerEffectProps) => { - const frontComponentHostCommunicationApiRef = useRef( - frontComponentHostCommunicationApi, - ); - frontComponentHostCommunicationApiRef.current = - frontComponentHostCommunicationApi; + const isInitializedRef = useRef(false); useEffect(() => { + if (isInitializedRef.current) { + return; + } + const newReceiver = new RemoteReceiver({ retain, release }); const worker = createRemoteWorker(); worker.onerror = (event: ErrorEvent) => { const workerError = - event.error ?? - new Error(event.message || 'Unknown worker error'); + event.error ?? new Error(event.message || 'Unknown worker error'); console.error('[FrontComponentRenderer] Worker error:', workerError); setError(workerError); }; - const stableFrontComponentHostCommunicationApi: FrontComponentHostCommunicationApi = - { - navigate: (...args) => - frontComponentHostCommunicationApiRef.current.navigate(...args), - }; - const thread = new ThreadWebWorker< WorkerExports, FrontComponentHostCommunicationApi >(worker, { - exports: stableFrontComponentHostCommunicationApi, + exports: frontComponentHostCommunicationApi, }); + setThread(thread); thread.imports @@ -74,6 +68,7 @@ export const FrontComponentWorkerEffect = ({ }); setReceiver(newReceiver); + isInitializedRef.current = true; return () => { setThread(null); @@ -86,6 +81,7 @@ export const FrontComponentWorkerEffect = ({ setError, setReceiver, setThread, + frontComponentHostCommunicationApi, ]); return null; diff --git a/packages/twenty-sdk/src/front-component-renderer/remote/worker/remote-worker.ts b/packages/twenty-sdk/src/front-component-renderer/remote/worker/remote-worker.ts index 1785846ea0..7c3da1c9ff 100644 --- a/packages/twenty-sdk/src/front-component-renderer/remote/worker/remote-worker.ts +++ b/packages/twenty-sdk/src/front-component-renderer/remote/worker/remote-worker.ts @@ -17,7 +17,6 @@ import { installStylePropertyOnRemoteElements } from '@/front-component-renderer import { patchRemoteElementSetAttribute } from '@/front-component-renderer/remote/utils/patchRemoteElementSetAttribute'; import { HTML_TAG_TO_CUSTOM_ELEMENT_TAG } from '@/sdk/front-component-api/constants/HtmlTagToRemoteComponent'; import { setFrontComponentExecutionContext } from '@/sdk/front-component-api/context/frontComponentContext'; -import { setNavigate } from '@/sdk/front-component-api/functions/navigate'; import { type FrontComponentExecutionContext } from '../../types/FrontComponentExecutionContext'; import { type FrontComponentHostCommunicationApi } from '../../types/FrontComponentHostCommunicationApi'; @@ -89,7 +88,12 @@ const initializeHostCommunicationApi: WorkerExports['initializeHostCommunication async () => { const hostApi = ThreadWebWorker.self.import(); - setNavigate(hostApi.navigate); + + frontComponentHostCommunicationApi.navigate = hostApi.navigate; + frontComponentHostCommunicationApi.openSidePanelPage = + hostApi.openSidePanelPage; + frontComponentHostCommunicationApi.unmountFrontComponent = + hostApi.unmountFrontComponent; }; const updateContext: WorkerExports['updateContext'] = async ( diff --git a/packages/twenty-sdk/src/front-component-renderer/types/FrontComponentHostCommunicationApi.ts b/packages/twenty-sdk/src/front-component-renderer/types/FrontComponentHostCommunicationApi.ts index ef088c2e23..e40b85c0a8 100644 --- a/packages/twenty-sdk/src/front-component-renderer/types/FrontComponentHostCommunicationApi.ts +++ b/packages/twenty-sdk/src/front-component-renderer/types/FrontComponentHostCommunicationApi.ts @@ -1,10 +1,11 @@ -import { type AppPath, type NavigateOptions } from 'twenty-shared/types'; +import { + type NavigateFunction, + type OpenSidePanelPageFunction, + type UnmountFrontComponentFunction, +} from '../../sdk/front-component-api/globals/frontComponentHostCommunicationApi'; export type FrontComponentHostCommunicationApi = { - navigate: ( - to: AppPath, - params?: Record, - queryParams?: Record, - options?: NavigateOptions, - ) => Promise; + navigate: NavigateFunction; + openSidePanelPage: OpenSidePanelPageFunction; + unmountFrontComponent: UnmountFrontComponentFunction; }; diff --git a/packages/twenty-sdk/src/sdk/action/Action.tsx b/packages/twenty-sdk/src/sdk/action/Action.tsx new file mode 100644 index 0000000000..0d3242f4be --- /dev/null +++ b/packages/twenty-sdk/src/sdk/action/Action.tsx @@ -0,0 +1,31 @@ +import { useEffect, useState } from 'react'; + +import { unmountFrontComponent } from '../front-component-api'; + +export type ActionProps = { + execute: () => void | Promise; +}; + +export const Action = ({ execute }: ActionProps) => { + const [hasExecuted, setHasExecuted] = useState(false); + + useEffect(() => { + if (hasExecuted) { + return; + } + + setHasExecuted(true); + + const run = async () => { + try { + await execute(); + } finally { + await unmountFrontComponent(); + } + }; + + run(); + }, [execute, hasExecuted]); + + return null; +}; diff --git a/packages/twenty-sdk/src/sdk/action/ActionLink.tsx b/packages/twenty-sdk/src/sdk/action/ActionLink.tsx new file mode 100644 index 0000000000..454fd72bd8 --- /dev/null +++ b/packages/twenty-sdk/src/sdk/action/ActionLink.tsx @@ -0,0 +1,42 @@ +import { useEffect, useState } from 'react'; + +import { type NavigateOptions } from 'react-router-dom'; +import { type AppPath } from 'twenty-shared/types'; +import { type getAppPath } from 'twenty-shared/utils'; +import { navigate, unmountFrontComponent } from '../front-component-api'; + +export type ActionLinkProps = { + to: T; + params?: Parameters>[1]; + queryParams?: Record; + options?: NavigateOptions; +}; + +export const ActionLink = ({ + to, + params, + queryParams, + options, +}: ActionLinkProps) => { + const [hasExecuted, setHasExecuted] = useState(false); + + useEffect(() => { + if (hasExecuted) { + return; + } + + setHasExecuted(true); + + const run = async () => { + try { + await navigate(to, params, queryParams, options); + } finally { + await unmountFrontComponent(); + } + }; + + run(); + }, [to, params, queryParams, options, hasExecuted]); + + return null; +}; diff --git a/packages/twenty-sdk/src/sdk/action/ActionOpenSidePanelPage.tsx b/packages/twenty-sdk/src/sdk/action/ActionOpenSidePanelPage.tsx new file mode 100644 index 0000000000..bfbffb256b --- /dev/null +++ b/packages/twenty-sdk/src/sdk/action/ActionOpenSidePanelPage.tsx @@ -0,0 +1,52 @@ +import { + openSidePanelPage, + unmountFrontComponent, +} from '@/sdk/front-component-api'; +import { useEffect, useState } from 'react'; + +import { type CommandMenuPages } from 'twenty-shared/types'; + +export type ActionOpenSidePanelPageProps = { + page: CommandMenuPages; + pageTitle: string; + pageIcon: string; + onClick?: () => void; + shouldResetSearchState?: boolean; +}; + +export const ActionOpenSidePanelPage = ({ + page, + pageTitle, + pageIcon, + onClick, + shouldResetSearchState = false, +}: ActionOpenSidePanelPageProps) => { + const [hasExecuted, setHasExecuted] = useState(false); + + useEffect(() => { + if (hasExecuted) { + return; + } + + setHasExecuted(true); + + const run = async () => { + onClick?.(); + + try { + await openSidePanelPage({ + page, + pageTitle, + pageIcon, + shouldResetSearchState, + }); + } finally { + await unmountFrontComponent(); + } + }; + + run(); + }, [page, pageTitle, pageIcon, shouldResetSearchState, onClick, hasExecuted]); + + return null; +}; diff --git a/packages/twenty-sdk/src/sdk/action/index.ts b/packages/twenty-sdk/src/sdk/action/index.ts new file mode 100644 index 0000000000..712559c9fe --- /dev/null +++ b/packages/twenty-sdk/src/sdk/action/index.ts @@ -0,0 +1,6 @@ +export { Action } from './Action'; +export type { ActionProps } from './Action'; +export { ActionLink } from './ActionLink'; +export type { ActionLinkProps } from './ActionLink'; +export { ActionOpenSidePanelPage } from './ActionOpenSidePanelPage'; +export type { ActionOpenSidePanelPageProps } from './ActionOpenSidePanelPage'; diff --git a/packages/twenty-sdk/src/sdk/front-component-api/functions/navigate.ts b/packages/twenty-sdk/src/sdk/front-component-api/functions/navigate.ts index bbc3890eda..64caeeb349 100644 --- a/packages/twenty-sdk/src/sdk/front-component-api/functions/navigate.ts +++ b/packages/twenty-sdk/src/sdk/front-component-api/functions/navigate.ts @@ -1,30 +1,17 @@ -import { type AppPath, type NavigateOptions } from 'twenty-shared/types'; import { isDefined } from 'twenty-shared/utils'; -type NavigateFunction = ( - to: AppPath, - params?: Record, - queryParams?: Record, - options?: NavigateOptions, -) => Promise; - -// State is stored on globalThis so the worker's SDK instance and each -// front component's bundled SDK copy share the same backing store. -const NAVIGATE_KEY = '__twentySdkNavigateFunction__'; - -export const setNavigate = (fn: NavigateFunction): void => { - (globalThis as Record)[NAVIGATE_KEY] = fn; -}; +import { + frontComponentHostCommunicationApi, + type NavigateFunction, +} from '../globals/frontComponentHostCommunicationApi'; export const navigate: NavigateFunction = ( - to: AppPath, - params?: Record, - queryParams?: Record, - options?: NavigateOptions, -): Promise => { - const navigateFunction = (globalThis as Record)[ - NAVIGATE_KEY - ] as NavigateFunction | undefined; + to, + params, + queryParams, + options, +) => { + const navigateFunction = frontComponentHostCommunicationApi.navigate; if (!isDefined(navigateFunction)) { throw new Error('navigateFunction is not set'); diff --git a/packages/twenty-sdk/src/sdk/front-component-api/functions/openSidePanelPage.ts b/packages/twenty-sdk/src/sdk/front-component-api/functions/openSidePanelPage.ts new file mode 100644 index 0000000000..e761bb8641 --- /dev/null +++ b/packages/twenty-sdk/src/sdk/front-component-api/functions/openSidePanelPage.ts @@ -0,0 +1,17 @@ +import { isDefined } from 'twenty-shared/utils'; + +import { + frontComponentHostCommunicationApi, + type OpenSidePanelPageFunction, +} from '../globals/frontComponentHostCommunicationApi'; + +export const openSidePanelPage: OpenSidePanelPageFunction = (params) => { + const openSidePanelPageFunction = + frontComponentHostCommunicationApi.openSidePanelPage; + + if (!isDefined(openSidePanelPageFunction)) { + throw new Error('openSidePanelPageFunction is not set'); + } + + return openSidePanelPageFunction(params); +}; diff --git a/packages/twenty-sdk/src/sdk/front-component-api/functions/unmountFrontComponent.ts b/packages/twenty-sdk/src/sdk/front-component-api/functions/unmountFrontComponent.ts new file mode 100644 index 0000000000..0958c7c4d5 --- /dev/null +++ b/packages/twenty-sdk/src/sdk/front-component-api/functions/unmountFrontComponent.ts @@ -0,0 +1,17 @@ +import { isDefined } from 'twenty-shared/utils'; + +import { + frontComponentHostCommunicationApi, + type UnmountFrontComponentFunction, +} from '../globals/frontComponentHostCommunicationApi'; + +export const unmountFrontComponent: UnmountFrontComponentFunction = () => { + const unmountFrontComponentFunction = + frontComponentHostCommunicationApi.unmountFrontComponent; + + if (!isDefined(unmountFrontComponentFunction)) { + throw new Error('unmountFrontComponentFunction is not set'); + } + + return unmountFrontComponentFunction(); +}; diff --git a/packages/twenty-sdk/src/sdk/front-component-api/globals/frontComponentHostCommunicationApi.ts b/packages/twenty-sdk/src/sdk/front-component-api/globals/frontComponentHostCommunicationApi.ts new file mode 100644 index 0000000000..43167b6dab --- /dev/null +++ b/packages/twenty-sdk/src/sdk/front-component-api/globals/frontComponentHostCommunicationApi.ts @@ -0,0 +1,37 @@ +import { + type AppPath, + type CommandMenuPages, + type NavigateOptions, +} from 'twenty-shared/types'; +import { type getAppPath } from 'twenty-shared/utils'; + +export type NavigateFunction = ( + to: T, + params?: Parameters>[1], + queryParams?: Record, + options?: NavigateOptions, +) => Promise; + +export type OpenSidePanelPageFunction = (params: { + page: CommandMenuPages; + pageTitle: string; + pageIcon?: string; + shouldResetSearchState?: boolean; +}) => Promise; + +export type UnmountFrontComponentFunction = () => Promise; + +export type FrontComponentHostCommunicationApiStore = { + navigate?: NavigateFunction; + openSidePanelPage?: OpenSidePanelPageFunction; + unmountFrontComponent?: UnmountFrontComponentFunction; +}; + +declare global { + var frontComponentHostCommunicationApi: FrontComponentHostCommunicationApiStore; +} + +globalThis.frontComponentHostCommunicationApi ??= {}; + +export const frontComponentHostCommunicationApi = + globalThis.frontComponentHostCommunicationApi; diff --git a/packages/twenty-sdk/src/sdk/front-component-api/index.ts b/packages/twenty-sdk/src/sdk/front-component-api/index.ts index 325b1a3143..fe2a5ae6d7 100644 --- a/packages/twenty-sdk/src/sdk/front-component-api/index.ts +++ b/packages/twenty-sdk/src/sdk/front-component-api/index.ts @@ -1,11 +1,13 @@ export { setFrontComponentExecutionContext } from './context/frontComponentContext'; -export { navigate, setNavigate } from './functions/navigate'; +export { navigate } from './functions/navigate'; +export { openSidePanelPage } from './functions/openSidePanelPage'; +export { unmountFrontComponent } from './functions/unmountFrontComponent'; export { useFrontComponentExecutionContext } from './hooks/useFrontComponentExecutionContext'; export { useUserId } from './hooks/useUserId'; export type { FrontComponentExecutionContext } from './types/FrontComponentExecutionContext'; -export type { AllowedHtmlElement } from './constants/AllowedHtmlElements'; export { ALLOWED_HTML_ELEMENTS } from './constants/AllowedHtmlElements'; +export type { AllowedHtmlElement } from './constants/AllowedHtmlElements'; export { COMMON_HTML_EVENTS } from './constants/CommonHtmlEvents'; export { EVENT_TO_REACT } from './constants/EventToReact'; export { HTML_COMMON_PROPERTIES } from './constants/HtmlCommonProperties'; diff --git a/packages/twenty-sdk/src/sdk/index.ts b/packages/twenty-sdk/src/sdk/index.ts index a6c99f9383..f3dcc751f1 100644 --- a/packages/twenty-sdk/src/sdk/index.ts +++ b/packages/twenty-sdk/src/sdk/index.ts @@ -63,14 +63,26 @@ export { defineSkill } from './skills/define-skill'; export { defineView } from './views/define-view'; export type { ViewConfig } from './views/view-config'; +// Action components for front components +export { Action } from './action'; +export type { ActionProps } from './action'; +export { ActionLink } from './action'; +export type { ActionLinkProps } from './action'; +export { ActionOpenSidePanelPage } from './action'; +export type { ActionOpenSidePanelPageProps } from './action'; + // Front Component API exports export { navigate, + openSidePanelPage, + unmountFrontComponent, useFrontComponentExecutionContext, useUserId, } from './front-component-api'; export type { FrontComponentExecutionContext } from './front-component-api'; +export { AppPath, CommandMenuPages } from 'twenty-shared/types'; + // Front Component Common exports export { ALLOWED_HTML_ELEMENTS, diff --git a/packages/twenty-server/src/database/typeorm/core/migrations/common/1771509478665-add-is-headless-to-front-component.ts b/packages/twenty-server/src/database/typeorm/core/migrations/common/1771509478665-add-is-headless-to-front-component.ts new file mode 100644 index 0000000000..bd8924ffbc --- /dev/null +++ b/packages/twenty-server/src/database/typeorm/core/migrations/common/1771509478665-add-is-headless-to-front-component.ts @@ -0,0 +1,19 @@ +import { type MigrationInterface, type QueryRunner } from 'typeorm'; + +export class AddIsHeadlessToFrontComponent1771509478665 + implements MigrationInterface +{ + name = 'AddIsHeadlessToFrontComponent1771509478665'; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE "core"."frontComponent" ADD "isHeadless" boolean NOT NULL DEFAULT false`, + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE "core"."frontComponent" DROP COLUMN "isHeadless"`, + ); + } +} diff --git a/packages/twenty-server/src/engine/core-modules/application/utils/from-front-component-manifest-to-universal-flat-front-component.util.ts b/packages/twenty-server/src/engine/core-modules/application/utils/from-front-component-manifest-to-universal-flat-front-component.util.ts index 10057415dd..b848d922dc 100644 --- a/packages/twenty-server/src/engine/core-modules/application/utils/from-front-component-manifest-to-universal-flat-front-component.util.ts +++ b/packages/twenty-server/src/engine/core-modules/application/utils/from-front-component-manifest-to-universal-flat-front-component.util.ts @@ -20,6 +20,7 @@ export const fromFrontComponentManifestToUniversalFlatFrontComponent = ({ builtComponentPath: frontComponentManifest.builtComponentPath, componentName: frontComponentManifest.componentName, builtComponentChecksum: frontComponentManifest.builtComponentChecksum, + isHeadless: frontComponentManifest.isHeadless ?? false, createdAt: now, updatedAt: now, }; diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-entity/constant/__tests__/__snapshots__/all-universal-flat-entity-properties-to-compare-and-stringify.constant.spec.ts.snap b/packages/twenty-server/src/engine/metadata-modules/flat-entity/constant/__tests__/__snapshots__/all-universal-flat-entity-properties-to-compare-and-stringify.constant.spec.ts.snap index 3bab5b4738..36ff560e1e 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-entity/constant/__tests__/__snapshots__/all-universal-flat-entity-properties-to-compare-and-stringify.constant.spec.ts.snap +++ b/packages/twenty-server/src/engine/metadata-modules/flat-entity/constant/__tests__/__snapshots__/all-universal-flat-entity-properties-to-compare-and-stringify.constant.spec.ts.snap @@ -59,6 +59,7 @@ exports[`ALL_UNIVERSAL_FLAT_ENTITY_PROPERTIES_TO_COMPARE_AND_STRINGIFY should ma "sourceComponentPath", "builtComponentPath", "componentName", + "isHeadless", ], "propertiesToStringify": [], }, diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-entity/constant/all-entity-properties-configuration-by-metadata-name.constant.ts b/packages/twenty-server/src/engine/metadata-modules/flat-entity/constant/all-entity-properties-configuration-by-metadata-name.constant.ts index ba8edf0d8e..09ba08db54 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-entity/constant/all-entity-properties-configuration-by-metadata-name.constant.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-entity/constant/all-entity-properties-configuration-by-metadata-name.constant.ts @@ -1221,6 +1221,11 @@ export const ALL_ENTITY_PROPERTIES_CONFIGURATION_BY_METADATA_NAME = { toStringify: false, universalProperty: undefined, }, + isHeadless: { + toCompare: true, + toStringify: false, + universalProperty: undefined, + }, createdAt: { toCompare: false, toStringify: false, diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-front-component/constants/flat-front-component-editable-properties.constant.ts b/packages/twenty-server/src/engine/metadata-modules/flat-front-component/constants/flat-front-component-editable-properties.constant.ts index c7ad99815d..561087b04a 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-front-component/constants/flat-front-component-editable-properties.constant.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-front-component/constants/flat-front-component-editable-properties.constant.ts @@ -7,4 +7,5 @@ export const FLAT_FRONT_COMPONENT_EDITABLE_PROPERTIES = [ 'sourceComponentPath', 'builtComponentPath', 'componentName', + 'isHeadless', ] as const satisfies MetadataEntityPropertyName<'frontComponent'>[]; diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-front-component/utils/from-create-front-component-input-to-flat-front-component-to-create.util.ts b/packages/twenty-server/src/engine/metadata-modules/flat-front-component/utils/from-create-front-component-input-to-flat-front-component-to-create.util.ts index c5e8c5579c..5e9d2669ad 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-front-component/utils/from-create-front-component-input-to-flat-front-component-to-create.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-front-component/utils/from-create-front-component-input-to-flat-front-component-to-create.util.ts @@ -33,6 +33,7 @@ export const fromCreateFrontComponentInputToFlatFrontComponentToCreate = ({ builtComponentPath: createFrontComponentInput.builtComponentPath, componentName: createFrontComponentInput.componentName, builtComponentChecksum: createFrontComponentInput.builtComponentChecksum, + isHeadless: false, workspaceId, createdAt: now, updatedAt: now, diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-front-component/utils/from-flat-front-component-to-front-component-dto.util.ts b/packages/twenty-server/src/engine/metadata-modules/flat-front-component/utils/from-flat-front-component-to-front-component-dto.util.ts index d6a17c056e..0d4f1e1dfc 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-front-component/utils/from-flat-front-component-to-front-component-dto.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-front-component/utils/from-flat-front-component-to-front-component-dto.util.ts @@ -20,6 +20,7 @@ export const fromFlatFrontComponentToFrontComponentDto = ( : undefined, workspaceId: flatFrontComponent.workspaceId, applicationId: flatFrontComponent.applicationId, + isHeadless: flatFrontComponent.isHeadless, createdAt: new Date(flatFrontComponent.createdAt), updatedAt: new Date(flatFrontComponent.updatedAt), }); diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-front-component/utils/from-front-component-entity-to-flat-front-component.util.ts b/packages/twenty-server/src/engine/metadata-modules/flat-front-component/utils/from-front-component-entity-to-flat-front-component.util.ts index 388117df09..04ae15c704 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-front-component/utils/from-front-component-entity-to-flat-front-component.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-front-component/utils/from-front-component-entity-to-flat-front-component.util.ts @@ -31,6 +31,7 @@ export const fromFrontComponentEntityToFlatFrontComponent = ({ builtComponentPath: frontComponentEntity.builtComponentPath, componentName: frontComponentEntity.componentName, builtComponentChecksum: frontComponentEntity.builtComponentChecksum, + isHeadless: frontComponentEntity.isHeadless, workspaceId: frontComponentEntity.workspaceId, universalIdentifier: frontComponentEntity.universalIdentifier, applicationId: frontComponentEntity.applicationId, diff --git a/packages/twenty-server/src/engine/metadata-modules/front-component/dtos/front-component.dto.ts b/packages/twenty-server/src/engine/metadata-modules/front-component/dtos/front-component.dto.ts index b73cbdc7f2..8a26cbc81e 100644 --- a/packages/twenty-server/src/engine/metadata-modules/front-component/dtos/front-component.dto.ts +++ b/packages/twenty-server/src/engine/metadata-modules/front-component/dtos/front-component.dto.ts @@ -1,6 +1,7 @@ import { Field, HideField, ObjectType } from '@nestjs/graphql'; import { + IsBoolean, IsDateString, IsNotEmpty, IsOptional, @@ -63,6 +64,10 @@ export class FrontComponentDTO { @Field() updatedAt: Date; + @IsBoolean() + @Field() + isHeadless: boolean; + @Field(() => ApplicationTokenPairDTO, { nullable: true }) applicationTokenPair?: ApplicationTokenPairDTO; } diff --git a/packages/twenty-server/src/engine/metadata-modules/front-component/entities/front-component.entity.ts b/packages/twenty-server/src/engine/metadata-modules/front-component/entities/front-component.entity.ts index 61a286a87d..fb2ce1a1e4 100644 --- a/packages/twenty-server/src/engine/metadata-modules/front-component/entities/front-component.entity.ts +++ b/packages/twenty-server/src/engine/metadata-modules/front-component/entities/front-component.entity.ts @@ -34,6 +34,9 @@ export class FrontComponentEntity @Column({ nullable: false }) builtComponentChecksum: string; + @Column({ default: false }) + isHeadless: boolean; + @CreateDateColumn({ type: 'timestamptz' }) createdAt: Date; diff --git a/packages/twenty-shared/src/application/frontComponentManifestType.ts b/packages/twenty-shared/src/application/frontComponentManifestType.ts index f8ab8eedb1..e85d582220 100644 --- a/packages/twenty-shared/src/application/frontComponentManifestType.ts +++ b/packages/twenty-shared/src/application/frontComponentManifestType.ts @@ -22,5 +22,6 @@ export type FrontComponentManifest = { builtComponentPath: string; builtComponentChecksum: string; componentName: string; + isHeadless?: boolean; command?: FrontComponentCommandManifest; }; diff --git a/packages/twenty-front/src/modules/command-menu/types/CommandMenuPages.ts b/packages/twenty-shared/src/types/CommandMenuPages.ts similarity index 100% rename from packages/twenty-front/src/modules/command-menu/types/CommandMenuPages.ts rename to packages/twenty-shared/src/types/CommandMenuPages.ts diff --git a/packages/twenty-shared/src/types/index.ts b/packages/twenty-shared/src/types/index.ts index 90339dda26..febd5dcfd6 100644 --- a/packages/twenty-shared/src/types/index.ts +++ b/packages/twenty-shared/src/types/index.ts @@ -14,6 +14,7 @@ export { AppBasePath } from './AppBasePath'; export { AppPath } from './AppPath'; export type { Arrayable } from './Arrayable'; export type { ArraySortDirection } from './ArraySortDirection'; +export { CommandMenuPages } from './CommandMenuPages'; export type { ActorMetadata } from './composite-types/actor.composite-type'; export { FieldActorSource,