Skip to content

Commit d920ef4

Browse files
authored
Merge pull request #1158 from topcoder-platform/dev
[PROD] - Copilot Portal Fixes & Updates
2 parents 7d2c9f9 + ef6d0cd commit d920ef4

File tree

36 files changed

+632
-66
lines changed

36 files changed

+632
-66
lines changed

.circleci/config.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,7 @@ workflows:
222222
- LVT-256
223223
- CORE-635
224224
- feat/system-admin
225-
- pm-1448_1
225+
- pm-1365_1
226226

227227
- deployQa:
228228
context: org-global

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
"scripts": {
77
"dev": "craco start --mode ${LOGICAL_ENV:-dev}",
88
"start": "bash start.sh",
9-
"build": "export CI=false && craco build --mode ${LOGICAL_ENV:-prod}",
9+
"build": "rimraf ./build && export CI=false && craco build --mode ${LOGICAL_ENV:-prod}",
1010
"build:dev": "craco build --mode ${LOGICAL_ENV:-dev}",
1111
"demo": "npx http-server --port 443 -a 0.0.0.0 -S -C ./ssl/rootCA.crt -K ./ssl/rootCA.key -P https://local.topcoder-dev.com? --proxy-options.secure false ./build",
1212
"lint": "eslint -c ./src/.eslintrc.js 'src/**/*.{ts,tsx,js,jsx}'",
@@ -189,6 +189,7 @@
189189
"react-docgen-typescript": "^2.2.2",
190190
"react-hot-loader": "^4.3.3",
191191
"resolve-url-loader": "^5.0.0",
192+
"rimraf": "^6.0.1",
192193
"sass-loader": "^13.3.3",
193194
"serve": "^14.0.1",
194195
"start-server-and-test": "^1.14.0",

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,13 @@ export const childRoutes = [
3535
rolesRequired: [UserRole.administrator, UserRole.projectManager] as UserRole[],
3636
route: '/requests/new',
3737
},
38+
{
39+
authRequired: true,
40+
element: <CopilotsRequestForm />,
41+
id: 'CopilotRequestEditForm',
42+
rolesRequired: [UserRole.administrator, UserRole.projectManager] as UserRole[],
43+
route: '/requests/edit/:requestId',
44+
},
3845
{
3946
authRequired: true,
4047
element: <CopilotsRequests />,

src/apps/copilots/src/models/CopilotApplication.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@ export enum CopilotApplicationStatus {
44
PENDING = 'pending',
55
}
66

7+
export interface ExistingMembership {
8+
role: string,
9+
id: number,
10+
}
11+
712
export interface CopilotApplication {
813
id: number,
914
notes?: string,
@@ -13,4 +18,6 @@ export interface CopilotApplication {
1318
userId: number,
1419
status: CopilotApplicationStatus,
1520
opportunityStatus: string,
21+
existingMembership?: ExistingMembership,
22+
projectName: string,
1623
}

src/apps/copilots/src/models/CopilotOpportunity.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,12 @@ export interface CopilotOpportunity {
1515
numHoursPerWeek: number,
1616
numWeeks: number,
1717
overview: string,
18+
opportunityTitle: string,
1819
paymentType: string,
1920
otherPaymentType: string,
2021
requiresCommunication: 'yes' | 'no',
2122
skills: UserSkill[],
2223
startDate: Date,
2324
tzRestrictions: 'yes' | 'no',
2425
createdAt: Date,
25-
canApplyAsCopilot: boolean,
2626
}

src/apps/copilots/src/models/CopilotRequest.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ export interface CopilotRequest {
1313
numHoursPerWeek: number,
1414
numWeeks: number,
1515
overview: string,
16+
opportunityTitle: string,
1617
paymentType: string,
1718
otherPaymentType: string,
1819
requiresCommunication: 'yes' | 'no',

src/apps/copilots/src/pages/copilot-opportunity-details/apply-opportunity-modal/styles.module.scss

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,13 @@
44
.info {
55
margin-bottom: 12px;
66
}
7+
8+
&:global(.react-responsive-modal-modal) {
9+
10+
:global(.modal-body) {
11+
@include ltemd {
12+
height: 100%;
13+
}
14+
}
15+
}
716
}

src/apps/copilots/src/pages/copilot-opportunity-details/index.tsx

Lines changed: 36 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,10 @@ const CopilotOpportunityDetails: FC<{}> = () => {
6565
[profile],
6666
)
6767
const { data: copilotApplications }: { data?: CopilotApplication[] } = useCopilotApplications(opportunityId)
68+
const appliedCopilotApplications = useMemo(
69+
() => copilotApplications?.filter(item => item.userId === profile?.userId),
70+
[copilotApplications, profile],
71+
)
6872
const { data: members }: { data?: FormattedMembers[]} = useMembers(
6973
copilotApplications ? copilotApplications?.map(item => item.userId) : [],
7074
)
@@ -95,13 +99,16 @@ const CopilotOpportunityDetails: FC<{}> = () => {
9599
}, [getHashFromTabId, setActiveTab])
96100

97101
useEffect(() => {
102+
if (opportunity) {
103+
setShowNotFound(false)
104+
return undefined
105+
}
106+
98107
const timer = setTimeout(() => {
99-
if (!opportunity) {
100-
setShowNotFound(true)
101-
}
102-
}, 2000)
108+
setShowNotFound(true)
109+
}, 1000)
103110

104-
return () => clearTimeout(timer) // Cleanup on unmount
111+
return () => clearTimeout(timer)
105112
}, [opportunity])
106113

