Compare commits

...

7 Commits

Author SHA1 Message Date
Devessier 291b356a8c Merge remote-tracking branch 'origin/main' into fields-widget-edition-cross-button 2026-03-11 13:40:01 +01:00
Devessier 922bb91f7a fix: use correct fields widget page 2026-03-10 19:41:51 +01:00
Devessier dfe4b956b9 refactor: restrict which pages are considered sub pages 2026-03-10 19:20:56 +01:00
Devessier 1501b7f7a4 refactor: improve implementation 2026-03-10 18:20:23 +01:00
Devessier 82d2a7f4fa fix: fix sub page name 2026-03-10 17:37:19 +01:00
Devessier 32212feaa3 Merge remote-tracking branch 'origin/main' into fields-widget-edition-cross-button 2026-03-10 17:22:53 +01:00
Devessier 4eeb26be9b feat: scaffold 2026-03-10 17:22:40 +01:00
6 changed files with 59 additions and 64 deletions
@@ -2,7 +2,11 @@ import { CommandMenuContextProvider } from '@/command-menu-item/contexts/Command
import { SidePanelContainer } from '@/side-panel/components/SidePanelContainer';
import { SidePanelTopBar } from '@/side-panel/components/SidePanelTopBar';
import { SIDE_PANEL_PAGES_CONFIG } from '@/side-panel/constants/SidePanelPagesConfig';
import { SIDE_PANEL_SUB_PAGES } from '@/side-panel/constants/SidePanelSubPages';
import { useSidePanelHistory } from '@/side-panel/hooks/useSidePanelHistory';
import { SidePanelSubPageNavigationHeader } from '@/side-panel/pages/common/components/SidePanelSubPageNavigationHeader';
import { SidePanelPageComponentInstanceContext } from '@/side-panel/states/contexts/SidePanelPageComponentInstanceContext';
import { sidePanelNavigationStackState } from '@/side-panel/states/sidePanelNavigationStackState';
import { sidePanelPageInfoState } from '@/side-panel/states/sidePanelPageInfoState';
import { sidePanelPageState } from '@/side-panel/states/sidePanelPageState';
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
@@ -20,6 +24,14 @@ const StyledSidePanelContent = styled.div`
export const SidePanelRouter = () => {
const sidePanelPage = useAtomStateValue(sidePanelPageState);
const sidePanelPageInfo = useAtomStateValue(sidePanelPageInfoState);
const sidePanelNavigationStack = useAtomStateValue(
sidePanelNavigationStackState,
);
const { goBackFromSidePanel } = useSidePanelHistory();
const isSubPage =
sidePanelNavigationStack.length > 1 &&
SIDE_PANEL_SUB_PAGES.has(sidePanelPage);
const rawPageComponent = isDefined(sidePanelPage)
? SIDE_PANEL_PAGES_CONFIG.get(sidePanelPage)
@@ -50,6 +62,12 @@ export const SidePanelRouter = () => {
>
<SidePanelTopBar />
</motion.div>
{isSubPage && isDefined(sidePanelPageInfo.title) && (
<SidePanelSubPageNavigationHeader
title={sidePanelPageInfo.title}
onBackClick={goBackFromSidePanel}
/>
)}
<StyledSidePanelContent>
<CommandMenuContextProvider
isInSidePanel={true}
@@ -2,28 +2,29 @@ import { SidePanelBackButton } from '@/side-panel/components/SidePanelBackButton
import { SidePanelPageInfo } from '@/side-panel/components/SidePanelPageInfo';
import { SidePanelTopBarInputFocusEffect } from '@/side-panel/components/SidePanelTopBarInputFocusEffect';
import { SidePanelTopBarRightCornerIcon } from '@/side-panel/components/SidePanelTopBarRightCornerIcon';
import { SIDE_PANEL_FOCUS_ID } from '@/side-panel/constants/SidePanelFocusId';
import { SIDE_PANEL_SUB_PAGES } from '@/side-panel/constants/SidePanelSubPages';
import { SIDE_PANEL_TOP_BAR_HEIGHT } from '@/side-panel/constants/SidePanelTopBarHeight';
import { SIDE_PANEL_TOP_BAR_HEIGHT_MOBILE } from '@/side-panel/constants/SidePanelTopBarHeightMobile';
import { SIDE_PANEL_FOCUS_ID } from '@/side-panel/constants/SidePanelFocusId';
import { useSidePanelMenu } from '@/side-panel/hooks/useSidePanelMenu';
import { useSidePanelContextChips } from '@/side-panel/hooks/useSidePanelContextChips';
import { useSidePanelMenu } from '@/side-panel/hooks/useSidePanelMenu';
import { sidePanelNavigationStackState } from '@/side-panel/states/sidePanelNavigationStackState';
import { sidePanelPageState } from '@/side-panel/states/sidePanelPageState';
import { sidePanelSearchState } from '@/side-panel/states/sidePanelSearchState';
import { usePushFocusItemToFocusStack } from '@/ui/utilities/focus/hooks/usePushFocusItemToFocusStack';
import { useRemoveFocusItemFromFocusStackById } from '@/ui/utilities/focus/hooks/useRemoveFocusItemFromFocusStackById';
import { FocusComponentType } from '@/ui/utilities/focus/types/FocusComponentType';
import { styled } from '@linaria/react';
import { useLingui } from '@lingui/react/macro';
import { AnimatePresence, motion } from 'framer-motion';
import { useAtomState } from '@/ui/utilities/state/jotai/hooks/useAtomState';
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
import { styled } from '@linaria/react';
import { useLingui } from '@lingui/react/macro';
import { motion } from 'framer-motion';
import { useContext, useRef } from 'react';
import { SidePanelPages } from 'twenty-shared/types';
import { IconX } from 'twenty-ui/display';
import { IconButton } from 'twenty-ui/input';
import { useIsMobile } from 'twenty-ui/utilities';
import { ThemeContext, themeCssVariables } from 'twenty-ui/theme-constants';
import { useIsMobile } from 'twenty-ui/utilities';
const StyledInputContainer = styled.div<{ isMobile: boolean }>`
align-items: center;
@@ -93,10 +94,6 @@ export const SidePanelTopBar = () => {
const sidePanelPage = useAtomStateValue(sidePanelPageState);
const sidePanelNavigationStack = useAtomStateValue(
sidePanelNavigationStackState,
);
const { theme } = useContext(ThemeContext);
const { contextChips } = useSidePanelContextChips();
@@ -124,45 +121,36 @@ export const SidePanelTopBar = () => {
});
};
const sidePanelNavigationStack = useAtomStateValue(
sidePanelNavigationStackState,
);
const isOnSubPage = SIDE_PANEL_SUB_PAGES.has(sidePanelPage);
const canGoBack = sidePanelNavigationStack.length > 1;
const shouldShowCloseButton =
!isMobile && sidePanelNavigationStack.length === 1;
const shouldShowBackButton = canGoBack;
const shouldShowBackButton = !isMobile && canGoBack && !isOnSubPage;
const shouldShowCloseButton = !isMobile && !shouldShowBackButton;
const lastChip = contextChips.at(-1);
return (
<StyledInputContainer isMobile={isMobile}>
<StyledContentContainer>
<AnimatePresence>
{shouldShowBackButton && (
<motion.div
exit={{ opacity: 0, width: 0 }}
transition={{
duration: theme.animation.duration.instant,
}}
>
<SidePanelBackButton />
</motion.div>
)}
{shouldShowCloseButton && (
<motion.div
exit={{ opacity: 0, width: 0 }}
transition={{
duration: theme.animation.duration.instant,
}}
>
<IconButton
Icon={IconX}
size="small"
variant="tertiary"
onClick={closeSidePanelMenu}
/>
</motion.div>
)}
</AnimatePresence>
{shouldShowBackButton && <SidePanelBackButton />}
{shouldShowCloseButton && (
<motion.div
exit={{ opacity: 0, width: 0 }}
transition={{
duration: theme.animation.duration.instant,
}}
>
<IconButton
Icon={IconX}
size="small"
variant="tertiary"
onClick={closeSidePanelMenu}
/>
</motion.div>
)}
{lastChip &&
sidePanelPage !== SidePanelPages.Root &&
sidePanelPage !== SidePanelPages.SearchRecords && (
@@ -0,0 +1,6 @@
import { SidePanelPages } from 'twenty-shared/types';
export const SIDE_PANEL_SUB_PAGES = new Set<SidePanelPages>([
SidePanelPages.PageLayoutGraphFilter,
SidePanelPages.PageLayoutFieldsLayout,
]);
@@ -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,
@@ -80,10 +76,6 @@ export const ChartFiltersSettings = ({
return (
<>
<SidePanelSubPageNavigationHeader
title={t`Filter`}
onBackClick={goBackFromSidePanel}
/>
<StyledChartFiltersPageContainer>
<div>
<InputLabel>{t`Conditions`}</InputLabel>
@@ -1,18 +1,15 @@
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;
@@ -30,8 +27,6 @@ const StyledContainer = styled.div`
`;
export const SidePanelPageLayoutFieldsLayout = () => {
const { goBackFromSidePanel } = useSidePanelHistory();
const { pageLayoutId } =
usePageLayoutIdForRecordPageLayoutFromContextStoreTargetedRecord();
@@ -52,10 +47,6 @@ export const SidePanelPageLayoutFieldsLayout = () => {
return (
<StyledOuterContainer>
<SidePanelSubPageNavigationHeader
title={t`Layout`}
onBackClick={goBackFromSidePanel}
/>
<StyledContainer>
<FieldsWidgetGroupsDraftInitializationEffect
viewId={fieldsConfiguration.viewId ?? null}
@@ -12,7 +12,7 @@ export const getPageLayoutPageTitle = (page: PageLayoutSidePanelPage) => {
case SidePanelPages.PageLayoutIframeSettings:
return t`iFrame Settings`;
case SidePanelPages.PageLayoutGraphFilter:
return t`Configure filters`;
return t`Filter`;
case SidePanelPages.PageLayoutTabSettings:
return t`Tab Settings`;
case SidePanelPages.PageLayoutFieldsSettings: