From bfefcd375573e44d659a84f107d35a8f4a93fc5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20Malfait?= Date: Mon, 8 Jun 2026 22:47:05 +0200 Subject: [PATCH] feat(twenty-front): generalize the page primary/secondary bars (flat redesign) (#21308) Replaces #21279 and #21282 with one clean PR from `main`. Generalizes the settings primary-bar / secondary-bar card chrome to the record index, record show and standalone pages via a shared `PageCardLayout` + `PageCardHeader` (the side panel sits as a sibling of the content card), and applies the new flat design direction: square corners on the card, side panel and loading skeletons. Iterating toward the new design (Figma node 102282-221623); the confirmed direction and the explicit "remove rounded corners" change are in, remaining designer specifics to follow. --- .../components/PageContentSkeletonLoader.tsx | 77 ++++++++------- .../components/RightPanelSkeletonLoader.tsx | 14 --- .../components/UserOrMetadataLoader.tsx | 4 +- .../src/modules/app/components/LazyRoute.tsx | 9 +- .../modules/app/hooks/useCreateAppRouter.tsx | 88 +++++++++-------- .../components/CommandMenuItemRenderer.tsx | 16 +++- .../PinnedCommandMenuItemButtons.tsx | 9 +- .../components/CommandMenuButton.tsx | 10 +- .../MainContainerLayoutWithSidePanel.tsx | 83 ---------------- .../components/RecordIndexContainer.tsx | 94 +++++++----------- .../components/RecordIndexContainerGater.tsx | 13 ++- .../components/RecordIndexPageHeader.tsx | 29 +++--- .../components/RecordIndexSkeletonLoader.tsx | 33 ++++++- .../components/RecordIndexViewBar.tsx | 39 ++++++++ .../components/SettingsSkeletonLoader.tsx | 63 ++++-------- .../components/layout/SettingsPageLayout.tsx | 95 ++++--------------- .../components/SidePanelForDesktop.tsx | 6 +- .../components/SidePanelToggleButton.tsx | 6 +- .../components/BackgroundMockPage.tsx | 74 ++++++++------- .../components/MainAppLayoutWithSidePanel.tsx | 37 ++++++++ .../page/components/PageCardHeader.tsx} | 37 +++++--- .../layout/page/components/PageCardLayout.tsx | 69 ++++++++++++++ .../ui/layout/page/components/PagePanel.tsx | 1 - .../page/components/ShowPageContainer.tsx | 1 - .../components/ResizablePanelGap.tsx | 7 ++ .../pages/object-record/RecordShowPage.tsx | 66 ++++++------- .../object-record/RecordShowPageHeader.tsx | 11 +-- .../page-layout/StandalonePageHeader.tsx | 19 +++- .../page-layout/StandalonePageLayoutPage.tsx | 35 ++++--- 29 files changed, 528 insertions(+), 517 deletions(-) delete mode 100644 packages/twenty-front/src/loading/components/RightPanelSkeletonLoader.tsx delete mode 100644 packages/twenty-front/src/modules/object-record/components/MainContainerLayoutWithSidePanel.tsx create mode 100644 packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexViewBar.tsx create mode 100644 packages/twenty-front/src/modules/ui/layout/page/components/MainAppLayoutWithSidePanel.tsx rename packages/twenty-front/src/modules/{settings/components/layout/SettingsPageHeader.tsx => ui/layout/page/components/PageCardHeader.tsx} (77%) create mode 100644 packages/twenty-front/src/modules/ui/layout/page/components/PageCardLayout.tsx diff --git a/packages/twenty-front/src/loading/components/PageContentSkeletonLoader.tsx b/packages/twenty-front/src/loading/components/PageContentSkeletonLoader.tsx index 2614194cc95..1389d4604d1 100644 --- a/packages/twenty-front/src/loading/components/PageContentSkeletonLoader.tsx +++ b/packages/twenty-front/src/loading/components/PageContentSkeletonLoader.tsx @@ -1,59 +1,58 @@ import { SKELETON_LOADER_HEIGHT_SIZES } from '@/activities/components/SkeletonLoader'; -import { PageBody } from '@/ui/layout/page/components/PageBody'; -import { PAGE_BAR_MIN_HEIGHT } from '@/ui/layout/page/constants/PageBarMinHeight'; +import { PageCardHeader } from '@/ui/layout/page/components/PageCardHeader'; +import { PageCardLayout } from '@/ui/layout/page/components/PageCardLayout'; import { styled } from '@linaria/react'; -import { useContext } from 'react'; +import { type ReactNode, useContext } from 'react'; import Skeleton, { SkeletonTheme } from 'react-loading-skeleton'; import { ThemeContext, themeCssVariables, } from 'twenty-ui-deprecated/theme-constants'; -const StyledHeaderSkeleton = styled.div` - align-items: center; - background: ${themeCssVariables.background.noisy}; +const StyledBody = styled.div` display: flex; - flex-direction: row; + flex-direction: column; gap: ${themeCssVariables.spacing[2]}; - justify-content: space-between; - min-height: ${PAGE_BAR_MIN_HEIGHT}px; padding: ${themeCssVariables.spacing[3]}; `; -const StyledHeaderLeft = styled.div` - flex: 1; -`; +type PageContentSkeletonLoaderProps = { + secondaryBar?: ReactNode; +}; -export const PageContentSkeletonLoader = () => { +export const PageContentSkeletonLoader = ({ + secondaryBar, +}: PageContentSkeletonLoaderProps) => { const { theme } = useContext(ThemeContext); return ( - <> - - - - - - - - + } + title={ + + } /> - - - {null} - + } + secondaryBar={secondaryBar} + showInformationBanner={false} + > + + + + + ); }; diff --git a/packages/twenty-front/src/loading/components/RightPanelSkeletonLoader.tsx b/packages/twenty-front/src/loading/components/RightPanelSkeletonLoader.tsx deleted file mode 100644 index aa182aed4f0..00000000000 --- a/packages/twenty-front/src/loading/components/RightPanelSkeletonLoader.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import { PageContentSkeletonLoader } from '~/loading/components/PageContentSkeletonLoader'; -import { styled } from '@linaria/react'; - -const StyledRightPanelContainer = styled.div` - display: flex; - flex-direction: column; - width: 100%; -`; - -export const RightPanelSkeletonLoader = () => ( - - - -); diff --git a/packages/twenty-front/src/loading/components/UserOrMetadataLoader.tsx b/packages/twenty-front/src/loading/components/UserOrMetadataLoader.tsx index 3a9b17c43ba..57e67406c84 100644 --- a/packages/twenty-front/src/loading/components/UserOrMetadataLoader.tsx +++ b/packages/twenty-front/src/loading/components/UserOrMetadataLoader.tsx @@ -9,7 +9,7 @@ import { } from 'twenty-ui-deprecated/theme-constants'; import { ModalBackdrop } from 'twenty-ui-deprecated/layout'; import { LeftPanelSkeletonLoader } from '~/loading/components/LeftPanelSkeletonLoader'; -import { RightPanelSkeletonLoader } from '~/loading/components/RightPanelSkeletonLoader'; +import { PageContentSkeletonLoader } from '~/loading/components/PageContentSkeletonLoader'; const StyledContainer = styled.div` background: ${themeCssVariables.background.noisy}; @@ -44,7 +44,7 @@ export const UserOrMetadataLoader = () => { - + ); }; diff --git a/packages/twenty-front/src/modules/app/components/LazyRoute.tsx b/packages/twenty-front/src/modules/app/components/LazyRoute.tsx index 0b49a0b7de7..91bc859f089 100644 --- a/packages/twenty-front/src/modules/app/components/LazyRoute.tsx +++ b/packages/twenty-front/src/modules/app/components/LazyRoute.tsx @@ -1,4 +1,3 @@ -import { PageContainer } from '@/ui/layout/page/components/PageContainer'; import { type ReactNode, Suspense } from 'react'; import { PageContentSkeletonLoader } from '~/loading/components/PageContentSkeletonLoader'; @@ -7,13 +6,7 @@ type LazyRouteProps = { fallback?: ReactNode; }; -const LazyRouteFallback = () => ( - - - -); - export const LazyRoute = ({ children, - fallback = , + fallback = , }: LazyRouteProps) => {children}; diff --git a/packages/twenty-front/src/modules/app/hooks/useCreateAppRouter.tsx b/packages/twenty-front/src/modules/app/hooks/useCreateAppRouter.tsx index 7fb09204b0c..c626b4c2a5a 100644 --- a/packages/twenty-front/src/modules/app/hooks/useCreateAppRouter.tsx +++ b/packages/twenty-front/src/modules/app/hooks/useCreateAppRouter.tsx @@ -5,8 +5,10 @@ import { VerifyLoginTokenEffect } from '@/auth/components/VerifyLoginTokenEffect import { VerifyEmailEffect } from '@/auth/components/VerifyEmailEffect'; import indexAppPath from '@/navigation/utils/indexAppPath'; +import { RecordIndexSkeletonLoader } from '@/object-record/record-index/components/RecordIndexSkeletonLoader'; import { BlankLayout } from '@/ui/layout/page/components/BlankLayout'; import { DefaultLayout } from '@/ui/layout/page/components/DefaultLayout'; +import { MainAppLayoutWithSidePanel } from '@/ui/layout/page/components/MainAppLayoutWithSidePanel'; import { AppPath } from 'twenty-shared/types'; import { lazy } from 'react'; @@ -209,48 +211,50 @@ export const useCreateAppRouter = ( } /> - } /> - - - - } - /> - - - - } - /> - - - - } - /> - - } - /> - - - - } - /> + }> + } /> + }> + + + } + /> + + + + } + /> + + + + } + /> + + } + /> + + + + } + /> + }> { const { commandMenuContextApi, isInPreviewMode } = useContext(CommandMenuContext); @@ -60,7 +62,10 @@ const CommandMenuItemButtonRenderer = ({ if (isInPreviewMode) { return ( - + ); } @@ -70,6 +75,7 @@ const CommandMenuItemButtonRenderer = ({ command={command} onClick={disabled ? undefined : handleClick} disabled={disabled} + isPrimaryAction={isPrimaryAction} /> ); }; @@ -167,11 +173,17 @@ const CommandMenuItemSelectableRenderer = ({ // oxlint-disable-next-line twenty/effect-components export const CommandMenuItemRenderer = ({ item, + isPrimaryAction, }: CommandMenuItemRendererProps) => { const { displayType } = useContext(CommandMenuContext); if (displayType === 'button') { - return ; + return ( + + ); } if (displayType === 'listItem' || displayType === 'dropdownItem') { diff --git a/packages/twenty-front/src/modules/command-menu-item/display/components/PinnedCommandMenuItemButtons.tsx b/packages/twenty-front/src/modules/command-menu-item/display/components/PinnedCommandMenuItemButtons.tsx index ad6dcf7bea0..d3fd73cf589 100644 --- a/packages/twenty-front/src/modules/command-menu-item/display/components/PinnedCommandMenuItemButtons.tsx +++ b/packages/twenty-front/src/modules/command-menu-item/display/components/PinnedCommandMenuItemButtons.tsx @@ -8,6 +8,7 @@ import { styled } from '@linaria/react'; import { motion } from 'framer-motion'; import { useContext, useMemo } from 'react'; import { ThemeContext } from 'twenty-ui-deprecated/theme-constants'; +import { EngineComponentKey } from '~/generated-metadata/graphql'; const StyledCommandMenuItemContainer = styled(motion.div)` align-items: center; @@ -80,7 +81,13 @@ export const PinnedCommandMenuItemButtons = () => { ease: 'easeInOut', }} > - + ))} diff --git a/packages/twenty-front/src/modules/command-menu/components/CommandMenuButton.tsx b/packages/twenty-front/src/modules/command-menu/components/CommandMenuButton.tsx index dd088832fd1..fd459c66ef1 100644 --- a/packages/twenty-front/src/modules/command-menu/components/CommandMenuButton.tsx +++ b/packages/twenty-front/src/modules/command-menu/components/CommandMenuButton.tsx @@ -28,6 +28,7 @@ export type CommandMenuButtonProps = { onClick?: (event?: MouseEvent) => void; to?: string; disabled?: boolean; + isPrimaryAction?: boolean; }; export const CommandMenuButton = ({ @@ -35,6 +36,7 @@ export const CommandMenuButton = ({ onClick, to, disabled = false, + isPrimaryAction = false, }: CommandMenuButtonProps) => { const resolvedLabel = getCommandMenuItemLabel(command.label); @@ -50,8 +52,8 @@ export const CommandMenuButton = ({