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:
@ -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
|
||||||
|
|||||||
@ -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) {
|
||||||
|
|||||||
@ -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,
|
||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -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>
|
||||||
|
|||||||
@ -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,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|||||||
@ -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',
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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,
|
||||||
},
|
},
|
||||||
|
|||||||
@ -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,
|
||||||
},
|
},
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
@ -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'));
|
||||||
|
|
||||||
|
|||||||
@ -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,
|
||||||
},
|
},
|
||||||
|
|||||||
@ -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: {
|
||||||
|
|||||||
@ -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>
|
||||||
|
|||||||
@ -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>
|
||||||
|
|||||||
@ -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,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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>
|
||||||
|
|||||||
@ -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(),
|
||||||
},
|
},
|
||||||
|
|||||||
@ -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');
|
||||||
|
|||||||
@ -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()}`,
|
||||||
);
|
);
|
||||||
|
|||||||
@ -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 > APIs
|
||||||
href="https://app.twenty.com/settings/developers"
|
|
||||||
>
|
|
||||||
Settings > Developers
|
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
{isLoading && (
|
{isLoading && (
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
Reference in New Issue
Block a user