-
Notifications
You must be signed in to change notification settings - Fork 16
[PROD] - Copilot Portal Fixes & Updates #1158
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
57a6076
bc1f9b4
4326b8d
f9328c1
f8efdd2
cc980c6
8d519f4
8968136
42fc635
530a90a
077ba3b
281e47d
7d06d63
2b67683
d505fbb
9eb0db8
472569b
b9353f0
e6bb39b
e7deb62
596ca36
8184aab
b784334
4519c74
2ead2c1
cfc2d1e
8bb8aba
ea3a141
11dc023
c781613
d9a1937
8d7cb57
bab6dc0
e22f459
7d2b011
4018c11
699f786
35bb492
c26a666
e1b02ab
6a38cb6
02e5b80
0e27236
d5d3e08
dbf2f69
571a3e7
8c44464
5deb954
d684c86
fa3371c
5d50793
86ad5b6
3311700
10dc17d
72145a5
dbaf5aa
63215ce
395bf68
2639b6b
f6011b2
8ac36ce
39685e5
748c5a2
fcb1d21
4282776
82920dc
94afdd5
fbe6eff
82c5f6a
4c33ab2
ba2fa07
61e08d4
c999669
dc4d07c
536bbdc
75c5608
2383f98
b62d5c7
f26509e
984aada
3a9b115
c58c4e9
5b0abec
4da8cb0
ef6d0cd
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 |
---|---|---|
|
@@ -4,4 +4,13 @@ | |
.info { | ||
margin-bottom: 12px; | ||
} | ||
|
||
&:global(.react-responsive-modal-modal) { | ||
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 class name instead of |
||
|
||
:global(.modal-body) { | ||
@include ltemd { | ||
height: 100%; | ||
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 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. Add a newline at the end of the file to adhere to best practices and avoid potential issues with some tools. |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -65,6 +65,10 @@ const CopilotOpportunityDetails: FC<{}> = () => { | |
[profile], | ||
) | ||
const { data: copilotApplications }: { data?: CopilotApplication[] } = useCopilotApplications(opportunityId) | ||
const appliedCopilotApplications = useMemo( | ||
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 |
||
() => copilotApplications?.filter(item => item.userId === profile?.userId), | ||
[copilotApplications, profile], | ||
) | ||
const { data: members }: { data?: FormattedMembers[]} = useMembers( | ||
copilotApplications ? copilotApplications?.map(item => item.userId) : [], | ||
) | ||
|
@@ -95,13 +99,16 @@ const CopilotOpportunityDetails: FC<{}> = () => { | |
}, [getHashFromTabId, setActiveTab]) | ||
|
||
useEffect(() => { | ||
if (opportunity) { | ||
setShowNotFound(false) | ||
return undefined | ||
} | ||
|
||
const timer = setTimeout(() => { | ||
if (!opportunity) { | ||
setShowNotFound(true) | ||
} | ||
}, 2000) | ||
setShowNotFound(true) | ||
}, 1000) | ||
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 timeout duration has been reduced from 2000ms to 1000ms. Ensure that this change does not negatively impact the user experience, as it may cause the 'not found' message to appear too quickly if there is a delay in fetching the opportunity data. |
||
|
||
return () => clearTimeout(timer) // Cleanup on unmount | ||
return () => clearTimeout(timer) | ||
}, [opportunity]) | ||
|
||
const onApplied: () => void = useCallback(() => { | ||
|
@@ -113,6 +120,14 @@ const CopilotOpportunityDetails: FC<{}> = () => { | |
setShowApplyOpportunityModal(false) | ||
}, [setShowApplyOpportunityModal]) | ||
|
||
if (isValidating && !opportunity) { | ||
return ( | ||
<ContentLayout title='Copilot Opportunity Details'> | ||
<LoadingSpinner /> | ||
</ContentLayout> | ||
) | ||
} | ||
|
||
if (!opportunity && showNotFound) { | ||
return ( | ||
<ContentLayout title='Copilot Opportunity Details'> | ||
|
@@ -164,17 +179,17 @@ const CopilotOpportunityDetails: FC<{}> = () => { | |
title='Copilot Opportunity' | ||
buttonConfig={ | ||
isCopilot | ||
&& copilotApplications | ||
&& copilotApplications.length === 0 | ||
&& appliedCopilotApplications | ||
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 variable |
||
&& appliedCopilotApplications.length === 0 | ||
&& opportunity?.status === 'active' | ||
&& opportunity?.canApplyAsCopilot ? applyCopilotOpportunityButton : undefined | ||
? applyCopilotOpportunityButton : undefined | ||
} | ||
secondaryButtonConfig={ | ||
opportunity?.status === 'active' | ||
&& isAdminOrPM ? cancelCopilotOpportunityButton : undefined | ||
} | ||
infoComponent={(isCopilot && !(copilotApplications | ||
&& copilotApplications.length === 0 | ||
infoComponent={(isCopilot && !(appliedCopilotApplications | ||
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 variable |
||
&& appliedCopilotApplications.length === 0 | ||
) && opportunity?.status === 'active' && !!application) && ( | ||
<div className={styles.applied}> | ||
<IconSolid.CheckCircleIcon className={styles.appliedIcon} /> | ||
|
@@ -194,7 +209,7 @@ const CopilotOpportunityDetails: FC<{}> = () => { | |
) } | ||
<div className={styles.wrapper}> | ||
<h1 className={styles.header}> | ||
{opportunity?.projectName} | ||
{opportunity?.opportunityTitle ?? opportunity?.projectName} | ||
</h1> | ||
<div className={styles.infoRow}> | ||
<div className={styles.infoColumn}> | ||
|
@@ -255,6 +270,16 @@ const CopilotOpportunityDetails: FC<{}> = () => { | |
<span className={styles.infoValue}>{opportunity?.tzRestrictions}</span> | ||
</div> | ||
</div> | ||
<div className={styles.infoColumn}> | ||
<IconOutline.CashIcon className={styles.icon} /> | ||
<div className={styles.infoText}> | ||
<span className={styles.infoHeading}>Payment</span> | ||
<span className={styles.infoValue}> | ||
{opportunity?.paymentType === 'standard' | ||
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 fallback value or handling the case where |
||
? opportunity.paymentType : opportunity?.otherPaymentType} | ||
</span> | ||
</div> | ||
</div> | ||
</div> | ||
{ | ||
initialized && ( | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
/* eslint-disable react/jsx-no-bind */ | ||
import { FC } from 'react' | ||
|
||
import { BaseModal, Button } from '~/libs/ui' | ||
import { CopilotApplication } from '~/apps/copilots/src/models/CopilotApplication' | ||
|
||
import styles from './styles.module.scss' | ||
|
||
interface AlreadyMemberModalProps { | ||
onClose: (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void | ||
copilotApplication: CopilotApplication | ||
handle?: string | ||
onApply: () => void | ||
projectName: string | ||
} | ||
|
||
const AlreadyMemberModal: FC<AlreadyMemberModalProps> = props => ( | ||
<BaseModal | ||
onClose={props.onClose as () => 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 |
||
open | ||
size='lg' | ||
title='User already member of the project' | ||
buttons={( | ||
<> | ||
<Button primary onClick={props.onApply} label='Confirm' /> | ||
<Button secondary onClick={props.onClose} label='Cancel' /> | ||
</> | ||
)} | ||
> | ||
<div className={styles.applyCopilotModal}> | ||
<div className={styles.info}> | ||
{`The copilot ${props.handle} is part of ${props.projectName} | ||
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 template literal for the entire string to avoid unnecessary line breaks and improve readability. The current formatting may lead to unexpected spaces in the rendered text. |
||
project with ${props.copilotApplication.existingMembership?.role} role.`} | ||
|
||
{ | ||
props.copilotApplication.existingMembership | ||
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 for |
||
&& ['copilot', 'manager'].includes(props.copilotApplication.existingMembership.role) | ||
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 logic here checks for specific roles. Ensure that the roles are correctly defined and consistent with the application's role management system. |
||
&& <div>Click 'Confirm' to accept and complete this opportunity.</div> | ||
} | ||
|
||
{ | ||
props.copilotApplication.existingMembership | ||
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 logic here checks for specific roles. Ensure that the roles are correctly defined and consistent with the application's role management system. |
||
&& ['observer', 'customer'].includes(props.copilotApplication.existingMembership.role) | ||
&& ( | ||
<div> | ||
Click 'Confirm' to accept by updating project role to 'Copilot' | ||
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 rephrasing the message for clarity. For example, 'Click 'Confirm' to update the project role to 'Copilot' and complete this opportunity.' |
||
and complete this opportunity | ||
</div> | ||
) | ||
} | ||
</div> | ||
</div> | ||
</BaseModal> | ||
) | ||
|
||
export default AlreadyMemberModal |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,19 +1,21 @@ | ||
import { useParams } from 'react-router-dom' | ||
import { toast } from 'react-toastify' | ||
import { mutate } from 'swr' | ||
import { useCallback, useMemo } from 'react' | ||
import { useCallback, useMemo, useState } from 'react' | ||
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 { assignCopilotOpportunity, copilotBaseUrl } from '~/apps/copilots/src/services/copilot-opportunities' | ||
import { CopilotApplication, CopilotApplicationStatus } from '~/apps/copilots/src/models/CopilotApplication' | ||
import { IconSolid, Tooltip } from '~/libs/ui' | ||
|
||
import AlreadyMemberModal from './AlreadyMemberModal' | ||
import styles from './styles.module.scss' | ||
|
||
const CopilotApplicationAction = ( | ||
copilotApplication: CopilotApplication, | ||
allCopilotApplications: CopilotApplication[], | ||
): JSX.Element => { | ||
const { opportunityId }: {opportunityId?: string} = useParams<{ opportunityId?: string }>() | ||
const [showAlreadyMemberModal, setShowAlreadyMemberModal] = useState(false) | ||
const isInvited = useMemo( | ||
() => allCopilotApplications | ||
&& allCopilotApplications.findIndex(item => item.status === CopilotApplicationStatus.INVITED) > -1, | ||
|
@@ -28,6 +30,11 @@ const CopilotApplicationAction = ( | |
return | ||
} | ||
|
||
if (copilotApplication.existingMembership) { | ||
setShowAlreadyMemberModal(true) | ||
return | ||
} | ||
|
||
if (opportunityId) { | ||
try { | ||
await assignCopilotOpportunity(opportunityId, copilotApplication.id) | ||
|
@@ -40,6 +47,29 @@ const CopilotApplicationAction = ( | |
|
||
} | ||
}, [opportunityId, copilotApplication]) | ||
|
||
const onApply = useCallback(async () => { | ||
try { | ||
if (!opportunityId) { | ||
return | ||
} | ||
|
||
await assignCopilotOpportunity(opportunityId, copilotApplication.id) | ||
toast.success('Accepted as copilot') | ||
mutate(`${copilotBaseUrl}/copilots/opportunity/${opportunityId}/applications`) | ||
setShowAlreadyMemberModal(false) | ||
} catch (e) { | ||
const error = e as Error | ||
toast.error(error.message) | ||
} | ||
}, [opportunityId, copilotApplication]) | ||
|
||
const onCloseModal = useCallback((e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => { | ||
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 dependency array for |
||
e.preventDefault() | ||
e.stopPropagation() | ||
setShowAlreadyMemberModal(false) | ||
}, [showAlreadyMemberModal]) | ||
|
||
return ( | ||
<div onClick={onClick} className={styles.actionWrapper}> | ||
{ | ||
|
@@ -67,6 +97,16 @@ const CopilotApplicationAction = ( | |
</Tooltip> | ||
) | ||
} | ||
|
||
{showAlreadyMemberModal && ( | ||
<AlreadyMemberModal | ||
projectName={copilotApplication.projectName} | ||
handle={copilotApplication.handle} | ||
onClose={onCloseModal} | ||
onApply={onApply} | ||
copilotApplication={copilotApplication} | ||
/> | ||
)} | ||
</div> | ||
) | ||
} | ||
|
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 providing a default value for
existingMembership
to ensure consistent behavior when this property is not explicitly set.