mirror of
https://github.com/harness/drone.git
synced 2025-05-09 12:50:38 +08:00
Consolidate pagination for the whole codebase + Use SearchInputWithSpinner component (#222)
* Remove center layout for empty repo info - hard to read * Consolidate pagination for the whole codebase * Use SearchInputWithSpinner component * Use SearchInputWithSpinner component
This commit is contained in:
parent
66cc979334
commit
1aa1123bd2
@ -1,40 +0,0 @@
|
||||
import React from 'react'
|
||||
import cx from 'classnames'
|
||||
import { Button, ButtonSize, Container, Layout } from '@harness/uicore'
|
||||
import { useStrings } from 'framework/strings'
|
||||
import css from './PrevNextPagination.module.scss'
|
||||
|
||||
interface PrevNextPaginationProps {
|
||||
onPrev?: false | (() => void)
|
||||
onNext?: false | (() => void)
|
||||
skipLayout?: boolean
|
||||
}
|
||||
|
||||
export function PrevNextPagination({ onPrev, onNext, skipLayout }: PrevNextPaginationProps) {
|
||||
const { getString } = useStrings()
|
||||
|
||||
return (
|
||||
<Container className={skipLayout ? undefined : css.main}>
|
||||
<Layout.Horizontal>
|
||||
<Button
|
||||
text={getString('prev')}
|
||||
icon="arrow-left"
|
||||
size={ButtonSize.SMALL}
|
||||
className={cx(css.roundedButton, css.buttonLeft)}
|
||||
iconProps={{ size: 12 }}
|
||||
onClick={onPrev ? onPrev : undefined}
|
||||
disabled={!onPrev}
|
||||
/>
|
||||
<Button
|
||||
text={getString('next')}
|
||||
rightIcon="arrow-right"
|
||||
size={ButtonSize.SMALL}
|
||||
className={cx(css.roundedButton, css.buttonRight)}
|
||||
iconProps={{ size: 12 }}
|
||||
onClick={onNext ? onNext : undefined}
|
||||
disabled={!onNext}
|
||||
/>
|
||||
</Layout.Horizontal>
|
||||
</Container>
|
||||
)
|
||||
}
|
@ -1,3 +1,7 @@
|
||||
.pagination {
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
.main {
|
||||
display: flex;
|
||||
align-items: center;
|
@ -1,6 +1,7 @@
|
||||
/* eslint-disable */
|
||||
// this is an auto-generated file
|
||||
declare const styles: {
|
||||
readonly pagination: string
|
||||
readonly main: string
|
||||
readonly roundedButton: string
|
||||
readonly selected: string
|
@ -0,0 +1,107 @@
|
||||
import React, { useCallback, useMemo } from 'react'
|
||||
import cx from 'classnames'
|
||||
import { Button, ButtonSize, Container, Layout, Pagination } from '@harness/uicore'
|
||||
import { useStrings } from 'framework/strings'
|
||||
import css from './ResourceListingPagination.module.scss'
|
||||
|
||||
interface ResourceListingPaginationProps {
|
||||
response: Response | null
|
||||
page: number
|
||||
setPage: React.Dispatch<React.SetStateAction<number>>
|
||||
scrollTop?: boolean
|
||||
}
|
||||
|
||||
// There are two type of pagination results returned from Code API.
|
||||
// One returns information that works with UICore Pagination component in which we know total pages, total items, etc... The other
|
||||
// has only information to render Prev, Next.
|
||||
//
|
||||
// This component consolidates both cases to remove same pagination logic in pages and components.
|
||||
export const ResourceListingPagination: React.FC<ResourceListingPaginationProps> = ({
|
||||
response,
|
||||
page,
|
||||
setPage,
|
||||
scrollTop = true
|
||||
}) => {
|
||||
const { X_NEXT_PAGE, X_PREV_PAGE, totalItems, totalPages, pageSize } = useParsePaginationInfo(response)
|
||||
const _setPage = useCallback(
|
||||
(_page: number) => {
|
||||
if (scrollTop) {
|
||||
setTimeout(() => {
|
||||
window.scrollTo({
|
||||
top: 0,
|
||||
left: 0,
|
||||
behavior: 'smooth'
|
||||
})
|
||||
}, 0)
|
||||
}
|
||||
setPage(_page)
|
||||
},
|
||||
[setPage, scrollTop]
|
||||
)
|
||||
|
||||
return totalItems ? (
|
||||
page === 1 && totalItems < pageSize ? null : (
|
||||
<Container margin={{ left: 'medium', right: 'medium' }}>
|
||||
<Pagination
|
||||
className={css.pagination}
|
||||
hidePageNumbers
|
||||
gotoPage={index => _setPage(index + 1)}
|
||||
itemCount={totalItems}
|
||||
pageCount={totalPages}
|
||||
pageIndex={page - 1}
|
||||
pageSize={pageSize}
|
||||
/>
|
||||
</Container>
|
||||
)
|
||||
) : page === 1 && !X_PREV_PAGE && !X_NEXT_PAGE ? null : (
|
||||
<PrevNextPagination
|
||||
onPrev={!!X_PREV_PAGE && (() => _setPage(page - 1))}
|
||||
onNext={!!X_NEXT_PAGE && (() => _setPage(page + 1))}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function useParsePaginationInfo(response: Nullable<Response>) {
|
||||
const totalItems = useMemo(() => parseInt(response?.headers?.get('x-total') || '0'), [response])
|
||||
const totalPages = useMemo(() => parseInt(response?.headers?.get('x-total-pages') || '0'), [response])
|
||||
const pageSize = useMemo(() => parseInt(response?.headers?.get('x-per-page') || '0'), [response])
|
||||
const X_NEXT_PAGE = useMemo(() => parseInt(response?.headers?.get('x-next-page') || '0'), [response])
|
||||
const X_PREV_PAGE = useMemo(() => parseInt(response?.headers?.get('x-prev-page') || '0'), [response])
|
||||
|
||||
return { totalItems, totalPages, pageSize, X_NEXT_PAGE, X_PREV_PAGE }
|
||||
}
|
||||
|
||||
interface PrevNextPaginationProps {
|
||||
onPrev?: false | (() => void)
|
||||
onNext?: false | (() => void)
|
||||
skipLayout?: boolean
|
||||
}
|
||||
|
||||
function PrevNextPagination({ onPrev, onNext, skipLayout }: PrevNextPaginationProps) {
|
||||
const { getString } = useStrings()
|
||||
|
||||
return (
|
||||
<Container className={skipLayout ? undefined : css.main}>
|
||||
<Layout.Horizontal>
|
||||
<Button
|
||||
text={getString('prev')}
|
||||
icon="arrow-left"
|
||||
size={ButtonSize.SMALL}
|
||||
className={cx(css.roundedButton, css.buttonLeft)}
|
||||
iconProps={{ size: 12 }}
|
||||
onClick={onPrev ? onPrev : undefined}
|
||||
disabled={!onPrev}
|
||||
/>
|
||||
<Button
|
||||
text={getString('next')}
|
||||
rightIcon="arrow-right"
|
||||
size={ButtonSize.SMALL}
|
||||
className={cx(css.roundedButton, css.buttonRight)}
|
||||
iconProps={{ size: 12 }}
|
||||
onClick={onNext ? onNext : undefined}
|
||||
disabled={!onNext}
|
||||
/>
|
||||
</Layout.Horizontal>
|
||||
</Container>
|
||||
)
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
.main {
|
||||
&,
|
||||
.layout {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
padding: 0 0 0 var(--spacing-small) !important;
|
||||
margin-bottom: 0 !important;
|
||||
|
||||
.input {
|
||||
span[data-icon],
|
||||
span[icon] {
|
||||
margin-top: 10px !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
9
web/src/components/SearchInputWithSpinner/SearchInputWithSpinner.module.scss.d.ts
vendored
Normal file
9
web/src/components/SearchInputWithSpinner/SearchInputWithSpinner.module.scss.d.ts
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
/* eslint-disable */
|
||||
// this is an auto-generated file
|
||||
declare const styles: {
|
||||
readonly main: string
|
||||
readonly layout: string
|
||||
readonly wrapper: string
|
||||
readonly input: string
|
||||
}
|
||||
export default styles
|
@ -0,0 +1,44 @@
|
||||
import React from 'react'
|
||||
import { Color, Container, Icon, IconName, Layout, TextInput } from '@harness/uicore'
|
||||
import { useStrings } from 'framework/strings'
|
||||
import css from './SearchInputWithSpinner.module.scss'
|
||||
|
||||
interface SearchInputWithSpinnerProps {
|
||||
query?: string
|
||||
setQuery: (value: string) => void
|
||||
loading?: boolean
|
||||
width?: number
|
||||
placeholder?: string
|
||||
icon?: IconName
|
||||
spinnerIcon?: IconName
|
||||
}
|
||||
|
||||
export const SearchInputWithSpinner: React.FC<SearchInputWithSpinnerProps> = ({
|
||||
query = '',
|
||||
setQuery,
|
||||
loading = false,
|
||||
width = 250,
|
||||
placeholder,
|
||||
icon = 'search',
|
||||
spinnerIcon = 'spinner'
|
||||
}) => {
|
||||
const { getString } = useStrings()
|
||||
return (
|
||||
<Container className={css.main}>
|
||||
<Layout.Horizontal className={css.layout}>
|
||||
{loading && <Icon name={spinnerIcon as IconName} color={Color.PRIMARY_7} />}
|
||||
<TextInput
|
||||
value={query}
|
||||
wrapperClassName={css.wrapper}
|
||||
className={css.input}
|
||||
placeholder={placeholder || getString('search')}
|
||||
leftIcon={icon as IconName}
|
||||
style={{ width }}
|
||||
autoFocus
|
||||
onFocus={event => event.target.select()}
|
||||
onInput={event => setQuery(event.currentTarget.value || '')}
|
||||
/>
|
||||
</Layout.Horizontal>
|
||||
</Container>
|
||||
)
|
||||
}
|
@ -221,6 +221,7 @@ export interface StringsMap {
|
||||
webhookCreated: string
|
||||
webhookDeleted: string
|
||||
webhookDetails: string
|
||||
webhookEmpty: string
|
||||
webhookEventsLabel: string
|
||||
webhookListingContent: string
|
||||
webhookSelectAllEvents: string
|
||||
|
@ -1,11 +0,0 @@
|
||||
import { useMemo } from 'react'
|
||||
|
||||
export function useGetPaginationInfo(response: Nullable<Response>) {
|
||||
const totalItems = useMemo(() => parseInt(response?.headers?.get('x-total') || '0'), [response])
|
||||
const totalPages = useMemo(() => parseInt(response?.headers?.get('x-total-pages') || '0'), [response])
|
||||
const pageSize = useMemo(() => parseInt(response?.headers?.get('x-per-page') || '0'), [response])
|
||||
const X_NEXT_PAGE = useMemo(() => parseInt(response?.headers?.get('x-next-page') || '0'), [response])
|
||||
const X_PREV_PAGE = useMemo(() => parseInt(response?.headers?.get('x-prev-page') || '0'), [response])
|
||||
|
||||
return { totalItems, totalPages, pageSize, X_NEXT_PAGE, X_PREV_PAGE }
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
import { useState } from 'react'
|
||||
|
||||
export function usePageIndex(index = 0) {
|
||||
export function usePageIndex(index = 1) {
|
||||
return useState(index)
|
||||
}
|
||||
|
14
web/src/hooks/useShowRequestError.ts
Normal file
14
web/src/hooks/useShowRequestError.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import { useToaster } from '@harness/uicore'
|
||||
import { useEffect } from 'react'
|
||||
import type { GetDataError } from 'restful-react'
|
||||
import { getErrorMessage } from 'utils/Utils'
|
||||
|
||||
export function useShowRequestError(error: GetDataError<Unknown> | null) {
|
||||
const { showError } = useToaster()
|
||||
|
||||
useEffect(() => {
|
||||
if (error) {
|
||||
showError(getErrorMessage(error))
|
||||
}
|
||||
}, [error, showError])
|
||||
}
|
@ -264,3 +264,4 @@ repoEmptyMarkdown: |
|
||||
```
|
||||
|
||||
You might need [to create an API token](CREATE_API_TOKEN_URL) in order to pull from or push into this repository.
|
||||
webhookEmpty: Here is no WebHooks. Try to
|
||||
|
@ -12,8 +12,8 @@ import { makeDiffRefs } from 'utils/GitUtils'
|
||||
import { CommitsView } from 'components/CommitsView/CommitsView'
|
||||
import { Changes } from 'components/Changes/Changes'
|
||||
import type { RepoCommit } from 'services/code'
|
||||
import { PrevNextPagination } from 'components/PrevNextPagination/PrevNextPagination'
|
||||
import { usePageIndex } from 'hooks/usePageIndex'
|
||||
import { ResourceListingPagination } from 'components/ResourceListingPagination/ResourceListingPagination'
|
||||
import { CompareContentHeader } from './CompareContentHeader/CompareContentHeader'
|
||||
import css from './Compare.module.scss'
|
||||
|
||||
@ -24,17 +24,18 @@ export default function Compare() {
|
||||
const { repoMetadata, error, loading, diffRefs } = useGetRepositoryMetadata()
|
||||
const [sourceGitRef, setSourceGitRef] = useState(diffRefs.sourceGitRef)
|
||||
const [targetGitRef, setTargetGitRef] = useState(diffRefs.targetGitRef)
|
||||
const [pageIndex, setPageIndex] = usePageIndex()
|
||||
const [page, setPage] = usePageIndex()
|
||||
const limit = LIST_FETCHING_LIMIT
|
||||
const {
|
||||
data: commits,
|
||||
error: commitsError,
|
||||
refetch
|
||||
refetch,
|
||||
response
|
||||
} = useGet<RepoCommit[]>({
|
||||
path: `/api/v1/repos/${repoMetadata?.path}/+/commits`,
|
||||
queryParams: {
|
||||
limit,
|
||||
page: pageIndex + 1,
|
||||
page,
|
||||
git_ref: sourceGitRef,
|
||||
after: targetGitRef
|
||||
},
|
||||
@ -87,7 +88,7 @@ export default function Compare() {
|
||||
id="branchesTags"
|
||||
defaultSelectedTabId="diff"
|
||||
large={false}
|
||||
onChange={() => setPageIndex(0)}
|
||||
onChange={() => setPage(1)}
|
||||
tabList={[
|
||||
{
|
||||
id: 'commits',
|
||||
@ -95,10 +96,7 @@ export default function Compare() {
|
||||
panel: (
|
||||
<Container padding="xlarge">
|
||||
{!!commits?.length && <CommitsView commits={commits} repoMetadata={repoMetadata} />}
|
||||
<PrevNextPagination
|
||||
onPrev={pageIndex > 0 && (() => setPageIndex(pageIndex - 1))}
|
||||
onNext={commits?.length === limit && (() => setPageIndex(pageIndex + 1))}
|
||||
/>
|
||||
<ResourceListingPagination response={response} page={page} setPage={setPage} />
|
||||
</Container>
|
||||
)
|
||||
},
|
||||
|
@ -4,8 +4,8 @@ import type { RepoCommit } from 'services/code'
|
||||
import type { GitInfoProps } from 'utils/GitUtils'
|
||||
import { voidFn, LIST_FETCHING_LIMIT } from 'utils/Utils'
|
||||
import { usePageIndex } from 'hooks/usePageIndex'
|
||||
import { ResourceListingPagination } from 'components/ResourceListingPagination/ResourceListingPagination'
|
||||
import { CommitsView } from 'components/CommitsView/CommitsView'
|
||||
import { PrevNextPagination } from 'components/PrevNextPagination/PrevNextPagination'
|
||||
import { PullRequestTabContentWrapper } from '../PullRequestTabContentWrapper'
|
||||
|
||||
export const PullRequestCommits: React.FC<Pick<GitInfoProps, 'repoMetadata' | 'pullRequestMetadata'>> = ({
|
||||
@ -13,17 +13,18 @@ export const PullRequestCommits: React.FC<Pick<GitInfoProps, 'repoMetadata' | 'p
|
||||
pullRequestMetadata
|
||||
}) => {
|
||||
const limit = LIST_FETCHING_LIMIT
|
||||
const [pageIndex, setPageIndex] = usePageIndex()
|
||||
const [page, setPage] = usePageIndex()
|
||||
const {
|
||||
data: commits,
|
||||
error,
|
||||
loading,
|
||||
refetch
|
||||
refetch,
|
||||
response
|
||||
} = useGet<RepoCommit[]>({
|
||||
path: `/api/v1/repos/${repoMetadata?.path}/+/commits`,
|
||||
queryParams: {
|
||||
limit,
|
||||
page: pageIndex + 1,
|
||||
page,
|
||||
git_ref: pullRequestMetadata.source_branch,
|
||||
after: pullRequestMetadata.target_branch
|
||||
},
|
||||
@ -34,10 +35,7 @@ export const PullRequestCommits: React.FC<Pick<GitInfoProps, 'repoMetadata' | 'p
|
||||
<PullRequestTabContentWrapper loading={loading} error={error} onRetry={voidFn(refetch)}>
|
||||
{!!commits?.length && <CommitsView commits={commits} repoMetadata={repoMetadata} />}
|
||||
|
||||
<PrevNextPagination
|
||||
onPrev={pageIndex > 0 && (() => setPageIndex(pageIndex - 1))}
|
||||
onNext={commits?.length === limit && (() => setPageIndex(pageIndex + 1))}
|
||||
/>
|
||||
<ResourceListingPagination response={response} page={page} setPage={setPage} />
|
||||
</PullRequestTabContentWrapper>
|
||||
)
|
||||
}
|
||||
|
@ -25,6 +25,7 @@ import { voidFn, getErrorMessage, LIST_FETCHING_LIMIT } from 'utils/Utils'
|
||||
import emptyStateImage from 'images/empty-state.svg'
|
||||
import { usePageIndex } from 'hooks/usePageIndex'
|
||||
import type { TypesPullReq } from 'services/code'
|
||||
import { ResourceListingPagination } from 'components/ResourceListingPagination/ResourceListingPagination'
|
||||
import { PullRequestsContentHeader } from './PullRequestsContentHeader/PullRequestsContentHeader'
|
||||
import prImgOpen from './pull-request-open.svg'
|
||||
import prImgMerged from './pull-request-merged.svg'
|
||||
@ -39,17 +40,18 @@ export default function PullRequests() {
|
||||
const { routes } = useAppContext()
|
||||
const [searchTerm, setSearchTerm] = useState('')
|
||||
const [filter, setFilter] = useState<string>(PullRequestFilterOption.OPEN)
|
||||
const [pageIndex, setPageIndex] = usePageIndex()
|
||||
const [page, setPage] = usePageIndex()
|
||||
const { repoMetadata, error, loading, refetch } = useGetRepositoryMetadata()
|
||||
const {
|
||||
data,
|
||||
error: prError,
|
||||
loading: prLoading
|
||||
loading: prLoading,
|
||||
response
|
||||
} = useGet<TypesPullReq[]>({
|
||||
path: `/api/v1/repos/${repoMetadata?.path}/+/pullreq`,
|
||||
queryParams: {
|
||||
limit: String(LIST_FETCHING_LIMIT),
|
||||
page: String(pageIndex + 1),
|
||||
page,
|
||||
sort: filter == PullRequestFilterOption.MERGED ? 'merged' : 'number',
|
||||
order: 'desc',
|
||||
query: searchTerm,
|
||||
@ -113,30 +115,33 @@ export default function PullRequests() {
|
||||
repoMetadata={repoMetadata}
|
||||
onPullRequestFilterChanged={_filter => {
|
||||
setFilter(_filter)
|
||||
setPageIndex(0)
|
||||
setPage(1)
|
||||
}}
|
||||
onSearchTermChanged={value => {
|
||||
setSearchTerm(value)
|
||||
setPageIndex(0)
|
||||
setPage(1)
|
||||
}}
|
||||
/>
|
||||
<Container padding="xlarge">
|
||||
{!!data?.length && (
|
||||
<TableV2<TypesPullReq>
|
||||
className={css.table}
|
||||
hideHeaders
|
||||
columns={columns}
|
||||
data={data}
|
||||
getRowClassName={() => css.row}
|
||||
onRowClick={row => {
|
||||
history.push(
|
||||
routes.toCODEPullRequest({
|
||||
repoPath: repoMetadata.path as string,
|
||||
pullRequestId: String(row.number)
|
||||
})
|
||||
)
|
||||
}}
|
||||
/>
|
||||
<>
|
||||
<TableV2<TypesPullReq>
|
||||
className={css.table}
|
||||
hideHeaders
|
||||
columns={columns}
|
||||
data={data}
|
||||
getRowClassName={() => css.row}
|
||||
onRowClick={row => {
|
||||
history.push(
|
||||
routes.toCODEPullRequest({
|
||||
repoPath: repoMetadata.path as string,
|
||||
pullRequestId: String(row.number)
|
||||
})
|
||||
)
|
||||
}}
|
||||
/>
|
||||
<ResourceListingPagination response={response} page={page} setPage={setPage} />
|
||||
</>
|
||||
)}
|
||||
{data?.length === 0 && (
|
||||
<Container className={css.noData}>
|
||||
|
@ -9,15 +9,6 @@
|
||||
> div {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.input {
|
||||
margin-bottom: 0 !important;
|
||||
|
||||
span[data-icon],
|
||||
span[icon] {
|
||||
margin-top: 10px !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.branchDropdown {
|
||||
|
@ -2,7 +2,6 @@
|
||||
// this is an auto-generated file
|
||||
declare const styles: {
|
||||
readonly main: string
|
||||
readonly input: string
|
||||
readonly branchDropdown: string
|
||||
}
|
||||
export default styles
|
||||
|
@ -1,9 +1,10 @@
|
||||
import { useHistory } from 'react-router-dom'
|
||||
import React, { useMemo, useState } from 'react'
|
||||
import { Container, Layout, FlexExpander, DropDown, ButtonVariation, TextInput, Button } from '@harness/uicore'
|
||||
import { Container, Layout, FlexExpander, DropDown, ButtonVariation, Button } from '@harness/uicore'
|
||||
import { useStrings } from 'framework/strings'
|
||||
import { CodeIcon, GitInfoProps, makeDiffRefs, PullRequestFilterOption } from 'utils/GitUtils'
|
||||
import { useAppContext } from 'AppContext'
|
||||
import { SearchInputWithSpinner } from 'components/SearchInputWithSpinner/SearchInputWithSpinner'
|
||||
import css from './PullRequestsContentHeader.module.scss'
|
||||
|
||||
interface PullRequestsContentHeaderProps extends Pick<GitInfoProps, 'repoMetadata'> {
|
||||
@ -36,7 +37,6 @@ export function PullRequestsContentHeader({
|
||||
],
|
||||
[getString]
|
||||
)
|
||||
const showSpinner = useMemo(() => loading, [loading])
|
||||
|
||||
return (
|
||||
<Container className={css.main} padding="xlarge">
|
||||
@ -51,18 +51,13 @@ export function PullRequestsContentHeader({
|
||||
popoverClassName={css.branchDropdown}
|
||||
/>
|
||||
<FlexExpander />
|
||||
<TextInput
|
||||
className={css.input}
|
||||
placeholder={getString('search')}
|
||||
autoFocus
|
||||
onFocus={event => event.target.select()}
|
||||
value={searchTerm}
|
||||
onInput={event => {
|
||||
const value = event.currentTarget.value
|
||||
<SearchInputWithSpinner
|
||||
loading={loading}
|
||||
query={searchTerm}
|
||||
setQuery={value => {
|
||||
setSearchTerm(value)
|
||||
onSearchTermChanged(value)
|
||||
}}
|
||||
leftIcon={showSpinner ? CodeIcon.InputSpinner : CodeIcon.InputSearch}
|
||||
/>
|
||||
<Button
|
||||
variation={ButtonVariation.PRIMARY}
|
||||
|
@ -13,6 +13,10 @@
|
||||
}
|
||||
}
|
||||
|
||||
.layout {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.input {
|
||||
span[data-icon],
|
||||
span[icon] {
|
||||
@ -98,7 +102,3 @@
|
||||
padding-top: var(--spacing-xsmall) !important;
|
||||
}
|
||||
}
|
||||
|
||||
.pagination {
|
||||
padding-top: 0;
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
// this is an auto-generated file
|
||||
declare const styles: {
|
||||
readonly main: string
|
||||
readonly layout: string
|
||||
readonly input: string
|
||||
readonly withError: string
|
||||
readonly table: string
|
||||
@ -13,6 +14,5 @@ declare const styles: {
|
||||
readonly repoName: string
|
||||
readonly repoScope: string
|
||||
readonly desc: string
|
||||
readonly pagination: string
|
||||
}
|
||||
export default styles
|
||||
|
@ -9,9 +9,7 @@ import {
|
||||
TableV2 as Table,
|
||||
Text,
|
||||
Color,
|
||||
Pagination,
|
||||
Icon,
|
||||
TextInput
|
||||
Icon
|
||||
} from '@harness/uicore'
|
||||
import type { CellProps, Column } from 'react-table'
|
||||
import Keywords from 'react-keywords'
|
||||
@ -23,10 +21,10 @@ import { voidFn, formatDate, getErrorMessage, LIST_FETCHING_LIMIT } from 'utils/
|
||||
import { NewRepoModalButton } from 'components/NewRepoModalButton/NewRepoModalButton'
|
||||
import type { TypesRepository } from 'services/code'
|
||||
import { usePageIndex } from 'hooks/usePageIndex'
|
||||
import { useGetPaginationInfo } from 'hooks/useGetPaginationInfo'
|
||||
import { useGetSpaceParam } from 'hooks/useGetSpaceParam'
|
||||
import { SearchInputWithSpinner } from 'components/SearchInputWithSpinner/SearchInputWithSpinner'
|
||||
import { useAppContext } from 'AppContext'
|
||||
import { CodeIcon } from 'utils/GitUtils'
|
||||
import { ResourceListingPagination } from 'components/ResourceListingPagination/ResourceListingPagination'
|
||||
import emptyStateImage from './empty-state.svg'
|
||||
import css from './RepositoriesListing.module.scss'
|
||||
|
||||
@ -38,7 +36,7 @@ export default function RepositoriesListing() {
|
||||
const space = useGetSpaceParam()
|
||||
const [searchTerm, setSearchTerm] = useState<string | undefined>()
|
||||
const { routes } = useAppContext()
|
||||
const [pageIndex, setPageIndex] = usePageIndex()
|
||||
const [page, setPage] = usePageIndex()
|
||||
const {
|
||||
data: repositories,
|
||||
error,
|
||||
@ -47,14 +45,13 @@ export default function RepositoriesListing() {
|
||||
response
|
||||
} = useGet<TypesRepository[]>({
|
||||
path: `/api/v1/spaces/${space}/+/repos`,
|
||||
queryParams: { page: pageIndex + 1, limit: LIST_FETCHING_LIMIT, query: searchTerm }
|
||||
queryParams: { page, limit: LIST_FETCHING_LIMIT, query: searchTerm }
|
||||
})
|
||||
const { totalItems, totalPages, pageSize } = useGetPaginationInfo(response)
|
||||
|
||||
useEffect(() => {
|
||||
setSearchTerm(undefined)
|
||||
setPageIndex(0)
|
||||
}, [space, setPageIndex])
|
||||
setPage(1)
|
||||
}, [space, setPage])
|
||||
|
||||
const columns: Column<TypesRepository>[] = useMemo(
|
||||
() => [
|
||||
@ -138,20 +135,10 @@ export default function RepositoriesListing() {
|
||||
button: NewRepoButton
|
||||
}}>
|
||||
<Container padding="xlarge">
|
||||
<Layout.Horizontal spacing="large">
|
||||
<Layout.Horizontal spacing="large" className={css.layout}>
|
||||
{NewRepoButton}
|
||||
<FlexExpander />
|
||||
<TextInput
|
||||
className={css.input}
|
||||
placeholder={getString('search')}
|
||||
leftIcon={loading && searchTerm !== undefined ? CodeIcon.InputSpinner : CodeIcon.InputSearch}
|
||||
style={{ width: 250 }}
|
||||
autoFocus
|
||||
onInput={event => {
|
||||
setSearchTerm(event.currentTarget.value || '')
|
||||
setPageIndex(0)
|
||||
}}
|
||||
/>
|
||||
<SearchInputWithSpinner loading={loading} query={searchTerm} setQuery={setSearchTerm} />
|
||||
</Layout.Horizontal>
|
||||
<Container margin={{ top: 'medium' }}>
|
||||
<Table<TypesRepository>
|
||||
@ -164,19 +151,7 @@ export default function RepositoriesListing() {
|
||||
getRowClassName={row => cx(css.row, !row.original.description && css.noDesc)}
|
||||
/>
|
||||
</Container>
|
||||
{!!repositories?.length && (
|
||||
<Container margin={{ left: 'medium', right: 'medium' }}>
|
||||
<Pagination
|
||||
className={css.pagination}
|
||||
hidePageNumbers
|
||||
gotoPage={index => setPageIndex(index)}
|
||||
itemCount={totalItems}
|
||||
pageCount={totalPages}
|
||||
pageIndex={pageIndex}
|
||||
pageSize={pageSize}
|
||||
/>
|
||||
</Container>
|
||||
)}
|
||||
<ResourceListingPagination response={response} page={page} setPage={setPage} />
|
||||
</Container>
|
||||
</PageBody>
|
||||
</Container>
|
||||
|
@ -1,11 +1,12 @@
|
||||
import React from 'react'
|
||||
import { Container, Color, Layout, Button, FlexExpander, ButtonVariation, Heading } from '@harness/uicore'
|
||||
import { Container, Color, Layout, Button, FlexExpander, ButtonVariation, Heading, Icon } from '@harness/uicore'
|
||||
import { useHistory } from 'react-router-dom'
|
||||
import { useGet } from 'restful-react'
|
||||
import cx from 'classnames'
|
||||
import { MarkdownViewer } from 'components/SourceCodeViewer/SourceCodeViewer'
|
||||
import { useAppContext } from 'AppContext'
|
||||
import type { OpenapiContentInfo, OpenapiGetContentOutput, RepoFileContent, TypesRepository } from 'services/code'
|
||||
import { useShowRequestError } from 'hooks/useShowRequestError'
|
||||
import { CodeIcon } from 'utils/GitUtils'
|
||||
import css from './Readme.module.scss'
|
||||
|
||||
@ -21,7 +22,7 @@ function ReadmeViewer({ metadata, gitRef, readmeInfo, contentOnly, maxWidth }: F
|
||||
const history = useHistory()
|
||||
const { routes } = useAppContext()
|
||||
|
||||
const { data /*error, loading, refetch, response */ } = useGet<OpenapiGetContentOutput>({
|
||||
const { data, error, loading } = useGet<OpenapiGetContentOutput>({
|
||||
path: `/api/v1/repos/${metadata.path}/+/content/${readmeInfo.path}`,
|
||||
queryParams: {
|
||||
include_commit: false,
|
||||
@ -29,6 +30,8 @@ function ReadmeViewer({ metadata, gitRef, readmeInfo, contentOnly, maxWidth }: F
|
||||
}
|
||||
})
|
||||
|
||||
useShowRequestError(error)
|
||||
|
||||
return (
|
||||
<Container
|
||||
className={cx(css.readmeContainer, contentOnly ? css.contentOnly : '')}
|
||||
@ -38,6 +41,7 @@ function ReadmeViewer({ metadata, gitRef, readmeInfo, contentOnly, maxWidth }: F
|
||||
<Layout.Horizontal padding="small" className={css.heading}>
|
||||
<Heading level={5}>{readmeInfo.name}</Heading>
|
||||
<FlexExpander />
|
||||
{loading && <Icon name="spinner" color={Color.PRIMARY_7} />}
|
||||
<Button
|
||||
variation={ButtonVariation.ICON}
|
||||
icon={CodeIcon.Edit}
|
||||
|
@ -1,11 +1,13 @@
|
||||
import React, { useMemo, useState } from 'react'
|
||||
import { Container, Layout, FlexExpander, DropDown, ButtonVariation, TextInput } from '@harness/uicore'
|
||||
import { Container, Layout, FlexExpander, DropDown, ButtonVariation } from '@harness/uicore'
|
||||
import { useStrings } from 'framework/strings'
|
||||
import { GitBranchType, CodeIcon, GitInfoProps } from 'utils/GitUtils'
|
||||
import { SearchInputWithSpinner } from 'components/SearchInputWithSpinner/SearchInputWithSpinner'
|
||||
import { CreateBranchModalButton } from 'components/CreateBranchModal/CreateBranchModal'
|
||||
import css from './BranchesContentHeader.module.scss'
|
||||
|
||||
interface BranchesContentHeaderProps extends Pick<GitInfoProps, 'repoMetadata'> {
|
||||
loading?: boolean
|
||||
activeBranchType?: GitBranchType
|
||||
onBranchTypeSwitched: (branchType: GitBranchType) => void
|
||||
onSearchTermChanged: (searchTerm: string) => void
|
||||
@ -17,7 +19,8 @@ export function BranchesContentHeader({
|
||||
onSearchTermChanged,
|
||||
activeBranchType = GitBranchType.ALL,
|
||||
repoMetadata,
|
||||
onNewBranchCreated
|
||||
onNewBranchCreated,
|
||||
loading
|
||||
}: BranchesContentHeaderProps) {
|
||||
const { getString } = useStrings()
|
||||
const [branchType, setBranchType] = useState(activeBranchType)
|
||||
@ -45,13 +48,10 @@ export function BranchesContentHeader({
|
||||
popoverClassName={css.branchDropdown}
|
||||
/>
|
||||
<FlexExpander />
|
||||
<TextInput
|
||||
placeholder={getString('searchBranches')}
|
||||
autoFocus
|
||||
onFocus={event => event.target.select()}
|
||||
value={searchTerm}
|
||||
onInput={event => {
|
||||
const value = event.currentTarget.value
|
||||
<SearchInputWithSpinner
|
||||
loading={loading}
|
||||
query={searchTerm}
|
||||
setQuery={value => {
|
||||
setSearchTerm(value)
|
||||
onSearchTermChanged(value)
|
||||
}}
|
||||
|
@ -4,11 +4,11 @@ import { useGet } from 'restful-react'
|
||||
import { useHistory } from 'react-router-dom'
|
||||
import type { RepoBranch } from 'services/code'
|
||||
import { usePageIndex } from 'hooks/usePageIndex'
|
||||
import { useGetPaginationInfo } from 'hooks/useGetPaginationInfo'
|
||||
import { LIST_FETCHING_LIMIT } from 'utils/Utils'
|
||||
import { useAppContext } from 'AppContext'
|
||||
import type { GitInfoProps } from 'utils/GitUtils'
|
||||
import { PrevNextPagination } from 'components/PrevNextPagination/PrevNextPagination'
|
||||
import { ResourceListingPagination } from 'components/ResourceListingPagination/ResourceListingPagination'
|
||||
import { useShowRequestError } from 'hooks/useShowRequestError'
|
||||
import { BranchesContentHeader } from './BranchesContentHeader/BranchesContentHeader'
|
||||
import { BranchesContent } from './BranchesContent/BranchesContent'
|
||||
import css from './RepositoryBranchesContent.module.scss'
|
||||
@ -17,30 +17,34 @@ export function RepositoryBranchesContent({ repoMetadata }: Pick<GitInfoProps, '
|
||||
const { routes } = useAppContext()
|
||||
const history = useHistory()
|
||||
const [searchTerm, setSearchTerm] = useState('')
|
||||
const [pageIndex, setPageIndex] = usePageIndex()
|
||||
const [page, setPage] = usePageIndex()
|
||||
const {
|
||||
data: branches,
|
||||
response /*error, loading,*/,
|
||||
response,
|
||||
error,
|
||||
loading,
|
||||
refetch
|
||||
} = useGet<RepoBranch[]>({
|
||||
path: `/api/v1/repos/${repoMetadata.path}/+/branches`,
|
||||
queryParams: {
|
||||
limit: LIST_FETCHING_LIMIT,
|
||||
page: pageIndex + 1,
|
||||
page,
|
||||
sort: 'date',
|
||||
order: 'desc',
|
||||
include_commit: true,
|
||||
query: searchTerm
|
||||
}
|
||||
})
|
||||
const { X_NEXT_PAGE, X_PREV_PAGE } = useGetPaginationInfo(response)
|
||||
|
||||
useShowRequestError(error)
|
||||
|
||||
return (
|
||||
<Container padding="xlarge" className={css.resourceContent}>
|
||||
<BranchesContentHeader
|
||||
loading={loading}
|
||||
repoMetadata={repoMetadata}
|
||||
onBranchTypeSwitched={gitRef => {
|
||||
setPageIndex(0)
|
||||
setPage(1)
|
||||
history.push(
|
||||
routes.toCODECommits({
|
||||
repoPath: repoMetadata.path as string,
|
||||
@ -50,7 +54,7 @@ export function RepositoryBranchesContent({ repoMetadata }: Pick<GitInfoProps, '
|
||||
}}
|
||||
onSearchTermChanged={value => {
|
||||
setSearchTerm(value)
|
||||
setPageIndex(0)
|
||||
setPage(1)
|
||||
}}
|
||||
onNewBranchCreated={refetch}
|
||||
/>
|
||||
@ -64,12 +68,7 @@ export function RepositoryBranchesContent({ repoMetadata }: Pick<GitInfoProps, '
|
||||
/>
|
||||
)}
|
||||
|
||||
<PrevNextPagination
|
||||
onPrev={!!X_PREV_PAGE && (() => setPageIndex(pageIndex - 1))}
|
||||
onNext={!!X_NEXT_PAGE && (() => setPageIndex(pageIndex + 1))}
|
||||
/>
|
||||
<ResourceListingPagination response={response} page={page} setPage={setPage} />
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
|
||||
// TODO: Handle loading and error
|
||||
|
@ -7,10 +7,6 @@
|
||||
background-color: var(--primary-bg);
|
||||
}
|
||||
|
||||
.pagination {
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
.contentHeader {
|
||||
> div {
|
||||
align-items: center;
|
||||
|
@ -3,7 +3,6 @@
|
||||
declare const styles: {
|
||||
readonly main: string
|
||||
readonly resourceContent: string
|
||||
readonly pagination: string
|
||||
readonly contentHeader: string
|
||||
}
|
||||
export default styles
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React from 'react'
|
||||
import { Container, FlexExpander, Layout, PageBody, Pagination } from '@harness/uicore'
|
||||
import { Container, FlexExpander, Layout, PageBody } from '@harness/uicore'
|
||||
import { useHistory } from 'react-router-dom'
|
||||
import { useGet } from 'restful-react'
|
||||
import { useGetRepositoryMetadata } from 'hooks/useGetRepositoryMetadata'
|
||||
@ -7,11 +7,11 @@ import { useAppContext } from 'AppContext'
|
||||
import { usePageIndex } from 'hooks/usePageIndex'
|
||||
import type { RepoCommit } from 'services/code'
|
||||
import { voidFn, getErrorMessage, LIST_FETCHING_LIMIT } from 'utils/Utils'
|
||||
import { useGetPaginationInfo } from 'hooks/useGetPaginationInfo'
|
||||
import { useStrings } from 'framework/strings'
|
||||
import { RepositoryPageHeader } from 'components/RepositoryPageHeader/RepositoryPageHeader'
|
||||
import { ResourceListingPagination } from 'components/ResourceListingPagination/ResourceListingPagination'
|
||||
import { BranchTagSelect } from 'components/BranchTagSelect/BranchTagSelect'
|
||||
import { CommitsView } from '../../components/CommitsView/CommitsView'
|
||||
import { CommitsView } from 'components/CommitsView/CommitsView'
|
||||
import css from './RepositoryCommits.module.scss'
|
||||
|
||||
export default function RepositoryCommits() {
|
||||
@ -19,7 +19,7 @@ export default function RepositoryCommits() {
|
||||
const { routes } = useAppContext()
|
||||
const history = useHistory()
|
||||
const { getString } = useStrings()
|
||||
const [pageIndex, setPageIndex] = usePageIndex()
|
||||
const [page, setPage] = usePageIndex()
|
||||
const {
|
||||
data: commits,
|
||||
response,
|
||||
@ -29,12 +29,11 @@ export default function RepositoryCommits() {
|
||||
path: `/api/v1/repos/${repoMetadata?.path}/+/commits`,
|
||||
queryParams: {
|
||||
limit: LIST_FETCHING_LIMIT,
|
||||
page: pageIndex + 1,
|
||||
page,
|
||||
git_ref: commitRef || repoMetadata?.default_branch
|
||||
},
|
||||
lazy: !repoMetadata
|
||||
})
|
||||
const { totalItems, totalPages, pageSize } = useGetPaginationInfo(response)
|
||||
|
||||
return (
|
||||
<Container className={css.main}>
|
||||
@ -58,7 +57,7 @@ export default function RepositoryCommits() {
|
||||
disableViewAllBranches
|
||||
gitRef={commitRef || (repoMetadata.default_branch as string)}
|
||||
onSelect={ref => {
|
||||
setPageIndex(0)
|
||||
setPage(1)
|
||||
history.push(
|
||||
routes.toCODECommits({
|
||||
repoPath: repoMetadata.path as string,
|
||||
@ -73,17 +72,7 @@ export default function RepositoryCommits() {
|
||||
|
||||
<CommitsView commits={commits} repoMetadata={repoMetadata} />
|
||||
|
||||
<Container margin={{ left: 'large', right: 'large' }}>
|
||||
<Pagination
|
||||
className={css.pagination}
|
||||
hidePageNumbers
|
||||
gotoPage={index => setPageIndex(index)}
|
||||
itemCount={totalItems}
|
||||
pageCount={totalPages}
|
||||
pageIndex={pageIndex}
|
||||
pageSize={pageSize}
|
||||
/>
|
||||
</Container>
|
||||
<ResourceListingPagination response={response} page={page} setPage={setPage} />
|
||||
</Container>
|
||||
)) ||
|
||||
null}
|
||||
|
@ -13,4 +13,8 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.noData > div {
|
||||
height: calc(100vh - var(--page-header-height, 64px) - 120px) !important;
|
||||
}
|
||||
}
|
||||
|
@ -5,5 +5,6 @@ declare const styles: {
|
||||
readonly table: string
|
||||
readonly row: string
|
||||
readonly title: string
|
||||
readonly noData: string
|
||||
}
|
||||
export default styles
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React, { useMemo } from 'react'
|
||||
import React, { useMemo, useState } from 'react'
|
||||
import {
|
||||
Button,
|
||||
Container,
|
||||
@ -11,7 +11,8 @@ import {
|
||||
Icon,
|
||||
Utils,
|
||||
useToaster,
|
||||
IconName
|
||||
IconName,
|
||||
NoDataCard
|
||||
} from '@harness/uicore'
|
||||
import { useHistory } from 'react-router-dom'
|
||||
import { useGet, useMutate } from 'restful-react'
|
||||
@ -26,6 +27,7 @@ import emptyStateImage from 'images/empty-state.svg'
|
||||
import { OptionsMenuButton } from 'components/OptionsMenuButton/OptionsMenuButton'
|
||||
import { useConfirmAct } from 'hooks/useConfirmAction'
|
||||
import { usePageIndex } from 'hooks/usePageIndex'
|
||||
import { ResourceListingPagination } from 'components/ResourceListingPagination/ResourceListingPagination'
|
||||
import type { OpenapiWebhookType } from 'services/code'
|
||||
import { WebhooksHeader } from './WebhooksHeader/WebhooksHeader'
|
||||
import css from './Webhooks.module.scss'
|
||||
@ -34,20 +36,23 @@ export default function Webhooks() {
|
||||
const { getString } = useStrings()
|
||||
const history = useHistory()
|
||||
const { routes } = useAppContext()
|
||||
const [pageIndex, setPageIndex] = usePageIndex()
|
||||
const [page, setPage] = usePageIndex()
|
||||
const [searchTerm, setSearchTerm] = useState('')
|
||||
const { repoMetadata, error, loading, refetch } = useGetRepositoryMetadata()
|
||||
const {
|
||||
data: webhooks,
|
||||
loading: webhooksLoading,
|
||||
error: webhooksError,
|
||||
refetch: refetchWebhooks
|
||||
refetch: refetchWebhooks,
|
||||
response
|
||||
} = useGet<OpenapiWebhookType[]>({
|
||||
path: `/api/v1/repos/${repoMetadata?.path}/+/webhooks`,
|
||||
queryParams: {
|
||||
limit: LIST_FETCHING_LIMIT,
|
||||
page: pageIndex + 1,
|
||||
page,
|
||||
sort: 'date',
|
||||
order: 'asc'
|
||||
order: 'desc',
|
||||
query: searchTerm
|
||||
},
|
||||
lazy: !repoMetadata
|
||||
})
|
||||
@ -115,7 +120,7 @@ export default function Webhooks() {
|
||||
deleteWebhook({})
|
||||
.then(() => {
|
||||
showSuccess(getString('webhookDeleted'), 5000)
|
||||
setPageIndex(0)
|
||||
setPage(1)
|
||||
refetchWebhooks()
|
||||
})
|
||||
.catch(exception => {
|
||||
@ -132,53 +137,68 @@ export default function Webhooks() {
|
||||
}
|
||||
}
|
||||
],
|
||||
[history, getString, refetchWebhooks, repoMetadata?.path, routes, setPageIndex]
|
||||
[history, getString, refetchWebhooks, repoMetadata?.path, routes, setPage]
|
||||
)
|
||||
|
||||
return (
|
||||
<Container className={css.main}>
|
||||
<RepositoryPageHeader repoMetadata={repoMetadata} title={getString('webhooks')} dataTooltipId="webhooks" />
|
||||
<PageBody
|
||||
loading={loading || webhooksLoading}
|
||||
error={getErrorMessage(error || webhooksError)}
|
||||
retryOnError={voidFn(refetch)}
|
||||
noData={{
|
||||
// TODO: Use NoDataCard, this won't render toolbar
|
||||
// when search returns empty response
|
||||
when: () => webhooks?.length === 0,
|
||||
message: getString('noWebHooks'),
|
||||
image: emptyStateImage,
|
||||
button: (
|
||||
<Button
|
||||
variation={ButtonVariation.PRIMARY}
|
||||
text={getString('createWebhook')}
|
||||
icon={CodeIcon.Add}
|
||||
onClick={() => history.push(routes.toCODEWebhookNew({ repoPath: repoMetadata?.path as string }))}
|
||||
/>
|
||||
)
|
||||
}}>
|
||||
<PageBody loading={loading} error={getErrorMessage(error || webhooksError)} retryOnError={voidFn(refetch)}>
|
||||
{repoMetadata && (
|
||||
<Layout.Vertical>
|
||||
<WebhooksHeader repoMetadata={repoMetadata} />
|
||||
{!!webhooks?.length && (
|
||||
<Container padding="xlarge">
|
||||
<TableV2<OpenapiWebhookType>
|
||||
className={css.table}
|
||||
hideHeaders
|
||||
columns={columns}
|
||||
data={webhooks}
|
||||
getRowClassName={() => css.row}
|
||||
onRowClick={row => {
|
||||
history.push(
|
||||
routes.toCODEWebhookDetails({
|
||||
repoPath: repoMetadata.path as string,
|
||||
webhookId: String(row.id)
|
||||
})
|
||||
)
|
||||
}}
|
||||
/>
|
||||
</Container>
|
||||
)}
|
||||
<WebhooksHeader
|
||||
repoMetadata={repoMetadata}
|
||||
loading={webhooksLoading}
|
||||
onSearchTermChanged={value => {
|
||||
setSearchTerm(value)
|
||||
setPage(1)
|
||||
}}
|
||||
/>
|
||||
<Container padding="xlarge">
|
||||
{!!webhooks?.length && (
|
||||
<>
|
||||
<TableV2<OpenapiWebhookType>
|
||||
className={css.table}
|
||||
hideHeaders
|
||||
columns={columns}
|
||||
data={webhooks}
|
||||
getRowClassName={() => css.row}
|
||||
onRowClick={row => {
|
||||
history.push(
|
||||
routes.toCODEWebhookDetails({
|
||||
repoPath: repoMetadata.path as string,
|
||||
webhookId: String(row.id)
|
||||
})
|
||||
)
|
||||
}}
|
||||
/>
|
||||
|
||||
<ResourceListingPagination response={response} page={page} setPage={setPage} />
|
||||
</>
|
||||
)}
|
||||
{webhooks?.length === 0 && (
|
||||
<Container className={css.noData}>
|
||||
<NoDataCard
|
||||
image={emptyStateImage}
|
||||
message={getString('webhookEmpty')}
|
||||
button={
|
||||
<Button
|
||||
variation={ButtonVariation.PRIMARY}
|
||||
text={getString('createWebhook')}
|
||||
icon={CodeIcon.Add}
|
||||
onClick={() => {
|
||||
history.push(
|
||||
routes.toCODEWebhookNew({
|
||||
repoPath: repoMetadata?.path as string
|
||||
})
|
||||
)
|
||||
}}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</Container>
|
||||
)}
|
||||
</Container>
|
||||
</Layout.Vertical>
|
||||
)}
|
||||
</PageBody>
|
||||
|
@ -1,4 +1,6 @@
|
||||
.main {
|
||||
padding-bottom: 0 !important;
|
||||
|
||||
div[class*='TextInput'] {
|
||||
margin-bottom: 0 !important;
|
||||
margin-left: 0 !important;
|
||||
@ -8,7 +10,14 @@
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
padding-bottom: 0 !important;
|
||||
.input {
|
||||
margin-bottom: 0 !important;
|
||||
|
||||
span[data-icon],
|
||||
span[icon] {
|
||||
margin-top: 10px !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.branchDropdown {
|
||||
|
@ -2,6 +2,7 @@
|
||||
// this is an auto-generated file
|
||||
declare const styles: {
|
||||
readonly main: string
|
||||
readonly input: string
|
||||
readonly branchDropdown: string
|
||||
}
|
||||
export default styles
|
||||
|
@ -1,13 +1,20 @@
|
||||
import { useHistory } from 'react-router-dom'
|
||||
import React from 'react'
|
||||
import React, { useState } from 'react'
|
||||
import { Container, Layout, FlexExpander, ButtonVariation, Button } from '@harness/uicore'
|
||||
import { useStrings } from 'framework/strings'
|
||||
import { CodeIcon, GitInfoProps } from 'utils/GitUtils'
|
||||
import { useAppContext } from 'AppContext'
|
||||
import { SearchInputWithSpinner } from 'components/SearchInputWithSpinner/SearchInputWithSpinner'
|
||||
import css from './WebhooksHeader.module.scss'
|
||||
|
||||
export function WebhooksHeader({ repoMetadata }: Pick<GitInfoProps, 'repoMetadata'>) {
|
||||
interface WebhooksHeaderProps extends Pick<GitInfoProps, 'repoMetadata'> {
|
||||
loading?: boolean
|
||||
onSearchTermChanged: (searchTerm: string) => void
|
||||
}
|
||||
|
||||
export function WebhooksHeader({ repoMetadata, loading, onSearchTermChanged }: WebhooksHeaderProps) {
|
||||
const history = useHistory()
|
||||
const [searchTerm, setSearchTerm] = useState('')
|
||||
const { routes } = useAppContext()
|
||||
const { getString } = useStrings()
|
||||
|
||||
@ -15,6 +22,14 @@ export function WebhooksHeader({ repoMetadata }: Pick<GitInfoProps, 'repoMetadat
|
||||
<Container className={css.main} padding="xlarge">
|
||||
<Layout.Horizontal spacing="medium">
|
||||
<FlexExpander />
|
||||
<SearchInputWithSpinner
|
||||
loading={loading}
|
||||
query={searchTerm}
|
||||
setQuery={value => {
|
||||
setSearchTerm(value)
|
||||
onSearchTermChanged(value)
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
variation={ButtonVariation.PRIMARY}
|
||||
text={getString('createWebhook')}
|
||||
|
@ -86,7 +86,7 @@ export const CodeIcon = {
|
||||
Repo: 'code-repo' as IconName,
|
||||
Settings: 'code-settings' as IconName,
|
||||
Webhook: 'code-webhook' as IconName,
|
||||
InputSpinner: 'steps-spinner' as IconName,
|
||||
InputSpinner: 'spinner' as IconName,
|
||||
InputSearch: 'search' as IconName,
|
||||
Chat: 'code-chat' as IconName
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user