Make repo settings pages available for uncloned repos (#57609)

The repo settings pages contain a bunch of useful information for repos even when they aren't cloned yet - like log outputs, corruption logs, and ways to exclude the repo from cloning.
Thus, it should be visible before the repo is cloned successfully.
This also consolidates mirroring and options into one menu, like discussed.
This commit is contained in:
Erik Seliger 2023-10-19 21:15:16 +02:00 committed by GitHub
parent 8acc71a840
commit 29fbcfb868
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 305 additions and 230 deletions

View File

@ -1380,13 +1380,13 @@ ts_project(
"src/repo/settings/RepoSettingsArea.tsx",
"src/repo/settings/RepoSettingsIndexPage.tsx",
"src/repo/settings/RepoSettingsMirrorPage.tsx",
"src/repo/settings/RepoSettingsOptionsPage.tsx",
"src/repo/settings/RepoSettingsOptions.tsx",
"src/repo/settings/RepoSettingsSidebar.tsx",
"src/repo/settings/backend.ts",
"src/repo/settings/components/ActionContainer.tsx",
"src/repo/settings/components/ExternalServiceEntry.tsx",
"src/repo/settings/components/RedirectionAlert.tsx",
"src/repo/settings/routes.ts",
"src/repo/settings/routes.tsx",
"src/repo/settings/sidebaritems.ts",
"src/repo/stats/RepositoryStatsArea.tsx",
"src/repo/stats/RepositoryStatsContributorsPage.tsx",

View File

@ -91,9 +91,9 @@ export const RepoSettingsPermissionsPage: FC<RepoSettingsPermissionsPageProps> =
return (
<>
<PageTitle title="Permissions" />
<PageTitle title="Repo Permissions" />
<PageHeader
path={[{ text: 'Permissions' }]}
path={[{ text: 'Repo Permissions' }]}
headingElement="h2"
className="mb-3"
description={

View File

@ -19,7 +19,7 @@ export const enterpriseRepoSettingsSidebarGroups: RepoSettingsSideBarGroups =
{
to: '/permissions',
exact: true,
label: 'Permissions',
label: 'Repo Permissions',
},
],
},

View File

@ -25,7 +25,7 @@ import type { SettingsCascadeProps } from '@sourcegraph/shared/src/settings/sett
import type { TelemetryProps } from '@sourcegraph/shared/src/telemetry/telemetryService'
import { lazyComponent } from '@sourcegraph/shared/src/util/lazyComponent'
import { makeRepoURI } from '@sourcegraph/shared/src/util/url'
import { Panel, useObservable } from '@sourcegraph/wildcard'
import { LoadingSpinner, Panel, useObservable } from '@sourcegraph/wildcard'
import type { AuthenticatedUser } from '../auth'
import type { BatchChangesProps } from '../batches'
@ -48,7 +48,7 @@ import type { RouteV6Descriptor } from '../util/contributions'
import { parseBrowserRepoURL } from '../util/url'
import { GoToCodeHostAction } from './actions/GoToCodeHostAction'
import { fetchFileExternalLinks, type ResolvedRevision, resolveRepoRevision } from './backend'
import { fetchFileExternalLinks, type ResolvedRevision, resolveRepoRevision, Repo } from './backend'
import { AskCodyButton } from './cody/AskCodyButton'
import { RepoContainerError } from './RepoContainerError'
import { RepoHeader, type RepoHeaderActionButton, type RepoHeaderContributionsLifecycleProps } from './RepoHeader'
@ -142,35 +142,11 @@ export interface HoverThresholdProps {
* Renders a horizontal bar and content for a repository page.
*/
export const RepoContainer: FC<RepoContainerProps> = props => {
const { extensionsController, repoContainerRoutes, authenticatedUser, selectedSearchContextSpec } = props
const { authenticatedUser } = props
const location = useLocation()
const { repoName, revision, rawRevision, filePath, commitRange, position, range } = parseBrowserRepoURL(
location.pathname + location.search + location.hash
)
const {
isSidebarOpen: isCodySidebarOpen,
setIsSidebarOpen: setIsCodySidebarOpen,
scope,
setEditorScope,
logTranscriptEvent,
} = useCodySidebar()
const { sidebarSize, setSidebarSize: setCodySidebarSize } = useSidebarSize()
/* eslint-disable react-hooks/exhaustive-deps */
const codySidebarSize = useMemo(() => sidebarSize, [isCodySidebarOpen])
/* eslint-enable react-hooks/exhaustive-deps */
useEffect(() => {
const activeEditor = scope.editor.getActiveTextEditor()
if (activeEditor?.repoName !== repoName) {
setEditorScope(new RepoContainerEditor(repoName))
}
}, [scope.editor, repoName, setEditorScope])
const { repoName, revision, rawRevision } = parseBrowserRepoURL(location.pathname + location.search + location.hash)
const resolvedRevisionOrError = useObservable(
useMemo(
@ -207,8 +183,6 @@ export const RepoContainer: FC<RepoContainerProps> = props => {
)
)
const focusCodyShortcut = useKeyboardShortcut('focusCody')
/**
* A long time ago, we fetched `repo` in a separate GraphQL query.
* This GraphQL query was merged into the `resolveRevision` query to
@ -218,9 +192,6 @@ export const RepoContainer: FC<RepoContainerProps> = props => {
*/
const repoOrError = isErrorLike(resolvedRevisionOrError) ? resolvedRevisionOrError : resolvedRevisionOrError?.repo
// The external links to show in the repository header, if any.
const [externalLinks, setExternalLinks] = useState<ExternalLinkFields[] | undefined>()
// The lifecycle props for repo header contributions.
const [repoHeaderContributionsLifecycleProps, setRepoHeaderContributionsLifecycleProps] =
useState<RepoHeaderContributionsLifecycleProps>()
@ -244,6 +215,115 @@ export const RepoContainer: FC<RepoContainerProps> = props => {
}, [resolvedRevisionOrError, repoOrError, repoName])
)
// must exactly match how the revision was encoded in the URL
const repoNameAndRevision = `${repoName}${typeof rawRevision === 'string' ? `@${rawRevision}` : ''}`
return (
<>
<div className={classNames('w-100 d-flex flex-row')}>
<div className={classNames('w-100 d-flex flex-column', styles.repoContainer)}>
<RepoHeader
actionButtons={props.repoHeaderActionButtons}
breadcrumbs={props.breadcrumbs}
repoName={repoName}
revision={revision}
onLifecyclePropsChange={setRepoHeaderContributionsLifecycleProps}
settingsCascade={props.settingsCascade}
authenticatedUser={authenticatedUser}
platformContext={props.platformContext}
telemetryService={props.telemetryService}
/>
<Suspense fallback={<LoadingSpinner />}>
<Routes>
{props.authenticatedUser?.siteAdmin && (
<Route
path={repoNameAndRevision + repoSettingsAreaPath}
errorElement={<RouteError />}
// Always render the `RepoSettingsArea` even for empty repo to allow side-admins access it.
element={
<RepoSettingsArea
repoName={repoName}
authenticatedUser={props.authenticatedUser}
repoSettingsAreaRoutes={props.repoSettingsAreaRoutes}
repoSettingsSidebarGroups={props.repoSettingsSidebarGroups}
setBreadcrumb={childBreadcrumbSetters.setBreadcrumb}
useBreadcrumb={childBreadcrumbSetters.useBreadcrumb}
telemetryService={props.telemetryService}
/>
}
/>
)}
<Route
path="*"
errorElement={<RouteError />}
element={
<RepoUserContainer
{...props}
childBreadcrumbSetters={childBreadcrumbSetters}
repoOrError={repoOrError}
resolvedRevisionOrError={resolvedRevisionOrError}
repoHeaderContributionsLifecycleProps={repoHeaderContributionsLifecycleProps}
/>
}
/>
</Routes>
</Suspense>
</div>
</div>
</>
)
}
interface RepoUserContainerProps extends RepoContainerProps {
repoHeaderContributionsLifecycleProps?: RepoHeaderContributionsLifecycleProps
resolvedRevisionOrError: (ResolvedRevision & Repo) | ErrorLike | undefined
repoOrError: ErrorLike | RepositoryFields | undefined
childBreadcrumbSetters: BreadcrumbSetters
}
const RepoUserContainer: FC<RepoUserContainerProps> = ({
resolvedRevisionOrError,
repoOrError,
childBreadcrumbSetters,
repoHeaderContributionsLifecycleProps,
...props
}) => {
const { extensionsController, repoContainerRoutes, authenticatedUser, selectedSearchContextSpec } = props
const location = useLocation()
const { repoName, revision, rawRevision, filePath, commitRange, position, range } = parseBrowserRepoURL(
location.pathname + location.search + location.hash
)
const {
isSidebarOpen: isCodySidebarOpen,
setIsSidebarOpen: setIsCodySidebarOpen,
scope,
setEditorScope,
logTranscriptEvent,
} = useCodySidebar()
const { sidebarSize, setSidebarSize: setCodySidebarSize } = useSidebarSize()
/* eslint-disable react-hooks/exhaustive-deps */
const codySidebarSize = useMemo(() => sidebarSize, [isCodySidebarOpen])
/* eslint-enable react-hooks/exhaustive-deps */
useEffect(() => {
const activeEditor = scope.editor.getActiveTextEditor()
if (activeEditor?.repoName !== repoName) {
setEditorScope(new RepoContainerEditor(repoName))
}
}, [scope.editor, repoName, setEditorScope])
const focusCodyShortcut = useKeyboardShortcut('focusCody')
// The external links to show in the repository header, if any.
const [externalLinks, setExternalLinks] = useState<ExternalLinkFields[] | undefined>()
// Update the workspace roots service to reflect the current repo / resolved revision
useEffect(() => {
const workspaceRootUri =
@ -363,151 +443,128 @@ export const RepoContainer: FC<RepoContainerProps> = props => {
}}
/>
))}
<div className={classNames('w-100 d-flex flex-row')}>
<div className={classNames('w-100 d-flex flex-column', styles.repoContainer)}>
<RepoHeader
actionButtons={props.repoHeaderActionButtons}
breadcrumbs={props.breadcrumbs}
<RepoHeaderContributionPortal
position="right"
priority={1}
id="cody"
{...repoHeaderContributionsLifecycleProps}
>
{() =>
!isCodySidebarOpen ? (
<AskCodyButton
onClick={() => {
logTranscriptEvent(EventName.CODY_SIDEBAR_CHAT_OPENED, { repo, path: filePath })
setIsCodySidebarOpen(true)
}}
/>
) : null
}
</RepoHeaderContributionPortal>
<RepoHeaderContributionPortal
position="right"
priority={2}
id="go-to-code-host"
{...repoHeaderContributionsLifecycleProps}
>
{({ actionType }) => (
<GoToCodeHostAction
repo={repo}
repoName={repoName}
revision={revision}
onLifecyclePropsChange={setRepoHeaderContributionsLifecycleProps}
settingsCascade={props.settingsCascade}
authenticatedUser={authenticatedUser}
platformContext={props.platformContext}
telemetryService={props.telemetryService}
// We need a revision to generate code host URLs, if revision isn't available, we use the default branch or HEAD.
revision={rawRevision || repo?.defaultBranch?.displayName || 'HEAD'}
filePath={filePath}
commitRange={commitRange}
range={range}
position={position}
perforceCodeHostUrlToSwarmUrlMap={perforceCodeHostUrlToSwarmUrlMap}
fetchFileExternalLinks={fetchFileExternalLinks}
actionType={actionType}
source="repoHeader"
key="go-to-code-host"
externalLinks={externalLinks}
/>
)}
</RepoHeaderContributionPortal>
<RepoHeaderContributionPortal
position="right"
priority={1}
id="cody"
{...repoHeaderContributionsLifecycleProps}
>
{() =>
!isCodySidebarOpen ? (
<AskCodyButton
onClick={() => {
logTranscriptEvent(EventName.CODY_SIDEBAR_CHAT_OPENED, { repo, path: filePath })
setIsCodySidebarOpen(true)
}}
/>
) : null
}
</RepoHeaderContributionPortal>
<RepoHeaderContributionPortal
position="right"
priority={2}
id="go-to-code-host"
{...repoHeaderContributionsLifecycleProps}
>
{({ actionType }) => (
<GoToCodeHostAction
repo={repo}
{isBrainDotVisible && (
<RepoHeaderContributionPortal
position="right"
priority={110}
id="code-intelligence-status"
{...repoHeaderContributionsLifecycleProps}
>
{({ actionType }) =>
props.brainDot && actionType === 'nav' ? (
<props.brainDot
key="code-intelligence-status"
repoName={repoName}
// We need a revision to generate code host URLs, if revision isn't available, we use the default branch or HEAD.
revision={rawRevision || repo?.defaultBranch?.displayName || 'HEAD'}
filePath={filePath}
commitRange={commitRange}
range={range}
position={position}
perforceCodeHostUrlToSwarmUrlMap={perforceCodeHostUrlToSwarmUrlMap}
fetchFileExternalLinks={fetchFileExternalLinks}
actionType={actionType}
source="repoHeader"
key="go-to-code-host"
externalLinks={externalLinks}
path={filePath}
commit={resolvedRevision?.commitID ?? ''}
/>
)}
</RepoHeaderContributionPortal>
) : null
}
</RepoHeaderContributionPortal>
)}
{isBrainDotVisible && (
<RepoHeaderContributionPortal
position="right"
priority={110}
id="code-intelligence-status"
{...repoHeaderContributionsLifecycleProps}
>
{({ actionType }) =>
props.brainDot && actionType === 'nav' ? (
<props.brainDot
key="code-intelligence-status"
repoName={repoName}
path={filePath}
commit={resolvedRevision?.commitID ?? ''}
/>
<Suspense fallback={<LoadingSpinner />}>
<Routes>
{repoContainerRoutes.map(({ path, render, condition = () => true }) => (
<Route
key="hardcoded-key" // see https://github.com/ReactTraining/react-router/issues/4578#issuecomment-334489490
path={repoNameAndRevision + path}
errorElement={<RouteError />}
element={
/**
* `repoContainerRoutes` depend on `repo`. We render these routes only when
* the `repo` value is resolved. If repo resolves to error due to empty repository
* then we return Empty Repository.
*/
repo && condition({ ...repoContainerContext, repo }) ? (
render({ ...repoContainerContext, repo })
) : isEmptyRepo ? (
<EmptyRepo />
) : null
}
</RepoHeaderContributionPortal>
)}
<Suspense fallback={null}>
<Routes>
{repoContainerRoutes.map(({ path, render, condition = () => true }) => (
<Route
key="hardcoded-key" // see https://github.com/ReactTraining/react-router/issues/4578#issuecomment-334489490
path={repoNameAndRevision + path}
errorElement={<RouteError />}
element={
/**
* `repoContainerRoutes` depend on `repo`. We render these routes only when
* the `repo` value is resolved. If repo resolves to error due to empty repository
* then we return Empty Repository.
*/
repo && condition({ ...repoContainerContext, repo }) ? (
render({ ...repoContainerContext, repo })
) : isEmptyRepo ? (
<EmptyRepo />
) : null
}
/>
))}
{props.authenticatedUser?.siteAdmin && (
<Route
path={repoNameAndRevision + repoSettingsAreaPath}
errorElement={<RouteError />}
// Always render the `RepoSettingsArea` even for empty repo to allow side-admins access it.
element={<RepoSettingsArea {...repoRevisionContainerContext} repoName={repoName} />}
/>
)}
<Route
key="hardcoded-key" // see https://github.com/ReactTraining/react-router/issues/4578#issuecomment-334489490
path={repoNameAndRevision + '/*'}
errorElement={<RouteError />}
element={
isEmptyRepo ? (
<EmptyRepo />
) : (
<RepoRevisionContainer
{...repoRevisionContainerContext}
{...childBreadcrumbSetters}
routes={props.repoRevisionContainerRoutes}
/>
)
}
/>
</Routes>
</Suspense>
</div>
{isCodySidebarOpen && (
<Panel
className="cody-sidebar-panel"
position="right"
ariaLabel="Cody sidebar"
maxSize={CODY_SIDEBAR_SIZES.max}
minSize={CODY_SIDEBAR_SIZES.min}
defaultSize={codySidebarSize || CODY_SIDEBAR_SIZES.default}
storageKey="size-cache-cody-sidebar"
onResize={setCodySidebarSize}
>
<CodySidebar
onClose={() => setIsCodySidebarOpen(false)}
authenticatedUser={props.authenticatedUser}
/>
</Panel>
)}
</div>
))}
<Route
key="hardcoded-key" // see https://github.com/ReactTraining/react-router/issues/4578#issuecomment-334489490
path={repoNameAndRevision + '/*'}
errorElement={<RouteError />}
element={
isEmptyRepo ? (
<EmptyRepo />
) : (
<RepoRevisionContainer
{...repoRevisionContainerContext}
{...childBreadcrumbSetters}
routes={props.repoRevisionContainerRoutes}
/>
)
}
/>
</Routes>
</Suspense>
{isCodySidebarOpen && (
<Panel
className="cody-sidebar-panel"
position="right"
ariaLabel="Cody sidebar"
maxSize={CODY_SIDEBAR_SIZES.max}
minSize={CODY_SIDEBAR_SIZES.min}
defaultSize={codySidebarSize || CODY_SIDEBAR_SIZES.default}
storageKey="size-cache-cody-sidebar"
onResize={setCodySidebarSize}
>
<CodySidebar
onClose={() => setIsCodySidebarOpen(false)}
authenticatedUser={props.authenticatedUser}
/>
</Panel>
)}
</>
)
}

View File

@ -10,7 +10,7 @@ import {
} from '@sourcegraph/shared/src/backend/errors'
import { RepoQuestionIcon } from '@sourcegraph/shared/src/components/icons'
import { displayRepoName } from '@sourcegraph/shared/src/components/RepoLink'
import { Code, ErrorMessage } from '@sourcegraph/wildcard'
import { Code, ErrorMessage, Link, Text } from '@sourcegraph/wildcard'
import { HeroPage } from '../components/HeroPage'
@ -41,15 +41,38 @@ export const RepoContainerError: React.FunctionComponent<React.PropsWithChildren
icon={SourceRepositoryIcon}
title={displayRepoName(repoName)}
className="repository-cloning-in-progress-page"
subtitle="Cloning in progress"
detail={<Code>{(repoFetchError as CloneInProgressError).progress}</Code>}
subtitle={<Text>Cloning in progress</Text>}
detail={
<>
<Code>{(repoFetchError as CloneInProgressError).progress}</Code>
{viewerCanAdminister && (
<Text className="mt-4">
<Link to={`${repoName}/-/settings`}>Go to settings</Link> to view details
</Text>
)}
</>
}
body={<DirectImportRepoAlert className="mt-3" />}
/>
)
}
if (isRevisionNotFoundErrorLike(repoFetchError)) {
return <HeroPage icon={RepoQuestionIcon} title="Empty repository" />
return (
<HeroPage
icon={RepoQuestionIcon}
title="Empty repository"
detail={
<>
{viewerCanAdminister && (
<Text>
<Link to={`${repoName}/-/settings`}>Go to settings</Link>
</Text>
)}
</>
}
/>
)
}
return <HeroPage icon={AlertCircleIcon} title="Error" subtitle={<ErrorMessage error={repoFetchError} />} />

