Fix navigation and settings UI polish (#21523)

## Summary

This PR groups the requested UI polish pass across navigation, settings,
AI settings, data model, community/lab, and dashboard chips.

### Navigation and drawer polish
- Smooths app/settings route switching with a 300ms content transition.
- Smooths app menu/settings menu drawer swaps with a fade transition
while preserving the existing drawer dimensions.
- Keeps navigation and AI chat history panes mounted to avoid flicker
when switching tabs.
- Aligns the first app/settings section with the AI chat history section
title.
- Removes the main app navigation "Other" section.
- Aligns the settings exit header with the workspace switcher header.
- Sets the settings exit icon gap to 8px, uses the small icon stroke
token, and keeps the icon color on text-secondary.
- Makes the workspace switcher button 28px high inside its 32px
container, moves the dropdown up so the workspace name does not jump,
and keeps a 2px gap between header icon buttons.
- Moves Settings below Support in the workspace switcher menu.
- Pins the Advanced toggle to the bottom of the settings drawer and
aligns its right padding with nav items.

### Settings surface polish
- Fixes vertically cropped dropdown menu headers.
- Makes wizard parent titles tertiary when a nested wizard title is
visible.
- Places danger-zone buttons side by side.
- Uses a 32px Visualize button.
- Restores 8px top/bottom padding on settings tables.
- Separates AI overview counts into three columns like layout overview.
- Adds separators inside setting cards, including the Smart Model / Fast
Model card.
- Groups lab/early-access toggles into one card with separators and
full-width row backgrounds.
- Replaces deprecated Enterprise adornment tags with the Organization
adornment on gated AI/security/settings surfaces.
- Uses the Discord brand icon in Community while keeping the standard
icon component rendering pattern.
- Tightens Skills/Tools search-to-table spacing so switching tabs does
not shake the table top.
- Fixes the small gap in nested navigation breadcrumbs between Emails
and Calendars.

### Dashboard/data table polish
- Fixes vertically cropped "Not shared" chips on dashboards.
- Keeps table row/header spacing stable after the settings table padding
restoration.

## Validation

- `npx nx lint:diff-with-main twenty-front`
- `npx tsc -p packages/twenty-front/tsconfig.json --noEmit`
- Manual Chrome pass on `apple.localhost:3001` for profile, app
navigation, workspace switcher, AI overview/models/skills/tools/usage,
community/lab, data model, new-field wizard, and dashboards.

## Visual QA

### Settings surface fixes

<img width="1324" height="2668" alt="Settings surface fixes before/after
board"
src="https://github.com/user-attachments/assets/312131fa-3922-4724-9460-46fd373754d1"
/>

### Navigation drawer fixes

<img width="1324" height="1816" alt="Navigation drawer fixes
before/after board"
src="https://github.com/user-attachments/assets/ca0b29e5-e393-46c0-81f7-e6bed2557b59"
/>

### Tables, wizards, gates and chips

<img width="1324" height="2242" alt="Tables, wizards, gates and chips
before/after board"
src="https://github.com/user-attachments/assets/5f358240-c31c-4c15-8ca8-04ed761fbf85"
/>
This commit is contained in:
Thomas des Francs
2026-06-13 13:13:55 +02:00
committed by GitHub
parent 869680a5a1
commit b14195428a
42 changed files with 349 additions and 229 deletions
@@ -19,7 +19,7 @@ const StyledAnimatedContainer = styled(motion.div)`
flex-shrink: 0;
height: 100%;
overflow: hidden;
padding: ${themeCssVariables.spacing[3]} 0 ${themeCssVariables.spacing[4]}
padding: ${themeCssVariables.spacing[2]} 0 ${themeCssVariables.spacing[4]}
${themeCssVariables.spacing[2]};
`;
@@ -27,8 +27,7 @@ const StyledThreadList = styled.div`
flex: 1;
flex-direction: column;
min-height: 0;
padding: ${themeCssVariables.spacing[2]} ${themeCssVariables.spacing[0]};
width: calc(100% - ${themeCssVariables.spacing[2]});
width: 100%;
`;
const StyledSectionsContainer = styled.div`
@@ -5,20 +5,15 @@ import { themeCssVariables } from 'twenty-ui-deprecated/theme-constants';
import { NavigationDrawerAiChatThreadItem } from '@/ai/components/NavigationDrawerAiChatThreadItem';
import { NavigationDrawerAnimatedCollapseWrapper } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerAnimatedCollapseWrapper';
import { NavigationDrawerSection } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerSection';
import { NavigationDrawerSectionTitle } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerSectionTitle';
import { useNavigationSection } from '@/ui/navigation/navigation-drawer/hooks/useNavigationSection';
import { type AgentChatThread } from '~/generated-metadata/graphql';
const StyledSection = styled.section`
display: flex;
flex-direction: column;
`;
const StyledThreadList = styled.div`
display: flex;
flex-direction: column;
gap: ${themeCssVariables.spacing['0.5']};
padding-top: ${themeCssVariables.betweenSiblingsGap};
`;
export type NavigationDrawerAiChatThreadSectionProps = {
@@ -44,7 +39,7 @@ export const NavigationDrawerAiChatThreadSection = ({
useNavigationSection(sectionId);
return (
<StyledSection>
<NavigationDrawerSection>
<NavigationDrawerAnimatedCollapseWrapper>
<NavigationDrawerSectionTitle
label={title}
@@ -74,6 +69,6 @@ export const NavigationDrawerAiChatThreadSection = ({
</StyledThreadList>
</AnimatedExpandableContainer>
) : null}
</StyledSection>
</NavigationDrawerSection>
);
};
@@ -1,6 +1,7 @@
import { isLayoutCustomizationModeEnabledState } from '@/layout-customization/states/isLayoutCustomizationModeEnabledState';
import { NavigationSections } from '@/navigation-menu-item/common/constants/NavigationSections.constants';
import { NavigationMenuItemDndKitProvider } from '@/navigation-menu-item/display/dnd/providers/NavigationMenuItemDndKitProvider';
import { useSortedNavigationMenuItems } from '@/navigation-menu-item/display/hooks/useSortedNavigationMenuItems';
import { FavoritesSection } from '@/navigation-menu-item/display/sections/favorites/components/FavoritesSection';
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
import { AnimatedEaseInOut } from 'twenty-ui-deprecated/utilities';
@@ -9,6 +10,13 @@ export const FavoritesSectionDispatcher = () => {
const isLayoutCustomizationModeEnabled = useAtomStateValue(
isLayoutCustomizationModeEnabledState,
);
const { navigationMenuItemsSorted } = useSortedNavigationMenuItems();
const hasFavorites = navigationMenuItemsSorted.some((item) => !item.folderId);
if (!hasFavorites) {
return null;
}
return (
<AnimatedEaseInOut isOpen={!isLayoutCustomizationModeEnabled} initial>
@@ -34,6 +34,7 @@ export const MainNavigationDrawer = ({ className }: { className?: string }) => {
<NavigationDrawerScrollableContent>
<NavigationDrawerTabbedContent
showAiChatContent={showAiChatContent}
shouldMountAiChatContent={hasAiPermission}
navigationContent={<MainNavigationDrawerNavigationContent />}
/>
</NavigationDrawerScrollableContent>
@@ -1,9 +1,6 @@
import { NavigationDrawerOpenedSection } from '@/navigation-menu-item/display/sections/components/NavigationDrawerOpenedSection';
import { NavigationDrawerWorkspaceSectionSkeletonLoader } from '@/object-metadata/components/NavigationDrawerWorkspaceSectionSkeletonLoader';
import { isLayoutCustomizationModeEnabledState } from '@/layout-customization/states/isLayoutCustomizationModeEnabledState';
import { NavigationDrawerOtherSection } from '@/navigation/components/NavigationDrawerOtherSection';
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
import { styled } from '@linaria/react';
import { lazy, Suspense } from 'react';
@@ -32,10 +29,6 @@ const StyledScrollableItemsContainer = styled.div`
`;
export const MainNavigationDrawerScrollableItems = () => {
const isLayoutCustomizationModeEnabled = useAtomStateValue(
isLayoutCustomizationModeEnabledState,
);
return (
<StyledScrollableItemsContainer>
<NavigationDrawerOpenedSection />
@@ -43,7 +36,6 @@ export const MainNavigationDrawerScrollableItems = () => {
<FavoritesSectionDispatcher />
<WorkspaceSectionDispatcher />
</Suspense>
{!isLayoutCustomizationModeEnabled && <NavigationDrawerOtherSection />}
</StyledScrollableItemsContainer>
);
};
@@ -1,9 +1,10 @@
import { NavigationDrawerAiChatContent } from '@/ai/components/NavigationDrawerAiChatContent';
import { styled } from '@linaria/react';
import { type ReactNode, useState } from 'react';
import { type ReactNode } from 'react';
type NavigationDrawerTabbedContentProps = {
showAiChatContent: boolean;
shouldMountAiChatContent: boolean;
navigationContent: ReactNode;
};
@@ -13,20 +14,15 @@ const StyledTabContent = styled.div<{ isHidden: boolean }>`
export const NavigationDrawerTabbedContent = ({
showAiChatContent,
shouldMountAiChatContent,
navigationContent,
}: NavigationDrawerTabbedContentProps) => {
const [hasOpenedAiChat, setHasOpenedAiChat] = useState(showAiChatContent);
if (showAiChatContent && !hasOpenedAiChat) {
setHasOpenedAiChat(true);
}
return (
<>
<StyledTabContent isHidden={showAiChatContent}>
{navigationContent}
</StyledTabContent>
{hasOpenedAiChat && (
{shouldMountAiChatContent && (
<StyledTabContent isHidden={!showAiChatContent}>
<NavigationDrawerAiChatContent />
</StyledTabContent>
@@ -4,12 +4,14 @@ import { useHasPermissionFlag } from '@/settings/roles/hooks/useHasPermissionFla
import { SettingsNavigationDrawerItems } from '@/settings/components/SettingsNavigationDrawerItems';
import { NavigationDrawer } from '@/ui/navigation/navigation-drawer/components/NavigationDrawer';
import { NavigationDrawerFixedContent } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerFixedContent';
import { NavigationDrawerSection } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerSection';
import { NavigationDrawerScrollableContent } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerScrollableContent';
import { isAdvancedModeEnabledState } from '@/ui/navigation/navigation-drawer/states/isAdvancedModeEnabledState';
import { navigationDrawerActiveTabState } from '@/ui/navigation/states/navigationDrawerActiveTabState';
import { NAVIGATION_DRAWER_TABS } from '@/ui/navigation/states/navigationDrawerTabs';
import { useAtomState } from '@/ui/utilities/state/jotai/hooks/useAtomState';
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
import { css } from '@linaria/core';
import { styled } from '@linaria/react';
import { useLingui } from '@lingui/react/macro';
import { useIsMobile } from 'twenty-ui-deprecated/utilities';
@@ -17,9 +19,17 @@ import { AdvancedSettingsToggle } from 'twenty-ui-deprecated/navigation';
import { themeCssVariables } from 'twenty-ui-deprecated/theme-constants';
import { PermissionFlagType } from '~/generated-metadata/graphql';
const StyledAdvancedToggleWrapper = styled.div<{ isMobile: boolean }>`
const StyledAdvancedToggleFixedContent = styled.div<{ isMobile: boolean }>`
flex-shrink: 0;
margin-top: auto;
padding-left: ${({ isMobile }) =>
isMobile ? themeCssVariables.spacing[5] : '0'};
padding-right: ${({ isMobile }) =>
isMobile ? '0' : themeCssVariables.spacing[1]};
isMobile ? themeCssVariables.spacing[5] : '0'};
`;
const advancedSettingsToggleClassName = css`
padding-right: 0;
`;
export const SettingsNavigationDrawer = ({
@@ -42,7 +52,7 @@ export const SettingsNavigationDrawer = ({
navigationDrawerActiveTab === NAVIGATION_DRAWER_TABS.AI_CHAT_HISTORY;
return (
<NavigationDrawer className={className} title={t`Exit Settings`}>
<NavigationDrawer className={className} title={t`Settings`}>
{hasAiPermission && (
<NavigationDrawerFixedContent>
<MainNavigationDrawerTabsRow />
@@ -52,20 +62,22 @@ export const SettingsNavigationDrawer = ({
<NavigationDrawerScrollableContent>
<NavigationDrawerTabbedContent
showAiChatContent={showAiChatContent}
shouldMountAiChatContent={hasAiPermission}
navigationContent={<SettingsNavigationDrawerItems />}
/>
</NavigationDrawerScrollableContent>
{!showAiChatContent && (
<NavigationDrawerFixedContent>
<StyledAdvancedToggleWrapper isMobile={isMobile}>
<StyledAdvancedToggleFixedContent isMobile={isMobile}>
<NavigationDrawerSection>
<AdvancedSettingsToggle
className={advancedSettingsToggleClassName}
isAdvancedModeEnabled={isAdvancedModeEnabled}
setIsAdvancedModeEnabled={setIsAdvancedModeEnabled}
label={t`Advanced`}
/>
</StyledAdvancedToggleWrapper>
</NavigationDrawerFixedContent>
</NavigationDrawerSection>
</StyledAdvancedToggleFixedContent>
)}
</NavigationDrawer>
);
@@ -17,11 +17,12 @@ const StyledContainer = styled.div`
font-size: ${themeCssVariables.font.size.md};
font-weight: ${themeCssVariables.font.weight.regular};
gap: ${themeCssVariables.spacing[1]};
height: ${themeCssVariables.spacing[3]};
height: ${themeCssVariables.spacing[5]};
line-height: ${themeCssVariables.text.lineHeight.md};
overflow: hidden;
padding: ${themeCssVariables.spacing[1]};
padding: 0 ${themeCssVariables.spacing[1]};
user-select: none;
white-space: nowrap;
`;
export const ForbiddenFieldDisplay = () => {
@@ -4,13 +4,7 @@ import { useMutation, useQuery } from '@apollo/client/react';
import { t } from '@lingui/core/macro';
import { SettingsPath } from 'twenty-shared/types';
import { getSettingsPath } from 'twenty-shared/utils';
import { Tag } from 'twenty-ui-deprecated/components';
import {
H2Title,
IconBolt,
IconLock,
IconRobot,
} from 'twenty-ui-deprecated/display';
import { H2Title, IconBolt, IconRobot } from 'twenty-ui-deprecated/display';
import { Card, Section } from 'twenty-ui-deprecated/layout';
import { themeCssVariables } from 'twenty-ui-deprecated/theme-constants';
@@ -49,6 +43,7 @@ import {
AiModelRole,
type AdminAiModelConfig,
} from '~/generated-admin/graphql';
import { OrganizationAdornment } from '~/pages/settings/enterprise/components/OrganizationAdornment';
const USAGE_TABLE_GRID_TEMPLATE_COLUMNS = '1fr 120px';
@@ -202,14 +197,7 @@ export const SettingsAdminAI = () => {
<H2Title
title={t`Custom Providers`}
description={t`Add custom endpoints, private gateways, or additional regions.`}
adornment={
<Tag
text={t`Enterprise`}
color="transparent"
Icon={IconLock}
variant="border"
/>
}
adornment={<OrganizationAdornment />}
/>
<SettingsAdminAiProviderListCard
@@ -230,6 +218,7 @@ export const SettingsAdminAI = () => {
Icon={IconRobot}
title={t`Smart Model`}
description={t`Default model for chats and complex reasoning`}
divider
>
<Select
dropdownId="admin-smart-model-select"
@@ -318,12 +307,7 @@ export const SettingsAdminAI = () => {
selectSizeVariant="small"
/>
) : (
<Tag
text={t`Enterprise`}
color="transparent"
Icon={IconLock}
variant="border"
/>
<OrganizationAdornment />
)
}
/>
@@ -5,6 +5,7 @@ import {
StyledSettingsCardTextContainer,
StyledSettingsCardTitle,
} from '@/settings/components/SettingsOptions/SettingsCardContentBase';
import { Separator } from '@/settings/components/Separator';
import { SettingsOptionIconCustomizer } from '@/settings/components/SettingsOptions/SettingsOptionIconCustomizer';
import { styled } from '@linaria/react';
import {
@@ -16,6 +17,7 @@ type SettingsOptionCardContentSelectProps = {
Icon?: IconComponent;
title: React.ReactNode;
description?: string;
divider?: boolean;
disabled?: boolean;
children?: React.ReactNode;
};
@@ -31,25 +33,29 @@ export const SettingsOptionCardContentSelect = ({
Icon,
title,
description,
divider,
disabled = false,
children,
}: SettingsOptionCardContentSelectProps) => {
return (
<StyledSettingsCardContent disabled={disabled}>
{Icon && (
<StyledSettingsCardIcon>
<SettingsOptionIconCustomizer Icon={Icon} />
</StyledSettingsCardIcon>
)}
<StyledSettingsCardTextContainer>
<StyledSettingsCardTitle>{title}</StyledSettingsCardTitle>
{description && (
<StyledSettingsCardDescription>
<OverflowingTextWithTooltip text={description} />
</StyledSettingsCardDescription>
<>
<StyledSettingsCardContent disabled={disabled}>
{Icon && (
<StyledSettingsCardIcon>
<SettingsOptionIconCustomizer Icon={Icon} />
</StyledSettingsCardIcon>
)}
</StyledSettingsCardTextContainer>
<StyledSelectContainer>{children}</StyledSelectContainer>
</StyledSettingsCardContent>
<StyledSettingsCardTextContainer>
<StyledSettingsCardTitle>{title}</StyledSettingsCardTitle>
{description && (
<StyledSettingsCardDescription>
<OverflowingTextWithTooltip text={description} />
</StyledSettingsCardDescription>
)}
</StyledSettingsCardTextContainer>
<StyledSelectContainer>{children}</StyledSelectContainer>
</StyledSettingsCardContent>
{divider && <Separator />}
</>
);
};
@@ -12,6 +12,7 @@ type SettingsPageLayoutProps = {
secondaryBar?: ReactNode;
children: ReactNode;
tag?: JSX.Element;
titleColor?: string;
};
export const SettingsPageLayout = ({
@@ -21,6 +22,7 @@ export const SettingsPageLayout = ({
secondaryBar,
children,
tag,
titleColor,
}: SettingsPageLayoutProps) => (
<PageCardLayout
header={
@@ -30,6 +32,7 @@ export const SettingsPageLayout = ({
tag={tag}
actionButton={actionButton}
centerTitle
titleColor={titleColor}
/>
}
secondaryBar={
@@ -1,12 +1,18 @@
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
import { SettingsOptionCardContentToggle } from '@/settings/components/SettingsOptions/SettingsOptionCardContentToggle';
import { useLabPublicFeatureFlags } from '@/settings/lab/hooks/useLabPublicFeatureFlags';
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
import { styled } from '@linaria/react';
import { isNonEmptyString } from '@sniptt/guards';
import { useState } from 'react';
import {
IconRelationManyToMany,
IconSparkles,
type IconComponent,
} from 'twenty-ui-deprecated/display';
import { Card } from 'twenty-ui-deprecated/layout';
import { themeCssVariables } from 'twenty-ui-deprecated/theme-constants';
import { type FeatureFlagKey } from '~/generated-metadata/graphql';
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
import { FeatureFlagKey } from '~/generated-metadata/graphql';
const StyledCardGrid = styled.div`
display: grid;
@@ -22,6 +28,11 @@ const StyledImage = styled.img`
width: 100%;
`;
const labFeatureFlagIcons: Partial<Record<FeatureFlagKey, IconComponent>> = {
[FeatureFlagKey.IS_JUNCTION_RELATIONS_ENABLED]: IconRelationManyToMany,
[FeatureFlagKey.IS_SETTINGS_DISCOVERY_HERO_ENABLED]: IconSparkles,
};
export const SettingsLabContent = () => {
const currentWorkspace = useAtomStateValue(currentWorkspaceState);
const { labPublicFeatureFlags, handleLabPublicFeatureFlagUpdate } =
@@ -38,38 +49,59 @@ export const SettingsLabContent = () => {
setHasImageLoadingError((prev) => ({ ...prev, [key]: true }));
};
const labPublicFeatureFlagsWithImage = labPublicFeatureFlags.filter((flag) =>
isNonEmptyString(flag.metadata.imagePath),
);
const labPublicFeatureFlagsWithoutImage = labPublicFeatureFlags.filter(
(flag) => !isNonEmptyString(flag.metadata.imagePath),
);
return (
currentWorkspace?.id && (
<StyledCardGrid>
{[...labPublicFeatureFlags]
.sort((a, b) => {
// Sort flags with images first
if (a.metadata.imagePath !== '' && b.metadata.imagePath === '')
return -1;
if (a.metadata.imagePath === '' && b.metadata.imagePath !== '')
return 1;
return 0;
})
.map((flag) => (
<Card key={flag.key} rounded>
{flag.metadata.imagePath && !hasImageLoadingError[flag.key] ? (
<StyledImage
src={flag.metadata.imagePath}
alt={flag.metadata.label}
onError={() => handleImageError(flag.key)}
/>
) : (
<></>
)}
{labPublicFeatureFlagsWithImage.map((flag) => (
<Card
key={flag.key}
rounded
backgroundColor={themeCssVariables.background.secondary}
>
{!hasImageLoadingError[flag.key] && (
<StyledImage
src={flag.metadata.imagePath ?? undefined}
alt={flag.metadata.label}
onError={() => handleImageError(flag.key)}
/>
)}
<SettingsOptionCardContentToggle
Icon={labFeatureFlagIcons[flag.key]}
title={flag.metadata.label}
description={flag.metadata.description}
checked={flag.value}
onChange={(value) => handleToggle(flag.key, value)}
toggleCentered={false}
/>
</Card>
))}
{labPublicFeatureFlagsWithoutImage.length > 0 && (
<Card
rounded
backgroundColor={themeCssVariables.background.secondary}
>
{labPublicFeatureFlagsWithoutImage.map((flag, index) => (
<SettingsOptionCardContentToggle
key={flag.key}
Icon={labFeatureFlagIcons[flag.key]}
title={flag.metadata.label}
description={flag.metadata.description}
checked={flag.value}
onChange={(value) => handleToggle(flag.key, value)}
toggleCentered={false}
divider={index < labPublicFeatureFlagsWithoutImage.length - 1}
/>
</Card>
))}
))}
</Card>
)}
</StyledCardGrid>
)
);
@@ -22,8 +22,10 @@ import {
const DELETE_ACCOUNT_MODAL_ID = 'delete-account-modal';
const LEAVE_WORKSPACE_MODAL_ID = 'leave-workspace-modal';
const StyledDiv = styled.div`
margin-bottom: ${themeCssVariables.spacing[2]};
const StyledDangerActions = styled.div`
display: flex;
flex-wrap: wrap;
gap: ${themeCssVariables.spacing[2]};
`;
export const DeleteAccount = () => {
@@ -75,38 +77,39 @@ export const DeleteAccount = () => {
: t`Delete account and all the associated data`
}
/>
{userHasMultipleWorkspaces && (
<StyledDiv>
<StyledDangerActions>
{userHasMultipleWorkspaces && (
<Button
accent="danger"
onClick={() => openModal(LEAVE_WORKSPACE_MODAL_ID)}
variant="secondary"
title={t`Leave workspace`}
/>
<ConfirmationModal
confirmationValue={userEmail}
confirmationPlaceholder={userEmail ?? ''}
modalInstanceId={LEAVE_WORKSPACE_MODAL_ID}
title={t`Leave workspace`}
subtitle={
<>
{t`This action cannot be undone. This will permanently remove your membership from this workspace.`}
<br />
{t`Please type in your email to confirm.`}
</>
}
onConfirmClick={leaveWorkspace}
confirmButtonText={t`Leave workspace`}
/>
</StyledDiv>
)}
<Button
accent="danger"
onClick={() => openModal(DELETE_ACCOUNT_MODAL_ID)}
variant="secondary"
title={t`Delete account`}
/>
</StyledDangerActions>
{userHasMultipleWorkspaces && (
<ConfirmationModal
confirmationValue={userEmail}
confirmationPlaceholder={userEmail ?? ''}
modalInstanceId={LEAVE_WORKSPACE_MODAL_ID}
title={t`Leave workspace`}
subtitle={
<>
{t`This action cannot be undone. This will permanently remove your membership from this workspace.`}
<br />
{t`Please type in your email to confirm.`}
</>
}
onConfirmClick={leaveWorkspace}
confirmButtonText={t`Leave workspace`}
/>
)}
<Button
accent="danger"
onClick={() => openModal(DELETE_ACCOUNT_MODAL_ID)}
variant="secondary"
title={t`Delete account`}
/>
<ConfirmationModal
confirmationValue={userEmail}
confirmationPlaceholder={userEmail ?? ''}
@@ -19,6 +19,7 @@ import {
isRecordFilterValueValid,
} from 'twenty-shared/utils';
import { Button } from 'twenty-ui-deprecated/input';
import { themeCssVariables } from 'twenty-ui-deprecated/theme-constants';
import { useQuery } from '@apollo/client/react';
import {
type BillingEntitlement,
@@ -149,6 +150,7 @@ export const SettingsRolePermissionsObjectLevelObjectForm = ({
return (
<SettingsPageLayout
title={headerTitle}
titleColor={themeCssVariables.font.color.tertiary}
links={breadcrumbLinks}
secondaryBar={
<SettingsWizardStepBar
@@ -25,18 +25,17 @@ import { useAtomState } from '@/ui/utilities/state/jotai/hooks/useAtomState';
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
import { CombinedGraphQLErrors } from '@apollo/client/errors';
import { useMutation } from '@apollo/client/react';
import { Tag } from 'twenty-ui-deprecated/components';
import {
H2Title,
IconClockHour8,
IconHistory,
IconLock,
IconMail,
IconTrash,
} from 'twenty-ui-deprecated/display';
import { Card, Section } from 'twenty-ui-deprecated/layout';
import { themeCssVariables } from 'twenty-ui-deprecated/theme-constants';
import { UpdateWorkspaceDocument } from '~/generated-metadata/graphql';
import { OrganizationAdornment } from '~/pages/settings/enterprise/components/OrganizationAdornment';
const StyledContainer = styled.div`
width: 100%;
@@ -190,14 +189,7 @@ export const SettingsSecuritySettings = () => {
<H2Title
title={t`SSO`}
description={t`Configure an SSO connection`}
adornment={
<Tag
text={t`Enterprise`}
color="transparent"
Icon={IconLock}
variant="border"
/>
}
adornment={<OrganizationAdornment />}
/>
<SettingsSSOIdentitiesProvidersListCard />
</Section>
@@ -246,14 +238,7 @@ export const SettingsSecuritySettings = () => {
<H2Title
title={t`Audit Logs`}
description={t`Configure how long audit logs are retained`}
adornment={
<Tag
text={t`Enterprise`}
color="transparent"
Icon={IconLock}
variant="border"
/>
}
adornment={<OrganizationAdornment />}
/>
{hasEnterpriseAccess ? (
<Card rounded>
@@ -8,7 +8,6 @@ import {
useIsMobile,
} from 'twenty-ui-deprecated/utilities';
import { BACKGROUND_MOCK_OTHER_ITEMS } from '@/sign-in-background-mock/constants/BackgroundMockOtherItems';
import { BACKGROUND_MOCK_WORKSPACE_ITEMS } from '@/sign-in-background-mock/constants/BackgroundMockNavigationItems';
import { NavigationDrawer } from '@/ui/navigation/navigation-drawer/components/NavigationDrawer';
import { NavigationDrawerItem } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerItem';
@@ -63,17 +62,6 @@ export const BackgroundMockNavigationDrawer = ({
/>
))}
</NavigationDrawerSection>
<NavigationDrawerSection>
<NavigationDrawerSectionTitle label={t`Other`} />
{BACKGROUND_MOCK_OTHER_ITEMS.map((item) => (
<NavigationDrawerItem
key={item.label}
label={item.label}
Icon={item.Icon}
onClick={() => {}}
/>
))}
</NavigationDrawerSection>
</NavigationDrawer>
);
};
@@ -8,6 +8,7 @@ const StyledHeader = styled.li`
border-bottom: 1px solid ${themeCssVariables.border.color.light};
border-top-left-radius: ${themeCssVariables.border.radius.sm};
border-top-right-radius: ${themeCssVariables.border.radius.sm};
box-sizing: content-box;
color: ${themeCssVariables.font.color.primary};
cursor: ${({ onClick }) => (onClick ? 'pointer' : 'default')};
display: flex;
@@ -3,7 +3,19 @@ import { useCommandMenuHotKeys } from '@/command-menu/hooks/useCommandMenuHotKey
import { SidePanelForDesktop } from '@/side-panel/components/SidePanelForDesktop';
import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile';
import { styled } from '@linaria/react';
import { Outlet } from 'react-router-dom';
import { AnimatePresence, motion } from 'framer-motion';
import { useLocation, useOutlet } from 'react-router-dom';
import { AppPath } from 'twenty-shared/types';
const APP_TO_SETTINGS_TRANSITION_DURATION_IN_SECONDS = 0.3;
const SETTINGS_PATH_PREFIX = `/${AppPath.Settings}`;
const getMainAppLayoutRouteSection = (pathname: string) =>
pathname === SETTINGS_PATH_PREFIX ||
pathname.startsWith(`${SETTINGS_PATH_PREFIX}/`)
? 'settings'
: 'app';
const StyledRow = styled.div`
display: flex;
@@ -21,6 +33,47 @@ const StyledContent = styled.div`
overflow: hidden;
`;
const StyledContentTransitionContainer = styled.div`
display: grid;
flex: 1 1 0;
min-height: 0;
min-width: 0;
overflow: hidden;
`;
const StyledContentTransitionPage = styled(motion.div)`
display: flex;
grid-area: 1 / 1;
min-height: 0;
min-width: 0;
width: 100%;
`;
const MainAppLayoutOutlet = () => {
const { pathname } = useLocation();
const outlet = useOutlet();
const routeSection = getMainAppLayoutRouteSection(pathname);
return (
<StyledContentTransitionContainer>
<AnimatePresence initial={false}>
<StyledContentTransitionPage
key={routeSection}
initial={{ opacity: 0, y: 4 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -4 }}
transition={{
duration: APP_TO_SETTINGS_TRANSITION_DURATION_IN_SECONDS,
ease: 'easeInOut',
}}
>
{outlet}
</StyledContentTransitionPage>
</AnimatePresence>
</StyledContentTransitionContainer>
);
};
export const MainAppLayoutWithSidePanel = () => {
const isMobile = useIsMobile();
@@ -29,7 +82,7 @@ export const MainAppLayoutWithSidePanel = () => {
return (
<StyledRow>
<StyledContent>
<Outlet />
<MainAppLayoutOutlet />
</StyledContent>
{isMobile ? <CommandMenuForMobile /> : <SidePanelForDesktop />}
</StyledRow>
@@ -20,6 +20,7 @@ type PageCardHeaderProps = {
tag?: ReactNode;
actionButton?: ReactNode;
centerTitle?: boolean;
titleColor?: string;
};
const StyledHeader = styled.div`
@@ -43,9 +44,10 @@ const StyledLeft = styled.div`
overflow: hidden;
`;
const StyledTitle = styled.div`
const StyledTitle = styled.div<{ titleColor?: string }>`
align-items: center;
color: ${themeCssVariables.font.color.primary};
color: ${({ titleColor }) =>
titleColor ?? themeCssVariables.font.color.primary};
display: flex;
font-size: ${themeCssVariables.font.size.md};
font-weight: ${themeCssVariables.font.weight.semiBold};
@@ -79,6 +81,7 @@ export const PageCardHeader = ({
tag,
actionButton,
centerTitle = false,
titleColor,
}: PageCardHeaderProps) => {
const isMobile = useIsMobile();
const isNavigationDrawerExpanded = useNavigationDrawerExpanded();
@@ -104,11 +107,13 @@ export const PageCardHeader = ({
? breadcrumb
: isDefined(links) && <Breadcrumb links={links} />}
{!centerTitle && hasTitleContent && (
<StyledTitle>{titleContent}</StyledTitle>
<StyledTitle titleColor={titleColor}>{titleContent}</StyledTitle>
)}
</StyledLeft>
{centerTitle && hasTitleContent && (
<StyledCenteredTitle>{titleContent}</StyledCenteredTitle>
<StyledCenteredTitle titleColor={titleColor}>
{titleContent}
</StyledCenteredTitle>
)}
<StyledRight
data-click-outside-id={PAGE_ACTION_CONTAINER_CLICK_OUTSIDE_ID}
@@ -1,5 +1,20 @@
import { styled } from '@linaria/react';
import { themeCssVariables } from 'twenty-ui-deprecated/theme-constants';
const StyledTable = styled.div``;
const StyledTable = styled.div`
display: flex;
flex-direction: column;
width: 100%;
> [data-table-row]:first-child:not(:has([data-table-header])),
> [data-table-row]:has([data-table-header])
+ [data-table-row]:not(:has([data-table-header])) {
margin-top: ${themeCssVariables.spacing[2]};
}
> [data-table-row]:last-child:not(:has([data-table-header])) {
margin-bottom: ${themeCssVariables.spacing[2]};
}
`;
export { StyledTable as Table };
@@ -1,11 +1,14 @@
import { styled } from '@linaria/react';
import { type ComponentPropsWithoutRef, type Ref, forwardRef } from 'react';
import { themeCssVariables } from 'twenty-ui-deprecated/theme-constants';
const StyledTableHeader = styled.div<{
type TableHeaderProps = Omit<ComponentPropsWithoutRef<'div'>, 'onClick'> & {
align?: 'left' | 'center' | 'right';
onClick?: () => void;
padding?: string;
}>`
};
const StyledTableHeader = styled.div<TableHeaderProps>`
align-items: center;
border-bottom: 1px solid ${themeCssVariables.border.color.light};
color: ${themeCssVariables.font.color.tertiary};
@@ -24,4 +27,13 @@ const StyledTableHeader = styled.div<{
text-align: ${({ align }) => align ?? 'left'};
`;
export { StyledTableHeader as TableHeader };
export const TableHeader = forwardRef(
(props: TableHeaderProps, ref: Ref<HTMLDivElement>) => (
<StyledTableHeader
// oxlint-disable-next-line react/jsx-props-no-spreading
{...props}
ref={ref}
data-table-header
/>
),
);
@@ -107,6 +107,7 @@ export const TableRow = ({
className={className}
style={style}
data-clickable={isClickable}
data-table-row
mobileGridAutoColumns={mobileGridAutoColumns}
height={height}
cursor={cursor}
@@ -32,7 +32,7 @@ export const MultiWorkspaceDropdownButton = () => {
return (
<Dropdown
dropdownId={MULTI_WORKSPACE_DROPDOWN_ID}
dropdownOffset={{ y: -29, x: -5 }}
dropdownOffset={{ y: -31, x: -5 }}
clickableComponent={
<MultiWorkspaceDropdownClickableComponent
disabled={isLayoutCustomizationModeEnabled}
@@ -224,6 +224,13 @@ export const MultiWorkspaceDropdownDefaultComponents = () => {
>
<MenuItem LeftIcon={IconUserPlus} text={t`Invite user`} />
</UndecoratedLink>
{isSupportChatConfigured && (
<MenuItem
LeftIcon={IconMessage}
text={t`Support`}
onClick={handleSupport}
/>
)}
<UndecoratedLink
to={getSettingsPath(SettingsPath.ProfilePage)}
onClick={() => {
@@ -233,13 +240,6 @@ export const MultiWorkspaceDropdownDefaultComponents = () => {
>
<MenuItem LeftIcon={IconSettings} text={t`Settings`} />
</UndecoratedLink>
{isSupportChatConfigured && (
<MenuItem
LeftIcon={IconMessage}
text={t`Support`}
onClick={handleSupport}
/>
)}
</DropdownMenuItemsContainer>
</DropdownContent>
);
@@ -15,6 +15,7 @@ export const StyledContainer = styled.div<{
display: flex;
gap: ${({ isNavigationDrawerExpanded }) =>
isNavigationDrawerExpanded ? themeCssVariables.spacing[2] : '0'};
height: ${themeCssVariables.spacing[7]};
max-width: 100%;
min-width: 0;
opacity: ${({ disabled }) => (disabled ? 0.5 : 1)};
@@ -62,7 +62,7 @@ const StyledContainer = styled.div<{
flex-direction: column;
gap: ${themeCssVariables.spacing[3]};
height: 100%;
padding: ${themeCssVariables.spacing[3]} 0 ${themeCssVariables.spacing[4]}
padding: ${themeCssVariables.spacing[2]} 0 ${themeCssVariables.spacing[4]}
${themeCssVariables.spacing[2]};
width: ${({ isExpanded }) =>
isExpanded ? `var(${NAVIGATION_DRAWER_WIDTH_VAR})` : '100%'};
@@ -23,19 +23,22 @@ type NavigationDrawerBackButtonProps = {
const StyledIconAndButtonContainer = styled.button`
align-items: center;
background: inherit;
border: none;
color: ${themeCssVariables.font.color.secondary};
border: 1px solid transparent;
border-radius: ${themeCssVariables.border.radius.sm};
box-sizing: border-box;
color: ${themeCssVariables.font.color.primary};
cursor: pointer;
display: flex;
flex-direction: row;
font-family: ${themeCssVariables.font.family};
font-size: ${themeCssVariables.font.size.md};
font-weight: ${themeCssVariables.font.weight.medium};
gap: ${themeCssVariables.spacing[2]};
padding: ${themeCssVariables.spacing[1]};
width: 100%;
height: ${themeCssVariables.spacing[7]};
padding: 2px ${themeCssVariables.spacing[1]} 2px 2px;
width: fit-content;
&:hover {
background: ${themeCssVariables.background.transparent.light};
border-radius: ${themeCssVariables.border.radius.sm};
}
`;
@@ -43,6 +46,7 @@ const StyledContainer = styled.div`
align-items: center;
display: flex;
flex-direction: row;
flex-shrink: 0;
height: ${themeCssVariables.spacing[8]};
justify-content: space-between;
`;
@@ -84,8 +88,8 @@ export const NavigationDrawerBackButton = ({
<StyledIconAndButtonContainer>
<IconX
size={theme.icon.size.md}
stroke={theme.icon.stroke.lg}
color={theme.font.color.tertiary}
stroke={theme.icon.stroke.sm}
color={theme.font.color.secondary}
/>
<span>{title}</span>
</StyledIconAndButtonContainer>
@@ -6,6 +6,7 @@ import { useIsMobile } from 'twenty-ui-deprecated/utilities';
import { themeCssVariables } from 'twenty-ui-deprecated/theme-constants';
const StyledFixedContainer = styled.div<{ isMobile?: boolean }>`
flex-shrink: 0;
padding-left: ${({ isMobile }) =>
isMobile ? themeCssVariables.spacing[5] : '0'};
padding-right: ${({ isMobile }) =>
@@ -19,6 +19,7 @@ const StyledContainer = styled.div<{ isExpanded: boolean }>`
align-items: ${({ isExpanded }) => (isExpanded ? 'center' : 'flex-start')};
display: flex;
flex-direction: ${({ isExpanded }) => (isExpanded ? 'row' : 'column')};
flex-shrink: 0;
gap: ${({ isExpanded }) => (isExpanded ? '0' : themeCssVariables.spacing[4])};
min-height: ${PAGE_BAR_MIN_HEIGHT}px;
padding-right: ${themeCssVariables.spacing[2]};
@@ -37,7 +38,8 @@ const StyledRightActions = styled.div<{ isExpanded: boolean }>`
display: flex;
flex-direction: ${({ isExpanded }) => (isExpanded ? 'row' : 'column')};
flex-shrink: 0;
gap: ${({ isExpanded }) => (isExpanded ? '0' : themeCssVariables.spacing[1])};
gap: ${({ isExpanded }) =>
isExpanded ? '2px' : themeCssVariables.spacing[1]};
margin-left: ${({ isExpanded }) => (isExpanded ? 'auto' : '0')};
transition: gap calc(${themeCssVariables.animation.duration.normal} * 1s) ease;
`;
@@ -45,7 +47,7 @@ const StyledRightActions = styled.div<{ isExpanded: boolean }>`
const StyledNavigationDrawerCollapseButtonContainer = styled.div`
> * {
height: ${themeCssVariables.spacing[6]};
padding-right: ${themeCssVariables.spacing[1]};
padding-right: 0;
width: ${themeCssVariables.spacing[6]};
}
@@ -40,13 +40,13 @@ const StyledSecondaryFullVerticalBar = styled.div<{ darker: boolean }>`
? themeCssVariables.font.color.tertiary
: themeCssVariables.border.color.strong};
height: 28px;
height: calc(${themeCssVariables.spacing[7]} + 1px);
position: relative;
top: -17px;
width: 1px;
@media (max-width: ${MOBILE_VIEWPORT}px) {
height: ${themeCssVariables.spacing[8]};
height: calc(${themeCssVariables.spacing[8]} + 1px);
}
`;
@@ -9,10 +9,18 @@ const StyledItemsContainer = styled.div`
display: flex;
flex-direction: column;
height: 100%;
min-height: 0;
width: 100%;
`;
const StyledNavigationDrawerScrollWrapper = styled(ScrollWrapper)`
flex: 1 1 auto;
min-height: 0;
`;
const StyledScrollableMobileInnerContainer = styled.div`
height: 100%;
min-height: 0;
padding-left: ${themeCssVariables.spacing[5]};
padding-right: ${themeCssVariables.spacing[5]};
`;
@@ -26,7 +34,7 @@ export const NavigationDrawerScrollableContent = ({
const isMobile = useIsMobile();
return (
<ScrollWrapper
<StyledNavigationDrawerScrollWrapper
componentInstanceId={`scroll-wrapper-${
isSettingsDrawer ? 'settings-' : ''
}navigation-drawer`}
@@ -41,6 +49,6 @@ export const NavigationDrawerScrollableContent = ({
<>{children}</>
)}
</StyledItemsContainer>
</ScrollWrapper>
</StyledNavigationDrawerScrollWrapper>
);
};
@@ -22,8 +22,8 @@ import { SETTINGS_SKILL_TABLE_METADATA } from '~/pages/settings/ai/constants/Set
import { normalizeSearchText } from '~/utils/normalizeSearchText';
import { SettingsAgentSkillsTable } from './SettingsAgentSkillsTable';
const StyledSearchInput = styled(SearchInput)`
margin-bottom: ${themeCssVariables.spacing[4]};
const StyledSearchContainer = styled.div`
padding-bottom: ${themeCssVariables.spacing[2]};
`;
export const SettingsAgentSkillsTab = () => {
@@ -89,32 +89,34 @@ export const SettingsAgentSkillsTab = () => {
description={t`Use filter to see existing skills or create your own`}
/>
<StyledSearchInput
placeholder={t`Search a skill...`}
value={searchTerm}
onChange={setSearchTerm}
filterDropdown={(filterButton) => (
<Dropdown
dropdownId="settings-skills-filter-dropdown"
dropdownPlacement="bottom-end"
dropdownOffset={{ x: 0, y: 8 }}
clickableComponent={filterButton}
dropdownComponents={
<DropdownContent>
<DropdownMenuItemsContainer>
<MenuItemToggle
LeftIcon={IconArchive}
onToggleChange={setShowDeactivated}
toggled={showDeactivated}
text={t`Deactivated`}
toggleSize="small"
/>
</DropdownMenuItemsContainer>
</DropdownContent>
}
/>
)}
/>
<StyledSearchContainer>
<SearchInput
placeholder={t`Search a skill...`}
value={searchTerm}
onChange={setSearchTerm}
filterDropdown={(filterButton) => (
<Dropdown
dropdownId="settings-skills-filter-dropdown"
dropdownPlacement="bottom-end"
dropdownOffset={{ x: 0, y: 8 }}
clickableComponent={filterButton}
dropdownComponents={
<DropdownContent>
<DropdownMenuItemsContainer>
<MenuItemToggle
LeftIcon={IconArchive}
onToggleChange={setShowDeactivated}
toggled={showDeactivated}
text={t`Deactivated`}
toggleSize="small"
/>
</DropdownMenuItemsContainer>
</DropdownContent>
}
/>
)}
/>
</StyledSearchContainer>
<SettingsAgentSkillsTable
skills={filteredSkills}
loading={loading}
@@ -129,6 +129,7 @@ export const SettingsAiModelsTab = () => {
Icon={IconBrain}
title={t`Smart Model`}
description={t`Used for chats, agents, and complex reasoning`}
divider
>
<Select
dropdownId="models-tab-smart-model-select"
@@ -98,11 +98,15 @@ export const SettingsAiOverviewTab = () => {
label: t`Conversations`,
value: stats ? stats.conversationsCount.toString() : '—',
},
],
[
{
Icon: IconSparkles,
label: t`Skills`,
value: stats ? stats.skillsCount.toString() : '—',
},
],
[
{
Icon: IconTool,
label: t`Tools`,
@@ -14,9 +14,9 @@ import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomState
import { t } from '@lingui/core/macro';
import { SettingsPath } from 'twenty-shared/types';
import { getSettingsPath } from 'twenty-shared/utils';
import { Tag } from 'twenty-ui-deprecated/components';
import { H2Title, IconLock } from 'twenty-ui-deprecated/display';
import { H2Title } from 'twenty-ui-deprecated/display';
import { Section } from 'twenty-ui-deprecated/layout';
import { OrganizationAdornment } from '~/pages/settings/enterprise/components/OrganizationAdornment';
export const SettingsAiUsageTab = () => {
const currentWorkspace = useAtomStateValue(currentWorkspaceState);
@@ -41,14 +41,7 @@ export const SettingsAiUsageTab = () => {
<H2Title
title={t`AI Usage`}
description={t`Track AI consumption across your workspace.`}
adornment={
<Tag
text={t`Enterprise`}
color="transparent"
Icon={IconLock}
variant="border"
/>
}
adornment={<OrganizationAdornment />}
/>
<SettingsEnterpriseFeatureGateCard
title={t`Enterprise feature`}
@@ -8,9 +8,9 @@ import { useContext } from 'react';
import {
H2Title,
IconBriefcase,
type IconComponent,
IconMessage,
IconTransform,
type IconComponent,
useIcons,
} from 'twenty-ui-deprecated/display';
import { Section } from 'twenty-ui-deprecated/layout';
import { ThemeContext } from 'twenty-ui-deprecated/theme-constants';
@@ -29,6 +29,8 @@ type SettingsCommunityLink = {
export const SettingsCommunity = () => {
const { theme } = useContext(ThemeContext);
const { getIcon } = useIcons();
const IconBrandDiscord = getIcon('IconBrandDiscord');
const communityLinks: SettingsCommunityLink[] = [
{
@@ -42,7 +44,7 @@ export const SettingsCommunity = () => {
title: t`Discord`,
description: t`Join our community to get help and share feedback.`,
href: 'https://discord.com/invite/cx5n4Jzs57',
Icon: IconMessage,
Icon: IconBrandDiscord,
cardTitle: t`Join our Discord`,
},
{
@@ -108,7 +108,7 @@ export const SettingsObjects = () => {
<Button
title={t`Visualize`}
variant="secondary"
size="small"
size="medium"
Icon={IconEye}
/>
</UndecoratedLink>
@@ -24,6 +24,7 @@ import { getSettingsPath, isDefined } from 'twenty-shared/utils';
import { H2Title } from 'twenty-ui-deprecated/display';
import { Button } from 'twenty-ui-deprecated/input';
import { Section } from 'twenty-ui-deprecated/layout';
import { themeCssVariables } from 'twenty-ui-deprecated/theme-constants';
import { type z } from 'zod';
import { FieldMetadataType } from '~/generated-metadata/graphql';
import { useNavigateApp } from '~/hooks/useNavigateApp';
@@ -182,6 +183,7 @@ export const SettingsObjectNewFieldConfigure = () => {
>
<SettingsPageLayout
title={activeObjectMetadataItem.labelPlural}
titleColor={themeCssVariables.font.color.tertiary}
links={[
{
children: t`Workspace`,
@@ -13,6 +13,7 @@ import { FormProvider, useForm } from 'react-hook-form';
import { useParams } from 'react-router-dom';
import { AppPath, SettingsPath } from 'twenty-shared/types';
import { getSettingsPath, isDefined } from 'twenty-shared/utils';
import { themeCssVariables } from 'twenty-ui-deprecated/theme-constants';
import { z } from 'zod';
import { FieldMetadataType } from '~/generated-metadata/graphql';
import { useNavigateApp } from '~/hooks/useNavigateApp';
@@ -66,6 +67,7 @@ export const SettingsObjectNewFieldSelect = () => {
>
<SettingsPageLayout
title={activeObjectMetadataItem.labelPlural}
titleColor={themeCssVariables.font.color.tertiary}
links={[
{
children: t`Workspace`,
@@ -9,6 +9,7 @@ import { t } from '@lingui/core/macro';
import { Navigate, useParams, useSearchParams } from 'react-router-dom';
import { SettingsPath } from 'twenty-shared/types';
import { getSettingsPath, isDefined } from 'twenty-shared/utils';
import { themeCssVariables } from 'twenty-ui-deprecated/theme-constants';
import { useQuery } from '@apollo/client/react';
import { FindOneAgentDocument } from '~/generated-metadata/graphql';
@@ -84,6 +85,7 @@ export const SettingsRoleAddObjectLevel = () => {
<SettingsRolesQueryEffect />
<SettingsPageLayout
title={headerTitle}
titleColor={themeCssVariables.font.color.tertiary}
links={breadcrumbLinks}
secondaryBar={<SettingsWizardStepBar label={t`1. Select an object`} />}
>
@@ -24,12 +24,14 @@ type AdvancedSettingsToggleProps = {
isAdvancedModeEnabled: boolean;
setIsAdvancedModeEnabled: (enabled: boolean) => void;
label?: string;
className?: string;
};
export const AdvancedSettingsToggle = ({
isAdvancedModeEnabled,
setIsAdvancedModeEnabled,
label = 'Advanced',
className,
}: AdvancedSettingsToggleProps) => {
const { theme } = useContext(ThemeContext);
@@ -39,7 +41,7 @@ export const AdvancedSettingsToggle = ({
const instanceId = useId();
return (
<StyledContainer htmlFor={instanceId}>
<StyledContainer className={className} htmlFor={instanceId}>
<StyledText>{label}</StyledText>
<Toggle
id={instanceId}