Skip to content

Commit 431f6ae

Browse files
authored
feat(settings): move settings chrome into a single rounded card (#21131)
## What Replaces `SubMenuTopBarContainer` with a settings-specific `SettingsPageLayout` that puts the whole page chrome — breadcrumb, centered title, actions, an optional secondary bar (tabs or wizard step), and the 760px body — inside **one rounded card**, with `SidePanelForDesktop` as a sibling. Title, tabs and body content share one centered vertical axis at every card width. Supersedes #21122. One PR, no feature flag. ## New components (`@/settings/components/layout/`) - **SettingsPageLayout** — owns the rounded card + side-panel sibling, `useCommandMenuHotKeys`, mobile command menu - **SettingsPageHeader** — breadcrumb · centered title · actions in a symmetric `1fr auto 1fr` grid (symmetric padding throughout) - **SettingsSecondaryBar** — the secondary row, bracketed by top + bottom borders - **SettingsTabBar** — centered tabs reusing `activeTabIdComponentState` + `TabListFromUrlOptionalEffect` for URL-hash sync (does not touch the shared `TabList`) - **SettingsWizardStepBar** — back arrow · "N. Label" · optional trailing slot ## Migrations - Bulk rename across ~80 call sites (`SubMenuTopBarContainer` → `SettingsPageLayout`); old component deleted. - 5 tab pages (AI, APIs & Webhooks, Applications, Members, Role) + the Data Model object-detail page render their tabs in `secondaryBar` (object-detail keeps "See records" / "New Field" in the header actions). - The 2 role object-level steps render the wizard step bar with working back navigation. - Accounts consolidated into **General / Emails / Calendars** tabs; standalone `SettingsAccountsEmails` / `SettingsAccountsCalendars` pages + routes + stories removed. `SettingsPath.AccountsEmails` / `AccountsCalendars` now resolve to `accounts#emails` / `accounts#calendars`, so existing `getSettingsPath()` links deep-link to the right tab via the existing hash sync — no call-site changes. ## Verification - `nx typecheck twenty-front` and `nx lint twenty-front` both clean. - Browser (logged-in workspace): title / tab / body / card centers align on a single axis at multiple widths — width-invariant, so alignment holds when the AI side panel (a sibling) shrinks the card. Rounded card with even gaps on all four sides; tab row bracketed by two 1px lines; no-tab pages render header → body with no lines; wizard back navigation works; `…/accounts#emails` opens the Emails tab. The shared `PageHeader` and `TabList` are untouched. The settings side panel itself isn't wired to open yet — that's a follow-up PR.
1 parent e721ebe commit 431f6ae

107 files changed

Lines changed: 1363 additions & 1004 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

packages/twenty-front/src/modules/app/components/SettingsRoutes.tsx

Lines changed: 49 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -24,20 +24,6 @@ const SettingsRestPlayground = lazy(() =>
2424
),
2525
);
2626

