feat: create repo from gitspace (#2157)

This commit is contained in:
Deepesh Kumar 2024-07-02 17:19:48 +00:00 committed by Harness
parent ceb86704be
commit 37cce5c8a4
28 changed files with 3205 additions and 34 deletions

View File

@ -52,8 +52,8 @@ import AddUpdatePipeline from 'pages/AddUpdatePipeline/AddUpdatePipeline'
import { useAppContext } from 'AppContext'
import PipelineSettings from 'components/PipelineSettings/PipelineSettings'
import GitspaceDetail from 'cde/pages/GitspaceDetail/GitspaceDetail'
import Gitspaces from 'cde/pages/Gitspaces/Gitspaces'
import GitspacesListing from 'cde/pages/GitspacesListing/GitspacesListing'
import { GitspaceListing } from 'cde-gitness/pages/GitspaceListing/GitspaceListing'
import { GitspaceCreate } from 'cde-gitness/pages/GitspaceCreate/GitspaceCreate'
export const RouteDestinations: React.FC = React.memo(function RouteDestinations() {
const { getString } = useStrings()
@ -267,7 +267,15 @@ export const RouteDestinations: React.FC = React.memo(function RouteDestinations
{standalone && (
<Route exact path={routes.toCDEGitspacesEdit({ space: pathProps.space, gitspaceId: pathProps.gitspaceId })}>
<LayoutWithSideNav title={getString('cde.gitspaces')}>
<Gitspaces />
<GitspaceCreate />
</LayoutWithSideNav>
</Route>
)}
{standalone && (
<Route path={routes.toCDEGitspacesCreate({ space: pathProps.space })}>
<LayoutWithSideNav title={getString('cde.gitspaces')}>
<GitspaceCreate />
</LayoutWithSideNav>
</Route>
)}
@ -283,15 +291,7 @@ export const RouteDestinations: React.FC = React.memo(function RouteDestinations
{standalone && (
<Route path={routes.toCDEGitspaces({ space: pathProps.space })}>
<LayoutWithSideNav title={getString('cde.gitspaces')}>
<Gitspaces />
</LayoutWithSideNav>
</Route>
)}
{standalone && (
<Route path={routes.toCDEGitspacesCreate({ space: pathProps.space })}>
<LayoutWithSideNav title={getString('cde.gitspaces')}>
<GitspacesListing />
<GitspaceListing />
</LayoutWithSideNav>
</Route>
)}

View File

@ -0,0 +1,3 @@
.repoAndBranch {
margin-bottom: 0 !important;
}

View File

@ -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 repoAndBranch: string

View File

@ -0,0 +1,226 @@
import React, { useEffect, useState } from 'react'
import { useGet } from 'restful-react'
import { Container, ExpandingSearchInput, Layout, Text } from '@harnessio/uicore'
import { Menu, MenuItem } from '@blueprintjs/core'
import { Color } from '@harnessio/design-system'
import { Icon } from '@harnessio/icons'
import { useFormikContext } from 'formik'
import type { TypesRepository } from 'services/code'
import { useGetSpaceParam } from 'hooks/useGetSpaceParam'
import { String, useStrings } from 'framework/strings'
import { LIST_FETCHING_LIMIT } from 'utils/Utils'
import NewRepoModalButton from 'components/NewRepoModalButton/NewRepoModalButton'
import { GitspaceSelect } from '../../../cde/components/GitspaceSelect/GitspaceSelect'
import gitnessRepoLogo from './gitness.svg?url'
import css from './GitnessRepoImportForm.module.scss'
const RepositoryText = ({ repoList, value }: { repoList: TypesRepository[] | null; value?: string }) => {
const { getString } = useStrings()
const repoMetadata = repoList?.find(repo => repo.git_url === value)
const repoName = repoMetadata?.path
return (
<Layout.Horizontal spacing={'medium'} flex={{ justifyContent: 'flex-start', alignItems: 'center' }}>
<img src={gitnessRepoLogo} height={24} width={24} />
{repoName ? (
<Container margin={{ left: 'medium' }}>
<Layout.Vertical spacing="xsmall">
<Text font={'normal'}>{getString('cde.repository.repo')}</Text>
<Text color={Color.BLACK} font={'small'} lineClamp={1}>
{repoName || getString('cde.repository.repositoryURL')}
</Text>
</Layout.Vertical>
</Container>
) : (
<Text font={'normal'}>{getString('cde.repository.selectRepository')}</Text>
)}
</Layout.Horizontal>
)
}
const BranchText = ({ value }: { value?: string }) => {
const { getString } = useStrings()
return (
<Layout.Horizontal spacing={'medium'} flex={{ justifyContent: 'flex-start', alignItems: 'center' }}>
<Icon name={'git-branch'} size={24} />
{value ? (
<Container margin={{ left: 'medium' }}>
<Layout.Vertical spacing="xsmall">
<Text font={'normal'}>{getString('branch')}</Text>
<Text color={Color.BLACK} font={'small'} lineClamp={1}>
{value}
</Text>
</Layout.Vertical>
</Container>
) : (
<Text font={'normal'}>{getString('cde.create.selectBranchPlaceholder')}</Text>
)}
</Layout.Horizontal>
)
}
export const GitnessRepoImportForm = () => {
const { getString } = useStrings()
const space = useGetSpaceParam()
const [branchSearch, setBranchSearch] = useState('')
const [repoSearch, setRepoSearch] = useState('')
const [repoRef, setReporef] = useState('')
const {
data: repositories,
loading,
refetch: refetchRepos
} = useGet<TypesRepository[]>({
path: `/api/v1/spaces/${space}/+/repos`,
queryParams: { query: repoSearch },
debounce: 500
})
const {
data: branches,
refetch,
loading: loadingBranches
} = useGet<{ name: string }[]>({
path: `/api/v1/repos/${repoRef}/+/branches`,
queryParams: {
limit: LIST_FETCHING_LIMIT,
page: 1,
sort: 'date',
order: 'desc',
include_commit: false,
query: branchSearch
},
lazy: true
})
useEffect(() => {
if (repoRef || branchSearch) {
refetch()
}
}, [repoRef, branchSearch])
const repoListOptions = repositories || []
const formik = useFormikContext<any>()
const { values } = formik
const repoMetadata = repoListOptions.find(repo => repo.git_url === values.code_repo_url)
if (repoRef !== repoMetadata?.path) {
setReporef(repoMetadata?.path as string)
}
return (
<Container flex={{ justifyContent: 'space-between', alignItems: 'baseline' }}>
<Container width={'63%'}>
<GitspaceSelect
loading={loading}
formikName="code_repo_url"
formInputClassName={css.repoAndBranch}
text={<RepositoryText value={values.code_repo_url} repoList={repositories} />}
tooltipProps={{
onClose: () => {
setRepoSearch('')
}
}}
renderMenu={
<Menu>
<Container margin={'small'}>
<ExpandingSearchInput
placeholder={getString('cde.create.searchRepositoryPlaceholder')}
alwaysExpanded
autoFocus={false}
defaultValue={repoSearch}
onChange={setRepoSearch}
/>
</Container>
{loading ? (
<MenuItem disabled text={getString('loading')} />
) : repoListOptions?.length ? (
repoListOptions.map(repo => (
<MenuItem
key={repo.path}
text={
<Layout.Horizontal spacing="medium">
<img src={gitnessRepoLogo} height={16} width={16} />
<Text>{repo.path}</Text>
</Layout.Horizontal>
}
active={repo.git_url === values.code_repo_url}
onClick={() => {
formik.setValues((prvValues: any) => {
return {
...prvValues,
code_repo_url: repo.git_url,
id: repo.path,
name: repo.path
}
})
formik.setFieldValue('code_repo_url', repo.git_url)
}}
/>
))
) : (
<Container>
<NewRepoModalButton
space={space}
newRepoModalOnly
notFoundRepoName={repoSearch}
modalTitle={getString('createRepo')}
onSubmit={() => {
refetchRepos()
}}
/>
</Container>
)}
</Menu>
}
/>
</Container>
<Container width={'35%'}>
<GitspaceSelect
formikName="branch"
loading={loadingBranches}
disabled={!values.code_repo_url}
formInputClassName={css.repoAndBranch}
text={<BranchText value={values.branch} />}
tooltipProps={{
onClose: () => {
setBranchSearch('')
}
}}
renderMenu={
<Menu>
<Container margin={'small'}>
<ExpandingSearchInput
placeholder={getString('cde.create.searchBranchPlaceholder')}
alwaysExpanded
autoFocus={false}
defaultValue={branchSearch}
onChange={setBranchSearch}
/>
</Container>
{loadingBranches ? (
<MenuItem disabled text={getString('loading')} />
) : branches?.length ? (
branches?.map(branch => (
<MenuItem
key={branch.name}
icon="git-branch"
text={branch.name}
active={branch.name === values.branch}
onClick={() => formik.setFieldValue('branch', branch.name)}
/>
))
) : (
<MenuItem
icon="warning-sign"
text={<String stringID="branchNotFound" vars={{ branch: branchSearch }} useRichText />}
/>
)}
</Menu>
}
/>
</Container>
</Container>
)
}

View File

@ -0,0 +1 @@
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 25"><path fill-rule="evenodd" clip-rule="evenodd" d="M12 .482c-6.627 0-12 5.373-12 12s5.373 12 12 12h2.4a1.6 1.6 0 0 0 1.6-1.6v-2.4a4 4 0 0 1 4-4h-4 6.4a1.6 1.6 0 0 0 1.6-1.6v-4.8a1.6 1.6 0 0 0-1.6-1.6h-4.8a1.6 1.6 0 0 0-1.6 1.6v2.4a4 4 0 1 1-4-4h2.4a1.6 1.6 0 0 0 1.6-1.6v-4.8a1.6 1.6 0 0 0-1.6-1.6H12Z" fill="#1B2E49"/></svg>

After

Width:  |  Height:  |  Size: 395 B

View File

@ -0,0 +1,55 @@
/*
* 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.
*/
.table {
margin-top: var(--spacing-large);
div[class*='TableV2--cell'] {
width: auto !important;
}
div[class*='TableV2--cells'],
div[class*='TableV2--header'] {
display: grid !important;
grid-template-columns: 1fr 1fr 1fr 0.7fr 50px;
}
}
.popover {
> div[class*='popover-arrow'] {
display: none;
}
.listContainer {
:global {
a.bp3-menu-item:hover,
.bp3-active {
background: var(--primary-1) !important;
color: var(--grey-1000) !important;
}
}
}
}
.gitspaceURL {
white-space: nowrap !important;
overflow: hidden;
text-overflow: ellipsis;
}
.repositoryCell {
width: fit-content !important;
}

View File

@ -0,0 +1,23 @@
/*
* 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 gitspaceUrl: string
export declare const listContainer: string
export declare const popover: string
export declare const repositoryCell: string
export declare const table: string

View File

@ -0,0 +1,505 @@
/*
* 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.
*/
import { Container, Layout, TableV2, Text, useToaster } from '@harnessio/uicore'
import React from 'react'
import { Color } from '@harnessio/design-system'
import type { Renderer, CellProps } from 'react-table'
import ReactTimeago from 'react-timeago'
import {
Circle,
GitBranch,
Cpu,
Clock,
Play,
Square,
Db,
ModernTv,
OpenInBrowser,
DeleteCircle,
EditPencil,
ViewColumns2,
GithubCircle,
GitLabFull,
Code,
Bitbucket as BitbucketIcon
} from 'iconoir-react'
import { Menu, MenuItem, PopoverInteractionKind, Position } from '@blueprintjs/core'
import { useHistory } from 'react-router-dom'
import { isNil } from 'lodash-es'
import { UseStringsReturn, useStrings } from 'framework/strings'
import { useAppContext } from 'AppContext'
import { getErrorMessage } from 'utils/Utils'
import { useConfirmAct } from 'hooks/useConfirmAction'
import VSCode from 'cde/icons/VSCode.svg?url'
import { GitspaceStatus } from 'cde/constants'
import {
EnumGitspaceStateType,
EnumIDEType,
useDeleteGitspace,
type TypesGitspaceConfig,
type EnumCodeRepoType
} from 'cde-gitness/services'
import css from './ListGitspaces.module.scss'
enum CodeRepoType {
Github = 'github',
Gitlab = 'gitlab',
HarnessCode = 'harness_code',
Bitbucket = 'bitbucket',
Unknown = 'unknown'
}
const getIconByRepoType = ({ repoType }: { repoType?: EnumCodeRepoType }): React.ReactNode => {
switch (repoType) {
case CodeRepoType.Github:
return <GithubCircle height={40} />
case CodeRepoType.Gitlab:
return <GitLabFull height={40} />
case CodeRepoType.Bitbucket:
return <BitbucketIcon height={40} />
default:
case CodeRepoType.Unknown:
case CodeRepoType.HarnessCode:
return <Code height={40} />
}
}
export const getStatusColor = (status?: EnumGitspaceStateType) => {
switch (status) {
case GitspaceStatus.RUNNING:
return '#42AB45'
case GitspaceStatus.STOPPED:
return '#F3F3FA'
case GitspaceStatus.ERROR:
return '#FF0000'
default:
return '#000000'
}
}
export const getStatusText = (getString: UseStringsReturn['getString'], status?: EnumGitspaceStateType) => {
switch (status) {
case GitspaceStatus.RUNNING:
return getString('cde.listing.online')
case GitspaceStatus.STOPPED:
return getString('cde.listing.offline')
case GitspaceStatus.ERROR:
return getString('cde.listing.error')
default:
return getString('cde.listing.offline')
}
}
enum IDEType {
VSCODE = 'vs_code',
VSCODEWEB = 'vs_code_web'
}
const getUsageTemplate = (
getString: UseStringsReturn['getString'],
icon: React.ReactNode,
resource_usage?: string,
total_time_used?: number
): React.ReactElement | null => {
return (
<Layout.Horizontal spacing={'small'} flex={{ alignItems: 'center', justifyContent: 'start' }}>
{icon}
<Text color={Color.GREY_500} font={{ align: 'left', size: 'normal' }}>
{getString('cde.used')} {resource_usage || 0}
</Text>
<Text>/</Text>
<Text color={Color.GREY_500} font={{ align: 'left', size: 'normal' }}>
{total_time_used || 0} {getString('cde.hours')}
</Text>
</Layout.Horizontal>
)
}
export const RenderGitspaceName: Renderer<CellProps<TypesGitspaceConfig>> = ({ row }) => {
const details = row.original
const { name } = details
return (
<Layout.Horizontal spacing={'small'} flex={{ alignItems: 'center', justifyContent: 'start' }}>
<img src={VSCode} height={20} width={20} />
<Text color={Color.BLACK} title={name} font={{ align: 'left', size: 'normal', weight: 'semi-bold' }}>
{name}
</Text>
</Layout.Horizontal>
)
}
export const RenderRepository: Renderer<CellProps<TypesGitspaceConfig>> = ({ row }) => {
const { getString } = useStrings()
const details = row.original
const { name, branch, code_repo_url, code_repo_type, instance } = details || {}
return (
<Layout.Vertical>
<Layout.Horizontal
spacing={'small'}
className={css.repositoryCell}
flex={{ alignItems: 'center', justifyContent: 'start' }}
onClick={e => {
e.preventDefault()
e.stopPropagation()
window.open(code_repo_url, '_blank')
}}>
{getIconByRepoType({ repoType: code_repo_type })}
<Text className={css.gitspaceUrl} color={Color.PRIMARY_7} title={name} font={{ align: 'left', size: 'normal' }}>
{name}
</Text>
<Text color={Color.PRIMARY_7}>:</Text>
<GitBranch />
<Text color={Color.PRIMARY_7} title={name} font={{ align: 'left', size: 'normal' }}>
{branch}
</Text>
</Layout.Horizontal>
{(isNil(instance?.tracked_changes) || instance?.tracked_changes === '') && (
<Text color={Color.GREY_300} font={{ align: 'left', size: 'small', weight: 'semi-bold' }}>
{getString('cde.noChange')}
</Text>
)}
</Layout.Vertical>
)
}
export const RenderCPUUsage: Renderer<CellProps<TypesGitspaceConfig>> = ({ row }) => {
const { getString } = useStrings()
const instance = row.original.instance
const { resource_usage, total_time_used } = instance || {}
return getUsageTemplate(getString, <Cpu />, resource_usage, total_time_used)
}
export const RenderStorageUsage: Renderer<CellProps<TypesGitspaceConfig>> = ({ row }) => {
const { getString } = useStrings()
const instance = row.original.instance
const { resource_usage, total_time_used } = instance || {}
return getUsageTemplate(getString, <Db />, resource_usage, total_time_used)
}
export const RenderLastActivity: Renderer<CellProps<TypesGitspaceConfig>> = ({ row }) => {
const { getString } = useStrings()
const instance = row.original.instance
const { last_used } = instance || {}
return (
<Layout.Horizontal spacing={'small'} flex={{ alignItems: 'center', justifyContent: 'start' }}>
<Clock />
{last_used ? (
<ReactTimeago date={last_used} />
) : (
<Text color={Color.GREY_500} font={{ align: 'left', size: 'normal' }}>
{getString('cde.na')}
</Text>
)}
</Layout.Horizontal>
)
}
export const RenderGitspaceStatus: Renderer<CellProps<TypesGitspaceConfig>> = ({ row }) => {
const { getString } = useStrings()
const details = row.original
const { instance, name } = details
const { state } = instance || {}
const color = getStatusColor(state)
return (
<Layout.Horizontal spacing={'small'} flex={{ alignItems: 'center', justifyContent: 'start' }}>
<Circle height={10} width={10} color={color} fill={color} />
<Text color={Color.BLACK} title={name} font={{ align: 'left', size: 'normal', weight: 'semi-bold' }}>
{getStatusText(getString, state)}
</Text>
</Layout.Horizontal>
)
}
export const StartStopButton = ({ state, loading }: { state?: EnumGitspaceStateType; loading?: boolean }) => {
const { getString } = useStrings()
return (
<Layout.Horizontal spacing="small" flex={{ alignItems: 'center', justifyContent: 'flex-start' }}>
{loading ? <></> : state === GitspaceStatus.RUNNING ? <Square /> : <Play />}
<Text icon={loading ? 'loading' : undefined}>
{state === GitspaceStatus.RUNNING
? getString('cde.details.stopGitspace')
: getString('cde.details.startGitspace')}
</Text>
</Layout.Horizontal>
)
}
export const OpenGitspaceButton = ({ ide }: { ide?: EnumIDEType }) => {
const { getString } = useStrings()
return (
<Layout.Horizontal spacing="small" flex={{ alignItems: 'center', justifyContent: 'flex-start' }}>
{ide === IDEType.VSCODE ? <ModernTv /> : <OpenInBrowser />}
<Text>{ide === IDEType.VSCODE ? getString('cde.ide.openVSCode') : getString('cde.ide.openBrowser')}</Text>
</Layout.Horizontal>
)
}
interface ActionMenuProps {
data: TypesGitspaceConfig
refreshList: () => void
handleStartStop?: () => Promise<void>
loading?: boolean
actionLoading?: boolean
deleteLoading?: boolean
deleteGitspace: (e: React.MouseEvent<HTMLDivElement, MouseEvent>) => Promise<void>
}
const ActionMenu = ({
data,
deleteGitspace,
refreshList,
handleStartStop,
actionLoading,
deleteLoading
}: ActionMenuProps) => {
const { getString } = useStrings()
const { showError } = useToaster()
const { instance, ide } = data
const { id, state, url = ' ' } = instance || {}
const history = useHistory()
const { routes } = useAppContext()
const pathparamsList = instance?.space_path?.split('/') || []
const projectIdentifier = pathparamsList[pathparamsList.length - 1] || ''
return (
<Container
className={css.listContainer}
onClick={e => {
e.preventDefault()
e.stopPropagation()
}}>
<Menu>
<MenuItem
onClick={() => {
history.push(
routes.toCDEGitspaceDetail({
space: instance?.space_path || '',
gitspaceId: instance?.id || ''
})
)
}}
text={
<Layout.Horizontal spacing="small" flex={{ alignItems: 'center', justifyContent: 'flex-start' }}>
<ViewColumns2 />
<Text>{getString('cde.viewGitspace')}</Text>
</Layout.Horizontal>
}
/>
<MenuItem
onClick={() => {
history.push(
routes.toCDEGitspacesEdit({
space: instance?.space_path || '',
gitspaceId: instance?.id || ''
})
)
}}
text={
<Layout.Horizontal spacing="small" flex={{ alignItems: 'center', justifyContent: 'flex-start' }}>
<EditPencil />
<Text>{getString('cde.editGitspace')}</Text>
</Layout.Horizontal>
}
/>
<MenuItem
onClick={async e => {
try {
if (!actionLoading) {
e.preventDefault()
e.stopPropagation()
await handleStartStop?.()
await refreshList()
}
} catch (error) {
showError(getErrorMessage(error))
}
}}
text={
<Layout.Horizontal spacing="small">
<StartStopButton state={state} loading={actionLoading} />
</Layout.Horizontal>
}
/>
{ide && state == GitspaceStatus.RUNNING && (
<MenuItem
onClick={e => {
e.preventDefault()
e.stopPropagation()
if (ide === IDEType.VSCODE) {
window.open(`vscode://harness-inc.gitspaces/${projectIdentifier}/${id}`, '_blank')
} else {
window.open(url, '_blank')
}
}}
text={
<Layout.Horizontal spacing="small">
<OpenGitspaceButton ide={ide} />
</Layout.Horizontal>
}
/>
)}
<MenuItem
onClick={deleteGitspace as Unknown as () => void}
text={
<Layout.Horizontal spacing="small" flex={{ alignItems: 'center', justifyContent: 'flex-start' }}>
{deleteLoading ? <></> : <DeleteCircle />}
<Text icon={deleteLoading ? 'loading' : undefined}>{getString('cde.deleteGitspace')}</Text>
</Layout.Horizontal>
}
/>
</Menu>
</Container>
)
}
interface RenderActionsProps extends CellProps<TypesGitspaceConfig> {
refreshList: () => void
}
export const RenderActions = ({ row, refreshList }: RenderActionsProps) => {
const { getString } = useStrings()
const { showError, showSuccess } = useToaster()
const details = row.original
const { instance, name } = details
const { mutate: deleteGitspace, loading: deleteLoading } = useDeleteGitspace({})
// To be added in BE later.
// const { mutate: actionGitspace, loading: actionLoading } = useGitspaceAction({
// accountIdentifier,
// projectIdentifier,
// orgIdentifier,
// gitspaceIdentifier: instance?.id || ''
// })
// const handleStartStop = async () => {
// return await actionGitspace({
// action: instance?.state === GitspaceStatus.RUNNING ? GitspaceActionType.STOP : GitspaceActionType.START
// })
// }
const confirmDelete = useConfirmAct()
const handleDelete = async (e: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
confirmDelete({
title: getString('cde.deleteGitspaceTitle'),
message: getString('cde.deleteGitspaceText', { name: name }),
action: async () => {
try {
e.preventDefault()
e.stopPropagation()
await deleteGitspace(instance?.id || '')
showSuccess(getString('cde.deleteSuccess'))
await refreshList()
} catch (exception) {
showError(getErrorMessage(exception))
}
}
})
}
return (
<Text
onClick={e => {
e.preventDefault()
e.stopPropagation()
}}
style={{ cursor: 'pointer' }}
icon={deleteLoading || false ? 'steps-spinner' : 'Options'}
tooltip={
<ActionMenu
data={details}
actionLoading={false}
deleteLoading={deleteLoading}
deleteGitspace={handleDelete}
refreshList={refreshList}
/>
}
tooltipProps={{
interactionKind: PopoverInteractionKind.HOVER,
position: Position.BOTTOM_RIGHT,
usePortal: true,
popoverClassName: css.popover
}}
/>
)
}
export const ListGitspaces = ({ data, refreshList }: { data: TypesGitspaceConfig[]; refreshList: () => void }) => {
const history = useHistory()
const { getString } = useStrings()
const { routes } = useAppContext()
return (
<Container>
{data && (
<TableV2<TypesGitspaceConfig>
className={css.table}
onRowClick={row => {
const pathparamsList = row?.instance?.space_path?.split('/') || []
const projectIdentifier = pathparamsList[pathparamsList.length - 1] || ''
if (row?.instance?.state === GitspaceStatus.RUNNING) {
if (row?.ide === IDEType.VSCODE) {
window.open(`vscode://harness-inc.gitspaces/${projectIdentifier}/${row?.instance?.id}`, '_blank')
} else {
window.open(row?.instance.url, '_blank')
}
} else {
history.push(
routes.toCDEGitspaceDetail({
space: row?.instance?.space_path as string,
gitspaceId: row?.instance?.id as string
})
)
}
}}
columns={[
{
id: 'gitspaces',
Header: getString('cde.gitspaces'),
Cell: RenderGitspaceName
},
{
id: 'repository',
Header: getString('cde.repositoryAndBranch'),
Cell: RenderRepository
},
{
id: 'status',
Header: getString('cde.status'),
Cell: RenderGitspaceStatus
},
{
id: 'lastactivity',
Header: getString('cde.sessionDuration'),
Cell: RenderLastActivity
},
{
id: 'action',
Cell: (props: RenderActionsProps) => <RenderActions {...props} refreshList={refreshList} />
}
]}
data={data}
/>
)}
</Container>
)
}

View File

@ -0,0 +1,30 @@
.split-button {
display: flex;
border: 1px solid var(--grey-200);
border-radius: 4px;
overflow: hidden;
}
.split-button p {
flex-wrap: wrap;
padding: 4px 8px !important;
border: none;
align-self: center;
color: #1f2937 !important;
cursor: pointer;
font-size: 12px !important;
transition: background-color 0.3s;
&:first-child {
border-right: 1px solid var(--grey-200);
}
}
.split-button p.active {
background-color: var(--primary-1) !important;
color: var(--primary-8) !important;
}
.split-button p:hover {
background-color: var(--white);
}

View File

@ -0,0 +1,20 @@
/*
* 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 active: string
export declare const splitButton: string

View File

@ -0,0 +1,61 @@
import React, { useState } from 'react'
import { Container, Text } from '@harnessio/uicore'
import { useStrings } from 'framework/strings'
import { useConfirmAct } from 'hooks/useConfirmAction'
import css from './RepositoryTypeButton.module.scss'
export enum RepositoryType {
GITNESS = 'gitness',
THIRDPARTY = 'thirdParty'
}
const RepositoryTypeButton = ({
hasChange,
onChange
}: {
hasChange?: boolean
onChange: (type: RepositoryType) => void
}) => {
const { getString } = useStrings()
const confirmSwitch = useConfirmAct()
const [activeButton, setActiveButton] = useState(RepositoryType.GITNESS)
const handleSwitch = (type: RepositoryType) => {
const onConfirm = () => {
setActiveButton(type)
onChange(type)
}
if (hasChange) {
confirmSwitch({
title: getString('cde.create.unsaved.title'),
message: getString('cde.create.unsaved.message'),
action: async () => {
onConfirm()
}
})
} else {
onConfirm()
}
}
return (
<Container className={css.splitButton}>
<Text
className={activeButton === RepositoryType.GITNESS ? css.active : ''}
onClick={() => {
handleSwitch(RepositoryType.GITNESS)
}}>
{getString('cde.create.gitnessRepositories')}
</Text>
<Text
className={activeButton === RepositoryType.THIRDPARTY ? css.active : ''}
onClick={() => {
handleSwitch(RepositoryType.THIRDPARTY)
}}>
{getString('cde.create.thirdPartyGitRepositories')}
</Text>
</Container>
)
}
export default RepositoryTypeButton

View File

@ -0,0 +1,12 @@
.hideContainer {
:global {
--bp3-intent-color: unset !important;
.bp3-form-helper-text {
margin-top: unset !important;
}
}
}
.repoAndBranch {
width: 47% !important;
}

View File

@ -0,0 +1,20 @@
/*
* 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 hideContainer: string
export declare const repoAndBranch: string

View File

@ -0,0 +1,125 @@
import { FormInput, FormikForm, Layout } from '@harnessio/uicore'
import React, { useState } from 'react'
import { useFormikContext } from 'formik'
import { useStrings } from 'framework/strings'
import { GitProviders, ImportFormData, getOrgLabel, getOrgPlaceholder, getProviders } from 'utils/GitUtils'
import css from './ThirdPartyRepoImportForm.module.scss'
export interface ThirdPartyRepoImportFormProps extends ImportFormData {
branch: string
ide: string
id: string
}
export const ThirdPartyRepoImportForm = () => {
const [auth, setAuth] = useState(false)
const { getString } = useStrings()
const { values, setFieldValue, validateField } = useFormikContext<ThirdPartyRepoImportFormProps>()
return (
<FormikForm>
<FormInput.Select name={'gitProvider'} label={getString('importSpace.gitProvider')} items={getProviders()} />
{![GitProviders.GITHUB, GitProviders.GITLAB, GitProviders.BITBUCKET, GitProviders.AZURE].includes(
values.gitProvider
) && (
<FormInput.Text
className={css.hideContainer}
name="hostUrl"
label={getString('importRepo.url')}
placeholder={getString('importRepo.urlPlaceholder')}
tooltipProps={{
dataTooltipId: 'repositoryURLTextField'
}}
/>
)}
<FormInput.Text
className={css.hideContainer}
name="org"
label={getString(getOrgLabel(values.gitProvider))}
placeholder={getString(getOrgPlaceholder(values.gitProvider))}
/>
{values.gitProvider === GitProviders.AZURE && (
<FormInput.Text
className={css.hideContainer}
name="project"
label={getString('importRepo.project')}
placeholder={getString('importRepo.projectPlaceholder')}
/>
)}
<Layout.Horizontal spacing="medium" flex={{ justifyContent: 'space-between', alignItems: 'baseline' }}>
<Layout.Vertical className={css.repoAndBranch} spacing="small">
<FormInput.Text
className={css.hideContainer}
name="repo"
label={getString('importRepo.repo')}
placeholder={getString('importRepo.repoPlaceholder')}
onChange={event => {
const target = event.target as HTMLInputElement
setFieldValue('repo', target.value)
if (target.value) {
setFieldValue('name', target.value)
validateField('repo')
}
}}
/>
</Layout.Vertical>
<Layout.Vertical className={css.repoAndBranch} spacing="small">
<FormInput.Text
className={css.hideContainer}
name="branch"
label={getString('branch')}
placeholder={getString('cde.create.branchPlaceholder')}
onChange={event => {
const target = event.target as HTMLInputElement
setFieldValue('branch', target.value)
}}
/>
</Layout.Vertical>
</Layout.Horizontal>
<Layout.Horizontal spacing="medium">
<FormInput.CheckBox
name="authorization"
label={getString('importRepo.reqAuth')}
tooltipProps={{
dataTooltipId: 'authorization'
}}
onClick={() => {
setAuth(!auth)
}}
style={auth ? {} : { margin: 0 }}
/>
</Layout.Horizontal>
{auth ? (
<>
{[GitProviders.BITBUCKET, GitProviders.AZURE].includes(values.gitProvider) && (
<FormInput.Text
name="username"
label={getString('userName')}
placeholder={getString('importRepo.userPlaceholder')}
tooltipProps={{
dataTooltipId: 'repositoryUserTextField'
}}
/>
)}
<FormInput.Text
inputGroup={{ type: 'password' }}
name="password"
label={
[GitProviders.BITBUCKET, GitProviders.AZURE].includes(values.gitProvider)
? getString('importRepo.appPassword')
: getString('importRepo.passToken')
}
placeholder={
[GitProviders.BITBUCKET, GitProviders.AZURE].includes(values.gitProvider)
? getString('importRepo.appPasswordPlaceholder')
: getString('importRepo.passTokenPlaceholder')
}
tooltipProps={{
dataTooltipId: 'repositoryPasswordTextField'
}}
/>
</>
) : null}
</FormikForm>
)
}

View File

@ -0,0 +1,28 @@
import type { ThirdPartyRepoImportFormProps } from 'cde-gitness/components/ThirdPartyRepoImportForm/ThirdPartyRepoImportForm'
import type { EnumIDEType, OpenapiCreateGitspaceRequest } from 'cde-gitness/services'
import { GitProviders } from 'utils/GitUtils'
export const gitnessFormInitialValues: OpenapiCreateGitspaceRequest = {
branch: '',
code_repo_url: '',
devcontainer_path: '',
id: '',
ide: 'vsCode' as EnumIDEType,
infra_provider_resource_id: 'default',
name: ''
}
export const thirdPartyformInitialValues: ThirdPartyRepoImportFormProps = {
gitProvider: GitProviders.GITHUB,
hostUrl: '',
org: '',
project: '',
repo: '',
username: '',
password: '',
name: '',
description: '',
branch: '',
ide: 'vsCode' as EnumIDEType,
id: '',
importPipelineLabel: false
}

View File

@ -0,0 +1,49 @@
.main {
display: flex;
justify-content: center;
align-items: center;
.cardMain {
width: 50%;
}
.cardTitle {
font-size: 16px !important;
}
}
.subContainers {
margin: var(--spacing-xlarge) 0 !important;
margin-bottom: 0px !important;
}
.formDivider {
height: 2px !important;
border-top: 1px solid var(--grey-200) !important;
}
.formTitleContainer {
padding: var(--spacing-medium) var(--spacing-large) !important;
border: 1px solid var(--grey-200);
border-radius: 5px;
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
}
.formContainer {
border: 1px solid var(--grey-200);
border-radius: 5px;
padding: var(--spacing-medium) var(--spacing-large) !important;
border-top-right-radius: 0px;
border-top-left-radius: 0px;
border-top: none;
}
.formOuterContainer {
padding: var(--spacing-medium) 0 !important;
padding-bottom: 0px !important;
button {
margin-bottom: none;
}
}

View File

@ -0,0 +1,26 @@
/*
* 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 cardMain: string
export declare const cardTitle: string
export declare const formContainer: string
export declare const formDivider: string
export declare const formOuterContainer: string
export declare const formTitleContainer: string
export declare const main: string
export declare const subContainers: string

View File

@ -0,0 +1,123 @@
import React, { useState } from 'react'
import {
Button,
ButtonVariation,
Card,
Container,
Formik,
FormikForm,
Layout,
Page,
Text,
useToaster
} from '@harnessio/uicore'
import { FontVariation } from '@harnessio/design-system'
import { useHistory } from 'react-router-dom'
import { useStrings } from 'framework/strings'
import {
ThirdPartyRepoImportForm,
ThirdPartyRepoImportFormProps
} from 'cde-gitness/components/ThirdPartyRepoImportForm/ThirdPartyRepoImportForm'
import { GitnessRepoImportForm } from 'cde-gitness/components/GitnessRepoImportForm/GitnessRepoImportForm'
import { SelectIDE } from 'cde/components/CreateGitspace/components/SelectIDE/SelectIDE'
import { useCreateGitspace, type OpenapiCreateGitspaceRequest } from 'cde-gitness/services'
import { useGetSpaceParam } from 'hooks/useGetSpaceParam'
import { getErrorMessage } from 'utils/Utils'
import { useAppContext } from 'AppContext'
import RepositoryTypeButton, { RepositoryType } from '../../components/RepositoryTypeButton/RepositoryTypeButton'
import { gitnessFormInitialValues, thirdPartyformInitialValues } from './GitspaceCreate.constants'
import { handleImportSubmit, validateGitnessForm, validationSchemaStepOne } from './GitspaceCreate.utils'
import css from './GitspaceCreate.module.scss'
export const GitspaceCreate = () => {
const { getString } = useStrings()
const space = useGetSpaceParam()
const { routes } = useAppContext()
const history = useHistory()
const [activeButton, setActiveButton] = useState(RepositoryType.GITNESS)
const { showSuccess, showError } = useToaster()
const { mutate } = useCreateGitspace({})
return (
<>
<Page.Header title={getString('cde.gitspaces')} />
<Page.Body className={css.main}>
<Card className={css.cardMain}>
<Text className={css.cardTitle} font={{ variation: FontVariation.CARD_TITLE }}>
{getString('cde.createGitspace')}
</Text>
<Container className={css.subContainers}>
<Formik
onSubmit={async data => {
try {
const payload =
activeButton === RepositoryType.GITNESS
? data
: handleImportSubmit(data as ThirdPartyRepoImportFormProps)
await mutate({ ...payload, space_ref: space } as OpenapiCreateGitspaceRequest & {
space_ref?: string
})
showSuccess(getString('cde.create.gitspaceCreateSuccess'))
history.push(routes.toCDEGitspaces({ space }))
} catch (error) {
showError(getString('cde.create.gitspaceCreateFailed'))
showError(getErrorMessage(error))
}
}}
initialValues={
activeButton === RepositoryType.GITNESS ? gitnessFormInitialValues : thirdPartyformInitialValues
}
validationSchema={
activeButton === RepositoryType.GITNESS
? validateGitnessForm(getString)
: validationSchemaStepOne(getString)
}
formName="importRepoForm"
enableReinitialize>
{formik => {
return (
<>
<Layout.Horizontal
className={css.formTitleContainer}
flex={{ justifyContent: 'space-between', alignItems: 'center' }}>
<Text font={{ variation: FontVariation.CARD_TITLE }}>
{getString('cde.create.repositoryDetails')}
</Text>
<RepositoryTypeButton
hasChange={formik.dirty}
onChange={type => {
setActiveButton(type)
formik?.resetForm()
}}
/>
</Layout.Horizontal>
<FormikForm>
<Container className={css.formContainer}>
{activeButton === RepositoryType.GITNESS && (
<Container>
<GitnessRepoImportForm />
</Container>
)}
{activeButton === RepositoryType.THIRDPARTY && (
<Container>
<ThirdPartyRepoImportForm />
</Container>
)}
</Container>
<Container className={css.formOuterContainer}>
<SelectIDE />
<Button width={'100%'} variation={ButtonVariation.PRIMARY} height={50} type="submit">
{getString('cde.createGitspace')}
</Button>
</Container>
</FormikForm>
</>
)
}}
</Formik>
</Container>
</Card>
</Page.Body>
</>
)
}

View File

@ -0,0 +1,90 @@
import * as yup from 'yup'
import { compact } from 'lodash-es'
import type { UseStringsReturn } from 'framework/strings'
import { GitProviders, getProviderTypeMapping } from 'utils/GitUtils'
import type { ThirdPartyRepoImportFormProps } from 'cde-gitness/components/ThirdPartyRepoImportForm/ThirdPartyRepoImportForm'
export const validateGitnessForm = (getString: UseStringsReturn['getString']) =>
yup.object().shape({
branch: yup.string().trim().required(getString('cde.branchValidationMessage')),
code_repo_url: yup.string().trim().required(getString('cde.repoValidationMessage')),
id: yup.string().trim().required(),
ide: yup.string().trim().required(),
infra_provider_resource_id: yup.string().trim().required(getString('cde.machineValidationMessage')),
name: yup.string().trim().required()
})
export const validationSchemaStepOne = (getString: UseStringsReturn['getString']) =>
yup.object().shape({
gitProvider: yup.string().required(),
repo: yup
.string()
.trim()
.when('gitProvider', {
is: gitProvider => [GitProviders.GITHUB, GitProviders.GITLAB, GitProviders.BITBUCKET].includes(gitProvider),
then: yup.string().required(getString('importSpace.orgRequired'))
}),
branch: yup.string().trim().required(getString('cde.branchValidationMessage')),
hostUrl: yup
.string()
// .matches(MATCH_REPOURL_REGEX, getString('importSpace.invalidUrl'))
.when('gitProvider', {
is: gitProvider =>
![GitProviders.GITHUB, GitProviders.GITLAB, GitProviders.BITBUCKET, GitProviders.AZURE].includes(gitProvider),
then: yup.string().required(getString('importRepo.required')),
otherwise: yup.string().notRequired() // Optional based on your needs
}),
org: yup
.string()
.trim()
.when('gitProvider', {
is: GitProviders.AZURE,
then: yup.string().required(getString('importSpace.orgRequired'))
}),
project: yup
.string()
.trim()
.when('gitProvider', {
is: GitProviders.AZURE,
then: yup.string().required(getString('importSpace.spaceNameRequired'))
}),
name: yup.string().trim().required(getString('validation.nameIsRequired'))
})
export const handleImportSubmit = (formData: ThirdPartyRepoImportFormProps) => {
const type = getProviderTypeMapping(formData.gitProvider)
const provider = {
type,
username: formData.username,
password: formData.password,
host: ''
}
if (
![GitProviders.GITHUB, GitProviders.GITLAB, GitProviders.BITBUCKET, GitProviders.AZURE].includes(
formData.gitProvider
)
) {
provider.host = formData.hostUrl
}
const importPayload = {
name: formData.repo || formData.name,
description: formData.description || '',
id: formData.repo,
provider,
ide: formData.ide,
branch: formData.branch,
infra_provider_resource_id: 'default',
provider_repo: compact([
formData.org,
formData.gitProvider === GitProviders.AZURE ? formData.project : '',
formData.repo
])
.join('/')
.replace(/\.git$/, '')
}
return importPayload
}

View File

@ -0,0 +1,102 @@
import React from 'react'
import {
Button,
Page,
ButtonVariation,
Breadcrumbs,
HarnessDocTooltip,
Container,
Layout,
Text
} from '@harnessio/uicore'
import { FontVariation } from '@harnessio/design-system'
import { useHistory } from 'react-router-dom'
import { useGet } from 'restful-react'
import { useAppContext } from 'AppContext'
import { useStrings } from 'framework/strings'
import { useGetSpaceParam } from 'hooks/useGetSpaceParam'
import { LIST_FETCHING_LIMIT, PageBrowserProps, getErrorMessage } from 'utils/Utils'
import noSpace from 'cde/images/no-gitspace.svg?url'
import { ResourceListingPagination } from 'components/ResourceListingPagination/ResourceListingPagination'
import { useQueryParams } from 'hooks/useQueryParams'
import { usePageIndex } from 'hooks/usePageIndex'
import { ListGitspaces } from 'cde-gitness/components/GitspaceListing/ListGitspaces'
import type { TypesGitspaceConfig } from 'cde-gitness/services'
import css from './GitspacesListing.module.scss'
export const GitspaceListing = () => {
const space = useGetSpaceParam()
const history = useHistory()
const { getString } = useStrings()
const { routes } = useAppContext()
const pageBrowser = useQueryParams<PageBrowserProps>()
const pageInit = pageBrowser.page ? parseInt(pageBrowser.page) : 1
const [page, setPage] = usePageIndex(pageInit)
const {
data = '',
loading = false,
error = undefined,
refetch,
response
} = useGet<TypesGitspaceConfig[]>({
path: `/api/v1/spaces/${space}/+/gitspaces`,
queryParams: { page, limit: LIST_FETCHING_LIMIT },
debounce: 500
})
// useEffect(() => {
// if (!data && !loading) {
// history.push(routes.toCDEGitspacesCreate({ space }))
// }
// }, [data, loading])
return (
<>
<Page.Header
title={
<div className="ng-tooltip-native">
<h2 data-tooltip-id="artifactListPageHeading"> {getString('cde.gitspaces')}</h2>
<HarnessDocTooltip tooltipId="GitSpaceListPageHeading" useStandAlone={true} />
</div>
}
breadcrumbs={
<Breadcrumbs links={[{ url: routes.toCDEGitspaces({ space }), label: getString('cde.gitspaces') }]} />
}
toolbar={
<Button
onClick={() => history.push(routes.toCDEGitspacesCreate({ space }))}
variation={ButtonVariation.PRIMARY}>
{getString('cde.newGitspace')}
</Button>
}
/>
<Container className={css.main}>
<Layout.Vertical spacing={'large'}>
<Page.Body
loading={loading}
error={
error ? (
<Layout.Vertical spacing={'large'}>
<Text font={{ variation: FontVariation.FORM_MESSAGE_DANGER }}>{getErrorMessage(error)}</Text>
<Button
onClick={() => refetch?.()}
variation={ButtonVariation.PRIMARY}
text={getString('cde.retry')}
/>
</Layout.Vertical>
) : null
}
noData={{
when: () => data?.length === 0,
image: noSpace,
message: getString('cde.noGitspaces')
}}>
<ListGitspaces data={data || []} refreshList={refetch} />
<ResourceListingPagination response={response} page={page} setPage={setPage} />
</Page.Body>
</Layout.Vertical>
</Container>
</>
)
}

View File

@ -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.
*/
.main {
margin: var(--spacing-xxlarge) !important;
}

View File

@ -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 main: string

File diff suppressed because it is too large Load Diff

View File

@ -17,9 +17,8 @@
import React, { useEffect, useRef, useState } from 'react'
import cx from 'classnames'
import { Menu, PopoverInteractionKind, PopoverPosition } from '@blueprintjs/core'
import { Text, Button, Container, ButtonVariation, FormError } from '@harnessio/uicore'
import { Text, Button, Container, ButtonVariation, FormInput } from '@harnessio/uicore'
import type { IconName } from '@harnessio/icons'
import { useFormikContext } from 'formik'
import { useStrings } from 'framework/strings'
import css from './GitspaceSelect.module.scss'
@ -32,24 +31,25 @@ interface GitspaceSelectProps {
errorMessage?: string
formikName?: string
tooltipProps?: { [key: string]: any }
formInputClassName?: string
loading?: boolean
}
export const GitspaceSelect = ({
text,
icon,
loading,
renderMenu,
disabled,
overridePopOverWidth,
errorMessage,
formikName,
tooltipProps
tooltipProps,
formInputClassName
}: GitspaceSelectProps) => {
const { getString } = useStrings()
const buttonRef = useRef<HTMLDivElement | null>(null)
const [popoverWidth, setPopoverWidth] = useState(0)
const { touched } = useFormikContext<{ validated?: boolean }>()
const defaultTooltipProps = {
tooltip: (
<Container className={css.listContainer} width={overridePopOverWidth ? '100%' : popoverWidth}>
@ -85,18 +85,25 @@ export const GitspaceSelect = ({
const addTooltipProps = disabled ? {} : { ...defaultTooltipProps }
return (
<div className={css.buttonDiv} ref={buttonRef}>
<Button
className={cx(css.button, { [css.buttonWithoutIcon]: !icon })}
text={text}
rightIcon="chevron-down"
variation={ButtonVariation.TERTIARY}
iconProps={{ size: 14 }}
{...iconProp}
{...addTooltipProps}
disabled={disabled}
/>
{touched.validated && <FormError errorMessage={errorMessage} name={formikName || ''} />}
</div>
<FormInput.CustomRender
name={formikName || ''}
className={cx(formInputClassName)}
render={() => {
return (
<div className={css.buttonDiv} ref={buttonRef}>
<Button
className={cx(css.button, { [css.buttonWithoutIcon]: !icon })}
text={text}
rightIcon={loading ? 'loading' : 'chevron-down'}
variation={ButtonVariation.TERTIARY}
iconProps={{ size: 14 }}
{...iconProp}
{...addTooltipProps}
disabled={disabled}
/>
</div>
)
}}
/>
)
}

View File

@ -25,7 +25,7 @@ import { useStrings } from 'framework/strings'
import Gitspace from '../../icons/Gitspace.svg?url'
import css from './Gitspaces.module.scss'
const CreateGitspaceTitle = () => {
export const CreateGitspaceTitle = () => {
const { getString } = useStrings()
return (
<Container className={css.gitspaceTitle}>

View File

@ -49,7 +49,7 @@ import { useGet, useMutate } from 'restful-react'
import { Render } from 'react-jsx-match'
import { compact, get } from 'lodash-es'
import { useModalHook } from 'hooks/useModalHook'
import { useStrings } from 'framework/strings'
import { String, useStrings } from 'framework/strings'
import {
DEFAULT_BRANCH_NAME,
getErrorMessage,
@ -96,6 +96,8 @@ export interface NewRepoModalButtonProps extends Omit<ButtonProps, 'onClick' | '
submitButtonTitle?: string
cancelButtonTitle?: string
onSubmit: (data: TypesRepository & SpaceImportRepositoriesOutput) => void
newRepoModalOnly?: boolean
notFoundRepoName?: string
}
export const NewRepoModalButton: React.FC<NewRepoModalButtonProps> = ({
@ -478,7 +480,19 @@ export const NewRepoModalButton: React.FC<NewRepoModalButtonProps> = ({
},
[space]
)
return (
return props?.newRepoModalOnly ? (
<MenuItem
icon="plus"
text={<String stringID="cde.create.repoNotFound" vars={{ repo: props?.notFoundRepoName }} useRichText />}
onClick={e => {
e.preventDefault()
e.stopPropagation()
setRepoOption(repoCreateOptions[0])
setTimeout(() => openModal(), 0)
}}
/>
) : (
<SplitButton
{...props}
loading={false}

View File

@ -143,6 +143,18 @@ export interface StringsMap {
'cde.branchValidationMessage': string
'cde.cloudDeveloperExperience': string
'cde.cpu': string
'cde.create.branchPlaceholder': string
'cde.create.gitnessRepositories': string
'cde.create.gitspaceCreateFailed': string
'cde.create.gitspaceCreateSuccess': string
'cde.create.repoNotFound': string
'cde.create.repositoryDetails': string
'cde.create.searchBranchPlaceholder': string
'cde.create.searchRepositoryPlaceholder': string
'cde.create.selectBranchPlaceholder': string
'cde.create.thirdPartyGitRepositories': string
'cde.create.unsaved.message': string
'cde.create.unsaved.title': string
'cde.createGitspace': string
'cde.createRepo': string
'cde.deleteGitspace': string
@ -183,6 +195,9 @@ export interface StringsMap {
'cde.introText1': string
'cde.introText2': string
'cde.introText3': string
'cde.listing.error': string
'cde.listing.offline': string
'cde.listing.online': string
'cde.logs': string
'cde.machine': string
'cde.machineValidationMessage': string
@ -211,6 +226,7 @@ export interface StringsMap {
'cde.retry': string
'cde.sessionDuration': string
'cde.startingGitspace': string
'cde.status': string
'cde.stopingGitspace': string
'cde.updateGitspace': string
'cde.used': string

View File

@ -1162,6 +1162,25 @@ cde:
deleteGitspaceText: "Are you sure to delete the gitspace, '{{name}}'?"
deleteSuccess: Gitspace deleted succesfully
repositoryAndBranch: Repository & Branch
status: Status
listing:
online: Online
offline: Offline
error: Error
create:
repositoryDetails: Repository Details
gitnessRepositories: Gitness Repositories
thirdPartyGitRepositories: Third Party Git Repositories
branchPlaceholder: Enter the Branch name
selectBranchPlaceholder: Select branch
searchRepositoryPlaceholder: Search for a repository
searchBranchPlaceholder: Search for a branch
repoNotFound: 'Create a repo <strong>{{repo}}</strong> in Gitness'
gitspaceCreateSuccess: Gitspace created successfully
gitspaceCreateFailed: Gitspace creation failed
unsaved:
title: Unsaved Changes
message: 'You have unsaved changes, On switching all unsaved changes will be lost'
details:
actions: More Actions
openEditor: Open VS Code Editor