mirror of
https://github.com/harness/drone.git
synced 2025-05-10 00:29:29 +08:00
[CODE-1144] UI : Added author filter on PR page (#855)
This commit is contained in:
parent
0a4d370676
commit
d3fe654e23
@ -56,19 +56,18 @@ export default function PullRequests() {
|
|||||||
UserPreference.PULL_REQUESTS_FILTER_SELECTED_OPTIONS,
|
UserPreference.PULL_REQUESTS_FILTER_SELECTED_OPTIONS,
|
||||||
PullRequestFilterOption.OPEN
|
PullRequestFilterOption.OPEN
|
||||||
)
|
)
|
||||||
|
const [authorFilter, setAuthorFilter] = useState<string>()
|
||||||
const space = useGetSpaceParam()
|
const space = useGetSpaceParam()
|
||||||
const { updateQueryParams } = useUpdateQueryParams()
|
const { updateQueryParams } = useUpdateQueryParams()
|
||||||
|
|
||||||
const pageBrowser = useQueryParams<PageBrowserProps>()
|
const pageBrowser = useQueryParams<PageBrowserProps>()
|
||||||
const pageInit = pageBrowser.page ? parseInt(pageBrowser.page) : 1
|
const pageInit = pageBrowser.page ? parseInt(pageBrowser.page) : 1
|
||||||
const [page, setPage] = usePageIndex(pageInit)
|
const [page, setPage] = usePageIndex(pageInit)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (page > 1) {
|
if (page > 1) {
|
||||||
updateQueryParams({ page: page.toString() })
|
updateQueryParams({ page: page.toString() })
|
||||||
}
|
}
|
||||||
}, [setPage]) // eslint-disable-line react-hooks/exhaustive-deps
|
}, [setPage]) // eslint-disable-line react-hooks/exhaustive-deps
|
||||||
|
|
||||||
const { repoMetadata, error, loading, refetch } = useGetRepositoryMetadata()
|
const { repoMetadata, error, loading, refetch } = useGetRepositoryMetadata()
|
||||||
const {
|
const {
|
||||||
data,
|
data,
|
||||||
@ -84,7 +83,8 @@ export default function PullRequests() {
|
|||||||
sort: filter == PullRequestFilterOption.MERGED ? 'merged' : 'number',
|
sort: filter == PullRequestFilterOption.MERGED ? 'merged' : 'number',
|
||||||
order: 'desc',
|
order: 'desc',
|
||||||
query: searchTerm,
|
query: searchTerm,
|
||||||
state: filter == PullRequestFilterOption.ALL ? '' : filter
|
state: filter == PullRequestFilterOption.ALL ? '' : filter,
|
||||||
|
...(authorFilter && { created_by: Number(authorFilter) })
|
||||||
},
|
},
|
||||||
debounce: 500,
|
debounce: 500,
|
||||||
lazy: !repoMetadata
|
lazy: !repoMetadata
|
||||||
@ -234,6 +234,11 @@ export default function PullRequests() {
|
|||||||
setSearchTerm(value)
|
setSearchTerm(value)
|
||||||
setPage(1)
|
setPage(1)
|
||||||
}}
|
}}
|
||||||
|
activePullRequestAuthorFilterOption={authorFilter}
|
||||||
|
onPullRequestAuthorFilterChanged={_authorFilter => {
|
||||||
|
setAuthorFilter(_authorFilter)
|
||||||
|
setPage(1)
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
<Container padding="xlarge">
|
<Container padding="xlarge">
|
||||||
<Match expr={data?.length}>
|
<Match expr={data?.length}>
|
||||||
|
@ -16,11 +16,14 @@
|
|||||||
|
|
||||||
import { useHistory } from 'react-router-dom'
|
import { useHistory } from 'react-router-dom'
|
||||||
import React, { useMemo, useState } from 'react'
|
import React, { useMemo, useState } from 'react'
|
||||||
import { Container, Layout, FlexExpander, DropDown, ButtonVariation, Button } from '@harnessio/uicore'
|
import { Container, Layout, FlexExpander, DropDown, ButtonVariation, Button, SelectOption } from '@harnessio/uicore'
|
||||||
|
import { sortBy } from 'lodash-es'
|
||||||
|
import { getConfig, getUsingFetch } from 'services/config'
|
||||||
import { useStrings } from 'framework/strings'
|
import { useStrings } from 'framework/strings'
|
||||||
import { CodeIcon, GitInfoProps, makeDiffRefs, PullRequestFilterOption } from 'utils/GitUtils'
|
import { CodeIcon, GitInfoProps, makeDiffRefs, PullRequestFilterOption } from 'utils/GitUtils'
|
||||||
import { UserPreference, useUserPreference } from 'hooks/useUserPreference'
|
import { UserPreference, useUserPreference } from 'hooks/useUserPreference'
|
||||||
import { useGetSpaceParam } from 'hooks/useGetSpaceParam'
|
import { useGetSpaceParam } from 'hooks/useGetSpaceParam'
|
||||||
|
import type { TypesPrincipalInfo } from 'services/code'
|
||||||
import { useAppContext } from 'AppContext'
|
import { useAppContext } from 'AppContext'
|
||||||
import { SearchInputWithSpinner } from 'components/SearchInputWithSpinner/SearchInputWithSpinner'
|
import { SearchInputWithSpinner } from 'components/SearchInputWithSpinner/SearchInputWithSpinner'
|
||||||
import { permissionProps } from 'utils/Utils'
|
import { permissionProps } from 'utils/Utils'
|
||||||
@ -29,15 +32,19 @@ import css from './PullRequestsContentHeader.module.scss'
|
|||||||
interface PullRequestsContentHeaderProps extends Pick<GitInfoProps, 'repoMetadata'> {
|
interface PullRequestsContentHeaderProps extends Pick<GitInfoProps, 'repoMetadata'> {
|
||||||
loading?: boolean
|
loading?: boolean
|
||||||
activePullRequestFilterOption?: string
|
activePullRequestFilterOption?: string
|
||||||
|
activePullRequestAuthorFilterOption?: string
|
||||||
onPullRequestFilterChanged: (filter: string) => void
|
onPullRequestFilterChanged: (filter: string) => void
|
||||||
|
onPullRequestAuthorFilterChanged: (authorFilter: string) => void
|
||||||
onSearchTermChanged: (searchTerm: string) => void
|
onSearchTermChanged: (searchTerm: string) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export function PullRequestsContentHeader({
|
export function PullRequestsContentHeader({
|
||||||
loading,
|
loading,
|
||||||
onPullRequestFilterChanged,
|
onPullRequestFilterChanged,
|
||||||
|
onPullRequestAuthorFilterChanged,
|
||||||
onSearchTermChanged,
|
onSearchTermChanged,
|
||||||
activePullRequestFilterOption = PullRequestFilterOption.OPEN,
|
activePullRequestFilterOption = PullRequestFilterOption.OPEN,
|
||||||
|
activePullRequestAuthorFilterOption,
|
||||||
repoMetadata
|
repoMetadata
|
||||||
}: PullRequestsContentHeaderProps) {
|
}: PullRequestsContentHeaderProps) {
|
||||||
const history = useHistory()
|
const history = useHistory()
|
||||||
@ -47,10 +54,13 @@ export function PullRequestsContentHeader({
|
|||||||
UserPreference.PULL_REQUESTS_FILTER_SELECTED_OPTIONS,
|
UserPreference.PULL_REQUESTS_FILTER_SELECTED_OPTIONS,
|
||||||
activePullRequestFilterOption
|
activePullRequestFilterOption
|
||||||
)
|
)
|
||||||
const [searchTerm, setSearchTerm] = useState('')
|
|
||||||
const space = useGetSpaceParam()
|
|
||||||
|
|
||||||
const { standalone } = useAppContext()
|
const [authorFilterOption, setAuthorFilterOption] = useState(activePullRequestAuthorFilterOption)
|
||||||
|
const [searchTerm, setSearchTerm] = useState('')
|
||||||
|
const [query, setQuery] = useState<string>('')
|
||||||
|
const [loadingAuthors, setLoadingAuthors] = useState<boolean>(false)
|
||||||
|
const space = useGetSpaceParam()
|
||||||
|
const { standalone, routingId } = useAppContext()
|
||||||
const { hooks } = useAppContext()
|
const { hooks } = useAppContext()
|
||||||
const permPushResult = hooks?.usePermissionTranslate?.(
|
const permPushResult = hooks?.usePermissionTranslate?.(
|
||||||
{
|
{
|
||||||
@ -73,6 +83,40 @@ export function PullRequestsContentHeader({
|
|||||||
[getString]
|
[getString]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const getAuthorsPromise = (): Promise<SelectOption[]> => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
setLoadingAuthors(true)
|
||||||
|
try {
|
||||||
|
getUsingFetch(getConfig('code/api/v1'), `/principals`, {
|
||||||
|
queryParams: {
|
||||||
|
query: query?.trim(),
|
||||||
|
type: 'user',
|
||||||
|
accountIdentifier: routingId
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then((obj: TypesPrincipalInfo[]) => {
|
||||||
|
const updatedAuthorsList = Array.isArray(obj)
|
||||||
|
? ([
|
||||||
|
...(obj || []).map(item => ({
|
||||||
|
label: String(item?.display_name),
|
||||||
|
value: String(item?.id)
|
||||||
|
}))
|
||||||
|
] as SelectOption[])
|
||||||
|
: ([] as SelectOption[])
|
||||||
|
setLoadingAuthors(false)
|
||||||
|
resolve(sortBy(updatedAuthorsList, item => item.label.toLowerCase()))
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
setLoadingAuthors(false)
|
||||||
|
reject(error)
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
setLoadingAuthors(false)
|
||||||
|
reject(error)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container className={css.main} padding="xlarge">
|
<Container className={css.main} padding="xlarge">
|
||||||
<Layout.Horizontal spacing="medium">
|
<Layout.Horizontal spacing="medium">
|
||||||
@ -86,6 +130,27 @@ export function PullRequestsContentHeader({
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<FlexExpander />
|
<FlexExpander />
|
||||||
|
<DropDown
|
||||||
|
value={authorFilterOption}
|
||||||
|
items={() => getAuthorsPromise()}
|
||||||
|
disabled={loadingAuthors}
|
||||||
|
onChange={({ value, label }) => {
|
||||||
|
setAuthorFilterOption(label as string)
|
||||||
|
onPullRequestAuthorFilterChanged(value as string)
|
||||||
|
}}
|
||||||
|
popoverClassName={css.branchDropdown}
|
||||||
|
icon="nav-user-profile"
|
||||||
|
iconProps={{ size: 16 }}
|
||||||
|
placeholder="Select Authors"
|
||||||
|
addClearBtn={true}
|
||||||
|
resetOnClose
|
||||||
|
resetOnSelect
|
||||||
|
resetOnQuery
|
||||||
|
query={query}
|
||||||
|
onQueryChange={newQuery => {
|
||||||
|
setQuery(newQuery)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
<DropDown
|
<DropDown
|
||||||
value={filterOption}
|
value={filterOption}
|
||||||
items={items}
|
items={items}
|
||||||
|
@ -14,6 +14,8 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import qs, { IStringifyOptions } from 'qs'
|
||||||
|
|
||||||
export const getConfig = (str: string): string => {
|
export const getConfig = (str: string): string => {
|
||||||
// 'code/api/v1' -> 'api/v1' (standalone)
|
// 'code/api/v1' -> 'api/v1' (standalone)
|
||||||
// -> 'code/api/v1' (embedded inside Harness platform)
|
// -> 'code/api/v1' (embedded inside Harness platform)
|
||||||
@ -23,3 +25,66 @@ export const getConfig = (str: string): string => {
|
|||||||
|
|
||||||
return window.apiUrl ? `${window.apiUrl}/${str}` : `${window.harnessNameSpace || ''}/${str}`
|
return window.apiUrl ? `${window.apiUrl}/${str}` : `${window.harnessNameSpace || ''}/${str}`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface GetUsingFetchProps<
|
||||||
|
_TData = any,
|
||||||
|
_TError = any,
|
||||||
|
TQueryParams = {
|
||||||
|
[key: string]: any
|
||||||
|
},
|
||||||
|
TPathParams = {
|
||||||
|
[key: string]: any
|
||||||
|
}
|
||||||
|
> {
|
||||||
|
queryParams?: TQueryParams
|
||||||
|
queryParamStringifyOptions?: IStringifyOptions
|
||||||
|
pathParams?: TPathParams
|
||||||
|
requestOptions?: RequestInit
|
||||||
|
mock?: _TData
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getUsingFetch = <
|
||||||
|
TData = any,
|
||||||
|
_TError = any,
|
||||||
|
TQueryParams = {
|
||||||
|
[key: string]: any
|
||||||
|
},
|
||||||
|
TPathParams = {
|
||||||
|
[key: string]: any
|
||||||
|
}
|
||||||
|
>(
|
||||||
|
base: string,
|
||||||
|
path: string,
|
||||||
|
props: GetUsingFetchProps<TData, _TError, TQueryParams, TPathParams>,
|
||||||
|
signal?: RequestInit['signal']
|
||||||
|
): Promise<TData> => {
|
||||||
|
if (props.mock) return Promise.resolve(props.mock)
|
||||||
|
let url = base + path
|
||||||
|
if (props.queryParams && Object.keys(props.queryParams).length) {
|
||||||
|
url += `?${qs.stringify(props.queryParams, props.queryParamStringifyOptions)}`
|
||||||
|
}
|
||||||
|
return fetch(url, {
|
||||||
|
signal,
|
||||||
|
...(props.requestOptions || {})
|
||||||
|
// headers: getHeaders(props.requestOptions?.headers)
|
||||||
|
}).then(res => {
|
||||||
|
// custom event to allow the app framework to handle api responses
|
||||||
|
const responseEvent = new CustomEvent('PROMISE_API_RESPONSE', { detail: { response: res } })
|
||||||
|
window.dispatchEvent(responseEvent) // this will be captured in App.tsx to handle 401 and token refresh
|
||||||
|
|
||||||
|
const contentType = res.headers.get('content-type') || ''
|
||||||
|
|
||||||
|
if (contentType.toLowerCase().indexOf('application/json') > -1) {
|
||||||
|
if (res.status === 401) {
|
||||||
|
return res.json().then(json => Promise.reject(json))
|
||||||
|
}
|
||||||
|
return res.json()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (res.status === 401) {
|
||||||
|
return res.text().then(text => Promise.reject(text))
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.text()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user