From adba66caea158a97b3fdee7b4c874f0d84d8e05a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20Malfait?= Date: Wed, 10 Jun 2026 11:02:36 +0200 Subject: [PATCH] =?UTF-8?q?fix(twenty-front):=20new=20layout=20fast-follow?= =?UTF-8?q?s=20=E2=80=94=20settings=20drawer,=20loading=20&=20command=20me?= =?UTF-8?q?nu=20(#21389)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Second batch of new-layout fast-follows (master: twentyhq/core-team-issues#2478). All changes verified live against a running workspace. ## Settings drawer & header - **twentyhq/core-team-issues#2489** — sidebar icons render as plain 16px icons, no background tiles. - **twentyhq/core-team-issues#2488** — Advanced toggle spans the full drawer width; yellow dot removed. - **twentyhq/core-team-issues#2497** — page title stays centered in the settings header (breadcrumb stays left). - **twentyhq/core-team-issues#2490** — Exit Settings control aligned to the workspace switcher (24px, matching padding/gap). - **twentyhq/core-team-issues#2499** — 2px vertical gap restored between collapsible drawer section items. - **twentyhq/core-team-issues#2491** — settings drawer rhythm now matches the main app (28px items, 2px gaps, 28px section headers). - **twentyhq/core-team-issues#2492** — Home/Chat tab switch no longer flickers: both tab subtrees stay mounted (a shared `NavigationDrawerTabbedContent` toggles visibility instead of remounting + flashing the chat skeleton). ## Loading states - **twentyhq/core-team-issues#2486** — metadata loading shows an empty body (no dense skeleton rows). - **twentyhq/core-team-issues#2487** — settings table keeps its layout while loading, with the shimmer localized to the first row's first cell. ## Command menu & navigation - **twentyhq/core-team-issues#2501** — navigation section header height matches the nav item rhythm (28px). - **twentyhq/core-team-issues#2502 (part 1)** — the page side-panel toggle stays as the dots glyph while the command menu is open, instead of morphing into a second close control. ## New-field flow - **twentyhq/core-team-issues#2494** — the new-field stepper moved from a breadcrumb dropdown into a centered secondary wizard bar (back chevron + Save on the configure step); breadcrumb stays clean and the object label is the centered title. ## Descoped (substantive bugs already fixed) - **twentyhq/core-team-issues#2500** — command-menu highlight right gutter: the menu-item base measures full-width, so it's likely a scrollbar gutter on the list, not the shared component. Left for a focused follow-up. - **twentyhq/core-team-issues#2502 part 2** — moving the command-menu close from left to right is cosmetic (the duplicate-control bug is fixed by part 1) and would touch the shared `SidePanelTopBar` used by search/AI panels. ## Verification typecheck (tsgo) + oxlint + oxfmt green for all changed files; each change DOM-measured / screenshotted in the running app. --- .../components/MainNavigationDrawer.tsx | 11 +- .../NavigationDrawerTabbedContent.tsx | 36 +++++ .../components/SettingsNavigationDrawer.tsx | 17 +-- .../SettingsNavigationDrawerItem.tsx | 2 - .../components/SettingsSkeletonLoader.tsx | 6 +- .../components/layout/SettingsPageLayout.tsx | 1 + ...ngsDataModelNewFieldBreadcrumbDropDown.tsx | 135 ------------------ .../components/EventLogResultsTable.tsx | 31 ++-- .../components/SidePanelToggleButton.tsx | 17 ++- .../layout/page/components/PageCardHeader.tsx | 37 +++-- .../CollapsibleNavigationDrawerSection.tsx | 10 +- .../components/NavigationDrawerBackButton.tsx | 2 +- .../NavigationDrawerSectionTitle.tsx | 2 +- .../SettingsObjectNewFieldConfigure.tsx | 38 ++--- .../SettingsObjectNewFieldSelect.tsx | 11 +- .../components/AdvancedSettingsToggle.tsx | 50 ++----- 16 files changed, 161 insertions(+), 245 deletions(-) create mode 100644 packages/twenty-front/src/modules/navigation/components/NavigationDrawerTabbedContent.tsx delete mode 100644 packages/twenty-front/src/modules/settings/data-model/components/SettingsDataModelNewFieldBreadcrumbDropDown.tsx diff --git a/packages/twenty-front/src/modules/navigation/components/MainNavigationDrawer.tsx b/packages/twenty-front/src/modules/navigation/components/MainNavigationDrawer.tsx index cdaa5077fc..ff8072c49a 100644 --- a/packages/twenty-front/src/modules/navigation/components/MainNavigationDrawer.tsx +++ b/packages/twenty-front/src/modules/navigation/components/MainNavigationDrawer.tsx @@ -1,7 +1,7 @@ import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState'; -import { NavigationDrawerAiChatContent } from '@/ai/components/NavigationDrawerAiChatContent'; import { MainNavigationDrawerNavigationContent } from '@/navigation/components/MainNavigationDrawerNavigationContent'; import { MainNavigationDrawerTabsRow } from '@/navigation/components/MainNavigationDrawerTabsRow'; +import { NavigationDrawerTabbedContent } from '@/navigation/components/NavigationDrawerTabbedContent'; import { useHasPermissionFlag } from '@/settings/roles/hooks/useHasPermissionFlag'; import { NavigationDrawer } from '@/ui/navigation/navigation-drawer/components/NavigationDrawer'; import { NavigationDrawerFixedContent } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerFixedContent'; @@ -32,11 +32,10 @@ export const MainNavigationDrawer = ({ className }: { className?: string }) => { - {showAiChatContent ? ( - - ) : ( - - )} + } + /> ); diff --git a/packages/twenty-front/src/modules/navigation/components/NavigationDrawerTabbedContent.tsx b/packages/twenty-front/src/modules/navigation/components/NavigationDrawerTabbedContent.tsx new file mode 100644 index 0000000000..fffac0d093 --- /dev/null +++ b/packages/twenty-front/src/modules/navigation/components/NavigationDrawerTabbedContent.tsx @@ -0,0 +1,36 @@ +import { NavigationDrawerAiChatContent } from '@/ai/components/NavigationDrawerAiChatContent'; +import { styled } from '@linaria/react'; +import { type ReactNode, useState } from 'react'; + +type NavigationDrawerTabbedContentProps = { + showAiChatContent: boolean; + navigationContent: ReactNode; +}; + +const StyledTabContent = styled.div<{ isHidden: boolean }>` + display: ${({ isHidden }) => (isHidden ? 'none' : 'contents')}; +`; + +export const NavigationDrawerTabbedContent = ({ + showAiChatContent, + navigationContent, +}: NavigationDrawerTabbedContentProps) => { + const [hasOpenedAiChat, setHasOpenedAiChat] = useState(showAiChatContent); + + if (showAiChatContent && !hasOpenedAiChat) { + setHasOpenedAiChat(true); + } + + return ( + <> + + {navigationContent} + + {hasOpenedAiChat && ( + + + + )} + + ); +}; diff --git a/packages/twenty-front/src/modules/navigation/components/SettingsNavigationDrawer.tsx b/packages/twenty-front/src/modules/navigation/components/SettingsNavigationDrawer.tsx index 51bde137d6..721bc4933a 100644 --- a/packages/twenty-front/src/modules/navigation/components/SettingsNavigationDrawer.tsx +++ b/packages/twenty-front/src/modules/navigation/components/SettingsNavigationDrawer.tsx @@ -1,5 +1,5 @@ -import { NavigationDrawerAiChatContent } from '@/ai/components/NavigationDrawerAiChatContent'; import { MainNavigationDrawerTabsRow } from '@/navigation/components/MainNavigationDrawerTabsRow'; +import { NavigationDrawerTabbedContent } from '@/navigation/components/NavigationDrawerTabbedContent'; import { useHasPermissionFlag } from '@/settings/roles/hooks/useHasPermissionFlag'; import { SettingsNavigationDrawerItems } from '@/settings/components/SettingsNavigationDrawerItems'; import { NavigationDrawer } from '@/ui/navigation/navigation-drawer/components/NavigationDrawer'; @@ -18,10 +18,8 @@ import { themeCssVariables } from 'twenty-ui-deprecated/theme-constants'; import { PermissionFlagType } from '~/generated-metadata/graphql'; const StyledAdvancedToggleWrapper = styled.div<{ isMobile: boolean }>` - padding-left: ${({ isMobile }) => - isMobile ? '0' : themeCssVariables.spacing[5]}; padding-right: ${({ isMobile }) => - isMobile ? '0' : themeCssVariables.spacing[8]}; + isMobile ? '0' : themeCssVariables.spacing[1]}; `; export const SettingsNavigationDrawer = ({ @@ -52,11 +50,10 @@ export const SettingsNavigationDrawer = ({ )} - {showAiChatContent ? ( - - ) : ( - - )} + } + /> {!showAiChatContent && ( @@ -65,7 +62,7 @@ export const SettingsNavigationDrawer = ({ diff --git a/packages/twenty-front/src/modules/settings/components/SettingsNavigationDrawerItem.tsx b/packages/twenty-front/src/modules/settings/components/SettingsNavigationDrawerItem.tsx index cff5bdf0b5..9df0d0dbb1 100644 --- a/packages/twenty-front/src/modules/settings/components/SettingsNavigationDrawerItem.tsx +++ b/packages/twenty-front/src/modules/settings/components/SettingsNavigationDrawerItem.tsx @@ -39,7 +39,6 @@ export const SettingsNavigationDrawerItem = ({ label={item.label} to={href} Icon={item.Icon} - withIconBackground active={isActive} modifier={item.modifier} onClick={item.onClick} @@ -55,7 +54,6 @@ export const SettingsNavigationDrawerItem = ({ label={item.label} to={href || undefined} Icon={item.Icon} - withIconBackground active={isActive} modifier={item.modifier} onClick={item.onClick} diff --git a/packages/twenty-front/src/modules/settings/components/SettingsSkeletonLoader.tsx b/packages/twenty-front/src/modules/settings/components/SettingsSkeletonLoader.tsx index f5b047bb39..f0e5f551bd 100644 --- a/packages/twenty-front/src/modules/settings/components/SettingsSkeletonLoader.tsx +++ b/packages/twenty-front/src/modules/settings/components/SettingsSkeletonLoader.tsx @@ -1,6 +1,4 @@ import { SKELETON_LOADER_HEIGHT_SIZES } from '@/activities/components/SkeletonLoader'; -import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer'; -import { SettingsSectionSkeletonLoader } from '@/settings/components/SettingsSectionSkeletonLoader'; import { PageCardHeader } from '@/ui/layout/page/components/PageCardHeader'; import { PageCardLayout } from '@/ui/layout/page/components/PageCardLayout'; import { useContext } from 'react'; @@ -39,9 +37,7 @@ export const SettingsSkeletonLoader = () => { } showInformationBanner={false} > - - - + {null} ); diff --git a/packages/twenty-front/src/modules/settings/components/layout/SettingsPageLayout.tsx b/packages/twenty-front/src/modules/settings/components/layout/SettingsPageLayout.tsx index a0853eb488..ccfff453fd 100644 --- a/packages/twenty-front/src/modules/settings/components/layout/SettingsPageLayout.tsx +++ b/packages/twenty-front/src/modules/settings/components/layout/SettingsPageLayout.tsx @@ -29,6 +29,7 @@ export const SettingsPageLayout = ({ title={title} tag={tag} actionButton={actionButton} + centerTitle /> } secondaryBar={ diff --git a/packages/twenty-front/src/modules/settings/data-model/components/SettingsDataModelNewFieldBreadcrumbDropDown.tsx b/packages/twenty-front/src/modules/settings/data-model/components/SettingsDataModelNewFieldBreadcrumbDropDown.tsx deleted file mode 100644 index b631c4faa4..0000000000 --- a/packages/twenty-front/src/modules/settings/data-model/components/SettingsDataModelNewFieldBreadcrumbDropDown.tsx +++ /dev/null @@ -1,135 +0,0 @@ -import { type SettingsFieldType } from '@/settings/data-model/types/SettingsFieldType'; -import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown'; -import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent'; -import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; -import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown'; -import { styled } from '@linaria/react'; -import { t } from '@lingui/core/macro'; -import { useContext } from 'react'; -import { useLocation, useParams, useSearchParams } from 'react-router-dom'; -import { SettingsPath } from 'twenty-shared/types'; -import { isDefined } from 'twenty-shared/utils'; -import { IconChevronDown } from 'twenty-ui-deprecated/display'; -import { Button } from 'twenty-ui-deprecated/input'; -import { MenuItem } from 'twenty-ui-deprecated/navigation'; -import { - ThemeContext, - themeCssVariables, -} from 'twenty-ui-deprecated/theme-constants'; -import { useNavigateSettings } from '~/hooks/useNavigateSettings'; - -const StyledContainer = styled.div` - align-items: center; - color: ${themeCssVariables.font.color.tertiary}; - cursor: default; - display: flex; - font-size: ${themeCssVariables.font.size.md}; -`; - -const StyledButtonContainer = styled.div` - position: relative; - width: 100%; -`; - -const StyledDownChevronContainer = styled.span` - align-items: center; - color: ${themeCssVariables.font.color.primary}; - display: flex; - position: absolute; - right: ${themeCssVariables.spacing['1.5']}; - top: 50%; - transform: translateY(-50%); -`; - -const StyledMenuItemWrapper = styled.div<{ disabled?: boolean }>` - cursor: ${({ disabled }) => (disabled ? 'not-allowed' : 'pointer')}; - width: 100%; -`; - -const StyledSpan = styled.span` - margin-left: ${themeCssVariables.spacing[2]}; -`; - -const StyledButtonWrapper = styled.div` - button { - color: ${themeCssVariables.font.color.primary}; - padding-right: ${themeCssVariables.spacing[6]}; - } -`; - -export const SettingsDataModelNewFieldBreadcrumbDropDown = () => { - const { theme } = useContext(ThemeContext); - const dropdownId = `settings-object-new-field-breadcrumb-dropdown`; - const { closeDropdown } = useCloseDropdown(); - const navigate = useNavigateSettings(); - const location = useLocation(); - const { objectNamePlural = '' } = useParams(); - const [searchParams] = useSearchParams(); - - const fieldType = searchParams.get('fieldType') as SettingsFieldType; - const isConfigureStep = location.pathname.includes('/configure'); - - const handleClick = (step: 'select' | 'configure') => { - if (step === 'configure' && isDefined(fieldType)) { - navigate( - SettingsPath.ObjectNewFieldConfigure, - { objectNamePlural }, - { fieldType }, - ); - } else { - navigate( - SettingsPath.ObjectNewFieldSelect, - { objectNamePlural }, - fieldType ? { fieldType } : undefined, - ); - } - closeDropdown(dropdownId); - }; - - return ( - - {t`New Field`} - - - - - - {isConfigureStep ? ( - -