-
Notifications
You must be signed in to change notification settings - Fork 16
fix(PM-1273): show apply copilot only for projects where user is not a member #1112
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
5be25fc
f0e654d
5396629
4ca0221
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -125,6 +125,7 @@ const CopilotOpportunityDetails: FC<{}> = () => { | |
} | ||
|
||
const application = copilotApplications && copilotApplications[0] | ||
const isAlreadyMemberOfTheProject = profile && opportunity?.members?.includes(profile.userId) | ||
|
||
return ( | ||
<ContentLayout | ||
|
@@ -133,7 +134,8 @@ const CopilotOpportunityDetails: FC<{}> = () => { | |
isCopilot | ||
&& copilotApplications | ||
&& copilotApplications.length === 0 | ||
&& opportunity?.status === 'active' ? applyCopilotOpportunityButton : undefined | ||
&& opportunity?.status === 'active' | ||
&& !isAlreadyMemberOfTheProject ? applyCopilotOpportunityButton : undefined | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider renaming |
||
} | ||
infoComponent={(isCopilot && !(copilotApplications | ||
&& copilotApplications.length === 0 | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,14 +1,14 @@ | ||
import { FC, useContext, useMemo, useState } from 'react' | ||
import { bind, isEmpty } from 'lodash' | ||
import { bind, debounce, isEmpty } from 'lodash' | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The |
||
import { toast } from 'react-toastify' | ||
import classNames from 'classnames' | ||
|
||
import { profileContext, ProfileContextData } from '~/libs/core' | ||
import { Button, IconSolid, InputDatePicker, InputMultiselectOption, | ||
InputRadio, InputSelect, InputSelectOption, InputSelectReact, InputText, InputTextarea } from '~/libs/ui' | ||
InputRadio, InputSelect, InputSelectReact, InputText, InputTextarea } from '~/libs/ui' | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The |
||
import { InputSkillSelector } from '~/libs/shared' | ||
|
||
import { ProjectsResponse, useProjects } from '../../services/projects' | ||
import { getProjects } from '../../services/projects' | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The |
||
import { ProjectTypes, ProjectTypeValues } from '../../constants' | ||
import { saveCopilotRequest } from '../../services/copilot-requests' | ||
|
||
|
@@ -20,20 +20,9 @@ const CopilotRequestForm: FC<{}> = () => { | |
const [formValues, setFormValues] = useState<any>({}) | ||
const [isFormChanged, setIsFormChanged] = useState(false) | ||
const [formErrors, setFormErrors] = useState<any>({}) | ||
const [searchTerm, setSearchTerm] = useState<string>('') | ||
const { data: projectsData }: ProjectsResponse = useProjects(searchTerm) | ||
const [existingCopilot, setExistingCopilot] = useState<string>('') | ||
const [paymentType, setPaymentType] = useState<string>('') | ||
|
||
const projects = useMemo( | ||
() => ( | ||
projectsData | ||
? projectsData.map(project => ({ label: project.name, value: project.id })) | ||
: [] | ||
), | ||
[projectsData], | ||
) | ||
|
||
const projectTypes = ProjectTypes ? ProjectTypes.map(project => ({ | ||
label: project, | ||
value: ProjectTypeValues[project], | ||
|
@@ -63,18 +52,12 @@ const CopilotRequestForm: FC<{}> = () => { | |
setPaymentType(t) | ||
} | ||
|
||
function filterProjects(option: InputSelectOption, value: string): boolean { | ||
setSearchTerm(value) | ||
return ( | ||
option.label | ||
?.toString() | ||
.toLowerCase() | ||
.includes(value.toLowerCase()) ?? false | ||
) | ||
} | ||
|
||
function handleProjectSearch(inputValue: string): void { | ||
setSearchTerm(inputValue) | ||
async function handleProjectSearch(inputValue: string): Promise<Array<{ | ||
label: string; | ||
value: string; | ||
}>> { | ||
const response = await getProjects(inputValue) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider adding error handling for the |
||
return response.map(project => ({ label: project.name, value: project.id })) | ||
} | ||
|
||
function handleProjectSelect(option: React.ChangeEvent<HTMLInputElement>): void { | ||
|
@@ -268,6 +251,11 @@ const CopilotRequestForm: FC<{}> = () => { | |
setFormErrors(updatedFormErrors) | ||
} | ||
|
||
const debouncedProjectSearch = useMemo(() => debounce((inputValue: string, callback: (options: any[]) => void) => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider specifying the type for the |
||
handleProjectSearch(inputValue) | ||
.then(callback) | ||
}, 300), []) | ||
|
||
return ( | ||
<div className={classNames('d-flex flex-column justify-content-center align-items-center', styles.container)}> | ||
<div className={styles.form}> | ||
|
@@ -290,15 +278,14 @@ const CopilotRequestForm: FC<{}> = () => { | |
<p className={styles.formRow}>Select the project you want the copilot for</p> | ||
<InputSelectReact | ||
tabIndex={0} | ||
options={projects} | ||
value={formValues.projectId || ''} | ||
onChange={handleProjectSelect} | ||
onInputChange={handleProjectSearch} | ||
loadOptions={debouncedProjectSearch} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The |
||
async | ||
name='project' | ||
label='Project' | ||
placeholder='Start typing the name of the project' | ||
dirty | ||
filterOption={filterProjects} | ||
error={formErrors.projectId} | ||
/> | ||
<p className={styles.formRow}> | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -42,3 +42,9 @@ export const useProjects = (search?: string, config?: {isPaused?: () => boolean, | |
revalidateOnFocus: false, | ||
}) | ||
} | ||
|
||
export const getProjects = (search?: string, filter?: any): Promise<Project[]> => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider specifying a more precise type than |
||
const params = { name: `"${search}"`, ...filter } | ||
const url = buildUrl(baseUrl, params) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ensure that |
||
return xhrGetAsync<Project[]>(url) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,6 +8,8 @@ import { | |
useRef, | ||
} from 'react' | ||
import { find } from 'lodash' | ||
import AsyncCreatable from 'react-select/async-creatable' | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider checking if the newly added imports |
||
import AsyncSelect from 'react-select/async' | ||
import CreatableSelect from 'react-select/creatable' | ||
import ReactSelect, { GroupBase, OptionsOrGroups } from 'react-select' | ||
import classNames from 'classnames' | ||
|
@@ -33,7 +35,7 @@ interface InputSelectReactProps { | |
readonly name: string | ||
readonly onChange: (event: ChangeEvent<HTMLInputElement>) => void | ||
readonly onInputChange?: (newValue: string) => void | ||
readonly options: OptionsOrGroups<unknown, GroupBase<unknown>> | ||
readonly options?: OptionsOrGroups<unknown, GroupBase<unknown>> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. SuggestionConsider providing a default value or handling the case where |
||
readonly placeholder?: string | ||
readonly tabIndex?: number | ||
readonly value?: string | ||
|
@@ -43,6 +45,8 @@ interface InputSelectReactProps { | |
readonly onBlur?: (event: FocusEvent<HTMLInputElement>) => void | ||
readonly openMenuOnClick?: boolean | ||
readonly openMenuOnFocus?: boolean | ||
readonly async?: boolean | ||
readonly loadOptions?: (inputValue: string, callback: (option: any) => void) => void | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider specifying the type for |
||
readonly filterOption?: (option: InputSelectOption, value: string) => boolean | ||
} | ||
|
||
|
@@ -120,9 +124,13 @@ const InputSelectReact: FC<InputSelectReactProps> = props => { | |
} as FocusEvent<HTMLInputElement>) | ||
} | ||
|
||
const Input = useMemo(() => ( | ||
props.creatable ? CreatableSelect : ReactSelect | ||
), [props.creatable]) | ||
const Input = useMemo(() => { | ||
if (props.async) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider adding a type annotation for the |
||
return props.creatable ? AsyncCreatable : AsyncSelect | ||
} | ||
|
||
return props.creatable ? CreatableSelect : ReactSelect | ||
}, [props.creatable, props.async]) | ||
|
||
return ( | ||
<InputWrapper | ||
|
@@ -144,6 +152,7 @@ const InputSelectReact: FC<InputSelectReactProps> = props => { | |
styles.select, | ||
) | ||
} | ||
loadOptions={props.loadOptions} | ||
onChange={handleSelect} | ||
onInputChange={props.onInputChange} | ||
menuPortalTarget={menuPortalTarget} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider checking if the
rolesRequired
array should include any other roles that might be relevant for accessing the Copilot Request Form. Ensure that this change aligns with the intended access control requirements.