mirror of https://github.com/harness/drone.git
fix: public repo config and labels flicker (#2571)
* fix lint * label fixes and minor ui fix * udpate: label values api to use query params * fix: public repo config and labels flickerpull/3545/head
parent
e18a8c410f
commit
81b10ba01c
|
@ -72,6 +72,7 @@ export interface AppProps {
|
||||||
useLogsStreaming: Unknown
|
useLogsStreaming: Unknown
|
||||||
useFeatureFlags: Unknown
|
useFeatureFlags: Unknown
|
||||||
useGetSettingValue: Unknown
|
useGetSettingValue: Unknown
|
||||||
|
useGetAuthSettings: Unknown
|
||||||
useGetUserSourceCodeManagers?: Unknown
|
useGetUserSourceCodeManagers?: Unknown
|
||||||
useListAggregatedTokens?: Unknown
|
useListAggregatedTokens?: Unknown
|
||||||
useDeleteToken?: Unknown
|
useDeleteToken?: Unknown
|
||||||
|
|
|
@ -21,6 +21,7 @@ import { routes } from 'RouteDefinitions'
|
||||||
import { defaultCurrentUser } from 'AppContext'
|
import { defaultCurrentUser } from 'AppContext'
|
||||||
import { useFeatureFlags } from 'hooks/useFeatureFlag'
|
import { useFeatureFlags } from 'hooks/useFeatureFlag'
|
||||||
import { useGetSettingValue } from 'hooks/useGetSettingValue'
|
import { useGetSettingValue } from 'hooks/useGetSettingValue'
|
||||||
|
import { useGetAuthSettings } from 'hooks/useGetAuthSettings'
|
||||||
import { defaultUsefulOrNot } from 'components/DefaultUsefulOrNot/UsefulOrNot'
|
import { defaultUsefulOrNot } from 'components/DefaultUsefulOrNot/UsefulOrNot'
|
||||||
import App from './App'
|
import App from './App'
|
||||||
import './bootstrap.scss'
|
import './bootstrap.scss'
|
||||||
|
@ -41,7 +42,8 @@ ReactDOM.render(
|
||||||
useLogsContent: noop,
|
useLogsContent: noop,
|
||||||
useLogsStreaming: noop,
|
useLogsStreaming: noop,
|
||||||
useFeatureFlags,
|
useFeatureFlags,
|
||||||
useGetSettingValue
|
useGetSettingValue,
|
||||||
|
useGetAuthSettings
|
||||||
}}
|
}}
|
||||||
currentUser={defaultCurrentUser}
|
currentUser={defaultCurrentUser}
|
||||||
customComponents={{
|
customComponents={{
|
||||||
|
|
|
@ -93,6 +93,6 @@
|
||||||
}
|
}
|
||||||
.valuesList {
|
.valuesList {
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
gap: 7px;
|
gap: 8px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@ import cx from 'classnames'
|
||||||
import { Button, ButtonSize, ButtonVariation, Container, Layout, Tag, Text } from '@harnessio/uicore'
|
import { Button, ButtonSize, ButtonVariation, Container, Layout, Tag, Text } from '@harnessio/uicore'
|
||||||
import { FontVariation } from '@harnessio/design-system'
|
import { FontVariation } from '@harnessio/design-system'
|
||||||
import { useGet } from 'restful-react'
|
import { useGet } from 'restful-react'
|
||||||
import { Menu } from '@blueprintjs/core'
|
import { Menu, Spinner } from '@blueprintjs/core'
|
||||||
import { Icon } from '@harnessio/icons'
|
import { Icon } from '@harnessio/icons'
|
||||||
import { isEmpty } from 'lodash-es'
|
import { isEmpty } from 'lodash-es'
|
||||||
import { ColorName, LabelType, getColorsObj, getScopeData, getScopeIcon } from 'utils/Utils'
|
import { ColorName, LabelType, getColorsObj, getScopeData, getScopeIcon } from 'utils/Utils'
|
||||||
|
@ -329,7 +329,59 @@ export const LabelValuesList: React.FC<{
|
||||||
/>
|
/>
|
||||||
))
|
))
|
||||||
) : (
|
) : (
|
||||||
<Icon name="steps-spinner" size={16} />
|
<Spinner size={16} />
|
||||||
|
)}
|
||||||
|
</Layout.Horizontal>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToDo : Remove LabelValuesListQuery component when Encoding is handled by BE for Harness
|
||||||
|
export const LabelValuesListQuery: React.FC<{
|
||||||
|
name: string
|
||||||
|
scope: number
|
||||||
|
repoMetadata?: RepoRepositoryOutput
|
||||||
|
space?: string
|
||||||
|
standalone: boolean
|
||||||
|
}> = ({ name, scope, repoMetadata, space = '', standalone }) => {
|
||||||
|
const { scopeRef } = getScopeData(space as string, scope, standalone)
|
||||||
|
|
||||||
|
const getPath = () =>
|
||||||
|
scope === 0
|
||||||
|
? `/repos/${repoMetadata?.identifier}/labels/${encodeURIComponent(name)}/values`
|
||||||
|
: `/labels/${encodeURIComponent(name)}/values`
|
||||||
|
|
||||||
|
const {
|
||||||
|
data: labelValues,
|
||||||
|
refetch: refetchLabelValues,
|
||||||
|
loading: loadingLabelValues
|
||||||
|
} = useGet<TypesLabelValue[]>({
|
||||||
|
base: getConfig('code/api/v1'),
|
||||||
|
path: getPath(),
|
||||||
|
queryParams: {
|
||||||
|
accountIdentifier: scopeRef?.split('/')[0],
|
||||||
|
orgIdentifier: scopeRef?.split('/')[1],
|
||||||
|
projectIdentifier: scopeRef?.split('/')[2]
|
||||||
|
},
|
||||||
|
lazy: true
|
||||||
|
})
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
refetchLabelValues()
|
||||||
|
}, [name, scope, space, repoMetadata])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Layout.Horizontal className={css.valuesList}>
|
||||||
|
{!loadingLabelValues && labelValues && !isEmpty(labelValues) ? (
|
||||||
|
labelValues.map(value => (
|
||||||
|
<Label
|
||||||
|
key={`${name}-${value.value}`}
|
||||||
|
name={name}
|
||||||
|
scope={scope}
|
||||||
|
label_value={{ name: value.value, color: value.color as ColorName }}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<Spinner size={16} />
|
||||||
)}
|
)}
|
||||||
</Layout.Horizontal>
|
</Layout.Horizontal>
|
||||||
)
|
)
|
||||||
|
|
|
@ -129,14 +129,13 @@ export const LabelFilter = (props: LabelFilterProps) => {
|
||||||
const getLabelValuesPromise = async (key: string, scope: number): Promise<SelectOption[]> => {
|
const getLabelValuesPromise = async (key: string, scope: number): Promise<SelectOption[]> => {
|
||||||
setLoadingLabelValues(true)
|
setLoadingLabelValues(true)
|
||||||
const { scopeRef } = getScopeData(spaceRef, scope, standalone)
|
const { scopeRef } = getScopeData(spaceRef, scope, standalone)
|
||||||
if (scope === 0) {
|
const getPath = () =>
|
||||||
|
scope === 0
|
||||||
|
? `/repos/${encodeURIComponent(repoMetadata?.path as string)}/labels/${encodeURIComponent(key)}/values`
|
||||||
|
: `/spaces/${encodeURIComponent(scopeRef)}/labels/${encodeURIComponent(key)}/values`
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const fetchedValues: TypesLabelValue[] = await getUsingFetch(
|
const fetchedValues: TypesLabelValue[] = await getUsingFetch(getConfig('code/api/v1'), getPath(), bearerToken, {})
|
||||||
getConfig('code/api/v1'),
|
|
||||||
`/repos/${encodeURIComponent(repoMetadata?.path as string)}/labels/${encodeURIComponent(key)}/values`,
|
|
||||||
bearerToken,
|
|
||||||
{}
|
|
||||||
)
|
|
||||||
const updatedValuesList = mapToSelectOptions(fetchedValues)
|
const updatedValuesList = mapToSelectOptions(fetchedValues)
|
||||||
setLoadingLabelValues(false)
|
setLoadingLabelValues(false)
|
||||||
return updatedValuesList
|
return updatedValuesList
|
||||||
|
@ -145,22 +144,26 @@ export const LabelFilter = (props: LabelFilterProps) => {
|
||||||
showError(getErrorMessage(error))
|
showError(getErrorMessage(error))
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
|
|
||||||
|
// ToDo : Remove getLabelValuesPromiseQuery component when Encoding is handled by BE for Harness
|
||||||
|
const getLabelValuesPromiseQuery = async (key: string, scope: number): Promise<SelectOption[]> => {
|
||||||
|
setLoadingLabelValues(true)
|
||||||
|
const { scopeRef } = getScopeData(spaceRef, scope, standalone)
|
||||||
|
const getPath = () =>
|
||||||
|
scope === 0
|
||||||
|
? `/repos/${repoMetadata?.identifier}/labels/${encodeURIComponent(key)}/values`
|
||||||
|
: `/labels/${encodeURIComponent(key)}/values`
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const fetchedValues: TypesLabelValue[] = await getUsingFetch(
|
const fetchedValues: TypesLabelValue[] = await getUsingFetch(getConfig('code/api/v1'), getPath(), bearerToken, {
|
||||||
getConfig('code/api/v1'),
|
queryParams: {
|
||||||
`/spaces/${encodeURIComponent(scopeRef)}/labels/${encodeURIComponent(key)}/values`,
|
accountIdentifier: scopeRef?.split('/')[0],
|
||||||
bearerToken,
|
orgIdentifier: scopeRef?.split('/')[1],
|
||||||
{}
|
projectIdentifier: scopeRef?.split('/')[2]
|
||||||
)
|
}
|
||||||
const updatedValuesList = Array.isArray(fetchedValues)
|
})
|
||||||
? ([
|
const updatedValuesList = mapToSelectOptions(fetchedValues)
|
||||||
...(fetchedValues || []).map(item => ({
|
|
||||||
label: JSON.stringify(item),
|
|
||||||
value: String(item?.id)
|
|
||||||
}))
|
|
||||||
] as SelectOption[])
|
|
||||||
: ([] as SelectOption[])
|
|
||||||
setLoadingLabelValues(false)
|
setLoadingLabelValues(false)
|
||||||
return updatedValuesList
|
return updatedValuesList
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
@ -169,7 +172,6 @@ export const LabelFilter = (props: LabelFilterProps) => {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
const containsFilter = (filterObjArr: LabelFilterObj[], currentObj: any, type: utilFilterType) => {
|
const containsFilter = (filterObjArr: LabelFilterObj[], currentObj: any, type: utilFilterType) => {
|
||||||
let res = false
|
let res = false
|
||||||
|
@ -320,11 +322,20 @@ export const LabelFilter = (props: LabelFilterProps) => {
|
||||||
setIsVisible(true)
|
setIsVisible(true)
|
||||||
setValueQuery('')
|
setValueQuery('')
|
||||||
setHighlightItem(item.label as string)
|
setHighlightItem(item.label as string)
|
||||||
|
// ToDo : Remove this check once BE has support for encoding
|
||||||
|
if (standalone) {
|
||||||
getLabelValuesPromise(itemObj.key, itemObj.scope)
|
getLabelValuesPromise(itemObj.key, itemObj.scope)
|
||||||
.then(res => setLabelValues(res))
|
.then(res => setLabelValues(res))
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
showError(getErrorMessage(err))
|
showError(getErrorMessage(err))
|
||||||
})
|
})
|
||||||
|
} else {
|
||||||
|
getLabelValuesPromiseQuery(itemObj.key, itemObj.scope)
|
||||||
|
.then(res => setLabelValues(res))
|
||||||
|
.catch(err => {
|
||||||
|
showError(getErrorMessage(err))
|
||||||
|
})
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
tooltip={
|
tooltip={
|
||||||
labelsValueList && !loadingLabelValues ? (
|
labelsValueList && !loadingLabelValues ? (
|
||||||
|
|
|
@ -56,6 +56,7 @@ export interface LabelSelectorProps {
|
||||||
setQuery: React.Dispatch<React.SetStateAction<string>>
|
setQuery: React.Dispatch<React.SetStateAction<string>>
|
||||||
query: string
|
query: string
|
||||||
labelListLoading: boolean
|
labelListLoading: boolean
|
||||||
|
refetchActivities: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface LabelSelectProps extends Omit<ButtonProps, 'onSelect'> {
|
export interface LabelSelectProps extends Omit<ButtonProps, 'onSelect'> {
|
||||||
|
@ -87,6 +88,7 @@ export const LabelSelector: React.FC<LabelSelectorProps> = ({
|
||||||
query,
|
query,
|
||||||
setQuery,
|
setQuery,
|
||||||
labelListLoading,
|
labelListLoading,
|
||||||
|
refetchActivities,
|
||||||
...props
|
...props
|
||||||
}) => {
|
}) => {
|
||||||
const [popoverDialogOpen, setPopoverDialogOpen] = useState<boolean>(false)
|
const [popoverDialogOpen, setPopoverDialogOpen] = useState<boolean>(false)
|
||||||
|
@ -110,7 +112,7 @@ export const LabelSelector: React.FC<LabelSelectorProps> = ({
|
||||||
resourceType: 'CODE_REPOSITORY',
|
resourceType: 'CODE_REPOSITORY',
|
||||||
resourceIdentifier: repoMetadata?.identifier as string
|
resourceIdentifier: repoMetadata?.identifier as string
|
||||||
},
|
},
|
||||||
permissions: ['code_repo_edit']
|
permissions: ['code_repo_push']
|
||||||
},
|
},
|
||||||
[space]
|
[space]
|
||||||
)
|
)
|
||||||
|
@ -121,6 +123,7 @@ export const LabelSelector: React.FC<LabelSelectorProps> = ({
|
||||||
text={<span className={css.prefix}>{getString('add')}</span>}
|
text={<span className={css.prefix}>{getString('add')}</span>}
|
||||||
variation={ButtonVariation.TERTIARY}
|
variation={ButtonVariation.TERTIARY}
|
||||||
minimal
|
minimal
|
||||||
|
disabled={labelListLoading}
|
||||||
size={ButtonSize.SMALL}
|
size={ButtonSize.SMALL}
|
||||||
tooltip={
|
tooltip={
|
||||||
<PopoverContent
|
<PopoverContent
|
||||||
|
@ -139,6 +142,7 @@ export const LabelSelector: React.FC<LabelSelectorProps> = ({
|
||||||
setQuery('')
|
setQuery('')
|
||||||
setPopoverDialogOpen(false)
|
setPopoverDialogOpen(false)
|
||||||
showSuccess(`Applied '${label.key}' label`)
|
showSuccess(`Applied '${label.key}' label`)
|
||||||
|
refetchActivities()
|
||||||
})
|
})
|
||||||
.catch(error => showError(getErrorMessage(error)))
|
.catch(error => showError(getErrorMessage(error)))
|
||||||
} catch (exception) {
|
} catch (exception) {
|
||||||
|
@ -161,6 +165,7 @@ export const LabelSelector: React.FC<LabelSelectorProps> = ({
|
||||||
setCurrentLabel({ key: '', id: -1 })
|
setCurrentLabel({ key: '', id: -1 })
|
||||||
setPopoverDialogOpen(false)
|
setPopoverDialogOpen(false)
|
||||||
showSuccess(`Applied '${labelKey}:${valueKey}' label`)
|
showSuccess(`Applied '${labelKey}:${valueKey}' label`)
|
||||||
|
refetchActivities()
|
||||||
})
|
})
|
||||||
.catch(error => showError(getErrorMessage(error)))
|
.catch(error => showError(getErrorMessage(error)))
|
||||||
} catch (exception) {
|
} catch (exception) {
|
||||||
|
@ -185,6 +190,7 @@ export const LabelSelector: React.FC<LabelSelectorProps> = ({
|
||||||
setCurrentLabel({ key: '', id: -1 })
|
setCurrentLabel({ key: '', id: -1 })
|
||||||
setPopoverDialogOpen(false)
|
setPopoverDialogOpen(false)
|
||||||
setQuery('')
|
setQuery('')
|
||||||
|
refetchActivities()
|
||||||
})
|
})
|
||||||
.catch(error => showError(getErrorMessage(error)))
|
.catch(error => showError(getErrorMessage(error)))
|
||||||
} catch (exception) {
|
} catch (exception) {
|
||||||
|
@ -266,60 +272,62 @@ const PopoverContent: React.FC<LabelSelectProps> = ({
|
||||||
}
|
}
|
||||||
}, [menuItemIndex, menuState, labelsList, labelsValueList])
|
}, [menuItemIndex, menuState, labelsList, labelsValueList])
|
||||||
|
|
||||||
const handleKeyDownLabels: React.KeyboardEventHandler<HTMLInputElement> = e => {
|
// const handleKeyDownLabels: React.KeyboardEventHandler<HTMLInputElement> = e => {
|
||||||
if (labelsList && labelsList.length !== 0) {
|
// if (labelsList && labelsList.length !== 0) {
|
||||||
switch (e.key) {
|
// e.preventDefault()
|
||||||
case 'ArrowDown':
|
// switch (e.key) {
|
||||||
setMenuItemIndex((index: number) => {
|
// case 'ArrowDown':
|
||||||
return index + 1 > labelsList.length ? 1 : index + 1
|
// setMenuItemIndex((index: number) => {
|
||||||
})
|
// return index + 1 > labelsList.length ? 1 : index + 1
|
||||||
break
|
// })
|
||||||
case 'ArrowUp':
|
// break
|
||||||
setMenuItemIndex((index: number) => {
|
// case 'ArrowUp':
|
||||||
return index - 1 > 0 ? index - 1 : labelsList.length
|
// setMenuItemIndex((index: number) => {
|
||||||
})
|
// return index - 1 > 0 ? index - 1 : labelsList.length
|
||||||
break
|
// })
|
||||||
case 'Enter':
|
// break
|
||||||
if (labelsList[menuItemIndex - 1]) {
|
// case 'Enter':
|
||||||
onSelectLabel(labelsList[menuItemIndex - 1])
|
// if (labelsList[menuItemIndex - 1]) {
|
||||||
setQuery('')
|
// onSelectLabel(labelsList[menuItemIndex - 1])
|
||||||
}
|
// setQuery('')
|
||||||
break
|
// }
|
||||||
default:
|
// break
|
||||||
break
|
// default:
|
||||||
}
|
// break
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
const handleKeyDownValue: React.KeyboardEventHandler<HTMLInputElement> = e => {
|
const handleKeyDownValue: React.KeyboardEventHandler<HTMLInputElement> = e => {
|
||||||
if (e.key === 'Backspace' && !query && currentLabel) {
|
if (e.key === 'Backspace' && !query && currentLabel) {
|
||||||
setQuery('')
|
setQuery('')
|
||||||
handleValueRemove && handleValueRemove()
|
handleValueRemove && handleValueRemove()
|
||||||
} else if (labelsValueList && labelsValueList.length !== 0) {
|
|
||||||
switch (e.key) {
|
|
||||||
case 'ArrowDown':
|
|
||||||
setMenuItemIndex((index: number) => {
|
|
||||||
return index + 1 > labelsValueList.length ? 1 : index + 1
|
|
||||||
})
|
|
||||||
break
|
|
||||||
case 'ArrowUp':
|
|
||||||
setMenuItemIndex((index: number) => {
|
|
||||||
return index - 1 > 0 ? index - 1 : labelsValueList.length
|
|
||||||
})
|
|
||||||
break
|
|
||||||
case 'Enter':
|
|
||||||
onSelectValue(
|
|
||||||
currentLabel.id ?? -1,
|
|
||||||
labelsValueList[menuItemIndex - 1].id ?? -1,
|
|
||||||
currentLabel.key ?? '',
|
|
||||||
labelsValueList[menuItemIndex - 1].value ?? ''
|
|
||||||
)
|
|
||||||
setQuery('')
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
// } else if (labelsValueList && labelsValueList.length !== 0) {
|
||||||
|
// switch (e.key) {
|
||||||
|
// case 'ArrowDown':
|
||||||
|
// setMenuItemIndex((index: number) => {
|
||||||
|
// return index + 1 > labelsValueList.length ? 1 : index + 1
|
||||||
|
// })
|
||||||
|
// break
|
||||||
|
// case 'ArrowUp':
|
||||||
|
// setMenuItemIndex((index: number) => {
|
||||||
|
// return index - 1 > 0 ? index - 1 : labelsValueList.length
|
||||||
|
// })
|
||||||
|
// break
|
||||||
|
// case 'Enter':
|
||||||
|
// onSelectValue(
|
||||||
|
// currentLabel.id ?? -1,
|
||||||
|
// labelsValueList[menuItemIndex - 1].id ?? -1,
|
||||||
|
// currentLabel.key ?? '',
|
||||||
|
// labelsValueList[menuItemIndex - 1].value ?? ''
|
||||||
|
// )
|
||||||
|
// setQuery('')
|
||||||
|
// break
|
||||||
|
// default:
|
||||||
|
// break
|
||||||
|
// }
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -343,7 +351,7 @@ const PopoverContent: React.FC<LabelSelectProps> = ({
|
||||||
className: css.closeBtn,
|
className: css.closeBtn,
|
||||||
size: 20
|
size: 20
|
||||||
}}
|
}}
|
||||||
onKeyDown={handleKeyDownLabels}
|
// onKeyDown={handleKeyDownLabels}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
currentLabel &&
|
currentLabel &&
|
||||||
|
|
|
@ -76,6 +76,7 @@ import type {
|
||||||
} from 'services/code'
|
} from 'services/code'
|
||||||
import { useAppContext } from 'AppContext'
|
import { useAppContext } from 'AppContext'
|
||||||
import type { TypesRepository } from 'cde-gitness/services'
|
import type { TypesRepository } from 'cde-gitness/services'
|
||||||
|
import { usePublicResourceConfig } from 'hooks/usePublicResourceConfig'
|
||||||
import ImportForm from './ImportForm/ImportForm'
|
import ImportForm from './ImportForm/ImportForm'
|
||||||
import ImportReposForm from './ImportReposForm/ImportReposForm'
|
import ImportReposForm from './ImportReposForm/ImportReposForm'
|
||||||
import Private from '../../icons/private.svg?url'
|
import Private from '../../icons/private.svg?url'
|
||||||
|
@ -112,7 +113,8 @@ 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 [branchName, setBranchName] = useState(DEFAULT_BRANCH_NAME)
|
||||||
const [enablePublicRepo, setEnablePublicRepo] = useState(false)
|
const { allowPublicResourceCreation, configLoading, systemConfigError, errorWhileFetchingAuthSettings } =
|
||||||
|
usePublicResourceConfig()
|
||||||
const { showError } = useToaster()
|
const { showError } = useToaster()
|
||||||
|
|
||||||
const { mutate: createRepo, loading: submitLoading } = useMutate<RepoRepositoryOutput>({
|
const { mutate: createRepo, loading: submitLoading } = useMutate<RepoRepositoryOutput>({
|
||||||
|
@ -147,31 +149,19 @@ export const NewRepoModalButton: React.FC<NewRepoModalButtonProps> = ({
|
||||||
loading: licenseLoading,
|
loading: licenseLoading,
|
||||||
error: licenseError
|
error: licenseError
|
||||||
} = useGet({ path: '/api/v1/resources/license' })
|
} = useGet({ path: '/api/v1/resources/license' })
|
||||||
const {
|
|
||||||
data: systemConfig,
|
|
||||||
loading: systemConfigLoading,
|
|
||||||
error: systemConfigError
|
|
||||||
} = useGet({ path: 'api/v1/system/config' })
|
|
||||||
|
|
||||||
const loading =
|
const loading =
|
||||||
submitLoading ||
|
submitLoading || gitIgnoreLoading || licenseLoading || importRepoLoading || submitImportLoading || configLoading
|
||||||
gitIgnoreLoading ||
|
|
||||||
licenseLoading ||
|
|
||||||
importRepoLoading ||
|
|
||||||
submitImportLoading ||
|
|
||||||
systemConfigLoading
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (gitIgnoreError || licenseError || systemConfigError) {
|
if (gitIgnoreError || licenseError || systemConfigError || errorWhileFetchingAuthSettings) {
|
||||||
showError(getErrorMessage(gitIgnoreError || licenseError || systemConfigError), 0)
|
showError(
|
||||||
|
getErrorMessage(gitIgnoreError || licenseError || systemConfigError || errorWhileFetchingAuthSettings),
|
||||||
|
0
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}, [gitIgnoreError, licenseError, systemConfigError, showError])
|
}, [gitIgnoreError, licenseError, systemConfigError, errorWhileFetchingAuthSettings, showError])
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (systemConfig) {
|
|
||||||
setEnablePublicRepo(systemConfig.public_resource_creation_enabled)
|
|
||||||
}
|
|
||||||
}, [systemConfig])
|
|
||||||
const handleSubmit = (formData: RepoFormData) => {
|
const handleSubmit = (formData: RepoFormData) => {
|
||||||
try {
|
try {
|
||||||
const payload: OpenapiCreateRepositoryRequest = {
|
const payload: OpenapiCreateRepositoryRequest = {
|
||||||
|
@ -354,7 +344,7 @@ export const NewRepoModalButton: React.FC<NewRepoModalButtonProps> = ({
|
||||||
{getString('createRepoModal.branch')}
|
{getString('createRepoModal.branch')}
|
||||||
</Text>
|
</Text>
|
||||||
</Container>
|
</Container>
|
||||||
<Render when={enablePublicRepo}>
|
<Render when={allowPublicResourceCreation}>
|
||||||
<hr className={css.dividerContainer} />
|
<hr className={css.dividerContainer} />
|
||||||
<Container>
|
<Container>
|
||||||
<FormInput.RadioGroup
|
<FormInput.RadioGroup
|
||||||
|
|
|
@ -651,8 +651,11 @@ export interface StringsMap {
|
||||||
'labels.createdIn': string
|
'labels.createdIn': string
|
||||||
'labels.deleteLabel': string
|
'labels.deleteLabel': string
|
||||||
'labels.deleteLabelConfirm': string
|
'labels.deleteLabelConfirm': string
|
||||||
|
'labels.deletedLabel': string
|
||||||
'labels.descriptionOptional': string
|
'labels.descriptionOptional': string
|
||||||
'labels.failedToDeleteLabel': string
|
'labels.failedToDeleteLabel': string
|
||||||
|
'labels.failedtoFetchLabels': string
|
||||||
|
'labels.failedtoFetchValues': string
|
||||||
'labels.filterByLabels': string
|
'labels.filterByLabels': string
|
||||||
'labels.findALabel': string
|
'labels.findALabel': string
|
||||||
'labels.findOrAdd': string
|
'labels.findOrAdd': string
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2023 Harness, Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export function useGetAuthSettings<T = Record<string, boolean>>() {
|
||||||
|
return {} as T
|
||||||
|
}
|
|
@ -0,0 +1,51 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2023 Harness, Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { useEffect, useMemo } from 'react'
|
||||||
|
import { useGet } from 'restful-react'
|
||||||
|
import { useAppContext } from 'AppContext'
|
||||||
|
|
||||||
|
export function usePublicResourceConfig() {
|
||||||
|
const { standalone, hooks } = useAppContext()
|
||||||
|
const { refetchAuthSettings, authSettings, fetchingAuthSettings, errorWhileFetchingAuthSettings } =
|
||||||
|
hooks.useGetAuthSettings()
|
||||||
|
const {
|
||||||
|
data: systemConfig,
|
||||||
|
loading: systemConfigLoading,
|
||||||
|
error: systemConfigError
|
||||||
|
} = useGet({ path: 'api/v1/system/config' })
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!standalone) refetchAuthSettings()
|
||||||
|
}, [refetchAuthSettings])
|
||||||
|
|
||||||
|
const allowPublicResourceCreation = useMemo(() => {
|
||||||
|
if (systemConfigLoading || fetchingAuthSettings) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if (standalone) {
|
||||||
|
return systemConfig?.public_resource_creation_enabled
|
||||||
|
}
|
||||||
|
return !!(systemConfig?.public_resource_creation_enabled && authSettings?.resource?.publicAccessEnabled)
|
||||||
|
}, [authSettings, systemConfig, standalone, systemConfigLoading, fetchingAuthSettings])
|
||||||
|
|
||||||
|
return {
|
||||||
|
allowPublicResourceCreation,
|
||||||
|
configLoading: fetchingAuthSettings || systemConfigLoading,
|
||||||
|
systemConfigError,
|
||||||
|
errorWhileFetchingAuthSettings
|
||||||
|
}
|
||||||
|
}
|
|
@ -128,9 +128,9 @@ repos:
|
||||||
createRepoModal:
|
createRepoModal:
|
||||||
branchLabel: 'Your repository will be initialized with a '
|
branchLabel: 'Your repository will be initialized with a '
|
||||||
branch: ' branch.'
|
branch: ' branch.'
|
||||||
publicLabel: Anyone with access to the Gitness environment can clone this repo.
|
publicLabel: Anyone who can view the repository can clone it.
|
||||||
privateLabel: You choose who can see and commit to this repository.
|
privateLabel: You choose who can see and commit to this repository.
|
||||||
publicWarning: Please note that anyone with access to the Gitness environment can clone this repo.
|
publicWarning: Please note that anyone who can view the repository can clone it.
|
||||||
validation:
|
validation:
|
||||||
repoNamePatternIsNotValid: Registry name should be 1~255 characters long with lower case characters, numbers and ._- and must be start with numbers or characters
|
repoNamePatternIsNotValid: Registry name should be 1~255 characters long with lower case characters, numbers and ._- and must be start with numbers or characters
|
||||||
gitBranchNameInvalid: Branch name is invalid.
|
gitBranchNameInvalid: Branch name is invalid.
|
||||||
|
@ -1315,6 +1315,7 @@ labels:
|
||||||
addValue: 'Add value'
|
addValue: 'Add value'
|
||||||
removeLabel: Remove Label
|
removeLabel: Remove Label
|
||||||
deleteLabel: Delete Label
|
deleteLabel: Delete Label
|
||||||
|
deletedLabel: 'Deleted Label : {tag}'
|
||||||
provideLabelName: Provide Label name
|
provideLabelName: Provide Label name
|
||||||
labelCreated: Label created
|
labelCreated: Label created
|
||||||
labelUpdated: Label updated
|
labelUpdated: Label updated
|
||||||
|
@ -1347,6 +1348,8 @@ labels:
|
||||||
deleteLabelConfirm: Are you sure you want to delete label <strong>{{name}}</strong>? You can't undo this action.
|
deleteLabelConfirm: Are you sure you want to delete label <strong>{{name}}</strong>? You can't undo this action.
|
||||||
intentText: Editing/deleting a label or its values will impact all the areas it has been used.
|
intentText: Editing/deleting a label or its values will impact all the areas it has been used.
|
||||||
prCount: 'Showing {count} {count|1:result,results}'
|
prCount: 'Showing {count} {count|1:result,results}'
|
||||||
|
failedtoFetchLabels: Failed to fetch labels
|
||||||
|
failedtoFetchValues: Failed to fetch label values
|
||||||
noResults: No results found
|
noResults: No results found
|
||||||
labelPreview: Label Preview
|
labelPreview: Label Preview
|
||||||
filterByLabels: Filter by Label/s
|
filterByLabels: Filter by Label/s
|
||||||
|
|
|
@ -33,7 +33,7 @@ import {
|
||||||
} from '@harnessio/uicore'
|
} from '@harnessio/uicore'
|
||||||
import { Icon } from '@harnessio/icons'
|
import { Icon } from '@harnessio/icons'
|
||||||
import { Color, FontVariation } from '@harnessio/design-system'
|
import { Color, FontVariation } from '@harnessio/design-system'
|
||||||
import { Menu, MenuItem, PopoverInteractionKind, Position } from '@blueprintjs/core'
|
import { Menu, MenuItem, PopoverInteractionKind, Position, Spinner } from '@blueprintjs/core'
|
||||||
import * as Yup from 'yup'
|
import * as Yup from 'yup'
|
||||||
import { FieldArray } from 'formik'
|
import { FieldArray } from 'formik'
|
||||||
import { useGet, useMutate } from 'restful-react'
|
import { useGet, useMutate } from 'restful-react'
|
||||||
|
@ -181,30 +181,51 @@ const useLabelModal = ({ refetchlabelsList }: LabelModalProps) => {
|
||||||
path: updateLabel?.scope ? `/spaces/${scopeRef as string}/+/labels` : `/spaces/${space as string}/+/labels`
|
path: updateLabel?.scope ? `/spaces/${scopeRef as string}/+/labels` : `/spaces/${space as string}/+/labels`
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const getPath = () =>
|
||||||
|
updateLabel?.scope === 0 && repoMetadata
|
||||||
|
? `/repos/${encodeURIComponent(repoMetadata?.path as string)}/labels/${encodeURIComponent(
|
||||||
|
updateLabel?.key ? updateLabel?.key : ''
|
||||||
|
)}/values`
|
||||||
|
: `/spaces/${encodeURIComponent(scopeRef)}/labels/${encodeURIComponent(
|
||||||
|
updateLabel?.key ? updateLabel?.key : ''
|
||||||
|
)}/values`
|
||||||
|
|
||||||
const {
|
const {
|
||||||
data: repoLabelValues,
|
data: initLabelValues,
|
||||||
loading: repoValueListLoading,
|
loading: initValueListLoading,
|
||||||
refetch: refetchRepoValuesList
|
refetch: refetchInitValuesList
|
||||||
} = useGet<TypesLabelValue[]>({
|
} = useGet<TypesLabelValue[]>({
|
||||||
base: getConfig('code/api/v1'),
|
base: getConfig('code/api/v1'),
|
||||||
path: `/repos/${encodeURIComponent(repoMetadata?.path as string)}/labels/${encodeURIComponent(
|
path: getPath(),
|
||||||
updateLabel?.key ? updateLabel?.key : ''
|
|
||||||
)}/values`,
|
|
||||||
lazy: true
|
lazy: true
|
||||||
})
|
})
|
||||||
|
|
||||||
|
//ToDo : Remove getLabelValuesPromiseQuery component when Encoding is handled by BE for Harness
|
||||||
|
|
||||||
|
const getPathHarness = () =>
|
||||||
|
updateLabel?.scope === 0 && repoMetadata
|
||||||
|
? `/repos/${repoMetadata?.identifier}/labels/${encodeURIComponent(
|
||||||
|
updateLabel?.key ? updateLabel?.key : ''
|
||||||
|
)}/values`
|
||||||
|
: `/labels/${encodeURIComponent(updateLabel?.key ? updateLabel?.key : '')}/values`
|
||||||
|
|
||||||
const {
|
const {
|
||||||
data: spaceLabelValues,
|
data: initLabelValuesQuery,
|
||||||
loading: spaceValueListLoading,
|
loading: initValueListLoadingQuery,
|
||||||
refetch: refetchSpaceValuesList
|
refetch: refetchInitValuesListQuery
|
||||||
} = useGet<TypesLabelValue[]>({
|
} = useGet<TypesLabelValue[]>({
|
||||||
base: getConfig('code/api/v1'),
|
base: getConfig('code/api/v1'),
|
||||||
path: `/spaces/${encodeURIComponent(scopeRef)}/labels/${encodeURIComponent(
|
path: getPathHarness(),
|
||||||
updateLabel?.key ? updateLabel?.key : ''
|
queryParams: {
|
||||||
)}/values`,
|
accountIdentifier: scopeRef?.split('/')[0],
|
||||||
|
orgIdentifier: scopeRef?.split('/')[1],
|
||||||
|
projectIdentifier: scopeRef?.split('/')[2]
|
||||||
|
},
|
||||||
lazy: true
|
lazy: true
|
||||||
})
|
})
|
||||||
|
|
||||||
|
//ToDo: Remove all references of suffix with Query when Encoding is handled by BE for Harness
|
||||||
|
|
||||||
const [openModal, hideModal] = useModalHook(() => {
|
const [openModal, hideModal] = useModalHook(() => {
|
||||||
const handleLabelSubmit = (formData: LabelFormData) => {
|
const handleLabelSubmit = (formData: LabelFormData) => {
|
||||||
const { labelName, color, labelValues, description, allowDynamicValues, id } = formData
|
const { labelName, color, labelValues, description, allowDynamicValues, id } = formData
|
||||||
|
@ -329,20 +350,20 @@ const useLabelModal = ({ refetchlabelsList }: LabelModalProps) => {
|
||||||
return { ...baseValues, color: ColorName.Blue }
|
return { ...baseValues, color: ColorName.Blue }
|
||||||
}
|
}
|
||||||
|
|
||||||
if (repoMetadata && updateLabel?.scope === 0) {
|
if (modalMode === ModalMode.UPDATE && updateLabel?.value_count === 0) return baseValues
|
||||||
return { ...baseValues, labelValues: getLabelValues(repoLabelValues ?? undefined) }
|
|
||||||
}
|
|
||||||
|
|
||||||
return { ...baseValues, labelValues: getLabelValues(spaceLabelValues ?? undefined) }
|
if (standalone) {
|
||||||
|
return { ...baseValues, labelValues: getLabelValues(initLabelValues ?? undefined) }
|
||||||
|
}
|
||||||
|
return { ...baseValues, labelValues: getLabelValues(initLabelValuesQuery ?? undefined) }
|
||||||
})()
|
})()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog
|
<Dialog
|
||||||
isOpen
|
isOpen
|
||||||
onOpening={() => {
|
onOpening={() => {
|
||||||
if (modalMode === ModalMode.UPDATE) {
|
if (modalMode === ModalMode.UPDATE && updateLabel?.value_count !== 0) {
|
||||||
if (repoMetadata && updateLabel?.scope === 0) refetchRepoValuesList()
|
standalone ? refetchInitValuesList() : refetchInitValuesListQuery()
|
||||||
else refetchSpaceValuesList()
|
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
enforceFocus={false}
|
enforceFocus={false}
|
||||||
|
@ -408,6 +429,13 @@ const useLabelModal = ({ refetchlabelsList }: LabelModalProps) => {
|
||||||
<FieldArray
|
<FieldArray
|
||||||
name="labelValues"
|
name="labelValues"
|
||||||
render={({ push, remove }) => {
|
render={({ push, remove }) => {
|
||||||
|
if (
|
||||||
|
modalMode === ModalMode.UPDATE &&
|
||||||
|
updateLabel?.value_count !== 0 &&
|
||||||
|
(initValueListLoading || initValueListLoadingQuery)
|
||||||
|
)
|
||||||
|
return <Spinner size={20} />
|
||||||
|
else
|
||||||
return (
|
return (
|
||||||
<Layout.Vertical>
|
<Layout.Vertical>
|
||||||
{formik.values.labelValues?.map((_, index) => (
|
{formik.values.labelValues?.map((_, index) => (
|
||||||
|
@ -498,6 +526,11 @@ const useLabelModal = ({ refetchlabelsList }: LabelModalProps) => {
|
||||||
<Layout.Vertical
|
<Layout.Vertical
|
||||||
style={{ width: '45%', padding: '25px 35px 25px 35px', borderLeft: '1px solid var(--grey-100)' }}>
|
style={{ width: '45%', padding: '25px 35px 25px 35px', borderLeft: '1px solid var(--grey-100)' }}>
|
||||||
<Text>{getString('labels.labelPreview')}</Text>
|
<Text>{getString('labels.labelPreview')}</Text>
|
||||||
|
{modalMode === ModalMode.UPDATE &&
|
||||||
|
updateLabel?.value_count !== 0 &&
|
||||||
|
(initValueListLoading || initValueListLoadingQuery) ? (
|
||||||
|
<Spinner size={20} />
|
||||||
|
) : (
|
||||||
<Layout.Vertical spacing={'medium'}>
|
<Layout.Vertical spacing={'medium'}>
|
||||||
{formik.values.labelValues?.length
|
{formik.values.labelValues?.length
|
||||||
? formik.values.labelValues?.map((valueObj, i) => (
|
? formik.values.labelValues?.map((valueObj, i) => (
|
||||||
|
@ -529,6 +562,7 @@ const useLabelModal = ({ refetchlabelsList }: LabelModalProps) => {
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Layout.Vertical>
|
</Layout.Vertical>
|
||||||
|
)}
|
||||||
</Layout.Vertical>
|
</Layout.Vertical>
|
||||||
</Layout.Horizontal>
|
</Layout.Horizontal>
|
||||||
</FormikForm>
|
</FormikForm>
|
||||||
|
@ -539,11 +573,12 @@ const useLabelModal = ({ refetchlabelsList }: LabelModalProps) => {
|
||||||
)
|
)
|
||||||
}, [
|
}, [
|
||||||
updateLabel,
|
updateLabel,
|
||||||
repoLabelValues,
|
initLabelValues,
|
||||||
spaceLabelValues,
|
initLabelValuesQuery,
|
||||||
repoValueListLoading,
|
initValueListLoading,
|
||||||
spaceValueListLoading,
|
initValueListLoadingQuery,
|
||||||
refetchlabelsList,
|
refetchlabelsList,
|
||||||
|
refetchInitValuesListQuery,
|
||||||
modalMode
|
modalMode
|
||||||
])
|
])
|
||||||
|
|
||||||
|
|
|
@ -16,16 +16,7 @@
|
||||||
|
|
||||||
.main {
|
.main {
|
||||||
background-color: var(--primary-bg) !important;
|
background-color: var(--primary-bg) !important;
|
||||||
.table {
|
|
||||||
.row {
|
|
||||||
height: 80px;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
.title {
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.noData > div {
|
.noData > div {
|
||||||
height: calc(100vh - var(--page-header-height, 64px) - 120px) !important;
|
height: calc(100vh - var(--page-header-height, 64px) - 120px) !important;
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,10 +19,7 @@
|
||||||
export declare const cancelButton: string
|
export declare const cancelButton: string
|
||||||
export declare const main: string
|
export declare const main: string
|
||||||
export declare const noData: string
|
export declare const noData: string
|
||||||
export declare const row: string
|
|
||||||
export declare const scopeCheckbox: string
|
export declare const scopeCheckbox: string
|
||||||
export declare const table: string
|
|
||||||
export declare const title: string
|
|
||||||
export declare const toggle: string
|
export declare const toggle: string
|
||||||
export declare const toggleDisable: string
|
export declare const toggleDisable: string
|
||||||
export declare const toggleEnable: string
|
export declare const toggleEnable: string
|
||||||
|
|
|
@ -18,9 +18,8 @@ import { Container, Layout, FlexExpander, ButtonVariation, Button, Checkbox } fr
|
||||||
import { Render } from 'react-jsx-match'
|
import { Render } from 'react-jsx-match'
|
||||||
import { useStrings } from 'framework/strings'
|
import { useStrings } from 'framework/strings'
|
||||||
import { CodeIcon } from 'utils/GitUtils'
|
import { CodeIcon } from 'utils/GitUtils'
|
||||||
import { useAppContext } from 'AppContext'
|
|
||||||
import { SearchInputWithSpinner } from 'components/SearchInputWithSpinner/SearchInputWithSpinner'
|
import { SearchInputWithSpinner } from 'components/SearchInputWithSpinner/SearchInputWithSpinner'
|
||||||
import { LabelsPageScope, permissionProps } from 'utils/Utils'
|
import type { LabelsPageScope } from 'utils/Utils'
|
||||||
import type { RepoRepositoryOutput } from 'services/code'
|
import type { RepoRepositoryOutput } from 'services/code'
|
||||||
import css from './LabelsHeader.module.scss'
|
import css from './LabelsHeader.module.scss'
|
||||||
|
|
||||||
|
@ -30,28 +29,10 @@ const LabelsHeader = ({
|
||||||
showParentScopeFilter,
|
showParentScopeFilter,
|
||||||
inheritLabels,
|
inheritLabels,
|
||||||
setInheritLabels,
|
setInheritLabels,
|
||||||
openLabelCreateModal,
|
openLabelCreateModal
|
||||||
spaceRef,
|
|
||||||
repoMetadata,
|
|
||||||
currentPageScope
|
|
||||||
}: LabelsHeaderProps) => {
|
}: LabelsHeaderProps) => {
|
||||||
const [searchTerm, setSearchTerm] = useState('')
|
const [searchTerm, setSearchTerm] = useState('')
|
||||||
const { getString } = useStrings()
|
const { getString } = useStrings()
|
||||||
const { hooks, standalone } = useAppContext()
|
|
||||||
|
|
||||||
const permPushResult = hooks?.usePermissionTranslate?.(
|
|
||||||
{
|
|
||||||
resource: {
|
|
||||||
resourceType: 'CODE_REPOSITORY',
|
|
||||||
resourceIdentifier:
|
|
||||||
currentPageScope === LabelsPageScope.REPOSITORY && repoMetadata
|
|
||||||
? repoMetadata.identifier
|
|
||||||
: (spaceRef as string)
|
|
||||||
},
|
|
||||||
permissions: ['code_repo_edit']
|
|
||||||
},
|
|
||||||
[spaceRef]
|
|
||||||
)
|
|
||||||
|
|
||||||
//ToDo: check space permissions as well in case of spaces
|
//ToDo: check space permissions as well in case of spaces
|
||||||
|
|
||||||
|
@ -63,7 +44,6 @@ const LabelsHeader = ({
|
||||||
text={getString('labels.newLabel')}
|
text={getString('labels.newLabel')}
|
||||||
icon={CodeIcon.Add}
|
icon={CodeIcon.Add}
|
||||||
onClick={openLabelCreateModal}
|
onClick={openLabelCreateModal}
|
||||||
{...permissionProps(permPushResult, standalone)}
|
|
||||||
/>
|
/>
|
||||||
<Render when={showParentScopeFilter}>
|
<Render when={showParentScopeFilter}>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
|
|
|
@ -29,60 +29,16 @@
|
||||||
max-width: 55%;
|
max-width: 55%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
div[class*='TableV2--row'] {
|
||||||
|
padding: 3px var(--spacing-medium);
|
||||||
|
justify-content: center;
|
||||||
|
min-height: 48px;
|
||||||
|
}
|
||||||
|
|
||||||
div[class*='TableV2--rowSubComponent'] {
|
div[class*='TableV2--rowSubComponent'] {
|
||||||
border-top: 1px solid var(--grey-100);
|
border-top: 1px solid var(--grey-100);
|
||||||
padding: 20px 4px 4px 5%;
|
padding: 14px var(--spacing-xsmall) 14px 4.7%;
|
||||||
}
|
margin-top: var(--spacing-tiny);
|
||||||
}
|
|
||||||
|
|
||||||
.dividerContainer {
|
|
||||||
opacity: 0.2;
|
|
||||||
height: 1px;
|
|
||||||
color: var(--grey-100);
|
|
||||||
margin: 10px 0;
|
|
||||||
width: 95%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.border {
|
|
||||||
border-bottom: 1px solid var(--grey-100) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hideDetailsContainer {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.appliedRulesTextContainer {
|
|
||||||
border-radius: 4px;
|
|
||||||
background: var(--grey-50) !important;
|
|
||||||
font-size: 12px !important;
|
|
||||||
font-weight: 500 !important;
|
|
||||||
padding: var(--spacing-small) !important;
|
|
||||||
margin-bottom: var(--spacing-xsmall) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.popover {
|
|
||||||
z-index: 999;
|
|
||||||
padding: var(--spacing-tiny) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.widthContainer {
|
|
||||||
max-width: calc(100% - 100px);
|
|
||||||
overflow-x: hidden;
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hideButtonIcon {
|
|
||||||
:global {
|
|
||||||
[class*='ConfirmationDialog--header'] {
|
|
||||||
.bp3-icon {
|
|
||||||
display: none !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[class*='ConfirmationDialog--body'] {
|
|
||||||
padding-left: 3px !important;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,16 +16,9 @@
|
||||||
|
|
||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
// This is an auto-generated file
|
// This is an auto-generated file
|
||||||
export declare const appliedRulesTextContainer: string
|
|
||||||
export declare const border: string
|
|
||||||
export declare const dividerContainer: string
|
|
||||||
export declare const hideButtonIcon: string
|
|
||||||
export declare const hideDetailsContainer: string
|
|
||||||
export declare const labelCtn: string
|
export declare const labelCtn: string
|
||||||
export declare const main: string
|
export declare const main: string
|
||||||
export declare const optionItem: string
|
export declare const optionItem: string
|
||||||
export declare const popover: string
|
|
||||||
export declare const row: string
|
export declare const row: string
|
||||||
export declare const table: string
|
export declare const table: string
|
||||||
export declare const toggleAccordion: string
|
export declare const toggleAccordion: string
|
||||||
export declare const widthContainer: string
|
|
||||||
|
|
|
@ -38,7 +38,6 @@ import { usePageIndex } from 'hooks/usePageIndex'
|
||||||
import {
|
import {
|
||||||
getErrorMessage,
|
getErrorMessage,
|
||||||
LIST_FETCHING_LIMIT,
|
LIST_FETCHING_LIMIT,
|
||||||
permissionProps,
|
|
||||||
type PageBrowserProps,
|
type PageBrowserProps,
|
||||||
ColorName,
|
ColorName,
|
||||||
LabelTypes,
|
LabelTypes,
|
||||||
|
@ -54,7 +53,7 @@ import { useConfirmAction } from 'hooks/useConfirmAction'
|
||||||
import { OptionsMenuButton } from 'components/OptionsMenuButton/OptionsMenuButton'
|
import { OptionsMenuButton } from 'components/OptionsMenuButton/OptionsMenuButton'
|
||||||
import { useAppContext } from 'AppContext'
|
import { useAppContext } from 'AppContext'
|
||||||
import { useUpdateQueryParams } from 'hooks/useUpdateQueryParams'
|
import { useUpdateQueryParams } from 'hooks/useUpdateQueryParams'
|
||||||
import { LabelTitle, LabelValuesList } from 'components/Label/Label'
|
import { LabelTitle, LabelValuesList, LabelValuesListQuery } from 'components/Label/Label'
|
||||||
import { LoadingSpinner } from 'components/LoadingSpinner/LoadingSpinner'
|
import { LoadingSpinner } from 'components/LoadingSpinner/LoadingSpinner'
|
||||||
import { getConfig } from 'services/config'
|
import { getConfig } from 'services/config'
|
||||||
import LabelsHeader from './LabelsHeader/LabelsHeader'
|
import LabelsHeader from './LabelsHeader/LabelsHeader'
|
||||||
|
@ -63,7 +62,7 @@ import css from './LabelsListing.module.scss'
|
||||||
|
|
||||||
const LabelsListing = (props: LabelListingProps) => {
|
const LabelsListing = (props: LabelListingProps) => {
|
||||||
const { activeTab, currentPageScope, repoMetadata, space } = props
|
const { activeTab, currentPageScope, repoMetadata, space } = props
|
||||||
const { hooks, standalone } = useAppContext()
|
const { standalone } = useAppContext()
|
||||||
const { getString } = useStrings()
|
const { getString } = useStrings()
|
||||||
const { showError, showSuccess } = useToaster()
|
const { showError, showSuccess } = useToaster()
|
||||||
const history = useHistory()
|
const history = useHistory()
|
||||||
|
@ -133,6 +132,7 @@ const LabelsListing = (props: LabelListingProps) => {
|
||||||
|
|
||||||
const { openModal: openLabelCreateModal, openUpdateLabelModal } = useLabelModal({ refetchlabelsList })
|
const { openModal: openLabelCreateModal, openUpdateLabelModal } = useLabelModal({ refetchlabelsList })
|
||||||
const renderRowSubComponent = React.useCallback(({ row }: { row: Row<LabelTypes> }) => {
|
const renderRowSubComponent = React.useCallback(({ row }: { row: Row<LabelTypes> }) => {
|
||||||
|
if (standalone) {
|
||||||
return (
|
return (
|
||||||
<LabelValuesList
|
<LabelValuesList
|
||||||
name={row.original?.key as string}
|
name={row.original?.key as string}
|
||||||
|
@ -142,6 +142,17 @@ const LabelsListing = (props: LabelListingProps) => {
|
||||||
standalone={standalone}
|
standalone={standalone}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
<LabelValuesListQuery
|
||||||
|
name={row.original?.key as string}
|
||||||
|
scope={row.original?.scope as number}
|
||||||
|
repoMetadata={repoMetadata}
|
||||||
|
space={space}
|
||||||
|
standalone={standalone}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
const ToggleAccordionCell: Renderer<{
|
const ToggleAccordionCell: Renderer<{
|
||||||
|
@ -215,7 +226,7 @@ const LabelsListing = (props: LabelListingProps) => {
|
||||||
width: '40%',
|
width: '40%',
|
||||||
sort: 'true',
|
sort: 'true',
|
||||||
Cell: ({ row }: CellProps<LabelTypes>) => {
|
Cell: ({ row }: CellProps<LabelTypes>) => {
|
||||||
return <Text lineClamp={3}>{row.original?.description}</Text>
|
return <Text lineClamp={1}>{row.original?.description}</Text>
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -235,6 +246,24 @@ const LabelsListing = (props: LabelListingProps) => {
|
||||||
path: deleteLabelPath
|
path: deleteLabelPath
|
||||||
})
|
})
|
||||||
|
|
||||||
|
//ToDo : Remove the following block when Encoding is handled by BE for Harness
|
||||||
|
const deleteLabelPathHarness =
|
||||||
|
row.original?.scope === 0
|
||||||
|
? `/repos/${repoMetadata?.identifier}/labels/${encodedLabelKey}`
|
||||||
|
: `/labels/${encodedLabelKey}`
|
||||||
|
|
||||||
|
const { mutate: deleteLabelQueryCall } = useMutate({
|
||||||
|
verb: 'DELETE',
|
||||||
|
base: getConfig('code/api/v1'),
|
||||||
|
path: deleteLabelPathHarness,
|
||||||
|
queryParams: {
|
||||||
|
accountIdentifier: scopeRef?.split('/')[0],
|
||||||
|
orgIdentifier: scopeRef?.split('/')[1],
|
||||||
|
projectIdentifier: scopeRef?.split('/')[2]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
//ToDo: remove type check of standalone when Encoding is handled by BE for Harness
|
||||||
const confirmLabelDelete = useConfirmAction({
|
const confirmLabelDelete = useConfirmAction({
|
||||||
title: getString('labels.deleteLabel'),
|
title: getString('labels.deleteLabel'),
|
||||||
confirmText: getString('delete'),
|
confirmText: getString('delete'),
|
||||||
|
@ -242,23 +271,19 @@ const LabelsListing = (props: LabelListingProps) => {
|
||||||
message: <String useRichText stringID="labels.deleteLabelConfirm" vars={{ name: row.original.key }} />,
|
message: <String useRichText stringID="labels.deleteLabelConfirm" vars={{ name: row.original.key }} />,
|
||||||
action: async e => {
|
action: async e => {
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
deleteLabel({})
|
const handleSuccess = (tag: string) => {
|
||||||
.then(() => {
|
showSuccess(<StringSubstitute str={getString('labels.deletedLabel')} vars={{ tag }} />, 5000)
|
||||||
showSuccess(
|
|
||||||
<StringSubstitute
|
|
||||||
str={getString('labels.deleteLabel')}
|
|
||||||
vars={{
|
|
||||||
tag: row.original.key
|
|
||||||
}}
|
|
||||||
/>,
|
|
||||||
5000
|
|
||||||
)
|
|
||||||
refetchlabelsList()
|
refetchlabelsList()
|
||||||
setPage(1)
|
setPage(1)
|
||||||
})
|
}
|
||||||
.catch(error => {
|
|
||||||
|
const handleError = (error: any) => {
|
||||||
showError(getErrorMessage(error), 0, getString('labels.failedToDeleteLabel'))
|
showError(getErrorMessage(error), 0, getString('labels.failedToDeleteLabel'))
|
||||||
})
|
}
|
||||||
|
|
||||||
|
const deleteAction = standalone ? deleteLabel({}) : deleteLabelQueryCall({})
|
||||||
|
|
||||||
|
deleteAction.then(() => handleSuccess(row.original.key ?? '')).catch(handleError)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
return (
|
return (
|
||||||
|
@ -296,18 +321,6 @@ const LabelsListing = (props: LabelListingProps) => {
|
||||||
[history, getString, repoMetadata?.path, space, setPage, showError, showSuccess]
|
[history, getString, repoMetadata?.path, space, setPage, showError, showSuccess]
|
||||||
)
|
)
|
||||||
|
|
||||||
const permPushResult = hooks?.usePermissionTranslate?.(
|
|
||||||
{
|
|
||||||
resource: {
|
|
||||||
resourceType: 'CODE_REPOSITORY',
|
|
||||||
resourceIdentifier:
|
|
||||||
currentPageScope === LabelsPageScope.REPOSITORY ? (repoMetadata?.identifier as string) : (space as string)
|
|
||||||
},
|
|
||||||
permissions: ['code_repo_edit']
|
|
||||||
},
|
|
||||||
[space]
|
|
||||||
)
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container>
|
<Container>
|
||||||
<LabelsHeader
|
<LabelsHeader
|
||||||
|
@ -346,7 +359,6 @@ const LabelsListing = (props: LabelListingProps) => {
|
||||||
message={getString('labels.noLabelsFound')}
|
message={getString('labels.noLabelsFound')}
|
||||||
buttonText={getString('labels.newLabel')}
|
buttonText={getString('labels.newLabel')}
|
||||||
onButtonClick={() => openLabelCreateModal()}
|
onButtonClick={() => openLabelCreateModal()}
|
||||||
permissionProp={permissionProps(permPushResult, standalone)}
|
|
||||||
/>
|
/>
|
||||||
</Container>
|
</Container>
|
||||||
)
|
)
|
||||||
|
|
|
@ -51,6 +51,7 @@
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
box-shadow: 0px 0.5px 2px 0px rgba(96, 97, 112, 0.16), 0px 0px 1px 0px rgba(40, 41, 61, 0.08);
|
box-shadow: 0px 0.5px 2px 0px rgba(96, 97, 112, 0.16), 0px 0px 1px 0px rgba(40, 41, 61, 0.08);
|
||||||
background: var(--grey-0);
|
background: var(--grey-0);
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
&.expanded {
|
&.expanded {
|
||||||
.chevron {
|
.chevron {
|
||||||
|
|
|
@ -81,6 +81,8 @@ export interface ConversationProps extends Pick<GitInfoProps, 'repoMetadata' | '
|
||||||
standalone: boolean
|
standalone: boolean
|
||||||
routingId: string
|
routingId: string
|
||||||
pullReqCommits: TypesListCommitResponse | undefined
|
pullReqCommits: TypesListCommitResponse | undefined
|
||||||
|
refetchActivities: () => void
|
||||||
|
refetchPullReq: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Conversation: React.FC<ConversationProps> = ({
|
export const Conversation: React.FC<ConversationProps> = ({
|
||||||
|
@ -93,7 +95,9 @@ export const Conversation: React.FC<ConversationProps> = ({
|
||||||
prChecksDecisionResult,
|
prChecksDecisionResult,
|
||||||
standalone,
|
standalone,
|
||||||
routingId,
|
routingId,
|
||||||
pullReqCommits
|
pullReqCommits,
|
||||||
|
refetchActivities,
|
||||||
|
refetchPullReq
|
||||||
}) => {
|
}) => {
|
||||||
const { getString } = useStrings()
|
const { getString } = useStrings()
|
||||||
const { currentUser, routes, hooks } = useAppContext()
|
const { currentUser, routes, hooks } = useAppContext()
|
||||||
|
@ -442,6 +446,7 @@ export const Conversation: React.FC<ConversationProps> = ({
|
||||||
setActivityFilter={setActivityFilter}
|
setActivityFilter={setActivityFilter}
|
||||||
loadingReviewers={loadingReviewers}
|
loadingReviewers={loadingReviewers}
|
||||||
refetchCodeOwners={refetchCodeOwners}
|
refetchCodeOwners={refetchCodeOwners}
|
||||||
|
refetchPullReq={refetchPullReq}
|
||||||
activities={activities}
|
activities={activities}
|
||||||
/>
|
/>
|
||||||
</Container>
|
</Container>
|
||||||
|
@ -508,6 +513,7 @@ export const Conversation: React.FC<ConversationProps> = ({
|
||||||
refetchReviewers={refetchReviewers}
|
refetchReviewers={refetchReviewers}
|
||||||
labels={labels}
|
labels={labels}
|
||||||
refetchLabels={refetchLabels}
|
refetchLabels={refetchLabels}
|
||||||
|
refetchActivities={refetchActivities}
|
||||||
/>
|
/>
|
||||||
</Layout.Horizontal>
|
</Layout.Horizontal>
|
||||||
</Container>
|
</Container>
|
||||||
|
|
|
@ -35,7 +35,11 @@ import { useAppContext } from 'AppContext'
|
||||||
import type { ConversationProps } from './Conversation'
|
import type { ConversationProps } from './Conversation'
|
||||||
import css from './Conversation.module.scss'
|
import css from './Conversation.module.scss'
|
||||||
|
|
||||||
interface DescriptionBoxProps extends Omit<ConversationProps, 'onCancelEditDescription' | 'pullReqCommits'> {
|
interface DescriptionBoxProps
|
||||||
|
extends Omit<
|
||||||
|
ConversationProps,
|
||||||
|
'onCancelEditDescription' | 'pullReqCommits' | 'refetchActivities' | 'refetchPullReq'
|
||||||
|
> {
|
||||||
onCancelEditDescription: () => void
|
onCancelEditDescription: () => void
|
||||||
pullReqCommits: TypesListCommitResponse | undefined
|
pullReqCommits: TypesListCommitResponse | undefined
|
||||||
}
|
}
|
||||||
|
|
|
@ -65,7 +65,8 @@ export const PullRequestActionsBox: React.FC<PullRequestActionsBoxProps> = ({
|
||||||
allowedStrategy,
|
allowedStrategy,
|
||||||
pullReqCommits,
|
pullReqCommits,
|
||||||
PRStateLoading,
|
PRStateLoading,
|
||||||
setConflictingFiles
|
setConflictingFiles,
|
||||||
|
refetchPullReq
|
||||||
}) => {
|
}) => {
|
||||||
const { getString } = useStrings()
|
const { getString } = useStrings()
|
||||||
const { showError } = useToaster()
|
const { showError } = useToaster()
|
||||||
|
@ -127,7 +128,8 @@ export const PullRequestActionsBox: React.FC<PullRequestActionsBoxProps> = ({
|
||||||
setAllowedStrats,
|
setAllowedStrats,
|
||||||
pullRequestSection,
|
pullRequestSection,
|
||||||
showError,
|
showError,
|
||||||
setConflictingFiles
|
setConflictingFiles,
|
||||||
|
refetchPullReq
|
||||||
) // eslint-disable-next-line react-hooks/exhaustive-deps
|
) // eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [unchecked, pullReqMetadata?.source_sha])
|
}, [unchecked, pullReqMetadata?.source_sha])
|
||||||
const [prMerged, setPrMerged] = useState(false)
|
const [prMerged, setPrMerged] = useState(false)
|
||||||
|
@ -146,7 +148,8 @@ export const PullRequestActionsBox: React.FC<PullRequestActionsBoxProps> = ({
|
||||||
setAllowedStrats,
|
setAllowedStrats,
|
||||||
pullRequestSection,
|
pullRequestSection,
|
||||||
showError,
|
showError,
|
||||||
setConflictingFiles
|
setConflictingFiles,
|
||||||
|
refetchPullReq
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}, POLLING_INTERVAL) // Poll every 20 seconds
|
}, POLLING_INTERVAL) // Poll every 20 seconds
|
||||||
|
@ -154,7 +157,7 @@ export const PullRequestActionsBox: React.FC<PullRequestActionsBoxProps> = ({
|
||||||
return () => {
|
return () => {
|
||||||
clearInterval(intervalId)
|
clearInterval(intervalId)
|
||||||
} // eslint-disable-next-line react-hooks/exhaustive-deps
|
} // eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [onPRStateChanged, prMerged])
|
}, [onPRStateChanged, prMerged, pullReqMetadata?.source_sha])
|
||||||
const isDraft = pullReqMetadata.is_draft
|
const isDraft = pullReqMetadata.is_draft
|
||||||
const mergeOptions: PRMergeOption[] = [
|
const mergeOptions: PRMergeOption[] = [
|
||||||
{
|
{
|
||||||
|
|
|
@ -51,6 +51,7 @@ interface PullRequestOverviewPanelProps {
|
||||||
setActivityFilter: (val: SelectOption) => void
|
setActivityFilter: (val: SelectOption) => void
|
||||||
loadingReviewers: boolean
|
loadingReviewers: boolean
|
||||||
refetchCodeOwners: () => void
|
refetchCodeOwners: () => void
|
||||||
|
refetchPullReq: () => void
|
||||||
activities: TypesPullReqActivity[] | undefined
|
activities: TypesPullReqActivity[] | undefined
|
||||||
pullReqCommits: TypesListCommitResponse | undefined
|
pullReqCommits: TypesListCommitResponse | undefined
|
||||||
}
|
}
|
||||||
|
@ -67,7 +68,8 @@ const PullRequestOverviewPanel = (props: PullRequestOverviewPanelProps) => {
|
||||||
loadingReviewers,
|
loadingReviewers,
|
||||||
refetchCodeOwners,
|
refetchCodeOwners,
|
||||||
activities,
|
activities,
|
||||||
pullReqCommits
|
pullReqCommits,
|
||||||
|
refetchPullReq
|
||||||
} = props
|
} = props
|
||||||
const { getString } = useStrings()
|
const { getString } = useStrings()
|
||||||
const { showError } = useToaster()
|
const { showError } = useToaster()
|
||||||
|
@ -174,6 +176,7 @@ const PullRequestOverviewPanel = (props: PullRequestOverviewPanelProps) => {
|
||||||
pullRequestSection,
|
pullRequestSection,
|
||||||
showError,
|
showError,
|
||||||
setConflictingFiles,
|
setConflictingFiles,
|
||||||
|
refetchPullReq,
|
||||||
setRequiresCommentApproval,
|
setRequiresCommentApproval,
|
||||||
setAtLeastOneReviewerRule,
|
setAtLeastOneReviewerRule,
|
||||||
setReqCodeOwnerApproval,
|
setReqCodeOwnerApproval,
|
||||||
|
@ -197,6 +200,7 @@ const PullRequestOverviewPanel = (props: PullRequestOverviewPanelProps) => {
|
||||||
allowedStrategy={allowedStrats}
|
allowedStrategy={allowedStrats}
|
||||||
pullReqCommits={pullReqCommits}
|
pullReqCommits={pullReqCommits}
|
||||||
PRStateLoading={PRStateLoading || loadingReviewers}
|
PRStateLoading={PRStateLoading || loadingReviewers}
|
||||||
|
refetchPullReq={refetchPullReq}
|
||||||
/>
|
/>
|
||||||
{pullReqMetadata.state !== PullRequestState.CLOSED && (
|
{pullReqMetadata.state !== PullRequestState.CLOSED && (
|
||||||
<PullRequestPanelSections
|
<PullRequestPanelSections
|
||||||
|
|
|
@ -132,7 +132,7 @@ const MergeSection = (props: MergeSectionProps) => {
|
||||||
</Layout.Vertical>
|
</Layout.Vertical>
|
||||||
)}
|
)}
|
||||||
</Layout.Horizontal>
|
</Layout.Horizontal>
|
||||||
{!mergeable && (
|
{!mergeable && !unchecked && (
|
||||||
<Button
|
<Button
|
||||||
padding={{ right: 'unset' }}
|
padding={{ right: 'unset' }}
|
||||||
className={cx(css.blueText, css.buttonPadding)}
|
className={cx(css.blueText, css.buttonPadding)}
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, { useState } from 'react'
|
import React, { useState } from 'react'
|
||||||
import { PopoverInteractionKind } from '@blueprintjs/core'
|
import { PopoverInteractionKind, Spinner } from '@blueprintjs/core'
|
||||||
import { useGet, useMutate } from 'restful-react'
|
import { useGet, useMutate } from 'restful-react'
|
||||||
import { omit } from 'lodash-es'
|
import { omit } from 'lodash-es'
|
||||||
import cx from 'classnames'
|
import cx from 'classnames'
|
||||||
|
@ -43,13 +43,15 @@ interface PullRequestSideBarProps {
|
||||||
pullRequestMetadata: TypesPullReq
|
pullRequestMetadata: TypesPullReq
|
||||||
refetchReviewers: () => void
|
refetchReviewers: () => void
|
||||||
refetchLabels: () => void
|
refetchLabels: () => void
|
||||||
|
refetchActivities: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
const PullRequestSideBar = (props: PullRequestSideBarProps) => {
|
const PullRequestSideBar = (props: PullRequestSideBarProps) => {
|
||||||
const { standalone, hooks } = useAppContext()
|
const { standalone, hooks } = useAppContext()
|
||||||
const { CODE_PULLREQ_LABELS: isLabelEnabled } = hooks?.useFeatureFlags()
|
const { CODE_PULLREQ_LABELS: isLabelEnabled } = hooks?.useFeatureFlags()
|
||||||
const [labelQuery, setLabelQuery] = useState<string>('')
|
const [labelQuery, setLabelQuery] = useState<string>('')
|
||||||
const { reviewers, repoMetadata, pullRequestMetadata, refetchReviewers, labels, refetchLabels } = props
|
const { reviewers, repoMetadata, pullRequestMetadata, refetchReviewers, labels, refetchLabels, refetchActivities } =
|
||||||
|
props
|
||||||
const { getString } = useStrings()
|
const { getString } = useStrings()
|
||||||
const { showError, showSuccess } = useToaster()
|
const { showError, showSuccess } = useToaster()
|
||||||
const generateReviewDecisionInfo = (
|
const generateReviewDecisionInfo = (
|
||||||
|
@ -166,13 +168,10 @@ const PullRequestSideBar = (props: PullRequestSideBarProps) => {
|
||||||
path: ({ id }) => `/api/v1/repos/${repoMetadata.path}/+/pullreq/${pullRequestMetadata?.number}/reviewers/${id}`
|
path: ({ id }) => `/api/v1/repos/${repoMetadata.path}/+/pullreq/${pullRequestMetadata?.number}/reviewers/${id}`
|
||||||
})
|
})
|
||||||
|
|
||||||
const { mutate: removeLabel } = useMutate({
|
const { mutate: removeLabel, loading: removingLabel } = useMutate({
|
||||||
verb: 'DELETE',
|
verb: 'DELETE',
|
||||||
base: getConfig('code/api/v1'),
|
base: getConfig('code/api/v1'),
|
||||||
path: ({ label_id }) =>
|
path: ({ label_id }) => `/repos/${repoMetadata.path}/+/pullreq/${pullRequestMetadata?.number}/labels/${label_id}`
|
||||||
`/repos/${encodeURIComponent(repoMetadata.path as string)}/pullreq/${
|
|
||||||
pullRequestMetadata?.number
|
|
||||||
}/labels/${encodeURIComponent(label_id)}`
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const {
|
const {
|
||||||
|
@ -420,6 +419,7 @@ const PullRequestSideBar = (props: PullRequestSideBarProps) => {
|
||||||
query={labelQuery}
|
query={labelQuery}
|
||||||
setQuery={setLabelQuery}
|
setQuery={setLabelQuery}
|
||||||
labelListLoading={labelListLoading}
|
labelListLoading={labelListLoading}
|
||||||
|
refetchActivities={refetchActivities}
|
||||||
/>
|
/>
|
||||||
</Layout.Horizontal>
|
</Layout.Horizontal>
|
||||||
<Container padding={{ top: 'medium', bottom: 'large' }}>
|
<Container padding={{ top: 'medium', bottom: 'large' }}>
|
||||||
|
@ -451,6 +451,7 @@ const PullRequestSideBar = (props: PullRequestSideBarProps) => {
|
||||||
label: label.key
|
label: label.key
|
||||||
}) as string
|
}) as string
|
||||||
)
|
)
|
||||||
|
refetchActivities()
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
showError(getErrorMessage(err))
|
showError(getErrorMessage(err))
|
||||||
|
@ -464,6 +465,7 @@ const PullRequestSideBar = (props: PullRequestSideBarProps) => {
|
||||||
{getString('labels.noLabels')}
|
{getString('labels.noLabels')}
|
||||||
</Text>
|
</Text>
|
||||||
)}
|
)}
|
||||||
|
{removingLabel && <Spinner size={16} />}
|
||||||
</Layout.Horizontal>
|
</Layout.Horizontal>
|
||||||
</Container>
|
</Container>
|
||||||
</Layout.Vertical>
|
</Layout.Vertical>
|
||||||
|
|
|
@ -59,6 +59,7 @@ export default function PullRequest() {
|
||||||
commitSHA,
|
commitSHA,
|
||||||
refetchActivities,
|
refetchActivities,
|
||||||
refetchCommits,
|
refetchCommits,
|
||||||
|
refetchPullReq,
|
||||||
retryOnErrorFunc
|
retryOnErrorFunc
|
||||||
} = useGetPullRequestInfo()
|
} = useGetPullRequestInfo()
|
||||||
|
|
||||||
|
@ -158,6 +159,8 @@ export default function PullRequest() {
|
||||||
prStats={pullReqStats}
|
prStats={pullReqStats}
|
||||||
showEditDescription={showEditDescription}
|
showEditDescription={showEditDescription}
|
||||||
onCancelEditDescription={() => setShowEditDescription(false)}
|
onCancelEditDescription={() => setShowEditDescription(false)}
|
||||||
|
refetchPullReq={refetchPullReq}
|
||||||
|
refetchActivities={refetchActivities}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
|
|
@ -207,6 +207,7 @@ export function useGetPullRequestInfo() {
|
||||||
commitSHA,
|
commitSHA,
|
||||||
refetchActivities,
|
refetchActivities,
|
||||||
refetchCommits,
|
refetchCommits,
|
||||||
|
refetchPullReq,
|
||||||
retryOnErrorFunc
|
retryOnErrorFunc
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -274,8 +274,7 @@ export default function PullRequests() {
|
||||||
(isLabelEnabled || standalone) &&
|
(isLabelEnabled || standalone) &&
|
||||||
row.original &&
|
row.original &&
|
||||||
row.original.labels &&
|
row.original.labels &&
|
||||||
row.original.labels.length !== 0 &&
|
row.original.labels.length !== 0
|
||||||
!prLoading
|
|
||||||
}>
|
}>
|
||||||
{row.original?.labels?.map((label, index) => (
|
{row.original?.labels?.map((label, index) => (
|
||||||
<Label
|
<Label
|
||||||
|
@ -406,19 +405,14 @@ export default function PullRequests() {
|
||||||
<Layout.Horizontal
|
<Layout.Horizontal
|
||||||
flex={{ alignItems: 'center', justifyContent: 'flex-start' }}
|
flex={{ alignItems: 'center', justifyContent: 'flex-start' }}
|
||||||
style={{ flexWrap: 'wrap', gap: '5px' }}>
|
style={{ flexWrap: 'wrap', gap: '5px' }}>
|
||||||
<Render when={!prLoading}>
|
|
||||||
{isEmpty(data) ? (
|
|
||||||
<Text color={Color.GREY_400}>{getString('labels.noResults')}</Text>
|
|
||||||
) : (
|
|
||||||
<Text color={Color.GREY_400}>
|
<Text color={Color.GREY_400}>
|
||||||
{
|
{isEmpty(data)
|
||||||
stringSubstitute(getString('labels.prCount'), {
|
? getString('labels.noResults')
|
||||||
|
: (stringSubstitute(getString('labels.prCount'), {
|
||||||
count: data?.length
|
count: data?.length
|
||||||
}) as string
|
}) as string)}
|
||||||
}
|
|
||||||
</Text>
|
</Text>
|
||||||
)}
|
|
||||||
</Render>
|
|
||||||
{(isLabelEnabled || standalone) &&
|
{(isLabelEnabled || standalone) &&
|
||||||
labelFilter &&
|
labelFilter &&
|
||||||
labelFilter?.length !== 0 &&
|
labelFilter?.length !== 0 &&
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, { useState, useEffect } from 'react'
|
import React, { useState } from 'react'
|
||||||
import {
|
import {
|
||||||
Container,
|
Container,
|
||||||
Layout,
|
Layout,
|
||||||
|
@ -32,7 +32,7 @@ import cx from 'classnames'
|
||||||
import { Color, FontVariation, Intent } from '@harnessio/design-system'
|
import { Color, FontVariation, Intent } from '@harnessio/design-system'
|
||||||
import { Icon } from '@harnessio/icons'
|
import { Icon } from '@harnessio/icons'
|
||||||
import { noop } from 'lodash-es'
|
import { noop } from 'lodash-es'
|
||||||
import { useMutate, useGet } from 'restful-react'
|
import { useMutate } from 'restful-react'
|
||||||
import { Render } from 'react-jsx-match'
|
import { Render } from 'react-jsx-match'
|
||||||
import { ACCESS_MODES, getErrorMessage, permissionProps, voidFn } from 'utils/Utils'
|
import { ACCESS_MODES, getErrorMessage, permissionProps, voidFn } from 'utils/Utils'
|
||||||
import { useStrings } from 'framework/strings'
|
import { useStrings } from 'framework/strings'
|
||||||
|
@ -42,6 +42,7 @@ import { useGetSpaceParam } from 'hooks/useGetSpaceParam'
|
||||||
import { RepoVisibility } from 'utils/GitUtils'
|
import { RepoVisibility } from 'utils/GitUtils'
|
||||||
import { BranchTagSelect } from 'components/BranchTagSelect/BranchTagSelect'
|
import { BranchTagSelect } from 'components/BranchTagSelect/BranchTagSelect'
|
||||||
import { useModalHook } from 'hooks/useModalHook'
|
import { useModalHook } from 'hooks/useModalHook'
|
||||||
|
import { usePublicResourceConfig } from 'hooks/usePublicResourceConfig'
|
||||||
import useDeleteRepoModal from './DeleteRepoModal/DeleteRepoModal'
|
import useDeleteRepoModal from './DeleteRepoModal/DeleteRepoModal'
|
||||||
import useDefaultBranchModal from './DefaultBranchModal/DefaultBranchModal'
|
import useDefaultBranchModal from './DefaultBranchModal/DefaultBranchModal'
|
||||||
import Private from '../../../icons/private.svg?url'
|
import Private from '../../../icons/private.svg?url'
|
||||||
|
@ -62,14 +63,13 @@ const GeneralSettingsContent = (props: GeneralSettingsProps) => {
|
||||||
const [defaultBranch, setDefaultBranch] = useState(ACCESS_MODES.VIEW)
|
const [defaultBranch, setDefaultBranch] = useState(ACCESS_MODES.VIEW)
|
||||||
const { openModal: openDefaultBranchModal } = useDefaultBranchModal({ currentGitRef, setDefaultBranch, refetch })
|
const { openModal: openDefaultBranchModal } = useDefaultBranchModal({ currentGitRef, setDefaultBranch, refetch })
|
||||||
const { showError, showSuccess } = useToaster()
|
const { showError, showSuccess } = useToaster()
|
||||||
|
const { standalone, hooks } = useAppContext()
|
||||||
const space = useGetSpaceParam()
|
const space = useGetSpaceParam()
|
||||||
const { standalone, hooks, isPublicAccessEnabledOnResources } = useAppContext()
|
const { allowPublicResourceCreation } = usePublicResourceConfig()
|
||||||
const { getString } = useStrings()
|
const { getString } = useStrings()
|
||||||
const currRepoVisibility = repoMetadata?.is_public === true ? RepoVisibility.PUBLIC : RepoVisibility.PRIVATE
|
const currRepoVisibility = repoMetadata?.is_public === true ? RepoVisibility.PUBLIC : RepoVisibility.PRIVATE
|
||||||
|
|
||||||
const [repoVis, setRepoVis] = useState<RepoVisibility>(currRepoVisibility)
|
const [repoVis, setRepoVis] = useState<RepoVisibility>(currRepoVisibility)
|
||||||
const [enablePublicRepo, setEnablePublicRepo] = useState(false)
|
|
||||||
const { mutate } = useMutate({
|
const { mutate } = useMutate({
|
||||||
verb: 'PATCH',
|
verb: 'PATCH',
|
||||||
path: `/api/v1/repos/${repoMetadata?.path}/+/`
|
path: `/api/v1/repos/${repoMetadata?.path}/+/`
|
||||||
|
@ -100,19 +100,11 @@ const GeneralSettingsContent = (props: GeneralSettingsProps) => {
|
||||||
},
|
},
|
||||||
[space]
|
[space]
|
||||||
)
|
)
|
||||||
const { data: systemConfig } = useGet({ path: 'api/v1/system/config' })
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (systemConfig) {
|
|
||||||
setEnablePublicRepo(systemConfig.public_resource_creation_enabled)
|
|
||||||
}
|
|
||||||
}, [systemConfig])
|
|
||||||
|
|
||||||
const ModalComponent: React.FC = () => {
|
const ModalComponent: React.FC = () => {
|
||||||
return (
|
return (
|
||||||
<Dialog
|
<Dialog
|
||||||
className={css.dialogContainer}
|
className={css.dialogContainer}
|
||||||
style={{ width: 585, maxHeight: '95vh', overflow: 'auto' }}
|
|
||||||
title={<Text font={{ variation: FontVariation.H4 }}>{getString('changeRepoVis')}</Text>}
|
title={<Text font={{ variation: FontVariation.H4 }}>{getString('changeRepoVis')}</Text>}
|
||||||
isOpen
|
isOpen
|
||||||
onClose={hideModal}>
|
onClose={hideModal}>
|
||||||
|
@ -144,7 +136,6 @@ const GeneralSettingsContent = (props: GeneralSettingsProps) => {
|
||||||
</Container>
|
</Container>
|
||||||
<Layout.Horizontal className={css.buttonContainer}>
|
<Layout.Horizontal className={css.buttonContainer}>
|
||||||
<Button
|
<Button
|
||||||
margin={{ right: 'medium' }}
|
|
||||||
type="submit"
|
type="submit"
|
||||||
text={
|
text={
|
||||||
<StringSubstitute
|
<StringSubstitute
|
||||||
|
@ -333,7 +324,7 @@ const GeneralSettingsContent = (props: GeneralSettingsProps) => {
|
||||||
</Container>
|
</Container>
|
||||||
</Layout.Horizontal>
|
</Layout.Horizontal>
|
||||||
</Container>
|
</Container>
|
||||||
<Render when={enablePublicRepo && isPublicAccessEnabledOnResources}>
|
<Render when={allowPublicResourceCreation}>
|
||||||
<Container padding="large" margin={{ bottom: 'medium' }} className={css.generalContainer}>
|
<Container padding="large" margin={{ bottom: 'medium' }} className={css.generalContainer}>
|
||||||
<Layout.Horizontal padding={{ bottom: 'medium' }}>
|
<Layout.Horizontal padding={{ bottom: 'medium' }}>
|
||||||
<Container className={css.label}>
|
<Container className={css.label}>
|
||||||
|
|
|
@ -121,6 +121,9 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.dialogContainer {
|
.dialogContainer {
|
||||||
|
width: 600px;
|
||||||
|
max-height: 95vh;
|
||||||
|
overflow: auto;
|
||||||
:global(.bp3-dialog-header) {
|
:global(.bp3-dialog-header) {
|
||||||
margin-bottom: var(--spacing-medium) !important;
|
margin-bottom: var(--spacing-medium) !important;
|
||||||
}
|
}
|
||||||
|
@ -144,7 +147,7 @@
|
||||||
.buttonContainer {
|
.buttonContainer {
|
||||||
width: 20%;
|
width: 20%;
|
||||||
padding-top: var(--spacing-xsmall) !important;
|
padding-top: var(--spacing-xsmall) !important;
|
||||||
gap: 10px;
|
gap: 15px;
|
||||||
margin-left: auto !important;
|
margin-left: auto !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -85,6 +85,12 @@ export const getUsingFetch = <
|
||||||
if (res.status === 401) {
|
if (res.status === 401) {
|
||||||
return res.json().then(json => Promise.reject(json))
|
return res.json().then(json => Promise.reject(json))
|
||||||
}
|
}
|
||||||
|
if (res.status === 400) {
|
||||||
|
return res.text().then(text => Promise.reject(text))
|
||||||
|
}
|
||||||
|
if (res.status === 403) {
|
||||||
|
return res.text().then(text => Promise.reject(text))
|
||||||
|
}
|
||||||
return res.json()
|
return res.json()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -100,6 +106,14 @@ export const getUsingFetch = <
|
||||||
return res.text().then(text => Promise.reject(text))
|
return res.text().then(text => Promise.reject(text))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (res.status === 403) {
|
||||||
|
return res.text().then(text => Promise.reject(text))
|
||||||
|
}
|
||||||
|
|
||||||
|
if (res.status === 400) {
|
||||||
|
return res.text().then(text => Promise.reject(text))
|
||||||
|
}
|
||||||
|
|
||||||
return res.text()
|
return res.text()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -468,6 +468,8 @@ export const getProviders = () =>
|
||||||
export const codeOwnersNotFoundMessage = 'CODEOWNERS file not found'
|
export const codeOwnersNotFoundMessage = 'CODEOWNERS file not found'
|
||||||
export const codeOwnersNotFoundMessage2 = `path "CODEOWNERS" not found`
|
export const codeOwnersNotFoundMessage2 = `path "CODEOWNERS" not found`
|
||||||
export const codeOwnersNotFoundMessage3 = `failed to find node 'CODEOWNERS' in 'main': failed to get tree node: failed to ls file: path "CODEOWNERS" not found`
|
export const codeOwnersNotFoundMessage3 = `failed to find node 'CODEOWNERS' in 'main': failed to get tree node: failed to ls file: path "CODEOWNERS" not found`
|
||||||
|
export const oldCommitRefetchRequired = 'A newer commit is available. Only the latest commit can be merged.'
|
||||||
|
export const prMergedRefetchRequired = 'Pull request already merged'
|
||||||
|
|
||||||
export const dryMerge = (
|
export const dryMerge = (
|
||||||
isMounted: React.MutableRefObject<boolean>,
|
isMounted: React.MutableRefObject<boolean>,
|
||||||
|
@ -504,6 +506,7 @@ export const dryMerge = (
|
||||||
pullRequestSection: string | undefined,
|
pullRequestSection: string | undefined,
|
||||||
showError: (message: React.ReactNode, timeout?: number | undefined, key?: string | undefined) => void,
|
showError: (message: React.ReactNode, timeout?: number | undefined, key?: string | undefined) => void,
|
||||||
setConflictingFiles: React.Dispatch<React.SetStateAction<string[] | undefined>>,
|
setConflictingFiles: React.Dispatch<React.SetStateAction<string[] | undefined>>,
|
||||||
|
refetchPullReq: () => void,
|
||||||
setRequiresCommentApproval?: (value: React.SetStateAction<boolean>) => void,
|
setRequiresCommentApproval?: (value: React.SetStateAction<boolean>) => void,
|
||||||
setAtLeastOneReviewerRule?: (value: React.SetStateAction<boolean>) => void,
|
setAtLeastOneReviewerRule?: (value: React.SetStateAction<boolean>) => void,
|
||||||
setReqCodeOwnerApproval?: (value: React.SetStateAction<boolean>) => void,
|
setReqCodeOwnerApproval?: (value: React.SetStateAction<boolean>) => void,
|
||||||
|
@ -555,6 +558,11 @@ export const dryMerge = (
|
||||||
setReqCodeOwnerLatestApproval?.(err.requires_code_owners_approval_latest)
|
setReqCodeOwnerLatestApproval?.(err.requires_code_owners_approval_latest)
|
||||||
setMinReqLatestApproval?.(err.minimum_required_approvals_count_latest)
|
setMinReqLatestApproval?.(err.minimum_required_approvals_count_latest)
|
||||||
setConflictingFiles?.(err.conflict_files)
|
setConflictingFiles?.(err.conflict_files)
|
||||||
|
} else if (
|
||||||
|
err.status === 400 &&
|
||||||
|
(getErrorMessage(err) === oldCommitRefetchRequired || getErrorMessage(err) === prMergedRefetchRequired)
|
||||||
|
) {
|
||||||
|
refetchPullReq()
|
||||||
} else if (
|
} else if (
|
||||||
getErrorMessage(err) === codeOwnersNotFoundMessage ||
|
getErrorMessage(err) === codeOwnersNotFoundMessage ||
|
||||||
getErrorMessage(err) === codeOwnersNotFoundMessage2 ||
|
getErrorMessage(err) === codeOwnersNotFoundMessage2 ||
|
||||||
|
|
|
@ -171,6 +171,7 @@ export interface PullRequestActionsBoxProps extends Pick<GitInfoProps, 'repoMeta
|
||||||
PRStateLoading: boolean
|
PRStateLoading: boolean
|
||||||
conflictingFiles: string[] | undefined
|
conflictingFiles: string[] | undefined
|
||||||
setConflictingFiles: React.Dispatch<React.SetStateAction<string[] | undefined>>
|
setConflictingFiles: React.Dispatch<React.SetStateAction<string[] | undefined>>
|
||||||
|
refetchPullReq: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PRMergeOption extends SelectOption {
|
export interface PRMergeOption extends SelectOption {
|
||||||
|
|
Loading…
Reference in New Issue