Fix settings navigation active state for sub-pages (#12318)

Changes the default behavior for settings navigation items to stay
active when navigating to sub-pages.

**Problem:**
- Navigation items like "Data Model" and "Webhooks" were not staying
highlighted when navigating to detail pages
- This was because `matchSubPages` defaulted to requiring exact path
matches

**Solution:**
- Updated logic to make sub-page matching the default behavior (`end:
item.matchSubPages === false`)
- Only "Accounts" explicitly sets `matchSubPages: false` for its custom
sub-item navigation
- Removed redundant `matchSubPages: true` declarations throughout the
codebase

**URL Changes:** -- checked with @Bonapara 
- `/settings/workspace` → `/settings/general`
- `/settings/workspace-members` → `/settings/members`
- `/settings/api-keys` → `/settings/apis`
- `/settings/developers/webhooks` → `/settings/webhooks`

before: 


https://github.com/user-attachments/assets/56b94a49-9c31-4bb5-9875-ec24f4bc4d1e

after:


https://github.com/user-attachments/assets/38742599-c045-44d1-8020-56f3eacca779

---------

Co-authored-by: Félix Malfait <felix.malfait@gmail.com>
This commit is contained in:
nitin
2025-05-30 11:49:20 +05:30
committed by GitHub
parent dc0401edb5
commit da00dee8a1
22 changed files with 57 additions and 51 deletions

View File

@ -431,15 +431,15 @@ export const SettingsRoutes = ({
element={<SettingsRestPlayground />} element={<SettingsRestPlayground />}
/> />
<Route <Route
path={SettingsPath.DevelopersNewApiKey} path={SettingsPath.NewApiKey}
element={<SettingsDevelopersApiKeysNew />} element={<SettingsDevelopersApiKeysNew />}
/> />
<Route <Route
path={SettingsPath.DevelopersApiKeyDetail} path={SettingsPath.ApiKeyDetail}
element={<SettingsDevelopersApiKeyDetail />} element={<SettingsDevelopersApiKeyDetail />}
/> />
<Route <Route
path={SettingsPath.DevelopersNewWebhookDetail} path={SettingsPath.WebhookDetail}
element={<SettingsDevelopersWebhooksDetail />} element={<SettingsDevelopersWebhooksDetail />}
/> />
<Route <Route

View File

@ -4,8 +4,8 @@ import { AdvancedSettingsWrapper } from '@/settings/components/AdvancedSettingsW
import { SettingsNavigationItem } from '@/settings/hooks/useSettingsNavigationItems'; import { SettingsNavigationItem } from '@/settings/hooks/useSettingsNavigationItems';
import { NavigationDrawerItem } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerItem'; import { NavigationDrawerItem } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerItem';
import { NavigationDrawerSubItemState } from '@/ui/navigation/navigation-drawer/types/NavigationDrawerSubItemState'; import { NavigationDrawerSubItemState } from '@/ui/navigation/navigation-drawer/types/NavigationDrawerSubItemState';
import { getSettingsPath } from '~/utils/navigation/getSettingsPath';
import { isDefined } from 'twenty-shared/utils'; import { isDefined } from 'twenty-shared/utils';
import { getSettingsPath } from '~/utils/navigation/getSettingsPath';
type SettingsNavigationDrawerItemProps = { type SettingsNavigationDrawerItemProps = {
item: SettingsNavigationItem; item: SettingsNavigationItem;
@ -20,7 +20,7 @@ export const SettingsNavigationDrawerItem = ({
const pathName = useResolvedPath(href).pathname; const pathName = useResolvedPath(href).pathname;
const isActive = !!useMatch({ const isActive = !!useMatch({
path: pathName, path: pathName,
end: !item.matchSubPages, end: item.matchSubPages === false,
}); });
if (isDefined(item.isHidden) && item.isHidden) { if (isDefined(item.isHidden) && item.isHidden) {

View File

@ -11,8 +11,8 @@ import { TableHeader } from '@/ui/layout/table/components/TableHeader';
import { TableRow } from '@/ui/layout/table/components/TableRow'; import { TableRow } from '@/ui/layout/table/components/TableRow';
import styled from '@emotion/styled'; import styled from '@emotion/styled';
import { Trans } from '@lingui/react/macro'; import { Trans } from '@lingui/react/macro';
import { getSettingsPath } from '~/utils/navigation/getSettingsPath';
import { MOBILE_VIEWPORT } from 'twenty-ui/theme'; import { MOBILE_VIEWPORT } from 'twenty-ui/theme';
import { getSettingsPath } from '~/utils/navigation/getSettingsPath';
const StyledTableBody = styled(TableBody)` const StyledTableBody = styled(TableBody)`
border-bottom: 1px solid ${({ theme }) => theme.border.color.light}; border-bottom: 1px solid ${({ theme }) => theme.border.color.light};
@ -55,7 +55,7 @@ export const SettingsApiKeysTable = () => {
<SettingsApiKeysFieldItemTableRow <SettingsApiKeysFieldItemTableRow
key={fieldItem.id} key={fieldItem.id}
fieldItem={fieldItem as ApiFieldItem} fieldItem={fieldItem as ApiFieldItem}
to={getSettingsPath(SettingsPath.DevelopersApiKeyDetail, { to={getSettingsPath(SettingsPath.ApiKeyDetail, {
apiKeyId: fieldItem.id, apiKeyId: fieldItem.id,
})} })}
/> />

View File

@ -4,10 +4,12 @@ import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSi
import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords'; import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords';
import { SettingsDevelopersWebhookTableRow } from '@/settings/developers/components/SettingsDevelopersWebhookTableRow'; import { SettingsDevelopersWebhookTableRow } from '@/settings/developers/components/SettingsDevelopersWebhookTableRow';
import { Webhook } from '@/settings/developers/types/webhook/Webhook'; import { Webhook } from '@/settings/developers/types/webhook/Webhook';
import { SettingsPath } from '@/types/SettingsPath';
import { Table } from '@/ui/layout/table/components/Table'; import { Table } from '@/ui/layout/table/components/Table';
import { TableBody } from '@/ui/layout/table/components/TableBody'; import { TableBody } from '@/ui/layout/table/components/TableBody';
import { TableHeader } from '@/ui/layout/table/components/TableHeader'; import { TableHeader } from '@/ui/layout/table/components/TableHeader';
import { TableRow } from '@/ui/layout/table/components/TableRow'; import { TableRow } from '@/ui/layout/table/components/TableRow';
import { getSettingsPath } from '~/utils/navigation/getSettingsPath';
const StyledTableBody = styled(TableBody)` const StyledTableBody = styled(TableBody)`
border-bottom: 1px solid ${({ theme }) => theme.border.color.light}; border-bottom: 1px solid ${({ theme }) => theme.border.color.light};
@ -36,7 +38,9 @@ export const SettingsWebhooksTable = () => {
<SettingsDevelopersWebhookTableRow <SettingsDevelopersWebhookTableRow
key={webhookFieldItem.id} key={webhookFieldItem.id}
fieldItem={webhookFieldItem} fieldItem={webhookFieldItem}
to={`/settings/developers/webhooks/${webhookFieldItem.id}`} to={getSettingsPath(SettingsPath.WebhookDetail, {
webhookId: webhookFieldItem.id,
})}
/> />
))} ))}
</StyledTableBody> </StyledTableBody>

View File

@ -8,7 +8,6 @@ import { useSettingsPermissionMap } from '@/settings/roles/hooks/useSettingsPerm
import { NavigationDrawerItemIndentationLevel } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerItem'; import { NavigationDrawerItemIndentationLevel } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerItem';
import { t } from '@lingui/core/macro'; import { t } from '@lingui/core/macro';
import { useRecoilValue } from 'recoil'; import { useRecoilValue } from 'recoil';
import { SettingPermissionType } from '~/generated/graphql';
import { import {
IconApi, IconApi,
IconApps, IconApps,
@ -31,6 +30,7 @@ import {
IconUsers, IconUsers,
IconWebhook, IconWebhook,
} from 'twenty-ui/display'; } from 'twenty-ui/display';
import { SettingPermissionType } from '~/generated/graphql';
export type SettingsNavigationSection = { export type SettingsNavigationSection = {
label: string; label: string;
@ -203,6 +203,7 @@ const useSettingsNavigationItems = (): SettingsNavigationSection[] => {
label: t`Logout`, label: t`Logout`,
onClick: signOut, onClick: signOut,
Icon: IconDoorEnter, Icon: IconDoorEnter,
matchSubPages: false,
}, },
], ],
}, },

View File

@ -16,14 +16,14 @@ export enum SettingsPath {
ServerlessFunctions = 'functions', ServerlessFunctions = 'functions',
NewServerlessFunction = 'functions/new', NewServerlessFunction = 'functions/new',
ServerlessFunctionDetail = 'functions/:serverlessFunctionId', ServerlessFunctionDetail = 'functions/:serverlessFunctionId',
WorkspaceMembersPage = 'workspace-members', WorkspaceMembersPage = 'members',
Workspace = 'workspace', Workspace = 'general',
Domain = 'domain', Domain = 'general/domain',
APIs = 'api-keys', APIs = 'apis',
RestPlayground = 'playground/rest/:schema', RestPlayground = 'playground/rest/:schema',
GraphQLPlayground = 'playground/graphql/:schema', GraphQLPlayground = 'playground/graphql/:schema',
DevelopersNewApiKey = 'api-keys/new', NewApiKey = 'apis/new',
DevelopersApiKeyDetail = 'api-keys/:apiKeyId', ApiKeyDetail = 'apis/:apiKeyId',
Integrations = 'integrations', Integrations = 'integrations',
IntegrationDatabase = 'integrations/:databaseKey', IntegrationDatabase = 'integrations/:databaseKey',
IntegrationDatabaseConnection = 'integrations/:databaseKey/:connectionId', IntegrationDatabaseConnection = 'integrations/:databaseKey/:connectionId',
@ -33,8 +33,7 @@ export enum SettingsPath {
NewSSOIdentityProvider = 'security/sso/new', NewSSOIdentityProvider = 'security/sso/new',
NewApprovedAccessDomain = 'security/approved-access-domain/new', NewApprovedAccessDomain = 'security/approved-access-domain/new',
Webhooks = 'webhooks', Webhooks = 'webhooks',
DevelopersNewWebhook = 'developers/webhooks/new', WebhookDetail = 'webhooks/:webhookId',
DevelopersNewWebhookDetail = 'developers/webhooks/:webhookId',
Releases = 'releases', Releases = 'releases',
AdminPanel = 'admin-panel', AdminPanel = 'admin-panel',
AdminPanelHealthStatus = 'admin-panel#health-status', AdminPanelHealthStatus = 'admin-panel#health-status',

View File

@ -208,7 +208,7 @@ export const SettingsWorkspaceMembers = () => {
<Section> <Section>
<H2Title <H2Title
title={t`Manage Members`} title={t`Manage Members`}
description={t`Manage the members of your space here`} description={t`Manage the members of your workspace here`}
/> />
<StyledSearchContainer> <StyledSearchContainer>
<StyledSearchInput <StyledSearchInput

View File

@ -12,7 +12,7 @@ const meta: Meta<PageDecoratorArgs> = {
title: 'Pages/Settings/SettingsWorkspace', title: 'Pages/Settings/SettingsWorkspace',
component: SettingsWorkspace, component: SettingsWorkspace,
decorators: [PageDecorator], decorators: [PageDecorator],
args: { routePath: '/settings/workspace' }, args: { routePath: '/settings/general' },
parameters: { parameters: {
msw: graphqlMocks, msw: graphqlMocks,
}, },

View File

@ -15,7 +15,7 @@ const meta: Meta<PageDecoratorArgs> = {
title: 'Pages/Settings/SettingsWorkspaceMembers', title: 'Pages/Settings/SettingsWorkspaceMembers',
component: SettingsWorkspaceMembers, component: SettingsWorkspaceMembers,
decorators: [PageDecorator], decorators: [PageDecorator],
args: { routePath: '/settings/workspace-members' }, args: { routePath: '/settings/members' },
parameters: { parameters: {
msw: graphqlMocks, msw: graphqlMocks,
}, },

View File

@ -69,7 +69,7 @@ export const SettingsObjectNewFieldSelect = () => {
<SubMenuTopBarContainer <SubMenuTopBarContainer
title={t`1. Select a field type`} title={t`1. Select a field type`}
links={[ links={[
{ children: t`Workspace`, href: '/settings/workspace' }, { children: t`Workspace`, href: '/settings/general' },
{ children: t`Objects`, href: '/settings/objects' }, { children: t`Objects`, href: '/settings/objects' },
{ {
children: activeObjectMetadataItem.labelPlural, children: activeObjectMetadataItem.labelPlural,

View File

@ -15,7 +15,7 @@ const meta: Meta<PageDecoratorArgs> = {
component: SettingsDevelopersApiKeyDetail, component: SettingsDevelopersApiKeyDetail,
decorators: [PageDecorator], decorators: [PageDecorator],
args: { args: {
routePath: '/settings/developers/api-keys/:apiKeyId', routePath: '/settings/apis/:apiKeyId',
routeParams: { routeParams: {
':apiKeyId': 'f7c6d736-8fcd-4e9c-ab99-28f6a9031570', ':apiKeyId': 'f7c6d736-8fcd-4e9c-ab99-28f6a9031570',
}, },
@ -50,14 +50,14 @@ export type Story = StoryObj<typeof SettingsDevelopersApiKeyDetail>;
export const Default: Story = { export const Default: Story = {
play: async ({ canvasElement }) => { play: async ({ canvasElement }) => {
const canvas = within(canvasElement); const canvas = within(canvasElement);
await canvas.findByText('sfsfdsf API Key', undefined, { timeout: 3000 }); await canvas.findByText('sfsfdsf', undefined, { timeout: 3000 });
}, },
}; };
export const RegenerateApiKey: Story = { export const RegenerateApiKey: Story = {
play: async ({ step }) => { play: async ({ step }) => {
const canvas = within(document.body); const canvas = within(document.body);
await canvas.findByText('sfsfdsf API Key', undefined, { timeout: 3000 }); await canvas.findByText('sfsfdsf', undefined, { timeout: 3000 });
await userEvent.click(await canvas.findByText('Regenerate Key')); await userEvent.click(await canvas.findByText('Regenerate Key'));
@ -85,7 +85,7 @@ export const RegenerateApiKey: Story = {
export const DeleteApiKey: Story = { export const DeleteApiKey: Story = {
play: async ({ canvasElement, step }) => { play: async ({ canvasElement, step }) => {
const canvas = within(canvasElement); const canvas = within(canvasElement);
await canvas.findByText('sfsfdsf API Key', undefined, { timeout: 3000 }); await canvas.findByText('sfsfdsf', undefined, { timeout: 3000 });
await userEvent.click(await canvas.findByText('Delete')); await userEvent.click(await canvas.findByText('Delete'));

View File

@ -14,7 +14,7 @@ const meta: Meta<PageDecoratorArgs> = {
title: 'Pages/Settings/ApiKeys/SettingsDevelopersApiKeysNew', title: 'Pages/Settings/ApiKeys/SettingsDevelopersApiKeysNew',
component: SettingsDevelopersApiKeysNew, component: SettingsDevelopersApiKeysNew,
decorators: [PageDecorator], decorators: [PageDecorator],
args: { routePath: getSettingsPath(SettingsPath.DevelopersNewApiKey) }, args: { routePath: getSettingsPath(SettingsPath.NewApiKey) },
parameters: { parameters: {
msw: graphqlMocks, msw: graphqlMocks,
}, },

View File

@ -14,7 +14,7 @@ const meta: Meta<PageDecoratorArgs> = {
component: SettingsDevelopersWebhooksDetail, component: SettingsDevelopersWebhooksDetail,
decorators: [PageDecorator], decorators: [PageDecorator],
args: { args: {
routePath: '/settings/developers/webhooks/:webhookId', routePath: '/settings/webhooks/:webhookId',
routeParams: { ':webhookId': '1234' }, routeParams: { ':webhookId': '1234' },
}, },
parameters: { parameters: {

View File

@ -66,7 +66,7 @@ export const SettingsApiKeys = () => {
title={t`Create API key`} title={t`Create API key`}
size="small" size="small"
variant="secondary" variant="secondary"
to={getSettingsPath(SettingsPath.DevelopersNewApiKey)} to={getSettingsPath(SettingsPath.NewApiKey)}
/> />
</StyledButtonContainer> </StyledButtonContainer>
</Section> </Section>

View File

@ -143,7 +143,7 @@ export const SettingsDevelopersApiKeyDetail = () => {
if (isNonEmptyString(apiKey?.token)) { if (isNonEmptyString(apiKey?.token)) {
setApiKeyTokenCallback(apiKey.id, apiKey.token); setApiKeyTokenCallback(apiKey.id, apiKey.token);
navigate(SettingsPath.DevelopersApiKeyDetail, { navigate(SettingsPath.ApiKeyDetail, {
apiKeyId: apiKey.id, apiKeyId: apiKey.id,
}); });
} }
@ -173,7 +173,7 @@ export const SettingsDevelopersApiKeyDetail = () => {
children: t`APIs`, children: t`APIs`,
href: getSettingsPath(SettingsPath.APIs), href: getSettingsPath(SettingsPath.APIs),
}, },
{ children: t`${apiKeyName} API Key` }, { children: t`${apiKeyName}` },
]} ]}
> >
<SettingsPageContainer> <SettingsPageContainer>

View File

@ -15,12 +15,12 @@ import { SubMenuTopBarContainer } from '@/ui/layout/page/components/SubMenuTopBa
import { useLingui } from '@lingui/react/macro'; import { useLingui } from '@lingui/react/macro';
import { useRecoilCallback } from 'recoil'; import { useRecoilCallback } from 'recoil';
import { Key } from 'ts-key-enum'; import { Key } from 'ts-key-enum';
import { useGenerateApiKeyTokenMutation } from '~/generated/graphql';
import { useNavigateSettings } from '~/hooks/useNavigateSettings';
import { getSettingsPath } from '~/utils/navigation/getSettingsPath';
import { isDefined } from 'twenty-shared/utils'; import { isDefined } from 'twenty-shared/utils';
import { H2Title } from 'twenty-ui/display'; import { H2Title } from 'twenty-ui/display';
import { Section } from 'twenty-ui/layout'; import { Section } from 'twenty-ui/layout';
import { useGenerateApiKeyTokenMutation } from '~/generated/graphql';
import { useNavigateSettings } from '~/hooks/useNavigateSettings';
import { getSettingsPath } from '~/utils/navigation/getSettingsPath';
export const SettingsDevelopersApiKeysNew = () => { export const SettingsDevelopersApiKeysNew = () => {
const { t } = useLingui(); const { t } = useLingui();
@ -72,7 +72,7 @@ export const SettingsDevelopersApiKeysNew = () => {
newApiKey.id, newApiKey.id,
tokenData.data.generateApiKeyToken.token, tokenData.data.generateApiKeyToken.token,
); );
navigateSettings(SettingsPath.DevelopersApiKeyDetail, { navigateSettings(SettingsPath.ApiKeyDetail, {
apiKeyId: newApiKey.id, apiKeyId: newApiKey.id,
}); });
} }

View File

@ -5,6 +5,7 @@ import { useParams, useSearchParams } from 'react-router-dom';
import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems'; import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems';
import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer'; import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer';
import { SettingsSkeletonLoader } from '@/settings/components/SettingsSkeletonLoader';
import { useWebhookUpdateForm } from '@/settings/developers/hooks/useWebhookUpdateForm'; import { useWebhookUpdateForm } from '@/settings/developers/hooks/useWebhookUpdateForm';
import { SettingsPath } from '@/types/SettingsPath'; import { SettingsPath } from '@/types/SettingsPath';
import { Select } from '@/ui/input/components/Select'; import { Select } from '@/ui/input/components/Select';
@ -101,7 +102,7 @@ export const SettingsDevelopersWebhooksDetail = () => {
]; ];
if (loading || !formData) { if (loading || !formData) {
return <></>; return <SettingsSkeletonLoader />;
} }
const confirmationText = t`yes`; const confirmationText = t`yes`;
@ -115,7 +116,11 @@ export const SettingsDevelopersWebhooksDetail = () => {
children: t`Workspace`, children: t`Workspace`,
href: getSettingsPath(SettingsPath.Workspace), href: getSettingsPath(SettingsPath.Workspace),
}, },
{ children: t`Webhook` }, {
children: t`Webhooks`,
href: getSettingsPath(SettingsPath.Webhooks),
},
{ children: title },
]} ]}
> >
<SettingsPageContainer> <SettingsPageContainer>

View File

@ -5,12 +5,12 @@ import { SubMenuTopBarContainer } from '@/ui/layout/page/components/SubMenuTopBa
import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile'; import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile';
import styled from '@emotion/styled'; import styled from '@emotion/styled';
import { Trans, useLingui } from '@lingui/react/macro'; 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 { v4 } from 'uuid'; import { v4 } from 'uuid';
import { getSettingsPath } from '~/utils/navigation/getSettingsPath'; import { getSettingsPath } from '~/utils/navigation/getSettingsPath';
import { Button } from 'twenty-ui/input';
import { H2Title, IconPlus } from 'twenty-ui/display';
import { MOBILE_VIEWPORT } from 'twenty-ui/theme';
import { Section } from 'twenty-ui/layout';
const StyledButtonContainer = styled.div` const StyledButtonContainer = styled.div`
display: flex; display: flex;
@ -58,7 +58,7 @@ export const SettingsWebhooks = () => {
size="small" size="small"
variant="secondary" variant="secondary"
to={getSettingsPath( to={getSettingsPath(
SettingsPath.DevelopersNewWebhookDetail, SettingsPath.WebhookDetail,
{ {
webhookId: v4(), webhookId: v4(),
}, },

View File

@ -36,10 +36,10 @@ describe('title-utils', () => {
expect(getPageTitleFromPath('/settings/accounts/emails/:accountUuid')).toBe( expect(getPageTitleFromPath('/settings/accounts/emails/:accountUuid')).toBe(
SettingsPageTitles.Accounts, SettingsPageTitles.Accounts,
); );
expect(getPageTitleFromPath('/settings/workspace-members')).toBe( expect(getPageTitleFromPath('/settings/members')).toBe(
SettingsPageTitles.Members, SettingsPageTitles.Members,
); );
expect(getPageTitleFromPath('/settings/workspace')).toBe( expect(getPageTitleFromPath('/settings/general')).toBe(
SettingsPageTitles.General, SettingsPageTitles.General,
); );
expect(getPageTitleFromPath('/')).toBe('Twenty'); expect(getPageTitleFromPath('/')).toBe('Twenty');

View File

@ -3,6 +3,7 @@ import { BadRequestException, Injectable } from '@nestjs/common';
import { Request } from 'express'; import { Request } from 'express';
import { CreateManyQueryFactory } from 'src/engine/api/rest/core/query-builder/factories/create-many-query.factory'; import { CreateManyQueryFactory } from 'src/engine/api/rest/core/query-builder/factories/create-many-query.factory';
import { CreateVariablesFactory } from 'src/engine/api/rest/core/query-builder/factories/create-variables.factory';
import { FindDuplicatesQueryFactory } from 'src/engine/api/rest/core/query-builder/factories/find-duplicates-query.factory'; import { FindDuplicatesQueryFactory } from 'src/engine/api/rest/core/query-builder/factories/find-duplicates-query.factory';
import { FindDuplicatesVariablesFactory } from 'src/engine/api/rest/core/query-builder/factories/find-duplicates-variables.factory'; import { FindDuplicatesVariablesFactory } from 'src/engine/api/rest/core/query-builder/factories/find-duplicates-variables.factory';
import { computeDepth } from 'src/engine/api/rest/core/query-builder/utils/compute-depth.utils'; import { computeDepth } from 'src/engine/api/rest/core/query-builder/utils/compute-depth.utils';
@ -17,7 +18,6 @@ import { getObjectMetadataMapItemByNamePlural } from 'src/engine/metadata-module
import { getObjectMetadataMapItemByNameSingular } from 'src/engine/metadata-modules/utils/get-object-metadata-map-item-by-name-singular.util'; import { getObjectMetadataMapItemByNameSingular } from 'src/engine/metadata-modules/utils/get-object-metadata-map-item-by-name-singular.util';
import { WorkspaceMetadataCacheService } from 'src/engine/metadata-modules/workspace-metadata-cache/services/workspace-metadata-cache.service'; import { WorkspaceMetadataCacheService } from 'src/engine/metadata-modules/workspace-metadata-cache/services/workspace-metadata-cache.service';
import { WorkspaceCacheStorageService } from 'src/engine/workspace-cache-storage/workspace-cache-storage.service'; import { WorkspaceCacheStorageService } from 'src/engine/workspace-cache-storage/workspace-cache-storage.service';
import { CreateVariablesFactory } from 'src/engine/api/rest/core/query-builder/factories/create-variables.factory';
@Injectable() @Injectable()
export class CoreQueryBuilderFactory { export class CoreQueryBuilderFactory {
@ -63,7 +63,7 @@ export class CoreQueryBuilderFactory {
`No object was found for the workspace associated with this API key. You may generate a new one here ${this.domainManagerService `No object was found for the workspace associated with this API key. You may generate a new one here ${this.domainManagerService
.buildWorkspaceURL({ .buildWorkspaceURL({
workspace, workspace,
pathname: '/settings/developers', pathname: '/settings/apis',
}) })
.toString()}`, .toString()}`,
); );

View File

@ -61,11 +61,8 @@ const Playground = ({
A token is required as APIs are dynamically generated for each A token is required as APIs are dynamically generated for each
workspace based on their unique metadata. <br /> Generate your token workspace based on their unique metadata. <br /> Generate your token
under{' '} under{' '}
<a <a className="link" href="https://app.twenty.com/settings/apis">
className="link" Settings &gt; APIs
href="https://app.twenty.com/settings/developers"
>
Settings &gt; Developers
</a> </a>
</div> </div>
{isLoading && ( {isLoading && (

View File

@ -21,7 +21,7 @@ export default {
label: 'Api Key', label: 'Api Key',
type: 'string', type: 'string',
helpText: helpText:
'Create an API key in [your twenty workspace](https://app.twenty.com/settings/developers)', 'Create an API key in [your twenty workspace](https://app.twenty.com/settings/apis)',
}, },
{ {
computed: false, computed: false,