diff --git a/web/src/cde/components/CreateGitspace/CreateGitspace.tsx b/web/src/cde/components/CreateGitspace/CreateGitspace.tsx index 742d39dbd..f3e59af6e 100644 --- a/web/src/cde/components/CreateGitspace/CreateGitspace.tsx +++ b/web/src/cde/components/CreateGitspace/CreateGitspace.tsx @@ -16,12 +16,15 @@ import React from 'react' import * as yup from 'yup' -import { Button, ButtonVariation, Card, Formik, FormikForm, Layout, Text } from '@harnessio/uicore' +import { Button, ButtonVariation, Card, Formik, FormikForm, Layout, Text, useToaster } from '@harnessio/uicore' import { FontVariation } from '@harnessio/design-system' -import { useCreateGitspace, type EnumCodeRepoType, type EnumIDEType } from 'services/cde' +import { useHistory } from 'react-router-dom' +import { useCreateGitspace, OpenapiCreateGitspaceRequest } from 'services/cde' import { useGetSpaceParam } from 'hooks/useGetSpaceParam' import { useStrings } from 'framework/strings' import { IDEType } from 'cde/constants' +import { useAppContext } from 'AppContext' +import { getErrorMessage } from 'utils/Utils' import { SelectIDE } from './components/SelectIDE/SelectIDE' import { SelectRepository } from './components/SelectRepository/SelectRepository' import { BranchInput } from './components/BranchInput/BranchInput' @@ -32,33 +35,53 @@ const initData = { ide: IDEType.VSCODE } -export interface GitspaceFormInterface { - ide?: EnumIDEType - branch?: string - codeRepoId?: string - codeRepoUrl?: string - codeRepoType?: EnumCodeRepoType - region?: string - infra_provider_resource_id?: string -} - -interface GitspaceFormProps { - onSubmit: (data: GitspaceFormInterface) => void -} - -const GitspaceForm = ({ onSubmit }: GitspaceFormProps) => { +const GitspaceForm = () => { const { getString } = useStrings() + const { routes } = useAppContext() + const space = useGetSpaceParam() + const { showError } = useToaster() + const history = useHistory() + + const { mutate, loading, error } = useCreateGitspace({ + accountIdentifier: space?.split('/')[0], + orgIdentifier: space?.split('/')[1], + projectIdentifier: space?.split('/')[2] + }) + + if (error) { + showError(getErrorMessage(error)) + } + return ( - - onSubmit={async data => await onSubmit(data)} + + onSubmit={async data => { + try { + const createdGitspace = await mutate(data) + history.push( + `${routes.toCDEGitspaceDetail({ + space, + gitspaceId: createdGitspace?.id || '' + })}?redirectFrom=login` + ) + } catch (err) { + showError(getErrorMessage(err)) + } + }} formName={'createGitSpace'} initialValues={initData} + validateOnMount={false} validationSchema={yup.object().shape({ - codeRepoId: yup.string().trim().required(), branch: yup.string().trim().required(), + code_repo_id: yup.string().trim().required(), + code_repo_type: yup.string().trim().required(), + code_repo_url: yup.string().trim().required(), + id: yup.string().trim().required(), ide: yup.string().trim().required(), - region: yup.string().trim().required(), - infra_provider_resource_id: yup.string().trim().required() + infra_provider_resource_id: yup.string().trim().required(), + name: yup.string().trim().required(), + metadata: yup.object().shape({ + region: yup.string().trim().required() + }) })}> {_ => { return ( @@ -70,7 +93,7 @@ const GitspaceForm = ({ onSubmit }: GitspaceFormProps) => { - @@ -82,20 +105,14 @@ const GitspaceForm = ({ onSubmit }: GitspaceFormProps) => { } export const CreateGitspace = () => { - const space = useGetSpaceParam() const { getString } = useStrings() - const { mutate } = useCreateGitspace({ - accountIdentifier: space?.split('/')[0], - orgIdentifier: space?.split('/')[1], - projectIdentifier: space?.split('/')[2] - }) return ( {getString('cde.createGitspace')} - + ) } diff --git a/web/src/cde/components/CreateGitspace/components/SelectIDE/SelectIDE.tsx b/web/src/cde/components/CreateGitspace/components/SelectIDE/SelectIDE.tsx index a45cd65bd..9b19bef9e 100644 --- a/web/src/cde/components/CreateGitspace/components/SelectIDE/SelectIDE.tsx +++ b/web/src/cde/components/CreateGitspace/components/SelectIDE/SelectIDE.tsx @@ -21,8 +21,8 @@ import { useFormikContext } from 'formik' import { GitspaceSelect } from 'cde/components/GitspaceSelect/GitspaceSelect' import { useStrings, type UseStringsReturn } from 'framework/strings' import { IDEType } from 'cde/constants' +import type { OpenapiCreateGitspaceRequest } from 'services/cde' import VSCode from '../../../../icons/VSCode.svg?url' -import type { GitspaceFormInterface } from '../../CreateGitspace' export const getIDESelectItems = (getString: UseStringsReturn['getString']) => [ { label: getString('cde.ide.desktop'), value: IDEType.VSCODE }, @@ -30,7 +30,7 @@ export const getIDESelectItems = (getString: UseStringsReturn['getString']) => [ ] export const SelectIDE = () => { - const { values, errors, setFieldValue: onChange } = useFormikContext() + const { values, errors, setFieldValue: onChange } = useFormikContext() const { ide } = values const { getString } = useStrings() const IDESelectItems = getIDESelectItems(getString) diff --git a/web/src/cde/components/CreateGitspace/components/SelectInfraProvider/SelectInfraProvider.tsx b/web/src/cde/components/CreateGitspace/components/SelectInfraProvider/SelectInfraProvider.tsx index b8901222d..54ae770ba 100644 --- a/web/src/cde/components/CreateGitspace/components/SelectInfraProvider/SelectInfraProvider.tsx +++ b/web/src/cde/components/CreateGitspace/components/SelectInfraProvider/SelectInfraProvider.tsx @@ -19,13 +19,12 @@ import { defaultTo, isObject } from 'lodash-es' import { Layout } from '@harnessio/uicore' import { useFormikContext } from 'formik' import { CDEPathParams, useGetCDEAPIParams } from 'cde/hooks/useGetCDEAPIParams' -import { useListInfraProviderResources } from 'services/cde' -import type { GitspaceFormInterface } from 'cde/components/CreateGitspace/CreateGitspace' +import { OpenapiCreateGitspaceRequest, useListInfraProviderResources } from 'services/cde' import { SelectRegion } from '../SelectRegion/SelectRegion' import { SelectMachine } from '../SelectMachine/SelectMachine' export const SelectInfraProvider = () => { - const { values } = useFormikContext() + const { values } = useFormikContext() const { accountIdentifier, orgIdentifier, projectIdentifier } = useGetCDEAPIParams() as CDEPathParams const { data } = useListInfraProviderResources({ accountIdentifier, @@ -44,7 +43,7 @@ export const SelectInfraProvider = () => { const machineOptions = optionsList - ?.filter(item => item?.region === values?.region) + ?.filter(item => item?.region === values?.metadata?.region) ?.map(item => { return { ...item } }) || [] diff --git a/web/src/cde/components/CreateGitspace/components/SelectMachine/SelectMachine.module.scss b/web/src/cde/components/CreateGitspace/components/SelectMachine/SelectMachine.module.scss new file mode 100644 index 000000000..fe2e99b71 --- /dev/null +++ b/web/src/cde/components/CreateGitspace/components/SelectMachine/SelectMachine.module.scss @@ -0,0 +1,4 @@ +.tags { + background: var(--grey-100) !important; + border-radius: 10px; +} diff --git a/web/src/cde/components/CreateGitspace/components/SelectMachine/SelectMachine.module.scss.d.ts b/web/src/cde/components/CreateGitspace/components/SelectMachine/SelectMachine.module.scss.d.ts new file mode 100644 index 000000000..7e5cf0d8d --- /dev/null +++ b/web/src/cde/components/CreateGitspace/components/SelectMachine/SelectMachine.module.scss.d.ts @@ -0,0 +1,19 @@ +/* + * Copyright 2023 Harness, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* eslint-disable */ +// This is an auto-generated file +export declare const tags: string diff --git a/web/src/cde/components/CreateGitspace/components/SelectMachine/SelectMachine.tsx b/web/src/cde/components/CreateGitspace/components/SelectMachine/SelectMachine.tsx index 705269c94..9fbd072b5 100644 --- a/web/src/cde/components/CreateGitspace/components/SelectMachine/SelectMachine.tsx +++ b/web/src/cde/components/CreateGitspace/components/SelectMachine/SelectMachine.tsx @@ -19,16 +19,11 @@ import { Layout, Text } from '@harnessio/uicore' import { Menu, MenuItem } from '@blueprintjs/core' import { Cpu } from 'iconoir-react' import { useFormikContext } from 'formik' +import { FontVariation } from '@harnessio/design-system' import { GitspaceSelect } from 'cde/components/GitspaceSelect/GitspaceSelect' -import type { TypesInfraProviderResourceResponse } from 'services/cde' +import type { OpenapiCreateGitspaceRequest, TypesInfraProviderResourceResponse } from 'services/cde' import { useStrings } from 'framework/strings' -import RAM8 from './assests/RAM8.svg?url' -import RAM16 from './assests/RAM16.svg?url' -import Storage32 from './assests/Storage32.svg?url' -import Storage64 from './assests/Storage64.svg?url' -import CPU4Cores from './assests/CPU4Cores.svg?url' -import CPU8Cores from './assests/CPU8Cores.svg?url' -import type { GitspaceFormInterface } from '../../CreateGitspace' +import css from './SelectMachine.module.scss' export const machineIdToLabel = { '4core_8gb_32gb': 'Standard', @@ -46,26 +41,30 @@ interface SelectMachineInterface { export const SelectMachine = ({ options }: SelectMachineInterface) => { const { getString } = useStrings() - const { values, errors, setFieldValue: onChange } = useFormikContext() + const { values, errors, setFieldValue: onChange } = useFormikContext() const { infra_provider_resource_id: machine } = values const [machineState, setMachineState] = useState(machine || '') const machineTypes = options.map(item => { - const { cpu, disk, memory, infra_provider_config_id } = item + const { cpu, disk, memory, id, name } = item return { - infra_provider_config_id, - id: `${cpu}_${disk}_${memory}`, - label: machineIdToLabel[`${cpu}_${disk}_${memory}` as keyof typeof machineIdToLabel] + id, + label: name, + cpu, + disk, + memory } }) + const data = (machineTypes?.find(item => item.id === machine) || {}) as (typeof machineTypes)[0] + return ( - {machine || getString('cde.machine')} + {data.label || getString('cde.machine')} } errorMessage={errors.infra_provider_resource_id} @@ -76,15 +75,26 @@ export const SelectMachine = ({ options }: SelectMachineInterface) => { {machineTypes.map(item => { return ( - {machineIdToLabel[item.id as keyof typeof machineIdToLabel]} - + + {item.label?.toUpperCase()} + + + Cpu: {item.cpu} + + + Disk: {item.disk} + + + Memory: {item.memory} + + + } onClick={() => { - onChange('infra_provider_resource_id', item.infra_provider_config_id || '') + onChange('infra_provider_resource_id', item.id || '') }} onMouseOver={(e: React.MouseEvent) => { const dd = e.currentTarget.innerText as keyof typeof labelToMachineId @@ -94,26 +104,6 @@ export const SelectMachine = ({ options }: SelectMachineInterface) => { ) })} - - {machineState === labelToMachineId.Standard && ( - - - - - - - - )} - {machineState === labelToMachineId.Large && ( - - - - - - - - )} - } /> diff --git a/web/src/cde/components/CreateGitspace/components/SelectRegion/SelectRegion.tsx b/web/src/cde/components/CreateGitspace/components/SelectRegion/SelectRegion.tsx index 385b7ba09..697716177 100644 --- a/web/src/cde/components/CreateGitspace/components/SelectRegion/SelectRegion.tsx +++ b/web/src/cde/components/CreateGitspace/components/SelectRegion/SelectRegion.tsx @@ -22,21 +22,37 @@ import { useFormikContext } from 'formik' import { GitspaceSelect } from 'cde/components/GitspaceSelect/GitspaceSelect' import { useStrings } from 'framework/strings' import { GitspaceRegion } from 'cde/constants' +import type { OpenapiCreateGitspaceRequest } from 'services/cde' import USWest from './assests/USWest.png' import USEast from './assests/USEast.png' import Australia from './assests/Aus.png' import Europe from './assests/Europe.png' -import type { GitspaceFormInterface } from '../../CreateGitspace' +import Empty from './assests/Empty.png' interface SelectRegionInterface { options: SelectOption[] } +export const getMapFromRegion = (region: string) => { + switch (region) { + case GitspaceRegion.USEast: + return USEast + case GitspaceRegion.USWest: + return USWest + case GitspaceRegion.Europe: + return Europe + case GitspaceRegion.Australia: + return Australia + default: + return Empty + } +} + export const SelectRegion = ({ options }: SelectRegionInterface) => { const { getString } = useStrings() - const { values, errors, setFieldValue: onChange } = useFormikContext() - const { region = '' } = values - const [regionState, setRegionState] = useState(region) + const { values, errors, setFieldValue: onChange } = useFormikContext() + const { metadata } = values + const [regionState, setRegionState] = useState(metadata?.region) return ( { text={ - {region || getString('cde.region')} + {metadata?.region || getString('cde.region')} } - formikName="region" - errorMessage={errors.region} + formikName="metadata.region" + errorMessage={ + ( + errors['metadata'] as unknown as { + [key: string]: string + } + )?.region as unknown as string + } renderMenu={ @@ -57,9 +79,9 @@ export const SelectRegion = ({ options }: SelectRegionInterface) => { {label}} + text={{label.toUpperCase()}} onClick={() => { - onChange('region', label) + onChange('metadata.region', label.toLowerCase()) }} onMouseOver={(e: React.MouseEvent) => { setRegionState(e.currentTarget.innerText) @@ -69,10 +91,7 @@ export const SelectRegion = ({ options }: SelectRegionInterface) => { })} - {regionState === GitspaceRegion.USEast && } - {regionState === GitspaceRegion.USWest && } - {regionState === GitspaceRegion.Europe && } - {regionState === GitspaceRegion.Australia && } + } diff --git a/web/src/cde/components/CreateGitspace/components/SelectRegion/assests/Empty.png b/web/src/cde/components/CreateGitspace/components/SelectRegion/assests/Empty.png new file mode 100644 index 000000000..e783ce985 Binary files /dev/null and b/web/src/cde/components/CreateGitspace/components/SelectRegion/assests/Empty.png differ diff --git a/web/src/cde/components/CreateGitspace/components/SelectRepository/SelectRepository.tsx b/web/src/cde/components/CreateGitspace/components/SelectRepository/SelectRepository.tsx index 683f8c1fe..324e493ec 100644 --- a/web/src/cde/components/CreateGitspace/components/SelectRepository/SelectRepository.tsx +++ b/web/src/cde/components/CreateGitspace/components/SelectRepository/SelectRepository.tsx @@ -23,17 +23,32 @@ import { noop } from 'lodash-es' import { Icon } from '@harnessio/icons' import { useFormikContext } from 'formik' import { useGetSpaceParam } from 'hooks/useGetSpaceParam' -import { OpenapiGetCodeRepositoryResponse, useGetCodeRepository } from 'services/cde' +import { OpenapiCreateGitspaceRequest, OpenapiGetCodeRepositoryResponse, useGetCodeRepository } from 'services/cde' import { GitspaceSelect } from 'cde/components/GitspaceSelect/GitspaceSelect' import { useStrings } from 'framework/strings' import { CodeRepoAccessType } from 'cde/constants' import { getErrorMessage } from 'utils/Utils' -import type { GitspaceFormInterface } from '../../CreateGitspace' import css from './SelectRepository.module.scss' export const getRepoNameFromURL = (repoURL?: string) => { const repoURLSplit = repoURL?.split('/') return repoURLSplit?.[repoURLSplit?.length - 1] + ?.replace(/-/g, '') + ?.replace(/_/g, '') + .replace(/\./g, '') + ?.toLowerCase() +} + +export function generateAlphaNumericHash(length: number) { + let result = '' + const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789' + const charactersLength = characters.length + + for (let i = 0; i < length; i++) { + result += characters.charAt(Math.floor(Math.random() * charactersLength)) + } + + return result } const RepositoryText = ({ repoURL }: { repoURL?: string }) => { @@ -53,9 +68,9 @@ const RepositoryText = ({ repoURL }: { repoURL?: string }) => { export const SelectRepository = () => { const { getString } = useStrings() - const { values, errors, setFieldValue: onChange } = useFormikContext() + const { values, errors, setFormikState } = useFormikContext() - const { codeRepoUrl } = values + const { code_repo_url } = values const space = useGetSpaceParam() const [validatedOnce, setValidatedOnce] = useState(false) @@ -69,10 +84,10 @@ export const SelectRepository = () => { return ( } + text={} icon={'code'} - errorMessage={errors.codeRepoId} - formikName="codeRepoId" + errorMessage={errors.code_repo_url} + formikName="code_repo_url" renderMenu={ { formLoading={loading} onSubmit={() => noop()} formName={'publicURL'} - initialValues={{ url: codeRepoUrl }} + initialValues={{ url: code_repo_url }} validate={async ({ url }) => { - if (!url) { + if (!url || loading) { return {} } let errorMessages = undefined @@ -109,6 +124,7 @@ export const SelectRepository = () => { {formikProps => { if (!formikProps.touched.url && validatedOnce) { formikProps.setFieldTouched('url', true) + setRepoMetadata(undefined) } return ( @@ -135,9 +151,22 @@ export const SelectRepository = () => { { - onChange('codeRepoUrl', repoMetadata?.url || '') - onChange('codeRepoType', repoMetadata?.repo_type || '') - onChange('codeRepoId', repoMetadata?.url || '') + setFormikState((prv: any) => { + const repoId = getRepoNameFromURL(repoMetadata?.url) + const hash = generateAlphaNumericHash(5)?.toLowerCase() + return { + ...prv, + values: { + ...prv.values, + id: `${repoId}${hash}`, + name: `${repoId} ${hash}`?.toLowerCase(), + code_repo_url: repoMetadata?.url || '', + code_repo_type: repoMetadata?.repo_type || '', + code_repo_id: repoId, + branch: repoMetadata?.branch + } + } + }) }} text={ { +export const getGitspaceDetailTitle = ({ + getString, + status, + loading, + redirectFrom, + actionError +}: { + getString: UseStringsReturn['getString'] + status?: EnumGitspaceStateType + loading?: boolean + redirectFrom?: string + actionError?: GetDataError | null +}) => { + if (loading) { + return getString('cde.details.fetchingGitspace') + } else if ( + status === GitspaceStatus.UNKNOWN || + (status === GitspaceStatus.STOPPED && !!redirectFrom && !actionError) + ) { + return getString('cde.details.provisioningGitspace') + } else if (status === GitspaceStatus.STOPPED) { + return getString('cde.details.gitspaceStopped') + } else if (status === GitspaceStatus.RUNNING) { + return getString('cde.details.gitspaceRunning') + } else if (!loading && isUndefined(status)) { + getString('cde.details.noData') + } +} + +export const GitspaceDetails = ({ + data, + error, + loading, + refetch, + mutate, + refetchLogs, + mutateLoading, + isfetchingInProgress, + actionError +}: { + data?: OpenapiGetGitspaceResponse | null + error?: GetDataError | null + loading?: boolean + mutateLoading?: boolean + isfetchingInProgress?: boolean + refetch: ( + options?: Partial, 'lazy'>> | undefined + ) => Promise + refetchLogs?: ( + options?: Partial, 'lazy'>> | undefined + ) => Promise + mutate: MutateMethod + actionError?: GetDataError | null +}) => { const { getString } = useStrings() - const { accountIdentifier, orgIdentifier, projectIdentifier } = useGetCDEAPIParams() as CDEPathParams const { gitspaceId } = useParams() + const { routes } = useAppContext() + const space = useGetSpaceParam() + const { showError } = useToaster() + const history = useHistory() + const { projectIdentifier } = useGetCDEAPIParams() as CDEPathParams + const { redirectFrom = '' } = useQueryParams<{ redirectFrom?: string }>() - const { data, loading, error, refetch } = useGetGitspace({ - accountIdentifier, - orgIdentifier, - projectIdentifier, - gitspaceIdentifier: gitspaceId || '' - }) - - const { config, status } = data || {} - - const { mutate } = useGitspaceAction({ - accountIdentifier, - orgIdentifier, - projectIdentifier, - gitspaceIdentifier: gitspaceId || '' - }) + const { config, status, url } = data || {} const openEditorLabel = config?.ide === IDEType.VSCODE ? getString('cde.details.openEditor') : getString('cde.details.openBrowser') + const color = getStatusColor(status as EnumGitspaceStateType) + return ( @@ -65,12 +122,11 @@ export const GitspaceDetails = () => { {error ? ( refetch()} message={getErrorMessage(error)} /> ) : ( - - {status === GitspaceStatus.UNKNOWN && getString('cde.details.provisioningGitspace')} - {status === GitspaceStatus.STOPPED && getString('cde.details.gitspaceStopped')} - {status === GitspaceStatus.RUNNING && getString('cde.details.gitspaceRunning')} - {loading && getString('cde.details.fetchingGitspace')} - {!loading && isUndefined(status) && getString('cde.details.noData')} + + {getGitspaceDetailTitle({ getString, status, loading, redirectFrom, actionError })} )} @@ -85,7 +141,7 @@ export const GitspaceDetails = () => { - + {config?.code_repo_id?.toUpperCase()} @@ -110,10 +166,27 @@ export const GitspaceDetails = () => { {status === GitspaceStatus.UNKNOWN && ( <> - - @@ -121,17 +194,46 @@ export const GitspaceDetails = () => { {status === GitspaceStatus.STOPPED && ( <> - - + )} {status === GitspaceStatus.RUNNING && ( <> - + - {getErrorMessage(error)} -