mirror of
https://github.com/harness/drone.git
synced 2025-05-06 18:52:05 +08:00
Initial skeleton for Repository Resource View flow (#30)
* API integration for Repos listing page * Add custom branch when creating a repo * Skeleton for Repository Resource View flow
This commit is contained in:
parent
1cf07b6417
commit
6cadb048ba
1
web/.gitignore
vendored
1
web/.gitignore
vendored
@ -3,3 +3,4 @@ dist
|
|||||||
coverage
|
coverage
|
||||||
.env
|
.env
|
||||||
yarn-error*
|
yarn-error*
|
||||||
|
.DS_Store
|
@ -35,7 +35,6 @@ module.exports = {
|
|||||||
'./App': './src/App.tsx',
|
'./App': './src/App.tsx',
|
||||||
'./Welcome': './src/views/Welcome/Welcome.tsx',
|
'./Welcome': './src/views/Welcome/Welcome.tsx',
|
||||||
'./Repos': './src/views/Repos/Repos.tsx',
|
'./Repos': './src/views/Repos/Repos.tsx',
|
||||||
'./NewRepo': './src/views/NewRepo/NewRepo.tsx',
|
|
||||||
'./RepoFiles': './src/views/RepoFiles/RepoFiles.tsx',
|
'./RepoFiles': './src/views/RepoFiles/RepoFiles.tsx',
|
||||||
'./RepoFileDetails': './src/views/RepoFileDetails/RepoFileDetails.tsx',
|
'./RepoFileDetails': './src/views/RepoFileDetails/RepoFileDetails.tsx',
|
||||||
'./RepoCommits': './src/views/RepoCommits/RepoCommits.tsx',
|
'./RepoCommits': './src/views/RepoCommits/RepoCommits.tsx',
|
||||||
|
@ -34,10 +34,10 @@ const devConfig = {
|
|||||||
hot: true,
|
hot: true,
|
||||||
host: 'localhost',
|
host: 'localhost',
|
||||||
historyApiFallback: true,
|
historyApiFallback: true,
|
||||||
port: 3000,
|
port: 3001,
|
||||||
proxy: {
|
proxy: {
|
||||||
'/api': {
|
'/api': {
|
||||||
target: targetLocalHost ? 'http://localhost:3001' : baseUrl,
|
target: targetLocalHost ? 'http://localhost:3000' : baseUrl,
|
||||||
logLevel: 'debug',
|
logLevel: 'debug',
|
||||||
secure: false,
|
secure: false,
|
||||||
changeOrigin: true
|
changeOrigin: true
|
||||||
|
@ -88,7 +88,6 @@
|
|||||||
"react-popper": "^2.2.3",
|
"react-popper": "^2.2.3",
|
||||||
"react-qr-code": "^1.1.1",
|
"react-qr-code": "^1.1.1",
|
||||||
"react-router-dom": "^5.2.0",
|
"react-router-dom": "^5.2.0",
|
||||||
"react-shadow": "^19.0.3",
|
|
||||||
"react-split-pane": "^0.1.92",
|
"react-split-pane": "^0.1.92",
|
||||||
"react-table": "^7.1.0",
|
"react-table": "^7.1.0",
|
||||||
"react-table-sticky": "^1.1.3",
|
"react-table-sticky": "^1.1.3",
|
||||||
|
@ -29,9 +29,10 @@ const App: React.FC<AppProps> = React.memo(function App({
|
|||||||
}: AppProps) {
|
}: AppProps) {
|
||||||
const [strings, setStrings] = useState<LanguageRecord>()
|
const [strings, setStrings] = useState<LanguageRecord>()
|
||||||
const [token, setToken] = useAPIToken(apiToken)
|
const [token, setToken] = useAPIToken(apiToken)
|
||||||
const getRequestOptions = useCallback((): Partial<RequestInit> => {
|
const getRequestOptions = useCallback(
|
||||||
return buildResfulReactRequestOptions(hooks.useGetToken?.() || apiToken || 'default')
|
(): Partial<RequestInit> => buildResfulReactRequestOptions(hooks.useGetToken?.() || apiToken || token),
|
||||||
}, []) // eslint-disable-line react-hooks/exhaustive-deps
|
[apiToken, token, hooks]
|
||||||
|
) // eslint-disable-line react-hooks/exhaustive-deps
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
languageLoader(lang).then(setStrings)
|
languageLoader(lang).then(setStrings)
|
||||||
|
@ -9,6 +9,8 @@ interface PathProps {
|
|||||||
commitId?: string
|
commitId?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface QueryProps {}
|
||||||
|
|
||||||
export const pathProps: Readonly<Required<PathProps>> = {
|
export const pathProps: Readonly<Required<PathProps>> = {
|
||||||
accountId: ':accountId',
|
accountId: ':accountId',
|
||||||
orgIdentifier: ':orgIdentifier',
|
orgIdentifier: ':orgIdentifier',
|
||||||
@ -37,10 +39,6 @@ export default {
|
|||||||
({ orgIdentifier, projectIdentifier }: PathProps) =>
|
({ orgIdentifier, projectIdentifier }: PathProps) =>
|
||||||
`/scm/orgs/${orgIdentifier}/projects/${projectIdentifier}/repos`
|
`/scm/orgs/${orgIdentifier}/projects/${projectIdentifier}/repos`
|
||||||
),
|
),
|
||||||
toSCMNewRepo: withAccountId(
|
|
||||||
({ orgIdentifier, projectIdentifier }: PathProps) =>
|
|
||||||
`/scm/orgs/${orgIdentifier}/projects/${projectIdentifier}/repos/new`
|
|
||||||
),
|
|
||||||
toSCMRepoSettings: withAccountId(
|
toSCMRepoSettings: withAccountId(
|
||||||
({ orgIdentifier, projectIdentifier, repoName }: RequireField<PathProps, 'repoName'>) =>
|
({ orgIdentifier, projectIdentifier, repoName }: RequireField<PathProps, 'repoName'>) =>
|
||||||
`/scm/orgs/${orgIdentifier}/projects/${projectIdentifier}/repos/${repoName}/settings`
|
`/scm/orgs/${orgIdentifier}/projects/${projectIdentifier}/repos/${repoName}/settings`
|
||||||
|
@ -4,7 +4,6 @@ import { SignIn } from 'pages/SignIn/SignIn'
|
|||||||
import { SignUp } from 'pages/SignUp/SignUp'
|
import { SignUp } from 'pages/SignUp/SignUp'
|
||||||
import routes, { pathProps } from 'RouteDefinitions'
|
import routes, { pathProps } from 'RouteDefinitions'
|
||||||
import Welcome from 'views/Welcome/Welcome'
|
import Welcome from 'views/Welcome/Welcome'
|
||||||
import NewRepo from 'views/NewRepo/NewRepo'
|
|
||||||
import Repos from 'views/Repos/Repos'
|
import Repos from 'views/Repos/Repos'
|
||||||
import RepoSettings from 'views/RepoSettings/RepoSettings'
|
import RepoSettings from 'views/RepoSettings/RepoSettings'
|
||||||
import RepoFiles from 'views/RepoFiles/RepoFiles'
|
import RepoFiles from 'views/RepoFiles/RepoFiles'
|
||||||
@ -27,9 +26,6 @@ export const RouteDestinations: React.FC = React.memo(function RouteDestinations
|
|||||||
<Route path={routes.toSCMHome(pathProps)}>
|
<Route path={routes.toSCMHome(pathProps)}>
|
||||||
<Welcome />
|
<Welcome />
|
||||||
</Route>
|
</Route>
|
||||||
<Route path={routes.toSCMNewRepo(pathProps)}>
|
|
||||||
<NewRepo />
|
|
||||||
</Route>
|
|
||||||
<Route path={routes.toSCMRepos(pathProps)} exact>
|
<Route path={routes.toSCMRepos(pathProps)} exact>
|
||||||
<Repos />
|
<Repos />
|
||||||
</Route>
|
</Route>
|
||||||
|
@ -9,6 +9,6 @@ import './bootstrap.scss'
|
|||||||
window.STRIP_SCM_PREFIX = true
|
window.STRIP_SCM_PREFIX = true
|
||||||
|
|
||||||
ReactDOM.render(
|
ReactDOM.render(
|
||||||
<App standalone accountId="default" apiToken="default" hooks={{}} components={{}} />,
|
<App standalone accountId="default" apiToken="" hooks={{}} components={{}} />,
|
||||||
document.getElementById('react-root')
|
document.getElementById('react-root')
|
||||||
)
|
)
|
||||||
|
@ -3,17 +3,29 @@
|
|||||||
* Use the command `yarn strings` to regenerate this file.
|
* Use the command `yarn strings` to regenerate this file.
|
||||||
*/
|
*/
|
||||||
export interface StringsMap {
|
export interface StringsMap {
|
||||||
|
addGitIgnore: string
|
||||||
|
addLicense: string
|
||||||
|
addReadMe: string
|
||||||
cancel: string
|
cancel: string
|
||||||
commits: string
|
commits: string
|
||||||
content: string
|
content: string
|
||||||
|
create: string
|
||||||
|
createRepo: string
|
||||||
|
description: string
|
||||||
email: string
|
email: string
|
||||||
|
enterDescription: string
|
||||||
|
enterRepoName: string
|
||||||
existingAccount: string
|
existingAccount: string
|
||||||
|
failedToCreateRepo: string
|
||||||
files: string
|
files: string
|
||||||
history: string
|
history: string
|
||||||
name: string
|
name: string
|
||||||
newFile: string
|
newFile: string
|
||||||
newFolder: string
|
newFolder: string
|
||||||
|
newRepo: string
|
||||||
noAccount: string
|
noAccount: string
|
||||||
|
none: string
|
||||||
|
ok: string
|
||||||
pageNotFound: string
|
pageNotFound: string
|
||||||
password: string
|
password: string
|
||||||
private: string
|
private: string
|
||||||
@ -21,11 +33,17 @@ export interface StringsMap {
|
|||||||
pullRequests: string
|
pullRequests: string
|
||||||
'repos.activities': string
|
'repos.activities': string
|
||||||
'repos.data': string
|
'repos.data': string
|
||||||
|
'repos.enterBranchName': string
|
||||||
'repos.lastChange': string
|
'repos.lastChange': string
|
||||||
'repos.name': string
|
'repos.name': string
|
||||||
|
'repos.noDataMessage': string
|
||||||
|
'repos.noDataTitle': string
|
||||||
'repos.updated': string
|
'repos.updated': string
|
||||||
repositories: string
|
repositories: string
|
||||||
|
search: string
|
||||||
settings: string
|
settings: string
|
||||||
signIn: string
|
signIn: string
|
||||||
signUp: string
|
signUp: string
|
||||||
|
'validation.gitBranchNameInvalid': string
|
||||||
|
'validation.namePatternIsNotValid': string
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { useLocalStorage } from 'hooks/useLocalStorage'
|
import { useLocalStorage } from 'hooks/useLocalStorage'
|
||||||
|
|
||||||
const API_TOKEN_KEY = 'HARNESS_STANDALONE_APP_API_TOKEN'
|
const API_TOKEN_KEY = 'HARNESS_SCM_STANDALONE_APP_API_TOKEN'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get and Set API token to use in Restful React calls.
|
* Get and Set API token to use in Restful React calls.
|
||||||
|
@ -9,6 +9,8 @@ public: Public
|
|||||||
private: Private
|
private: Private
|
||||||
cancel: Cancel
|
cancel: Cancel
|
||||||
name: Name
|
name: Name
|
||||||
|
search: Search
|
||||||
|
description: Description
|
||||||
repositories: Repositories
|
repositories: Repositories
|
||||||
files: Files
|
files: Files
|
||||||
commits: Commits
|
commits: Commits
|
||||||
@ -18,9 +20,26 @@ newFile: New File
|
|||||||
newFolder: New Folder
|
newFolder: New Folder
|
||||||
content: Content
|
content: Content
|
||||||
history: History
|
history: History
|
||||||
|
newRepo: New Repository
|
||||||
|
createRepo: Create Repository
|
||||||
|
enterRepoName: Enter Repository Name
|
||||||
|
enterDescription: Enter a description (optional)
|
||||||
|
addLicense: Add License
|
||||||
|
none: None
|
||||||
|
create: Create
|
||||||
|
ok: OK
|
||||||
|
addGitIgnore: Add a .gitignore
|
||||||
|
addReadMe: Add a README file
|
||||||
|
failedToCreateRepo: Failed to create Repository. Please try again.
|
||||||
repos:
|
repos:
|
||||||
name: Repo Name
|
name: Repo Name
|
||||||
data: Repo Data
|
data: Repo Data
|
||||||
activities: Monthly Activities
|
activities: Monthly Activities
|
||||||
updated: Updated Date
|
updated: Updated Date
|
||||||
lastChange: Last Change
|
lastChange: Last Change
|
||||||
|
noDataTitle: No Repository Found
|
||||||
|
noDataMessage: Create a new repository by clicking the button below.
|
||||||
|
enterBranchName: Enter a branch name
|
||||||
|
validation:
|
||||||
|
namePatternIsNotValid: 'Name can only contain alphanumerics, . _ and -'
|
||||||
|
gitBranchNameInvalid: Branch name is invalid.
|
||||||
|
17
web/src/types/Repository.ts
Normal file
17
web/src/types/Repository.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
export interface Repository {
|
||||||
|
id: number
|
||||||
|
spaceId: number
|
||||||
|
pathName: string
|
||||||
|
path: string
|
||||||
|
name: string
|
||||||
|
description?: string
|
||||||
|
isPublic: boolean
|
||||||
|
createdBy: number
|
||||||
|
created: number
|
||||||
|
updated: number
|
||||||
|
forkId: number
|
||||||
|
numForks: number
|
||||||
|
numPulls: number
|
||||||
|
numClosedPulls: number
|
||||||
|
numOpenPulls: number
|
||||||
|
}
|
25
web/src/utils/GitUtils.ts
Normal file
25
web/src/utils/GitUtils.ts
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
// Code copied from https://github.com/vweevers/is-git-ref-name-valid and
|
||||||
|
// https://github.com/vweevers/is-git-branch-name-valid (MIT, © Vincent Weevers)
|
||||||
|
// Last updated for git 2.29.0.
|
||||||
|
|
||||||
|
import type { IconName } from '@harness/icons'
|
||||||
|
|
||||||
|
// eslint-disable-next-line no-control-regex
|
||||||
|
const badGitRefRegrex = /(^|[/.])([/.]|$)|^@$|@{|[\x00-\x20\x7f~^:?*[\\]|\.lock(\/|$)/
|
||||||
|
const badGitBranchRegrex = /^(-|HEAD$)/
|
||||||
|
|
||||||
|
function isGitRefValid(name: string, onelevel: boolean): boolean {
|
||||||
|
return !badGitRefRegrex.test(name) && (!!onelevel || name.includes('/'))
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isGitBranchNameValid(name: string): boolean {
|
||||||
|
return isGitRefValid(name, true) && !badGitBranchRegrex.test(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const GitIcon: Readonly<Record<string, IconName>> = {
|
||||||
|
FILE: 'file',
|
||||||
|
REPOSITORY: 'git-repo',
|
||||||
|
COMMIT: 'git-branch-existing',
|
||||||
|
PULL_REQUEST: 'git-pull',
|
||||||
|
SETTINGS: 'cog'
|
||||||
|
}
|
@ -5,7 +5,15 @@ import moment from 'moment'
|
|||||||
import { useEffect } from 'react'
|
import { useEffect } from 'react'
|
||||||
import { useAppContext } from 'AppContext'
|
import { useAppContext } from 'AppContext'
|
||||||
|
|
||||||
|
export const LIST_FETCHING_PAGE_SIZE = 20
|
||||||
|
export const DEFAULT_DATE_FORMAT = 'MM/DD/YYYY hh:mm a'
|
||||||
|
export const X_TOTAL = 'x-total'
|
||||||
|
export const X_TOTAL_PAGES = 'x-total-pages'
|
||||||
|
export const X_PER_PAGE = 'x-per-page'
|
||||||
export type Unknown = any // eslint-disable-line @typescript-eslint/no-explicit-any
|
export type Unknown = any // eslint-disable-line @typescript-eslint/no-explicit-any
|
||||||
|
export const DEFAULT_BRANCH_NAME = 'main'
|
||||||
|
export const REGEX_VALID_REPO_NAME = /^[A-Za-z0-9_.-][A-Za-z0-9 _.-]*$/
|
||||||
|
export const SUGGESTED_BRANCH_NAMES = ['main', 'master']
|
||||||
|
|
||||||
/** This utility shows a toaster without being bound to any component.
|
/** This utility shows a toaster without being bound to any component.
|
||||||
* It's useful to show cross-page/component messages */
|
* It's useful to show cross-page/component messages */
|
||||||
@ -25,7 +33,7 @@ export const MonacoEditorOptions = {
|
|||||||
codeLens: false,
|
codeLens: false,
|
||||||
scrollBeyondLastLine: false,
|
scrollBeyondLastLine: false,
|
||||||
smartSelect: false,
|
smartSelect: false,
|
||||||
tabSize: 4,
|
tabSize: 2,
|
||||||
insertSpaces: true,
|
insertSpaces: true,
|
||||||
overviewRulerBorder: false
|
overviewRulerBorder: false
|
||||||
}
|
}
|
||||||
@ -45,13 +53,38 @@ export const deselectAllMonacoEditor = (editor?: EDITOR.IStandaloneCodeEditor):
|
|||||||
}, 0)
|
}, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const LIST_FETCHING_PAGE_SIZE = 20
|
|
||||||
export const DEFAULT_DATE_FORMAT = 'MM/DD/YYYY hh:mm a'
|
|
||||||
|
|
||||||
export const displayDateTime = (value: number): string | null => {
|
export const displayDateTime = (value: number): string | null => {
|
||||||
return value ? moment.unix(value / 1000).format(DEFAULT_DATE_FORMAT) : null
|
return value ? moment.unix(value / 1000).format(DEFAULT_DATE_FORMAT) : null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const LOCALE = Intl.NumberFormat().resolvedOptions?.().locale || 'en-US'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format a timestamp to short format time (i.e: 7:41 AM)
|
||||||
|
* @param timestamp Timestamp
|
||||||
|
* @param timeStyle Optional DateTimeFormat's `timeStyle` option.
|
||||||
|
*/
|
||||||
|
export function formatTime(timestamp: number, timeStyle = 'short'): string {
|
||||||
|
return new Intl.DateTimeFormat(LOCALE, {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
|
// @ts-ignore: TS built-in type for DateTimeFormat is not correct
|
||||||
|
timeStyle
|
||||||
|
}).format(new Date(timestamp))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format a timestamp to medium format date (i.e: Jan 1, 2021)
|
||||||
|
* @param timestamp Timestamp
|
||||||
|
* @param dateStyle Optional DateTimeFormat's `dateStyle` option.
|
||||||
|
*/
|
||||||
|
export function formatDate(timestamp: number, dateStyle = 'medium'): string {
|
||||||
|
return new Intl.DateTimeFormat(LOCALE, {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
|
// @ts-ignore: TS built-in type for DateTimeFormat is not correct
|
||||||
|
dateStyle
|
||||||
|
}).format(new Date(timestamp))
|
||||||
|
}
|
||||||
|
|
||||||
export enum Editions {
|
export enum Editions {
|
||||||
ENTERPRISE = 'ENTERPRISE',
|
ENTERPRISE = 'ENTERPRISE',
|
||||||
TEAM = 'TEAM',
|
TEAM = 'TEAM',
|
||||||
|
@ -1,3 +0,0 @@
|
|||||||
.main {
|
|
||||||
padding: var(--spacing-xlarge);
|
|
||||||
}
|
|
@ -1,7 +0,0 @@
|
|||||||
import React from 'react'
|
|
||||||
import { Container } from '@harness/uicore'
|
|
||||||
import css from './NewRepo.module.scss'
|
|
||||||
|
|
||||||
export default function NewRepo(): JSX.Element {
|
|
||||||
return <Container className={css.main}>NewRepo!</Container>
|
|
||||||
}
|
|
@ -5,6 +5,7 @@ import SplitPane from 'react-split-pane'
|
|||||||
import { PopoverInteractionKind } from '@blueprintjs/core'
|
import { PopoverInteractionKind } from '@blueprintjs/core'
|
||||||
import { useStrings } from 'framework/strings'
|
import { useStrings } from 'framework/strings'
|
||||||
import { ButtonRoleProps } from 'utils/Utils'
|
import { ButtonRoleProps } from 'utils/Utils'
|
||||||
|
import { GitIcon } from 'utils/GitUtils'
|
||||||
import { ResourceTree } from './ResourceTree/ResourceTree'
|
import { ResourceTree } from './ResourceTree/ResourceTree'
|
||||||
import { ResourceContent } from './ResourceContent/ResourceContent'
|
import { ResourceContent } from './ResourceContent/ResourceContent'
|
||||||
import css from './RepoFiles.module.scss'
|
import css from './RepoFiles.module.scss'
|
||||||
@ -27,7 +28,7 @@ export default function RepoFiles(): JSX.Element {
|
|||||||
<Text
|
<Text
|
||||||
inline
|
inline
|
||||||
className={css.repoDropdown}
|
className={css.repoDropdown}
|
||||||
icon="git-repo"
|
icon={GitIcon.REPOSITORY}
|
||||||
iconProps={{
|
iconProps={{
|
||||||
size: 14,
|
size: 14,
|
||||||
color: Color.GREY_500,
|
color: Color.GREY_500,
|
||||||
@ -60,28 +61,28 @@ export default function RepoFiles(): JSX.Element {
|
|||||||
id: 'files',
|
id: 'files',
|
||||||
title: getString('files'),
|
title: getString('files'),
|
||||||
panel: <Files />,
|
panel: <Files />,
|
||||||
iconProps: { name: 'file' },
|
iconProps: { name: GitIcon.FILE },
|
||||||
disabled: false
|
disabled: false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'commits',
|
id: 'commits',
|
||||||
title: getString('commits'),
|
title: getString('commits'),
|
||||||
panel: <Commits />,
|
panel: <Commits />,
|
||||||
iconProps: { name: 'git-branch-existing' },
|
iconProps: { name: GitIcon.COMMIT },
|
||||||
disabled: true
|
disabled: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'pull-requests',
|
id: 'pull-requests',
|
||||||
title: getString('pullRequests'),
|
title: getString('pullRequests'),
|
||||||
panel: <PullRequests />,
|
panel: <PullRequests />,
|
||||||
iconProps: { name: 'git-pull' },
|
iconProps: { name: GitIcon.PULL_REQUEST },
|
||||||
disabled: true
|
disabled: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'settings',
|
id: 'settings',
|
||||||
title: getString('settings'),
|
title: getString('settings'),
|
||||||
panel: <Settings />,
|
panel: <Settings />,
|
||||||
iconProps: { name: 'cog' },
|
iconProps: { name: GitIcon.SETTINGS },
|
||||||
disabled: true
|
disabled: true
|
||||||
}
|
}
|
||||||
]}></Tabs>
|
]}></Tabs>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import React, { useMemo, useRef, useState } from 'react'
|
import React, { useMemo, useRef, useState } from 'react'
|
||||||
import cx from 'classnames'
|
import cx from 'classnames'
|
||||||
// import root from 'react-shadow'
|
// import root from 'react-shadow'
|
||||||
// import MonacoEditor from 'react-monaco-editor'
|
import MonacoEditor from 'react-monaco-editor'
|
||||||
import type { editor as EDITOR } from 'monaco-editor/esm/vs/editor/editor.api'
|
import type { editor as EDITOR } from 'monaco-editor/esm/vs/editor/editor.api'
|
||||||
import {
|
import {
|
||||||
Container,
|
Container,
|
||||||
@ -22,8 +22,9 @@ import { useStrings } from 'framework/strings'
|
|||||||
// import markdownCSS from '!raw-loader!@uiw/react-markdown-preview/dist/markdown.css'
|
// import markdownCSS from '!raw-loader!@uiw/react-markdown-preview/dist/markdown.css'
|
||||||
import markdown from './sampleREADME.md'
|
import markdown from './sampleREADME.md'
|
||||||
import css from './ResourceContent.module.scss'
|
import css from './ResourceContent.module.scss'
|
||||||
|
import sampleCSs from '!raw-loader!./ResourceContent.module.scss'
|
||||||
|
|
||||||
import('!raw-loader!./sampleREADME.md').then(foo => console.log('dynamic data:', foo.default))
|
// import('!raw-loader!./sampleREADME.md').then(foo => console.log('dynamic data:', foo.default))
|
||||||
|
|
||||||
// TODO: USE FROM SERVICE (DOES NOT EXIST YET)
|
// TODO: USE FROM SERVICE (DOES NOT EXIST YET)
|
||||||
interface Folder {
|
interface Folder {
|
||||||
@ -169,9 +170,11 @@ export function FolderListing(): JSX.Element {
|
|||||||
const inputContainerRef = useRef<HTMLDivElement>(null)
|
const inputContainerRef = useRef<HTMLDivElement>(null)
|
||||||
const [inputEditor, setInputEditor] = useState<EDITOR.IStandaloneCodeEditor>()
|
const [inputEditor, setInputEditor] = useState<EDITOR.IStandaloneCodeEditor>()
|
||||||
|
|
||||||
|
console.log({ sampleCSs })
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container>
|
<Container>
|
||||||
<Table<Folder>
|
{/* <Table<Folder>
|
||||||
className={css.table}
|
className={css.table}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
data={repodata || []}
|
data={repodata || []}
|
||||||
@ -179,7 +182,7 @@ export function FolderListing(): JSX.Element {
|
|||||||
// onPolicyClicked(data)
|
// onPolicyClicked(data)
|
||||||
}}
|
}}
|
||||||
getRowClassName={() => css.row}
|
getRowClassName={() => css.row}
|
||||||
/>
|
/> */}
|
||||||
|
|
||||||
<Container className={css.fileContentContainer} background={Color.WHITE}>
|
<Container className={css.fileContentContainer} background={Color.WHITE}>
|
||||||
<Layout.Horizontal padding="small" className={css.fileContentHeading}>
|
<Layout.Horizontal padding="small" className={css.fileContentHeading}>
|
||||||
@ -187,27 +190,20 @@ export function FolderListing(): JSX.Element {
|
|||||||
<FlexExpander />
|
<FlexExpander />
|
||||||
<Button variation={ButtonVariation.ICON} icon="edit" />
|
<Button variation={ButtonVariation.ICON} icon="edit" />
|
||||||
</Layout.Horizontal>
|
</Layout.Horizontal>
|
||||||
<Container className={css.readmeContainer}>
|
<Container className={css.readmeContainer} style={{ display: 'none' }}>
|
||||||
{/* <root.div style={{ all: 'initial' }}> */}
|
|
||||||
<MarkdownPreview
|
<MarkdownPreview
|
||||||
source={markdown}
|
source={markdown}
|
||||||
rehypeRewrite={(node, _index, parent) => {
|
// rehypeRewrite={(node, _index, parent) => {
|
||||||
if (
|
// if (
|
||||||
(node as unknown as Element).tagName === 'a' &&
|
// (node as unknown as Element).tagName === 'a' &&
|
||||||
parent &&
|
// parent &&
|
||||||
/^h(1|2|3|4|5|6)/.test((parent as unknown as Element).tagName)
|
// /^h(1|2|3|4|5|6)/.test((parent as unknown as Element).tagName)
|
||||||
) {
|
// ) {
|
||||||
parent.children = parent.children.slice(1)
|
// parent.children = parent.children.slice(1)
|
||||||
}
|
// }
|
||||||
}}
|
// }}
|
||||||
/>
|
/>
|
||||||
{/* <style type="text/css">{markdownCSS}</style>
|
<Container flex ref={inputContainerRef} style={{ maxHeight: '95%', height: '900px' }}>
|
||||||
</root.div> */}
|
|
||||||
{/* <Container
|
|
||||||
flex
|
|
||||||
// className={css.ioEditor}
|
|
||||||
ref={inputContainerRef}
|
|
||||||
style={{ maxHeight: '95%', height: '900px' }}>
|
|
||||||
<MonacoEditor
|
<MonacoEditor
|
||||||
language="json"
|
language="json"
|
||||||
theme="vs-light"
|
theme="vs-light"
|
||||||
@ -216,7 +212,7 @@ export function FolderListing(): JSX.Element {
|
|||||||
onChange={updateInput}
|
onChange={updateInput}
|
||||||
editorDidMount={setInputEditor}
|
editorDidMount={setInputEditor}
|
||||||
/>
|
/>
|
||||||
</Container> */}
|
</Container>
|
||||||
</Container>
|
</Container>
|
||||||
</Container>
|
</Container>
|
||||||
</Container>
|
</Container>
|
||||||
|
@ -0,0 +1,3 @@
|
|||||||
|
.divider {
|
||||||
|
margin: var(--spacing-medium) 0 var(--spacing-large) 0 !important;
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
// this is an auto-generated file
|
// this is an auto-generated file
|
||||||
declare const styles: {
|
declare const styles: {
|
||||||
readonly main: string
|
readonly divider: string
|
||||||
}
|
}
|
||||||
export default styles
|
export default styles
|
@ -5,8 +5,8 @@
|
|||||||
* https://polyformproject.org/wp-content/uploads/2020/06/PolyForm-Shield-1.0.0.txt.
|
* https://polyformproject.org/wp-content/uploads/2020/06/PolyForm-Shield-1.0.0.txt.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, { useState } from 'react'
|
import React, { useMemo, useState } from 'react'
|
||||||
import { Dialog, Intent } from '@blueprintjs/core'
|
import { Icon as BPIcon, Classes, Dialog, Intent, Menu, MenuDivider, MenuItem } from '@blueprintjs/core'
|
||||||
import * as yup from 'yup'
|
import * as yup from 'yup'
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
@ -20,29 +20,39 @@ import {
|
|||||||
Heading,
|
Heading,
|
||||||
useToaster,
|
useToaster,
|
||||||
FormInput,
|
FormInput,
|
||||||
Text
|
Text,
|
||||||
|
ButtonVariation,
|
||||||
|
ButtonSize,
|
||||||
|
TextInput
|
||||||
} from '@harness/uicore'
|
} from '@harness/uicore'
|
||||||
import { FontVariation } from '@harness/design-system'
|
import { FontVariation } from '@harness/design-system'
|
||||||
|
import { useMutate } from 'restful-react'
|
||||||
|
import { get } from 'lodash-es'
|
||||||
import { useModalHook } from '@harness/use-modal'
|
import { useModalHook } from '@harness/use-modal'
|
||||||
import { useStrings } from 'framework/strings'
|
import { useStrings } from 'framework/strings'
|
||||||
import { getErrorMessage, Unknown } from 'utils/Utils'
|
import {
|
||||||
|
DEFAULT_BRANCH_NAME,
|
||||||
|
getErrorMessage,
|
||||||
|
REGEX_VALID_REPO_NAME,
|
||||||
|
SUGGESTED_BRANCH_NAMES,
|
||||||
|
Unknown
|
||||||
|
} from 'utils/Utils'
|
||||||
|
import type { Repository } from 'types/Repository'
|
||||||
|
import { isGitBranchNameValid } from 'utils/GitUtils'
|
||||||
|
import licences from './licences'
|
||||||
|
import gitignores from './gitignores'
|
||||||
|
import css from './NewRepoModalButton.module.scss'
|
||||||
|
|
||||||
export interface NewRepoModalButtonProps extends Omit<ButtonProps, 'onClick' | 'onSubmit'> {
|
export interface NewRepoModalButtonProps extends Omit<ButtonProps, 'onClick' | 'onSubmit'> {
|
||||||
accountIdentifier: string
|
space: string
|
||||||
orgIdentifier: string
|
|
||||||
projectIdentifier: string
|
|
||||||
|
|
||||||
modalTitle: string
|
modalTitle: string
|
||||||
submitButtonTitle?: string
|
submitButtonTitle?: string
|
||||||
cancelButtonTitle?: string
|
cancelButtonTitle?: string
|
||||||
|
onSubmit: (data: Repository) => void
|
||||||
onSubmit: (data: Unknown) => void
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const NewRepoModalButton: React.FC<NewRepoModalButtonProps> = ({
|
export const NewRepoModalButton: React.FC<NewRepoModalButtonProps> = ({
|
||||||
accountIdentifier,
|
space,
|
||||||
orgIdentifier,
|
|
||||||
projectIdentifier,
|
|
||||||
modalTitle,
|
modalTitle,
|
||||||
submitButtonTitle,
|
submitButtonTitle,
|
||||||
cancelButtonTitle,
|
cancelButtonTitle,
|
||||||
@ -51,23 +61,47 @@ export const NewRepoModalButton: React.FC<NewRepoModalButtonProps> = ({
|
|||||||
}) => {
|
}) => {
|
||||||
const ModalComponent: React.FC = () => {
|
const ModalComponent: React.FC = () => {
|
||||||
const { getString } = useStrings()
|
const { getString } = useStrings()
|
||||||
|
const [branchName, setBranchName] = useState(DEFAULT_BRANCH_NAME)
|
||||||
const { showError } = useToaster()
|
const { showError } = useToaster()
|
||||||
|
const { mutate: createRepo, loading } = useMutate({
|
||||||
|
verb: 'POST',
|
||||||
|
path: '/api/v1/repos'
|
||||||
|
})
|
||||||
|
|
||||||
const [submitLoading, setSubmitLoading] = useState(false)
|
const handleSubmit = (formData?: Unknown): void => {
|
||||||
const handleSubmit = (_data?: Unknown): void => {
|
|
||||||
setSubmitLoading(true)
|
|
||||||
try {
|
try {
|
||||||
onSubmit({})
|
// TODO: Backend is lacking support for these fields except
|
||||||
|
// name and description (Oct 5)
|
||||||
|
createRepo({
|
||||||
|
pathName: get(formData, 'name', '').trim(),
|
||||||
|
name: get(formData, 'name', '').trim(),
|
||||||
|
spaceId: space,
|
||||||
|
description: get(formData, 'description', '').trim(),
|
||||||
|
isPublic: false,
|
||||||
|
license: get(formData, 'license', 'none'),
|
||||||
|
gitIgnore: get(formData, 'gitignore', 'none'),
|
||||||
|
defaultBranch: get(formData, 'defaultBranch', 'main'),
|
||||||
|
addReadme: get(formData, 'addReadme', false)
|
||||||
|
})
|
||||||
|
.then(response => {
|
||||||
|
hideModal()
|
||||||
|
onSubmit(response)
|
||||||
|
})
|
||||||
|
.catch(_error => {
|
||||||
|
showError(getErrorMessage(_error), 0, 'failedToCreateRepo')
|
||||||
|
})
|
||||||
} catch (exception) {
|
} catch (exception) {
|
||||||
setSubmitLoading(false)
|
showError(getErrorMessage(exception), 0, 'failedToCreateRepo')
|
||||||
showError(getErrorMessage(exception), 0, 'cf.save.ff.error')
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const loading = submitLoading
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog isOpen enforceFocus={false} onClose={hideModal} title={''} style={{ width: 700, maxHeight: '95vh' }}>
|
<Dialog
|
||||||
|
isOpen
|
||||||
|
enforceFocus={false}
|
||||||
|
onClose={hideModal}
|
||||||
|
title={''}
|
||||||
|
style={{ width: 700, maxHeight: '95vh', overflow: 'auto' }}>
|
||||||
<Layout.Vertical
|
<Layout.Vertical
|
||||||
padding={{ left: 'xxlarge' }}
|
padding={{ left: 'xxlarge' }}
|
||||||
style={{ height: '100%' }}
|
style={{ height: '100%' }}
|
||||||
@ -79,13 +113,21 @@ export const NewRepoModalButton: React.FC<NewRepoModalButtonProps> = ({
|
|||||||
<Container margin={{ right: 'xxlarge' }}>
|
<Container margin={{ right: 'xxlarge' }}>
|
||||||
<Formik
|
<Formik
|
||||||
initialValues={{
|
initialValues={{
|
||||||
gitDetails: '',
|
name: '',
|
||||||
autoCommit: ''
|
description: '',
|
||||||
|
license: '',
|
||||||
|
defaultBranch: 'main',
|
||||||
|
gitignore: '',
|
||||||
|
addReadme: false
|
||||||
}}
|
}}
|
||||||
formName="editVariations"
|
formName="editVariations"
|
||||||
enableReinitialize={true}
|
enableReinitialize={true}
|
||||||
validationSchema={yup.object().shape({
|
validationSchema={yup.object().shape({
|
||||||
// gitDetails: 'gitSyncFormMeta?.gitSyncValidationSchema'
|
name: yup
|
||||||
|
.string()
|
||||||
|
.trim()
|
||||||
|
.required()
|
||||||
|
.matches(REGEX_VALID_REPO_NAME, getString('validation.namePatternIsNotValid'))
|
||||||
})}
|
})}
|
||||||
validateOnChange
|
validateOnChange
|
||||||
validateOnBlur
|
validateOnBlur
|
||||||
@ -93,8 +135,8 @@ export const NewRepoModalButton: React.FC<NewRepoModalButtonProps> = ({
|
|||||||
<FormikForm>
|
<FormikForm>
|
||||||
<FormInput.Text
|
<FormInput.Text
|
||||||
name="name"
|
name="name"
|
||||||
label="Name"
|
label={getString('name')}
|
||||||
placeholder="Enter Repository Name"
|
placeholder={getString('enterRepoName')}
|
||||||
tooltipProps={{
|
tooltipProps={{
|
||||||
dataTooltipId: 'repositoryNameTextField'
|
dataTooltipId: 'repositoryNameTextField'
|
||||||
}}
|
}}
|
||||||
@ -102,51 +144,50 @@ export const NewRepoModalButton: React.FC<NewRepoModalButtonProps> = ({
|
|||||||
/>
|
/>
|
||||||
<FormInput.Text
|
<FormInput.Text
|
||||||
name="description"
|
name="description"
|
||||||
label="Description"
|
label={getString('description')}
|
||||||
placeholder="Enter a description (optional)"
|
placeholder={getString('enterDescription')}
|
||||||
tooltipProps={{
|
tooltipProps={{
|
||||||
dataTooltipId: 'repositoryDescriptionTextField'
|
dataTooltipId: 'repositoryDescriptionTextField'
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Container margin={{ top: 'medium', bottom: 'xlarge' }}>
|
<Container margin={{ top: 'medium', bottom: 'xlarge' }}>
|
||||||
<Text>
|
<Text>
|
||||||
Your repository will be initialized with a <strong>main</strong> branch.
|
Your repository will be initialized with a{' '}
|
||||||
|
<strong>
|
||||||
|
<Button
|
||||||
|
text={branchName}
|
||||||
|
icon="git-new-branch"
|
||||||
|
rightIcon="chevron-down"
|
||||||
|
variation={ButtonVariation.TERTIARY}
|
||||||
|
size={ButtonSize.SMALL}
|
||||||
|
iconProps={{ size: 14 }}
|
||||||
|
tooltip={<BranchName currentBranchName={branchName} onSelect={name => setBranchName(name)} />}
|
||||||
|
tooltipProps={{ interactionKind: 'click' }}
|
||||||
|
/>
|
||||||
|
</strong>{' '}
|
||||||
|
branch.
|
||||||
</Text>
|
</Text>
|
||||||
</Container>
|
</Container>
|
||||||
|
|
||||||
<FormInput.Select
|
<FormInput.Select
|
||||||
name="license"
|
name="license"
|
||||||
label="Add License"
|
label={getString('addLicense')}
|
||||||
placeholder="None"
|
placeholder={getString('none')}
|
||||||
items={[
|
items={licences}
|
||||||
{ label: 'Red', value: 'red' },
|
usePortal
|
||||||
{ label: 'Blue', value: 'blue' },
|
|
||||||
{
|
|
||||||
label: 'TryingTryingTryingTryingTryingTryingTryingTryingTryingTryingTryingTryingTrying',
|
|
||||||
value: 'xyz'
|
|
||||||
},
|
|
||||||
{ label: 'Trying a long phrase with spaces to try out different combinations', value: 'abcd' }
|
|
||||||
]}
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<FormInput.Select
|
<FormInput.Select
|
||||||
name="gitignore"
|
name="gitignore"
|
||||||
label="Add a .gitignore"
|
label={getString('addGitIgnore')}
|
||||||
placeholder="None"
|
placeholder={getString('none')}
|
||||||
items={[
|
items={gitignores}
|
||||||
{ label: 'Red', value: 'red' },
|
usePortal
|
||||||
{ label: 'Blue', value: 'blue' },
|
|
||||||
{
|
|
||||||
label: 'TryingTryingTryingTryingTryingTryingTryingTryingTryingTryingTryingTryingTrying',
|
|
||||||
value: 'xyz'
|
|
||||||
},
|
|
||||||
{ label: 'Trying a long phrase with spaces to try out different combinations', value: 'abcd' }
|
|
||||||
]}
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<FormInput.CheckBox
|
<FormInput.CheckBox
|
||||||
name="addReadme"
|
name="addReadme"
|
||||||
label="Add a README file"
|
label={getString('addReadMe')}
|
||||||
tooltipProps={{
|
tooltipProps={{
|
||||||
dataTooltipId: 'addReadMe'
|
dataTooltipId: 'addReadMe'
|
||||||
}}
|
}}
|
||||||
@ -155,7 +196,7 @@ export const NewRepoModalButton: React.FC<NewRepoModalButtonProps> = ({
|
|||||||
spacing="small"
|
spacing="small"
|
||||||
padding={{ right: 'xxlarge', top: 'xxxlarge', bottom: 'large' }}
|
padding={{ right: 'xxlarge', top: 'xxxlarge', bottom: 'large' }}
|
||||||
style={{ alignItems: 'center' }}>
|
style={{ alignItems: 'center' }}>
|
||||||
<Button type="submit" text={'Create Repository'} intent={Intent.PRIMARY} disabled={loading} />
|
<Button type="submit" text={getString('createRepo')} intent={Intent.PRIMARY} disabled={loading} />
|
||||||
<Button text={cancelButtonTitle || getString('cancel')} minimal onClick={hideModal} />
|
<Button text={cancelButtonTitle || getString('cancel')} minimal onClick={hideModal} />
|
||||||
<FlexExpander />
|
<FlexExpander />
|
||||||
|
|
||||||
@ -173,3 +214,55 @@ export const NewRepoModalButton: React.FC<NewRepoModalButtonProps> = ({
|
|||||||
|
|
||||||
return <Button onClick={openModal} {...props} />
|
return <Button onClick={openModal} {...props} />
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface BranchNameProps {
|
||||||
|
currentBranchName: string
|
||||||
|
onSelect: (branchName: string) => void
|
||||||
|
}
|
||||||
|
const BranchName: React.FC<BranchNameProps> = ({ currentBranchName, onSelect }) => {
|
||||||
|
const { getString } = useStrings()
|
||||||
|
const [customName, setCustomName] = useState(
|
||||||
|
SUGGESTED_BRANCH_NAMES.includes(currentBranchName) ? '' : currentBranchName
|
||||||
|
)
|
||||||
|
const isCustomNameValid = useMemo(() => !customName || isGitBranchNameValid(customName), [customName])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Container padding="medium" width={250}>
|
||||||
|
<Layout.Vertical spacing="small">
|
||||||
|
<Menu>
|
||||||
|
{SUGGESTED_BRANCH_NAMES.map(name => (
|
||||||
|
<MenuItem
|
||||||
|
key={name}
|
||||||
|
text={name}
|
||||||
|
labelElement={name === currentBranchName ? <BPIcon icon="small-tick" /> : undefined}
|
||||||
|
disabled={name === currentBranchName}
|
||||||
|
onClick={() => onSelect(name)}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
<MenuDivider className={css.divider} />
|
||||||
|
<TextInput
|
||||||
|
defaultValue={customName}
|
||||||
|
autoFocus
|
||||||
|
placeholder={getString('repos.enterBranchName')}
|
||||||
|
onInput={e => setCustomName((e.currentTarget.value || '').trim())}
|
||||||
|
errorText={isCustomNameValid ? undefined : getString('validation.gitBranchNameInvalid')}
|
||||||
|
intent={!customName ? undefined : isCustomNameValid ? 'success' : 'danger'}
|
||||||
|
/>
|
||||||
|
</Menu>
|
||||||
|
<Container>
|
||||||
|
<Layout.Horizontal>
|
||||||
|
<Button
|
||||||
|
type="submit"
|
||||||
|
text={getString('ok')}
|
||||||
|
intent={Intent.PRIMARY}
|
||||||
|
className={Classes.POPOVER_DISMISS}
|
||||||
|
disabled={!customName || !isCustomNameValid}
|
||||||
|
onClick={() => onSelect(customName)}
|
||||||
|
/>
|
||||||
|
<Button text={getString('cancel')} minimal className={Classes.POPOVER_DISMISS} />
|
||||||
|
</Layout.Horizontal>
|
||||||
|
</Container>
|
||||||
|
</Layout.Vertical>
|
||||||
|
</Container>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
@ -3,6 +3,10 @@
|
|||||||
background-color: var(--primary-bg) !important;
|
background-color: var(--primary-bg) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.withError {
|
||||||
|
display: grid;
|
||||||
|
}
|
||||||
|
|
||||||
.table {
|
.table {
|
||||||
[class*='TableV2--header'] [class*='variation-table-headers'] {
|
[class*='TableV2--header'] [class*='variation-table-headers'] {
|
||||||
text-transform: none;
|
text-transform: none;
|
||||||
|
1
web/src/views/Repos/Repos.module.scss.d.ts
vendored
1
web/src/views/Repos/Repos.module.scss.d.ts
vendored
@ -2,6 +2,7 @@
|
|||||||
// this is an auto-generated file
|
// this is an auto-generated file
|
||||||
declare const styles: {
|
declare const styles: {
|
||||||
readonly main: string
|
readonly main: string
|
||||||
|
readonly withError: string
|
||||||
readonly table: string
|
readonly table: string
|
||||||
readonly row: string
|
readonly row: string
|
||||||
readonly nameContainer: string
|
readonly nameContainer: string
|
||||||
|
@ -13,52 +13,17 @@ import {
|
|||||||
Pagination
|
Pagination
|
||||||
} from '@harness/uicore'
|
} from '@harness/uicore'
|
||||||
import type { CellProps, Column } from 'react-table'
|
import type { CellProps, Column } from 'react-table'
|
||||||
|
import cx from 'classnames'
|
||||||
|
import { useGet } from 'restful-react'
|
||||||
import { useStrings } from 'framework/strings'
|
import { useStrings } from 'framework/strings'
|
||||||
|
import { formatDate, getErrorMessage, X_PER_PAGE, X_TOTAL, X_TOTAL_PAGES } from 'utils/Utils'
|
||||||
|
import type { Repository } from 'types/Repository'
|
||||||
import { NewRepoModalButton } from './NewRepoModalButton'
|
import { NewRepoModalButton } from './NewRepoModalButton'
|
||||||
import { PinnedRibbon } from './PinnedRibbon'
|
import { PinnedRibbon } from './PinnedRibbon'
|
||||||
import chartImg from './chart.svg'
|
import chartImg from './chart.svg'
|
||||||
|
import emptyStateImage from './empty_state.png'
|
||||||
import css from './Repos.module.scss'
|
import css from './Repos.module.scss'
|
||||||
|
|
||||||
// TODO: USE FROM SERVICE (DOES NOT EXIST YET)
|
|
||||||
interface Repository {
|
|
||||||
accountId: string
|
|
||||||
name: string
|
|
||||||
description?: string
|
|
||||||
pinned?: boolean
|
|
||||||
public?: boolean
|
|
||||||
metadata?: {
|
|
||||||
language: string
|
|
||||||
branch: number
|
|
||||||
commit: number
|
|
||||||
fork: number
|
|
||||||
}
|
|
||||||
activities: string
|
|
||||||
updated: number
|
|
||||||
}
|
|
||||||
|
|
||||||
const repodata: Repository[] = [...Array(50).keys()].map(number => ({
|
|
||||||
accountId: `Harness`,
|
|
||||||
name: `Repo ${number}`,
|
|
||||||
description:
|
|
||||||
number < 4
|
|
||||||
? 'Repo very long name very long name long long long longRepo very long name very long name long long long long'
|
|
||||||
: '',
|
|
||||||
data: `Data ${number}`,
|
|
||||||
pinned: number < 3,
|
|
||||||
public: number < 2,
|
|
||||||
metadata:
|
|
||||||
number % 2
|
|
||||||
? {
|
|
||||||
language: 'Java',
|
|
||||||
branch: 1000,
|
|
||||||
commit: 12323,
|
|
||||||
fork: 2
|
|
||||||
}
|
|
||||||
: undefined,
|
|
||||||
activities: `Activity ${number}`,
|
|
||||||
updated: Date.now() - 40000
|
|
||||||
}))
|
|
||||||
|
|
||||||
function RepoMetadata(): JSX.Element {
|
function RepoMetadata(): JSX.Element {
|
||||||
return (
|
return (
|
||||||
<Container width="70%">
|
<Container width="70%">
|
||||||
@ -79,6 +44,20 @@ export default function Repos(): JSX.Element {
|
|||||||
const { getString } = useStrings()
|
const { getString } = useStrings()
|
||||||
const ref = useRef<HTMLDivElement>(null)
|
const ref = useRef<HTMLDivElement>(null)
|
||||||
const [nameTextWidth, setNameTextWidth] = useState(300)
|
const [nameTextWidth, setNameTextWidth] = useState(300)
|
||||||
|
const space = 3
|
||||||
|
const {
|
||||||
|
data: repositories,
|
||||||
|
error,
|
||||||
|
loading,
|
||||||
|
refetch,
|
||||||
|
response
|
||||||
|
} = useGet<Repository[]>({
|
||||||
|
path: `/api/v1/spaces/${space}/repos`
|
||||||
|
})
|
||||||
|
const itemCount = useMemo(() => parseInt(response?.headers?.get(X_TOTAL) || '0'), [response])
|
||||||
|
const pageCount = useMemo(() => parseInt(response?.headers?.get(X_TOTAL_PAGES) || '0'), [response])
|
||||||
|
const pageSize = useMemo(() => parseInt(response?.headers?.get(X_PER_PAGE) || '0'), [response])
|
||||||
|
|
||||||
const columns: Column<Repository>[] = useMemo(
|
const columns: Column<Repository>[] = useMemo(
|
||||||
() => [
|
() => [
|
||||||
{
|
{
|
||||||
@ -140,7 +119,7 @@ export default function Repos(): JSX.Element {
|
|||||||
Cell: ({ row }: CellProps<Repository>) => {
|
Cell: ({ row }: CellProps<Repository>) => {
|
||||||
return (
|
return (
|
||||||
<Text color={Color.BLACK} lineClamp={1}>
|
<Text color={Color.BLACK} lineClamp={1}>
|
||||||
{'July 13, 2022' || row.original.updated}
|
{formatDate(row.original.updated)}
|
||||||
</Text>
|
</Text>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
@ -155,66 +134,79 @@ export default function Repos(): JSX.Element {
|
|||||||
setNameTextWidth((ref.current.closest('div[role="cell"]') as HTMLDivElement)?.offsetWidth - 100)
|
setNameTextWidth((ref.current.closest('div[role="cell"]') as HTMLDivElement)?.offsetWidth - 100)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const NewRepoButton = (
|
||||||
|
<NewRepoModalButton
|
||||||
|
space={space}
|
||||||
|
modalTitle={getString('newRepo')}
|
||||||
|
text={getString('newRepo')}
|
||||||
|
variation={ButtonVariation.PRIMARY}
|
||||||
|
icon="plus"
|
||||||
|
onSubmit={_data => {
|
||||||
|
// TODO: navigate to Repo instead of refetch
|
||||||
|
refetch()
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
onResize()
|
onResize()
|
||||||
window.addEventListener('resize', onResize)
|
window.addEventListener('resize', onResize)
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
window.removeEventListener('resize', onResize)
|
window.removeEventListener('resize', onResize)
|
||||||
}
|
}
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
const itemCount = repodata.length
|
console.log({ response, repositories, pageCount, pageIndex, pageSize, itemCount })
|
||||||
const pageSize = 25
|
|
||||||
const pageCount = itemCount
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container className={css.main}>
|
<Container className={css.main}>
|
||||||
<PageHeader title={getString('repositories')} />
|
<PageHeader title={getString('repositories')} />
|
||||||
<PageBody>
|
<PageBody
|
||||||
|
loading={loading}
|
||||||
|
className={cx({ [css.withError]: !!error })}
|
||||||
|
error={error ? getErrorMessage(error) : null}
|
||||||
|
retryOnError={() => {
|
||||||
|
refetch()
|
||||||
|
}}
|
||||||
|
noData={{
|
||||||
|
when: () => repositories?.length === 0,
|
||||||
|
image: emptyStateImage,
|
||||||
|
messageTitle: getString('repos.noDataTitle'),
|
||||||
|
message: getString('repos.noDataMessage'),
|
||||||
|
button: NewRepoButton
|
||||||
|
}}>
|
||||||
<Container padding="xlarge">
|
<Container padding="xlarge">
|
||||||
<Layout.Horizontal spacing="large">
|
<Layout.Horizontal spacing="large">
|
||||||
{/* <Button text="New Repository" variation={ButtonVariation.PRIMARY} icon="book" /> */}
|
{NewRepoButton}
|
||||||
<NewRepoModalButton
|
|
||||||
accountIdentifier=""
|
|
||||||
orgIdentifier=""
|
|
||||||
projectIdentifier=""
|
|
||||||
modalTitle="Create Repository"
|
|
||||||
text="New Repository"
|
|
||||||
variation={ButtonVariation.PRIMARY}
|
|
||||||
icon="plus"
|
|
||||||
onSubmit={_data => {
|
|
||||||
console.log(_data)
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<FlexExpander />
|
<FlexExpander />
|
||||||
<TextInput placeholder="Search" leftIcon="search" style={{ width: 350 }} autoFocus />
|
<TextInput placeholder={getString('search')} leftIcon="search" style={{ width: 350 }} autoFocus />
|
||||||
</Layout.Horizontal>
|
</Layout.Horizontal>
|
||||||
<Container margin={{ top: 'medium' }}>
|
<Container margin={{ top: 'medium' }}>
|
||||||
<Table<Repository>
|
<Table<Repository>
|
||||||
rowDataTestID={(_, index: number) => `scm-repo-${index}`}
|
rowDataTestID={(_, index: number) => `scm-repo-${index}`}
|
||||||
className={css.table}
|
className={css.table}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
data={repodata || []}
|
data={repositories || []}
|
||||||
onRowClick={_data => {
|
onRowClick={_data => {
|
||||||
// onPolicyClicked(data)
|
// onPolicyClicked(data)
|
||||||
}}
|
}}
|
||||||
getRowClassName={() => css.row}
|
getRowClassName={() => css.row}
|
||||||
/>
|
/>
|
||||||
</Container>
|
</Container>
|
||||||
<Container margin={{ bottom: 'medium', left: 'xxxlarge', right: 'xxxlarge' }}>
|
{!!repositories?.length && (
|
||||||
<Pagination
|
<Container margin={{ bottom: 'medium', left: 'xxxlarge', right: 'xxxlarge' }}>
|
||||||
className={css.pagination}
|
<Pagination
|
||||||
hidePageNumbers
|
className={css.pagination}
|
||||||
gotoPage={index => setPageIndex(index)}
|
hidePageNumbers
|
||||||
itemCount={itemCount}
|
gotoPage={index => setPageIndex(index)}
|
||||||
pageCount={pageCount}
|
itemCount={itemCount}
|
||||||
pageIndex={pageIndex}
|
pageCount={pageCount}
|
||||||
pageSize={pageSize}
|
pageIndex={pageIndex}
|
||||||
pageSizeOptions={[5, 10, 20, 40]}
|
pageSize={pageSize}
|
||||||
/>
|
pageSizeOptions={[5, 10, 20, 40]}
|
||||||
</Container>
|
/>
|
||||||
|
</Container>
|
||||||
|
)}
|
||||||
</Container>
|
</Container>
|
||||||
</PageBody>
|
</PageBody>
|
||||||
</Container>
|
</Container>
|
||||||
|
BIN
web/src/views/Repos/empty_state.png
Normal file
BIN
web/src/views/Repos/empty_state.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 91 KiB |
130
web/src/views/Repos/gitignores.ts
Normal file
130
web/src/views/Repos/gitignores.ts
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
export default [
|
||||||
|
'None',
|
||||||
|
'Actionscript',
|
||||||
|
'Ada',
|
||||||
|
'Agda',
|
||||||
|
'Android',
|
||||||
|
'AppEngine',
|
||||||
|
'AppceleratorTitanium',
|
||||||
|
'ArchLinuxPackages',
|
||||||
|
'Autotools',
|
||||||
|
'C',
|
||||||
|
'C++',
|
||||||
|
'CFWheels',
|
||||||
|
'CMake',
|
||||||
|
'CUDA',
|
||||||
|
'CakePHP',
|
||||||
|
'ChefCookbook',
|
||||||
|
'Clojure',
|
||||||
|
'CodeIgniter',
|
||||||
|
'CommonLisp',
|
||||||
|
'Composer',
|
||||||
|
'Concrete5',
|
||||||
|
'Coq',
|
||||||
|
'CraftCMS',
|
||||||
|
'D',
|
||||||
|
'DM',
|
||||||
|
'Dart',
|
||||||
|
'Delphi',
|
||||||
|
'Drupal',
|
||||||
|
'EPiServer',
|
||||||
|
'Eagle',
|
||||||
|
'Elisp',
|
||||||
|
'Elixir',
|
||||||
|
'Elm',
|
||||||
|
'Erlang',
|
||||||
|
'ExpressionEngine',
|
||||||
|
'ExtJs',
|
||||||
|
'Fancy',
|
||||||
|
'Finale',
|
||||||
|
'ForceDotCom',
|
||||||
|
'Fortran',
|
||||||
|
'FuelPHP',
|
||||||
|
'GWT',
|
||||||
|
'GitBook',
|
||||||
|
'Go',
|
||||||
|
'Godot',
|
||||||
|
'Gradle',
|
||||||
|
'Grails',
|
||||||
|
'Haskell',
|
||||||
|
'IGORPro',
|
||||||
|
'Idris',
|
||||||
|
'JENKINS_HOME',
|
||||||
|
'Java',
|
||||||
|
'Jboss',
|
||||||
|
'Jekyll',
|
||||||
|
'Joomla',
|
||||||
|
'Julia',
|
||||||
|
'KiCAD',
|
||||||
|
'Kohana',
|
||||||
|
'Kotlin',
|
||||||
|
'LabVIEW',
|
||||||
|
'Laravel',
|
||||||
|
'Leiningen',
|
||||||
|
'LemonStand',
|
||||||
|
'Lilypond',
|
||||||
|
'Lithium',
|
||||||
|
'Lua',
|
||||||
|
'Magento',
|
||||||
|
'Maven',
|
||||||
|
'Mercury',
|
||||||
|
'MetaProgrammingSystem',
|
||||||
|
'Nim',
|
||||||
|
'Node',
|
||||||
|
'OCaml',
|
||||||
|
'Objective-C',
|
||||||
|
'Opa',
|
||||||
|
'OracleForms',
|
||||||
|
'Packer',
|
||||||
|
'Perl',
|
||||||
|
'Perl6',
|
||||||
|
'Phalcon',
|
||||||
|
'PlayFramework',
|
||||||
|
'Plone',
|
||||||
|
'Prestashop',
|
||||||
|
'Processing',
|
||||||
|
'PureScript',
|
||||||
|
'Python',
|
||||||
|
'Qooxdoo',
|
||||||
|
'Qt',
|
||||||
|
'R',
|
||||||
|
'ROS',
|
||||||
|
'Rails',
|
||||||
|
'RhodesRhomobile',
|
||||||
|
'Ruby',
|
||||||
|
'Rust',
|
||||||
|
'SCons',
|
||||||
|
'Sass',
|
||||||
|
'Scala',
|
||||||
|
'Scheme',
|
||||||
|
'Scrivener',
|
||||||
|
'Sdcc',
|
||||||
|
'SeamGen',
|
||||||
|
'SketchUp',
|
||||||
|
'Smalltalk',
|
||||||
|
'SugarCRM',
|
||||||
|
'Swift',
|
||||||
|
'Symfony',
|
||||||
|
'SymphonyCMS',
|
||||||
|
'TeX',
|
||||||
|
'Terraform',
|
||||||
|
'Textpattern',
|
||||||
|
'TurboGears2',
|
||||||
|
'Typo3',
|
||||||
|
'Umbraco',
|
||||||
|
'Unity',
|
||||||
|
'UnrealEngine',
|
||||||
|
'VVVV',
|
||||||
|
'VisualStudio',
|
||||||
|
'Waf',
|
||||||
|
'WordPress',
|
||||||
|
'Xojo',
|
||||||
|
'Yeoman',
|
||||||
|
'Yii',
|
||||||
|
'ZendFramework',
|
||||||
|
'Zephir',
|
||||||
|
'gcov',
|
||||||
|
'nanoc',
|
||||||
|
'opencart',
|
||||||
|
'stella'
|
||||||
|
].map(entry => ({ label: entry, value: entry }))
|
31
web/src/views/Repos/licences.ts
Normal file
31
web/src/views/Repos/licences.ts
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
export default [
|
||||||
|
{ label: 'None', value: 'none' },
|
||||||
|
{ label: 'Academic Free License v3.0', value: 'afl-3.0' },
|
||||||
|
{ label: 'Apache license 2.0', value: 'apache-2.0' },
|
||||||
|
{ label: 'Artistic license 2.0', value: 'artistic-2.0' },
|
||||||
|
{ label: 'Boost Software License 1.0', value: 'bsl-1.0' },
|
||||||
|
{ label: 'BSD 2-clause "Simplified" license', value: 'bsd-2-clause' },
|
||||||
|
{ label: 'BSD 3-clause "New" or "Revised" license', value: 'bsd-3-clause' },
|
||||||
|
{ label: 'BSD 3-clause Clear license', value: 'bsd-3-clause-clear' },
|
||||||
|
{ label: 'Creative Commons license family', value: 'cc' },
|
||||||
|
{ label: 'Creative Commons Zero v1.0 Universal', value: 'cc0-1.0' },
|
||||||
|
{ label: 'Creative Commons Attribution 4.0', value: 'cc-by-4.0' },
|
||||||
|
{ label: 'Creative Commons Attribution Share Alike 4.0', value: 'cc-by-sa-4.0' },
|
||||||
|
{ label: 'Educational Community License v2.0', value: 'ecl-2.0' },
|
||||||
|
{ label: 'Eclipse Public License 1.0', value: 'epl-1.0' },
|
||||||
|
{ label: 'Eclipse Public License 2.0', value: 'epl-2.0' },
|
||||||
|
{ label: 'European Union Public License 1.1', value: 'eupl-1.1' },
|
||||||
|
{ label: 'GNU Affero General Public License v3.0', value: 'agpl-3.0' },
|
||||||
|
{ label: 'GNU General Public License family', value: 'gpl' },
|
||||||
|
{ label: 'GNU General Public License v2.0', value: 'gpl-2.0' },
|
||||||
|
{ label: 'GNU General Public License v3.0', value: 'gpl-3.0' },
|
||||||
|
{ label: 'GNU Lesser General Public License family', value: 'lgpl' },
|
||||||
|
{ label: 'GNU Lesser General Public License v2.1', value: 'lgpl-2.1' },
|
||||||
|
{ label: 'GNU Lesser General Public License v3.0', value: 'lgpl-3.0' },
|
||||||
|
{ label: 'ISC', value: 'isc' },
|
||||||
|
{ label: 'MIT', value: 'mit' },
|
||||||
|
{ label: 'Mozilla Public License 2.0', value: 'mpl-2.0' },
|
||||||
|
{ label: 'Open Software License 3.0', value: 'osl-3.0' },
|
||||||
|
{ label: 'The Unlicense', value: 'unlicense' },
|
||||||
|
{ label: 'zLib License', value: 'zlib' }
|
||||||
|
]
|
@ -1,3 +0,0 @@
|
|||||||
.main {
|
|
||||||
padding: var(--spacing-xlarge);
|
|
||||||
}
|
|
12
web/src/views/TestView/TestView.module.scss.d.ts
vendored
12
web/src/views/TestView/TestView.module.scss.d.ts
vendored
@ -1,12 +0,0 @@
|
|||||||
/* eslint-disable */
|
|
||||||
/**
|
|
||||||
* Copyright 2021 Harness Inc. All rights reserved.
|
|
||||||
* Use of this source code is governed by the PolyForm Shield 1.0.0 license
|
|
||||||
* that can be found in the licenses directory at the root of this repository, also available at
|
|
||||||
* https://polyformproject.org/wp-content/uploads/2020/06/PolyForm-Shield-1.0.0.txt.
|
|
||||||
**/
|
|
||||||
// this is an auto-generated file, do not update this manually
|
|
||||||
declare const styles: {
|
|
||||||
readonly main: string
|
|
||||||
}
|
|
||||||
export default styles
|
|
@ -1,7 +0,0 @@
|
|||||||
import React from 'react'
|
|
||||||
import { Container } from '@harness/uicore'
|
|
||||||
import css from './TestView.module.scss'
|
|
||||||
|
|
||||||
export const TestView: React.FC = () => {
|
|
||||||
return <Container className={css.main}>TestView</Container>
|
|
||||||
}
|
|
4903
web/yarn.lock
4903
web/yarn.lock
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user