431f6ae98f
## 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.
228 lines
6.8 KiB
TypeScript
228 lines
6.8 KiB
TypeScript
import { FeatureFlagKey, SettingsPath } from 'twenty-shared/types';
|
|
|
|
import { useAuth } from '@/auth/hooks/useAuth';
|
|
import { currentUserState } from '@/auth/states/currentUserState';
|
|
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
|
|
import { billingState } from '@/client-config/states/billingState';
|
|
import { supportChatState } from '@/client-config/states/supportChatState';
|
|
import { usePermissionFlagMap } from '@/settings/roles/hooks/usePermissionFlagMap';
|
|
import { getDocumentationUrl } from '@/support/utils/getDocumentationUrl';
|
|
import {
|
|
type NavigationDrawerItemIndentationLevel,
|
|
type NavigationDrawerItemModifier,
|
|
} from '@/ui/navigation/navigation-drawer/components/NavigationDrawerItem';
|
|
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
|
|
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
|
|
import { t } from '@lingui/core/macro';
|
|
import { isNonEmptyString } from '@sniptt/guards';
|
|
import {
|
|
IconApi,
|
|
// IconApps, // TODO: Re-enable when integrations page is ready
|
|
IconAt,
|
|
IconCalendarEvent,
|
|
IconColorSwatch,
|
|
type IconComponent,
|
|
IconCurrencyDollar,
|
|
IconDoorEnter,
|
|
IconHelpCircle,
|
|
IconHierarchy2,
|
|
IconLayout,
|
|
IconMail,
|
|
IconMessage,
|
|
IconPlug,
|
|
IconServer,
|
|
IconSettings,
|
|
IconSparkles,
|
|
IconUserCircle,
|
|
IconUsers,
|
|
} from 'twenty-ui/display';
|
|
import { PermissionFlagType } from '~/generated-metadata/graphql';
|
|
|
|
export type SettingsNavigationSection = {
|
|
label: string;
|
|
items: SettingsNavigationItem[];
|
|
isAdvanced?: boolean;
|
|
};
|
|
|
|
export type SettingsNavigationItem = {
|
|
label: string;
|
|
path?: SettingsPath;
|
|
onClick?: () => void;
|
|
Icon: IconComponent;
|
|
indentationLevel?: NavigationDrawerItemIndentationLevel;
|
|
matchSubPages?: boolean;
|
|
isHidden?: boolean;
|
|
subItems?: SettingsNavigationItem[];
|
|
isAdvanced?: boolean;
|
|
modifier?: NavigationDrawerItemModifier;
|
|
};
|
|
|
|
const useSettingsNavigationItems = (): SettingsNavigationSection[] => {
|
|
const billing = useAtomStateValue(billingState);
|
|
const { signOut } = useAuth();
|
|
const supportChat = useAtomStateValue(supportChatState);
|
|
const currentWorkspaceMember = useAtomStateValue(currentWorkspaceMemberState);
|
|
|
|
const isBillingEnabled = billing?.isBillingEnabled ?? false;
|
|
const currentUser = useAtomStateValue(currentUserState);
|
|
const isAdminEnabled =
|
|
(currentUser?.canImpersonate || currentUser?.canAccessFullAdminPanel) ??
|
|
false;
|
|
const isSupportChatConfigured =
|
|
supportChat?.supportDriver === 'FRONT' &&
|
|
isNonEmptyString(supportChat.supportFrontChatId);
|
|
|
|
const permissionMap = usePermissionFlagMap();
|
|
const isEmailGroupFeatureEnabled = useIsFeatureEnabled(
|
|
FeatureFlagKey.IS_EMAIL_GROUP_ENABLED,
|
|
);
|
|
return [
|
|
{
|
|
label: t`User`,
|
|
items: [
|
|
{
|
|
label: t`Profile`,
|
|
path: SettingsPath.ProfilePage,
|
|
Icon: IconUserCircle,
|
|
},
|
|
{
|
|
label: t`Experience`,
|
|
path: SettingsPath.Experience,
|
|
Icon: IconColorSwatch,
|
|
},
|
|
{
|
|
label: t`Accounts`,
|
|
path: SettingsPath.Accounts,
|
|
Icon: IconAt,
|
|
isHidden: !permissionMap[PermissionFlagType.CONNECTED_ACCOUNTS],
|
|
subItems: [
|
|
{
|
|
label: t`Emails`,
|
|
path: SettingsPath.AccountsEmails,
|
|
Icon: IconMail,
|
|
isHidden: !permissionMap[PermissionFlagType.CONNECTED_ACCOUNTS],
|
|
indentationLevel: 2,
|
|
},
|
|
{
|
|
label: t`Calendars`,
|
|
path: SettingsPath.AccountsCalendars,
|
|
Icon: IconCalendarEvent,
|
|
isHidden: !permissionMap[PermissionFlagType.CONNECTED_ACCOUNTS],
|
|
indentationLevel: 2,
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
{
|
|
label: t`Workspace`,
|
|
items: [
|
|
{
|
|
label: t`General`,
|
|
path: SettingsPath.General,
|
|
Icon: IconSettings,
|
|
isHidden: !permissionMap[PermissionFlagType.WORKSPACE],
|
|
},
|
|
{
|
|
label: t`Data model`,
|
|
path: SettingsPath.Objects,
|
|
Icon: IconHierarchy2,
|
|
isHidden: !permissionMap[PermissionFlagType.DATA_MODEL],
|
|
},
|
|
{
|
|
label: t`Layout`,
|
|
path: SettingsPath.Layout,
|
|
Icon: IconLayout,
|
|
isHidden: !permissionMap[PermissionFlagType.LAYOUTS],
|
|
},
|
|
{
|
|
label: t`Members`,
|
|
path: SettingsPath.WorkspaceMembersPage,
|
|
Icon: IconUsers,
|
|
isHidden: !permissionMap[PermissionFlagType.WORKSPACE_MEMBERS],
|
|
},
|
|
{
|
|
label: t`Billing`,
|
|
path: SettingsPath.Billing,
|
|
Icon: IconCurrencyDollar,
|
|
isHidden:
|
|
!isBillingEnabled || !permissionMap[PermissionFlagType.WORKSPACE],
|
|
},
|
|
{
|
|
label: t`APIs & Webhooks`,
|
|
path: SettingsPath.ApiWebhooks,
|
|
Icon: IconApi,
|
|
isHidden: !permissionMap[PermissionFlagType.API_KEYS_AND_WEBHOOKS],
|
|
},
|
|
// TODO: Re-enable when integrations page is ready
|
|
// {
|
|
// label: t`Integrations`,
|
|
// path: SettingsPath.Integrations,
|
|
// Icon: IconApps,
|
|
// isHidden: !permissionMap[PermissionFlagType.API_KEYS_AND_WEBHOOKS],
|
|
// },
|
|
{
|
|
label: t`Apps`,
|
|
path: SettingsPath.Applications,
|
|
Icon: IconPlug,
|
|
isHidden: !permissionMap[PermissionFlagType.APPLICATIONS],
|
|
},
|
|
{
|
|
label: t`AI`,
|
|
path: SettingsPath.AI,
|
|
Icon: IconSparkles,
|
|
isHidden: !permissionMap[PermissionFlagType.AI],
|
|
},
|
|
{
|
|
label: t`Email`,
|
|
path: SettingsPath.WorkspaceEmail,
|
|
Icon: IconMail,
|
|
isHidden:
|
|
!isEmailGroupFeatureEnabled ||
|
|
!permissionMap[PermissionFlagType.WORKSPACE],
|
|
},
|
|
],
|
|
},
|
|
{
|
|
label: t`Other`,
|
|
items: [
|
|
{
|
|
label: t`Admin Panel`,
|
|
path: SettingsPath.AdminPanel,
|
|
Icon: IconServer,
|
|
isHidden: !isAdminEnabled,
|
|
},
|
|
{
|
|
label: t`Community`,
|
|
path: SettingsPath.Community,
|
|
Icon: IconUsers,
|
|
isHidden: !permissionMap[PermissionFlagType.WORKSPACE],
|
|
},
|
|
{
|
|
label: t`Support`,
|
|
onClick: () => window.FrontChat?.('show'),
|
|
Icon: IconMessage,
|
|
isHidden: !isSupportChatConfigured,
|
|
},
|
|
{
|
|
label: t`Documentation`,
|
|
onClick: () =>
|
|
window.open(
|
|
getDocumentationUrl({ locale: currentWorkspaceMember?.locale }),
|
|
'_blank',
|
|
),
|
|
Icon: IconHelpCircle,
|
|
},
|
|
{
|
|
label: t`Logout`,
|
|
onClick: signOut,
|
|
Icon: IconDoorEnter,
|
|
matchSubPages: false,
|
|
},
|
|
],
|
|
},
|
|
];
|
|
};
|
|
|
|
export { useSettingsNavigationItems };
|