107114
const onApplied: () => void = useCallback(() => {
@@ -113,6 +120,14 @@ const CopilotOpportunityDetails: FC<{}> = () => {
113120
setShowApplyOpportunityModal(false)
114121
}, [setShowApplyOpportunityModal])
115122

123+
if (isValidating && !opportunity) {
124+
return (
125+
<ContentLayout title='Copilot Opportunity Details'>
126+
<LoadingSpinner />
127+
</ContentLayout>
128+
)
129+
}
130+
116131
if (!opportunity && showNotFound) {
117132
return (
118133
<ContentLayout title='Copilot Opportunity Details'>
@@ -164,17 +179,17 @@ const CopilotOpportunityDetails: FC<{}> = () => {
164179
title='Copilot Opportunity'
165180
buttonConfig={
166181
isCopilot
167-
&& copilotApplications
168-
&& copilotApplications.length === 0
182+
&& appliedCopilotApplications
183+
&& appliedCopilotApplications.length === 0
169184
&& opportunity?.status === 'active'
170-
&& opportunity?.canApplyAsCopilot ? applyCopilotOpportunityButton : undefined
185+
? applyCopilotOpportunityButton : undefined
171186
}
172187
secondaryButtonConfig={
173188
opportunity?.status === 'active'
174189
&& isAdminOrPM ? cancelCopilotOpportunityButton : undefined
175190
}
176-
infoComponent={(isCopilot && !(copilotApplications
177-
&& copilotApplications.length === 0
191+
infoComponent={(isCopilot && !(appliedCopilotApplications
192+
&& appliedCopilotApplications.length === 0
178193
) && opportunity?.status === 'active' && !!application) && (
179194
<div className={styles.applied}>
180195
<IconSolid.CheckCircleIcon className={styles.appliedIcon} />
@@ -194,7 +209,7 @@ const CopilotOpportunityDetails: FC<{}> = () => {
194209
) }
195210
<div className={styles.wrapper}>
196211
<h1 className={styles.header}>
197-
{opportunity?.projectName}
212+
{opportunity?.opportunityTitle ?? opportunity?.projectName}
198213
</h1>
199214
<div className={styles.infoRow}>
200215
<div className={styles.infoColumn}>
@@ -255,6 +270,16 @@ const CopilotOpportunityDetails: FC<{}> = () => {
255270
<span className={styles.infoValue}>{opportunity?.tzRestrictions}</span>
256271
</div>
257272
</div>
273+
<div className={styles.infoColumn}>
274+
<IconOutline.CashIcon className={styles.icon} />
275+
<div className={styles.infoText}>
276+
<span className={styles.infoHeading}>Payment</span>
277+
<span className={styles.infoValue}>
278+
{opportunity?.paymentType === 'standard'
279+
? opportunity.paymentType : opportunity?.otherPaymentType}
280+
</span>
281+
</div>
282+
</div>
258283
</div>
259284
{
260285
initialized && (

src/apps/copilots/src/pages/copilot-opportunity-details/styles.module.scss

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,12 @@
1717
color: $teal-100;
1818
}
1919

20+
@media (max-width: 767px) {
21+
.header {
22+
line-height: 48px;
23+
}
24+
}
25+
2026

2127
.infoRow {
2228
display: flex;
@@ -27,6 +33,13 @@
2733
padding: 12px 0;
2834
}
2935

36+
@media (max-width: 767px) {
37+
.infoRow {
38+
flex-wrap: wrap;
39+
margin: 0;
40+
}
41+
}
42+
3043
.infoText {
3144
display: flex;
3245
flex-direction: column;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/* eslint-disable react/jsx-no-bind */
2+
import { FC } from 'react'
3+
4+
import { BaseModal, Button } from '~/libs/ui'
5+
import { CopilotApplication } from '~/apps/copilots/src/models/CopilotApplication'
6+
7+
import styles from './styles.module.scss'
8+
9+
interface AlreadyMemberModalProps {
10+
onClose: (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void
11+
copilotApplication: CopilotApplication
12+
handle?: string
13+
onApply: () => void
14+
projectName: string
15+
}
16+
17+
const AlreadyMemberModal: FC<AlreadyMemberModalProps> = props => (
18+
<BaseModal
19+
onClose={props.onClose as () => void}
20+
open
21+
size='lg'
22+
title='User already member of the project'
23+
buttons={(
24+
<>
25+
<Button primary onClick={props.onApply} label='Confirm' />
26+
<Button secondary onClick={props.onClose} label='Cancel' />
27+
</>
28+
)}
29+
>
30+
<div className={styles.applyCopilotModal}>
31+
<div className={styles.info}>
32+
{`The copilot ${props.handle} is part of ${props.projectName}
33+
project with ${props.copilotApplication.existingMembership?.role} role.`}
34+
35+
{
36+
props.copilotApplication.existingMembership
37+
&& ['copilot', 'manager'].includes(props.copilotApplication.existingMembership.role)
38+
&& <div>Click &apos;Confirm&apos; to accept and complete this opportunity.</div>
39+
}
40+
41+
{
42+
props.copilotApplication.existingMembership
43+
&& ['observer', 'customer'].includes(props.copilotApplication.existingMembership.role)
44+
&& (
45+
<div>
46+
Click &apos;Confirm&apos; to accept by updating project role to &apos;Copilot&apos;
47+
and complete this opportunity
48+
</div>
49+
)
50+
}
51+
</div>
52+
</div>
53+
</BaseModal>
54+
)
55+
56+
export default AlreadyMemberModal

0 commit comments

Comments
 (0)