Skip to content

Commit

Permalink
Implement Create Branch (#78)
Browse files Browse the repository at this point in the history
* Update new icons

* Implement Create Branch
  • Loading branch information
tan-nhu committed Nov 17, 2022
1 parent 60f0ea9 commit 7116c2f
Show file tree
Hide file tree
Showing 35 changed files with 479 additions and 159 deletions.
2 changes: 1 addition & 1 deletion web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
"@emotion/core": "^10.0.28",
"@emotion/styled": "^10.0.27",
"@harness/design-system": "1.4.0",
"@harness/icons": "1.82.0",
"@harness/icons": "1.84.1",
"@harness/monaco-yaml": ">=1.0.0",
"@harness/ng-tooltip": ">=1.31.25",
"@harness/telemetry": ">=1.0.42",
Expand Down
1 change: 0 additions & 1 deletion web/src/AppProps.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import type React from 'react'
import type { SCMRoutes } from 'RouteDefinitions'
import type { Unknown } from 'utils/Utils'
import type { LangLocale } from './framework/strings/languageLoader'

/**
Expand Down
3 changes: 2 additions & 1 deletion web/src/components/CommitActions/CommitActions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import cx from 'classnames'
import { Container, Layout, Button, ButtonVariation, Utils, Text, Color } from '@harness/uicore'
import { useStrings } from 'framework/strings'
import css from './CommitActions.module.scss'
import { GitIcon } from 'utils/GitUtils'

interface CommitActionButtonProps {
sha: string
Expand Down Expand Up @@ -37,7 +38,7 @@ export function CommitActions({ sha, href, enableCopy }: CommitActionButtonProps
<Button
id={css.commitCopyButton}
variation={ButtonVariation.ICON}
icon={copied ? 'tick' : 'copy-alt'}
icon={copied ? 'tick' : GitIcon.CodeCopy}
iconProps={{ size: 14, color: copied ? Color.GREEN_500 : undefined }}
onClick={() => {
setCopied(true)
Expand Down
6 changes: 3 additions & 3 deletions web/src/components/CommitModalButton/CommitModalButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import { useMutate } from 'restful-react'
import { get } from 'lodash-es'
import { useModalHook } from '@harness/use-modal'
import { String, useStrings } from 'framework/strings'
import { DEFAULT_BRANCH_NAME, getErrorMessage, Unknown } from 'utils/Utils'
import { DEFAULT_BRANCH_NAME, getErrorMessage } from 'utils/Utils'
import type { TypesRepository, OpenapiCreateRepositoryRequest } from 'services/scm'
import { useAppContext } from 'AppContext'
import css from './CommitModalButton.module.scss'
Expand Down Expand Up @@ -97,10 +97,10 @@ export const CommitModalButton: React.FC<CommitModalButtonProps> = ({
onSubmit(response)
})
.catch(_error => {
showError(getErrorMessage(_error), 0, 'failedToCreateRepo')
showError(getErrorMessage(_error), 0, getString('failedToCreateRepo'))
})
} catch (exception) {
showError(getErrorMessage(exception), 0, 'failedToCreateRepo')
showError(getErrorMessage(exception), 0, getString('failedToCreateRepo'))
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
.main {
.title {
--typography-size: 20px !important;
}

.label {
color: var(--grey-600) !important;
font-size: var(--form-input-font-size) !important;
font-weight: 500 !important;
}

.branchSourceDesc {
color: var(--grey-400) !important;
font-size: var(--form-input-font-size) !important;
font-weight: 500 !important;
padding: var(--spacing-xsmall) 0 var(--spacing-small) !important;
}

.divider {
margin: var(--spacing-medium) 0 var(--spacing-large) 0 !important;
}
}

.branchDropdown {
background-color: var(--white);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/* eslint-disable */
// this is an auto-generated file
declare const styles: {
readonly main: string
readonly title: string
readonly label: string
readonly branchSourceDesc: string
readonly divider: string
readonly branchDropdown: string
}
export default styles
219 changes: 219 additions & 0 deletions web/src/components/CreateBranchModalButton/CreateBranchModalButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
/*
* Copyright 2021 Harness Inc. All rights reserved.
* Use of this source code is governed by the PolyForm Shield 1.0.0 license
* that can be found in the licenses directory at the root of this repository, also available at
* https://polyformproject.org/wp-content/uploads/2020/06/PolyForm-Shield-1.0.0.txt.
*/

import React, { ChangeEvent, useEffect, useState } from 'react'
import { Dialog, Intent } from '@blueprintjs/core'
import * as yup from 'yup'
import {
Button,
ButtonProps,
Container,
Layout,
FlexExpander,
Icon,
Formik,
FormikForm,
Heading,
useToaster,
FormInput,
Label,
DropDown,
SelectOption,
Text
} from '@harness/uicore'
import { Color, FontVariation } from '@harness/design-system'
import { useGet, useMutate } from 'restful-react'
import { get } from 'lodash-es'
import { useModalHook } from '@harness/use-modal'
import { useStrings } from 'framework/strings'
import { BRANCH_PER_PAGE, getErrorMessage } from 'utils/Utils'
import { GitIcon, GitInfoProps, isGitBranchNameValid } from 'utils/GitUtils'
import type { RepoBranch } from 'services/scm'
import css from './CreateBranchModalButton.module.scss'

interface FormData {
name: string
sourceBranch: string
}

export interface CreateBranchModalButtonProps extends Omit<ButtonProps, 'onClick'>, Pick<GitInfoProps, 'repoMetadata'> {
onSuccess: (data: RepoBranch) => void
showSuccessMessage?: boolean
}

export const CreateBranchModalButton: React.FC<CreateBranchModalButtonProps> = ({
onSuccess,
repoMetadata,
showSuccessMessage,
...props
}) => {
const ModalComponent: React.FC = () => {
const { getString } = useStrings()
const [sourceBranch, setSourceBranch] = useState(repoMetadata.defaultBranch as string)
const { showError, showSuccess } = useToaster()
const { mutate: createBranch, loading } = useMutate<RepoBranch>({
verb: 'POST',
path: `/api/v1/repos/${repoMetadata.path}/+/branches`
})
const handleSubmit = (formData?: Unknown): void => {
const name = get(formData, 'name').trim()
try {
createBranch({
name,
target: sourceBranch
})
.then(response => {
hideModal()
onSuccess(response)
if (showSuccessMessage) {
showSuccess(getString('branchCreated').replace('__branch__', name), 5000)
}
})
.catch(_error => {
showError(getErrorMessage(_error), 0, getString('failedToCreateBranch'))
})
} catch (exception) {
showError(getErrorMessage(exception), 0, getString('failedToCreateBranch'))
}
}

return (
<Dialog
isOpen
enforceFocus={false}
onClose={hideModal}
title={''}
style={{ width: 700, maxHeight: '95vh', overflow: 'auto' }}>
<Layout.Vertical padding={{ left: 'xxlarge' }} style={{ height: '100%' }} className={css.main}>
<Heading className={css.title} font={{ variation: FontVariation.H3 }} margin={{ bottom: 'xlarge' }}>
<Icon name={GitIcon.CodeBranch} size={22} /> {getString('createABranch')}
</Heading>
<Container margin={{ right: 'xxlarge' }}>
<Formik<FormData>
initialValues={{
name: '',
sourceBranch: ''
}}
formName="createGitBranch"
enableReinitialize={true}
validationSchema={yup.object().shape({
name: yup
.string()
.trim()
.required()
.test('valid-branch-name', getString('validation.gitBranchNameInvalid'), value => {
const val = value || ''
return !!val && isGitBranchNameValid(val)
})
})}
validateOnChange
validateOnBlur
onSubmit={handleSubmit}>
<FormikForm>
<FormInput.Text
name="name"
label={getString('branchName')}
placeholder={getString('nameYourBranch')}
tooltipProps={{
dataTooltipId: 'repositoryBranchTextField'
}}
inputGroup={{ autoFocus: true }}
/>
<Container margin={{ top: 'medium', bottom: 'medium' }}>
<Label className={css.label}>{getString('branchSource')}</Label>
<Text className={css.branchSourceDesc}>{getString('branchSourceDesc')}</Text>
<Layout.Horizontal spacing="medium" padding={{ top: 'xsmall' }}>
<BranchDropdown
repoMetadata={repoMetadata}
currentBranchName={sourceBranch}
onSelect={name => setSourceBranch(name)}
/>
<FlexExpander />
</Layout.Horizontal>
</Container>

<Layout.Horizontal
spacing="small"
padding={{ right: 'xxlarge', top: 'xxxlarge', bottom: 'large' }}
style={{ alignItems: 'center' }}>
<Button type="submit" text={getString('createBranch')} intent={Intent.PRIMARY} disabled={loading} />
<Button text={getString('cancel')} minimal onClick={hideModal} />
<FlexExpander />

{loading && <Icon intent={Intent.PRIMARY} name="spinner" size={16} />}
</Layout.Horizontal>
</FormikForm>
</Formik>
</Container>
</Layout.Vertical>
</Dialog>
)
}

const [openModal, hideModal] = useModalHook(ModalComponent, [onSuccess])

return <Button onClick={openModal} {...props} />
}

interface BranchDropdownProps extends Pick<GitInfoProps, 'repoMetadata'> {
currentBranchName: string
onSelect: (branchName: string) => void
}

const BranchDropdown: React.FC<BranchDropdownProps> = ({ repoMetadata, onSelect }) => {
const { getString } = useStrings()
const [activeBranch, setActiveBranch] = useState(repoMetadata.defaultBranch)
const [query, setQuery] = useState('')
const [branches, setBranches] = useState<SelectOption[]>([])
const { data, loading } = useGet<RepoBranch[]>({
path: `/api/v1/repos/${repoMetadata.path}/+/branches`,
queryParams: { sort: 'date', direction: 'desc', per_page: BRANCH_PER_PAGE, page: 1, query }
})

useEffect(() => {
if (data?.length) {
setBranches(
data
.map(e => e.name)
.map(_branch => ({
label: _branch,
value: _branch
})) as SelectOption[]
)
}
}, [data])

return (
<DropDown
icon={GitIcon.CodeBranch}
value={activeBranch}
items={branches}
{...{
inputProps: {
leftElement: <Icon name={loading ? 'steps-spinner' : 'thinner-search'} size={12} color={Color.GREY_500} />,
placeholder: getString('searchBranch'),
onInput: (event: ChangeEvent<HTMLInputElement>) => {
if (event.target.value !== query) {
setQuery(event.target.value)
}
},
onBlur: (event: ChangeEvent<HTMLInputElement>) => {
setTimeout(() => {
setQuery(event.target.value || '')
}, 250)
}
}
}}
onChange={({ value: switchBranch }) => {
setActiveBranch(switchBranch as string)
onSelect(switchBranch as string)
}}
popoverClassName={css.branchDropdown}
usePortal
/>
)
}
2 changes: 1 addition & 1 deletion web/src/components/LatestCommit/LatestCommit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ export function LatestCommitForFile({
<FlexExpander />
<Button
size={ButtonSize.SMALL}
icon={GitIcon.HISTORY}
icon={GitIcon.CodeHistory}
text={getString('history')}
variation={ButtonVariation.PRIMARY}
/>
Expand Down
12 changes: 3 additions & 9 deletions web/src/components/NewRepoModalButton/NewRepoModalButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,7 @@ import { useGet, useMutate } from 'restful-react'
import { get } from 'lodash-es'
import { useModalHook } from '@harness/use-modal'
import { useStrings } from 'framework/strings'
import {
DEFAULT_BRANCH_NAME,
getErrorMessage,
REGEX_VALID_REPO_NAME,
SUGGESTED_BRANCH_NAMES,
Unknown
} from 'utils/Utils'
import { DEFAULT_BRANCH_NAME, getErrorMessage, REGEX_VALID_REPO_NAME, SUGGESTED_BRANCH_NAMES } from 'utils/Utils'
import { isGitBranchNameValid } from 'utils/GitUtils'
import type { TypesRepository, OpenapiCreateRepositoryRequest } from 'services/scm'
import { useAppContext } from 'AppContext'
Expand Down Expand Up @@ -130,10 +124,10 @@ export const NewRepoModalButton: React.FC<NewRepoModalButtonProps> = ({
onSubmit(response)
})
.catch(_error => {
showError(getErrorMessage(_error), 0, 'failedToCreateRepo')
showError(getErrorMessage(_error), 0, getString('failedToCreateRepo'))
})
} catch (exception) {
showError(getErrorMessage(exception), 0, 'failedToCreateRepo')
showError(getErrorMessage(exception), 0, getString('failedToCreateRepo'))
}
}

Expand Down
Loading

0 comments on commit 7116c2f

Please sign in to comment.