27-
const SettingsAccountsCalendars = lazy(() =>
28-
import('~/pages/settings/accounts/SettingsAccountsCalendars').then(
29-
(module) => ({
30-
default: module.SettingsAccountsCalendars,
31-
}),
32-
),
33-
);
34-
35-
const SettingsAccountsEmails = lazy(() =>
36-
import('~/pages/settings/accounts/SettingsAccountsEmails').then((module) => ({
37-
default: module.SettingsAccountsEmails,
38-
})),
39-
);
40-
4127
const SettingsAccountsConfiguration = lazy(() =>
4228
import('~/pages/settings/accounts/SettingsAccountsConfiguration').then(
4329
(module) => ({
@@ -122,20 +108,20 @@ const SettingsLogicFunctionDetail = lazy(() =>
122108
),
123109
);
124110

125-
const SettingsWorkspace = lazy(() =>
126-
import('~/pages/settings/SettingsWorkspace').then((module) => ({
127-
default: module.SettingsWorkspace,
111+
const SettingsGeneral = lazy(() =>
112+
import('~/pages/settings/general/SettingsGeneral').then((module) => ({
113+
default: module.SettingsGeneral,
128114
})),
129115
);
130116

131117
const SettingsWorkspaceEmail = lazy(() =>
132-
import('~/pages/settings/SettingsWorkspaceEmail').then((module) => ({
118+
import('~/pages/settings/email/SettingsWorkspaceEmail').then((module) => ({
133119
default: module.SettingsWorkspaceEmail,
134120
})),
135121
);
136122

137123
const SettingsWorkspaceEmailGroupChannelDetail = lazy(() =>
138-
import('~/pages/settings/workspace/SettingsWorkspaceEmailGroupChannelDetail').then(
124+
import('~/pages/settings/email/SettingsWorkspaceEmailGroupChannelDetail').then(
139125
(module) => ({
140126
default: module.SettingsWorkspaceEmailGroupChannelDetail,
141127
}),
@@ -157,9 +143,11 @@ const SettingsCustomDomainPage = lazy(() =>
157143
);
158144

159145
const SettingsApiWebhooks = lazy(() =>
160-
import('~/pages/settings/workspace/SettingsApiWebhooks').then((module) => ({
161-
default: module.SettingsApiWebhooks,
162-
})),
146+
import('~/pages/settings/api-webhooks/SettingsApiWebhooks').then(
147+
(module) => ({
148+
default: module.SettingsApiWebhooks,
149+
}),
150+
),
163151
);
164152

165153
const SettingsAI = lazy(() =>
@@ -319,13 +307,13 @@ const SettingsWorkspaceMember = lazy(() =>
319307
);
320308

321309
const SettingsProfile = lazy(() =>
322-
import('~/pages/settings/SettingsProfile').then((module) => ({
310+
import('~/pages/settings/profile/SettingsProfile').then((module) => ({
323311
default: module.SettingsProfile,
324312
})),
325313
);
326314

327315
const SettingsTwoFactorAuthenticationMethod = lazy(() =>
328-
import('~/pages/settings/SettingsTwoFactorAuthenticationMethod').then(
316+
import('~/pages/settings/profile/SettingsTwoFactorAuthenticationMethod').then(
329317
(module) => ({
330318
default: module.SettingsTwoFactorAuthenticationMethod,
331319
}),
@@ -346,20 +334,34 @@ const SettingsAccounts = lazy(() =>
346334
})),
347335
);
348336

337+
const SettingsAccountsEmails = lazy(() =>
338+
import('~/pages/settings/accounts/SettingsAccountsEmails').then((module) => ({
339+
default: module.SettingsAccountsEmails,
340+
})),
341+
);
342+
343+
const SettingsAccountsCalendars = lazy(() =>
344+
import('~/pages/settings/accounts/SettingsAccountsCalendars').then(
345+
(module) => ({
346+
default: module.SettingsAccountsCalendars,
347+
}),
348+
),
349+
);
350+
349351
const SettingsBilling = lazy(() =>
350-
import('~/pages/settings/SettingsBilling').then((module) => ({
352+
import('~/pages/settings/billing/SettingsBilling').then((module) => ({
351353
default: module.SettingsBilling,
352354
})),
353355
);
354356

355357
const SettingsUsage = lazy(() =>
356-
import('~/pages/settings/SettingsUsage').then((module) => ({
358+
import('~/pages/settings/billing/SettingsUsage').then((module) => ({
357359
default: module.SettingsUsage,
358360
})),
359361
);
360362

361363
const SettingsUsageUserDetail = lazy(() =>
362-
import('~/pages/settings/SettingsUsageUserDetail').then((module) => ({
364+
import('~/pages/settings/billing/SettingsUsageUserDetail').then((module) => ({
363365
default: module.SettingsUsageUserDetail,
364366
})),
365367
);
@@ -417,12 +419,6 @@ const SettingsObjectFieldEdit = lazy(() =>
417419
),
418420
);
419421

420-
const SettingsSecurity = lazy(() =>
421-
import('~/pages/settings/security/SettingsSecurity').then((module) => ({
422-
default: module.SettingsSecurity,
423-
})),
424-
);
425-
426422
const SettingsSecuritySSOIdentifyProvider = lazy(() =>
427423
import('~/pages/settings/security/SettingsSecuritySSOIdentifyProvider').then(
428424
(module) => ({
@@ -565,9 +561,9 @@ const SettingsAdminWorkspaceChatThread = lazy(() =>
565561
),
566562
);
567563

568-
const SettingsUpdates = lazy(() =>
569-
import('~/pages/settings/updates/SettingsUpdates').then((module) => ({
570-
default: module.SettingsUpdates,
564+
const SettingsCommunity = lazy(() =>
565+
import('~/pages/settings/community/SettingsCommunity').then((module) => ({
566+
default: module.SettingsCommunity,
571567
})),
572568
);
573569

@@ -624,20 +620,20 @@ export const SettingsRoutes = ({ isAdminPageEnabled }: SettingsRoutesProps) => (
624620
>
625621
<Route path={SettingsPath.Accounts} element={<SettingsAccounts />} />
626622
<Route
627-
path={SettingsPath.NewAccount}
628-
element={<SettingsNewAccount />}
629-
/>
630-
<Route
631-
path={SettingsPath.AccountsConfiguration}
632-
element={<SettingsAccountsConfiguration />}
623+
path={SettingsPath.AccountsEmails}
624+
element={<SettingsAccountsEmails />}
633625
/>
634626
<Route
635627
path={SettingsPath.AccountsCalendars}
636628
element={<SettingsAccountsCalendars />}
637629
/>
638630
<Route
639-
path={SettingsPath.AccountsEmails}
640-
element={<SettingsAccountsEmails />}
631+
path={SettingsPath.NewAccount}
632+
element={<SettingsNewAccount />}
633+
/>
634+
<Route
635+
path={SettingsPath.AccountsConfiguration}
636+
element={<SettingsAccountsConfiguration />}
641637
/>
642638
<Route
643639
path={SettingsPath.NewImapSmtpCaldavConnection}
@@ -655,7 +651,7 @@ export const SettingsRoutes = ({ isAdminPageEnabled }: SettingsRoutesProps) => (
655651
/>
656652
}
657653
>
658-
<Route path={SettingsPath.Workspace} element={<SettingsWorkspace />} />
654+
<Route path={SettingsPath.General} element={<SettingsGeneral />} />
659655
<Route
660656
path={SettingsPath.WorkspaceEmail}
661657
element={<SettingsWorkspaceEmail />}
@@ -916,14 +912,20 @@ export const SettingsRoutes = ({ isAdminPageEnabled }: SettingsRoutesProps) => (
916912
/>
917913
</Route>
918914

915+
<Route
916+
path="security"
917+
element={
918+
<Navigate to={getSettingsPath(SettingsPath.Security)} replace />
919+
}
920+
/>
921+
919922
<Route
920923
element={
921924
<SettingsProtectedRouteWrapper
922925
settingsPermission={PermissionFlagType.SECURITY}
923926
/>
924927
}
925928
>
926-
<Route path={SettingsPath.Security} element={<SettingsSecurity />} />
927929
<Route
928930
path={SettingsPath.NewSSOIdentityProvider}
929931
element={<SettingsSecuritySSOIdentifyProvider />}
@@ -1018,7 +1020,7 @@ export const SettingsRoutes = ({ isAdminPageEnabled }: SettingsRoutesProps) => (
10181020
/>
10191021
}
10201022
>
1021-
<Route path={SettingsPath.Updates} element={<SettingsUpdates />} />
1023+
<Route path={SettingsPath.Community} element={<SettingsCommunity />} />
10221024
</Route>
10231025
</Routes>
10241026
</Suspense>
Lines changed: 11 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,70 +1,32 @@
1-
import { styled } from '@linaria/react';
2-
1+
import { type CalendarChannel } from '@/accounts/types/CalendarChannel';
32
import { SettingsAccountsCalendarChannelDetails } from '@/settings/accounts/components/SettingsAccountsCalendarChannelDetails';
4-
import { SettingsNewAccountSection } from '@/settings/accounts/components/SettingsNewAccountSection';
53
import { SETTINGS_ACCOUNT_CALENDAR_CHANNELS_TAB_LIST_COMPONENT_ID } from '@/settings/accounts/constants/SettingsAccountCalendarChannelsTabListComponentId';
6-
import { useMyCalendarChannels } from '@/settings/accounts/hooks/useMyCalendarChannels';
7-
import { TabList } from '@/ui/layout/tab-list/components/TabList';
8-
import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTabIdComponentState';
9-
import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue';
4+
import { useSettingsActiveTabId } from '@/settings/components/layout/useSettingsActiveTabId';
105
import React from 'react';
11-
import { CalendarChannelSyncStage } from 'twenty-shared/types';
12-
import { themeCssVariables } from 'twenty-ui/theme-constants';
136

14-
const StyledCalenderContainer = styled.div`
15-
padding-bottom: ${themeCssVariables.spacing[6]};
16-
`;
7+
type SettingsAccountsCalendarChannelsContainerProps = {
8+
calendarChannels: CalendarChannel[];
9+
};
1710

18-
export const SettingsAccountsCalendarChannelsContainer = () => {
19-
const activeTabId = useAtomComponentStateValue(
20-
activeTabIdComponentState,
11+
export const SettingsAccountsCalendarChannelsContainer = ({
12+
calendarChannels,
13+
}: SettingsAccountsCalendarChannelsContainerProps) => {
14+
const activeTabId = useSettingsActiveTabId(
2115
SETTINGS_ACCOUNT_CALENDAR_CHANNELS_TAB_LIST_COMPONENT_ID,
16+
calendarChannels.map((channel) => channel.id),
2217
);
2318

24-
const { channels: allCalendarChannels } = useMyCalendarChannels();
25-
26-
const calendarChannels = allCalendarChannels.filter(
27-
(channel) =>
28-
channel.syncStage !== CalendarChannelSyncStage.PENDING_CONFIGURATION,
29-
);
30-
31-
const tabs = [
32-
...calendarChannels.map((calendarChannel) => ({
33-
id: calendarChannel.id,
34-
title: calendarChannel.handle,
35-
})),
36-
];
37-
38-
if (!calendarChannels.length) {
39-
return <SettingsNewAccountSection />;
40-
}
41-
4219
return (
4320
<>
44-
{tabs.length > 1 && (
45-
<StyledCalenderContainer>
46-
<TabList
47-
tabs={tabs}
48-
componentInstanceId={
49-
SETTINGS_ACCOUNT_CALENDAR_CHANNELS_TAB_LIST_COMPONENT_ID
50-
}
51-
/>
52-
</StyledCalenderContainer>
53-
)}
5421
{calendarChannels.map((calendarChannel) => (
5522
<React.Fragment key={calendarChannel.id}>
56-
{(calendarChannels.length === 1 ||
57-
calendarChannel.id === activeTabId) && (
23+
{calendarChannel.id === activeTabId && (
5824
<SettingsAccountsCalendarChannelDetails
5925
calendarChannel={calendarChannel}
6026
/>
6127
)}
6228
</React.Fragment>
6329
))}
64-
{/* TODO: remove or keep? */}
65-
{/* {activeTabId === 'general' && (
66-
<SettingsAccountsCalendarChannelsGeneral />
67-
)} */}
6830
</>
6931
);
7032
};

packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsEditImapSmtpCaldavConnection.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { useParams } from 'react-router-dom';
55

66
import { SaveAndCancelButtons } from '@/settings/components/SaveAndCancelButtons/SaveAndCancelButtons';
77
import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer';
8-
import { SubMenuTopBarContainer } from '@/ui/layout/page/components/SubMenuTopBarContainer';
8+
import { SettingsPageLayout } from '@/settings/components/layout/SettingsPageLayout';
99
import { SettingsPath } from 'twenty-shared/types';
1010

1111
import { Loader } from 'twenty-ui/feedback';
@@ -64,7 +64,7 @@ export const SettingsAccountsEditImapSmtpCaldavConnection = () => {
6464
const renderForm = () => (
6565
// oxlint-disable-next-line react/jsx-props-no-spreading
6666
<FormProvider {...formMethods}>
67-
<SubMenuTopBarContainer
67+
<SettingsPageLayout
6868
title={t`Edit Account`}
6969
links={[
7070
{
@@ -94,7 +94,7 @@ export const SettingsAccountsEditImapSmtpCaldavConnection = () => {
9494
existingProtocols={existingProtocols}
9595
/>
9696
</SettingsPageContainer>
97-
</SubMenuTopBarContainer>
97+
</SettingsPageLayout>
9898
</FormProvider>
9999
);
100100

0 commit comments

Comments
 (0)