[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
This commit is contained in:
File diff suppressed because one or more lines are too long
+1
-1
@@ -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 = ({
|
||||
|
||||
+1
-1
@@ -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<string, ActionConfig> = {
|
||||
|
||||
@@ -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 = () => {
|
||||
<PageFavicon />
|
||||
<Outlet />
|
||||
<GlobalFilePreviewModal />
|
||||
<HeadlessFrontComponentMountRoot />
|
||||
</StrictMode>
|
||||
</DialogManager>
|
||||
</DialogComponentInstanceContext.Provider>
|
||||
|
||||
@@ -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';
|
||||
|
||||
+1
@@ -8,6 +8,7 @@ export const COMMAND_MENU_ITEM_FRAGMENT = gql`
|
||||
frontComponent {
|
||||
id
|
||||
name
|
||||
isHeadless
|
||||
}
|
||||
label
|
||||
icon
|
||||
|
||||
+22
-8
@@ -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: (
|
||||
<Action
|
||||
onClick={() =>
|
||||
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,
|
||||
}),
|
||||
);
|
||||
|
||||
|
||||
+1
-1
@@ -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';
|
||||
|
||||
|
||||
@@ -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';
|
||||
|
||||
|
||||
+1
-1
@@ -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';
|
||||
|
||||
@@ -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';
|
||||
|
||||
+1
-1
@@ -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<HTMLInputElement>;
|
||||
|
||||
+1
-1
@@ -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';
|
||||
|
||||
+1
-1
@@ -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';
|
||||
|
||||
+1
-1
@@ -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';
|
||||
|
||||
|
||||
+1
-1
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
+1
-1
@@ -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 }) => (
|
||||
|
||||
+1
-1
@@ -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();
|
||||
|
||||
+1
-1
@@ -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 }) => (
|
||||
|
||||
+1
-1
@@ -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';
|
||||
|
||||
+3
-3
@@ -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();
|
||||
|
||||
+1
-1
@@ -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';
|
||||
|
||||
+1
-1
@@ -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';
|
||||
|
||||
+1
-1
@@ -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';
|
||||
|
||||
+1
-1
@@ -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 = {
|
||||
|
||||
+2
-2
@@ -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'),
|
||||
|
||||
@@ -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 = () => {
|
||||
|
||||
+1
-1
@@ -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 = () => {
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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 = () => {
|
||||
|
||||
@@ -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';
|
||||
|
||||
|
||||
+1
-1
@@ -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';
|
||||
|
||||
|
||||
+2
-2
@@ -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();
|
||||
|
||||
+2
-2
@@ -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();
|
||||
|
||||
+1
-1
@@ -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';
|
||||
|
||||
|
||||
+1
-1
@@ -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';
|
||||
|
||||
@@ -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';
|
||||
|
||||
+2
-2
@@ -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();
|
||||
|
||||
+1
-1
@@ -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';
|
||||
|
||||
@@ -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 = () => {
|
||||
|
||||
@@ -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,
|
||||
|
||||
+2
-2
@@ -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 },
|
||||
|
||||
+1
-1
@@ -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';
|
||||
|
||||
+1
-1
@@ -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,
|
||||
|
||||
+1
-1
@@ -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 = {
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import { type CommandMenuPages } from '@/command-menu/types/CommandMenuPages';
|
||||
import { type CommandMenuPages } from 'twenty-shared/types';
|
||||
|
||||
export type PageLayoutCommandMenuPage =
|
||||
| CommandMenuPages.PageLayoutWidgetTypeSelect
|
||||
|
||||
+1
-1
@@ -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,
|
||||
|
||||
+1
-1
@@ -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) => {
|
||||
|
||||
+2
-2
@@ -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;
|
||||
|
||||
@@ -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<CommandMenuPages>({
|
||||
key: 'command-menu/commandMenuPageState',
|
||||
|
||||
+1
-1
@@ -20,7 +20,7 @@ export const FrontComponentRenderer = ({
|
||||
const theme = useTheme();
|
||||
const { enqueueErrorSnackBar } = useSnackBar();
|
||||
const { executionContext, frontComponentHostCommunicationApi } =
|
||||
useFrontComponentExecutionContext();
|
||||
useFrontComponentExecutionContext({ frontComponentId });
|
||||
|
||||
const handleError = useCallback(
|
||||
(error?: Error) => {
|
||||
|
||||
+31
@@ -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) => (
|
||||
<Suspense key={frontComponentId} fallback={null}>
|
||||
<FrontComponentRenderer frontComponentId={frontComponentId} />
|
||||
</Suspense>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
};
|
||||
+1
@@ -7,6 +7,7 @@ export const FIND_ONE_FRONT_COMPONENT = gql`
|
||||
name
|
||||
applicationId
|
||||
builtComponentChecksum
|
||||
isHeadless
|
||||
applicationTokenPair {
|
||||
applicationAccessToken {
|
||||
token
|
||||
|
||||
+34
-2
@@ -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 {
|
||||
|
||||
+17
@@ -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;
|
||||
};
|
||||
+19
@@ -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;
|
||||
};
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
import { atom } from 'recoil';
|
||||
|
||||
export const mountedHeadlessFrontComponentIdsState = atom<Set<string>>({
|
||||
key: 'mountedHeadlessFrontComponentIdsState',
|
||||
default: new Set(),
|
||||
});
|
||||
+1
-1
@@ -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';
|
||||
|
||||
|
||||
+1
-1
@@ -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`
|
||||
|
||||
+1
-1
@@ -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 = () => {
|
||||
|
||||
+1
-1
@@ -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';
|
||||
|
||||
|
||||
@@ -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';
|
||||
|
||||
|
||||
+1
-1
@@ -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`
|
||||
|
||||
+4
-4
@@ -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,
|
||||
|
||||
+6
-6
@@ -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,
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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';
|
||||
|
||||
+3
-3
@@ -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,
|
||||
|
||||
+1
-1
@@ -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,
|
||||
|
||||
+4
@@ -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,
|
||||
},
|
||||
],
|
||||
|
||||
|
||||
+1
@@ -343,6 +343,7 @@ export const EXPECTED_MANIFEST: Manifest = {
|
||||
sourceComponentPath: 'my.front-component.tsx',
|
||||
builtComponentPath: 'my.front-component.mjs',
|
||||
builtComponentChecksum: '[checksum]',
|
||||
isHeadless: false,
|
||||
},
|
||||
],
|
||||
views: [],
|
||||
|
||||
@@ -227,6 +227,7 @@ export const buildManifest = async (
|
||||
sourceComponentPath: relativeFilePath,
|
||||
builtComponentPath: relativeFilePath.replace(/\.tsx?$/, '.mjs'),
|
||||
builtComponentChecksum: '',
|
||||
isHeadless: rest.isHeadless ?? false,
|
||||
};
|
||||
|
||||
frontComponents.push(config);
|
||||
|
||||
+10
-14
@@ -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;
|
||||
|
||||
@@ -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<FrontComponentHostCommunicationApi>();
|
||||
setNavigate(hostApi.navigate);
|
||||
|
||||
frontComponentHostCommunicationApi.navigate = hostApi.navigate;
|
||||
frontComponentHostCommunicationApi.openSidePanelPage =
|
||||
hostApi.openSidePanelPage;
|
||||
frontComponentHostCommunicationApi.unmountFrontComponent =
|
||||
hostApi.unmountFrontComponent;
|
||||
};
|
||||
|
||||
const updateContext: WorkerExports['updateContext'] = async (
|
||||
|
||||
+8
-7
@@ -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<string, string | null>,
|
||||
queryParams?: Record<string, unknown>,
|
||||
options?: NavigateOptions,
|
||||
) => Promise<void>;
|
||||
navigate: NavigateFunction;
|
||||
openSidePanelPage: OpenSidePanelPageFunction;
|
||||
unmountFrontComponent: UnmountFrontComponentFunction;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
import { unmountFrontComponent } from '../front-component-api';
|
||||
|
||||
export type ActionProps = {
|
||||
execute: () => void | Promise<void>;
|
||||
};
|
||||
|
||||
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;
|
||||
};
|
||||
@@ -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<T extends AppPath> = {
|
||||
to: T;
|
||||
params?: Parameters<typeof getAppPath<T>>[1];
|
||||
queryParams?: Record<string, any>;
|
||||
options?: NavigateOptions;
|
||||
};
|
||||
|
||||
export const ActionLink = <T extends AppPath>({
|
||||
to,
|
||||
params,
|
||||
queryParams,
|
||||
options,
|
||||
}: ActionLinkProps<T>) => {
|
||||
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;
|
||||
};
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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';
|
||||
@@ -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<string, string | null>,
|
||||
queryParams?: Record<string, unknown>,
|
||||
options?: NavigateOptions,
|
||||
) => Promise<void>;
|
||||
|
||||
// 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<string, unknown>)[NAVIGATE_KEY] = fn;
|
||||
};
|
||||
import {
|
||||
frontComponentHostCommunicationApi,
|
||||
type NavigateFunction,
|
||||
} from '../globals/frontComponentHostCommunicationApi';
|
||||
|
||||
export const navigate: NavigateFunction = (
|
||||
to: AppPath,
|
||||
params?: Record<string, string | null>,
|
||||
queryParams?: Record<string, unknown>,
|
||||
options?: NavigateOptions,
|
||||
): Promise<void> => {
|
||||
const navigateFunction = (globalThis as Record<string, unknown>)[
|
||||
NAVIGATE_KEY
|
||||
] as NavigateFunction | undefined;
|
||||
to,
|
||||
params,
|
||||
queryParams,
|
||||
options,
|
||||
) => {
|
||||
const navigateFunction = frontComponentHostCommunicationApi.navigate;
|
||||
|
||||
if (!isDefined(navigateFunction)) {
|
||||
throw new Error('navigateFunction is not set');
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
@@ -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();
|
||||
};
|
||||
+37
@@ -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 = <T extends AppPath>(
|
||||
to: T,
|
||||
params?: Parameters<typeof getAppPath<T>>[1],
|
||||
queryParams?: Record<string, any>,
|
||||
options?: NavigateOptions,
|
||||
) => Promise<void>;
|
||||
|
||||
export type OpenSidePanelPageFunction = (params: {
|
||||
page: CommandMenuPages;
|
||||
pageTitle: string;
|
||||
pageIcon?: string;
|
||||
shouldResetSearchState?: boolean;
|
||||
}) => Promise<void>;
|
||||
|
||||
export type UnmountFrontComponentFunction = () => Promise<void>;
|
||||
|
||||
export type FrontComponentHostCommunicationApiStore = {
|
||||
navigate?: NavigateFunction;
|
||||
openSidePanelPage?: OpenSidePanelPageFunction;
|
||||
unmountFrontComponent?: UnmountFrontComponentFunction;
|
||||
};
|
||||
|
||||
declare global {
|
||||
var frontComponentHostCommunicationApi: FrontComponentHostCommunicationApiStore;
|
||||
}
|
||||
|
||||
globalThis.frontComponentHostCommunicationApi ??= {};
|
||||
|
||||
export const frontComponentHostCommunicationApi =
|
||||
globalThis.frontComponentHostCommunicationApi;
|
||||
@@ -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';
|
||||
|
||||
@@ -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,
|
||||
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
import { type MigrationInterface, type QueryRunner } from 'typeorm';
|
||||
|
||||
export class AddIsHeadlessToFrontComponent1771509478665
|
||||
implements MigrationInterface
|
||||
{
|
||||
name = 'AddIsHeadlessToFrontComponent1771509478665';
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "core"."frontComponent" ADD "isHeadless" boolean NOT NULL DEFAULT false`,
|
||||
);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "core"."frontComponent" DROP COLUMN "isHeadless"`,
|
||||
);
|
||||
}
|
||||
}
|
||||
+1
@@ -20,6 +20,7 @@ export const fromFrontComponentManifestToUniversalFlatFrontComponent = ({
|
||||
builtComponentPath: frontComponentManifest.builtComponentPath,
|
||||
componentName: frontComponentManifest.componentName,
|
||||
builtComponentChecksum: frontComponentManifest.builtComponentChecksum,
|
||||
isHeadless: frontComponentManifest.isHeadless ?? false,
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
};
|
||||
|
||||
+1
@@ -59,6 +59,7 @@ exports[`ALL_UNIVERSAL_FLAT_ENTITY_PROPERTIES_TO_COMPARE_AND_STRINGIFY should ma
|
||||
"sourceComponentPath",
|
||||
"builtComponentPath",
|
||||
"componentName",
|
||||
"isHeadless",
|
||||
],
|
||||
"propertiesToStringify": [],
|
||||
},
|
||||
|
||||
+5
@@ -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,
|
||||
|
||||
+1
@@ -7,4 +7,5 @@ export const FLAT_FRONT_COMPONENT_EDITABLE_PROPERTIES = [
|
||||
'sourceComponentPath',
|
||||
'builtComponentPath',
|
||||
'componentName',
|
||||
'isHeadless',
|
||||
] as const satisfies MetadataEntityPropertyName<'frontComponent'>[];
|
||||
|
||||
+1
@@ -33,6 +33,7 @@ export const fromCreateFrontComponentInputToFlatFrontComponentToCreate = ({
|
||||
builtComponentPath: createFrontComponentInput.builtComponentPath,
|
||||
componentName: createFrontComponentInput.componentName,
|
||||
builtComponentChecksum: createFrontComponentInput.builtComponentChecksum,
|
||||
isHeadless: false,
|
||||
workspaceId,
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
|
||||
+1
@@ -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),
|
||||
});
|
||||
|
||||
+1
@@ -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,
|
||||
|
||||
+5
@@ -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;
|
||||
}
|
||||
|
||||
+3
@@ -34,6 +34,9 @@ export class FrontComponentEntity
|
||||
@Column({ nullable: false })
|
||||
builtComponentChecksum: string;
|
||||
|
||||
@Column({ default: false })
|
||||
isHeadless: boolean;
|
||||
|
||||
@CreateDateColumn({ type: 'timestamptz' })
|
||||
createdAt: Date;
|
||||
|
||||
|
||||
@@ -22,5 +22,6 @@ export type FrontComponentManifest = {
|
||||
builtComponentPath: string;
|
||||
builtComponentChecksum: string;
|
||||
componentName: string;
|
||||
isHeadless?: boolean;
|
||||
command?: FrontComponentCommandManifest;
|
||||
};
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user