mirror of
https://github.com/Unleash/unleash.git
synced 2025-05-08 01:15:49 +02:00
chore: remove createProjectWithEnvironmentConfig and newCreateProjectUI flags (#7429)
This PR removes the last two flags related to the project managament improvements project, making the new project creation form GA. In doing so, we can also delete the old project creation form (or at least the page, the form is still in use in the project settings).
This commit is contained in:
parent
5b4ff92454
commit
0af5bbad38
@ -1,141 +1,7 @@
|
||||
import { Navigate, useNavigate } from 'react-router-dom';
|
||||
import ProjectForm from '../ProjectForm/ProjectForm';
|
||||
import useProjectForm, {
|
||||
DEFAULT_PROJECT_STICKINESS,
|
||||
} from '../hooks/useProjectForm';
|
||||
import { CreateButton } from 'component/common/CreateButton/CreateButton';
|
||||
import FormTemplate from 'component/common/FormTemplate/FormTemplate';
|
||||
import { CREATE_PROJECT } from 'component/providers/AccessProvider/permissions';
|
||||
import useProjectApi from 'hooks/api/actions/useProjectApi/useProjectApi';
|
||||
import { useAuthUser } from 'hooks/api/getters/useAuth/useAuthUser';
|
||||
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
||||
import useToast from 'hooks/useToast';
|
||||
import { formatUnknownError } from 'utils/formatUnknownError';
|
||||
import { GO_BACK } from 'constants/navigate';
|
||||
import { usePlausibleTracker } from 'hooks/usePlausibleTracker';
|
||||
import { Button, styled } from '@mui/material';
|
||||
import { useUiFlag } from 'hooks/useUiFlag';
|
||||
|
||||
const CREATE_PROJECT_BTN = 'CREATE_PROJECT_BTN';
|
||||
|
||||
const StyledButton = styled(Button)(({ theme }) => ({
|
||||
marginLeft: theme.spacing(3),
|
||||
}));
|
||||
import { Navigate } from 'react-router-dom';
|
||||
|
||||
const CreateProject = () => {
|
||||
const { setToastData, setToastApiError } = useToast();
|
||||
const { refetchUser } = useAuthUser();
|
||||
const { uiConfig } = useUiConfig();
|
||||
const useNewProjectForm = useUiFlag('newCreateProjectUI');
|
||||
const navigate = useNavigate();
|
||||
const { trackEvent } = usePlausibleTracker();
|
||||
const {
|
||||
projectId,
|
||||
projectName,
|
||||
projectDesc,
|
||||
projectMode,
|
||||
setProjectMode,
|
||||
setProjectId,
|
||||
setProjectName,
|
||||
setProjectDesc,
|
||||
getCreateProjectPayload,
|
||||
clearErrors,
|
||||
validateProjectId,
|
||||
validateName,
|
||||
setProjectStickiness,
|
||||
projectStickiness,
|
||||
errors,
|
||||
} = useProjectForm();
|
||||
|
||||
if (useNewProjectForm) {
|
||||
return <Navigate to={`/projects?create=true`} replace />;
|
||||
}
|
||||
|
||||
const { createProject, loading } = useProjectApi();
|
||||
|
||||
const handleSubmit = async (e: Event) => {
|
||||
e.preventDefault();
|
||||
clearErrors();
|
||||
const validName = validateName();
|
||||
const validId = useNewProjectForm || (await validateProjectId());
|
||||
|
||||
if (validName && validId) {
|
||||
const payload = getCreateProjectPayload({
|
||||
omitId: useNewProjectForm,
|
||||
});
|
||||
try {
|
||||
const createdProject = await createProject(payload);
|
||||
refetchUser();
|
||||
navigate(`/projects/${createdProject.id}`, { replace: true });
|
||||
setToastData({
|
||||
title: 'Project created',
|
||||
text: 'Now you can add flags to this project',
|
||||
confetti: true,
|
||||
type: 'success',
|
||||
});
|
||||
|
||||
if (projectStickiness !== DEFAULT_PROJECT_STICKINESS) {
|
||||
trackEvent('project_stickiness_set');
|
||||
}
|
||||
trackEvent('project-mode', {
|
||||
props: { mode: projectMode, action: 'added' },
|
||||
});
|
||||
} catch (error: unknown) {
|
||||
setToastApiError(formatUnknownError(error));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const formatApiCode = () => {
|
||||
return `curl --location --request POST '${uiConfig.unleashUrl}/api/admin/projects' \\
|
||||
--header 'Authorization: INSERT_API_KEY' \\
|
||||
--header 'Content-Type: application/json' \\
|
||||
--data-raw '${JSON.stringify(
|
||||
getCreateProjectPayload({ omitId: useNewProjectForm }),
|
||||
undefined,
|
||||
2,
|
||||
)}'`;
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
navigate(GO_BACK);
|
||||
};
|
||||
|
||||
return (
|
||||
<FormTemplate
|
||||
loading={loading}
|
||||
title='Create project'
|
||||
description='Projects allows you to group feature flags together in the management UI.'
|
||||
documentationLink='https://docs.getunleash.io/reference/projects'
|
||||
documentationLinkLabel='Projects documentation'
|
||||
formatApiCode={formatApiCode}
|
||||
>
|
||||
<ProjectForm
|
||||
errors={errors}
|
||||
handleSubmit={handleSubmit}
|
||||
projectId={projectId}
|
||||
setProjectId={setProjectId}
|
||||
projectName={projectName}
|
||||
projectStickiness={projectStickiness}
|
||||
projectMode={projectMode}
|
||||
setProjectMode={setProjectMode}
|
||||
setProjectStickiness={setProjectStickiness}
|
||||
setProjectName={setProjectName}
|
||||
projectDesc={projectDesc}
|
||||
setProjectDesc={setProjectDesc}
|
||||
mode='Create'
|
||||
clearErrors={clearErrors}
|
||||
validateProjectId={validateProjectId}
|
||||
>
|
||||
<CreateButton
|
||||
name='project'
|
||||
permission={CREATE_PROJECT}
|
||||
data-testid={CREATE_PROJECT_BTN}
|
||||
/>
|
||||
<StyledButton onClick={handleCancel}>Cancel</StyledButton>
|
||||
</ProjectForm>
|
||||
</FormTemplate>
|
||||
);
|
||||
};
|
||||
|
||||
export default CreateProject;
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { useContext, useEffect, useMemo, useState } from 'react';
|
||||
import { useNavigate, useSearchParams } from 'react-router-dom';
|
||||
import { useSearchParams } from 'react-router-dom';
|
||||
import useProjects from 'hooks/api/getters/useProjects/useProjects';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import type { IProjectCard } from 'interfaces/project';
|
||||
@ -20,7 +20,6 @@ import { ReactComponent as ProPlanIcon } from 'assets/icons/pro-enterprise-featu
|
||||
import { ReactComponent as ProPlanIconLight } from 'assets/icons/pro-enterprise-feature-badge-light.svg';
|
||||
import { safeRegExp } from '@server/util/escape-regex';
|
||||
import { ThemeMode } from 'component/common/ThemeMode/ThemeMode';
|
||||
import { useUiFlag } from 'hooks/useUiFlag';
|
||||
import { useProfile } from 'hooks/api/getters/useProfile/useProfile';
|
||||
import { groupProjects } from './group-projects';
|
||||
import { ProjectGroup } from './ProjectGroup';
|
||||
@ -92,7 +91,6 @@ const ProjectCreationButton = () => {
|
||||
const showCreateDialog = Boolean(searchParams.get('create'));
|
||||
const [openCreateDialog, setOpenCreateDialog] = useState(showCreateDialog);
|
||||
const { hasAccess } = useContext(AccessContext);
|
||||
const navigate = useNavigate();
|
||||
const { isOss } = useUiConfig();
|
||||
|
||||
const createButtonData = resolveCreateButtonData(
|
||||
@ -100,14 +98,12 @@ const ProjectCreationButton = () => {
|
||||
hasAccess(CREATE_PROJECT),
|
||||
);
|
||||
|
||||
const useNewProjectForm = useUiFlag('newCreateProjectUI');
|
||||
|
||||
const CreateButton: React.FC<{ onClick: () => void }> = ({ onClick }) => {
|
||||
return (
|
||||
<>
|
||||
<ResponsiveButton
|
||||
Icon={Add}
|
||||
endIcon={createButtonData.endIcon}
|
||||
onClick={onClick}
|
||||
onClick={() => setOpenCreateDialog(true)}
|
||||
maxWidth='700px'
|
||||
permission={CREATE_PROJECT}
|
||||
disabled={createButtonData.disabled}
|
||||
@ -116,22 +112,12 @@ const ProjectCreationButton = () => {
|
||||
>
|
||||
New project
|
||||
</ResponsiveButton>
|
||||
);
|
||||
};
|
||||
|
||||
if (useNewProjectForm) {
|
||||
return (
|
||||
<>
|
||||
<CreateButton onClick={() => setOpenCreateDialog(true)} />
|
||||
<CreateProjectDialog
|
||||
open={openCreateDialog}
|
||||
onClose={() => setOpenCreateDialog(false)}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
} else {
|
||||
return <CreateButton onClick={() => navigate('/projects/create')} />;
|
||||
}
|
||||
};
|
||||
|
||||
export const ProjectListNew = () => {
|
||||
|
@ -81,8 +81,6 @@ export type UiFlags = {
|
||||
projectOverviewRefactorFeedback?: boolean;
|
||||
featureLifecycle?: boolean;
|
||||
scimApi?: boolean;
|
||||
createProjectWithEnvironmentConfig?: boolean;
|
||||
newCreateProjectUI?: boolean;
|
||||
manyStrategiesPagination?: boolean;
|
||||
enableLegacyVariants?: boolean;
|
||||
navigationSidebar?: boolean;
|
||||
|
@ -83,7 +83,6 @@ exports[`should create default config 1`] = `
|
||||
"celebrateUnleash": false,
|
||||
"collectTrafficDataUsage": false,
|
||||
"commandBarUI": false,
|
||||
"createProjectWithEnvironmentConfig": false,
|
||||
"demo": false,
|
||||
"disableBulkToggle": false,
|
||||
"disableMetrics": false,
|
||||
@ -138,7 +137,6 @@ exports[`should create default config 1`] = `
|
||||
},
|
||||
"migrationLock": true,
|
||||
"navigationSidebar": true,
|
||||
"newCreateProjectUI": false,
|
||||
"outdatedSdksBanner": false,
|
||||
"parseProjectFromSession": false,
|
||||
"personalAccessTokensKillSwitch": false,
|
||||
|
@ -79,9 +79,7 @@ beforeAll(async () => {
|
||||
const config = createTestConfig({
|
||||
getLogger,
|
||||
experimental: {
|
||||
flags: {
|
||||
createProjectWithEnvironmentConfig: true,
|
||||
},
|
||||
flags: {},
|
||||
},
|
||||
});
|
||||
eventService = new EventService(stores, config);
|
||||
@ -2715,26 +2713,5 @@ describe('automatic ID generation for create project', () => {
|
||||
expect(project.id).toBe(id);
|
||||
},
|
||||
);
|
||||
|
||||
test.each(['', undefined, ' '])(
|
||||
'if the flag to enable auto ID generation is off, not providing a valid ID (testing `%s`) throws an error',
|
||||
async (id) => {
|
||||
// @ts-expect-error
|
||||
projectService.flagResolver.isEnabled = () => {
|
||||
return false;
|
||||
};
|
||||
|
||||
const createProject = () =>
|
||||
projectService.createProject(
|
||||
{
|
||||
name: randomId(),
|
||||
id,
|
||||
},
|
||||
user,
|
||||
auditUser,
|
||||
);
|
||||
expect(createProject).rejects.toThrow();
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
|
@ -294,10 +294,7 @@ export default class ProjectService {
|
||||
}
|
||||
|
||||
async validateProjectEnvironments(environments: string[] | undefined) {
|
||||
if (
|
||||
this.flagResolver.isEnabled('createProjectWithEnvironmentConfig') &&
|
||||
environments
|
||||
) {
|
||||
if (environments) {
|
||||
if (environments.length === 0) {
|
||||
throw new BadDataError(
|
||||
'A project must always have at least one environment.',
|
||||
@ -339,12 +336,7 @@ export default class ProjectService {
|
||||
const validateData = async () => {
|
||||
await this.validateProjectEnvironments(newProject.environments);
|
||||
|
||||
if (
|
||||
!newProject.id?.trim() &&
|
||||
this.flagResolver.isEnabled(
|
||||
'createProjectWithEnvironmentConfig',
|
||||
)
|
||||
) {
|
||||
if (!newProject.id?.trim()) {
|
||||
newProject.id = await this.generateUniqueProjectId(
|
||||
newProject.name,
|
||||
);
|
||||
@ -362,9 +354,7 @@ export default class ProjectService {
|
||||
|
||||
await this.projectStore.create(data);
|
||||
|
||||
const envsToEnable =
|
||||
this.flagResolver.isEnabled('createProjectWithEnvironmentConfig') &&
|
||||
newProject.environments?.length
|
||||
const envsToEnable = newProject.environments?.length
|
||||
? newProject.environments
|
||||
: (
|
||||
await this.environmentStore.getAll({
|
||||
@ -378,10 +368,7 @@ export default class ProjectService {
|
||||
}),
|
||||
);
|
||||
|
||||
if (
|
||||
this.isEnterprise &&
|
||||
this.flagResolver.isEnabled('createProjectWithEnvironmentConfig')
|
||||
) {
|
||||
if (this.isEnterprise) {
|
||||
if (newProject.changeRequestEnvironments) {
|
||||
await this.validateEnvironmentsExist(
|
||||
newProject.changeRequestEnvironments.map((env) => env.name),
|
||||
|
@ -56,9 +56,7 @@ export type IFlagKey =
|
||||
| 'featureLifecycle'
|
||||
| 'featureLifecycleMetrics'
|
||||
| 'parseProjectFromSession'
|
||||
| 'createProjectWithEnvironmentConfig'
|
||||
| 'manyStrategiesPagination'
|
||||
| 'newCreateProjectUI'
|
||||
| 'enableLegacyVariants'
|
||||
| 'navigationSidebar'
|
||||
| 'commandBarUI'
|
||||
@ -272,14 +270,6 @@ const flags: IFlags = {
|
||||
process.env.UNLEASH_EXPERIMENTAL_PARSE_PROJECT_FROM_SESSION,
|
||||
false,
|
||||
),
|
||||
createProjectWithEnvironmentConfig: parseEnvVarBoolean(
|
||||
process.env.UNLEASH_EXPERIMENTAL_CREATE_PROJECT_WITH_ENVIRONMENT_CONFIG,
|
||||
false,
|
||||
),
|
||||
newCreateProjectUI: parseEnvVarBoolean(
|
||||
process.env.UNLEASH_EXPERIMENTAL_NEW_CREATE_PROJECT_UI,
|
||||
false,
|
||||
),
|
||||
manyStrategiesPagination: parseEnvVarBoolean(
|
||||
process.env.UNLEASH_EXPERIMENTAL_MANY_STRATEGIES_PAGINATION,
|
||||
false,
|
||||
|
@ -50,7 +50,6 @@ process.nextTick(async () => {
|
||||
projectOverviewRefactorFeedback: true,
|
||||
featureLifecycle: true,
|
||||
parseProjectFromSession: true,
|
||||
createProjectWithEnvironmentConfig: true,
|
||||
manyStrategiesPagination: true,
|
||||
enableLegacyVariants: false,
|
||||
commandBarUI: true,
|
||||
|
Loading…
Reference in New Issue
Block a user