From cb316f012c14800a7ac0ddb2086034d0d6ec2546 Mon Sep 17 00:00:00 2001 From: David Leek Date: Wed, 13 Nov 2024 11:03:19 +0100 Subject: [PATCH] feat: frontend initial page for creating release templates (#8732) --- .../__snapshots__/routes.test.tsx.snap | 14 +++ frontend/src/component/menu/routes.ts | 11 ++ .../CreateReleasePlanTemplate.tsx | 102 ++++++++++++++++++ .../useReleasePlanTemplatesApi.ts | 19 ++++ 4 files changed, 146 insertions(+) create mode 100644 frontend/src/component/releases/ReleasePlanTemplate/CreateReleasePlanTemplate.tsx diff --git a/frontend/src/component/menu/__tests__/__snapshots__/routes.test.tsx.snap b/frontend/src/component/menu/__tests__/__snapshots__/routes.test.tsx.snap index 7d8e5143bc..0f22209818 100644 --- a/frontend/src/component/menu/__tests__/__snapshots__/routes.test.tsx.snap +++ b/frontend/src/component/menu/__tests__/__snapshots__/routes.test.tsx.snap @@ -270,6 +270,20 @@ exports[`returns all baseRoutes 1`] = ` "title": "Release management", "type": "protected", }, + { + "component": [Function], + "enterprise": true, + "flag": "releasePlans", + "menu": { + "mode": [ + "enterprise", + ], + }, + "parent": "/release-management", + "path": "/release-management/create-template", + "title": "Create release plan template", + "type": "protected", + }, { "component": [Function], "enterprise": true, diff --git a/frontend/src/component/menu/routes.ts b/frontend/src/component/menu/routes.ts index 21f9f99144..cf9d207faf 100644 --- a/frontend/src/component/menu/routes.ts +++ b/frontend/src/component/menu/routes.ts @@ -48,6 +48,7 @@ import { Signals } from 'component/signals/Signals'; import { LazyCreateProject } from '../project/Project/CreateProject/LazyCreateProject'; import { PersonalDashboard } from '../personalDashboard/PersonalDashboard'; import { ReleaseManagement } from 'component/releases/ReleaseManagement/ReleaseManagement'; +import { CreateReleasePlanTemplate } from 'component/releases/ReleasePlanTemplate/CreateReleasePlanTemplate'; import { EditReleasePlanTemplate } from 'component/releases/ReleasePlanTemplate/EditReleasePlanTemplate'; export const routes: IRoute[] = [ @@ -281,6 +282,16 @@ export const routes: IRoute[] = [ flag: 'releasePlans', enterprise: true, }, + { + path: '/release-management/create-template', + title: 'Create release plan template', + parent: '/release-management', + component: CreateReleasePlanTemplate, + type: 'protected', + menu: { mode: ['enterprise'] }, + flag: 'releasePlans', + enterprise: true, + }, { path: '/release-management/edit/:templateId', title: 'Edit release plan template', diff --git a/frontend/src/component/releases/ReleasePlanTemplate/CreateReleasePlanTemplate.tsx b/frontend/src/component/releases/ReleasePlanTemplate/CreateReleasePlanTemplate.tsx new file mode 100644 index 0000000000..ef52aff4de --- /dev/null +++ b/frontend/src/component/releases/ReleasePlanTemplate/CreateReleasePlanTemplate.tsx @@ -0,0 +1,102 @@ +import FormTemplate from 'component/common/FormTemplate/FormTemplate'; +import { usePageTitle } from 'hooks/usePageTitle'; +import { Button, styled } from '@mui/material'; +import { TemplateForm } from './TemplateForm'; +import { useTemplateForm } from '../hooks/useTemplateForm'; +import { CreateButton } from 'component/common/CreateButton/CreateButton'; +import { ADMIN } from '@server/types/permissions'; +import { useNavigate } from 'react-router-dom'; +import { GO_BACK } from 'constants/navigate'; +import useReleasePlanTemplatesApi from 'hooks/api/actions/useReleasePlanTemplatesApi/useReleasePlanTemplatesApi'; +import { scrollToTop } from 'component/common/util'; +import useToast from 'hooks/useToast'; +import { formatUnknownError } from 'utils/formatUnknownError'; +import { useUiFlag } from 'hooks/useUiFlag'; + +const StyledForm = styled('form')(() => ({ + display: 'flex', + flexDirection: 'column', + height: '100%', +})); + +const StyledButtonContainer = styled('div')(() => ({ + marginTop: 'auto', + display: 'flex', + justifyContent: 'flex-end', +})); + +const StyledCancelButton = styled(Button)(({ theme }) => ({ + marginLeft: theme.spacing(3), +})); + +export const CreateReleasePlanTemplate = () => { + const releasePlansEnabled = useUiFlag('releasePlans'); + usePageTitle('Create release plan template'); + const { setToastApiError, setToastData } = useToast(); + const navigate = useNavigate(); + const { createReleasePlanTemplate } = useReleasePlanTemplatesApi(); + const { + name, + setName, + description, + setDescription, + errors, + clearErrors, + validate, + getTemplatePayload, + } = useTemplateForm(); + + const handleCancel = () => { + navigate(GO_BACK); + }; + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + clearErrors(); + const isValid = validate(); + if (isValid) { + const payload = getTemplatePayload(); + try { + const template = await createReleasePlanTemplate(payload); + scrollToTop(); + setToastData({ + type: 'success', + title: 'Release plan template created', + }); + navigate(`/release-management/edit/${template.id}`); + } catch (error: unknown) { + setToastApiError(formatUnknownError(error)); + } + } + }; + + if (!releasePlansEnabled) { + return null; + } + + return ( + <> + + + + + + + Cancel + + + + + + ); +}; diff --git a/frontend/src/hooks/api/actions/useReleasePlanTemplatesApi/useReleasePlanTemplatesApi.ts b/frontend/src/hooks/api/actions/useReleasePlanTemplatesApi/useReleasePlanTemplatesApi.ts index e1eb349a0d..0bef65e2ff 100644 --- a/frontend/src/hooks/api/actions/useReleasePlanTemplatesApi/useReleasePlanTemplatesApi.ts +++ b/frontend/src/hooks/api/actions/useReleasePlanTemplatesApi/useReleasePlanTemplatesApi.ts @@ -21,6 +21,24 @@ export const useReleasePlanTemplatesApi = () => { return makeRequest(req.caller, req.id); }; + const createReleasePlanTemplate = async ( + template: IReleasePlanTemplatePayload, + ): Promise => { + const requestId = 'createReleasePlanTemplate'; + const path = 'api/admin/release-plan-templates'; + const req = createRequest( + path, + { + method: 'POST', + body: JSON.stringify(template), + }, + requestId, + ); + + const res = await makeRequest(req.caller, req.id); + return res.json(); + }; + const updateReleasePlanTemplate = async ( template: IReleasePlanTemplatePayload, ) => { @@ -41,6 +59,7 @@ export const useReleasePlanTemplatesApi = () => { return { deleteReleasePlanTemplate, updateReleasePlanTemplate, + createReleasePlanTemplate, }; };