Side Panel Sub Page Framework® (#18579)

Replace hard-coded implementations for sub pages in the side panel with
a proper framework
This commit is contained in:
Baptiste Devessier
2026-03-12 16:17:29 +01:00
committed by GitHub
parent 5bfa4c5c39
commit 2a6fcfcfb3
45 changed files with 824 additions and 551 deletions
@@ -20,14 +20,8 @@ import {
} from '~/testing/mock-data/users';
import { sleep } from '~/utils/sleep';
import { CommandMenuComponentInstanceContext } from '@/command-menu/states/contexts/CommandMenuComponentInstanceContext';
import { currentUserWorkspaceState } from '@/auth/states/currentUserWorkspaceState';
import { SidePanelRouter } from '@/side-panel/components/SidePanelRouter';
import { SIDE_PANEL_COMPONENT_INSTANCE_ID } from '@/side-panel/constants/SidePanelComponentInstanceId';
import { SIDE_PANEL_FOCUS_ID } from '@/side-panel/constants/SidePanelFocusId';
import { type SidePanelRootPage } from '@/side-panel/pages/root/components/SidePanelRootPage';
import { sidePanelNavigationStackState } from '@/side-panel/states/sidePanelNavigationStackState';
import { isSidePanelOpenedState } from '@/side-panel/states/isSidePanelOpenedState';
import { CommandMenuComponentInstanceContext } from '@/command-menu/states/contexts/CommandMenuComponentInstanceContext';
import { MAIN_CONTEXT_STORE_INSTANCE_ID } from '@/context-store/constants/MainContextStoreInstanceId';
import { contextStoreCurrentObjectMetadataItemIdComponentState } from '@/context-store/states/contextStoreCurrentObjectMetadataItemIdComponentState';
import { contextStoreCurrentViewTypeComponentState } from '@/context-store/states/contextStoreCurrentViewTypeComponentState';
@@ -35,11 +29,19 @@ import { ContextStoreComponentInstanceContext } from '@/context-store/states/con
import { ContextStoreViewType } from '@/context-store/types/ContextStoreViewType';
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
import { RecordComponentInstanceContextsWrapper } from '@/object-record/components/RecordComponentInstanceContextsWrapper';
import { SidePanelRouter } from '@/side-panel/components/SidePanelRouter';
import { SIDE_PANEL_COMPONENT_INSTANCE_ID } from '@/side-panel/constants/SidePanelComponentInstanceId';
import { SIDE_PANEL_FOCUS_ID } from '@/side-panel/constants/SidePanelFocusId';
import { type SidePanelRootPage } from '@/side-panel/pages/root/components/SidePanelRootPage';
import { isSidePanelOpenedState } from '@/side-panel/states/isSidePanelOpenedState';
import { sidePanelNavigationStackState } from '@/side-panel/states/sidePanelNavigationStackState';
import { sidePanelPageInfoState } from '@/side-panel/states/sidePanelPageInfoState';
import { sidePanelPageState } from '@/side-panel/states/sidePanelPageState';
import { ViewComponentInstanceContext } from '@/views/states/contexts/ViewComponentInstanceContext';
import { HttpResponse, graphql } from 'msw';
import { SidePanelPages } from 'twenty-shared/types';
import { isDefined } from 'twenty-shared/utils';
import { IconDotsVertical } from 'twenty-ui/display';
import { IconDotsVertical, IconPlus } from 'twenty-ui/display';
import { JestContextStoreSetter } from '~/testing/jest/JestContextStoreSetter';
const openTimeout = 50;
@@ -87,6 +89,10 @@ const meta: Meta<typeof SidePanelRootPage> = {
mockedUserData.currentUserWorkspace,
);
jotaiStore.set(isSidePanelOpenedState.atom, true);
jotaiStore.set(sidePanelPageInfoState.atom, {
title: 'Command Menu',
instanceId: SIDE_PANEL_COMPONENT_INSTANCE_ID,
});
jotaiStore.set(sidePanelNavigationStackState.atom, [
{
page: SidePanelPages.Root,
@@ -244,3 +250,43 @@ export const NoResultsSearchFallback: Story = {
// expect(await canvas.findByText('Search records')).toBeVisible();
// },
// };
export const SubPageNavigation: Story = {
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
const objectButton = await canvas.findByText('Object');
expect(objectButton).toBeVisible();
await userEvent.click(objectButton);
expect(await canvas.findByText('Pick an object')).toBeVisible();
const backButton = await canvas.findByRole('button', { name: 'Go back' });
await userEvent.click(backButton);
expect(await canvas.findByText('Object')).toBeVisible();
},
decorators: [
(Story) => {
jotaiStore.set(
sidePanelPageState.atom,
SidePanelPages.NavigationMenuAddItem,
);
jotaiStore.set(sidePanelPageInfoState.atom, {
title: 'Add item',
instanceId: SIDE_PANEL_COMPONENT_INSTANCE_ID,
});
jotaiStore.set(sidePanelNavigationStackState.atom, [
{
page: SidePanelPages.NavigationMenuAddItem,
pageTitle: 'Add item',
pageIcon: IconPlus,
pageId: '1',
},
]);
return <Story />;
},
],
};
@@ -1,20 +1,20 @@
import { SIDE_PANEL_COMPONENT_INSTANCE_ID } from '@/side-panel/constants/SidePanelComponentInstanceId';
import { SIDE_PANEL_FOCUS_ID } from '@/side-panel/constants/SidePanelFocusId';
import { useSidePanelMenu } from '@/side-panel/hooks/useSidePanelMenu';
import { useSidePanelHistory } from '@/side-panel/hooks/useSidePanelHistory';
import { useOpenAskAIPageInSidePanel } from '@/side-panel/hooks/useOpenAskAIPageInSidePanel';
import { useOpenRecordsSearchPageInSidePanel } from '@/side-panel/hooks/useOpenRecordsSearchPageInSidePanel';
import { useSetGlobalCommandMenuContext } from '@/command-menu/hooks/useSetGlobalCommandMenuContext';
import { sidePanelPageState } from '@/side-panel/states/sidePanelPageState';
import { sidePanelSearchState } from '@/side-panel/states/sidePanelSearchState';
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
import { useKeyboardShortcutMenu } from '@/keyboard-shortcut-menu/hooks/useKeyboardShortcutMenu';
import { SIDE_PANEL_COMPONENT_INSTANCE_ID } from '@/side-panel/constants/SidePanelComponentInstanceId';
import { SIDE_PANEL_FOCUS_ID } from '@/side-panel/constants/SidePanelFocusId';
import { useOpenAskAIPageInSidePanel } from '@/side-panel/hooks/useOpenAskAIPageInSidePanel';
import { useOpenRecordsSearchPageInSidePanel } from '@/side-panel/hooks/useOpenRecordsSearchPageInSidePanel';
import { useSidePanelHistory } from '@/side-panel/hooks/useSidePanelHistory';
import { useSidePanelMenu } from '@/side-panel/hooks/useSidePanelMenu';
import { sidePanelPageState } from '@/side-panel/states/sidePanelPageState';
import { sidePanelSearchState } from '@/side-panel/states/sidePanelSearchState';
import { useGlobalHotkeys } from '@/ui/utilities/hotkey/hooks/useGlobalHotkeys';
import { useHotkeysOnFocusedElement } from '@/ui/utilities/hotkey/hooks/useHotkeysOnFocusedElement';
import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue';
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
import { isNonEmptyString } from '@sniptt/guards';
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
import { Key } from 'ts-key-enum';
import { SidePanelPages } from 'twenty-shared/types';
import { FeatureFlagKey } from '~/generated-metadata/graphql';
@@ -26,7 +26,8 @@ export const useCommandMenuHotKeys = () => {
const { openAskAIPage } = useOpenAskAIPageInSidePanel();
const { goBackFromSidePanel } = useSidePanelHistory();
const { goBackFromSidePanel, goBackOneSubPageOrMainPage } =
useSidePanelHistory();
const { setGlobalCommandMenuContext } = useSetGlobalCommandMenuContext();
@@ -108,7 +109,7 @@ export const useCommandMenuHotKeys = () => {
setGlobalCommandMenuContext();
}
if (sidePanelPage !== SidePanelPages.Root) {
goBackFromSidePanel();
goBackOneSubPageOrMainPage();
}
},
focusId: SIDE_PANEL_FOCUS_ID,
@@ -116,7 +117,7 @@ export const useCommandMenuHotKeys = () => {
sidePanelPage,
sidePanelSearch,
contextStoreTargetedRecordsRule,
goBackFromSidePanel,
goBackOneSubPageOrMainPage,
setGlobalCommandMenuContext,
],
options: {
@@ -5,9 +5,9 @@ import { sidePanelPageState } from '@/side-panel/states/sidePanelPageState';
import { useSetAtomComponentState } from '@/ui/utilities/state/jotai/hooks/useSetAtomComponentState';
import { SidePanelPages } from 'twenty-shared/types';
import { useNavigatePageLayoutSidePanel } from '@/side-panel/pages/page-layout/hooks/useNavigatePageLayoutSidePanel';
import { PageLayoutComponentInstanceContext } from '@/page-layout/states/contexts/PageLayoutComponentInstanceContext';
import { pageLayoutEditingWidgetIdComponentState } from '@/page-layout/states/pageLayoutEditingWidgetIdComponentState';
import { useNavigatePageLayoutSidePanel } from '@/side-panel/pages/page-layout/hooks/useNavigatePageLayoutSidePanel';
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
import { useSetAtomState } from '@/ui/utilities/state/jotai/hooks/useSetAtomState';
import { t } from '@lingui/core/macro';
@@ -36,14 +36,13 @@ export const useEditPageLayoutWidget = (pageLayoutIdFromProps?: string) => {
widgetId: string;
widgetType: WidgetType;
}) => {
setPageLayoutEditingWidgetId(widgetId);
if (widgetType === WidgetType.IFRAME) {
navigatePageLayoutSidePanel({
sidePanelPage: SidePanelPages.PageLayoutIframeSettings,
pageTitle: t`Edit iFrame`,
resetNavigationStack: true,
});
setPageLayoutEditingWidgetId(widgetId);
return;
}
@@ -53,6 +52,7 @@ export const useEditPageLayoutWidget = (pageLayoutIdFromProps?: string) => {
pageTitle: t`Edit Graph`,
resetNavigationStack: true,
});
setPageLayoutEditingWidgetId(widgetId);
return;
}
@@ -62,6 +62,7 @@ export const useEditPageLayoutWidget = (pageLayoutIdFromProps?: string) => {
pageTitle: t`Edit Fields`,
resetNavigationStack: true,
});
setPageLayoutEditingWidgetId(widgetId);
return;
}
@@ -5,6 +5,9 @@ import {
OverflowingTextWithTooltip,
} from 'twenty-ui/display';
import { NavigationMenuItemType } from '@/navigation-menu-item/constants/NavigationMenuItemType';
import { useWorkspaceSectionItems } from '@/navigation-menu-item/hooks/useWorkspaceSectionItems';
import { selectedNavigationMenuItemInEditModeState } from '@/navigation-menu-item/states/selectedNavigationMenuItemInEditModeState';
import { SidePanelAskAIInfo } from '@/side-panel/components/SidePanelAskAIInfo';
import { SidePanelFolderInfo } from '@/side-panel/components/SidePanelFolderInfo';
import { SidePanelLinkInfo } from '@/side-panel/components/SidePanelLinkInfo';
@@ -14,9 +17,6 @@ import { SidePanelPageInfoLayout } from '@/side-panel/components/SidePanelPageIn
import { SidePanelPageLayoutInfo } from '@/side-panel/components/SidePanelPageLayoutInfo';
import { SidePanelRecordInfo } from '@/side-panel/components/SidePanelRecordInfo';
import { SidePanelWorkflowStepInfo } from '@/side-panel/components/SidePanelWorkflowStepInfo';
import { NavigationMenuItemType } from '@/navigation-menu-item/constants/NavigationMenuItemType';
import { useWorkspaceSectionItems } from '@/navigation-menu-item/hooks/useWorkspaceSectionItems';
import { selectedNavigationMenuItemInEditModeState } from '@/navigation-menu-item/states/selectedNavigationMenuItemInEditModeState';
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
import { SidePanelPages } from 'twenty-shared/types';
@@ -98,11 +98,9 @@ export const SidePanelPageInfo = ({ pageChip }: SidePanelPageInfoProps) => {
? [
SidePanelPages.PageLayoutWidgetTypeSelect,
SidePanelPages.PageLayoutGraphTypeSelect,
SidePanelPages.PageLayoutGraphFilter,
SidePanelPages.PageLayoutIframeSettings,
SidePanelPages.PageLayoutTabSettings,
SidePanelPages.PageLayoutFieldsSettings,
SidePanelPages.PageLayoutFieldsLayout,
].includes(pageChip.page?.page)
: false;
@@ -1,5 +1,6 @@
import { CommandMenuContextProvider } from '@/command-menu-item/contexts/CommandMenuContextProvider';
import { SidePanelContainer } from '@/side-panel/components/SidePanelContainer';
import { SidePanelSubPageRouter } from '@/side-panel/components/SidePanelSubPageRouter';
import { SidePanelTopBar } from '@/side-panel/components/SidePanelTopBar';
import { SIDE_PANEL_PAGES_CONFIG } from '@/side-panel/constants/SidePanelPagesConfig';
import { SidePanelPageComponentInstanceContext } from '@/side-panel/states/contexts/SidePanelPageComponentInstanceContext';
@@ -56,7 +57,9 @@ export const SidePanelRouter = () => {
displayType="listItem"
containerType="command-menu-list"
>
{sidePanelPageComponent}
<SidePanelSubPageRouter>
{sidePanelPageComponent}
</SidePanelSubPageRouter>
</CommandMenuContextProvider>
</StyledSidePanelContent>
</SidePanelPageComponentInstanceContext.Provider>
@@ -0,0 +1,54 @@
import { SIDE_PANEL_SUB_PAGES_CONFIG } from '@/side-panel/constants/SidePanelSubPagesConfig';
import { useSidePanelSubPageHistory } from '@/side-panel/hooks/useSidePanelSubPageHistory';
import { SidePanelSubPageNavigationHeader } from '@/side-panel/pages/common/components/SidePanelSubPageNavigationHeader';
import { styled } from '@linaria/react';
import React, { type ReactNode } from 'react';
import { isDefined } from 'twenty-shared/utils';
const StyledSubPageContainer = styled.div`
display: flex;
flex-direction: column;
height: 100%;
`;
type SidePanelSubPageRouterProps = {
children: ReactNode;
};
export const SidePanelSubPageRouter = ({
children,
}: SidePanelSubPageRouterProps) => {
const { currentSidePanelSubPage, goBackFromSidePanelSubPage } =
useSidePanelSubPageHistory();
if (!isDefined(currentSidePanelSubPage)) {
return <>{children}</>;
}
const subPageComponent = SIDE_PANEL_SUB_PAGES_CONFIG.get(
currentSidePanelSubPage.subPage,
);
if (!isDefined(subPageComponent)) {
throw new Error(
`Missing side panel sub-page config for "${currentSidePanelSubPage.subPage}". ` +
'Please add it to SIDE_PANEL_SUB_PAGES_CONFIG.',
);
}
const keyedSubPageComponent = React.isValidElement(subPageComponent)
? React.cloneElement(subPageComponent, {
key: currentSidePanelSubPage.id,
})
: subPageComponent;
return (
<StyledSubPageContainer>
<SidePanelSubPageNavigationHeader
title={currentSidePanelSubPage.title}
onBackClick={goBackFromSidePanelSubPage}
/>
{keyedSubPageComponent}
</StyledSubPageContainer>
);
};
@@ -1,7 +1,6 @@
import { styled } from '@linaria/react';
import { type ReactNode } from 'react';
import { SidePanelSubPageNavigationHeader } from '@/side-panel/pages/common/components/SidePanelSubPageNavigationHeader';
import { themeCssVariables } from 'twenty-ui/theme-constants';
const StyledSubViewContainer = styled.div`
@@ -50,8 +49,6 @@ const StyledScrollableListWrapper = styled.div`
`;
type SidePanelSubViewWithSearchProps = {
backBarTitle: string;
onBack: () => void;
searchPlaceholder: string;
searchValue: string;
onSearchChange: (value: string) => void;
@@ -60,8 +57,6 @@ type SidePanelSubViewWithSearchProps = {
};
export const SidePanelSubViewWithSearch = ({
backBarTitle,
onBack,
searchPlaceholder,
searchValue,
onSearchChange,
@@ -69,10 +64,6 @@ export const SidePanelSubViewWithSearch = ({
children,
}: SidePanelSubViewWithSearchProps) => (
<StyledSubViewContainer>
<SidePanelSubPageNavigationHeader
title={backBarTitle}
onBackClick={onBack}
/>
<StyledSearchContainer>
<StyledSearchInput
placeholder={searchPlaceholder}
@@ -1,9 +1,10 @@
import { type PageLayoutTab } from '@/page-layout/types/PageLayoutTab';
import { type PageLayoutWidget } from '@/page-layout/types/PageLayoutWidget';
import { GRAPH_TYPE_INFORMATION } from '@/side-panel/pages/page-layout/constants/GraphTypeInformation';
import { getCurrentGraphTypeFromConfig } from '@/side-panel/pages/page-layout/utils/getCurrentGraphTypeFromConfig';
import { isWidgetConfigurationOfTypeGraph } from '@/side-panel/pages/page-layout/utils/isWidgetConfigurationOfTypeGraph';
import { type PageLayoutTab } from '@/page-layout/types/PageLayoutTab';
import { type PageLayoutWidget } from '@/page-layout/types/PageLayoutWidget';
import { t } from '@lingui/core/macro';
import { useContext } from 'react';
import { SidePanelPages } from 'twenty-shared/types';
import { isDefined } from 'twenty-shared/utils';
import {
@@ -14,7 +15,6 @@ import {
type IconComponent,
} from 'twenty-ui/display';
import { ThemeContext } from 'twenty-ui/theme-constants';
import { useContext } from 'react';
type PageLayoutHeaderInfo = {
headerIcon: IconComponent | undefined;
@@ -105,8 +105,7 @@ export const usePageLayoutHeaderInfo = ({
};
}
case SidePanelPages.PageLayoutGraphTypeSelect:
case SidePanelPages.PageLayoutGraphFilter: {
case SidePanelPages.PageLayoutGraphTypeSelect: {
if (!isDefined(pageLayoutEditingWidgetId)) {
return null;
}
@@ -127,12 +126,6 @@ export const usePageLayoutHeaderInfo = ({
widgetInEditMode.configuration,
);
const graphTypeInfo = GRAPH_TYPE_INFORMATION[currentGraphType];
const graphTypeLabel = t(graphTypeInfo.label);
const headerType =
sidePanelPage === SidePanelPages.PageLayoutGraphFilter
? graphTypeLabel
: t`Chart`;
const title = isDefined(editedTitle)
? editedTitle
@@ -143,16 +136,15 @@ export const usePageLayoutHeaderInfo = ({
return {
headerIcon: graphTypeInfo.icon,
headerIconColor: iconColor,
headerType,
headerType: t`Chart`,
title,
isReadonly: sidePanelPage === SidePanelPages.PageLayoutGraphFilter,
isReadonly: false,
tab: undefined,
widgetInEditMode,
};
}
case SidePanelPages.PageLayoutFieldsSettings:
case SidePanelPages.PageLayoutFieldsLayout: {
case SidePanelPages.PageLayoutFieldsSettings: {
if (!isDefined(pageLayoutEditingWidgetId)) {
return null;
}
@@ -6,9 +6,7 @@ import { SidePanelMessageThreadPage } from '@/side-panel/pages/message-thread/co
import { SidePanelNavigationMenuItemEditPage } from '@/side-panel/pages/navigation-menu-item/components/SidePanelNavigationMenuItemEditPage';
import { SidePanelNewSidebarItemPage } from '@/side-panel/pages/navigation-menu-item/components/SidePanelNewSidebarItemPage';
import { SidePanelPageLayoutChartSettings } from '@/side-panel/pages/page-layout/components/SidePanelPageLayoutChartSettings';
import { SidePanelPageLayoutFieldsLayout } from '@/side-panel/pages/page-layout/components/SidePanelPageLayoutFieldsLayout';
import { SidePanelPageLayoutFieldsSettings } from '@/side-panel/pages/page-layout/components/SidePanelPageLayoutFieldsSettings';
import { SidePanelPageLayoutGraphFilter } from '@/side-panel/pages/page-layout/components/SidePanelPageLayoutGraphFilter';
import { SidePanelPageLayoutIframeSettings } from '@/side-panel/pages/page-layout/components/SidePanelPageLayoutIframeSettings';
import { SidePanelPageLayoutTabSettings } from '@/side-panel/pages/page-layout/components/SidePanelPageLayoutTabSettings';
import { SidePanelPageLayoutWidgetTypeSelect } from '@/side-panel/pages/page-layout/components/SidePanelPageLayoutWidgetTypeSelect';
@@ -55,7 +53,6 @@ export const SIDE_PANEL_PAGES_CONFIG = new Map<SidePanelPages, React.ReactNode>(
SidePanelPages.PageLayoutGraphTypeSelect,
<SidePanelPageLayoutChartSettings />,
],
[SidePanelPages.PageLayoutGraphFilter, <SidePanelPageLayoutGraphFilter />],
[
SidePanelPages.PageLayoutIframeSettings,
<SidePanelPageLayoutIframeSettings />,
@@ -65,10 +62,6 @@ export const SIDE_PANEL_PAGES_CONFIG = new Map<SidePanelPages, React.ReactNode>(
SidePanelPages.PageLayoutFieldsSettings,
<SidePanelPageLayoutFieldsSettings />,
],
[
SidePanelPages.PageLayoutFieldsLayout,
<SidePanelPageLayoutFieldsLayout />,
],
[SidePanelPages.ViewFrontComponent, <SidePanelFrontComponentPage />],
[
SidePanelPages.NavigationMenuItemEdit,
@@ -0,0 +1,44 @@
import { SidePanelEditFolderPickerSubPage } from '@/side-panel/pages/navigation-menu-item/components/SidePanelEditFolderPickerSubPage';
import { SidePanelNewSidebarItemObjectSubPage } from '@/side-panel/pages/navigation-menu-item/components/SidePanelNewSidebarItemObjectSubPage';
import { SidePanelNewSidebarItemObjectSystemPickerSubPage } from '@/side-panel/pages/navigation-menu-item/components/SidePanelNewSidebarItemObjectSystemPickerSubPage';
import { SidePanelNewSidebarItemRecordSubPage } from '@/side-panel/pages/navigation-menu-item/components/SidePanelNewSidebarItemRecordSubPage';
import { SidePanelNewSidebarItemViewObjectPickerSubPage } from '@/side-panel/pages/navigation-menu-item/components/SidePanelNewSidebarItemViewObjectPickerSubPage';
import { SidePanelNewSidebarItemViewPickerSubPage } from '@/side-panel/pages/navigation-menu-item/components/SidePanelNewSidebarItemViewPickerSubPage';
import { SidePanelNewSidebarItemViewSystemPickerSubPage } from '@/side-panel/pages/navigation-menu-item/components/SidePanelNewSidebarItemViewSystemPickerSubPage';
import { SidePanelChartFilterSubPage } from '@/side-panel/pages/page-layout/components/SidePanelChartFilterSubPage';
import { SidePanelFieldsLayoutSubPage } from '@/side-panel/pages/page-layout/components/SidePanelFieldsLayoutSubPage';
import { SidePanelSubPages } from '@/side-panel/types/SidePanelSubPages';
import React from 'react';
export const SIDE_PANEL_SUB_PAGES_CONFIG = new Map<
SidePanelSubPages,
React.ReactNode
>([
[SidePanelSubPages.PageLayoutGraphFilter, <SidePanelChartFilterSubPage />],
[SidePanelSubPages.PageLayoutFieldsLayout, <SidePanelFieldsLayoutSubPage />],
[
SidePanelSubPages.NewSidebarItemViewObjectPicker,
<SidePanelNewSidebarItemViewObjectPickerSubPage />,
],
[
SidePanelSubPages.NewSidebarItemViewPicker,
<SidePanelNewSidebarItemViewPickerSubPage />,
],
[
SidePanelSubPages.NewSidebarItemViewSystemPicker,
<SidePanelNewSidebarItemViewSystemPickerSubPage />,
],
[
SidePanelSubPages.NewSidebarItemObjectPicker,
<SidePanelNewSidebarItemObjectSubPage />,
],
[
SidePanelSubPages.NewSidebarItemObjectSystemPicker,
<SidePanelNewSidebarItemObjectSystemPickerSubPage />,
],
[
SidePanelSubPages.NewSidebarItemRecord,
<SidePanelNewSidebarItemRecordSubPage />,
],
[SidePanelSubPages.EditFolderPicker, <SidePanelEditFolderPickerSubPage />],
]);
@@ -1,22 +1,65 @@
import { useCallback } from 'react';
import { useSidePanelMenu } from '@/side-panel/hooks/useSidePanelMenu';
import { hasUserSelectedSidePanelListItemState } from '@/side-panel/states/hasUserSelectedSidePanelListItemState';
import { sidePanelNavigationMorphItemsByPageState } from '@/side-panel/states/sidePanelNavigationMorphItemsByPageState';
import { sidePanelNavigationStackState } from '@/side-panel/states/sidePanelNavigationStackState';
import { sidePanelPageInfoState } from '@/side-panel/states/sidePanelPageInfoState';
import { sidePanelPageState } from '@/side-panel/states/sidePanelPageState';
import { hasUserSelectedSidePanelListItemState } from '@/side-panel/states/hasUserSelectedSidePanelListItemState';
import { sidePanelSubPageStackComponentState } from '@/side-panel/states/sidePanelSubPageStackComponentState';
import { getShowPageTabListComponentId } from '@/ui/layout/show-page/utils/getShowPageTabListComponentId';
import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTabIdComponentState';
import { isNonEmptyArray } from '@sniptt/guards';
import { isDefined } from 'twenty-shared/utils';
import { useStore } from 'jotai';
import { isDefined } from 'twenty-shared/utils';
export const useSidePanelHistory = () => {
const store = useStore();
const { closeSidePanelMenu } = useSidePanelMenu();
const cleanupCurrentPage = useCallback(() => {
const currentNavigationStack = store.get(
sidePanelNavigationStackState.atom,
);
const currentMorphItems = store.get(
sidePanelNavigationMorphItemsByPageState.atom,
);
if (currentNavigationStack.length > 0) {
const removedItem = currentNavigationStack.at(-1);
if (isDefined(removedItem)) {
const newMorphItems = new Map(currentMorphItems);
newMorphItems.delete(removedItem.pageId);
store.set(sidePanelNavigationMorphItemsByPageState.atom, newMorphItems);
store.set(
sidePanelSubPageStackComponentState.atomFamily({
instanceId: removedItem.pageId,
}),
[],
);
const morphItems = currentMorphItems.get(removedItem.pageId);
if (isNonEmptyArray(morphItems)) {
store.set(
activeTabIdComponentState.atomFamily({
instanceId: getShowPageTabListComponentId({
pageId: removedItem.pageId,
targetObjectId: morphItems[0].recordId,
}),
}),
null,
);
}
}
}
}, [store]);
const goBackFromSidePanel = useCallback(() => {
cleanupCurrentPage();
const currentNavigationStack = store.get(
sidePanelNavigationStackState.atom,
);
@@ -39,35 +82,30 @@ export const useSidePanelHistory = () => {
store.set(sidePanelNavigationStackState.atom, newNavigationStack);
const currentMorphItems = store.get(
sidePanelNavigationMorphItemsByPageState.atom,
store.set(hasUserSelectedSidePanelListItemState.atom, false);
}, [cleanupCurrentPage, closeSidePanelMenu, store]);
const goBackOneSubPageOrMainPage = useCallback(() => {
const currentPageInfo = store.get(sidePanelPageInfoState.atom);
const subPageStack = store.get(
sidePanelSubPageStackComponentState.atomFamily({
instanceId: currentPageInfo.instanceId,
}),
);
if (currentNavigationStack.length > 0) {
const removedItem = currentNavigationStack.at(-1);
if (isDefined(removedItem)) {
const newMorphItems = new Map(currentMorphItems);
newMorphItems.delete(removedItem.pageId);
store.set(sidePanelNavigationMorphItemsByPageState.atom, newMorphItems);
const morphItems = currentMorphItems.get(removedItem.pageId);
if (isNonEmptyArray(morphItems)) {
store.set(
activeTabIdComponentState.atomFamily({
instanceId: getShowPageTabListComponentId({
pageId: removedItem.pageId,
targetObjectId: morphItems[0].recordId,
}),
}),
null,
);
}
}
if (isNonEmptyArray(subPageStack)) {
store.set(
sidePanelSubPageStackComponentState.atomFamily({
instanceId: currentPageInfo.instanceId,
}),
subPageStack.slice(0, -1),
);
return;
}
store.set(hasUserSelectedSidePanelListItemState.atom, false);
}, [closeSidePanelMenu, store]);
goBackFromSidePanel();
}, [goBackFromSidePanel, store]);
const navigateSidePanelHistory = useCallback(
(pageIndex: number) => {
@@ -99,6 +137,13 @@ export const useSidePanelHistory = () => {
for (const [pageId, morphItems] of currentMorphItems.entries()) {
if (!newNavigationStack.some((item) => item.pageId === pageId)) {
store.set(
sidePanelSubPageStackComponentState.atomFamily({
instanceId: pageId,
}),
[],
);
store.set(
activeTabIdComponentState.atomFamily({
instanceId: getShowPageTabListComponentId({
@@ -126,6 +171,7 @@ export const useSidePanelHistory = () => {
return {
goBackFromSidePanel,
goBackOneSubPageOrMainPage,
navigateSidePanelHistory,
};
};
@@ -0,0 +1,50 @@
import { useCallback } from 'react';
import { v4 } from 'uuid';
import { sidePanelSubPageStackComponentState } from '@/side-panel/states/sidePanelSubPageStackComponentState';
import { type SidePanelSubPageEntry } from '@/side-panel/types/SidePanelSubPageEntry';
import { type SidePanelSubPages } from '@/side-panel/types/SidePanelSubPages';
import { getSidePanelSubPageTitle } from '@/side-panel/utils/getSidePanelSubPageTitle';
import { useAtomComponentState } from '@/ui/utilities/state/jotai/hooks/useAtomComponentState';
import { isDefined } from 'twenty-shared/utils';
export const useSidePanelSubPageHistory = () => {
const [sidePanelSubPageStack, setSidePanelSubPageStack] =
useAtomComponentState(sidePanelSubPageStackComponentState);
const currentSidePanelSubPage: SidePanelSubPageEntry | undefined =
sidePanelSubPageStack.at(-1);
const hasSidePanelSubPages = isDefined(currentSidePanelSubPage);
const navigateToSidePanelSubPage = useCallback(
(subPage: SidePanelSubPages, titleOverride?: string) => {
const title = isDefined(titleOverride)
? titleOverride
: getSidePanelSubPageTitle(subPage);
setSidePanelSubPageStack((currentStack) => [
...currentStack,
{ id: v4(), subPage, title },
]);
},
[setSidePanelSubPageStack],
);
const goBackFromSidePanelSubPage = useCallback(() => {
setSidePanelSubPageStack((currentStack) => currentStack.slice(0, -1));
}, [setSidePanelSubPageStack]);
const resetSidePanelSubPages = useCallback(() => {
setSidePanelSubPageStack([]);
}, [setSidePanelSubPageStack]);
return {
navigateToSidePanelSubPage,
goBackFromSidePanelSubPage,
resetSidePanelSubPages,
currentSidePanelSubPage,
sidePanelSubPageStack,
hasSidePanelSubPages,
};
};
@@ -34,6 +34,7 @@ export const SidePanelSubPageNavigationHeader = ({
Icon={IconChevronLeft}
variant="tertiary"
size="small"
ariaLabel="Go back"
/>
<StyledText>{title}</StyledText>
</StyledContainer>
@@ -0,0 +1,91 @@
import { useLingui } from '@lingui/react/macro';
import { useState } from 'react';
import { useIcons } from 'twenty-ui/display';
import { CommandMenuItem } from '@/command-menu/components/CommandMenuItem';
import { NavigationMenuItemStyleIcon } from '@/navigation-menu-item/components/NavigationMenuItemStyleIcon';
import { FOLDER_ICON_DEFAULT } from '@/navigation-menu-item/constants/FolderIconDefault';
import { DEFAULT_NAVIGATION_MENU_ITEM_COLOR_FOLDER } from '@/navigation-menu-item/constants/NavigationMenuItemDefaultColorFolder';
import { SidePanelGroup } from '@/side-panel/components/SidePanelGroup';
import { SidePanelList } from '@/side-panel/components/SidePanelList';
import { SidePanelSubViewWithSearch } from '@/side-panel/components/SidePanelSubViewWithSearch';
import { useFolderPickerSelectionData } from '@/side-panel/pages/navigation-menu-item/hooks/useFolderPickerSelectionData';
import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem';
import { filterBySearchQuery } from '~/utils/filterBySearchQuery';
export const SidePanelEditFolderPickerSubPage = () => {
const { t } = useLingui();
const { getIcon } = useIcons();
const [searchValue, setSearchValue] = useState('');
const { foldersToShow, includeNoFolderOption, handleSelectFolder } =
useFolderPickerSelectionData();
const filteredFolders = filterBySearchQuery({
items: foldersToShow,
searchQuery: searchValue,
getSearchableValues: (folder) => [folder.name],
});
const isEmpty = filteredFolders.length === 0 && !includeNoFolderOption;
const selectableItemIds = [
...(includeNoFolderOption ? ['no-folder'] : []),
...(filteredFolders.length > 0 ? filteredFolders.map((f) => f.id) : []),
];
const noResultsText =
searchValue.trim().length > 0
? t`No results found`
: t`No folders available`;
return (
<SidePanelSubViewWithSearch
searchPlaceholder={t`Search a folder...`}
searchValue={searchValue}
onSearchChange={setSearchValue}
>
<SidePanelList
commandGroups={[]}
selectableItemIds={selectableItemIds}
noResults={isEmpty}
noResultsText={noResultsText}
>
<SidePanelGroup heading={t`Folders`}>
{includeNoFolderOption && (
<SelectableListItem
itemId="no-folder"
onEnter={() => handleSelectFolder(null)}
>
<CommandMenuItem
label={t`No folder`}
id="no-folder"
onClick={() => handleSelectFolder(null)}
/>
</SelectableListItem>
)}
{filteredFolders.map((folder) => {
const FolderIcon = getIcon(folder.icon ?? FOLDER_ICON_DEFAULT);
const folderColor =
folder.color ?? DEFAULT_NAVIGATION_MENU_ITEM_COLOR_FOLDER;
return (
<SelectableListItem
key={folder.id}
itemId={folder.id}
onEnter={() => handleSelectFolder(folder.id)}
>
<CommandMenuItem
LeftComponent={
<NavigationMenuItemStyleIcon
Icon={FolderIcon}
color={folderColor}
/>
}
label={folder.name}
id={folder.id}
onClick={() => handleSelectFolder(folder.id)}
/>
</SelectableListItem>
);
})}
</SidePanelGroup>
</SidePanelList>
</SidePanelSubViewWithSearch>
);
};
@@ -1,99 +0,0 @@
import { useLingui } from '@lingui/react/macro';
import { useState } from 'react';
import { useIcons } from 'twenty-ui/display';
import { SidePanelGroup } from '@/side-panel/components/SidePanelGroup';
import { CommandMenuItem } from '@/command-menu/components/CommandMenuItem';
import { SidePanelList } from '@/side-panel/components/SidePanelList';
import { SidePanelSubViewWithSearch } from '@/side-panel/components/SidePanelSubViewWithSearch';
import { useFolderPickerSelectionData } from '@/side-panel/pages/navigation-menu-item/hooks/useFolderPickerSelectionData';
import { NavigationMenuItemStyleIcon } from '@/navigation-menu-item/components/NavigationMenuItemStyleIcon';
import { FOLDER_ICON_DEFAULT } from '@/navigation-menu-item/constants/FolderIconDefault';
import { DEFAULT_NAVIGATION_MENU_ITEM_COLOR_FOLDER } from '@/navigation-menu-item/constants/NavigationMenuItemDefaultColorFolder';
import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem';
import { filterBySearchQuery } from '~/utils/filterBySearchQuery';
type SidePanelEditFolderPickerSubViewProps = {
onBack: () => void;
};
export const SidePanelEditFolderPickerSubView = ({
onBack,
}: SidePanelEditFolderPickerSubViewProps) => {
const { t } = useLingui();
const { getIcon } = useIcons();
const [searchValue, setSearchValue] = useState('');
const { foldersToShow, includeNoFolderOption, handleSelectFolder } =
useFolderPickerSelectionData({ onCloseSubView: onBack });
const filteredFolders = filterBySearchQuery({
items: foldersToShow,
searchQuery: searchValue,
getSearchableValues: (folder) => [folder.name],
});
const isEmpty = filteredFolders.length === 0 && !includeNoFolderOption;
const selectableItemIds = [
...(includeNoFolderOption ? ['no-folder'] : []),
...(filteredFolders.length > 0 ? filteredFolders.map((f) => f.id) : []),
];
const noResultsText =
searchValue.trim().length > 0
? t`No results found`
: t`No folders available`;
return (
<SidePanelSubViewWithSearch
backBarTitle={t`Move to a folder`}
onBack={onBack}
searchPlaceholder={t`Search a folder...`}
searchValue={searchValue}
onSearchChange={setSearchValue}
>
<SidePanelList
commandGroups={[]}
selectableItemIds={selectableItemIds}
noResults={isEmpty}
noResultsText={noResultsText}
>
<SidePanelGroup heading={t`Folders`}>
{includeNoFolderOption && (
<SelectableListItem
itemId="no-folder"
onEnter={() => handleSelectFolder(null)}
>
<CommandMenuItem
label={t`No folder`}
id="no-folder"
onClick={() => handleSelectFolder(null)}
/>
</SelectableListItem>
)}
{filteredFolders.map((folder) => {
const FolderIcon = getIcon(folder.icon ?? FOLDER_ICON_DEFAULT);
const folderColor =
folder.color ?? DEFAULT_NAVIGATION_MENU_ITEM_COLOR_FOLDER;
return (
<SelectableListItem
key={folder.id}
itemId={folder.id}
onEnter={() => handleSelectFolder(folder.id)}
>
<CommandMenuItem
LeftComponent={
<NavigationMenuItemStyleIcon
Icon={FolderIcon}
color={folderColor}
/>
}
label={folder.name}
id={folder.id}
onClick={() => handleSelectFolder(folder.id)}
/>
</SelectableListItem>
);
})}
</SidePanelGroup>
</SidePanelList>
</SidePanelSubViewWithSearch>
);
};
@@ -10,20 +10,20 @@ import { selectedNavigationMenuItemInEditModeState } from '@/navigation-menu-ite
import { parseThemeColor } from '@/navigation-menu-item/utils/parseThemeColor';
import { SidePanelGroup } from '@/side-panel/components/SidePanelGroup';
import { SidePanelList } from '@/side-panel/components/SidePanelList';
import { useSidePanelSubPageHistory } from '@/side-panel/hooks/useSidePanelSubPageHistory';
import { SidePanelEditColorOption } from '@/side-panel/pages/navigation-menu-item/components/SidePanelEditColorOption';
import { SidePanelEditFolderPickerSubView } from '@/side-panel/pages/navigation-menu-item/components/SidePanelEditFolderPickerSubView';
import { SidePanelEditLinkItemView } from '@/side-panel/pages/navigation-menu-item/components/SidePanelEditLinkItemView';
import { SidePanelEditObjectViewBase } from '@/side-panel/pages/navigation-menu-item/components/SidePanelEditObjectViewBase';
import { SidePanelEditOrganizeActions } from '@/side-panel/pages/navigation-menu-item/components/SidePanelEditOrganizeActions';
import { SidePanelEditOwnerSection } from '@/side-panel/pages/navigation-menu-item/components/SidePanelEditOwnerSection';
import { useNavigationMenuItemEditOrganizeActions } from '@/side-panel/pages/navigation-menu-item/hooks/useNavigationMenuItemEditOrganizeActions';
import { getOrganizeActionsSelectableItemIds } from '@/side-panel/pages/navigation-menu-item/utils/getOrganizeActionsSelectableItemIds';
import { SidePanelSubPages } from '@/side-panel/types/SidePanelSubPages';
import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem';
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
import { ViewKey } from '@/views/types/ViewKey';
import { styled } from '@linaria/react';
import { useLingui } from '@lingui/react/macro';
import { useState } from 'react';
import { isDefined } from 'twenty-shared/utils';
import { IconPlus } from 'twenty-ui/display';
import { themeCssVariables } from 'twenty-ui/theme-constants';
@@ -51,8 +51,9 @@ export const SidePanelNavigationMenuItemEditPage = () => {
useSelectedNavigationMenuItemEditItemObjectMetadata();
const selectedItemType = selectedItem?.itemType ?? null;
const [isFolderPickerOpen, setIsFolderPickerOpen] = useState(false);
const openFolderPicker = () => setIsFolderPickerOpen(true);
const { navigateToSidePanelSubPage } = useSidePanelSubPageHistory();
const openFolderPicker = () =>
navigateToSidePanelSubPage(SidePanelSubPages.EditFolderPicker);
const {
canMoveUp,
@@ -95,14 +96,6 @@ export const SidePanelNavigationMenuItemEditPage = () => {
);
}
if (isFolderPickerOpen) {
return (
<SidePanelEditFolderPickerSubView
onBack={() => setIsFolderPickerOpen(false)}
/>
);
}
switch (selectedItemType) {
case NavigationMenuItemType.VIEW:
if (!selectedItemObjectMetadata) return null;
@@ -1,4 +1,3 @@
import { useLingui } from '@lingui/react/macro';
import { useState } from 'react';
import { useAddObjectToNavigationMenuDraft } from '@/navigation-menu-item/hooks/useAddObjectToNavigationMenuDraft';
@@ -10,26 +9,19 @@ import { getStandardObjectIconColor } from '@/navigation-menu-item/utils/getStan
import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems';
import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems';
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { useSidePanelSubPageHistory } from '@/side-panel/hooks/useSidePanelSubPageHistory';
import { SidePanelObjectPickerSubView } from '@/side-panel/pages/navigation-menu-item/components/SidePanelObjectPickerSubView';
import { SidePanelSystemObjectPickerSubView } from '@/side-panel/pages/navigation-menu-item/components/SidePanelSystemObjectPickerSubView';
import { getAvailableObjectMetadataForNewSidebarItem } from '@/side-panel/pages/navigation-menu-item/utils/getAvailableObjectMetadataForNewSidebarItem';
import { SidePanelSubPages } from '@/side-panel/types/SidePanelSubPages';
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
import { useSetAtomState } from '@/ui/utilities/state/jotai/hooks/useSetAtomState';
import { ViewKey } from '@/views/types/ViewKey';
import { useIcons } from 'twenty-ui/display';
type SidePanelNewSidebarItemObjectFlowProps = {
onBack: () => void;
};
export const SidePanelNewSidebarItemObjectFlow = ({
onBack,
}: SidePanelNewSidebarItemObjectFlowProps) => {
const { t } = useLingui();
export const SidePanelNewSidebarItemObjectSubPage = () => {
const { navigateToSidePanelSubPage } = useSidePanelSubPageHistory();
const { getIcon } = useIcons();
const [objectSearchInput, setObjectSearchInput] = useState('');
const [systemObjectSearchInput, setSystemObjectSearchInput] = useState('');
const [isInSystemPicker, setIsInSystemPicker] = useState(false);
const { currentDraft } = useDraftNavigationMenuItems();
const { objectMetadataItems } = useObjectMetadataItems();
@@ -56,7 +48,7 @@ export const SidePanelNewSidebarItemObjectFlow = ({
.map((view) => view.objectMetadataId),
);
const { availableObjectMetadataItems, availableSystemObjectMetadataItems } =
const { availableObjectMetadataItems } =
getAvailableObjectMetadataForNewSidebarItem({
objectMetadataItems,
activeNonSystemObjectMetadataItems,
@@ -87,36 +79,18 @@ export const SidePanelNewSidebarItemObjectFlow = ({
});
};
const handleBackToObjectList = () => {
setIsInSystemPicker(false);
setSystemObjectSearchInput('');
};
const disableDrag = addMenuItemInsertionContext?.disableDrag === true;
if (isInSystemPicker) {
return (
<SidePanelSystemObjectPickerSubView
systemObjects={availableSystemObjectMetadataItems}
searchValue={systemObjectSearchInput}
onSearchChange={setSystemObjectSearchInput}
onBack={handleBackToObjectList}
isViewItem={false}
onChangeObject={handleSelectObject}
objectMenuItemVariant="add"
emptyNoResultsText={t`All system objects are already in the sidebar`}
disableDrag={disableDrag}
/>
);
}
return (
<SidePanelObjectPickerSubView
objects={availableObjectMetadataItems}
searchValue={objectSearchInput}
onSearchChange={setObjectSearchInput}
onBack={onBack}
onOpenSystemPicker={() => setIsInSystemPicker(true)}
onOpenSystemPicker={() =>
navigateToSidePanelSubPage(
SidePanelSubPages.NewSidebarItemObjectSystemPicker,
)
}
isViewItem={false}
onChangeObject={handleSelectObject}
objectMenuItemVariant="add"
@@ -0,0 +1,95 @@
import { useLingui } from '@lingui/react/macro';
import { useAddObjectToNavigationMenuDraft } from '@/navigation-menu-item/hooks/useAddObjectToNavigationMenuDraft';
import { useDraftNavigationMenuItems } from '@/navigation-menu-item/hooks/useDraftNavigationMenuItems';
import { useNavigationMenuObjectMetadataFromDraft } from '@/navigation-menu-item/hooks/useNavigationMenuObjectMetadataFromDraft';
import { useOpenNavigationMenuItemInSidePanel } from '@/navigation-menu-item/hooks/useOpenNavigationMenuItemInSidePanel';
import { addMenuItemInsertionContextState } from '@/navigation-menu-item/states/addMenuItemInsertionContextState';
import { getStandardObjectIconColor } from '@/navigation-menu-item/utils/getStandardObjectIconColor';
import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems';
import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems';
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { SidePanelSystemObjectPickerSubView } from '@/side-panel/pages/navigation-menu-item/components/SidePanelSystemObjectPickerSubView';
import { getAvailableObjectMetadataForNewSidebarItem } from '@/side-panel/pages/navigation-menu-item/utils/getAvailableObjectMetadataForNewSidebarItem';
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
import { useSetAtomState } from '@/ui/utilities/state/jotai/hooks/useSetAtomState';
import { ViewKey } from '@/views/types/ViewKey';
import { useState } from 'react';
import { useIcons } from 'twenty-ui/display';
export const SidePanelNewSidebarItemObjectSystemPickerSubPage = () => {
const { t } = useLingui();
const { getIcon } = useIcons();
const [searchValue, setSearchValue] = useState('');
const { currentDraft } = useDraftNavigationMenuItems();
const { objectMetadataItems } = useObjectMetadataItems();
const { addObjectToDraft } = useAddObjectToNavigationMenuDraft();
const { openNavigationMenuItemInSidePanel } =
useOpenNavigationMenuItemInSidePanel();
const { activeNonSystemObjectMetadataItems } =
useFilteredObjectMetadataItems();
const addMenuItemInsertionContext = useAtomStateValue(
addMenuItemInsertionContextState,
);
const setAddMenuItemInsertionContext = useSetAtomState(
addMenuItemInsertionContextState,
);
const {
views,
objectMetadataIdsWithIndexView,
objectMetadataIdsInWorkspace,
} = useNavigationMenuObjectMetadataFromDraft(currentDraft);
const objectMetadataIdsWithDisplayableViews = new Set(
views
.filter((view) => view.key !== ViewKey.Index)
.map((view) => view.objectMetadataId),
);
const { availableSystemObjectMetadataItems } =
getAvailableObjectMetadataForNewSidebarItem({
objectMetadataItems,
activeNonSystemObjectMetadataItems,
objectMetadataIdsWithIndexView,
objectMetadataIdsWithDisplayableViews,
});
const handleSelectObject = (
objectMetadataItem: ObjectMetadataItem,
defaultViewId: string,
) => {
if (objectMetadataIdsInWorkspace.has(objectMetadataItem.id)) {
return;
}
const itemId = addObjectToDraft(
objectMetadataItem.id,
defaultViewId,
currentDraft,
addMenuItemInsertionContext?.targetFolderId,
addMenuItemInsertionContext?.targetIndex,
getStandardObjectIconColor(objectMetadataItem.nameSingular),
);
setAddMenuItemInsertionContext(null);
openNavigationMenuItemInSidePanel({
itemId,
pageTitle: objectMetadataItem.labelSingular,
pageIcon: getIcon(objectMetadataItem.icon),
});
};
const disableDrag = addMenuItemInsertionContext?.disableDrag === true;
return (
<SidePanelSystemObjectPickerSubView
systemObjects={availableSystemObjectMetadataItems}
searchValue={searchValue}
onSearchChange={setSearchValue}
isViewItem={false}
onChangeObject={handleSelectObject}
objectMenuItemVariant="add"
emptyNoResultsText={t`All system objects are already in the sidebar`}
disableDrag={disableDrag}
/>
);
};
@@ -1,45 +1,23 @@
import { useState } from 'react';
import { addMenuItemInsertionContextState } from '@/navigation-menu-item/states/addMenuItemInsertionContextState';
import { useSidePanelSubPageHistory } from '@/side-panel/hooks/useSidePanelSubPageHistory';
import { SidePanelNewSidebarItemMainMenu } from '@/side-panel/pages/navigation-menu-item/components/SidePanelNewSidebarItemMainMenu';
import { SidePanelNewSidebarItemObjectFlow } from '@/side-panel/pages/navigation-menu-item/components/SidePanelNewSidebarItemObjectFlow';
import { SidePanelNewSidebarItemRecordSubView } from '@/side-panel/pages/navigation-menu-item/components/SidePanelNewSidebarItemRecordSubView';
import { SidePanelNewSidebarItemViewFlow } from '@/side-panel/pages/navigation-menu-item/components/SidePanelNewSidebarItemViewFlow';
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
import { isDefined } from 'twenty-shared/utils';
type SelectedOption = 'object' | 'record' | 'view' | null;
import { SidePanelSubPages } from '@/side-panel/types/SidePanelSubPages';
export const SidePanelNewSidebarItemPage = () => {
const [selectedOption, setSelectedOption] = useState<SelectedOption>(null);
const addMenuItemInsertionContext = useAtomStateValue(
addMenuItemInsertionContextState,
const { navigateToSidePanelSubPage } = useSidePanelSubPageHistory();
return (
<SidePanelNewSidebarItemMainMenu
onSelectRecord={() =>
navigateToSidePanelSubPage(SidePanelSubPages.NewSidebarItemRecord)
}
onSelectObject={() =>
navigateToSidePanelSubPage(SidePanelSubPages.NewSidebarItemObjectPicker)
}
onSelectView={() =>
navigateToSidePanelSubPage(
SidePanelSubPages.NewSidebarItemViewObjectPicker,
)
}
/>
);
const disableDrag = isDefined(addMenuItemInsertionContext);
const handleBackToMain = () => {
setSelectedOption(null);
};
switch (selectedOption) {
case 'view':
return <SidePanelNewSidebarItemViewFlow onBack={handleBackToMain} />;
case 'object':
return <SidePanelNewSidebarItemObjectFlow onBack={handleBackToMain} />;
case 'record':
return (
<SidePanelNewSidebarItemRecordSubView
onBack={handleBackToMain}
disableDrag={disableDrag}
/>
);
default:
return (
<SidePanelNewSidebarItemMainMenu
onSelectRecord={() => setSelectedOption('record')}
onSelectObject={() => setSelectedOption('object')}
onSelectView={() => setSelectedOption('view')}
/>
);
}
};
@@ -25,21 +25,12 @@ type SearchRecordBase = {
imageUrl?: string | null;
};
type SidePanelNewSidebarItemRecordSubViewProps = {
onBack: () => void;
disableDrag?: boolean;
};
export const SidePanelNewSidebarItemRecordSubView = ({
onBack,
disableDrag: disableDragProp,
}: SidePanelNewSidebarItemRecordSubViewProps) => {
export const SidePanelNewSidebarItemRecordSubPage = () => {
const { t } = useLingui();
const addMenuItemInsertionContext = useAtomStateValue(
addMenuItemInsertionContextState,
);
const disableDrag =
disableDragProp ?? addMenuItemInsertionContext?.disableDrag === true;
const disableDrag = addMenuItemInsertionContext?.disableDrag === true;
const { currentDraft } = useDraftNavigationMenuItems();
const { objectMetadataItems } = useObjectMetadataItems();
const [recordSearchInput, setRecordSearchInput] = useState('');
@@ -92,8 +83,6 @@ export const SidePanelNewSidebarItemRecordSubView = ({
return (
<SidePanelSubViewWithSearch
backBarTitle={t`Add a record`}
onBack={onBack}
searchPlaceholder={t`Search records...`}
searchValue={recordSearchInput}
onSearchChange={setRecordSearchInput}
@@ -1,115 +0,0 @@
import { useState } from 'react';
import { isDefined } from 'twenty-shared/utils';
import { useDraftNavigationMenuItems } from '@/navigation-menu-item/hooks/useDraftNavigationMenuItems';
import { useNavigationMenuObjectMetadataFromDraft } from '@/navigation-menu-item/hooks/useNavigationMenuObjectMetadataFromDraft';
import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems';
import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems';
import { SidePanelNewSidebarItemViewObjectPickerSubView } from '@/side-panel/pages/navigation-menu-item/components/SidePanelNewSidebarItemViewObjectPickerSubView';
import { SidePanelNewSidebarItemViewPickerSubView } from '@/side-panel/pages/navigation-menu-item/components/SidePanelNewSidebarItemViewPickerSubView';
import { SidePanelNewSidebarItemViewSystemSubView } from '@/side-panel/pages/navigation-menu-item/components/SidePanelNewSidebarItemViewSystemSubView';
import { getAvailableObjectMetadataForNewSidebarItem } from '@/side-panel/pages/navigation-menu-item/utils/getAvailableObjectMetadataForNewSidebarItem';
import { ViewKey } from '@/views/types/ViewKey';
import { ViewType } from '@/views/types/ViewType';
type SidePanelNewSidebarItemViewFlowProps = {
onBack: () => void;
};
export const SidePanelNewSidebarItemViewFlow = ({
onBack,
}: SidePanelNewSidebarItemViewFlowProps) => {
const [selectedObjectMetadataIdForView, setSelectedObjectMetadataIdForView] =
useState<string | null>(null);
const [objectSearchInput, setObjectSearchInput] = useState('');
const [systemObjectSearchInput, setSystemObjectSearchInput] = useState('');
const [isInSystemPicker, setIsInSystemPicker] = useState(false);
const { currentDraft } = useDraftNavigationMenuItems();
const { objectMetadataItems } = useObjectMetadataItems();
const { activeNonSystemObjectMetadataItems } =
useFilteredObjectMetadataItems();
const { views, objectMetadataIdsWithIndexView } =
useNavigationMenuObjectMetadataFromDraft(currentDraft);
const objectMetadataIdsWithDisplayableViews = new Set(
views
.filter(
(view) =>
view.key !== ViewKey.Index && view.type !== ViewType.FieldsWidget,
)
.map((view) => view.objectMetadataId),
);
const {
objectMetadataItemsWithViews,
availableSystemObjectMetadataItemsForView,
} = getAvailableObjectMetadataForNewSidebarItem({
objectMetadataItems,
activeNonSystemObjectMetadataItems,
objectMetadataIdsWithIndexView,
objectMetadataIdsWithDisplayableViews,
});
const handleBackFromViewPicker = () => {
const selectedObjectMetadataItem = isDefined(
selectedObjectMetadataIdForView,
)
? objectMetadataItems.find(
(item) => item.id === selectedObjectMetadataIdForView,
)
: undefined;
const cameFromSystemObjects = selectedObjectMetadataItem?.isSystem ?? false;
setSelectedObjectMetadataIdForView(null);
setIsInSystemPicker(cameFromSystemObjects);
};
const handleBackFromSystemPicker = () => {
setIsInSystemPicker(false);
};
const handleSelectObject = (objectId: string) => {
setSelectedObjectMetadataIdForView(objectId);
};
const handleSelectObjectFromSystem = (objectId: string) => {
setSelectedObjectMetadataIdForView(objectId);
setIsInSystemPicker(false);
};
if (isDefined(selectedObjectMetadataIdForView) && !isInSystemPicker) {
return (
<SidePanelNewSidebarItemViewPickerSubView
selectedObjectMetadataIdForView={selectedObjectMetadataIdForView}
onBack={handleBackFromViewPicker}
/>
);
}
if (isInSystemPicker) {
return (
<SidePanelNewSidebarItemViewSystemSubView
systemObjects={availableSystemObjectMetadataItemsForView}
searchValue={systemObjectSearchInput}
onSearchChange={setSystemObjectSearchInput}
onBack={handleBackFromSystemPicker}
onSelectObject={(item) => handleSelectObjectFromSystem(item.id)}
/>
);
}
return (
<SidePanelNewSidebarItemViewObjectPickerSubView
objects={objectMetadataItemsWithViews}
searchValue={objectSearchInput}
onSearchChange={setObjectSearchInput}
onBack={onBack}
onOpenSystemPicker={() => setIsInSystemPicker(true)}
onSelectObject={(item) => handleSelectObject(item.id)}
showSystemObjectsOption={
availableSystemObjectMetadataItemsForView.length > 0
}
/>
);
};
@@ -0,0 +1,73 @@
import { useDraftNavigationMenuItems } from '@/navigation-menu-item/hooks/useDraftNavigationMenuItems';
import { useNavigationMenuObjectMetadataFromDraft } from '@/navigation-menu-item/hooks/useNavigationMenuObjectMetadataFromDraft';
import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems';
import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems';
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { useSidePanelSubPageHistory } from '@/side-panel/hooks/useSidePanelSubPageHistory';
import { SidePanelNewSidebarItemViewObjectPickerSubView } from '@/side-panel/pages/navigation-menu-item/components/SidePanelNewSidebarItemViewObjectPickerSubView';
import { getAvailableObjectMetadataForNewSidebarItem } from '@/side-panel/pages/navigation-menu-item/utils/getAvailableObjectMetadataForNewSidebarItem';
import { selectedObjectMetadataIdForViewFlowState } from '@/side-panel/states/selectedObjectMetadataIdForViewFlowState';
import { SidePanelSubPages } from '@/side-panel/types/SidePanelSubPages';
import { useSetAtomComponentState } from '@/ui/utilities/state/jotai/hooks/useSetAtomComponentState';
import { ViewKey } from '@/views/types/ViewKey';
import { ViewType } from '@/views/types/ViewType';
import { useState } from 'react';
export const SidePanelNewSidebarItemViewObjectPickerSubPage = () => {
const { navigateToSidePanelSubPage } = useSidePanelSubPageHistory();
const [searchValue, setSearchValue] = useState('');
const setSelectedObjectMetadataIdForViewFlow = useSetAtomComponentState(
selectedObjectMetadataIdForViewFlowState,
);
const { currentDraft } = useDraftNavigationMenuItems();
const { objectMetadataItems } = useObjectMetadataItems();
const { activeNonSystemObjectMetadataItems } =
useFilteredObjectMetadataItems();
const { views, objectMetadataIdsWithIndexView } =
useNavigationMenuObjectMetadataFromDraft(currentDraft);
const objectMetadataIdsWithDisplayableViews = new Set(
views
.filter(
(view) =>
view.key !== ViewKey.Index && view.type !== ViewType.FieldsWidget,
)
.map((view) => view.objectMetadataId),
);
const {
objectMetadataItemsWithViews,
availableSystemObjectMetadataItemsForView,
} = getAvailableObjectMetadataForNewSidebarItem({
objectMetadataItems,
activeNonSystemObjectMetadataItems,
objectMetadataIdsWithIndexView,
objectMetadataIdsWithDisplayableViews,
});
const handleSelectObject = (objectMetadataItem: ObjectMetadataItem) => {
setSelectedObjectMetadataIdForViewFlow(objectMetadataItem.id);
navigateToSidePanelSubPage(
SidePanelSubPages.NewSidebarItemViewPicker,
objectMetadataItem.labelPlural,
);
};
return (
<SidePanelNewSidebarItemViewObjectPickerSubView
objects={objectMetadataItemsWithViews}
searchValue={searchValue}
onSearchChange={setSearchValue}
onOpenSystemPicker={() =>
navigateToSidePanelSubPage(
SidePanelSubPages.NewSidebarItemViewSystemPicker,
)
}
onSelectObject={handleSelectObject}
showSystemObjectsOption={
availableSystemObjectMetadataItemsForView.length > 0
}
/>
);
};
@@ -1,21 +1,20 @@
import { useLingui } from '@lingui/react/macro';
import { IconSettings, useIcons } from 'twenty-ui/display';
import { SidePanelGroup } from '@/side-panel/components/SidePanelGroup';
import { CommandMenuItem } from '@/command-menu/components/CommandMenuItem';
import { SidePanelList } from '@/side-panel/components/SidePanelList';
import { SidePanelSubViewWithSearch } from '@/side-panel/components/SidePanelSubViewWithSearch';
import { useSidePanelFilteredPickerItems } from '@/side-panel/hooks/useSidePanelFilteredPickerItems';
import { NavigationMenuItemStyleIcon } from '@/navigation-menu-item/components/NavigationMenuItemStyleIcon';
import { getStandardObjectIconColor } from '@/navigation-menu-item/utils/getStandardObjectIconColor';
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { SidePanelGroup } from '@/side-panel/components/SidePanelGroup';
import { SidePanelList } from '@/side-panel/components/SidePanelList';
import { SidePanelSubViewWithSearch } from '@/side-panel/components/SidePanelSubViewWithSearch';
import { useSidePanelFilteredPickerItems } from '@/side-panel/hooks/useSidePanelFilteredPickerItems';
import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem';
type SidePanelNewSidebarItemViewObjectPickerSubViewProps = {
objects: ObjectMetadataItem[];
searchValue: string;
onSearchChange: (value: string) => void;
onBack: () => void;
onOpenSystemPicker: () => void;
onSelectObject: (objectMetadataItem: ObjectMetadataItem) => void;
showSystemObjectsOption?: boolean;
@@ -25,7 +24,6 @@ export const SidePanelNewSidebarItemViewObjectPickerSubView = ({
objects,
searchValue,
onSearchChange,
onBack,
onOpenSystemPicker,
onSelectObject,
showSystemObjectsOption = true,
@@ -45,8 +43,6 @@ export const SidePanelNewSidebarItemViewObjectPickerSubView = ({
return (
<SidePanelSubViewWithSearch
backBarTitle={t`Pick an object`}
onBack={onBack}
searchPlaceholder={t`Search an object...`}
searchValue={searchValue}
onSearchChange={onSearchChange}
@@ -0,0 +1,20 @@
import { SidePanelNewSidebarItemViewPickerSubView } from '@/side-panel/pages/navigation-menu-item/components/SidePanelNewSidebarItemViewPickerSubView';
import { selectedObjectMetadataIdForViewFlowState } from '@/side-panel/states/selectedObjectMetadataIdForViewFlowState';
import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue';
import { isDefined } from 'twenty-shared/utils';
export const SidePanelNewSidebarItemViewPickerSubPage = () => {
const selectedObjectMetadataIdForViewFlow = useAtomComponentStateValue(
selectedObjectMetadataIdForViewFlowState,
);
if (!isDefined(selectedObjectMetadataIdForViewFlow)) {
return null;
}
return (
<SidePanelNewSidebarItemViewPickerSubView
selectedObjectMetadataIdForView={selectedObjectMetadataIdForViewFlow}
/>
);
};
@@ -27,12 +27,10 @@ import { ViewVisibility } from '~/generated-metadata/graphql';
type SidePanelNewSidebarItemViewPickerSubViewProps = {
selectedObjectMetadataIdForView: string;
onBack: () => void;
};
export const SidePanelNewSidebarItemViewPickerSubView = ({
selectedObjectMetadataIdForView,
onBack,
}: SidePanelNewSidebarItemViewPickerSubViewProps) => {
const { t } = useLingui();
const { getIcon } = useIcons();
@@ -63,8 +61,6 @@ export const SidePanelNewSidebarItemViewPickerSubView = ({
const selectedObjectMetadataItem = objectMetadataItems.find(
(item) => item.id === selectedObjectMetadataIdForView,
);
const backBarTitle =
selectedObjectMetadataItem?.labelPlural ?? t`Pick a view`;
const {
filteredItems: filteredViews,
@@ -102,8 +98,6 @@ export const SidePanelNewSidebarItemViewPickerSubView = ({
return (
<SidePanelSubViewWithSearch
backBarTitle={backBarTitle}
onBack={onBack}
searchPlaceholder={t`Search a view...`}
searchValue={searchValue}
onSearchChange={setSearchValue}
@@ -0,0 +1,63 @@
import { useDraftNavigationMenuItems } from '@/navigation-menu-item/hooks/useDraftNavigationMenuItems';
import { useNavigationMenuObjectMetadataFromDraft } from '@/navigation-menu-item/hooks/useNavigationMenuObjectMetadataFromDraft';
import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems';
import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems';
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { useSidePanelSubPageHistory } from '@/side-panel/hooks/useSidePanelSubPageHistory';
import { SidePanelNewSidebarItemViewSystemSubView } from '@/side-panel/pages/navigation-menu-item/components/SidePanelNewSidebarItemViewSystemSubView';
import { getAvailableObjectMetadataForNewSidebarItem } from '@/side-panel/pages/navigation-menu-item/utils/getAvailableObjectMetadataForNewSidebarItem';
import { selectedObjectMetadataIdForViewFlowState } from '@/side-panel/states/selectedObjectMetadataIdForViewFlowState';
import { SidePanelSubPages } from '@/side-panel/types/SidePanelSubPages';
import { useSetAtomComponentState } from '@/ui/utilities/state/jotai/hooks/useSetAtomComponentState';
import { ViewKey } from '@/views/types/ViewKey';
import { ViewType } from '@/views/types/ViewType';
import { useState } from 'react';
export const SidePanelNewSidebarItemViewSystemPickerSubPage = () => {
const { navigateToSidePanelSubPage } = useSidePanelSubPageHistory();
const [searchValue, setSearchValue] = useState('');
const setSelectedObjectMetadataIdForViewFlow = useSetAtomComponentState(
selectedObjectMetadataIdForViewFlowState,
);
const { currentDraft } = useDraftNavigationMenuItems();
const { objectMetadataItems } = useObjectMetadataItems();
const { activeNonSystemObjectMetadataItems } =
useFilteredObjectMetadataItems();
const { views, objectMetadataIdsWithIndexView } =
useNavigationMenuObjectMetadataFromDraft(currentDraft);
const objectMetadataIdsWithDisplayableViews = new Set(
views
.filter(
(view) =>
view.key !== ViewKey.Index && view.type !== ViewType.FieldsWidget,
)
.map((view) => view.objectMetadataId),
);
const { availableSystemObjectMetadataItemsForView } =
getAvailableObjectMetadataForNewSidebarItem({
objectMetadataItems,
activeNonSystemObjectMetadataItems,
objectMetadataIdsWithIndexView,
objectMetadataIdsWithDisplayableViews,
});
const handleSelectObject = (objectMetadataItem: ObjectMetadataItem) => {
setSelectedObjectMetadataIdForViewFlow(objectMetadataItem.id);
navigateToSidePanelSubPage(
SidePanelSubPages.NewSidebarItemViewPicker,
objectMetadataItem.labelPlural,
);
};
return (
<SidePanelNewSidebarItemViewSystemSubView
systemObjects={availableSystemObjectMetadataItemsForView}
searchValue={searchValue}
onSearchChange={setSearchValue}
onSelectObject={handleSelectObject}
/>
);
};
@@ -1,21 +1,20 @@
import { useLingui } from '@lingui/react/macro';
import { useIcons } from 'twenty-ui/display';
import { SidePanelGroup } from '@/side-panel/components/SidePanelGroup';
import { CommandMenuItem } from '@/command-menu/components/CommandMenuItem';
import { SidePanelList } from '@/side-panel/components/SidePanelList';
import { SidePanelSubViewWithSearch } from '@/side-panel/components/SidePanelSubViewWithSearch';
import { useSidePanelFilteredPickerItems } from '@/side-panel/hooks/useSidePanelFilteredPickerItems';
import { NavigationMenuItemStyleIcon } from '@/navigation-menu-item/components/NavigationMenuItemStyleIcon';
import { getStandardObjectIconColor } from '@/navigation-menu-item/utils/getStandardObjectIconColor';
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { SidePanelGroup } from '@/side-panel/components/SidePanelGroup';
import { SidePanelList } from '@/side-panel/components/SidePanelList';
import { SidePanelSubViewWithSearch } from '@/side-panel/components/SidePanelSubViewWithSearch';
import { useSidePanelFilteredPickerItems } from '@/side-panel/hooks/useSidePanelFilteredPickerItems';
import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem';
type SidePanelNewSidebarItemViewSystemSubViewProps = {
systemObjects: ObjectMetadataItem[];
searchValue: string;
onSearchChange: (value: string) => void;
onBack: () => void;
onSelectObject: (objectMetadataItem: ObjectMetadataItem) => void;
};
@@ -23,7 +22,6 @@ export const SidePanelNewSidebarItemViewSystemSubView = ({
systemObjects,
searchValue,
onSearchChange,
onBack,
onSelectObject,
}: SidePanelNewSidebarItemViewSystemSubViewProps) => {
const { t } = useLingui();
@@ -44,8 +42,6 @@ export const SidePanelNewSidebarItemViewSystemSubView = ({
return (
<SidePanelSubViewWithSearch
backBarTitle={t`System objects`}
onBack={onBack}
searchPlaceholder={t`Search a system object...`}
searchValue={searchValue}
onSearchChange={onSearchChange}
@@ -16,7 +16,6 @@ type SidePanelObjectPickerSubViewProps = {
objects: ObjectMetadataItem[];
searchValue: string;
onSearchChange: (value: string) => void;
onBack: () => void;
onOpenSystemPicker: () => void;
isViewItem: boolean;
onSelectObjectForViewEdit?: (objectMetadataItem: ObjectMetadataItem) => void;
@@ -33,7 +32,6 @@ export const SidePanelObjectPickerSubView = ({
objects,
searchValue,
onSearchChange,
onBack,
onOpenSystemPicker,
isViewItem,
onSelectObjectForViewEdit,
@@ -85,8 +83,6 @@ export const SidePanelObjectPickerSubView = ({
return (
<SidePanelSubViewWithSearch
backBarTitle={t`Pick an object`}
onBack={onBack}
searchPlaceholder={t`Search an object...`}
searchValue={searchValue}
onSearchChange={onSearchChange}
@@ -12,7 +12,6 @@ type SidePanelSystemObjectPickerSubViewProps = {
systemObjects: ObjectMetadataItem[];
searchValue: string;
onSearchChange: (value: string) => void;
onBack: () => void;
isViewItem: boolean;
onSelectObjectForViewEdit?: (objectMetadataItem: ObjectMetadataItem) => void;
onChangeObject: (
@@ -28,7 +27,6 @@ export const SidePanelSystemObjectPickerSubView = ({
systemObjects,
searchValue,
onSearchChange,
onBack,
isViewItem,
onSelectObjectForViewEdit,
onChangeObject,
@@ -69,8 +67,6 @@ export const SidePanelSystemObjectPickerSubView = ({
return (
<SidePanelSubViewWithSearch
backBarTitle={t`System objects`}
onBack={onBack}
searchPlaceholder={t`Search a system object...`}
searchValue={searchValue}
onSearchChange={onSearchChange}
@@ -1,13 +1,13 @@
import { isDefined } from 'twenty-shared/utils';
import { useSidePanelMenu } from '@/side-panel/hooks/useSidePanelMenu';
import { NavigationMenuItemType } from '@/navigation-menu-item/constants/NavigationMenuItemType';
import { useDraftNavigationMenuItemsAllFolders } from '@/navigation-menu-item/hooks/useDraftNavigationMenuItemsAllFolders';
import { useDraftNavigationMenuItemsWorkspaceFolders } from '@/navigation-menu-item/hooks/useDraftNavigationMenuItemsWorkspaceFolders';
import { useSelectedNavigationMenuItemEditItem } from '@/navigation-menu-item/hooks/useSelectedNavigationMenuItemEditItem';
import { useNavigationMenuItemMoveRemove } from '@/navigation-menu-item/hooks/useNavigationMenuItemMoveRemove';
import { useSelectedNavigationMenuItemEditItem } from '@/navigation-menu-item/hooks/useSelectedNavigationMenuItemEditItem';
import { selectedNavigationMenuItemInEditModeState } from '@/navigation-menu-item/states/selectedNavigationMenuItemInEditModeState';
import { useSidePanelMenu } from '@/side-panel/hooks/useSidePanelMenu';
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
import { NavigationMenuItemType } from '@/navigation-menu-item/constants/NavigationMenuItemType';
type FolderOption = {
id: string;
@@ -38,13 +38,7 @@ const excludeCurrentFolder = <T extends { id: string }>(
? folders
: folders.filter((folder) => folder.id !== currentFolderId);
type UseFolderPickerSelectionDataParams = {
onCloseSubView: () => void;
};
export const useFolderPickerSelectionData = ({
onCloseSubView,
}: UseFolderPickerSelectionDataParams) => {
export const useFolderPickerSelectionData = () => {
const { closeSidePanelMenu } = useSidePanelMenu();
const { moveToFolder } = useNavigationMenuItemMoveRemove();
const selectedNavigationMenuItemInEditMode = useAtomStateValue(
@@ -94,7 +88,6 @@ export const useFolderPickerSelectionData = ({
const handleSelectFolder = (folderId: string | null) => {
if (isDefined(selectedNavigationMenuItemInEditMode)) {
moveToFolder(selectedNavigationMenuItemInEditMode, folderId);
onCloseSubView();
closeSidePanelMenu();
}
};
@@ -1,5 +1,3 @@
import { useSidePanelHistory } from '@/side-panel/hooks/useSidePanelHistory';
import { SidePanelSubPageNavigationHeader } from '@/side-panel/pages/common/components/SidePanelSubPageNavigationHeader';
import { ChartFiltersSettingsInitializeStateEffect } from '@/side-panel/pages/page-layout/components/ChartFiltersSettingsInitializeStateEffect';
import { usePageLayoutIdFromContextStoreTargetedRecord } from '@/side-panel/pages/page-layout/hooks/usePageLayoutFromContextStoreTargetedRecord';
import { useUpdateCurrentWidgetConfig } from '@/side-panel/pages/page-layout/hooks/useUpdateCurrentWidgetConfig';
@@ -15,9 +13,9 @@ import { RecordFiltersComponentInstanceContext } from '@/object-record/record-fi
import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState';
import { InputLabel } from '@/ui/input/components/InputLabel';
import { useAtomComponentStateCallbackState } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateCallbackState';
import { useStore } from 'jotai';
import { styled } from '@linaria/react';
import { t } from '@lingui/core/macro';
import { useStore } from 'jotai';
import { themeCssVariables } from 'twenty-ui/theme-constants';
const StyledChartFiltersPageContainer = styled.div`
@@ -38,8 +36,6 @@ export const ChartFiltersSettings = ({
objectMetadataItem,
widget,
}: ChartFiltersSettingsProps) => {
const { goBackFromSidePanel } = useSidePanelHistory();
const { instanceId } = getChartFiltersSettingsInstanceId({
widgetId: widget.id,
objectMetadataItemId: objectMetadataItem.id,
@@ -79,32 +75,26 @@ export const ChartFiltersSettings = ({
};
return (
<>
<SidePanelSubPageNavigationHeader
title={t`Filter`}
onBackClick={goBackFromSidePanel}
/>
<StyledChartFiltersPageContainer>
<div>
<InputLabel>{t`Conditions`}</InputLabel>
<RecordFilterGroupsComponentInstanceContext.Provider
<StyledChartFiltersPageContainer>
<div>
<InputLabel>{t`Conditions`}</InputLabel>
<RecordFilterGroupsComponentInstanceContext.Provider
value={{ instanceId }}
>
<RecordFiltersComponentInstanceContext.Provider
value={{ instanceId }}
>
<RecordFiltersComponentInstanceContext.Provider
value={{ instanceId }}
>
<AdvancedFilterSidePanelContainer
onUpdate={handleFiltersUpdate}
objectMetadataItem={objectMetadataItem}
isWorkflowFindRecords={false}
/>
<ChartFiltersSettingsInitializeStateEffect
initialChartFilters={chartWidgetConfiguration.filter}
/>
</RecordFiltersComponentInstanceContext.Provider>
</RecordFilterGroupsComponentInstanceContext.Provider>
</div>
</StyledChartFiltersPageContainer>
</>
<AdvancedFilterSidePanelContainer
onUpdate={handleFiltersUpdate}
objectMetadataItem={objectMetadataItem}
isWorkflowFindRecords={false}
/>
<ChartFiltersSettingsInitializeStateEffect
initialChartFilters={chartWidgetConfiguration.filter}
/>
</RecordFiltersComponentInstanceContext.Provider>
</RecordFilterGroupsComponentInstanceContext.Provider>
</div>
</StyledChartFiltersPageContainer>
);
};
@@ -1,14 +1,14 @@
import { ChartFiltersSettings } from '@/side-panel/pages/page-layout/components/ChartFiltersSettings';
import { usePageLayoutIdFromContextStoreTargetedRecord } from '@/side-panel/pages/page-layout/hooks/usePageLayoutFromContextStoreTargetedRecord';
import { isChartWidget } from '@/side-panel/pages/page-layout/utils/isChartWidget';
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
import { pageLayoutDraftComponentState } from '@/page-layout/states/pageLayoutDraftComponentState';
import { pageLayoutEditingWidgetIdComponentState } from '@/page-layout/states/pageLayoutEditingWidgetIdComponentState';
import { ChartFiltersSettings } from '@/side-panel/pages/page-layout/components/ChartFiltersSettings';
import { usePageLayoutIdFromContextStoreTargetedRecord } from '@/side-panel/pages/page-layout/hooks/usePageLayoutFromContextStoreTargetedRecord';
import { isChartWidget } from '@/side-panel/pages/page-layout/utils/isChartWidget';
import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue';
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
import { isDefined } from 'twenty-shared/utils';
export const SidePanelPageLayoutGraphFilter = () => {
export const SidePanelChartFilterSubPage = () => {
const { pageLayoutId } = usePageLayoutIdFromContextStoreTargetedRecord();
const pageLayoutDraft = useAtomComponentStateValue(
@@ -29,29 +29,26 @@ export const SidePanelPageLayoutGraphFilter = () => {
if (
!isDefined(widgetInEditMode) ||
!isDefined(widgetInEditMode.objectMetadataId) ||
!isChartWidget(widgetInEditMode)
!isChartWidget(widgetInEditMode) ||
!isDefined(widgetInEditMode.objectMetadataId)
) {
return null;
}
const objectMetadataItem = objectMetadataItems.find(
(objectMetadataItem) =>
objectMetadataItem.id === widgetInEditMode?.objectMetadataId,
(item) => item.id === widgetInEditMode.objectMetadataId,
);
if (!isDefined(objectMetadataItem)) {
throw new Error(
`Object metadata item not found for id ${widgetInEditMode?.objectMetadataId}`,
`Object metadata item not found for id ${widgetInEditMode.objectMetadataId}`,
);
}
return (
<>
<ChartFiltersSettings
widget={widgetInEditMode}
objectMetadataItem={objectMetadataItem}
/>
</>
<ChartFiltersSettings
widget={widgetInEditMode}
objectMetadataItem={objectMetadataItem}
/>
);
};
@@ -1,26 +1,17 @@
import { useSidePanelHistory } from '@/side-panel/hooks/useSidePanelHistory';
import { SidePanelSubPageNavigationHeader } from '@/side-panel/pages/common/components/SidePanelSubPageNavigationHeader';
import { usePageLayoutIdForRecordPageLayoutFromContextStoreTargetedRecord } from '@/side-panel/pages/page-layout/hooks/usePageLayoutIdForRecordPageLayoutFromContextStoreTargetedRecord';
import { useWidgetInEditMode } from '@/side-panel/pages/page-layout/hooks/useWidgetInEditMode';
import { useTemporaryFieldsConfiguration } from '@/page-layout/hooks/useTemporaryFieldsConfiguration';
import { FieldsConfigurationEditor } from '@/page-layout/widgets/fields/components/FieldsConfigurationEditor';
import { FieldsWidgetGroupsDraftInitializationEffect } from '@/page-layout/widgets/fields/components/FieldsWidgetGroupsDraftInitializationEffect';
import { usePageLayoutIdForRecordPageLayoutFromContextStoreTargetedRecord } from '@/side-panel/pages/page-layout/hooks/usePageLayoutIdForRecordPageLayoutFromContextStoreTargetedRecord';
import { useWidgetInEditMode } from '@/side-panel/pages/page-layout/hooks/useWidgetInEditMode';
import { styled } from '@linaria/react';
import { t } from '@lingui/core/macro';
import { isDefined } from 'twenty-shared/utils';
import { themeCssVariables } from 'twenty-ui/theme-constants';
import {
type FieldsConfiguration,
WidgetConfigurationType,
} from '~/generated-metadata/graphql';
import { themeCssVariables } from 'twenty-ui/theme-constants';
const StyledOuterContainer = styled.div`
display: flex;
flex-direction: column;
height: 100%;
`;
const StyledContainer = styled.div`
const StyledFieldsLayoutContainer = styled.div`
display: flex;
flex: 1;
flex-direction: column;
@@ -29,9 +20,7 @@ const StyledContainer = styled.div`
padding: ${themeCssVariables.spacing[2]};
`;
export const SidePanelPageLayoutFieldsLayout = () => {
const { goBackFromSidePanel } = useSidePanelHistory();
export const SidePanelFieldsLayoutSubPage = () => {
const { pageLayoutId } =
usePageLayoutIdForRecordPageLayoutFromContextStoreTargetedRecord();
@@ -44,29 +33,23 @@ export const SidePanelPageLayoutFieldsLayout = () => {
const widgetConfiguration = widgetInEditMode.configuration;
const fieldsConfiguration: FieldsConfiguration =
const resolvedFieldsConfiguration: FieldsConfiguration =
isDefined(widgetConfiguration) &&
widgetConfiguration.configurationType === WidgetConfigurationType.FIELDS
? (widgetConfiguration as FieldsConfiguration)
: temporaryFieldsConfiguration;
return (
<StyledOuterContainer>
<SidePanelSubPageNavigationHeader
title={t`Layout`}
onBackClick={goBackFromSidePanel}
<StyledFieldsLayoutContainer>
<FieldsWidgetGroupsDraftInitializationEffect
viewId={resolvedFieldsConfiguration.viewId ?? null}
pageLayoutId={pageLayoutId}
widgetId={widgetInEditMode.id}
/>
<StyledContainer>
<FieldsWidgetGroupsDraftInitializationEffect
viewId={fieldsConfiguration.viewId ?? null}
pageLayoutId={pageLayoutId}
widgetId={widgetInEditMode.id}
/>
<FieldsConfigurationEditor
pageLayoutId={pageLayoutId}
widgetId={widgetInEditMode.id}
/>
</StyledContainer>
</StyledOuterContainer>
<FieldsConfigurationEditor
pageLayoutId={pageLayoutId}
widgetId={widgetInEditMode.id}
/>
</StyledFieldsLayoutContainer>
);
};
@@ -1,10 +1,10 @@
import { pageLayoutDraftComponentState } from '@/page-layout/states/pageLayoutDraftComponentState';
import { pageLayoutEditingWidgetIdComponentState } from '@/page-layout/states/pageLayoutEditingWidgetIdComponentState';
import { WidgetComponentInstanceContext } from '@/page-layout/widgets/states/contexts/WidgetComponentInstanceContext';
import { ChartSettings } from '@/side-panel/pages/page-layout/components/ChartSettings';
import { WidgetSettingsFooter } from '@/side-panel/pages/page-layout/components/WidgetSettingsFooter';
import { usePageLayoutIdFromContextStoreTargetedRecord } from '@/side-panel/pages/page-layout/hooks/usePageLayoutFromContextStoreTargetedRecord';
import { isChartWidget } from '@/side-panel/pages/page-layout/utils/isChartWidget';
import { pageLayoutDraftComponentState } from '@/page-layout/states/pageLayoutDraftComponentState';
import { pageLayoutEditingWidgetIdComponentState } from '@/page-layout/states/pageLayoutEditingWidgetIdComponentState';
import { WidgetComponentInstanceContext } from '@/page-layout/widgets/states/contexts/WidgetComponentInstanceContext';
import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue';
import { styled } from '@linaria/react';
import { isDefined } from 'twenty-shared/utils';
@@ -3,16 +3,16 @@ import { CommandMenuItemToggle } from '@/command-menu/components/CommandMenuItem
import { useFieldsWidgetGroups } from '@/page-layout/widgets/fields/hooks/useFieldsWidgetGroups';
import { SidePanelGroup } from '@/side-panel/components/SidePanelGroup';
import { SidePanelList } from '@/side-panel/components/SidePanelList';
import { useSidePanelSubPageHistory } from '@/side-panel/hooks/useSidePanelSubPageHistory';
import { NewFieldDefaultVisibilityToggle } from '@/side-panel/pages/page-layout/components/NewFieldDefaultVisibilityToggle';
import { WidgetSettingsFooter } from '@/side-panel/pages/page-layout/components/WidgetSettingsFooter';
import { useNavigatePageLayoutSidePanel } from '@/side-panel/pages/page-layout/hooks/useNavigatePageLayoutSidePanel';
import { usePageLayoutIdForRecordPageLayoutFromContextStoreTargetedRecord } from '@/side-panel/pages/page-layout/hooks/usePageLayoutIdForRecordPageLayoutFromContextStoreTargetedRecord';
import { useUpdateCurrentWidgetConfig } from '@/side-panel/pages/page-layout/hooks/useUpdateCurrentWidgetConfig';
import { useWidgetInEditMode } from '@/side-panel/pages/page-layout/hooks/useWidgetInEditMode';
import { SidePanelSubPages } from '@/side-panel/types/SidePanelSubPages';
import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem';
import { styled } from '@linaria/react';
import { useLingui } from '@lingui/react/macro';
import { SidePanelPages } from 'twenty-shared/types';
import { isDefined } from 'twenty-shared/utils';
import { IconChevronDown, IconLayoutSidebarRight } from 'twenty-ui/display';
import { type FieldsConfiguration } from '~/generated-metadata/graphql';
@@ -32,7 +32,7 @@ const StyledSidePanelContainer = styled.div`
export const SidePanelPageLayoutFieldsSettings = () => {
const { t } = useLingui();
const { navigatePageLayoutSidePanel } = useNavigatePageLayoutSidePanel();
const { navigateToSidePanelSubPage } = useSidePanelSubPageHistory();
const { pageLayoutId, objectNameSingular } =
usePageLayoutIdForRecordPageLayoutFromContextStoreTargetedRecord();
@@ -60,9 +60,7 @@ export const SidePanelPageLayoutFieldsSettings = () => {
);
const handleNavigateToLayout = () => {
navigatePageLayoutSidePanel({
sidePanelPage: SidePanelPages.PageLayoutFieldsLayout,
});
navigateToSidePanelSubPage(SidePanelSubPages.PageLayoutFieldsLayout);
};
const isShouldAllowUserToSeeHiddenFieldsToggled =
@@ -4,8 +4,8 @@ import { CommandMenuItemNumberInput } from '@/command-menu/components/CommandMen
import { CommandMenuItemTextInput } from '@/command-menu/components/CommandMenuItemTextInput';
import { CommandMenuItemToggle } from '@/command-menu/components/CommandMenuItemToggle';
import { SIDE_PANEL_SELECTABLE_LIST_ID } from '@/side-panel/constants/SidePanelSelectableListId';
import { useSidePanelSubPageHistory } from '@/side-panel/hooks/useSidePanelSubPageHistory';
import { useChartSettingsValues } from '@/side-panel/pages/page-layout/hooks/useChartSettingsValues';
import { useNavigatePageLayoutSidePanel } from '@/side-panel/pages/page-layout/hooks/useNavigatePageLayoutSidePanel';
import { usePageLayoutIdFromContextStoreTargetedRecord } from '@/side-panel/pages/page-layout/hooks/usePageLayoutFromContextStoreTargetedRecord';
import { useUpdateChartSettingInput } from '@/side-panel/pages/page-layout/hooks/useUpdateChartSettingInput';
import { useUpdateChartSettingTextInput } from '@/side-panel/pages/page-layout/hooks/useUpdateChartSettingTextInput';
@@ -14,6 +14,7 @@ import { type ChartConfiguration } from '@/side-panel/pages/page-layout/types/Ch
import { CHART_CONFIGURATION_SETTING_IDS } from '@/side-panel/pages/page-layout/types/ChartConfigurationSettingIds';
import { type ChartSettingsItem } from '@/side-panel/pages/page-layout/types/ChartSettingsGroup';
import { isMinMaxRangeValid } from '@/side-panel/pages/page-layout/utils/isMinMaxRangeValid';
import { SidePanelSubPages } from '@/side-panel/types/SidePanelSubPages';
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
import { useCloseAnyOpenDropdown } from '@/ui/layout/dropdown/hooks/useCloseAnyOpenDropdown';
import { useOpenDropdown } from '@/ui/layout/dropdown/hooks/useOpenDropdown';
@@ -21,7 +22,6 @@ 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 { SidePanelPages } from 'twenty-shared/types';
import { isDefined } from 'twenty-shared/utils';
type ChartSettingItemProps = {
@@ -41,7 +41,7 @@ export const ChartSettingItem = ({
const { setSelectedItemId } = useSelectableList(
SIDE_PANEL_SELECTABLE_LIST_ID,
);
const { navigatePageLayoutSidePanel } = useNavigatePageLayoutSidePanel();
const { navigateToSidePanelSubPage } = useSidePanelSubPageHistory();
const { getChartSettingsValues } = useChartSettingsValues({
objectMetadataId,
@@ -80,9 +80,7 @@ export const ChartSettingItem = ({
};
const handleFilterClick = () => {
navigatePageLayoutSidePanel({
sidePanelPage: SidePanelPages.PageLayoutGraphFilter,
});
navigateToSidePanelSubPage(SidePanelSubPages.PageLayoutGraphFilter);
};
if (item.id === CHART_CONFIGURATION_SETTING_IDS.FILTER) {
const filterValue = getChartSettingsValues(item.id);
@@ -4,7 +4,5 @@ export type PageLayoutSidePanelPage =
| SidePanelPages.PageLayoutWidgetTypeSelect
| SidePanelPages.PageLayoutGraphTypeSelect
| SidePanelPages.PageLayoutIframeSettings
| SidePanelPages.PageLayoutGraphFilter
| SidePanelPages.PageLayoutTabSettings
| SidePanelPages.PageLayoutFieldsSettings
| SidePanelPages.PageLayoutFieldsLayout;
| SidePanelPages.PageLayoutFieldsSettings;
@@ -4,9 +4,7 @@ import { assertUnreachable } from 'twenty-shared/utils';
import {
IconAppWindow,
IconChartPie,
IconFilter,
IconFrame,
IconLayoutSidebarRight,
IconList,
} from 'twenty-ui/display';
@@ -18,14 +16,10 @@ export const getPageLayoutIcon = (page: PageLayoutSidePanelPage) => {
return IconChartPie;
case SidePanelPages.PageLayoutIframeSettings:
return IconFrame;
case SidePanelPages.PageLayoutGraphFilter:
return IconFilter;
case SidePanelPages.PageLayoutTabSettings:
return IconAppWindow;
case SidePanelPages.PageLayoutFieldsSettings:
return IconList;
case SidePanelPages.PageLayoutFieldsLayout:
return IconLayoutSidebarRight;
default:
assertUnreachable(page);
}
@@ -11,14 +11,10 @@ export const getPageLayoutPageTitle = (page: PageLayoutSidePanelPage) => {
return t`Select Graph Type`;
case SidePanelPages.PageLayoutIframeSettings:
return t`iFrame Settings`;
case SidePanelPages.PageLayoutGraphFilter:
return t`Configure filters`;
case SidePanelPages.PageLayoutTabSettings:
return t`Tab Settings`;
case SidePanelPages.PageLayoutFieldsSettings:
return t`Fields Settings`;
case SidePanelPages.PageLayoutFieldsLayout:
return t`Layout`;
default:
assertUnreachable(page);
}
@@ -0,0 +1,9 @@
import { SidePanelPageComponentInstanceContext } from '@/side-panel/states/contexts/SidePanelPageComponentInstanceContext';
import { createAtomComponentState } from '@/ui/utilities/state/jotai/utils/createAtomComponentState';
export const selectedObjectMetadataIdForViewFlowState =
createAtomComponentState<string | null>({
key: 'side-panel/selected-object-metadata-id-for-view-flow',
defaultValue: null,
componentInstanceContext: SidePanelPageComponentInstanceContext,
});
@@ -0,0 +1,11 @@
import { SidePanelPageComponentInstanceContext } from '@/side-panel/states/contexts/SidePanelPageComponentInstanceContext';
import { type SidePanelSubPageEntry } from '@/side-panel/types/SidePanelSubPageEntry';
import { createAtomComponentState } from '@/ui/utilities/state/jotai/utils/createAtomComponentState';
export const sidePanelSubPageStackComponentState = createAtomComponentState<
SidePanelSubPageEntry[]
>({
key: 'side-panel/sub-page-stack',
defaultValue: [],
componentInstanceContext: SidePanelPageComponentInstanceContext,
});
@@ -0,0 +1,7 @@
import { type SidePanelSubPages } from '@/side-panel/types/SidePanelSubPages';
export type SidePanelSubPageEntry = {
id: string;
subPage: SidePanelSubPages;
title: string;
};
@@ -0,0 +1,11 @@
export enum SidePanelSubPages {
PageLayoutGraphFilter = 'page-layout-graph-filter',
PageLayoutFieldsLayout = 'page-layout-fields-layout',
NewSidebarItemViewObjectPicker = 'new-sidebar-item-view-object-picker',
NewSidebarItemViewPicker = 'new-sidebar-item-view-picker',
NewSidebarItemViewSystemPicker = 'new-sidebar-item-view-system-picker',
NewSidebarItemObjectPicker = 'new-sidebar-item-object-picker',
NewSidebarItemObjectSystemPicker = 'new-sidebar-item-object-system-picker',
NewSidebarItemRecord = 'new-sidebar-item-record',
EditFolderPicker = 'edit-folder-picker',
}
@@ -0,0 +1,30 @@
import { SidePanelSubPages } from '@/side-panel/types/SidePanelSubPages';
import { t } from '@lingui/core/macro';
import { assertUnreachable } from 'twenty-shared/utils';
export const getSidePanelSubPageTitle = (
subPage: SidePanelSubPages,
): string => {
switch (subPage) {
case SidePanelSubPages.PageLayoutGraphFilter:
return t`Filters`;
case SidePanelSubPages.PageLayoutFieldsLayout:
return t`Layout`;
case SidePanelSubPages.NewSidebarItemViewObjectPicker:
return t`Pick an object`;
case SidePanelSubPages.NewSidebarItemViewPicker:
return t`Pick a view`;
case SidePanelSubPages.NewSidebarItemViewSystemPicker:
return t`System objects`;
case SidePanelSubPages.NewSidebarItemObjectPicker:
return t`Pick an object`;
case SidePanelSubPages.NewSidebarItemObjectSystemPicker:
return t`System objects`;
case SidePanelSubPages.NewSidebarItemRecord:
return t`Add a record`;
case SidePanelSubPages.EditFolderPicker:
return t`Move to a folder`;
default:
assertUnreachable(subPage);
}
};
@@ -18,11 +18,9 @@ export enum SidePanelPages {
ViewPreviousAIChats = 'view-previous-ai-chats',
PageLayoutWidgetTypeSelect = 'page-layout-widget-type-select',
PageLayoutGraphTypeSelect = 'page-layout-graph-type-select',
PageLayoutGraphFilter = 'page-layout-graph-filter',
PageLayoutIframeSettings = 'page-layout-iframe-settings',
PageLayoutTabSettings = 'page-layout-tab-settings',
PageLayoutFieldsSettings = 'page-layout-fields-settings',
PageLayoutFieldsLayout = 'page-layout-fields-layout',
ViewFrontComponent = 'view-front-component',
NavigationMenuItemEdit = 'navigation-menu-item-edit',
NavigationMenuAddItem = 'navigation-menu-add-item',