feat: [CDE-704]: Added new delegate selector dropdown with options at infra level (#3615)

* fix: Added missing string
* fix: lint issues
* fix: Resolved the conflicts
* fix: lint issues
* fix: lint issues
* fix: lint issues
* fix: Passed the delegateSelector component from harness-core-ui to CDE module
* fix: lint issues
* feat: Added new delegate selector dropdown with options at infra level
This commit is contained in:
Neel Khamar 2025-04-07 05:03:24 +00:00 committed by Harness
parent cc64066d9a
commit 0a34d44cdd
14 changed files with 85 additions and 9 deletions

View File

@ -27,6 +27,7 @@ import { newCacheStrategy } from 'utils/CacheStrategy'
import { useGetSettingValue } from 'hooks/useGetSettingValue'
import { useFeatureFlags } from 'hooks/useFeatureFlag'
import { defaultUsefulOrNot } from 'components/DefaultUsefulOrNot/UsefulOrNot'
import { defaultDelegateSelectorsV2 } from 'components/DelegateSelector/DelegateSelector'
interface AppContextProps extends AppProps {
setAppContext: (value: Partial<AppProps>) => void
@ -50,7 +51,8 @@ const AppContext = React.createContext<AppContextProps>({
hooks: {},
currentUser: defaultCurrentUser,
customComponents: {
UsefulOrNot: defaultUsefulOrNot
UsefulOrNot: defaultUsefulOrNot,
DelegateSelectorsV2: defaultDelegateSelectorsV2
},
currentUserProfileURL: '',
routingId: '',

View File

@ -17,7 +17,7 @@
import type React from 'react'
import type { CODERoutes } from 'RouteDefinitions'
import type { TypesUser } from 'services/code'
import type { UsefulOrNotProps } from 'utils/types'
import type { DelegateSelectorsV2Props, UsefulOrNotProps } from 'utils/types'
import type { LangLocale } from './framework/strings/languageLoader'
/**
@ -61,6 +61,7 @@ export interface AppProps {
/** React Components which are passed down from the Parent that are needed by the child app */
customComponents: {
UsefulOrNot: (props: UsefulOrNotProps) => React.ReactElement
DelegateSelectorsV2: (props: DelegateSelectorsV2Props) => React.ReactElement
}
/** React Hooks that Harness Platform passes down. Note: Pass only hooks that your app need */
hooks: Partial<{
@ -77,6 +78,7 @@ export interface AppProps {
useListAggregatedTokens?: Unknown
useDeleteToken?: Unknown
useCreateToken?: Unknown
useGetDelegateSelectorsUpTheHierarchyV2?: Unknown
}>
currentUser: Required<TypesUser>

View File

@ -23,6 +23,7 @@ import { useFeatureFlags } from 'hooks/useFeatureFlag'
import { useGetSettingValue } from 'hooks/useGetSettingValue'
import { useGetAuthSettings } from 'hooks/useGetAuthSettings'
import { defaultUsefulOrNot } from 'components/DefaultUsefulOrNot/UsefulOrNot'
import { defaultDelegateSelectorsV2 } from 'components/DelegateSelector/DelegateSelector'
import App from './App'
import './bootstrap.scss'
@ -47,7 +48,8 @@ ReactDOM.render(
}}
currentUser={defaultCurrentUser}
customComponents={{
UsefulOrNot: defaultUsefulOrNot
UsefulOrNot: defaultUsefulOrNot,
DelegateSelectorsV2: defaultDelegateSelectorsV2
}}
currentUserProfileURL=""
routingId=""

View File

@ -64,7 +64,7 @@ export const SelectInfraProvider = () => {
infraOptions.push(payload)
}
})
if (CDE_HYBRID_ENABLED && !isHybridAvailable) {
if (CDE_HYBRID_ENABLED && !isHybridAvailable && data) {
history.push(
routes.toCDEGitspaceInfra({
accountId: accountInfo?.identifier

View File

@ -42,6 +42,11 @@ export enum IDEType {
RIDER = 'rider'
}
export interface DelegateSelector {
connected?: boolean
name?: string
}
export interface regionProp {
location: string
defaultSubnet: string

View File

@ -1,11 +1,28 @@
import React from 'react'
import { Container, FormInput, Layout, Text } from '@harnessio/uicore'
import { Color } from '@harnessio/design-system'
import type { FormikProps } from 'formik'
import { useStrings } from 'framework/strings'
import { useAppContext } from 'AppContext'
import css from './InfraDetails.module.scss'
const BasicDetails = () => {
interface BasicDetailProps {
formikProps: FormikProps<any>
}
const BasicDetails = ({ formikProps }: BasicDetailProps) => {
const { getString } = useStrings()
const { hooks, accountInfo, customComponents } = useAppContext()
const { DelegateSelectorsV2 } = customComponents
const queryParams = { accountId: accountInfo?.identifier }
const { data: delegateData } = hooks.useGetDelegateSelectorsUpTheHierarchyV2({
queryParams
})
const delegateHandler = (val: string[]) => {
formikProps.setFieldValue('delegateSelector', val)
}
return (
<Layout.Vertical spacing="medium" className={css.containerSpacing}>
<Text className={css.basicDetailsHeading}>{getString('cde.configureInfra.basicDetails')}</Text>
@ -34,6 +51,14 @@ const BasicDetails = () => {
placeholder={getString('cde.configureInfra.domain')}
/>
<Text className={css.noteText}>{getString('cde.configureInfra.basicNoteText')}</Text>
<Container className={css.delegateContainer}>
<Text className={css.delegateSelector}>{getString('cde.delegate.DelegateSelector')}</Text>
<DelegateSelectorsV2
data={delegateData?.resource ?? []}
selectedItems={formikProps?.values?.delegateSelector}
onTagInputChange={delegateHandler}
/>
</Container>
</Container>
</Layout.Vertical>
)

View File

@ -127,3 +127,17 @@
line-height: var(--spacing-main) !important;
text-transform: uppercase !important;
}
.delegateContainer {
margin: var(--padding-button) 0 !important;
[class*='DelegateSelectors-module_delegateInput'] {
border: 1px solid var(--grey-200) !important;
}
}
.delegateSelector {
padding-bottom: var(--spacing-xsmall) !important;
font-size: 13px !important;
font-weight: 500 !important;
}

View File

@ -21,6 +21,8 @@ export declare const basicDetailsContainer: string
export declare const basicDetailsHeading: string
export declare const bottomSpacing: string
export declare const containerSpacing: string
export declare const delegateContainer: string
export declare const delegateSelector: string
export declare const deleteContainer: string
export declare const dnone: string
export declare const formFooter: string

View File

@ -25,6 +25,7 @@ interface InfraDetailsFormikProps {
machine_type?: string
instances?: string
project?: string
delegateSelector?: string[]
}
const InfraDetails = () => {
@ -59,13 +60,15 @@ const InfraDetails = () => {
useEffect(() => {
if (data) {
const { identifier, metadata, name }: OpenapiCreateInfraProviderConfigRequest = data
const delegate = metadata?.delegate_selectors?.map((del: { selector: string }) => del.selector)
const payload: InfraDetailsFormikProps = {
identifier: identifier,
name: name,
domain: metadata?.domain,
machine_type: metadata?.gateway?.machine_type,
instances: metadata?.gateway?.instances,
project: metadata?.project?.id
project: metadata?.project?.id,
delegateSelector: delegate
}
const regions: regionProp[] = []
Object?.keys(data?.metadata?.region_configs ?? {}).forEach((key: string, index: number) => {
@ -103,7 +106,7 @@ const InfraDetails = () => {
onSubmit={async (values: InfraDetailsFormikProps) => {
try {
if (regionData?.length > 0) {
const { identifier, name, domain, machine_type, instances, project } = values
const { identifier, name, domain, machine_type, instances, project, delegateSelector } = values
const region_configs: Unknown = {}
regionData?.forEach((region: regionProp) => {
const { location, defaultSubnet, proxySubnet, dns, domain: regionDomain } = region
@ -122,11 +125,13 @@ const InfraDetails = () => {
}
}
})
const delegates = delegateSelector?.map((del: string) => ({ selector: del }))
const payload: OpenapiCreateInfraProviderConfigRequest = {
identifier,
metadata: {
domain,
name: identifier,
delegate_selectors: delegates,
region_configs,
project: {
id: project
@ -168,7 +173,7 @@ const InfraDetails = () => {
return (
<FormikForm>
<Layout.Vertical spacing="medium">
<BasicDetails />
<BasicDetails formikProps={formikProps} />
<GatewayDetails formikProps={formikProps} />
<ConfigureLocations regionData={regionData} setRegionData={setRegionData} initialData={initialData} />
<Layout.Horizontal className={css.formFooter}>

View File

@ -63,6 +63,8 @@ id: ID
gitspaceInfra: Gitspace Infrastructure
configureGCPInfra: Configure GCP Infrastructure
configureGitspaceInfra: Configure Gitspace Infrastructure
delegate:
DelegateSelector: Delegate Selector
gitspaceInfraHome:
configureGCPButton: Configure GCP infrastructure
description: Configure an infrastructure with locations and machines to be used as one of the infrastructure while creating a Gitspace

View File

@ -0,0 +1,7 @@
import React from 'react'
import { Container } from '@harnessio/uicore'
import type { DelegateSelectorsV2Props } from 'utils/types'
export const defaultDelegateSelectorsV2 = (props: DelegateSelectorsV2Props): React.ReactElement => {
return <Container {...(props as any)} />
}

View File

@ -1313,6 +1313,7 @@ export interface StringsMap {
'cde.createImport': string
'cde.createRepo': string
'cde.created': string
'cde.delegate.DelegateSelector': string
'cde.deleteGitspace': string
'cde.deleteGitspaceText': string
'cde.deleteGitspaceTitle': string

View File

@ -16,6 +16,7 @@ import type { LangLocale } from 'framework/strings/languageLoader'
import { AppContextProvider, defaultCurrentUser } from 'AppContext'
import { defaultUsefulOrNot } from 'components/DefaultUsefulOrNot/UsefulOrNot'
import { StringsContextProvider } from 'framework/strings/StringsContextProvider'
import { defaultDelegateSelectorsV2 } from 'components/DelegateSelector/DelegateSelector'
export interface TestWrapperProps {
path?: string
@ -70,7 +71,8 @@ export default function TestWrapper(props: PropsWithChildren<TestWrapperProps>)
hooks: {},
currentUser,
customComponents: {
UsefulOrNot: defaultUsefulOrNot
UsefulOrNot: defaultUsefulOrNot,
DelegateSelectorsV2: defaultDelegateSelectorsV2
},
currentUserProfileURL,
defaultSettingsURL,

View File

@ -15,6 +15,7 @@
*/
import type { DiffFile } from 'diff2html/lib/types'
import type { DelegateSelector } from 'cde-gitness/constants'
export interface DiffFileEntry extends DiffFile {
fileId: string
@ -43,6 +44,12 @@ export interface UsefulOrNotProps {
className?: string
}
export interface DelegateSelectorsV2Props {
data?: DelegateSelector[]
selectedItems?: string[]
onTagInputChange?: (values: string[]) => void
}
enum Vote {
None,
Up,