Skip to content

TCA-852 DICE Setup Required Modal -> TCA-851_wipro-mfa #454

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 3 commits into from
Dec 23, 2022
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
6 changes: 6 additions & 0 deletions src-ts/.eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,12 @@ module.exports = {
2,
4,
],
'react/jsx-no-bind': [
'error',
{
allowFunctions: true,
}
],
'react/jsx-no-useless-fragment': [
0
],
Expand Down
1 change: 1 addition & 0 deletions src-ts/config/environments/environment.default.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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`,
Expand Down
1 change: 1 addition & 0 deletions src-ts/config/environments/environment.prod.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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`,
Expand Down
2 changes: 1 addition & 1 deletion src-ts/lib/functions/user-functions/user-store/index.ts
Original file line number Diff line number Diff line change
@@ -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'
24 changes: 11 additions & 13 deletions src-ts/lib/functions/user-functions/user-store/user-xhr.store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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: {
Expand All @@ -12,19 +21,8 @@ export interface UserPatchRequest {
}
}

export async function getDiceStatusAsync(userId: number): Promise<boolean> {

interface DiceStatusResult {
result: {
content: {
diceEnabled: boolean
}
}
}
const result: DiceStatusResult
= await xhrGetAsync<DiceStatusResult>(`${userEndpoint(userId)}/2fa`)

return !!result.result.content.diceEnabled
export async function getMfaStatusAsync(userId: number): Promise<MfaStatusResult> {
return xhrGetAsync<MfaStatusResult>(`${userEndpoint(userId)}/2fa`)
}

export async function patchAsync(userId: number, request: UserPatchRequest): Promise<User> {
Expand Down
6 changes: 4 additions & 2 deletions src-ts/lib/functions/user-functions/user.functions.ts
Original file line number Diff line number Diff line change
@@ -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<boolean> {
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<void> {
Expand Down
1 change: 1 addition & 0 deletions src-ts/lib/global-config.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export interface GlobalConfig {
CUSTOMER_TOKEN: string
}
TOPCODER_URLS: {
ACCOUNT_SETTINGS: string
API_BASE: string
BLOG_PAGE: string
CHALLENGES_PAGE: string
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -43,6 +44,8 @@ const CourseCurriculum: FC<CourseCurriculumProps> = (props: CourseCurriculumProp

const [isTcAcademyPolicyModal, setIsTcAcademyPolicyModal]: [boolean, Dispatch<SetStateAction<boolean>>]
= useState<boolean>(false)
const [isDiceModalOpen, setIsDiceModalOpen]: [boolean, Dispatch<SetStateAction<boolean>>]
= useState<boolean>(false)

const status: string = props.progress?.status ?? UserCertificationProgressStatus.inititialized
const completedPercentage: number = (props.progress?.courseProgressPercentage ?? 0) / 100
Expand Down Expand Up @@ -89,8 +92,7 @@ const CourseCurriculum: FC<CourseCurriculumProps> = (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
}

Expand All @@ -102,7 +104,7 @@ const CourseCurriculum: FC<CourseCurriculumProps> = (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,
Expand Down Expand Up @@ -141,7 +143,7 @@ const CourseCurriculum: FC<CourseCurriculumProps> = (props: CourseCurriculumProp
}

handleStartCourse()
// eslint-disable-next-line react-hooks/exhaustive-deps
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [
handleStartCourse,
props.course.certificationId,
Expand All @@ -167,6 +169,14 @@ const CourseCurriculum: FC<CourseCurriculumProps> = (props: CourseCurriculumProp
}
}, [handleStartCourseClick, isLoggedIn, props.progressReady, searchParams])

function onAcademicHonestyModalClose(): void {
setIsTcAcademyPolicyModal(false)
}

function onDiceModalClose(): void {
setIsDiceModalOpen(false)
}

return (
<>
<div className={styles.wrap}>
Expand Down Expand Up @@ -211,9 +221,14 @@ const CourseCurriculum: FC<CourseCurriculumProps> = (props: CourseCurriculumProp

<TcAcademyPolicyModal
isOpen={isTcAcademyPolicyModal}
onClose={() => setIsTcAcademyPolicyModal(false)}
onClose={onAcademicHonestyModalClose}
onConfirm={handlePolicyAccept}
/>

<DiceModal
isOpen={isDiceModalOpen}
onClose={onDiceModalClose}
/>
</>
)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
@import '../../../../../lib/styles/includes';

.diceModal {

p {
margin-bottom: $space-lg;

&.buttonContainer {
display: flex;
justify-content: center;
}
}
}
Original file line number Diff line number Diff line change
@@ -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<DiceModalProps> = (props: DiceModalProps) => {

const [isOpen, setIsOpen]: [boolean, Dispatch<SetStateAction<boolean>>]
= useState<boolean>(false)

useEffect(() => {
setIsOpen(props.isOpen)
}, [props.isOpen])

return (
<BaseModal
onClose={props.onClose}
open={isOpen}
size='md'
title='DICE ID Multifactor Authentication Required'
>
<div className={styles.diceModal}>

<p>
Wipro requires employees to enable Multifactor Authentication
with DICE ID in order to take Topcoder Academy courses.
</p>
<p>
Please go to Account Settings to configure your account.
</p>
<p className={styles.buttonContainer}>
<Button
buttonStyle='primary'
label='Account Settings'
onClick={props.onClose}
target='_blank'
url={EnvironmentConfig.TOPCODER_URLS.ACCOUNT_SETTINGS}
/>
</p>
</div>

</BaseModal>
)
}

export default DiceModal
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as DiceModal } from './DiceModal'