Skip to content

Commit 5be25fc

Browse files
committed
fix: project list and made new request form restricted route
1 parent 2bf40eb commit 5be25fc

File tree

4 files changed

+36
-33
lines changed

4 files changed

+36
-33
lines changed

src/apps/copilots/src/copilots.routes.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ export const childRoutes = [
3232
authRequired: true,
3333
element: <CopilotsRequestForm />,
3434
id: 'CopilotRequestForm',
35+
rolesRequired: [UserRole.administrator, UserRole.projectManager] as UserRole[],
3536
route: '/requests/new',
3637
},
3738
{

src/apps/copilots/src/pages/copilot-request-form/index.tsx

Lines changed: 16 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
import { FC, useContext, useMemo, useState } from 'react'
2-
import { bind, isEmpty } from 'lodash'
2+
import { bind, debounce, isEmpty } from 'lodash'
33
import { toast } from 'react-toastify'
44
import classNames from 'classnames'
55

66
import { profileContext, ProfileContextData } from '~/libs/core'
77
import { Button, IconSolid, InputDatePicker, InputMultiselectOption,
8-
InputRadio, InputSelect, InputSelectOption, InputSelectReact, InputText, InputTextarea } from '~/libs/ui'
8+
InputRadio, InputSelect, InputSelectReact, InputText, InputTextarea } from '~/libs/ui'
99
import { InputSkillSelector } from '~/libs/shared'
1010

11-
import { ProjectsResponse, useProjects } from '../../services/projects'
11+
import { getProjects } from '../../services/projects'
1212
import { ProjectTypes, ProjectTypeValues } from '../../constants'
1313
import { saveCopilotRequest } from '../../services/copilot-requests'
1414

