Skip to content

fix(PM-1373, PM-1375, PM-1376, PM-1383, PM-1380, PM-1378): demo feedbacks implementation #1116

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

Merged
merged 8 commits into from
Jun 19, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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 ? (
Expand All @@ -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
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'

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The input field name has been changed from 'Notes' to 'Reason'. Ensure that this change is reflected consistently throughout the codebase, including any form handling logic or validation that may rely on the field name.

onChange={onChange}
value={notes}
error={error}
Expand Down
190 changes: 107 additions & 83 deletions src/apps/copilots/src/pages/copilot-opportunity-details/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
} from 'react'
import { useLocation, useNavigate, useParams } from 'react-router-dom'
import { mutate } from 'swr'
import { toast } from 'react-toastify'
import moment from 'moment'

import {
Expand All @@ -28,6 +29,7 @@ import { textFormatDateLocaleShortString } from '~/libs/shared'

import { CopilotApplication } from '../../models/CopilotApplication'
import {
cancelCopilotOpportunity,

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The function cancelCopilotOpportunity is imported but not used in this file. Consider removing it if it's not needed.

copilotBaseUrl,
CopilotOpportunityResponse,
useCopilotApplications,
Expand Down Expand Up @@ -104,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}`)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The URL path has been changed from 'copilots/opportunity' to 'copilot/opportunity'. Please ensure that this change is intentional and that the new path is correct and consistent with the API endpoint.

}, [])

const onCloseApplyModal: () => void = useCallback(() => {
Expand All @@ -120,11 +122,26 @@ const CopilotOpportunityDetails: FC<{}> = () => {
)
}

async function cancelCopilotOpportunityHandler(): Promise<void> {
if (opportunityId) {
await cancelCopilotOpportunity(opportunityId)
mutate(`${copilotBaseUrl}/copilots/opportunity/${opportunityId}/applications`)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The mutate function is called twice with similar URLs. Consider extracting the base URL and opportunity ID into a variable to avoid repetition and improve readability.

mutate(`${copilotBaseUrl}/copilot/opportunity/${opportunityId}`)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The URL path in the mutate function call has been changed from copilots to copilot. Ensure that this change is intentional and that the new path is correct, as it might affect the data fetching logic.

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 getOpportunityType = (type: string): ProjectType => {
Expand Down Expand Up @@ -152,6 +169,10 @@ const CopilotOpportunityDetails: FC<{}> = () => {
&& opportunity?.status === 'active'
&& opportunity?.canApplyAsCopilot ? applyCopilotOpportunityButton : undefined
}
secondaryButtonConfig={
opportunity?.status === 'active'
&& isAdminOrPM ? cancelCopilotOpportunityButton : undefined
}
infoComponent={(isCopilot && !(copilotApplications
&& copilotApplications.length === 0
) && opportunity?.status === 'active' && !!application) && (
Expand All @@ -171,98 +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')}
<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>
</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 && getOpportunityType(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)}
</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>
)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
@import '@libs/ui/styles/includes';

.wrapper {
min-height: 800px;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider using a more flexible unit for min-height, such as vh or %, to ensure better responsiveness across different screen sizes.

}

.header {
display: flex;
align-items: center;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
&& allCopilotApplications.findIndex(item => item.status === CopilotApplicationStatus.INVITED) > -1,
[allCopilotApplications],
)
const onClick = useCallback(async () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
.join(',')}
</div>
<h2 className={styles.subHeading}> Description </h2>
<p>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,11 @@ const tableColumns: TableColumn<CopilotOpportunity>[] = [
propertyName: 'startDate',
type: 'date',
},
{
label: 'Duration(Weeks)',
propertyName: 'numWeeks',
type: 'text',
},
{
label: 'Complexity',
propertyName: 'complexity',
Expand Down
Loading