mirror of
https://github.com/Unleash/unleash.git
synced 2025-02-04 00:18:01 +01:00
fix: decouple forms (#3162)
This PR decouples the forms for creating API tokens and project level API tokens. The point of having a hook that provides the functionality for the form is that we can create specific forms that take care of implementing the logic needed for that form instead of having one form serving multiple use cases.
This commit is contained in:
parent
4f475548ba
commit
045973a432
@ -26,8 +26,6 @@ import { HighlightCell } from 'component/common/Table/cells/HighlightCell/Highli
|
|||||||
import { Search } from 'component/common/Search/Search';
|
import { Search } from 'component/common/Search/Search';
|
||||||
import { useConditionallyHiddenColumns } from 'hooks/useConditionallyHiddenColumns';
|
import { useConditionallyHiddenColumns } from 'hooks/useConditionallyHiddenColumns';
|
||||||
import { TimeAgoCell } from 'component/common/Table/cells/TimeAgoCell/TimeAgoCell';
|
import { TimeAgoCell } from 'component/common/Table/cells/TimeAgoCell/TimeAgoCell';
|
||||||
import { Route, Routes } from 'react-router-dom';
|
|
||||||
import { ProjectApiTokenCreate } from './ProjectApiTokenCreate';
|
|
||||||
|
|
||||||
const hiddenColumnsSmall = ['Icon', 'createdAt'];
|
const hiddenColumnsSmall = ['Icon', 'createdAt'];
|
||||||
const hiddenColumnsCompact = ['Icon', 'project', 'seenAt'];
|
const hiddenColumnsCompact = ['Icon', 'project', 'seenAt'];
|
||||||
@ -67,11 +65,11 @@ export const ApiTokenTable = ({
|
|||||||
Cell: ({
|
Cell: ({
|
||||||
value,
|
value,
|
||||||
}: {
|
}: {
|
||||||
value: keyof typeof tokenDescriptions;
|
value: 'client' | 'admin' | 'frontend';
|
||||||
}) => (
|
}) => (
|
||||||
<HighlightCell
|
<HighlightCell
|
||||||
value={tokenDescriptions[value].label}
|
value={tokenDescriptions[value.toLowerCase()].label}
|
||||||
subtitle={tokenDescriptions[value].title}
|
subtitle={tokenDescriptions[value.toLowerCase()].title}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
minWidth: 280,
|
minWidth: 280,
|
||||||
@ -246,22 +244,11 @@ export const ApiTokenTable = ({
|
|||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<ConditionallyRender
|
|
||||||
condition={Boolean(filterForProject)}
|
|
||||||
show={
|
|
||||||
<Routes>
|
|
||||||
<Route
|
|
||||||
path="create"
|
|
||||||
element={<ProjectApiTokenCreate />}
|
|
||||||
/>
|
|
||||||
</Routes>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</PageContent>
|
</PageContent>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
//TODO fix me - remove duplicate keys
|
const tokenDescriptions: { [index: string]: { label: string; title: string } } =
|
||||||
const tokenDescriptions = {
|
{
|
||||||
client: {
|
client: {
|
||||||
label: 'CLIENT',
|
label: 'CLIENT',
|
||||||
title: 'Connect server-side SDK or Unleash Proxy',
|
title: 'Connect server-side SDK or Unleash Proxy',
|
||||||
@ -274,16 +261,4 @@ const tokenDescriptions = {
|
|||||||
label: 'ADMIN',
|
label: 'ADMIN',
|
||||||
title: 'Full access for managing Unleash',
|
title: 'Full access for managing Unleash',
|
||||||
},
|
},
|
||||||
CLiENT: {
|
|
||||||
label: 'CLIENT',
|
|
||||||
title: 'Connect server-side SDK or Unleash Proxy',
|
|
||||||
},
|
|
||||||
FRONTEND: {
|
|
||||||
label: 'FRONTEND',
|
|
||||||
title: 'Connect web and mobile SDK',
|
|
||||||
},
|
|
||||||
ADMIN: {
|
|
||||||
label: 'ADMIN',
|
|
||||||
title: 'Full access for managing Unleash',
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
@ -1,29 +0,0 @@
|
|||||||
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
|
||||||
import useProjectAccess from 'hooks/api/getters/useProjectAccess/useProjectAccess';
|
|
||||||
import { useAccess } from 'hooks/api/getters/useAccess/useAccess';
|
|
||||||
import { GO_BACK } from 'constants/navigate';
|
|
||||||
import { CreateApiToken } from '../CreateApiToken/CreateApiToken';
|
|
||||||
import { SidebarModal } from 'component/common/SidebarModal/SidebarModal';
|
|
||||||
import { useNavigate } from 'react-router-dom';
|
|
||||||
|
|
||||||
export const ProjectApiTokenCreate = () => {
|
|
||||||
const projectId = useRequiredPathParam('projectId');
|
|
||||||
const navigate = useNavigate();
|
|
||||||
|
|
||||||
const { access } = useProjectAccess(projectId);
|
|
||||||
const { users, serviceAccounts, groups } = useAccess();
|
|
||||||
|
|
||||||
if (!access || !users || !serviceAccounts || !groups) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<SidebarModal
|
|
||||||
open
|
|
||||||
onClose={() => navigate(GO_BACK)}
|
|
||||||
label={`Create API token`}
|
|
||||||
>
|
|
||||||
<CreateApiToken modal={true} project={projectId} />
|
|
||||||
</SidebarModal>
|
|
||||||
);
|
|
||||||
};
|
|
@ -7,32 +7,23 @@ import useApiTokensApi from 'hooks/api/actions/useApiTokensApi/useApiTokensApi';
|
|||||||
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
||||||
import useToast from 'hooks/useToast';
|
import useToast from 'hooks/useToast';
|
||||||
import { useApiTokenForm } from 'component/admin/apiToken/ApiTokenForm/useApiTokenForm';
|
import { useApiTokenForm } from 'component/admin/apiToken/ApiTokenForm/useApiTokenForm';
|
||||||
import {
|
import { CREATE_API_TOKEN } from 'component/providers/AccessProvider/permissions';
|
||||||
CREATE_API_TOKEN,
|
|
||||||
CREATE_PROJECT_API_TOKEN,
|
|
||||||
} from 'component/providers/AccessProvider/permissions';
|
|
||||||
import { ConfirmToken } from '../ConfirmToken/ConfirmToken';
|
import { ConfirmToken } from '../ConfirmToken/ConfirmToken';
|
||||||
import { scrollToTop } from 'component/common/util';
|
import { scrollToTop } from 'component/common/util';
|
||||||
import { formatUnknownError } from 'utils/formatUnknownError';
|
import { formatUnknownError } from 'utils/formatUnknownError';
|
||||||
import { usePageTitle } from 'hooks/usePageTitle';
|
import { usePageTitle } from 'hooks/usePageTitle';
|
||||||
import { GO_BACK } from 'constants/navigate';
|
import { GO_BACK } from 'constants/navigate';
|
||||||
import { useApiTokens } from 'hooks/api/getters/useApiTokens/useApiTokens';
|
import { useApiTokens } from 'hooks/api/getters/useApiTokens/useApiTokens';
|
||||||
import useProjectApiTokensApi from 'hooks/api/actions/useProjectApiTokensApi/useProjectApiTokensApi';
|
|
||||||
import { TokenInfo } from '../ApiTokenForm/TokenInfo/TokenInfo';
|
import { TokenInfo } from '../ApiTokenForm/TokenInfo/TokenInfo';
|
||||||
import { TokenTypeSelector } from '../ApiTokenForm/TokenTypeSelector/TokenTypeSelector';
|
import { TokenTypeSelector } from '../ApiTokenForm/TokenTypeSelector/TokenTypeSelector';
|
||||||
import { ProjectSelector } from '../ApiTokenForm/ProjectSelector/ProjectSelector';
|
import { ProjectSelector } from '../ApiTokenForm/ProjectSelector/ProjectSelector';
|
||||||
import { EnvironmentSelector } from '../ApiTokenForm/EnvironmentSelector/EnvironmentSelector';
|
import { EnvironmentSelector } from '../ApiTokenForm/EnvironmentSelector/EnvironmentSelector';
|
||||||
|
|
||||||
const pageTitle = 'Create API token';
|
const pageTitle = 'Create API token';
|
||||||
|
|
||||||
interface ICreateApiTokenProps {
|
interface ICreateApiTokenProps {
|
||||||
modal?: boolean;
|
modal?: boolean;
|
||||||
project?: string;
|
|
||||||
}
|
}
|
||||||
export const CreateApiToken = ({
|
export const CreateApiToken = ({ modal = false }: ICreateApiTokenProps) => {
|
||||||
modal = false,
|
|
||||||
project,
|
|
||||||
}: ICreateApiTokenProps) => {
|
|
||||||
const { setToastApiError } = useToast();
|
const { setToastApiError } = useToast();
|
||||||
const { uiConfig } = useUiConfig();
|
const { uiConfig } = useUiConfig();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
@ -52,22 +43,16 @@ export const CreateApiToken = ({
|
|||||||
isValid,
|
isValid,
|
||||||
errors,
|
errors,
|
||||||
clearErrors,
|
clearErrors,
|
||||||
} = useApiTokenForm(project);
|
} = useApiTokenForm();
|
||||||
|
|
||||||
const { createToken, loading: globalLoading } = useApiTokensApi();
|
const { createToken, loading } = useApiTokensApi();
|
||||||
const { createToken: createProjectToken, loading: projectLoading } =
|
|
||||||
useProjectApiTokensApi();
|
|
||||||
const { refetch } = useApiTokens();
|
const { refetch } = useApiTokens();
|
||||||
|
|
||||||
usePageTitle(pageTitle);
|
usePageTitle(pageTitle);
|
||||||
|
|
||||||
const PATH = Boolean(project)
|
const PATH = `api/admin/api-tokens`;
|
||||||
? `api/admin/project/${project}/api-tokens`
|
|
||||||
: 'api/admin/api-tokens';
|
const permission = CREATE_API_TOKEN;
|
||||||
const permission = Boolean(project)
|
|
||||||
? CREATE_PROJECT_API_TOKEN
|
|
||||||
: CREATE_API_TOKEN;
|
|
||||||
const loading = globalLoading || projectLoading;
|
|
||||||
|
|
||||||
const handleSubmit = async (e: Event) => {
|
const handleSubmit = async (e: Event) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
@ -76,23 +61,15 @@ export const CreateApiToken = ({
|
|||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const payload = getApiTokenPayload();
|
const payload = getApiTokenPayload();
|
||||||
if (project) {
|
|
||||||
await createProjectToken(payload, project)
|
|
||||||
.then(res => res.json())
|
|
||||||
.then(api => {
|
|
||||||
scrollToTop();
|
|
||||||
setToken(api.secret);
|
|
||||||
setShowConfirm(true);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
await createToken(payload)
|
await createToken(payload)
|
||||||
.then(res => res.json())
|
.then(res => res.json())
|
||||||
.then(api => {
|
.then(api => {
|
||||||
scrollToTop();
|
scrollToTop();
|
||||||
setToken(api.secret);
|
setToken(api.secret);
|
||||||
setShowConfirm(true);
|
setShowConfirm(true);
|
||||||
|
refetch();
|
||||||
});
|
});
|
||||||
}
|
|
||||||
} catch (error: unknown) {
|
} catch (error: unknown) {
|
||||||
setToastApiError(formatUnknownError(error));
|
setToastApiError(formatUnknownError(error));
|
||||||
}
|
}
|
||||||
@ -100,7 +77,6 @@ export const CreateApiToken = ({
|
|||||||
|
|
||||||
const closeConfirm = () => {
|
const closeConfirm = () => {
|
||||||
setShowConfirm(false);
|
setShowConfirm(false);
|
||||||
refetch();
|
|
||||||
navigate(GO_BACK);
|
navigate(GO_BACK);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -131,13 +107,7 @@ export const CreateApiToken = ({
|
|||||||
handleSubmit={handleSubmit}
|
handleSubmit={handleSubmit}
|
||||||
handleCancel={handleCancel}
|
handleCancel={handleCancel}
|
||||||
mode="Create"
|
mode="Create"
|
||||||
actions={
|
actions={<CreateButton name="token" permission={permission} />}
|
||||||
<CreateButton
|
|
||||||
name="token"
|
|
||||||
permission={permission}
|
|
||||||
projectId={project}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
<TokenInfo
|
<TokenInfo
|
||||||
username={username}
|
username={username}
|
||||||
|
@ -17,6 +17,7 @@ export const CreateApiTokenButton = () => {
|
|||||||
const permission = Boolean(project)
|
const permission = Boolean(project)
|
||||||
? CREATE_PROJECT_API_TOKEN
|
? CREATE_PROJECT_API_TOKEN
|
||||||
: CREATE_API_TOKEN;
|
: CREATE_API_TOKEN;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ResponsiveButton
|
<ResponsiveButton
|
||||||
Icon={Add}
|
Icon={Add}
|
||||||
|
@ -0,0 +1,18 @@
|
|||||||
|
import { GO_BACK } from 'constants/navigate';
|
||||||
|
import { SidebarModal } from 'component/common/SidebarModal/SidebarModal';
|
||||||
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
import { CreateProjectApiTokenForm } from './CreateProjectApiTokenForm';
|
||||||
|
|
||||||
|
export const CreateProjectApiToken = () => {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SidebarModal
|
||||||
|
open
|
||||||
|
onClose={() => navigate(GO_BACK)}
|
||||||
|
label={`Create API token`}
|
||||||
|
>
|
||||||
|
<CreateProjectApiTokenForm />
|
||||||
|
</SidebarModal>
|
||||||
|
);
|
||||||
|
};
|
@ -0,0 +1,144 @@
|
|||||||
|
import { useState } from 'react';
|
||||||
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
import FormTemplate from 'component/common/FormTemplate/FormTemplate';
|
||||||
|
|
||||||
|
import { CreateButton } from 'component/common/CreateButton/CreateButton';
|
||||||
|
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
||||||
|
import useToast from 'hooks/useToast';
|
||||||
|
import { useApiTokenForm } from 'component/admin/apiToken/ApiTokenForm/useApiTokenForm';
|
||||||
|
import { CREATE_PROJECT_API_TOKEN } from 'component/providers/AccessProvider/permissions';
|
||||||
|
import { scrollToTop } from 'component/common/util';
|
||||||
|
import { formatUnknownError } from 'utils/formatUnknownError';
|
||||||
|
import { usePageTitle } from 'hooks/usePageTitle';
|
||||||
|
import { GO_BACK } from 'constants/navigate';
|
||||||
|
import useProjectApiTokensApi from 'hooks/api/actions/useProjectApiTokensApi/useProjectApiTokensApi';
|
||||||
|
|
||||||
|
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
||||||
|
import ApiTokenForm from 'component/admin/apiToken/ApiTokenForm/ApiTokenForm';
|
||||||
|
import { EnvironmentSelector } from 'component/admin/apiToken/ApiTokenForm/EnvironmentSelector/EnvironmentSelector';
|
||||||
|
import { TokenInfo } from 'component/admin/apiToken/ApiTokenForm/TokenInfo/TokenInfo';
|
||||||
|
import { TokenTypeSelector } from 'component/admin/apiToken/ApiTokenForm/TokenTypeSelector/TokenTypeSelector';
|
||||||
|
import { ConfirmToken } from 'component/admin/apiToken/ConfirmToken/ConfirmToken';
|
||||||
|
import { useProjectApiTokens } from 'hooks/api/getters/useProjectApiTokens/useProjectApiTokens';
|
||||||
|
import { usePlausibleTracker } from 'hooks/usePlausibleTracker';
|
||||||
|
|
||||||
|
const pageTitle = 'Create project API token';
|
||||||
|
|
||||||
|
export const CreateProjectApiTokenForm = () => {
|
||||||
|
const project = useRequiredPathParam('projectId');
|
||||||
|
const { setToastApiError } = useToast();
|
||||||
|
const { uiConfig } = useUiConfig();
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const [showConfirm, setShowConfirm] = useState(false);
|
||||||
|
const [token, setToken] = useState('');
|
||||||
|
|
||||||
|
const {
|
||||||
|
getApiTokenPayload,
|
||||||
|
username,
|
||||||
|
type,
|
||||||
|
environment,
|
||||||
|
setUsername,
|
||||||
|
setTokenType,
|
||||||
|
setEnvironment,
|
||||||
|
isValid,
|
||||||
|
errors,
|
||||||
|
clearErrors,
|
||||||
|
} = useApiTokenForm(project);
|
||||||
|
|
||||||
|
const { createToken: createProjectToken, loading } =
|
||||||
|
useProjectApiTokensApi();
|
||||||
|
const { refetch: refetchProjectTokens } = useProjectApiTokens(project);
|
||||||
|
const { trackEvent } = usePlausibleTracker();
|
||||||
|
|
||||||
|
usePageTitle(pageTitle);
|
||||||
|
|
||||||
|
const PATH = `api/admin/project/${project}/api-tokens`;
|
||||||
|
const permission = CREATE_PROJECT_API_TOKEN;
|
||||||
|
|
||||||
|
const handleSubmit = async (e: Event) => {
|
||||||
|
e.preventDefault();
|
||||||
|
if (!isValid()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const payload = getApiTokenPayload();
|
||||||
|
|
||||||
|
await createProjectToken(payload, project)
|
||||||
|
.then(res => res.json())
|
||||||
|
.then(api => {
|
||||||
|
scrollToTop();
|
||||||
|
setToken(api.secret);
|
||||||
|
setShowConfirm(true);
|
||||||
|
trackEvent('project_api_tokens', {
|
||||||
|
props: { eventType: 'api_key_created' },
|
||||||
|
});
|
||||||
|
|
||||||
|
refetchProjectTokens();
|
||||||
|
});
|
||||||
|
} catch (error: unknown) {
|
||||||
|
setToastApiError(formatUnknownError(error));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const closeConfirm = () => {
|
||||||
|
setShowConfirm(false);
|
||||||
|
navigate(GO_BACK);
|
||||||
|
};
|
||||||
|
|
||||||
|
const formatApiCode = () => {
|
||||||
|
return `curl --location --request POST '${
|
||||||
|
uiConfig.unleashUrl
|
||||||
|
}/${PATH}' \\
|
||||||
|
--header 'Authorization: INSERT_API_KEY' \\
|
||||||
|
--header 'Content-Type: application/json' \\
|
||||||
|
--data-raw '${JSON.stringify(getApiTokenPayload(), undefined, 2)}'`;
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCancel = () => {
|
||||||
|
navigate(GO_BACK);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FormTemplate
|
||||||
|
loading={loading}
|
||||||
|
title={pageTitle}
|
||||||
|
modal
|
||||||
|
description="Unleash SDKs use API tokens to authenticate to the Unleash API. Client SDKs need a token with 'client privileges', which allows them to fetch feature toggle configurations and post usage metrics."
|
||||||
|
documentationLink="https://docs.getunleash.io/reference/api-tokens-and-client-keys"
|
||||||
|
documentationLinkLabel="API tokens documentation"
|
||||||
|
formatApiCode={formatApiCode}
|
||||||
|
>
|
||||||
|
<ApiTokenForm
|
||||||
|
handleSubmit={handleSubmit}
|
||||||
|
handleCancel={handleCancel}
|
||||||
|
mode="Create"
|
||||||
|
actions={
|
||||||
|
<CreateButton
|
||||||
|
name="token"
|
||||||
|
permission={permission}
|
||||||
|
projectId={project}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<TokenInfo
|
||||||
|
username={username}
|
||||||
|
setUsername={setUsername}
|
||||||
|
errors={errors}
|
||||||
|
clearErrors={clearErrors}
|
||||||
|
/>
|
||||||
|
<TokenTypeSelector type={type} setType={setTokenType} />
|
||||||
|
<EnvironmentSelector
|
||||||
|
type={type}
|
||||||
|
environment={environment}
|
||||||
|
setEnvironment={setEnvironment}
|
||||||
|
/>
|
||||||
|
</ApiTokenForm>
|
||||||
|
<ConfirmToken
|
||||||
|
open={showConfirm}
|
||||||
|
closeConfirm={closeConfirm}
|
||||||
|
token={token}
|
||||||
|
type={type}
|
||||||
|
/>
|
||||||
|
</FormTemplate>
|
||||||
|
);
|
||||||
|
};
|
@ -7,8 +7,10 @@ import { READ_PROJECT_API_TOKEN } from 'component/providers/AccessProvider/permi
|
|||||||
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
||||||
import { usePageTitle } from 'hooks/usePageTitle';
|
import { usePageTitle } from 'hooks/usePageTitle';
|
||||||
import { useProjectNameOrId } from 'hooks/api/getters/useProject/useProject';
|
import { useProjectNameOrId } from 'hooks/api/getters/useProject/useProject';
|
||||||
import { ApiTokenTable } from '../../../admin/apiToken/ApiTokenTable/ApiTokenTable';
|
import { CreateProjectApiToken } from 'component/project/Project/ProjectSettings/ProjectApiAccess/CreateProjectApiToken';
|
||||||
import { useProjectApiTokens } from '../../../../hooks/api/getters/useProjectApiTokens/useProjectApiTokens';
|
import { Routes, Route } from 'react-router-dom';
|
||||||
|
import { ApiTokenTable } from 'component/admin/apiToken/ApiTokenTable/ApiTokenTable';
|
||||||
|
import { useProjectApiTokens } from 'hooks/api/getters/useProjectApiTokens/useProjectApiTokens';
|
||||||
|
|
||||||
export const ProjectApiAccess = () => {
|
export const ProjectApiAccess = () => {
|
||||||
const projectId = useRequiredPathParam('projectId');
|
const projectId = useRequiredPathParam('projectId');
|
||||||
@ -37,6 +39,10 @@ export const ProjectApiAccess = () => {
|
|||||||
compact
|
compact
|
||||||
filterForProject={projectId}
|
filterForProject={projectId}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<Routes>
|
||||||
|
<Route path="create" element={<CreateProjectApiToken />} />
|
||||||
|
</Routes>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
@ -9,7 +9,7 @@ import { ITab, VerticalTabs } from 'component/common/VerticalTabs/VerticalTabs';
|
|||||||
import { ProjectAccess } from 'component/project/ProjectAccess/ProjectAccess';
|
import { ProjectAccess } from 'component/project/ProjectAccess/ProjectAccess';
|
||||||
import ProjectEnvironmentList from 'component/project/ProjectEnvironment/ProjectEnvironment';
|
import ProjectEnvironmentList from 'component/project/ProjectEnvironment/ProjectEnvironment';
|
||||||
import { ChangeRequestConfiguration } from './ChangeRequestConfiguration/ChangeRequestConfiguration';
|
import { ChangeRequestConfiguration } from './ChangeRequestConfiguration/ChangeRequestConfiguration';
|
||||||
import { ProjectApiAccess } from './ProjectApiAccess';
|
import { ProjectApiAccess } from './ProjectApiAccess/ProjectApiAccess';
|
||||||
import useUiConfig from '../../../../hooks/api/getters/useUiConfig/useUiConfig';
|
import useUiConfig from '../../../../hooks/api/getters/useUiConfig/useUiConfig';
|
||||||
|
|
||||||
export const ProjectSettings = () => {
|
export const ProjectSettings = () => {
|
||||||
|
@ -19,7 +19,8 @@ type CustomEvents =
|
|||||||
| 'project_overview'
|
| 'project_overview'
|
||||||
| 'suggest_tags'
|
| 'suggest_tags'
|
||||||
| 'unknown_ui_error'
|
| 'unknown_ui_error'
|
||||||
| 'export_import';
|
| 'export_import'
|
||||||
|
| 'project_api_tokens';
|
||||||
|
|
||||||
export const usePlausibleTracker = () => {
|
export const usePlausibleTracker = () => {
|
||||||
const plausible = useContext(PlausibleContext);
|
const plausible = useContext(PlausibleContext);
|
||||||
|
@ -42,6 +42,7 @@ process.nextTick(async () => {
|
|||||||
featuresExportImport: true,
|
featuresExportImport: true,
|
||||||
newProjectOverview: true,
|
newProjectOverview: true,
|
||||||
projectStatusApi: true,
|
projectStatusApi: true,
|
||||||
|
showProjectApiAccess: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
authentication: {
|
authentication: {
|
||||||
|
Loading…
Reference in New Issue
Block a user