View File

@ -265,7 +265,7 @@ export class RepoSettingsIndexPage extends React.PureComponent<Props, State> {
<>
<PageTitle title="Index settings" />
<PageHeader
path={[{ text: 'Indexing' }]}
path={[{ text: 'Search Indexing' }]}
headingElement="h2"
className="mb-3"
description={

View File

@ -46,6 +46,7 @@ import { DirectImportRepoAlert } from '../DirectImportRepoAlert'
import { FETCH_SETTINGS_AREA_REPOSITORY_GQL } from './backend'
import { ActionContainer, BaseActionContainer } from './components/ActionContainer'
import { RepoSettingsOptions } from './RepoSettingsOptions'
import styles from './RepoSettingsMirrorPage.module.scss'
@ -83,7 +84,7 @@ const UpdateMirrorRepositoryActionContainer: FC<UpdateMirrorRepositoryActionCont
</span>
)
buttonDisabled = true
info = <DirectImportRepoAlert className={styles.alert} />
info = <DirectImportRepoAlert className={classNames(styles.alert, 'mb-0')} />
} else if (props.repo.mirrorInfo.cloned) {
const updateSchedule = props.repo.mirrorInfo.updateSchedule
title = (
@ -244,35 +245,33 @@ const CorruptionLogsContainer: FC<CorruptionLogProps> = props => {
title="Repository corruption"
titleAs="h3"
description={<span>Recent corruption events that have been detected on this repository.</span>}
className="mb-0"
details={
<div className="flex-1">
{health}
<Collapse isOpen={isOpened} onOpenChange={setIsOpened}>
<CollapseHeader
as={Button}
outline={true}
focusLocked={true}
variant="secondary"
className="w-100 my-2"
disabled={!hasLogs}
>
{hasLogs ? (
<>
Show corruption history
<Icon
aria-hidden={true}
svgPath={isOpened ? mdiChevronUp : mdiChevronDown}
className="mr-1"
/>
</>
) : (
'No corruption history'
)}
</CollapseHeader>
<CollapsePanel>
<ul className="list-group">{logEvents}</ul>
</CollapsePanel>
</Collapse>
{!hasLogs && <Text className="mt-3 text-muted text-center mb-0">No corruption history</Text>}
{hasLogs && (
<Collapse isOpen={isOpened} onOpenChange={setIsOpened}>
<CollapseHeader
as={Button}
outline={true}
focusLocked={true}
variant="secondary"
className="w-100 my-2"
disabled={!hasLogs}
>
Show corruption history
<Icon
aria-hidden={true}
svgPath={isOpened ? mdiChevronUp : mdiChevronDown}
className="mr-1"
/>
</CollapseHeader>
<CollapsePanel>
<ul className="list-group">{logEvents}</ul>
</CollapsePanel>
</Collapse>
)}
</div>
}
/>
@ -312,6 +311,7 @@ export const RepoSettingsMirrorPage: FC<RepoSettingsMirrorPageProps> = props =>
<>
<PageTitle title="Mirror settings" />
<PageHeader path={[{ text: 'Mirroring and cloning' }]} headingElement="h2" className="mb-3" />
<RepoSettingsOptions repo={repo} />
<Container className="repo-settings-mirror-page">
{error && <ErrorAlert error={error} />}
@ -319,15 +319,16 @@ export const RepoSettingsMirrorPage: FC<RepoSettingsMirrorPageProps> = props =>
<Label>
{' '}
Remote repository URL{' '}
<small className="text-info">
<Icon aria-hidden={true} svgPath={mdiLock} /> Only visible to site admins
<small className="text-muted">
<Icon aria-hidden={true} svgPath={mdiLock} className="text-warning" /> Only visible to site
admins
</small>
</Label>
<Input value={repo.mirrorInfo.remoteURL || '(unknown)'} readOnly={true} className="mb-0" />
{repo.viewerCanAdminister && (
<small className="form-text text-muted">
Configure repository mirroring in{' '}
<Link to="/site-admin/external-services">external services</Link>.
<Link to="/site-admin/external-services">code host connections</Link>.
</small>
)}
</div>

View File

@ -3,10 +3,9 @@ import { type FC, useCallback, useEffect, useState } from 'react'
import { noop } from 'lodash'
import { useMutation, useQuery } from '@sourcegraph/http-client'
import { Button, Container, ErrorAlert, H2, LoadingSpinner, PageHeader, renderError, Text } from '@sourcegraph/wildcard'
import { Button, Container, ErrorAlert, H3, LoadingSpinner, renderError, Text } from '@sourcegraph/wildcard'
import { CopyableText } from '../../components/CopyableText'
import { PageTitle } from '../../components/PageTitle'
import type {
ExcludeRepoFromExternalServicesResult,
ExcludeRepoFromExternalServicesVariables,
@ -23,16 +22,13 @@ import { EXCLUDE_REPO_FROM_EXTERNAL_SERVICES, FETCH_SETTINGS_AREA_REPOSITORY_GQL
import { ExternalServiceEntry } from './components/ExternalServiceEntry'
import { RedirectionAlert } from './components/RedirectionAlert'
import styles from './RepoSettingsOptionsPage.module.scss'
import styles from './RepoSettingsOptions.module.scss'
interface Props {
repo: SettingsAreaRepositoryFields
}
/**
* The repository settings options page.
*/
export const RepoSettingsOptionsPage: FC<Props> = ({ repo }) => {
export const RepoSettingsOptions: FC<Props> = ({ repo }) => {
useEffect(() => {
eventLogger.logViewEvent('RepoSettings')
})
@ -71,12 +67,10 @@ export const RepoSettingsOptionsPage: FC<Props> = ({ repo }) => {
return (
<>
<PageTitle title="Repository settings" />
<PageHeader path={[{ text: 'Settings' }]} headingElement="h2" className="mb-3" />
<Container className="repo-settings-options-page">
<H2 className="mb-3">Repository name</H2>
<Container className="mb-3 repo-settings-options-page">
<H3>Repository name</H3>
<CopyableText className="mb-3" text={repo.name} size={repo.name.length} />
<H2 className="mb-3">Code host connections</H2>
<H3>Code host connections</H3>
{loading && <LoadingSpinner />}
{error && <ErrorAlert error={error} />}
{services && services.length > 0 && (

View File

@ -1,5 +1,7 @@
import { lazyComponent } from '@sourcegraph/shared/src/util/lazyComponent'
import { RedirectRoute } from '../../components/RedirectRoute'
import type { RepoSettingsAreaRoute } from './RepoSettingsArea'
export const repoSettingsAreaPath = '/-/settings/*'
@ -7,7 +9,7 @@ export const repoSettingsAreaPath = '/-/settings/*'
export const repoSettingsAreaRoutes: readonly RepoSettingsAreaRoute[] = [
{
path: '',
render: lazyComponent(() => import('./RepoSettingsOptionsPage'), 'RepoSettingsOptionsPage'),
render: lazyComponent(() => import('./RepoSettingsMirrorPage'), 'RepoSettingsMirrorPage'),
},
{
path: '/index',
@ -15,6 +17,9 @@ export const repoSettingsAreaRoutes: readonly RepoSettingsAreaRoute[] = [
},
{
path: '/mirror',
render: lazyComponent(() => import('./RepoSettingsMirrorPage'), 'RepoSettingsMirrorPage'),
// The /mirror page used to be separate but we combined this one and the
// '' route above, so we redirect here in case people still link to this
// page.
render: () => <RedirectRoute getRedirectURL={() => '..'} />,
},
]

View File

@ -8,17 +8,12 @@ export const settingsGroup = {
{
to: '',
exact: true,
label: 'Options',
label: 'Mirroring',
},
{
to: '/index',
exact: true,
label: 'Indexing',
},
{
to: '/mirror',
exact: true,
label: 'Mirroring',
label: 'Search Indexing',
},
],
}