diff --git a/.circleci/config.yml b/.circleci/config.yml index be36cb2f0..93ebaa7d6 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -222,7 +222,7 @@ workflows: - LVT-256 - CORE-635 - feat/system-admin - - pm-1448_1 + - pm-1365_1 - deployQa: context: org-global diff --git a/package.json b/package.json index 4836bfad5..99c00588a 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "scripts": { "dev": "craco start --mode ${LOGICAL_ENV:-dev}", "start": "bash start.sh", - "build": "export CI=false && craco build --mode ${LOGICAL_ENV:-prod}", + "build": "rimraf ./build && export CI=false && craco build --mode ${LOGICAL_ENV:-prod}", "build:dev": "craco build --mode ${LOGICAL_ENV:-dev}", "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", "lint": "eslint -c ./src/.eslintrc.js 'src/**/*.{ts,tsx,js,jsx}'", @@ -189,6 +189,7 @@ "react-docgen-typescript": "^2.2.2", "react-hot-loader": "^4.3.3", "resolve-url-loader": "^5.0.0", + "rimraf": "^6.0.1", "sass-loader": "^13.3.3", "serve": "^14.0.1", "start-server-and-test": "^1.14.0", diff --git a/src/apps/copilots/src/copilots.routes.tsx b/src/apps/copilots/src/copilots.routes.tsx index 1a3f9a9e4..ba479d965 100644 --- a/src/apps/copilots/src/copilots.routes.tsx +++ b/src/apps/copilots/src/copilots.routes.tsx @@ -35,6 +35,13 @@ export const childRoutes = [ rolesRequired: [UserRole.administrator, UserRole.projectManager] as UserRole[], route: '/requests/new', }, + { + authRequired: true, + element: , + id: 'CopilotRequestEditForm', + rolesRequired: [UserRole.administrator, UserRole.projectManager] as UserRole[], + route: '/requests/edit/:requestId', + }, { authRequired: true, element: , diff --git a/src/apps/copilots/src/models/CopilotApplication.ts b/src/apps/copilots/src/models/CopilotApplication.ts index a501980a2..bc380489c 100644 --- a/src/apps/copilots/src/models/CopilotApplication.ts +++ b/src/apps/copilots/src/models/CopilotApplication.ts @@ -4,6 +4,11 @@ export enum CopilotApplicationStatus { PENDING = 'pending', } +export interface ExistingMembership { + role: string, + id: number, +} + export interface CopilotApplication { id: number, notes?: string, @@ -13,4 +18,6 @@ export interface CopilotApplication { userId: number, status: CopilotApplicationStatus, opportunityStatus: string, + existingMembership?: ExistingMembership, + projectName: string, } diff --git a/src/apps/copilots/src/models/CopilotOpportunity.ts b/src/apps/copilots/src/models/CopilotOpportunity.ts index b53545377..da5040d32 100644 --- a/src/apps/copilots/src/models/CopilotOpportunity.ts +++ b/src/apps/copilots/src/models/CopilotOpportunity.ts @@ -15,6 +15,7 @@ export interface CopilotOpportunity { numHoursPerWeek: number, numWeeks: number, overview: string, + opportunityTitle: string, paymentType: string, otherPaymentType: string, requiresCommunication: 'yes' | 'no', @@ -22,5 +23,4 @@ export interface CopilotOpportunity { startDate: Date, tzRestrictions: 'yes' | 'no', createdAt: Date, - canApplyAsCopilot: boolean, } diff --git a/src/apps/copilots/src/models/CopilotRequest.ts b/src/apps/copilots/src/models/CopilotRequest.ts index e295bda9b..f4b4bd381 100644 --- a/src/apps/copilots/src/models/CopilotRequest.ts +++ b/src/apps/copilots/src/models/CopilotRequest.ts @@ -13,6 +13,7 @@ export interface CopilotRequest { numHoursPerWeek: number, numWeeks: number, overview: string, + opportunityTitle: string, paymentType: string, otherPaymentType: string, requiresCommunication: 'yes' | 'no', diff --git a/src/apps/copilots/src/pages/copilot-opportunity-details/apply-opportunity-modal/styles.module.scss b/src/apps/copilots/src/pages/copilot-opportunity-details/apply-opportunity-modal/styles.module.scss index 897a31420..c4f79481a 100644 --- a/src/apps/copilots/src/pages/copilot-opportunity-details/apply-opportunity-modal/styles.module.scss +++ b/src/apps/copilots/src/pages/copilot-opportunity-details/apply-opportunity-modal/styles.module.scss @@ -4,4 +4,13 @@ .info { margin-bottom: 12px; } + + &:global(.react-responsive-modal-modal) { + + :global(.modal-body) { + @include ltemd { + height: 100%; + } + } + } } \ No newline at end of file diff --git a/src/apps/copilots/src/pages/copilot-opportunity-details/index.tsx b/src/apps/copilots/src/pages/copilot-opportunity-details/index.tsx index 644e8c8cb..fe6197196 100644 --- a/src/apps/copilots/src/pages/copilot-opportunity-details/index.tsx +++ b/src/apps/copilots/src/pages/copilot-opportunity-details/index.tsx @@ -65,6 +65,10 @@ const CopilotOpportunityDetails: FC<{}> = () => { [profile], ) const { data: copilotApplications }: { data?: CopilotApplication[] } = useCopilotApplications(opportunityId) + const appliedCopilotApplications = useMemo( + () => 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) - 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 ( + + + + ) + } + if (!opportunity && showNotFound) { return ( @@ -164,17 +179,17 @@ const CopilotOpportunityDetails: FC<{}> = () => { title='Copilot Opportunity' buttonConfig={ isCopilot - && copilotApplications - && copilotApplications.length === 0 + && appliedCopilotApplications + && 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 + && appliedCopilotApplications.length === 0 ) && opportunity?.status === 'active' && !!application) && (
@@ -194,7 +209,7 @@ const CopilotOpportunityDetails: FC<{}> = () => { ) }

- {opportunity?.projectName} + {opportunity?.opportunityTitle ?? opportunity?.projectName}

@@ -255,6 +270,16 @@ const CopilotOpportunityDetails: FC<{}> = () => { {opportunity?.tzRestrictions}
+
+ +
+ Payment + + {opportunity?.paymentType === 'standard' + ? opportunity.paymentType : opportunity?.otherPaymentType} + +
+
{ initialized && ( diff --git a/src/apps/copilots/src/pages/copilot-opportunity-details/styles.module.scss b/src/apps/copilots/src/pages/copilot-opportunity-details/styles.module.scss index 9844231ae..1e82fa9ca 100644 --- a/src/apps/copilots/src/pages/copilot-opportunity-details/styles.module.scss +++ b/src/apps/copilots/src/pages/copilot-opportunity-details/styles.module.scss @@ -17,6 +17,12 @@ color: $teal-100; } +@media (max-width: 767px) { + .header { + line-height: 48px; + } +} + .infoRow { display: flex; @@ -27,6 +33,13 @@ padding: 12px 0; } +@media (max-width: 767px) { + .infoRow { + flex-wrap: wrap; + margin: 0; + } +} + .infoText { display: flex; flex-direction: column; diff --git a/src/apps/copilots/src/pages/copilot-opportunity-details/tabs/copilot-applications/AlreadyMemberModal.tsx b/src/apps/copilots/src/pages/copilot-opportunity-details/tabs/copilot-applications/AlreadyMemberModal.tsx new file mode 100644 index 000000000..9b06737c4 --- /dev/null +++ b/src/apps/copilots/src/pages/copilot-opportunity-details/tabs/copilot-applications/AlreadyMemberModal.tsx @@ -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) => void + copilotApplication: CopilotApplication + handle?: string + onApply: () => void + projectName: string +} + +const AlreadyMemberModal: FC = props => ( + void} + open + size='lg' + title='User already member of the project' + buttons={( + <> +