-
Notifications
You must be signed in to change notification settings - Fork 16
[PROD RELEASE] - Copilot Portal #1117
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 13 commits
a3c16e3
a6c733a
26ee3b3
cd48385
0393ba4
2001031
ce59d56
86575bb
389903d
d67e59e
9b6c7bc
cca9941
296bbcf
871976b
b3cc77f
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 |
---|---|---|
|
@@ -44,8 +44,8 @@ const ApplyOpportunityModal: FC<ApplyOpportunityModalProps> = props => { | |
size='lg' | ||
title={ | ||
success | ||
? `Your Application for ${props.projectName} Has Been Received!` | ||
: `Confirm Your Copilot Application for ${props.projectName}` | ||
? 'Your Application Has Been Received!' | ||
: 'Confirm Your Copilot Application' | ||
} | ||
buttons={ | ||
!success ? ( | ||
|
@@ -62,21 +62,21 @@ const ApplyOpportunityModal: FC<ApplyOpportunityModalProps> = props => { | |
<div className={styles.info}> | ||
{ | ||
success | ||
? `We appreciate the time and effort you've taken to apply | ||
for this exciting opportunity. Our team is committed | ||
to providing a seamless and efficient process to ensure a | ||
great experience for all copilots. We will review your application | ||
within short time.` | ||
? `Thank you for taking the time to apply for this exciting opportunity. | ||
We truly value your interest and effort. | ||
Your application will be reviewed promptly.` | ||
: `We're excited to see your interest in joining our team as a copilot | ||
for the ${props.projectName} project! Before we proceed, we want to | ||
for the "${props.projectName}" project! Before we proceed, we want to | ||
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 using consistent quotation marks for the project name. Either use single quotes or double quotes throughout the codebase for consistency. |
||
ensure that you have carefully reviewed the project requirements and | ||
are committed to meeting them.` | ||
are committed to meeting them. Please write below the reason(s) | ||
why you believe you're a good fit for this project | ||
(e.g., previous experience, availability, etc.).` | ||
} | ||
</div> | ||
{ | ||
!success && ( | ||
<InputTextarea | ||
name='Notes' | ||
name='Reason' | ||
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 name attribute has been changed from 'Notes' to 'Reason'. Ensure that this change is reflected wherever this component is used or referenced to avoid potential issues. |
||
onChange={onChange} | ||
value={notes} | ||
error={error} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,6 +12,7 @@ import { | |
} from 'react' | ||
import { useLocation, useNavigate, useParams } from 'react-router-dom' | ||
import { mutate } from 'swr' | ||
import { toast } from 'react-toastify' | ||
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 verifying if |
||
import moment from 'moment' | ||
|
||
import { | ||
|
@@ -28,13 +29,15 @@ import { textFormatDateLocaleShortString } from '~/libs/shared' | |
|
||
import { CopilotApplication } from '../../models/CopilotApplication' | ||
import { | ||
cancelCopilotOpportunity, | ||
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 |
||
copilotBaseUrl, | ||
CopilotOpportunityResponse, | ||
useCopilotApplications, | ||
useCopilotOpportunity, | ||
} from '../../services/copilot-opportunities' | ||
import { FormattedMembers, useMembers } from '../../services/members' | ||
import { copilotRoutesMap } from '../../copilots.routes' | ||
import { ProjectType } from '../../constants' | ||
|
||
import { ApplyOpportunityModal } from './apply-opportunity-modal' | ||
import { | ||
|
@@ -103,7 +106,7 @@ const CopilotOpportunityDetails: FC<{}> = () => { | |
|
||
const onApplied: () => void = useCallback(() => { | ||
mutate(`${copilotBaseUrl}/copilots/opportunity/${opportunityId}/applications`) | ||
mutate(`${copilotBaseUrl}/copilots/opportunity/${opportunityId}`) | ||
mutate(`${copilotBaseUrl}/copilot/opportunity/${opportunityId}`) | ||
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 URL path has been changed from |
||
}, []) | ||
|
||
const onCloseApplyModal: () => void = useCallback(() => { | ||
|
@@ -119,13 +122,42 @@ const CopilotOpportunityDetails: FC<{}> = () => { | |
) | ||
} | ||
|
||
async function cancelCopilotOpportunityHandler(): Promise<void> { | ||
if (opportunityId) { | ||
await cancelCopilotOpportunity(opportunityId) | ||
mutate(`${copilotBaseUrl}/copilots/opportunity/${opportunityId}/applications`) | ||
mutate(`${copilotBaseUrl}/copilot/opportunity/${opportunityId}`) | ||
toast.success('Canceled copilot opportunity successfully') | ||
} | ||
|
||
} | ||
|
||
const applyCopilotOpportunityButton: ButtonProps = { | ||
label: 'Apply as Copilot', | ||
onClick: () => setShowApplyOpportunityModal(true), | ||
} | ||
|
||
const cancelCopilotOpportunityButton: ButtonProps = { | ||
label: 'Cancel opportunity', | ||
onClick: cancelCopilotOpportunityHandler, | ||
} | ||
|
||
const application = copilotApplications && copilotApplications[0] | ||
const isAlreadyMemberOfTheProject = profile && opportunity?.members?.includes(profile.userId) | ||
|
||
const getOpportunityType = (type: string): ProjectType => { | ||
switch (type) { | ||
case 'ai': | ||
return ProjectType.ai | ||
case 'datascience': | ||
return ProjectType.datascience | ||
case 'dev': | ||
return ProjectType.developement | ||
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. Typo in 'developement'. It should be 'development'. |
||
case 'design': | ||
return ProjectType.design | ||
default: | ||
return ProjectType.qa | ||
} | ||
} | ||
|
||
return ( | ||
<ContentLayout | ||
|
@@ -135,7 +167,11 @@ const CopilotOpportunityDetails: FC<{}> = () => { | |
&& copilotApplications | ||
&& copilotApplications.length === 0 | ||
&& opportunity?.status === 'active' | ||
&& !isAlreadyMemberOfTheProject ? applyCopilotOpportunityButton : undefined | ||
&& opportunity?.canApplyAsCopilot ? 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 adding a null check for |
||
} | ||
secondaryButtonConfig={ | ||
opportunity?.status === 'active' | ||
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 null check for |
||
&& isAdminOrPM ? cancelCopilotOpportunityButton : undefined | ||
} | ||
infoComponent={(isCopilot && !(copilotApplications | ||
&& copilotApplications.length === 0 | ||
|
@@ -156,94 +192,101 @@ const CopilotOpportunityDetails: FC<{}> = () => { | |
{isValidating && !showNotFound && ( | ||
<LoadingSpinner /> | ||
) } | ||
<h1 className={styles.header}> | ||
{opportunity?.projectName} | ||
</h1> | ||
<div className={styles.infoRow}> | ||
<div className={styles.infoColumn}> | ||
<IconOutline.ClipboardCheckIcon className={styles.icon} /> | ||
<div className={styles.infoText}> | ||
<span className={styles.infoHeading}>Status</span> | ||
<span className={styles.infoValue}>{opportunity?.status}</span> | ||
<div className={styles.wrapper}> | ||
<h1 className={styles.header}> | ||
{opportunity?.projectName} | ||
</h1> | ||
<div className={styles.infoRow}> | ||
<div className={styles.infoColumn}> | ||
<IconOutline.ClipboardCheckIcon className={styles.icon} /> | ||
<div className={styles.infoText}> | ||
<span className={styles.infoHeading}>Status</span> | ||
<span className={styles.infoValue}>{opportunity?.status}</span> | ||
</div> | ||
</div> | ||
</div> | ||
<div className={styles.infoColumn}> | ||
<IconOutline.PlayIcon className={styles.icon} /> | ||
<div className={styles.infoText}> | ||
<span className={styles.infoHeading}>Start Date</span> | ||
<span className={styles.infoValue}> | ||
{moment(opportunity?.startDate) | ||
.format('MMM D, YYYY')} | ||
|
||
</span> | ||
<div className={styles.infoColumn}> | ||
<IconOutline.PlayIcon className={styles.icon} /> | ||
<div className={styles.infoText}> | ||
<span className={styles.infoHeading}>Start Date</span> | ||
<span className={styles.infoValue}> | ||
{moment(opportunity?.startDate) | ||
.format('MMM D, YYYY')} | ||
|
||
</span> | ||
</div> | ||
</div> | ||
</div> | ||
<div className={styles.infoColumn}> | ||
<IconOutline.CalendarIcon className={styles.icon} /> | ||
<div className={styles.infoText}> | ||
<span className={styles.infoHeading}>Duration</span> | ||
<span className={styles.infoValue}> | ||
{opportunity?.numWeeks} | ||
{' '} | ||
weeks | ||
</span> | ||
<div className={styles.infoColumn}> | ||
<IconOutline.CalendarIcon className={styles.icon} /> | ||
<div className={styles.infoText}> | ||
<span className={styles.infoHeading}>Duration</span> | ||
<span className={styles.infoValue}> | ||
{opportunity?.numWeeks} | ||
{' '} | ||
weeks | ||
</span> | ||
</div> | ||
</div> | ||
</div> | ||
<div className={styles.infoColumn}> | ||
<IconOutline.ClockIcon className={styles.icon} /> | ||
<div className={styles.infoText}> | ||
<span className={styles.infoHeading}>Hours</span> | ||
<span className={styles.infoValue}> | ||
{opportunity?.numHoursPerWeek} | ||
{' '} | ||
hours/week | ||
</span> | ||
<div className={styles.infoColumn}> | ||
<IconOutline.ClockIcon className={styles.icon} /> | ||
<div className={styles.infoText}> | ||
<span className={styles.infoHeading}>Hours</span> | ||
<span className={styles.infoValue}> | ||
{opportunity?.numHoursPerWeek} | ||
{' '} | ||
hours/week | ||
</span> | ||
</div> | ||
</div> | ||
</div> | ||
<div className={styles.infoColumn}> | ||
<IconOutline.CogIcon className={styles.icon} /> | ||
<div className={styles.infoText}> | ||
<span className={styles.infoHeading}>Type</span> | ||
<span className={styles.infoValue}>{opportunity?.type}</span> | ||
<div className={styles.infoColumn}> | ||
<IconOutline.CogIcon className={styles.icon} /> | ||
<div className={styles.infoText}> | ||
<span className={styles.infoHeading}>Type</span> | ||
<span | ||
className={styles.infoValue} | ||
> | ||
{opportunity?.type && getOpportunityType(opportunity?.type)} | ||
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 using a consistent approach for rendering the |
||
</span> | ||
</div> | ||
</div> | ||
</div> | ||
<div className={styles.infoColumn}> | ||
<IconOutline.GlobeAltIcon className={styles.icon} /> | ||
<div className={styles.infoText}> | ||
<span className={styles.infoHeading}>Working Hours</span> | ||
<span className={styles.infoValue}>{opportunity?.tzRestrictions}</span> | ||
<div className={styles.infoColumn}> | ||
<IconOutline.GlobeAltIcon className={styles.icon} /> | ||
<div className={styles.infoText}> | ||
<span className={styles.infoHeading}>Working Hours</span> | ||
<span className={styles.infoValue}>{opportunity?.tzRestrictions}</span> | ||
</div> | ||
</div> | ||
</div> | ||
</div> | ||
{ | ||
initialized && ( | ||
<TabsNavbar | ||
defaultActive={activeTab} | ||
onChange={handleTabChange} | ||
tabs={getCopilotDetailsTabsConfig(isAdminOrPM)} | ||
{ | ||
initialized && ( | ||
<TabsNavbar | ||
defaultActive={activeTab} | ||
onChange={handleTabChange} | ||
tabs={getCopilotDetailsTabsConfig(isAdminOrPM)} | ||
/> | ||
) | ||
} | ||
{activeTab === CopilotDetailsTabViews.details && <OpportunityDetails opportunity={opportunity} />} | ||
{activeTab === CopilotDetailsTabViews.applications && isAdminOrPM && opportunity && ( | ||
<CopilotApplications | ||
copilotApplications={copilotApplications} | ||
opportunity={opportunity} | ||
members={members} | ||
/> | ||
) | ||
} | ||
{activeTab === CopilotDetailsTabViews.details && <OpportunityDetails opportunity={opportunity} />} | ||
{activeTab === CopilotDetailsTabViews.applications && isAdminOrPM && opportunity && ( | ||
<CopilotApplications | ||
copilotApplications={copilotApplications} | ||
opportunity={opportunity} | ||
members={members} | ||
/> | ||
)} | ||
)} | ||
|
||
{ | ||
showApplyOpportunityModal | ||
&& opportunity && ( | ||
<ApplyOpportunityModal | ||
copilotOpportunityId={opportunity?.id} | ||
onClose={onCloseApplyModal} | ||
projectName={opportunity?.projectName} | ||
onApplied={onApplied} | ||
/> | ||
) | ||
} | ||
</div> | ||
|
||
{ | ||
showApplyOpportunityModal | ||
&& opportunity && ( | ||
<ApplyOpportunityModal | ||
copilotOpportunityId={opportunity?.id} | ||
onClose={onCloseApplyModal} | ||
projectName={opportunity?.projectName} | ||
onApplied={onApplied} | ||
/> | ||
) | ||
} | ||
</ContentLayout> | ||
) | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,7 +15,8 @@ const CopilotApplicationAction = ( | |
): JSX.Element => { | ||
const { opportunityId }: {opportunityId?: string} = useParams<{ opportunityId?: string }>() | ||
const isInvited = useMemo( | ||
() => allCopilotApplications.findIndex(item => item.status === CopilotApplicationStatus.INVITED) > -1, | ||
() => allCopilotApplications | ||
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 using optional chaining ( |
||
&& allCopilotApplications.findIndex(item => item.status === CopilotApplicationStatus.INVITED) > -1, | ||
[allCopilotApplications], | ||
) | ||
const onClick = useCallback(async () => { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,11 +11,8 @@ const OpportunityDetails: FC<{ | |
<div> | ||
<h2 className={styles.subHeading}> Required skills </h2> | ||
<div className={styles.skillsContainer}> | ||
{props.opportunity?.skills.map((skill: any) => ( | ||
<div key={skill.id} className={styles.skillPill}> | ||
{skill.name} | ||
</div> | ||
))} | ||
{props.opportunity?.skills.map(item => item.name) | ||
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 using a more descriptive variable name instead of |
||
.join(',')} | ||
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 current implementation joins skill names into a single string separated by commas. If the intention is to display skills as a list, consider using a list or similar structure to maintain the previous visual separation. |
||
</div> | ||
<h2 className={styles.subHeading}> Description </h2> | ||
<p> | ||
|
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.
The type of the
members
property has been changed tocanApplyAsCopilot
, which is a boolean. Ensure that this change is intentional and that all references tomembers
in the codebase are updated accordingly to prevent any runtime errors.