From 50521dec340332f9260b5fb786891111dfb0052d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20Malfait?= Date: Sat, 30 Aug 2025 16:43:04 +0200 Subject: [PATCH] Revamp settings navigation (#14174) Menu was getting too long so I'm removing the developer section and merging API+Webhook together (in the past we had made a split partially because it was in the advanced mode), I'm also merging Lab and Releases. I'm moving Approved Domains and Workspace Domain to a new dedicated page, which will also hold email domains by end of year, because it makes sense to do all those at once (involve DNS changes). --- .../modules/app/components/SettingsRoutes.tsx | 51 ++++---- .../SettingsDevelopersWebhookForm.tsx | 6 +- .../developers/hooks/useWebhookForm.ts | 4 +- .../hooks/useSettingsNavigationItems.tsx | 74 +++++------- .../src/modules/types/SettingsPath.ts | 17 +-- .../src/pages/settings/SettingsWorkspace.tsx | 36 +----- .../SettingsDevelopersApiKeyDetail.tsx | 6 +- .../api-keys/SettingsDevelopersApiKeysNew.tsx | 6 +- .../playground/SettingsGraphQLPlayground.tsx | 6 +- .../playground/SettingsRestPlayground.tsx | 8 +- .../settings/domains/SettingsDomains.tsx | 58 +++++++++ .../SettingsWorkspaceDomainCard.tsx | 36 ++++++ .../settings/domains/components/index.ts | 1 + .../src/pages/settings/domains/index.ts | 1 + .../src/pages/settings/lab/SettingsLab.tsx | 25 ---- .../settings/releases/SettingsReleases.tsx | 46 +++++++ .../SettingsReleasesChangelogContent.tsx} | 43 ++----- .../components/SettingsReleasesTabContent.tsx | 24 ++++ .../settings/releases/components/index.ts | 2 + .../constants/SettingsReleasesTabs.ts | 4 + .../constants/SettingsReleasesTabsId.ts | 1 + .../settings/releases/constants/index.ts | 2 + .../src/pages/settings/releases/index.ts | 1 + .../settings/security/SettingsSecurity.tsx | 10 +- .../SettingsSecurityApprovedAccessDomain.tsx | 8 +- .../workspace/SettingsApiWebhooks.tsx | 113 ++++++++++++++++++ .../settings/workspace/SettingsDomain.tsx | 6 +- .../twenty-front/src/utils/title-utils.ts | 7 +- .../developers/api-and-webhooks/api.mdx | 2 +- .../developers/api-and-webhooks/webhooks.mdx | 4 +- .../developers/backend-development/zapier.mdx | 2 +- .../configure-workspace-in-three-steps.mdx | 2 +- .../getting-started/getting-around-twenty.mdx | 2 +- .../user-guide/integrations/api-webhooks.mdx | 8 +- .../user-guide/integrations/workflows.mdx | 2 +- 35 files changed, 403 insertions(+), 221 deletions(-) create mode 100644 packages/twenty-front/src/pages/settings/domains/SettingsDomains.tsx create mode 100644 packages/twenty-front/src/pages/settings/domains/components/SettingsWorkspaceDomainCard.tsx create mode 100644 packages/twenty-front/src/pages/settings/domains/components/index.ts create mode 100644 packages/twenty-front/src/pages/settings/domains/index.ts delete mode 100644 packages/twenty-front/src/pages/settings/lab/SettingsLab.tsx create mode 100644 packages/twenty-front/src/pages/settings/releases/SettingsReleases.tsx rename packages/twenty-front/src/pages/settings/{Releases.tsx => releases/components/SettingsReleasesChangelogContent.tsx} (67%) create mode 100644 packages/twenty-front/src/pages/settings/releases/components/SettingsReleasesTabContent.tsx create mode 100644 packages/twenty-front/src/pages/settings/releases/components/index.ts create mode 100644 packages/twenty-front/src/pages/settings/releases/constants/SettingsReleasesTabs.ts create mode 100644 packages/twenty-front/src/pages/settings/releases/constants/SettingsReleasesTabsId.ts create mode 100644 packages/twenty-front/src/pages/settings/releases/constants/index.ts create mode 100644 packages/twenty-front/src/pages/settings/releases/index.ts create mode 100644 packages/twenty-front/src/pages/settings/workspace/SettingsApiWebhooks.tsx diff --git a/packages/twenty-front/src/modules/app/components/SettingsRoutes.tsx b/packages/twenty-front/src/modules/app/components/SettingsRoutes.tsx index e7164270a9..7a49e0414e 100644 --- a/packages/twenty-front/src/modules/app/components/SettingsRoutes.tsx +++ b/packages/twenty-front/src/modules/app/components/SettingsRoutes.tsx @@ -6,14 +6,6 @@ import { SettingsSkeletonLoader } from '@/settings/components/SettingsSkeletonLo import { SettingsPath } from '@/types/SettingsPath'; import { PermissionFlagType } from '~/generated/graphql'; -const SettingsApiKeys = lazy(() => - import('~/pages/settings/developers/api-keys/SettingsApiKeys').then( - (module) => ({ - default: module.SettingsApiKeys, - }), - ), -); - const SettingsGraphQLPlayground = lazy(() => import( '~/pages/settings/developers/playground/SettingsGraphQLPlayground' @@ -30,14 +22,6 @@ const SettingsRestPlayground = lazy(() => ), ); -const SettingsWebhooks = lazy(() => - import( - '~/pages/settings/developers/webhooks/components/SettingsWebhooks' - ).then((module) => ({ - default: module.SettingsWebhooks, - })), -); - const SettingsAccountsCalendars = lazy(() => import('~/pages/settings/accounts/SettingsAccountsCalendars').then( (module) => ({ @@ -112,12 +96,6 @@ const SettingsDevelopersApiKeysNew = lazy(() => })), ); -const Releases = lazy(() => - import('~/pages/settings/Releases').then((module) => ({ - default: module.Releases, - })), -); - const SettingsServerlessFunctions = lazy(() => import( '~/pages/settings/serverless-functions/SettingsServerlessFunctions' @@ -146,6 +124,18 @@ const SettingsWorkspace = lazy(() => })), ); +const SettingsDomains = lazy(() => + import('~/pages/settings/domains').then((module) => ({ + default: module.SettingsDomains, + })), +); + +const SettingsApiWebhooks = lazy(() => + import('~/pages/settings/workspace/SettingsApiWebhooks').then((module) => ({ + default: module.SettingsApiWebhooks, + })), +); + const SettingsAI = lazy(() => import('~/pages/settings/ai/SettingsAI').then((module) => ({ default: module.SettingsAI, @@ -347,9 +337,9 @@ const SettingsAdminConfigVariableDetails = lazy(() => })), ); -const SettingsLab = lazy(() => - import('~/pages/settings/lab/SettingsLab').then((module) => ({ - default: module.SettingsLab, +const SettingsReleases = lazy(() => + import('~/pages/settings/releases/SettingsReleases').then((module) => ({ + default: module.SettingsReleases, })), ); @@ -428,6 +418,11 @@ export const SettingsRoutes = ({ } > } /> + } /> + } + /> } /> } > - } /> - } /> } @@ -578,7 +571,7 @@ export const SettingsRoutes = ({ /> )} - } /> + } > - } /> + } /> diff --git a/packages/twenty-front/src/modules/settings/developers/components/SettingsDevelopersWebhookForm.tsx b/packages/twenty-front/src/modules/settings/developers/components/SettingsDevelopersWebhookForm.tsx index b026e2674a..b1ef94c7d9 100644 --- a/packages/twenty-front/src/modules/settings/developers/components/SettingsDevelopersWebhookForm.tsx +++ b/packages/twenty-front/src/modules/settings/developers/components/SettingsDevelopersWebhookForm.tsx @@ -131,8 +131,8 @@ export const SettingsDevelopersWebhookForm = ({ href: getSettingsPath(SettingsPath.Workspace), }, { - children: t`Webhooks`, - href: getSettingsPath(SettingsPath.Webhooks), + children: t`APIs & Webhooks`, + href: getSettingsPath(SettingsPath.ApiWebhooks), }, { children: isCreationMode ? t`New` : getTitle() }, ]} @@ -140,7 +140,7 @@ export const SettingsDevelopersWebhookForm = ({ navigate(SettingsPath.Webhooks)} + onCancel={() => navigate(SettingsPath.ApiWebhooks)} onSave={formConfig.handleSubmit(handleSave)} /> } diff --git a/packages/twenty-front/src/modules/settings/developers/hooks/useWebhookForm.ts b/packages/twenty-front/src/modules/settings/developers/hooks/useWebhookForm.ts index d56a9cfbee..46d1242d6d 100644 --- a/packages/twenty-front/src/modules/settings/developers/hooks/useWebhookForm.ts +++ b/packages/twenty-front/src/modules/settings/developers/hooks/useWebhookForm.ts @@ -100,7 +100,7 @@ export const useWebhookForm = ({ webhookId, mode }: UseWebhookFormProps) => { }); navigate( - createdWebhook ? SettingsPath.WebhookDetail : SettingsPath.Webhooks, + createdWebhook ? SettingsPath.WebhookDetail : SettingsPath.ApiWebhooks, createdWebhook ? { webhookId: createdWebhook.id } : undefined, ); } catch (error) { @@ -188,7 +188,7 @@ export const useWebhookForm = ({ webhookId, mode }: UseWebhookFormProps) => { message: t`Webhook deleted successfully`, }); - navigate(SettingsPath.Webhooks); + navigate(SettingsPath.ApiWebhooks); } catch (error) { enqueueErrorSnackBar({ apolloError: error instanceof ApolloError ? error : undefined, diff --git a/packages/twenty-front/src/modules/settings/hooks/useSettingsNavigationItems.tsx b/packages/twenty-front/src/modules/settings/hooks/useSettingsNavigationItems.tsx index eef4c74969..90a6fa5c9f 100644 --- a/packages/twenty-front/src/modules/settings/hooks/useSettingsNavigationItems.tsx +++ b/packages/twenty-front/src/modules/settings/hooks/useSettingsNavigationItems.tsx @@ -3,7 +3,6 @@ import { SettingsPath } from '@/types/SettingsPath'; import { useAuth } from '@/auth/hooks/useAuth'; import { currentUserState } from '@/auth/states/currentUserState'; import { billingState } from '@/client-config/states/billingState'; -import { labPublicFeatureFlagsState } from '@/client-config/states/labPublicFeatureFlagsState'; import { usePermissionFlagMap } from '@/settings/roles/hooks/usePermissionFlagMap'; import { type NavigationDrawerItemIndentationLevel } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerItem'; import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled'; @@ -18,7 +17,6 @@ import { type IconComponent, IconCurrencyDollar, IconDoorEnter, - IconFlask, IconFunction, IconHierarchy2, IconKey, @@ -31,7 +29,7 @@ import { IconSparkles, IconUserCircle, IconUsers, - IconWebhook, + IconWorld, } from 'twenty-ui/display'; import { FeatureFlagKey, PermissionFlagType } from '~/generated/graphql'; @@ -65,7 +63,6 @@ const useSettingsNavigationItems = (): SettingsNavigationSection[] => { const isAdminEnabled = (currentUser?.canImpersonate || currentUser?.canAccessFullAdminPanel) ?? false; - const labPublicFeatureFlags = useRecoilValue(labPublicFeatureFlagsState); const isAIEnabled = useIsFeatureEnabled(FeatureFlagKey.IS_AI_ENABLED); const isPageLayoutEnabled = useIsFeatureEnabled( FeatureFlagKey.IS_PAGE_LAYOUT_ENABLED, @@ -116,6 +113,12 @@ const useSettingsNavigationItems = (): SettingsNavigationSection[] => { Icon: IconSettings, isHidden: !permissionMap[PermissionFlagType.WORKSPACE], }, + { + label: t`Data model`, + path: SettingsPath.Objects, + Icon: IconHierarchy2, + isHidden: !permissionMap[PermissionFlagType.DATA_MODEL], + }, { label: t`Members`, path: SettingsPath.WorkspaceMembersPage, @@ -128,6 +131,12 @@ const useSettingsNavigationItems = (): SettingsNavigationSection[] => { Icon: IconLock, isHidden: !permissionMap[PermissionFlagType.ROLES], }, + { + label: t`Domains`, + path: SettingsPath.Domains, + Icon: IconWorld, + isHidden: !permissionMap[PermissionFlagType.WORKSPACE], + }, { label: t`Billing`, path: SettingsPath.Billing, @@ -136,13 +145,7 @@ const useSettingsNavigationItems = (): SettingsNavigationSection[] => { !isBillingEnabled || !permissionMap[PermissionFlagType.WORKSPACE], }, { - label: t`Data model`, - path: SettingsPath.Objects, - Icon: IconHierarchy2, - isHidden: !permissionMap[PermissionFlagType.DATA_MODEL], - }, - { - label: t`Page Layout`, + label: t`Layout`, path: SettingsPath.PageLayoutEdition, Icon: IconLayout, isHidden: @@ -150,6 +153,12 @@ const useSettingsNavigationItems = (): SettingsNavigationSection[] => { !permissionMap[PermissionFlagType.WORKSPACE], isNew: true, }, + { + label: t`APIs & Webhooks`, + path: SettingsPath.ApiWebhooks, + Icon: IconApi, + isHidden: !permissionMap[PermissionFlagType.API_KEYS_AND_WEBHOOKS], + }, { label: t`Integrations`, path: SettingsPath.Integrations, @@ -164,33 +173,6 @@ const useSettingsNavigationItems = (): SettingsNavigationSection[] => { !isAIEnabled || !permissionMap[PermissionFlagType.WORKSPACE], isNew: true, }, - { - label: t`Security`, - path: SettingsPath.Security, - Icon: IconKey, - isAdvanced: true, - isHidden: !permissionMap[PermissionFlagType.SECURITY], - }, - ], - }, - { - label: t`Developers`, - isAdvanced: false, - items: [ - { - label: t`APIs`, - path: SettingsPath.APIs, - Icon: IconApi, - isAdvanced: false, - isHidden: !permissionMap[PermissionFlagType.API_KEYS_AND_WEBHOOKS], - }, - { - label: t`Webhooks`, - path: SettingsPath.Webhooks, - Icon: IconWebhook, - isAdvanced: false, - isHidden: !permissionMap[PermissionFlagType.API_KEYS_AND_WEBHOOKS], - }, { label: t`Functions`, path: SettingsPath.ServerlessFunctions, @@ -198,6 +180,13 @@ const useSettingsNavigationItems = (): SettingsNavigationSection[] => { isHidden: !isFunctionSettingsEnabled, isAdvanced: true, }, + { + label: t`Security`, + path: SettingsPath.Security, + Icon: IconKey, + isAdvanced: true, + isHidden: !permissionMap[PermissionFlagType.SECURITY], + }, ], }, { @@ -209,18 +198,11 @@ const useSettingsNavigationItems = (): SettingsNavigationSection[] => { Icon: IconServer, isHidden: !isAdminEnabled, }, - { - label: t`Lab`, - path: SettingsPath.Lab, - Icon: IconFlask, - isHidden: - !labPublicFeatureFlags.length || - !permissionMap[PermissionFlagType.WORKSPACE], - }, { label: t`Releases`, path: SettingsPath.Releases, Icon: IconRocket, + isHidden: !permissionMap[PermissionFlagType.WORKSPACE], }, { label: t`Logout`, diff --git a/packages/twenty-front/src/modules/types/SettingsPath.ts b/packages/twenty-front/src/modules/types/SettingsPath.ts index 07d348716d..9b3d974a94 100644 --- a/packages/twenty-front/src/modules/types/SettingsPath.ts +++ b/packages/twenty-front/src/modules/types/SettingsPath.ts @@ -22,14 +22,18 @@ export enum SettingsPath { WorkspaceMembersPage = 'members', Workspace = 'general', Domain = 'general/domain', + Domains = 'domains', + Releases = 'releases', AI = 'ai', AINewAgent = 'ai/new-agent', AIAgentDetail = 'ai/agents/:agentId', - APIs = 'apis', + ApiWebhooks = 'api-webhooks', RestPlayground = 'playground/rest/:schema', GraphQLPlayground = 'playground/graphql/:schema', - NewApiKey = 'apis/new', - ApiKeyDetail = 'apis/:apiKeyId', + NewApiKey = 'api-webhooks/apis/new', + ApiKeyDetail = 'api-webhooks/apis/:apiKeyId', + NewWebhook = 'api-webhooks/webhooks/new', + WebhookDetail = 'api-webhooks/webhooks/:webhookId', Integrations = 'integrations', IntegrationMCP = 'integrations/mcp', IntegrationDatabase = 'integrations/:databaseKey', @@ -39,15 +43,12 @@ export enum SettingsPath { Security = 'security', NewSSOIdentityProvider = 'security/sso/new', NewApprovedAccessDomain = 'security/approved-access-domain/new', - Webhooks = 'webhooks', - NewWebhook = 'webhooks/new', - WebhookDetail = 'webhooks/:webhookId', - Releases = 'releases', + AdminPanel = 'admin-panel', AdminPanelHealthStatus = 'admin-panel#health-status', AdminPanelIndicatorHealthStatus = 'admin-panel/health-status/:indicatorId', AdminPanelConfigVariableDetails = 'admin-panel/config-variables/:variableName', - Lab = 'lab', + Roles = 'roles', RoleCreate = 'roles/create', RoleDetail = 'roles/:roleId', diff --git a/packages/twenty-front/src/pages/settings/SettingsWorkspace.tsx b/packages/twenty-front/src/pages/settings/SettingsWorkspace.tsx index c1a744b360..8f576dc179 100644 --- a/packages/twenty-front/src/pages/settings/SettingsWorkspace.tsx +++ b/packages/twenty-front/src/pages/settings/SettingsWorkspace.tsx @@ -1,24 +1,17 @@ import { useLingui } from '@lingui/react/macro'; -import { useRecoilValue } from 'recoil'; -import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState'; -import { isMultiWorkspaceEnabledState } from '@/client-config/states/isMultiWorkspaceEnabledState'; -import { SettingsCard } from '@/settings/components/SettingsCard'; import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer'; import { DeleteWorkspace } from '@/settings/profile/components/DeleteWorkspace'; import { NameField } from '@/settings/workspace/components/NameField'; import { WorkspaceLogoUploader } from '@/settings/workspace/components/WorkspaceLogoUploader'; import { SettingsPath } from '@/types/SettingsPath'; import { SubMenuTopBarContainer } from '@/ui/layout/page/components/SubMenuTopBarContainer'; -import { getSettingsPath } from '~/utils/navigation/getSettingsPath'; -import { H2Title, IconWorld, Status } from 'twenty-ui/display'; +import { H2Title } from 'twenty-ui/display'; import { Section } from 'twenty-ui/layout'; -import { UndecoratedLink } from 'twenty-ui/navigation'; +import { getSettingsPath } from '~/utils/navigation/getSettingsPath'; export const SettingsWorkspace = () => { - const isMultiWorkspaceEnabled = useRecoilValue(isMultiWorkspaceEnabledState); const { t } = useLingui(); - const currentWorkspace = useRecoilValue(currentWorkspaceState); return ( { - {isMultiWorkspaceEnabled && ( - <> -
- - - } - Status={ - currentWorkspace?.customDomain && - currentWorkspace?.isCustomDomainEnabled ? ( - - ) : currentWorkspace?.customDomain ? ( - - ) : undefined - } - /> - -
- - )} +
diff --git a/packages/twenty-front/src/pages/settings/developers/api-keys/SettingsDevelopersApiKeyDetail.tsx b/packages/twenty-front/src/pages/settings/developers/api-keys/SettingsDevelopersApiKeyDetail.tsx index 026a9af4ba..abe439ee24 100644 --- a/packages/twenty-front/src/pages/settings/developers/api-keys/SettingsDevelopersApiKeyDetail.tsx +++ b/packages/twenty-front/src/pages/settings/developers/api-keys/SettingsDevelopersApiKeyDetail.tsx @@ -143,7 +143,7 @@ export const SettingsDevelopersApiKeyDetail = () => { }, }); if (redirect) { - navigate(SettingsPath.APIs); + navigate(SettingsPath.ApiWebhooks); } } catch { enqueueErrorSnackBar({ message: t`Error deleting api key.` }); @@ -242,8 +242,8 @@ export const SettingsDevelopersApiKeyDetail = () => { href: getSettingsPath(SettingsPath.Workspace), }, { - children: t`APIs`, - href: getSettingsPath(SettingsPath.APIs), + children: t`APIs & Webhooks`, + href: getSettingsPath(SettingsPath.ApiWebhooks), }, { children: apiKey?.name }, ]} diff --git a/packages/twenty-front/src/pages/settings/developers/api-keys/SettingsDevelopersApiKeysNew.tsx b/packages/twenty-front/src/pages/settings/developers/api-keys/SettingsDevelopersApiKeysNew.tsx index f2694db776..88d26654fa 100644 --- a/packages/twenty-front/src/pages/settings/developers/api-keys/SettingsDevelopersApiKeysNew.tsx +++ b/packages/twenty-front/src/pages/settings/developers/api-keys/SettingsDevelopersApiKeysNew.tsx @@ -139,8 +139,8 @@ export const SettingsDevelopersApiKeysNew = () => { href: getSettingsPath(SettingsPath.Workspace), }, { - children: t`APIs`, - href: getSettingsPath(SettingsPath.APIs), + children: t`APIs & Webhooks`, + href: getSettingsPath(SettingsPath.ApiWebhooks), }, { children: t`New Key` }, ]} @@ -148,7 +148,7 @@ export const SettingsDevelopersApiKeysNew = () => { { - navigateSettings(SettingsPath.APIs); + navigateSettings(SettingsPath.ApiWebhooks); }} onSave={handleSave} /> diff --git a/packages/twenty-front/src/pages/settings/developers/playground/SettingsGraphQLPlayground.tsx b/packages/twenty-front/src/pages/settings/developers/playground/SettingsGraphQLPlayground.tsx index 11c475930b..410f632426 100644 --- a/packages/twenty-front/src/pages/settings/developers/playground/SettingsGraphQLPlayground.tsx +++ b/packages/twenty-front/src/pages/settings/developers/playground/SettingsGraphQLPlayground.tsx @@ -19,7 +19,7 @@ export const SettingsGraphQLPlayground = () => { : PlaygroundSchemas.CORE; const handleExitFullScreen = () => { - navigateSettings(SettingsPath.APIs); + navigateSettings(SettingsPath.ApiWebhooks); }; const handleOnError = () => { @@ -35,8 +35,8 @@ export const SettingsGraphQLPlayground = () => { href: getSettingsPath(SettingsPath.Workspace), }, { - children: APIs, - href: getSettingsPath(SettingsPath.APIs), + children: APIs & Webhooks, + href: getSettingsPath(SettingsPath.ApiWebhooks), }, { children: GraphQL API Playground }, ]} diff --git a/packages/twenty-front/src/pages/settings/developers/playground/SettingsRestPlayground.tsx b/packages/twenty-front/src/pages/settings/developers/playground/SettingsRestPlayground.tsx index 54654b28b9..55a2c70eac 100644 --- a/packages/twenty-front/src/pages/settings/developers/playground/SettingsRestPlayground.tsx +++ b/packages/twenty-front/src/pages/settings/developers/playground/SettingsRestPlayground.tsx @@ -14,7 +14,7 @@ export const SettingsRestPlayground = () => { }>(); const handleExitFullScreen = () => { - navigateSettings(SettingsPath.APIs); + navigateSettings(SettingsPath.ApiWebhooks); }; return ( @@ -26,15 +26,15 @@ export const SettingsRestPlayground = () => { href: getSettingsPath(SettingsPath.Workspace), }, { - children: APIs, - href: getSettingsPath(SettingsPath.APIs), + children: APIs & Webhooks, + href: getSettingsPath(SettingsPath.ApiWebhooks), }, { children: REST }, ]} > navigateSettings(SettingsPath.APIs)} + onError={() => navigateSettings(SettingsPath.ApiWebhooks)} /> ); diff --git a/packages/twenty-front/src/pages/settings/domains/SettingsDomains.tsx b/packages/twenty-front/src/pages/settings/domains/SettingsDomains.tsx new file mode 100644 index 0000000000..96a4fdbd37 --- /dev/null +++ b/packages/twenty-front/src/pages/settings/domains/SettingsDomains.tsx @@ -0,0 +1,58 @@ +import styled from '@emotion/styled'; +import { Trans, useLingui } from '@lingui/react/macro'; + +import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer'; +import { SettingsApprovedAccessDomainsListCard } from '@/settings/security/components/approvedAccessDomains/SettingsApprovedAccessDomainsListCard'; +import { SettingsPath } from '@/types/SettingsPath'; +import { SubMenuTopBarContainer } from '@/ui/layout/page/components/SubMenuTopBarContainer'; +import { H2Title } from 'twenty-ui/display'; +import { Section } from 'twenty-ui/layout'; +import { getSettingsPath } from '~/utils/navigation/getSettingsPath'; +import { SettingsWorkspaceDomainCard } from './components'; + +const StyledMainContent = styled.div` + display: flex; + flex-direction: column; + gap: ${({ theme }) => theme.spacing(10)}; + min-height: 200px; +`; + +const StyledSection = styled(Section)` + flex-shrink: 0; +`; + +export const SettingsDomains = () => { + const { t } = useLingui(); + + return ( + Workspace, + href: getSettingsPath(SettingsPath.Workspace), + }, + { children: Domains }, + ]} + > + + + + + + + + + + + + + + ); +}; diff --git a/packages/twenty-front/src/pages/settings/domains/components/SettingsWorkspaceDomainCard.tsx b/packages/twenty-front/src/pages/settings/domains/components/SettingsWorkspaceDomainCard.tsx new file mode 100644 index 0000000000..43f1122c71 --- /dev/null +++ b/packages/twenty-front/src/pages/settings/domains/components/SettingsWorkspaceDomainCard.tsx @@ -0,0 +1,36 @@ +import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState'; +import { isMultiWorkspaceEnabledState } from '@/client-config/states/isMultiWorkspaceEnabledState'; +import { SettingsCard } from '@/settings/components/SettingsCard'; +import { SettingsPath } from '@/types/SettingsPath'; +import { useLingui } from '@lingui/react/macro'; +import { useRecoilValue } from 'recoil'; +import { IconWorld, Status } from 'twenty-ui/display'; +import { UndecoratedLink } from 'twenty-ui/navigation'; +import { getSettingsPath } from '~/utils/navigation/getSettingsPath'; + +export const SettingsWorkspaceDomainCard = () => { + const { t } = useLingui(); + const isMultiWorkspaceEnabled = useRecoilValue(isMultiWorkspaceEnabledState); + const currentWorkspace = useRecoilValue(currentWorkspaceState); + + if (!isMultiWorkspaceEnabled) { + return null; + } + + return ( + + } + Status={ + currentWorkspace?.customDomain && + currentWorkspace?.isCustomDomainEnabled ? ( + + ) : currentWorkspace?.customDomain ? ( + + ) : undefined + } + /> + + ); +}; diff --git a/packages/twenty-front/src/pages/settings/domains/components/index.ts b/packages/twenty-front/src/pages/settings/domains/components/index.ts new file mode 100644 index 0000000000..1d79856ba4 --- /dev/null +++ b/packages/twenty-front/src/pages/settings/domains/components/index.ts @@ -0,0 +1 @@ +export { SettingsWorkspaceDomainCard } from './SettingsWorkspaceDomainCard'; diff --git a/packages/twenty-front/src/pages/settings/domains/index.ts b/packages/twenty-front/src/pages/settings/domains/index.ts new file mode 100644 index 0000000000..b2acb9460c --- /dev/null +++ b/packages/twenty-front/src/pages/settings/domains/index.ts @@ -0,0 +1 @@ +export { SettingsDomains } from './SettingsDomains'; diff --git a/packages/twenty-front/src/pages/settings/lab/SettingsLab.tsx b/packages/twenty-front/src/pages/settings/lab/SettingsLab.tsx deleted file mode 100644 index e98810565f..0000000000 --- a/packages/twenty-front/src/pages/settings/lab/SettingsLab.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer'; -import { SettingsLabContent } from '@/settings/lab/components/SettingsLabContent'; -import { SettingsPath } from '@/types/SettingsPath'; -import { SubMenuTopBarContainer } from '@/ui/layout/page/components/SubMenuTopBarContainer'; -import { getSettingsPath } from '~/utils/navigation/getSettingsPath'; -import { t } from '@lingui/core/macro'; - -export const SettingsLab = () => { - return ( - - - - - - ); -}; diff --git a/packages/twenty-front/src/pages/settings/releases/SettingsReleases.tsx b/packages/twenty-front/src/pages/settings/releases/SettingsReleases.tsx new file mode 100644 index 0000000000..6ec2284630 --- /dev/null +++ b/packages/twenty-front/src/pages/settings/releases/SettingsReleases.tsx @@ -0,0 +1,46 @@ +import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer'; +import { SettingsPath } from '@/types/SettingsPath'; +import { SubMenuTopBarContainer } from '@/ui/layout/page/components/SubMenuTopBarContainer'; +import { TabList } from '@/ui/layout/tab-list/components/TabList'; +import { t } from '@lingui/core/macro'; +import { IconFlask, IconRocket } from 'twenty-ui/display'; +import { getSettingsPath } from '~/utils/navigation/getSettingsPath'; +import { SettingsReleasesTabContent } from './components'; +import { SETTINGS_RELEASES_TABS, SETTINGS_RELEASES_TABS_ID } from './constants'; + +export const SettingsReleases = () => { + const tabs = [ + { + id: SETTINGS_RELEASES_TABS.CHANGELOG, + title: t`Changelog`, + Icon: IconRocket, + }, + { + id: SETTINGS_RELEASES_TABS.LAB, + title: t`Lab`, + Icon: IconFlask, + }, + ]; + + return ( + + + + + + + ); +}; diff --git a/packages/twenty-front/src/pages/settings/Releases.tsx b/packages/twenty-front/src/pages/settings/releases/components/SettingsReleasesChangelogContent.tsx similarity index 67% rename from packages/twenty-front/src/pages/settings/Releases.tsx rename to packages/twenty-front/src/pages/settings/releases/components/SettingsReleasesChangelogContent.tsx index 5f7be7c3f3..61191bb2ab 100644 --- a/packages/twenty-front/src/pages/settings/Releases.tsx +++ b/packages/twenty-front/src/pages/settings/releases/components/SettingsReleasesChangelogContent.tsx @@ -1,5 +1,4 @@ import styled from '@emotion/styled'; -import { Trans, useLingui } from '@lingui/react/macro'; import React, { useEffect, useState } from 'react'; import rehypeStringify from 'rehype-stringify'; import remarkParse from 'remark-parse'; @@ -7,11 +6,7 @@ import remarkRehype from 'remark-rehype'; import { type PluggableList, unified } from 'unified'; import { visit } from 'unist-util-visit'; -import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer'; -import { SettingsPath } from '@/types/SettingsPath'; -import { SubMenuTopBarContainer } from '@/ui/layout/page/components/SubMenuTopBarContainer'; import { ScrollWrapper } from '@/ui/utilities/scroll/components/ScrollWrapper'; -import { getSettingsPath } from '~/utils/navigation/getSettingsPath'; type ReleaseNote = { slug: string; @@ -79,8 +74,7 @@ const StyledReleaseDate = styled.span` color: ${({ theme }) => theme.font.color.tertiary}; `; -export const Releases = () => { - const { t } = useLingui(); +export const SettingsReleasesChangelogContent = () => { const [releases, setReleases] = useState([]); useEffect(() => { @@ -105,29 +99,16 @@ export const Releases = () => { }, []); return ( - Other, - href: getSettingsPath(SettingsPath.Workspace), - }, - { children: Releases }, - ]} - > - - - - {releases.map((release) => ( - - {release.release} - {release.date} -
-
- ))} -
-
-
-
+ + + {releases.map((release) => ( + + {release.release} + {release.date} +
+
+ ))} +
+
); }; diff --git a/packages/twenty-front/src/pages/settings/releases/components/SettingsReleasesTabContent.tsx b/packages/twenty-front/src/pages/settings/releases/components/SettingsReleasesTabContent.tsx new file mode 100644 index 0000000000..7ce6e1fc8c --- /dev/null +++ b/packages/twenty-front/src/pages/settings/releases/components/SettingsReleasesTabContent.tsx @@ -0,0 +1,24 @@ +import { SettingsLabContent } from '@/settings/lab/components/SettingsLabContent'; +import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTabIdComponentState'; +import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; +import { + SETTINGS_RELEASES_TABS, + SETTINGS_RELEASES_TABS_ID, +} from '../constants'; +import { SettingsReleasesChangelogContent } from './SettingsReleasesChangelogContent'; + +export const SettingsReleasesTabContent = () => { + const activeTabId = useRecoilComponentValue( + activeTabIdComponentState, + SETTINGS_RELEASES_TABS_ID, + ); + + switch (activeTabId) { + case SETTINGS_RELEASES_TABS.CHANGELOG: + return ; + case SETTINGS_RELEASES_TABS.LAB: + return ; + default: + return ; + } +}; diff --git a/packages/twenty-front/src/pages/settings/releases/components/index.ts b/packages/twenty-front/src/pages/settings/releases/components/index.ts new file mode 100644 index 0000000000..c5644b31ec --- /dev/null +++ b/packages/twenty-front/src/pages/settings/releases/components/index.ts @@ -0,0 +1,2 @@ +export { SettingsReleasesChangelogContent } from './SettingsReleasesChangelogContent'; +export { SettingsReleasesTabContent } from './SettingsReleasesTabContent'; diff --git a/packages/twenty-front/src/pages/settings/releases/constants/SettingsReleasesTabs.ts b/packages/twenty-front/src/pages/settings/releases/constants/SettingsReleasesTabs.ts new file mode 100644 index 0000000000..10a877fa62 --- /dev/null +++ b/packages/twenty-front/src/pages/settings/releases/constants/SettingsReleasesTabs.ts @@ -0,0 +1,4 @@ +export const SETTINGS_RELEASES_TABS = { + CHANGELOG: 'changelog', + LAB: 'lab', +}; diff --git a/packages/twenty-front/src/pages/settings/releases/constants/SettingsReleasesTabsId.ts b/packages/twenty-front/src/pages/settings/releases/constants/SettingsReleasesTabsId.ts new file mode 100644 index 0000000000..2cd50b2c81 --- /dev/null +++ b/packages/twenty-front/src/pages/settings/releases/constants/SettingsReleasesTabsId.ts @@ -0,0 +1 @@ +export const SETTINGS_RELEASES_TABS_ID = 'settings-releases-tabs'; diff --git a/packages/twenty-front/src/pages/settings/releases/constants/index.ts b/packages/twenty-front/src/pages/settings/releases/constants/index.ts new file mode 100644 index 0000000000..e03e3fecec --- /dev/null +++ b/packages/twenty-front/src/pages/settings/releases/constants/index.ts @@ -0,0 +1,2 @@ +export { SETTINGS_RELEASES_TABS } from './SettingsReleasesTabs'; +export { SETTINGS_RELEASES_TABS_ID } from './SettingsReleasesTabsId'; diff --git a/packages/twenty-front/src/pages/settings/releases/index.ts b/packages/twenty-front/src/pages/settings/releases/index.ts new file mode 100644 index 0000000000..75f0132c83 --- /dev/null +++ b/packages/twenty-front/src/pages/settings/releases/index.ts @@ -0,0 +1 @@ +export { SettingsReleases } from './SettingsReleases'; diff --git a/packages/twenty-front/src/pages/settings/security/SettingsSecurity.tsx b/packages/twenty-front/src/pages/settings/security/SettingsSecurity.tsx index c193d015ae..5bd5b19f1d 100644 --- a/packages/twenty-front/src/pages/settings/security/SettingsSecurity.tsx +++ b/packages/twenty-front/src/pages/settings/security/SettingsSecurity.tsx @@ -5,7 +5,7 @@ import { isMultiWorkspaceEnabledState } from '@/client-config/states/isMultiWork import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer'; import { SettingsSSOIdentitiesProvidersListCard } from '@/settings/security/components/SSO/SettingsSSOIdentitiesProvidersListCard'; import { SettingsSecurityAuthProvidersOptionsList } from '@/settings/security/components/SettingsSecurityAuthProvidersOptionsList'; -import { SettingsApprovedAccessDomainsListCard } from '@/settings/security/components/approvedAccessDomains/SettingsApprovedAccessDomainsListCard'; + import { ToggleImpersonate } from '@/settings/workspace/components/ToggleImpersonate'; import { SettingsPath } from '@/types/SettingsPath'; import { SubMenuTopBarContainer } from '@/ui/layout/page/components/SubMenuTopBarContainer'; @@ -63,13 +63,7 @@ export const SettingsSecurity = () => { /> - - - - +
{ enqueueSuccessSnackBar({ message: t`Please check your email for a verification link.`, }); - navigate(SettingsPath.Security); + navigate(SettingsPath.Domains); }, onError: (error) => { enqueueErrorSnackBar({ @@ -86,7 +86,7 @@ export const SettingsSecurityApprovedAccessDomain = () => { title="New Approved Access Domain" actionButton={ navigate(SettingsPath.Security)} + onCancel={() => navigate(SettingsPath.Domains)} isSaveDisabled={form.formState.isSubmitting} /> } @@ -96,8 +96,8 @@ export const SettingsSecurityApprovedAccessDomain = () => { href: getSettingsPath(SettingsPath.Workspace), }, { - children: Security, - href: getSettingsPath(SettingsPath.Security), + children: Domains, + href: getSettingsPath(SettingsPath.Domains), }, { children: New Approved Access Domain }, ]} diff --git a/packages/twenty-front/src/pages/settings/workspace/SettingsApiWebhooks.tsx b/packages/twenty-front/src/pages/settings/workspace/SettingsApiWebhooks.tsx new file mode 100644 index 0000000000..376c9fee2d --- /dev/null +++ b/packages/twenty-front/src/pages/settings/workspace/SettingsApiWebhooks.tsx @@ -0,0 +1,113 @@ +import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer'; +import { SettingsApiKeysTable } from '@/settings/developers/components/SettingsApiKeysTable'; +import { SettingsWebhooksTable } from '@/settings/developers/components/SettingsWebhooksTable'; +import { PlaygroundSetupForm } from '@/settings/playground/components/PlaygroundSetupForm'; +import { StyledSettingsApiPlaygroundCoverImage } from '@/settings/playground/components/SettingsPlaygroundCoverImage'; +import { SettingsPath } from '@/types/SettingsPath'; +import { SubMenuTopBarContainer } from '@/ui/layout/page/components/SubMenuTopBarContainer'; +import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile'; +import styled from '@emotion/styled'; +import { Trans, useLingui } from '@lingui/react/macro'; +import { H2Title, IconPlus } from 'twenty-ui/display'; +import { Button } from 'twenty-ui/input'; +import { Section } from 'twenty-ui/layout'; +import { MOBILE_VIEWPORT } from 'twenty-ui/theme'; +import { getSettingsPath } from '~/utils/navigation/getSettingsPath'; + +const StyledButtonContainer = styled.div` + display: flex; + justify-content: flex-end; + padding-top: ${({ theme }) => theme.spacing(2)}; + @media (max-width: ${MOBILE_VIEWPORT}px) { + padding-top: ${({ theme }) => theme.spacing(5)}; + } +`; + +const StyledMainContent = styled.div` + display: flex; + flex-direction: column; + gap: ${({ theme }) => theme.spacing(10)}; + min-height: 200px; +`; + +const StyledSection = styled(Section)` + flex-shrink: 0; +`; + +const StyledContainer = styled.div<{ isMobile?: boolean }>` + display: flex; + flex-direction: column; + overflow: ${({ isMobile }) => (isMobile ? 'hidden' : 'visible')}; + gap: ${({ theme }) => theme.spacing(2)}; +`; + +export const SettingsApiWebhooks = () => { + const isMobile = useIsMobile(); + const { t } = useLingui(); + + return ( + Workspace, + href: getSettingsPath(SettingsPath.Workspace), + }, + { children: APIs & Webhooks }, + ]} + > + + + + + + + + + + + + + + + +