From c9cb23b3379fa957772245432abfef3feeed7a8e Mon Sep 17 00:00:00 2001 From: Brooke Date: Fri, 23 Dec 2022 12:40:33 -0800 Subject: [PATCH 1/3] TCA-852 #comment This commit adds the modal to tell wipro users they need to set up mfa w/dice #time 1h --- src-ts/.eslintrc.js | 6 +++ .../environment.default.config.ts | 1 + .../environments/environment.prod.config.ts | 1 + .../user-store/user-xhr.store.ts | 3 +- src-ts/lib/global-config.model.ts | 1 + .../course-curriculum/CourseCurriculum.tsx | 25 +++++++-- .../dice-modal/DiceModal.module.scss | 13 +++++ .../dice-modal/DiceModal.tsx | 53 +++++++++++++++++++ .../course-curriculum/dice-modal/index.ts | 1 + 9 files changed, 98 insertions(+), 6 deletions(-) create mode 100644 src-ts/tools/learn/course-details/course-curriculum/dice-modal/DiceModal.module.scss create mode 100644 src-ts/tools/learn/course-details/course-curriculum/dice-modal/DiceModal.tsx create mode 100644 src-ts/tools/learn/course-details/course-curriculum/dice-modal/index.ts diff --git a/src-ts/.eslintrc.js b/src-ts/.eslintrc.js index c54020a0f..10a2a1fb5 100644 --- a/src-ts/.eslintrc.js +++ b/src-ts/.eslintrc.js @@ -251,6 +251,12 @@ module.exports = { 2, 4, ], + 'react/jsx-no-bind': [ + 'error', + { + allowFunctions: true, + } + ], 'react/jsx-no-useless-fragment': [ 0 ], diff --git a/src-ts/config/environments/environment.default.config.ts b/src-ts/config/environments/environment.default.config.ts index c616a5f2d..525463eda 100644 --- a/src-ts/config/environments/environment.default.config.ts +++ b/src-ts/config/environments/environment.default.config.ts @@ -36,6 +36,7 @@ export const EnvironmentConfigDefault: EnvironmentConfigModel = { 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJyb2xlcyI6WyJUb3Bjb2RlciBVc2VyIl0sImlzcyI6Imh0dHBzOi8vYXBpLnRvcGNvZGVyLWRldi5jb20iLCJoYW5kbGUiOiJ0ZXN0MSIsImV4cCI6MjU2MzA3NjY4OSwidXNlcklkIjoiNDAwNTEzMzMiLCJpYXQiOjE0NjMwNzYwODksImVtYWlsIjoidGVzdEB0b3Bjb2Rlci5jb20iLCJqdGkiOiJiMzNiNzdjZC1iNTJlLTQwZmUtODM3ZS1iZWI4ZTBhZTZhNGEifQ.jl6Lp_friVNwEP8nfsfmL-vrQFzOFp2IfM_HC7AwGcg', }, TOPCODER_URLS: { + ACCOUNT_SETTINGS: `${COMMUNITY_WEBSITE}/settings/account`, API_BASE: `${COMMUNITY_WEBSITE}/api`, BLOG_PAGE: `${COMMUNITY_WEBSITE}/blog`, CHALLENGES_PAGE: `${COMMUNITY_WEBSITE}/challenges`, diff --git a/src-ts/config/environments/environment.prod.config.ts b/src-ts/config/environments/environment.prod.config.ts index da9a2b5e9..222edbb03 100644 --- a/src-ts/config/environments/environment.prod.config.ts +++ b/src-ts/config/environments/environment.prod.config.ts @@ -34,6 +34,7 @@ export const EnvironmentConfigProd: EnvironmentConfigModel = { 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJyb2xlcyI6WyJUb3Bjb2RlciBVc2VyIl0sImlzcyI6Imh0dHBzOi8vYXBpLnRvcGNvZGVyLWRldi5jb20iLCJoYW5kbGUiOiJ0ZXN0MSIsImV4cCI6MjU2MzA3NjY4OSwidXNlcklkIjoiNDAwNTEzMzMiLCJpYXQiOjE0NjMwNzYwODksImVtYWlsIjoidGVzdEB0b3Bjb2Rlci5jb20iLCJqdGkiOiJiMzNiNzdjZC1iNTJlLTQwZmUtODM3ZS1iZWI4ZTBhZTZhNGEifQ.jl6Lp_friVNwEP8nfsfmL-vrQFzOFp2IfM_HC7AwGcg', }, TOPCODER_URLS: { + ACCOUNT_SETTINGS: `${COMMUNITY_WEBSITE}/settings/account`, API_BASE: `${COMMUNITY_WEBSITE}/api`, BLOG_PAGE: `${COMMUNITY_WEBSITE}/blog`, CHALLENGES_PAGE: `${COMMUNITY_WEBSITE}/challenges`, diff --git a/src-ts/lib/functions/user-functions/user-store/user-xhr.store.ts b/src-ts/lib/functions/user-functions/user-store/user-xhr.store.ts index faf3951b2..8cb4e1cc2 100644 --- a/src-ts/lib/functions/user-functions/user-store/user-xhr.store.ts +++ b/src-ts/lib/functions/user-functions/user-store/user-xhr.store.ts @@ -17,6 +17,7 @@ export async function getDiceStatusAsync(userId: number): Promise { interface DiceStatusResult { result: { content: { + mfaEnabled: boolean diceEnabled: boolean } } @@ -24,7 +25,7 @@ export async function getDiceStatusAsync(userId: number): Promise { const result: DiceStatusResult = await xhrGetAsync(`${userEndpoint(userId)}/2fa`) - return !!result.result.content.diceEnabled + return !!result.result.content.mfaEnabled && !!result.result.content.diceEnabled } export async function patchAsync(userId: number, request: UserPatchRequest): Promise { diff --git a/src-ts/lib/global-config.model.ts b/src-ts/lib/global-config.model.ts index 911bc42ba..18820b175 100644 --- a/src-ts/lib/global-config.model.ts +++ b/src-ts/lib/global-config.model.ts @@ -29,6 +29,7 @@ export interface GlobalConfig { CUSTOMER_TOKEN: string } TOPCODER_URLS: { + ACCOUNT_SETTINGS: string API_BASE: string BLOG_PAGE: string CHALLENGES_PAGE: string diff --git a/src-ts/tools/learn/course-details/course-curriculum/CourseCurriculum.tsx b/src-ts/tools/learn/course-details/course-curriculum/CourseCurriculum.tsx index ab1d97727..c6ee2a0a5 100644 --- a/src-ts/tools/learn/course-details/course-curriculum/CourseCurriculum.tsx +++ b/src-ts/tools/learn/course-details/course-curriculum/CourseCurriculum.tsx @@ -24,6 +24,7 @@ import { import { CurriculumSummary } from './curriculum-summary' import { TcAcademyPolicyModal } from './tc-academy-policy-modal' +import { DiceModal } from './dice-modal' import styles from './CourseCurriculum.module.scss' interface CourseCurriculumProps { @@ -43,6 +44,8 @@ const CourseCurriculum: FC = (props: CourseCurriculumProp const [isTcAcademyPolicyModal, setIsTcAcademyPolicyModal]: [boolean, Dispatch>] = useState(false) + const [isDiceModalOpen, setIsDiceModalOpen]: [boolean, Dispatch>] + = useState(false) const status: string = props.progress?.status ?? UserCertificationProgressStatus.inititialized const completedPercentage: number = (props.progress?.courseProgressPercentage ?? 0) / 100 @@ -89,8 +92,7 @@ const CourseCurriculum: FC = (props: CourseCurriculumProp // if the user is wipro and s/he hasn't set up DICE, // let the user know if (props.profile?.isWipro && !props.profile.diceEnabled) { - // TODO - console.debug('TODO: user needs dice') + setIsDiceModalOpen(true) return } @@ -102,7 +104,7 @@ const CourseCurriculum: FC = (props: CourseCurriculumProp // show the academic policy modal before starting a new course setIsTcAcademyPolicyModal(true) - // eslint-disable-next-line react-hooks/exhaustive-deps + // eslint-disable-next-line react-hooks/exhaustive-deps }, [ handleStartCourse, isLoggedIn, @@ -141,7 +143,7 @@ const CourseCurriculum: FC = (props: CourseCurriculumProp } handleStartCourse() - // eslint-disable-next-line react-hooks/exhaustive-deps + // eslint-disable-next-line react-hooks/exhaustive-deps }, [ handleStartCourse, props.course.certificationId, @@ -167,6 +169,14 @@ const CourseCurriculum: FC = (props: CourseCurriculumProp } }, [handleStartCourseClick, isLoggedIn, props.progressReady, searchParams]) + function onAcademicHonestyModalClose(): void { + setIsTcAcademyPolicyModal(false) + } + + function onDiceModalClose(): void { + setIsDiceModalOpen(false) + } + return ( <>
@@ -211,9 +221,14 @@ const CourseCurriculum: FC = (props: CourseCurriculumProp setIsTcAcademyPolicyModal(false)} + onClose={onAcademicHonestyModalClose} onConfirm={handlePolicyAccept} /> + + ) } diff --git a/src-ts/tools/learn/course-details/course-curriculum/dice-modal/DiceModal.module.scss b/src-ts/tools/learn/course-details/course-curriculum/dice-modal/DiceModal.module.scss new file mode 100644 index 000000000..df1b5b572 --- /dev/null +++ b/src-ts/tools/learn/course-details/course-curriculum/dice-modal/DiceModal.module.scss @@ -0,0 +1,13 @@ +@import '../../../../../lib/styles/includes'; + +.diceModal { + + p { + margin-bottom: $space-lg; + + &.buttonContainer { + display: flex; + justify-content: center; + } + } +} diff --git a/src-ts/tools/learn/course-details/course-curriculum/dice-modal/DiceModal.tsx b/src-ts/tools/learn/course-details/course-curriculum/dice-modal/DiceModal.tsx new file mode 100644 index 000000000..7d967e793 --- /dev/null +++ b/src-ts/tools/learn/course-details/course-curriculum/dice-modal/DiceModal.tsx @@ -0,0 +1,53 @@ +import { Dispatch, FC, SetStateAction, useEffect, useState } from 'react' + +import { EnvironmentConfig } from '../../../../../config' +import { BaseModal, Button } from '../../../../../lib' + +import styles from './DiceModal.module.scss' + +interface DiceModalProps { + isOpen: boolean + onClose: () => void +} + +const DiceModal: FC = (props: DiceModalProps) => { + + const [isOpen, setIsOpen]: [boolean, Dispatch>] + = useState(false) + + useEffect(() => { + setIsOpen(props.isOpen) + }, [props.isOpen]) + + return ( + +
+ +

+ Wipro requires employees to enable multifactor Authentication + with DICE in order to take Topcoder Academy courses. +

+

+ Please go to Account Settings to configure your account. +

+

+

+ +
+ ) +} + +export default DiceModal diff --git a/src-ts/tools/learn/course-details/course-curriculum/dice-modal/index.ts b/src-ts/tools/learn/course-details/course-curriculum/dice-modal/index.ts new file mode 100644 index 000000000..b74a55774 --- /dev/null +++ b/src-ts/tools/learn/course-details/course-curriculum/dice-modal/index.ts @@ -0,0 +1 @@ +export { default as DiceModal } from './DiceModal' From 337220c81ea6574ceda73a99dff19666b29fceec Mon Sep 17 00:00:00 2001 From: Brooke Date: Fri, 23 Dec 2022 12:55:27 -0800 Subject: [PATCH 2/3] TCA-853 Clean up DICE store #time 15m --- .../user-functions/user-store/index.ts | 2 +- .../user-store/user-xhr.store.ts | 25 ++++++++----------- .../user-functions/user.functions.ts | 6 +++-- 3 files changed, 16 insertions(+), 17 deletions(-) diff --git a/src-ts/lib/functions/user-functions/user-store/index.ts b/src-ts/lib/functions/user-functions/user-store/index.ts index 368784135..5e88d6a17 100644 --- a/src-ts/lib/functions/user-functions/user-store/index.ts +++ b/src-ts/lib/functions/user-functions/user-store/index.ts @@ -1,5 +1,5 @@ export { - getDiceStatusAsync as userStoreGetDiceStatusAsync, + getMfaStatusAsync as userStoreGetMfaStatusAsync, patchAsync as userStorePatchAsync, } from './user-xhr.store' export { type UserPatchRequest } from './user-xhr.store' diff --git a/src-ts/lib/functions/user-functions/user-store/user-xhr.store.ts b/src-ts/lib/functions/user-functions/user-store/user-xhr.store.ts index 8cb4e1cc2..82a5bca8d 100644 --- a/src-ts/lib/functions/user-functions/user-store/user-xhr.store.ts +++ b/src-ts/lib/functions/user-functions/user-store/user-xhr.store.ts @@ -3,6 +3,15 @@ import { xhrGetAsync, xhrPatchAsync } from '../../xhr-functions' import { user as userEndpoint } from './user-endpoint.config' +export interface MfaStatusResult { + result: { + content: { + diceEnabled: boolean + mfaEnabled: boolean + } + } +} + export interface UserPatchRequest { param: { credential: { @@ -12,20 +21,8 @@ export interface UserPatchRequest { } } -export async function getDiceStatusAsync(userId: number): Promise { - - interface DiceStatusResult { - result: { - content: { - mfaEnabled: boolean - diceEnabled: boolean - } - } - } - const result: DiceStatusResult - = await xhrGetAsync(`${userEndpoint(userId)}/2fa`) - - return !!result.result.content.mfaEnabled && !!result.result.content.diceEnabled +export async function getMfaStatusAsync(userId: number): Promise { + return xhrGetAsync(`${userEndpoint(userId)}/2fa`) } export async function patchAsync(userId: number, request: UserPatchRequest): Promise { diff --git a/src-ts/lib/functions/user-functions/user.functions.ts b/src-ts/lib/functions/user-functions/user.functions.ts index d240c8d5b..613e7100c 100644 --- a/src-ts/lib/functions/user-functions/user.functions.ts +++ b/src-ts/lib/functions/user-functions/user.functions.ts @@ -1,7 +1,9 @@ -import { UserPatchRequest, userStoreGetDiceStatusAsync, userStorePatchAsync } from './user-store' +import { UserPatchRequest, userStoreGetMfaStatusAsync, userStorePatchAsync } from './user-store' +import { MfaStatusResult } from './user-store/user-xhr.store' export async function getDiceStatusAsync(userId: number): Promise { - return userStoreGetDiceStatusAsync(userId) + const result: MfaStatusResult = await userStoreGetMfaStatusAsync(userId) + return !!result.result.content.mfaEnabled && !!result.result.content.diceEnabled } export async function updatePasswordAsync(userId: number, currentPassword: string, password: string): Promise { From d08943dd51d59de3133bbdb6d04d4406ecd393ed Mon Sep 17 00:00:00 2001 From: Brooke Date: Fri, 23 Dec 2022 13:06:29 -0800 Subject: [PATCH 3/3] TCA-853 Improve copy --- .../course-curriculum/dice-modal/DiceModal.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src-ts/tools/learn/course-details/course-curriculum/dice-modal/DiceModal.tsx b/src-ts/tools/learn/course-details/course-curriculum/dice-modal/DiceModal.tsx index 7d967e793..f580e189b 100644 --- a/src-ts/tools/learn/course-details/course-curriculum/dice-modal/DiceModal.tsx +++ b/src-ts/tools/learn/course-details/course-curriculum/dice-modal/DiceModal.tsx @@ -24,13 +24,13 @@ const DiceModal: FC = (props: DiceModalProps) => { onClose={props.onClose} open={isOpen} size='md' - title='DICE Multifactor Authentication Required' + title='DICE ID Multifactor Authentication Required' >

- Wipro requires employees to enable multifactor Authentication - with DICE in order to take Topcoder Academy courses. + Wipro requires employees to enable Multifactor Authentication + with DICE ID in order to take Topcoder Academy courses.

Please go to Account Settings to configure your account.