@@ -20,20 +20,9 @@ const CopilotRequestForm: FC<{}> = () => {
2020
const [formValues, setFormValues] = useState<any>({})
2121
const [isFormChanged, setIsFormChanged] = useState(false)
2222
const [formErrors, setFormErrors] = useState<any>({})
23-
const [searchTerm, setSearchTerm] = useState<string>('')
24-
const { data: projectsData }: ProjectsResponse = useProjects(searchTerm)
2523
const [existingCopilot, setExistingCopilot] = useState<string>('')
2624
const [paymentType, setPaymentType] = useState<string>('')
2725

28-
const projects = useMemo(
29-
() => (
30-
projectsData
31-
? projectsData.map(project => ({ label: project.name, value: project.id }))
32-
: []
33-
),
34-
[projectsData],
35-
)
36-
3726
const projectTypes = ProjectTypes ? ProjectTypes.map(project => ({
3827
label: project,
3928
value: ProjectTypeValues[project],
@@ -63,18 +52,12 @@ const CopilotRequestForm: FC<{}> = () => {
6352
setPaymentType(t)
6453
}
6554

66-
function filterProjects(option: InputSelectOption, value: string): boolean {
67-
setSearchTerm(value)
68-
return (
69-
option.label
70-
?.toString()
71-
.toLowerCase()
72-
.includes(value.toLowerCase()) ?? false
73-
)
74-
}
75-
76-
function handleProjectSearch(inputValue: string): void {
77-
setSearchTerm(inputValue)
55+
async function handleProjectSearch(inputValue: string): Promise<Array<{
56+
label: string;
57+
value: string;
58+
}>> {
59+
const response = await getProjects(inputValue)
60+
return response.map(project => ({ label: project.name, value: project.id }))
7861
}
7962

8063
function handleProjectSelect(option: React.ChangeEvent<HTMLInputElement>): void {
@@ -268,6 +251,11 @@ const CopilotRequestForm: FC<{}> = () => {
268251
setFormErrors(updatedFormErrors)
269252
}
270253

254+
const debouncedProjectSearch = useMemo(() => debounce((inputValue: string, callback: (options: any[]) => void) => {
255+
handleProjectSearch(inputValue)
256+
.then(callback)
257+
}, 300), [])
258+
271259
return (
272260
<div className={classNames('d-flex flex-column justify-content-center align-items-center', styles.container)}>
273261
<div className={styles.form}>
@@ -290,15 +278,14 @@ const CopilotRequestForm: FC<{}> = () => {
290278
<p className={styles.formRow}>Select the project you want the copilot for</p>
291279
<InputSelectReact
292280
tabIndex={0}
293-
options={projects}
294281
value={formValues.projectId || ''}
295282
onChange={handleProjectSelect}
296-
onInputChange={handleProjectSearch}
283+
loadOptions={debouncedProjectSearch}
284+
async
297285
name='project'
298286
label='Project'
299287
placeholder='Start typing the name of the project'
300288
dirty
301-
filterOption={filterProjects}
302289
error={formErrors.projectId}
303290
/>
304291
<p className={styles.formRow}>

src/apps/copilots/src/services/projects.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,3 +42,9 @@ export const useProjects = (search?: string, config?: {isPaused?: () => boolean,
4242
revalidateOnFocus: false,
4343
})
4444
}
45+
46+
export const getProjects = (search?: string, filter?: any): Promise<Project[]> => {
47+
const params = { name: `"${search}"`, ...filter }
48+
const url = buildUrl(baseUrl, params)
49+
return xhrGetAsync<Project[]>(url)
50+
}

src/libs/ui/lib/components/form/form-groups/form-input/input-select-react/InputSelectReact.tsx

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import {
88
useRef,
99
} from 'react'
1010
import { find } from 'lodash'
11+
import AsyncCreatable from 'react-select/async-creatable'
12+
import AsyncSelect from 'react-select/async'
1113
import CreatableSelect from 'react-select/creatable'
1214
import ReactSelect, { GroupBase, OptionsOrGroups } from 'react-select'
1315
import classNames from 'classnames'
@@ -33,7 +35,7 @@ interface InputSelectReactProps {
3335
readonly name: string
3436
readonly onChange: (event: ChangeEvent<HTMLInputElement>) => void
3537
readonly onInputChange?: (newValue: string) => void
36-
readonly options: OptionsOrGroups<unknown, GroupBase<unknown>>
38+
readonly options?: OptionsOrGroups<unknown, GroupBase<unknown>>
3739
readonly placeholder?: string
3840
readonly tabIndex?: number
3941
readonly value?: string
@@ -43,6 +45,8 @@ interface InputSelectReactProps {
4345
readonly onBlur?: (event: FocusEvent<HTMLInputElement>) => void
4446
readonly openMenuOnClick?: boolean
4547
readonly openMenuOnFocus?: boolean
48+
readonly async?: boolean
49+
readonly loadOptions?: (inputValue: string, callback: (option: any) => void) => void
4650
readonly filterOption?: (option: InputSelectOption, value: string) => boolean
4751
}
4852

@@ -120,9 +124,13 @@ const InputSelectReact: FC<InputSelectReactProps> = props => {
120124
} as FocusEvent<HTMLInputElement>)
121125
}
122126

123-
const Input = useMemo(() => (
124-
props.creatable ? CreatableSelect : ReactSelect
125-
), [props.creatable])
127+
const Input = useMemo(() => {
128+
if (props.async) {
129+
return props.creatable ? AsyncCreatable : AsyncSelect
130+
}
131+
132+
return props.creatable ? CreatableSelect : ReactSelect
133+
}, [props.creatable, props.async])
126134

127135
return (
128136
<InputWrapper
@@ -144,6 +152,7 @@ const InputSelectReact: FC<InputSelectReactProps> = props => {
144152
styles.select,
145153
)
146154
}
155+
loadOptions={props.loadOptions}
147156
onChange={handleSelect}
148157
onInputChange={props.onInputChange}
149158
menuPortalTarget={menuPortalTarget}

0 commit comments

Comments
 (0)