feat(settings): move settings chrome into a single rounded card#21131
Conversation
Replace SubMenuTopBarContainer with SettingsPageLayout: the breadcrumb, centered title, actions, an optional secondary bar (tabs or wizard step) and the 760px body now live inside one rounded card, with SidePanelForDesktop as a sibling. Title, tabs and body share one centered axis regardless of card width. - Add SettingsPageLayout, SettingsPageHeader, SettingsSecondaryBar, SettingsTabBar and SettingsWizardStepBar under @/settings/components/layout - Migrate the AI, APIs & Webhooks, Applications, Members and Role pages plus the object detail page to render their tabs in the secondary bar - Render the two role object-level steps in a wizard step bar with working back navigation - Consolidate Accounts into General/Emails/Calendars tabs; AccountsEmails and AccountsCalendars now resolve to accounts#emails and accounts#calendars so existing links deep-link via hash sync
Security settings now render as a tab on the General page instead of a standalone page. SettingsPath.Security resolves to general#security, so existing nav and SSO/event-log breadcrumb links deep-link to the tab via hash sync. The tab is gated by the SECURITY permission since the General route is not. - Extract the Security body into SettingsSecuritySettings (modules/settings/security/components) - SettingsWorkspace renders General + Security tabs - Remove the standalone SettingsSecurity page + its route + lazy import - Nav: Security becomes an advanced subitem under General
🔍 Automated Pre-Review✅ No issues detected - This PR is ready for human review. Automated pre-review. Human approval still required. |
|
🚀 Preview Environment Ready! Your preview environment is available at: https://chubby-units-beads-experiences.trycloudflare.com This environment will automatically shut down after 5 hours. |
There was a problem hiding this comment.
1 issue found across 5 files (changes from recent commits).
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
Resolve the naming/structure incoherences surfaced by the redesign and the Accounts/Security consolidations: - Rename SettingsPath.Workspace -> General (value 'general' unchanged) and the page SettingsWorkspace -> SettingsGeneral so the enum key and component match the General page; breadcrumb "Workspace" labels (the nav section name) are kept. - Move NewApprovedAccessDomain from members/ to security/approved-access-domain/new so every security sub-page shares the security/ namespace. - Folder the loose top-level pages: general/, profile/ (profile + two-factor), billing/ (billing + usage + usage detail), email/; rename workspace/ -> api-webhooks/ and move its email-group page into email/.
…gs paths The standard command-menu items hardcode frontend paths. Update the three broken by the Accounts and Security consolidations so the quick-actions deep-link to the right tab instead of 404ing: accounts emails/calendars -> /settings/accounts#emails|#calendars, security -> /settings/general#security.
…step bar; trim nav Root cause of the off-axis title/tabs: the header and wizard step bar used grid-template-columns: 1fr auto 1fr. Bare 1fr is minmax(auto, 1fr), and the breadcrumb cell also used justify-self: start (content-sized), so a long breadcrumb grew its track and pushed/overlapped the centered title while the flex-centered tabs stayed put. Fix: minmax(0, 1fr) side tracks + drop justify-self so the side cells fill their track and clip (breadcrumb truncates), keeping the title on the same axis as the tabs and body. - Wizard: move Finish into the step bar's trailing slot with primary styling (sub-flow action, not a page-level finish). - Nav: remove the Security item (now a tab on General) and the New badges on Apps and AI.
…ttom line The tab content was vertically centered in the 40px secondary bar, so the active tab's underline (an ::after pinned to bottom:0 of a 24px-tall tab) floated ~8px above the bar's bottom separator. Let the tab fill the bar height (drop align-items: center -> default stretch) and draw the bottom separator as a bottom-pinned ::after instead of a border-bottom (which box-sizing keeps 1px inside), so the underline now lands exactly on the line (measured gap 0). The wizard step bar is unaffected — it centers its own content via its grid align-items.
…rd sections
Rename the Updates settings page to Community: nav label + IconUsers, page title, SettingsPath.Updates -> Community ('community'), component/file SettingsUpdates -> SettingsCommunity, route, and title-utils entry.
Add two sections to the page: Partners (links to twenty.com/partners/list) and Discord (links to the Discord invite); the existing Releases and Early access sections are kept.
Server: repoint the goToSettingsUpdates command-menu item to /settings/community with a Community label/icon. Its key + universalIdentifier are unchanged because committed upgrade migrations reference them.
…k sections Remove the unused className prop from SettingsPageLayout and the unused loading prop from SettingsTabBar (no callers in the repo). Drop a redundant single-child fragment around SettingsObjectDetailPage's SettingsPageLayout. Collapse the three identical Partners/Discord/Releases sections in SettingsCommunity into one data-driven map so the icon size/stroke and link rel/target live in a single place.
Revert the General/Emails/Calendars category-tab consolidation on the Accounts page. Restore separate Accounts (parent), Emails, and Calendar pages, each on SettingsPageLayout, with AccountsEmails/AccountsCalendars back to real routes (accounts/emails, accounts/calendars). The Emails and Calendar pages now own the channel fetch and render one secondary-bar tab per connected account (only when there is more than one); the channel containers became presentational, rendering the active account's detail by activeTabId. The selected-message-channel effect now follows the active tab so the folder import card stays in sync.
Remove the narrative comments added across the settings layout components, keeping only two condensed one-liners for the genuinely non-obvious CSS: the header's minmax(0, 1fr) centering tracks and the secondary bar's ::after bottom separator.
There was a problem hiding this comment.
2 issues found across 17 files (changes from recent commits).
Tip: Review your code locally with the cubic CLI to iterate faster.
Re-trigger cubic
The settings card header was taller than the side panel (Ask AI) top bar and used the white card background. Match it to the side panel bar: fixed SIDE_PANEL_TOP_BAR_HEIGHT, background.secondary, and a medium bottom border, so the two line up when the side panel is open. Drop the secondary bar's top border since the header now provides the separator, avoiding a double line above the tabs.
… breadcrumb Add useSettingsActiveTabId to resolve the active tab synchronously (stored tab, else URL hash, else first tab). SettingsTabBar, SettingsGeneral, the object detail page, and the account channel containers/effect use it (or the equivalent first-tab fallback), so deep-links and multi-account pages render the right content on first paint instead of a blank/default frame, and SettingsTabBar no longer overwrites a hash-selected tab. Only visible tab ids are passed to the URL effect. Revert the goToSettingsSecurity command-menu path back to /settings/security (matches main, no data migration needed) and add a redirect from /settings/security to /settings/general#security so existing workspaces' command-menu items and old bookmarks still resolve. Drop the self-referencing href on the Community breadcrumb's Other crumb.
…r effect SettingsTabBar no longer writes the active tab in a useEffect. The active tab is resolved synchronously via useSettingsActiveTabId (stored ?? URL hash ?? first), the URL hash remains the source of truth (synced by the existing TabListFromUrlOptionalEffect on click/deep-link), and consumers either fall back to their first tab or set their own default (e.g. SettingsRole), so the bar only needs to render.
|
You're iterating quickly on this pull request. To help protect your rate limits, cubic has paused automatic reviews on new pushes for now—when you're ready for another review, comment |
There was a problem hiding this comment.
1 issue found across 10 files (changes from recent commits).
Tip: Review your code locally with the cubic CLI to iterate faster.
Re-trigger cubic
…nt edge The settings body scroll container was constrained to the 760px content column, so its scrollbar sat at the column edge (mid-card) on wide screens. Make the body wrapper full width so the scroll container spans the whole card (scrollbar at the card edge), and move the 760px centering onto the content inside the scroll (SettingsPageContainer).
useSettingsActiveTabId now checks the URL hash before the stored tab, so a deep-link or back/forward navigation renders the hashed tab on first paint instead of a stale stored tab. Memoize the filtered message/calendar channel arrays so the selected-channel effect only runs when the channels actually change.
… General The General page is the Workspace section landing (/settings/general), so its 'Workspace' breadcrumb crumb linked to itself. Render it as plain text instead.
AI, API & Webhooks, Applications, and Members read the active tab via useSettingsActiveTabId (URL hash ?? stored ?? first) instead of the raw atom with a first-tab fallback, so a deep-link renders the right tab on first paint instead of flickering through the default tab. Brings them in line with General, the object detail page, and the account pages.
|
Will continue in followup PRs |
…21209) Three small post-redesign UI fixes. Each is an independent commit, so they can be split into separate PRs if preferred. ## 1. Settings loading skeleton — match the rounded-card layout The redesign (#21131) moved settings chrome into a rounded card (`SettingsPageLayout`: bordered header with breadcrumb + centered title, optional secondary bar, 760px body), but `SettingsSkeletonLoader` still rendered the old flat `PageHeader` + `PageBody` — so pages painted as a full-width flat bar then snapped into the card. - `SettingsSkeletonLoader` now reproduces the card and **reuses the real `SettingsPageHeader` + `SettingsPageContainer`**, so the frame aligns by construction; the card CSS is replicated (not `SettingsPageLayout`) to avoid the layout's side effects (hotkeys, side panel, info banner). - It's **composed with `SettingsSectionSkeletonLoader`** so the loading body is identical whether or not chrome is present. Rule: no chrome on screen yet → full-page skeleton; chrome already on screen → body-only `SettingsSectionSkeletonLoader` (the admin Enterprise tab now uses it, matching its sibling tabs). A short comment on each component documents this. ## 2. Application detail header — pass a plain title `SettingsApplicationDetails` / `SettingsAvailableApplicationDetails` passed a custom `SettingsApplicationDetailTitle` (avatar + name + multi-line description, fixed width) into `SettingsPageLayout`'s **centered single-line title slot**, which broke the header. They now pass the app's display name like every other page. The available-app "unlisted" notice moves into the body as a reusable `InlineBanner`; the now-unused `SettingsApplicationDetailTitle` is removed. ## 3. Navigation — hide Favorites when empty Always rendering the Favorites section (#21087) left a stray "Favorites" title above Workspace for users with no favorites. It now renders only when at least one favorite exists (redundant per-child guards dropped). Note: the "+ add favorite" entry point therefore appears once you have ≥1 favorite; the first favorite is created from a record/view as before. ## Verification - `nx typecheck twenty-front` ✅ · `oxlint` + `oxfmt --check` on changed files ✅ - i18n catalogs intentionally untouched — handled by the repo's separate i18n pipeline.
What
Replaces
SubMenuTopBarContainerwith a settings-specificSettingsPageLayoutthat 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, withSidePanelForDesktopas 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/)useCommandMenuHotKeys, mobile command menu1fr auto 1frgrid (symmetric padding throughout)activeTabIdComponentState+TabListFromUrlOptionalEffectfor URL-hash sync (does not touch the sharedTabList)Migrations
SubMenuTopBarContainer→SettingsPageLayout); old component deleted.secondaryBar(object-detail keeps "See records" / "New Field" in the header actions).SettingsAccountsEmails/SettingsAccountsCalendarspages + routes + stories removed.SettingsPath.AccountsEmails/AccountsCalendarsnow resolve toaccounts#emails/accounts#calendars, so existinggetSettingsPath()links deep-link to the right tab via the existing hash sync — no call-site changes.Verification
nx typecheck twenty-frontandnx lint twenty-frontboth clean.…/accounts#emailsopens the Emails tab.The shared
PageHeaderandTabListare untouched. The settings side panel itself isn't wired to open yet — that's a follow-up PR.