mirror of https://github.com/harness/drone.git
feat: [CODE-2819]: Archive Repo functionality (#3097)
parent
f78f767ae2
commit
e9a7fd1e88
|
@ -44,6 +44,7 @@ import { getErrorMessage, timeDistance } from 'utils/Utils'
|
|||
import useLiveTimer from 'hooks/useLiveTimeHook'
|
||||
import { CommitActions } from 'components/CommitActions/CommitActions'
|
||||
import type { TypesExecution } from 'services/code'
|
||||
import { RepoArchivedBanner } from 'components/RepositoryArchivedBanner/RepositoryArchivedBanner'
|
||||
import css from './ExecutionPageHeader.module.scss'
|
||||
|
||||
interface BreadcrumbLink {
|
||||
|
@ -99,119 +100,127 @@ export function ExecutionPageHeader({
|
|||
}
|
||||
|
||||
return (
|
||||
<PageHeader
|
||||
className={css.pageHeader}
|
||||
title={title}
|
||||
breadcrumbs={
|
||||
<Layout.Horizontal
|
||||
spacing="small"
|
||||
className={css.breadcrumb}
|
||||
padding={{ bottom: 0 }}
|
||||
margin={{ bottom: 'small' }}>
|
||||
<Link to={routes.toCODERepositories({ space })}>{getString('repositories')}</Link>
|
||||
<Icon name="main-chevron-right" size={8} color={Color.GREY_500} />
|
||||
<Link to={routes.toCODERepository({ repoPath: repoMetadata.path as string, gitRef })}>
|
||||
{repoMetadata.identifier}
|
||||
</Link>
|
||||
{extraBreadcrumbLinks.map(link => (
|
||||
<Fragment key={link.url}>
|
||||
<Icon name="main-chevron-right" size={8} color={Color.GREY_500} />
|
||||
{/* This allows for outer most entities to not necessarily be links */}
|
||||
{link.url ? (
|
||||
<Link to={link.url}>{link.label}</Link>
|
||||
) : (
|
||||
<Text font={{ variation: FontVariation.SMALL }} color={Color.GREY_500}>
|
||||
{link.label}
|
||||
</Text>
|
||||
)}
|
||||
</Fragment>
|
||||
))}
|
||||
</Layout.Horizontal>
|
||||
}
|
||||
content={
|
||||
executionInfo && (
|
||||
<Container className={css.executionInfo}>
|
||||
<ExecutionStatus status={getStatus(executionInfo.status)} iconOnly noBackground iconSize={18} isCi />
|
||||
<Text inline lineClamp={1} color={Color.GREY_800} font={{ size: 'small' }}>
|
||||
{executionInfo.message}
|
||||
</Text>
|
||||
<PipeSeparator height={7} />
|
||||
<Avatar email={executionInfo.authorEmail} name={executionInfo.authorName} size="small" hoverCard={false} />
|
||||
<Text inline color={Color.GREY_500} font={{ size: 'small' }}>
|
||||
{executionInfo.authorName}
|
||||
</Text>
|
||||
<PipeSeparator height={7} />
|
||||
<GitFork height={12} width={12} color={Utils.getRealCSSColor(Color.GREY_500)} />
|
||||
<Text inline color={Color.GREY_500} font={{ size: 'small' }}>
|
||||
{executionInfo.source}
|
||||
</Text>
|
||||
<PipeSeparator height={7} />
|
||||
{executionInfo.hash && (
|
||||
<Container onClick={Utils.stopEvent}>
|
||||
<CommitActions
|
||||
href={routes.toCODECommit({
|
||||
repoPath: repoMetadata.path as string,
|
||||
commitRef: executionInfo.hash
|
||||
})}
|
||||
sha={executionInfo.hash}
|
||||
enableCopy
|
||||
/>
|
||||
</Container>
|
||||
)}
|
||||
<FlexExpander />
|
||||
{executionInfo.started && (
|
||||
<Layout.Horizontal spacing={'small'} style={{ alignItems: 'center' }} className={css.timer}>
|
||||
<Timer height={16} width={16} color={Utils.getRealCSSColor(Color.GREY_500)} />
|
||||
<Text inline color={Color.GREY_500} font={{ size: 'small' }}>
|
||||
{isActive
|
||||
? timeDistance(executionInfo.started, currentTime, true) // Live update time when status is 'RUNNING'
|
||||
: timeDistance(executionInfo.started, executionInfo.finished, true)}
|
||||
</Text>
|
||||
{executionInfo.finished && (
|
||||
<>
|
||||
<PipeSeparator height={7} />
|
||||
<Calendar height={16} width={16} color={Utils.getRealCSSColor(Color.GREY_500)} />
|
||||
<Text inline color={Color.GREY_500} font={{ size: 'small' }}>
|
||||
{timeDistance(executionInfo.finished, currentTime, true)} ago
|
||||
</Text>
|
||||
</>
|
||||
<>
|
||||
<PageHeader
|
||||
className={css.pageHeader}
|
||||
title={title}
|
||||
breadcrumbs={
|
||||
<Layout.Horizontal
|
||||
spacing="small"
|
||||
className={css.breadcrumb}
|
||||
padding={{ bottom: 0 }}
|
||||
margin={{ bottom: 'small' }}>
|
||||
<Link to={routes.toCODERepositories({ space })}>{getString('repositories')}</Link>
|
||||
<Icon name="main-chevron-right" size={8} color={Color.GREY_500} />
|
||||
<Link to={routes.toCODERepository({ repoPath: repoMetadata.path as string, gitRef })}>
|
||||
{repoMetadata.identifier}
|
||||
</Link>
|
||||
{extraBreadcrumbLinks.map(link => (
|
||||
<Fragment key={link.url}>
|
||||
<Icon name="main-chevron-right" size={8} color={Color.GREY_500} />
|
||||
{/* This allows for outer most entities to not necessarily be links */}
|
||||
{link.url ? (
|
||||
<Link to={link.url}>{link.label}</Link>
|
||||
) : (
|
||||
<Text font={{ variation: FontVariation.SMALL }} color={Color.GREY_500}>
|
||||
{link.label}
|
||||
</Text>
|
||||
)}
|
||||
</Layout.Horizontal>
|
||||
)}
|
||||
<>
|
||||
</Fragment>
|
||||
))}
|
||||
</Layout.Horizontal>
|
||||
}
|
||||
content={
|
||||
executionInfo && (
|
||||
<Container className={css.executionInfo}>
|
||||
<ExecutionStatus status={getStatus(executionInfo.status)} iconOnly noBackground iconSize={18} isCi />
|
||||
<Text inline lineClamp={1} color={Color.GREY_800} font={{ size: 'small' }}>
|
||||
{executionInfo.message}
|
||||
</Text>
|
||||
<PipeSeparator height={7} />
|
||||
<Button
|
||||
variation={ButtonVariation.PRIMARY}
|
||||
text={getString('pipelines.edit')}
|
||||
onClick={e => {
|
||||
e.stopPropagation()
|
||||
if (repoMetadata?.path && pipeline) {
|
||||
history.push(routes.toCODEPipelineEdit({ repoPath: repoMetadata.path, pipeline }))
|
||||
}
|
||||
}}
|
||||
<Avatar
|
||||
email={executionInfo.authorEmail}
|
||||
name={executionInfo.authorName}
|
||||
size="small"
|
||||
hoverCard={false}
|
||||
/>
|
||||
</>
|
||||
{[ExecutionState.RUNNING, ExecutionState.PENDING].includes(getStatus(executionInfo?.status)) && (
|
||||
<Text inline color={Color.GREY_500} font={{ size: 'small' }}>
|
||||
{executionInfo.authorName}
|
||||
</Text>
|
||||
<PipeSeparator height={7} />
|
||||
<GitFork height={12} width={12} color={Utils.getRealCSSColor(Color.GREY_500)} />
|
||||
<Text inline color={Color.GREY_500} font={{ size: 'small' }}>
|
||||
{executionInfo.source}
|
||||
</Text>
|
||||
<PipeSeparator height={7} />
|
||||
{executionInfo.hash && (
|
||||
<Container onClick={Utils.stopEvent}>
|
||||
<CommitActions
|
||||
href={routes.toCODECommit({
|
||||
repoPath: repoMetadata.path as string,
|
||||
commitRef: executionInfo.hash
|
||||
})}
|
||||
sha={executionInfo.hash}
|
||||
enableCopy
|
||||
/>
|
||||
</Container>
|
||||
)}
|
||||
<FlexExpander />
|
||||
{executionInfo.started && (
|
||||
<Layout.Horizontal spacing={'small'} style={{ alignItems: 'center' }} className={css.timer}>
|
||||
<Timer height={16} width={16} color={Utils.getRealCSSColor(Color.GREY_500)} />
|
||||
<Text inline color={Color.GREY_500} font={{ size: 'small' }}>
|
||||
{isActive
|
||||
? timeDistance(executionInfo.started, currentTime, true) // Live update time when status is 'RUNNING'
|
||||
: timeDistance(executionInfo.started, executionInfo.finished, true)}
|
||||
</Text>
|
||||
{executionInfo.finished && (
|
||||
<>
|
||||
<PipeSeparator height={7} />
|
||||
<Calendar height={16} width={16} color={Utils.getRealCSSColor(Color.GREY_500)} />
|
||||
<Text inline color={Color.GREY_500} font={{ size: 'small' }}>
|
||||
{timeDistance(executionInfo.finished, currentTime, true)} ago
|
||||
</Text>
|
||||
</>
|
||||
)}
|
||||
</Layout.Horizontal>
|
||||
)}
|
||||
<>
|
||||
<PipeSeparator height={7} />
|
||||
<Button
|
||||
variation={ButtonVariation.SECONDARY}
|
||||
text={getString('cancel')}
|
||||
onClick={async () => {
|
||||
try {
|
||||
await cancelExecution(null)
|
||||
clearToaster()
|
||||
showSuccess(getString('pipelines.executionCancelled'))
|
||||
} catch (exception) {
|
||||
showError(getErrorMessage(exception), 0, 'pipelines.executionCouldNotCancel')
|
||||
variation={ButtonVariation.PRIMARY}
|
||||
text={getString('pipelines.edit')}
|
||||
onClick={e => {
|
||||
e.stopPropagation()
|
||||
if (repoMetadata?.path && pipeline) {
|
||||
history.push(routes.toCODEPipelineEdit({ repoPath: repoMetadata.path, pipeline }))
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
/>
|
||||
{[ExecutionState.RUNNING, ExecutionState.PENDING].includes(getStatus(executionInfo?.status)) && (
|
||||
<>
|
||||
<PipeSeparator height={7} />
|
||||
<Button
|
||||
variation={ButtonVariation.SECONDARY}
|
||||
text={getString('cancel')}
|
||||
onClick={async () => {
|
||||
try {
|
||||
await cancelExecution(null)
|
||||
clearToaster()
|
||||
showSuccess(getString('pipelines.executionCancelled'))
|
||||
} catch (exception) {
|
||||
showError(getErrorMessage(exception), 0, 'pipelines.executionCouldNotCancel')
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
/>
|
||||
<RepoArchivedBanner isArchived={repoMetadata?.archived} updated={repoMetadata?.updated} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -147,7 +147,7 @@ const PipelineSettingsTab = ({ pipeline, repoPath, yamlPath }: SettingsContentPr
|
|||
<Container padding={'large'} className={css.generalContainer}>
|
||||
<Layout.Vertical>
|
||||
<Text icon="main-trash" color={Color.GREY_600} font={{ size: 'normal' }}>
|
||||
{getString('dangerDeleteRepo')}
|
||||
{getString('dangerDeletePipeline')}
|
||||
</Text>
|
||||
<Layout.Horizontal padding={{ top: 'medium', left: 'medium' }} flex={{ justifyContent: 'space-between' }}>
|
||||
<Container intent="warning" padding={'small'} className={css.yellowContainer}>
|
||||
|
|
|
@ -26,3 +26,8 @@
|
|||
padding: 0 9px !important;
|
||||
display: inline-block !important;
|
||||
}
|
||||
|
||||
.labelArchived {
|
||||
color: rgb(128, 92, 67, 1) !important;
|
||||
border-color: rgb(128, 92, 67, 1) !important;
|
||||
}
|
|
@ -17,3 +17,4 @@
|
|||
/* eslint-disable */
|
||||
// This is an auto-generated file
|
||||
export declare const label: string
|
||||
export declare const labelArchived: string
|
|
@ -15,19 +15,32 @@
|
|||
*/
|
||||
|
||||
import React from 'react'
|
||||
import classNames from 'classnames'
|
||||
import { Text, TextProps } from '@harnessio/uicore'
|
||||
import { useStrings } from 'framework/strings'
|
||||
import css from './RepoPublicLabel.module.scss'
|
||||
import css from './RepoTypeLabel.module.scss'
|
||||
|
||||
export const RepoPublicLabel: React.FC<{ isPublic?: boolean; margin?: TextProps['margin'] }> = ({
|
||||
export const RepoTypeLabel: React.FC<{ isPublic?: boolean; isArchived?: boolean; margin?: TextProps['margin'] }> = ({
|
||||
isPublic,
|
||||
isArchived,
|
||||
margin
|
||||
}) => {
|
||||
const { getString } = useStrings()
|
||||
|
||||
const visibility = getString(isPublic ? 'public' : 'private')
|
||||
const archiveStatus = isArchived ? ` ${getString('archived')}` : ''
|
||||
|
||||
return (
|
||||
<Text inline className={css.label} margin={margin}>
|
||||
{getString(isPublic ? 'public' : 'private')}
|
||||
</Text>
|
||||
<>
|
||||
<Text inline className={css.label} margin={margin}>
|
||||
{visibility}
|
||||
</Text>
|
||||
|
||||
{isArchived && (
|
||||
<Text inline className={classNames(css.label, css.labelArchived)} margin={margin}>
|
||||
{archiveStatus}
|
||||
</Text>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
.infoContainer {
|
||||
background-color: var(--orange-100) !important;
|
||||
padding: var(--spacing-small) var(--spacing-medium) !important;
|
||||
width: 100% !important;
|
||||
margin: 0 auto !important;
|
||||
}
|
19
web/src/components/RepositoryArchivedBanner/RepositoryArchivedBanner.module.scss.d.ts
vendored
Normal file
19
web/src/components/RepositoryArchivedBanner/RepositoryArchivedBanner.module.scss.d.ts
vendored
Normal 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 infoContainer: string
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* 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 React from 'react'
|
||||
import { Container, Text } from '@harnessio/uicore'
|
||||
import { Color } from '@harnessio/design-system'
|
||||
import { useStrings } from 'framework/strings'
|
||||
import css from './RepositoryArchivedBanner.module.scss'
|
||||
|
||||
export const RepoArchivedBanner: React.FC<{ isArchived?: boolean; updated?: number }> = ({ isArchived, updated }) => {
|
||||
const { getString } = useStrings()
|
||||
|
||||
return (
|
||||
<>
|
||||
{isArchived && (
|
||||
<Container className={css.infoContainer}>
|
||||
<Text
|
||||
icon="main-issue"
|
||||
iconProps={{ size: 16, color: Color.ORANGE_700, margin: { right: 'small' } }}
|
||||
color={Color.GREY_700}>
|
||||
{getString('repoArchive.infoText', {
|
||||
date: updated
|
||||
? new Intl.DateTimeFormat('en-US', {
|
||||
day: '2-digit',
|
||||
month: 'short',
|
||||
year: 'numeric'
|
||||
}).format(new Date(updated))
|
||||
: 'N/A'
|
||||
})}
|
||||
</Text>
|
||||
</Container>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
|
@ -25,6 +25,7 @@ import { useAppContext } from 'AppContext'
|
|||
import { useGetSpaceParam } from 'hooks/useGetSpaceParam'
|
||||
import type { CODEProps } from 'RouteDefinitions'
|
||||
import type { GitInfoProps } from 'utils/GitUtils'
|
||||
import { RepoArchivedBanner } from 'components/RepositoryArchivedBanner/RepositoryArchivedBanner'
|
||||
import css from './RepositoryPageHeader.module.scss'
|
||||
|
||||
interface BreadcrumbLink {
|
||||
|
@ -54,45 +55,48 @@ export function RepositoryPageHeader({
|
|||
const { routes, isCurrentSessionPublic } = useAppContext()
|
||||
|
||||
return (
|
||||
<PageHeader
|
||||
className={className}
|
||||
content={content}
|
||||
title=""
|
||||
breadcrumbs={
|
||||
<Container className={css.header}>
|
||||
<Layout.Horizontal
|
||||
spacing="small"
|
||||
className={cx(css.breadcrumb, { [css.hideBreadcrumbs]: isCurrentSessionPublic })}>
|
||||
<Link to={routes.toCODERepositories({ space })}>{getString('repositories')}</Link>
|
||||
<Icon name="main-chevron-right" size={8} color={Color.GREY_500} />
|
||||
<Link to={routes.toCODERepository({ repoPath: (repoMetadata?.path as string) || '', gitRef })}>
|
||||
{repoMetadata?.identifier || ''}
|
||||
</Link>
|
||||
{extraBreadcrumbLinks.map(link => (
|
||||
<Fragment key={link.url}>
|
||||
<Icon name="main-chevron-right" size={8} color={Color.GREY_500} />
|
||||
{/* This allows for outer most entities to not necessarily be links */}
|
||||
{link.url ? (
|
||||
<Link to={link.url}>{link.label}</Link>
|
||||
) : (
|
||||
<Text font={{ variation: FontVariation.SMALL }} color={Color.GREY_500}>
|
||||
{link.label}
|
||||
</Text>
|
||||
)}
|
||||
</Fragment>
|
||||
))}
|
||||
</Layout.Horizontal>
|
||||
<Container padding={{ top: 'small', bottom: 'small' }}>
|
||||
{typeof title === 'string' ? (
|
||||
<Text tag="h1" font={{ variation: FontVariation.H4 }} tooltipProps={{ dataTooltipId }}>
|
||||
{title}
|
||||
</Text>
|
||||
) : (
|
||||
title
|
||||
)}
|
||||
<>
|
||||
<PageHeader
|
||||
className={className}
|
||||
content={content}
|
||||
title=""
|
||||
breadcrumbs={
|
||||
<Container className={css.header}>
|
||||
<Layout.Horizontal
|
||||
spacing="small"
|
||||
className={cx(css.breadcrumb, { [css.hideBreadcrumbs]: isCurrentSessionPublic })}>
|
||||
<Link to={routes.toCODERepositories({ space })}>{getString('repositories')}</Link>
|
||||
<Icon name="main-chevron-right" size={8} color={Color.GREY_500} />
|
||||
<Link to={routes.toCODERepository({ repoPath: (repoMetadata?.path as string) || '', gitRef })}>
|
||||
{repoMetadata?.identifier || ''}
|
||||
</Link>
|
||||
{extraBreadcrumbLinks.map(link => (
|
||||
<Fragment key={link.url}>
|
||||
<Icon name="main-chevron-right" size={8} color={Color.GREY_500} />
|
||||
{/* This allows for outer most entities to not necessarily be links */}
|
||||
{link.url ? (
|
||||
<Link to={link.url}>{link.label}</Link>
|
||||
) : (
|
||||
<Text font={{ variation: FontVariation.SMALL }} color={Color.GREY_500}>
|
||||
{link.label}
|
||||
</Text>
|
||||
)}
|
||||
</Fragment>
|
||||
))}
|
||||
</Layout.Horizontal>
|
||||
<Container padding={{ top: 'small', bottom: 'small' }}>
|
||||
{typeof title === 'string' ? (
|
||||
<Text tag="h1" font={{ variation: FontVariation.H4 }} tooltipProps={{ dataTooltipId }}>
|
||||
{title}
|
||||
</Text>
|
||||
) : (
|
||||
title
|
||||
)}
|
||||
</Container>
|
||||
</Container>
|
||||
</Container>
|
||||
}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<RepoArchivedBanner isArchived={repoMetadata?.archived} updated={repoMetadata?.updated} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -38,6 +38,8 @@ export interface StringsMap {
|
|||
approve: string
|
||||
approved: string
|
||||
approvedBy: string
|
||||
archive: string
|
||||
archived: string
|
||||
artifacts: string
|
||||
ascending: string
|
||||
assignPeople: string
|
||||
|
@ -151,6 +153,7 @@ export interface StringsMap {
|
|||
cancelImport: string
|
||||
cancelImportConfirm: string
|
||||
cancelledImport: string
|
||||
cautionZone: string
|
||||
changePassword: string
|
||||
changePasswordSuccessfully: string
|
||||
changeRepoVis: string
|
||||
|
@ -300,7 +303,8 @@ export interface StringsMap {
|
|||
customSecond: string
|
||||
customTime: string
|
||||
customizeMergeCommitMessage: string
|
||||
dangerDeleteRepo: string
|
||||
dangerDeletePipeline: string
|
||||
dangerDeleteProject: string
|
||||
defaultBranch: string
|
||||
defaultBranchTitle: string
|
||||
delete: string
|
||||
|
@ -312,6 +316,8 @@ export interface StringsMap {
|
|||
deleteFile: string
|
||||
deleteImport: string
|
||||
deleteNotAllowed: string
|
||||
deleteRepo: string
|
||||
deleteRepoMsg: string
|
||||
deleteRepoText: string
|
||||
deleteRepoTitle: string
|
||||
deleteSpace: string
|
||||
|
@ -917,6 +923,16 @@ export interface StringsMap {
|
|||
replyAndReactivate: string
|
||||
replyAndResolve: string
|
||||
replyHere: string
|
||||
'repoArchive.archive': string
|
||||
'repoArchive.archiveInfo': string
|
||||
'repoArchive.archiveWarning': string
|
||||
'repoArchive.confirmButton': string
|
||||
'repoArchive.infoText': string
|
||||
'repoArchive.titleArchive': string
|
||||
'repoArchive.titleUnarchive': string
|
||||
'repoArchive.unarchive': string
|
||||
'repoArchive.unarchiveInfo': string
|
||||
'repoArchive.unarchiveWarning': string
|
||||
repoCloneHeader: string
|
||||
repoCloneLabel: string
|
||||
'repoDelete.deleteConfirm1': string
|
||||
|
@ -1109,6 +1125,7 @@ export interface StringsMap {
|
|||
'triggers.updateSuccess': string
|
||||
turnOnSemanticSearch: string
|
||||
unableToGetDivergence: string
|
||||
unarchive: string
|
||||
unorderedList: string
|
||||
unrsolvedComment: string
|
||||
'unsavedChanges.leave': string
|
||||
|
|
|
@ -163,6 +163,10 @@ createABranch: Create a branch
|
|||
createATag: Create a tag
|
||||
delete: Delete
|
||||
edit: Edit
|
||||
archive: Archive
|
||||
unarchive: Unarchive
|
||||
archived: Archived
|
||||
cautionZone: Caution Zone
|
||||
editAsText: Edit as Text
|
||||
branchName: Branch name
|
||||
enterBranchPlaceholder: Enter the branch name here
|
||||
|
@ -553,9 +557,12 @@ missingPerms: 'You are missing the following permission:'
|
|||
createRepoPerms: 'Create / Edit Repository'
|
||||
missingPermsContent: '"{PERMS}" in project "{PROJECT}"'
|
||||
repositoryName: Repository name
|
||||
dangerDeleteRepo: Danger, are you sure you want to delete it?
|
||||
deleteRepo: Delete this repository
|
||||
deleteRepoMsg: Once a repository is deleted, it cannot be recovered. Proceed with caution.
|
||||
dangerDeletePipeline: Danger, are you sure you want to delete this pipeline?
|
||||
dangerDeleteProject: Danger, are you sure you want to delete this project?
|
||||
repoUpdate: Repository Updated
|
||||
deleteRepoText: Are you sure you want to delete the repository '{REPONAME}'?
|
||||
deleteRepoText: Are you sure you want to delete repository '{REPONAME}'?
|
||||
deleteRepoTitle: Delete the repository
|
||||
resolve: Resolve
|
||||
reactivate: Reactivate
|
||||
|
@ -738,6 +745,17 @@ repoDelete:
|
|||
deleteConfirm2: To confirm, type "{{repo}}" in the box below
|
||||
deleteToastSuccess: Repository deleted successfully
|
||||
deleteConfirmButton2: Delete this repository
|
||||
repoArchive:
|
||||
titleArchive: Archive Repository
|
||||
titleUnarchive: Unarchive Repository
|
||||
confirmButton: I understand, {archiveVerb} the repository.
|
||||
archiveWarning: Archiving this repository will mark it as inactive and set it to read-only. Users can still view and clone the repository, but no new changes or contributions will be allowed.
|
||||
unarchiveWarning: This repository will no longer be archived and will be restored to an active state. All users and groups with permissions can contribute as usual.
|
||||
archive: Archive this repository
|
||||
archiveInfo: Set this repository to archived and restrict it to read-only access.
|
||||
unarchive: Unarchive this repository
|
||||
unarchiveInfo: Set this repository to unarchived and make it read-write.
|
||||
infoText: This repository has been archived on {{date}}. It is now read-only.
|
||||
pipelines:
|
||||
noData: There are no pipelines
|
||||
import: Import Pipelines
|
||||
|
@ -964,7 +982,7 @@ enterGithubPlaceholder: https://api.github.com/
|
|||
enterBitbucketPlaceholder: https://bitbucket.org/
|
||||
changeRepoVis: Change repository visibility
|
||||
changeRepoVisContent: Are you sure you want to make this repository {repoVis}?
|
||||
confirmRepoVisButton: Yes, make the Repository {repoVis}
|
||||
confirmRepoVisButton: Yes, make the repository {repoVis}
|
||||
repoVisibility: Repository visibility
|
||||
visibility: Visibility
|
||||
attachText: Attach images & videos by dragging & dropping, selecting or pasting them.
|
||||
|
|
|
@ -49,7 +49,7 @@ import { useAppContext } from 'AppContext'
|
|||
import { LoadingSpinner } from 'components/LoadingSpinner/LoadingSpinner'
|
||||
import { NoResultCard } from 'components/NoResultCard/NoResultCard'
|
||||
import { ResourceListingPagination } from 'components/ResourceListingPagination/ResourceListingPagination'
|
||||
import { RepoPublicLabel } from 'components/RepoPublicLabel/RepoPublicLabel'
|
||||
import { RepoTypeLabel } from 'components/RepoTypeLabel/RepoTypeLabel'
|
||||
import KeywordSearch from 'components/CodeSearch/KeywordSearch'
|
||||
import { OptionsMenuButton } from 'components/OptionsMenuButton/OptionsMenuButton'
|
||||
import { useConfirmAct } from 'hooks/useConfirmAction'
|
||||
|
@ -200,7 +200,11 @@ export default function RepositoriesListing() {
|
|||
<Layout.Vertical flex className={css.name} ref={rowContainerRef}>
|
||||
<Text className={css.repoName} width={nameTextWidth} lineClamp={2}>
|
||||
<Keywords value={searchTerm}>{record.identifier}</Keywords>
|
||||
<RepoPublicLabel isPublic={row.original.is_public} margin={{ left: 'small' }} />
|
||||
<RepoTypeLabel
|
||||
isPublic={row.original.is_public}
|
||||
isArchived={row.original.archived}
|
||||
margin={{ left: 'small' }}
|
||||
/>
|
||||
</Text>
|
||||
|
||||
<Text className={css.desc} width={nameTextWidth} lineClamp={1}>
|
||||
|
|
|
@ -19,7 +19,7 @@ import { Layout, Text } from '@harnessio/uicore'
|
|||
import { BookmarkBook } from 'iconoir-react'
|
||||
|
||||
import { FontVariation } from '@harnessio/design-system'
|
||||
import { RepoPublicLabel } from 'components/RepoPublicLabel/RepoPublicLabel'
|
||||
import { RepoTypeLabel } from 'components/RepoTypeLabel/RepoTypeLabel'
|
||||
import type { GitInfoProps } from 'utils/GitUtils'
|
||||
import { RepositoryPageHeader } from 'components/RepositoryPageHeader/RepositoryPageHeader'
|
||||
import type { RepoRepositoryOutput } from 'services/code'
|
||||
|
@ -45,7 +45,7 @@ export function RepositoryHeader(props: RepositoryHeaderProps) {
|
|||
<Text inline className={css.repoDropdown} font={{ variation: FontVariation.H4 }}>
|
||||
{repoMetadata.identifier}
|
||||
</Text>
|
||||
<RepoPublicLabel isPublic={repoMetadata.is_public} />
|
||||
<RepoTypeLabel isPublic={repoMetadata.is_public} isArchived={repoMetadata.archived} />
|
||||
</Layout.Horizontal>
|
||||
}
|
||||
dataTooltipId="repositoryTitle"
|
||||
|
|
|
@ -0,0 +1,124 @@
|
|||
/*
|
||||
* 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 React from 'react'
|
||||
import {
|
||||
Container,
|
||||
Button,
|
||||
ButtonVariation,
|
||||
Dialog,
|
||||
Layout,
|
||||
Text,
|
||||
useToaster,
|
||||
StringSubstitute
|
||||
} from '@harnessio/uicore'
|
||||
import { Color, FontVariation } from '@harnessio/design-system'
|
||||
import { useUpdateRepository } from 'services/code'
|
||||
import { useModalHook } from 'hooks/useModalHook'
|
||||
import { useStrings } from 'framework/strings'
|
||||
import { useGetRepositoryMetadata } from 'hooks/useGetRepositoryMetadata'
|
||||
import { getErrorMessage } from 'utils/Utils'
|
||||
import css from '../../RepositorySettings.module.scss'
|
||||
|
||||
interface ArchiveRepoModalProps {
|
||||
refetch: () => void
|
||||
}
|
||||
|
||||
const useArchiveRepoModal = ({ refetch: refetchMetaData }: ArchiveRepoModalProps) => {
|
||||
const { repoMetadata } = useGetRepositoryMetadata()
|
||||
const { getString } = useStrings()
|
||||
const { mutate: archiveRepository, loading: archivingRepository } = useUpdateRepository({
|
||||
repo_ref: `${repoMetadata?.path as string}/+`
|
||||
})
|
||||
const { showSuccess, showError } = useToaster()
|
||||
const [openModal, hideModal] = useModalHook(() => {
|
||||
const onClose = () => {
|
||||
hideModal()
|
||||
}
|
||||
return (
|
||||
<Dialog
|
||||
className={css.dialogContainer}
|
||||
isOpen
|
||||
enforceFocus={false}
|
||||
onClose={onClose}
|
||||
title={
|
||||
<Text font={{ variation: FontVariation.H4 }}>
|
||||
{repoMetadata?.archived === true
|
||||
? getString('repoArchive.titleUnarchive')
|
||||
: getString('repoArchive.titleArchive')}
|
||||
</Text>
|
||||
}>
|
||||
<Layout.Vertical spacing="xlarge">
|
||||
<Container
|
||||
intent="warning"
|
||||
background="yellow100"
|
||||
border={{
|
||||
color: 'orange500'
|
||||
}}
|
||||
margin={{ top: 'medium', bottom: 'medium' }}>
|
||||
<Text
|
||||
icon="warning-outline"
|
||||
iconProps={{ size: 16, margin: { right: 'small' } }}
|
||||
padding={{ left: 'large', right: 'large', top: 'small', bottom: 'small' }}
|
||||
color={Color.WARNING}>
|
||||
{repoMetadata?.archived === true
|
||||
? getString('repoArchive.unarchiveWarning')
|
||||
: getString('repoArchive.archiveWarning')}
|
||||
</Text>
|
||||
</Container>
|
||||
<Layout.Horizontal className={css.buttonContainer}>
|
||||
<Button
|
||||
type="submit"
|
||||
variation={ButtonVariation.PRIMARY}
|
||||
margin={{ right: 'small' }}
|
||||
onClick={async () => {
|
||||
try {
|
||||
await archiveRepository({ state: repoMetadata?.archived ? 0 : 4 })
|
||||
showSuccess(getString('repoUpdate'))
|
||||
hideModal()
|
||||
refetchMetaData()
|
||||
} catch (exception) {
|
||||
showError(getErrorMessage(exception), 0, 'failed to archive the repository')
|
||||
}
|
||||
refetchMetaData()
|
||||
}}>
|
||||
<StringSubstitute
|
||||
str={getString('repoArchive.confirmButton')}
|
||||
vars={{
|
||||
archiveVerb: <span className={css.text}>{repoMetadata?.archived ? 'unarchive' : 'archive'}</span>
|
||||
}}
|
||||
/>
|
||||
</Button>
|
||||
<Button
|
||||
text={getString('cancel')}
|
||||
variation={ButtonVariation.TERTIARY}
|
||||
onClick={() => {
|
||||
hideModal()
|
||||
}}
|
||||
/>
|
||||
</Layout.Horizontal>
|
||||
</Layout.Vertical>
|
||||
</Dialog>
|
||||
)
|
||||
}, [archivingRepository, repoMetadata])
|
||||
|
||||
return {
|
||||
openModal,
|
||||
hideModal
|
||||
}
|
||||
}
|
||||
|
||||
export default useArchiveRepoModal
|
|
@ -111,7 +111,6 @@ const useDeleteRepoModal = () => {
|
|||
margin={{ top: 'small' }}
|
||||
onClick={async () => {
|
||||
try {
|
||||
// this isn't implemented in the backend yet
|
||||
await deleteRepo(`${repoMetadata?.path as string}/+/`)
|
||||
setShowConfirmPage(false)
|
||||
setDeleteConfirmString('')
|
||||
|
|
|
@ -39,12 +39,13 @@ import { useStrings } from 'framework/strings'
|
|||
import type { RepoRepositoryOutput } from 'services/code'
|
||||
import { useAppContext } from 'AppContext'
|
||||
import { useGetSpaceParam } from 'hooks/useGetSpaceParam'
|
||||
import { RepoVisibility } from 'utils/GitUtils'
|
||||
import { RepoVisibility, RepoState } from 'utils/GitUtils'
|
||||
import { BranchTagSelect } from 'components/BranchTagSelect/BranchTagSelect'
|
||||
import { useModalHook } from 'hooks/useModalHook'
|
||||
import { usePublicResourceConfig } from 'hooks/usePublicResourceConfig'
|
||||
import useDeleteRepoModal from './DeleteRepoModal/DeleteRepoModal'
|
||||
import useDefaultBranchModal from './DefaultBranchModal/DefaultBranchModal'
|
||||
import useArchiveRepoModal from './ArchiveRepoModal/ArchiveRepoModal'
|
||||
import Private from '../../../icons/private.svg?url'
|
||||
import css from '../RepositorySettings.module.scss'
|
||||
|
||||
|
@ -68,6 +69,8 @@ const GeneralSettingsContent = (props: GeneralSettingsProps) => {
|
|||
const { allowPublicResourceCreation } = usePublicResourceConfig()
|
||||
const { getString } = useStrings()
|
||||
const currRepoVisibility = repoMetadata?.is_public === true ? RepoVisibility.PUBLIC : RepoVisibility.PRIVATE
|
||||
const repoState = repoMetadata?.archived === true ? RepoState.ARCHIVED : RepoState.UNARCHIVED
|
||||
const { openModal: openArchiveRepoModal } = useArchiveRepoModal({ refetch })
|
||||
const [repoVis, setRepoVis] = useState<RepoVisibility>(currRepoVisibility)
|
||||
|
||||
const { mutate } = useMutate({
|
||||
|
@ -186,7 +189,7 @@ const GeneralSettingsContent = (props: GeneralSettingsProps) => {
|
|||
{formik => {
|
||||
return (
|
||||
<Layout.Vertical padding={{ top: 'medium' }}>
|
||||
<Container padding="large" margin={{ bottom: 'medium' }} className={css.generalContainer}>
|
||||
<Container padding="medium" margin={{ bottom: 'medium' }} className={css.generalContainer}>
|
||||
<Layout.Horizontal padding={{ bottom: 'medium' }}>
|
||||
<Container className={css.label}>
|
||||
<Text color={Color.GREY_600} className={css.textSize}>
|
||||
|
@ -409,20 +412,51 @@ const GeneralSettingsContent = (props: GeneralSettingsProps) => {
|
|||
</Layout.Horizontal>
|
||||
</Container>
|
||||
</Render>
|
||||
<Container padding="medium" className={css.generalContainer}>
|
||||
<Container className={css.deleteContainer}>
|
||||
<Text icon="main-trash" color={Color.GREY_600} font={{ size: 'small' }}>
|
||||
{getString('dangerDeleteRepo')}
|
||||
</Text>
|
||||
<Button
|
||||
intent={Intent.DANGER}
|
||||
onClick={() => {
|
||||
openDeleteRepoModal()
|
||||
}}
|
||||
variation={ButtonVariation.SECONDARY}
|
||||
text={getString('delete')}
|
||||
{...permissionProps(permDeleteResult, standalone)}></Button>
|
||||
</Container>
|
||||
<Container padding="medium" margin={{ bottom: 'medium' }} className={css.generalContainer}>
|
||||
<Layout.Horizontal padding={{ bottom: 'medium' }}>
|
||||
<Container className={css.label}>
|
||||
<Text color={Color.GREY_600} className={css.textSize} margin={{ top: 'medium' }}>
|
||||
{getString('cautionZone')}
|
||||
</Text>
|
||||
</Container>
|
||||
<Layout.Vertical>
|
||||
<Container className={css.cautionContainer}>
|
||||
<Layout.Vertical spacing="small" padding={{ right: 'large' }}>
|
||||
<Text font={{ size: 'small' }} className={css.textSize}>
|
||||
{repoState === RepoState.ARCHIVED
|
||||
? getString('repoArchive.unarchive')
|
||||
: getString('repoArchive.archive')}
|
||||
</Text>
|
||||
<Text font={{ variation: FontVariation.TINY }}>
|
||||
{repoState === RepoState.ARCHIVED
|
||||
? getString('repoArchive.unarchiveInfo')
|
||||
: getString('repoArchive.archiveInfo')}
|
||||
</Text>
|
||||
</Layout.Vertical>
|
||||
<Button
|
||||
onClick={openArchiveRepoModal}
|
||||
variation={ButtonVariation.SECONDARY}
|
||||
text={repoState === RepoState.ARCHIVED ? getString('unarchive') : getString('archive')}
|
||||
{...permissionProps(permEditResult, standalone)}
|
||||
/>
|
||||
</Container>
|
||||
<Container className={css.cautionContainer}>
|
||||
<Layout.Vertical spacing="small" padding={{ right: 'large' }}>
|
||||
<Text font={{ size: 'small' }} className={css.textSize}>
|
||||
{getString('deleteRepo')}
|
||||
</Text>
|
||||
<Text font={{ variation: FontVariation.TINY }}>{getString('deleteRepoMsg')}</Text>
|
||||
</Layout.Vertical>
|
||||
<Button
|
||||
intent={Intent.DANGER}
|
||||
onClick={openDeleteRepoModal}
|
||||
variation={ButtonVariation.SECONDARY}
|
||||
text={getString('delete')}
|
||||
{...permissionProps(permDeleteResult, standalone)}
|
||||
/>
|
||||
</Container>
|
||||
</Layout.Vertical>
|
||||
</Layout.Horizontal>
|
||||
</Container>
|
||||
</Layout.Vertical>
|
||||
)
|
||||
|
|
|
@ -104,9 +104,13 @@
|
|||
width: 80%;
|
||||
}
|
||||
|
||||
.deleteContainer {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
.cautionContainer {
|
||||
display: flex !important;
|
||||
justify-content: space-between !important;
|
||||
align-items: center;
|
||||
width: 100% !important;
|
||||
padding: var(--spacing-medium) var(--spacing-small) !important;
|
||||
white-space: nowrap !important;
|
||||
}
|
||||
|
||||
.text {
|
||||
|
|
|
@ -18,9 +18,9 @@
|
|||
// This is an auto-generated file
|
||||
export declare const btn: string
|
||||
export declare const buttonContainer: string
|
||||
export declare const cautionContainer: string
|
||||
export declare const content: string
|
||||
export declare const contentContainer: string
|
||||
export declare const deleteContainer: string
|
||||
export declare const description: string
|
||||
export declare const descText: string
|
||||
export declare const dialogContainer: string
|
||||
|
|
|
@ -479,7 +479,7 @@ export default function GeneralSpaceSettings() {
|
|||
<Container className={css.deleteContainer}>
|
||||
<Layout.Vertical className={css.verticalContainer}>
|
||||
<Text icon="main-trash" color={Color.GREY_600} font={{ size: 'small' }}>
|
||||
{getString('dangerDeleteRepo')}
|
||||
{getString('dangerDeleteProject')}
|
||||
</Text>
|
||||
<Layout.Horizontal
|
||||
padding={{ top: 'medium', left: 'medium' }}
|
||||
|
|
|
@ -667,6 +667,7 @@ export interface OpenapiUpdateRepoPublicAccessRequest {
|
|||
|
||||
export interface OpenapiUpdateRepoRequest {
|
||||
description?: string | null
|
||||
state?: number | null
|
||||
}
|
||||
|
||||
export interface OpenapiUpdateRepoWebhookRequest {
|
||||
|
@ -878,6 +879,7 @@ export interface RepoRepositoryOutput {
|
|||
importing?: boolean
|
||||
is_empty?: boolean
|
||||
is_public?: boolean
|
||||
archived?: boolean
|
||||
num_closed_pulls?: number
|
||||
num_forks?: number
|
||||
num_merged_pulls?: number
|
||||
|
|
|
@ -12758,6 +12758,8 @@ components:
|
|||
type: object
|
||||
RepoRepositoryOutput:
|
||||
properties:
|
||||
archived:
|
||||
type: boolean
|
||||
created:
|
||||
type: integer
|
||||
created_by:
|
||||
|
|
|
@ -103,6 +103,11 @@ export enum RepoVisibility {
|
|||
PRIVATE = 'private'
|
||||
}
|
||||
|
||||
export enum RepoState {
|
||||
ARCHIVED = 'archived',
|
||||
UNARCHIVED = 'unarchived'
|
||||
}
|
||||
|
||||
export enum RepoCreationType {
|
||||
IMPORT = 'import',
|
||||
CREATE = 'create',
|
||||
|
|
Loading…
Reference in New Issue