Skip to content

2.6 Wipro DICE IDs Release - 2023-01-04 -> master #459

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 16 commits into from
Jan 4, 2023
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
6 changes: 4 additions & 2 deletions src-ts/lib/functions/token-functions/token.functions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@ export async function getAsync(): Promise<TokenModel> {
}

try {
const { handle, roles }: {
const { handle, roles, userId }: {
handle?: string
roles?: Array<string>
userId?: number
} = decodeToken(token)

// if we didn't find the handle, we have a bad token
Expand All @@ -26,8 +27,9 @@ export async function getAsync(): Promise<TokenModel> {
return Promise.resolve({})
}

return Promise.resolve({ handle, roles, token })
return Promise.resolve({ handle, roles, token, userId })

// eslint-disable-next-line @typescript-eslint/no-explicit-any
} catch (error: any) {
logError(error)
return Promise.resolve({})
Expand Down
1 change: 1 addition & 0 deletions src-ts/lib/functions/token-functions/token.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ export interface TokenModel {
handle?: string
roles?: Array<string>
token?: string
userId?: number
}
5 changes: 4 additions & 1 deletion src-ts/lib/functions/user-functions/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
export { updatePasswordAsync as userUpdatePasswordAsync } from './user.functions'
export {
getDiceStatusAsync as userGetDiceStatusAsync,
updatePasswordAsync as userUpdatePasswordAsync,
} from './user.functions'
5 changes: 4 additions & 1 deletion src-ts/lib/functions/user-functions/user-store/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
export { patchAsync as userPatchAsync } from './user-xhr.store'
export {
getMfaStatusAsync as userStoreGetMfaStatusAsync,
patchAsync as userStorePatchAsync,
} from './user-xhr.store'
export { type UserPatchRequest } from './user-xhr.store'
15 changes: 14 additions & 1 deletion src-ts/lib/functions/user-functions/user-store/user-xhr.store.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,17 @@
import { User } from '../../../../../types/tc-auth-lib'
import { xhrPatchAsync } from '../../xhr-functions'
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,6 +21,10 @@ export interface UserPatchRequest {
}
}

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

export async function patchAsync(userId: number, request: UserPatchRequest): Promise<User> {
const url: string = userEndpoint(userId)
return xhrPatchAsync(url, request)
Expand Down
10 changes: 8 additions & 2 deletions src-ts/lib/functions/user-functions/user.functions.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import { userPatchAsync, UserPatchRequest } 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> {
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> {
const request: UserPatchRequest = {
Expand All @@ -9,6 +15,6 @@ export async function updatePasswordAsync(userId: number, currentPassword: strin
},
},
}
return userPatchAsync(userId, request)
return userStorePatchAsync(userId, request)
.then(() => undefined)
}
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
18 changes: 11 additions & 7 deletions src-ts/lib/page-footer/PageFooter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,17 @@ const PageFooter: FC<{}> = () => {

const navElementId: string = 'footer-nav-el'

tcUniNav(
'init',
navElementId,
{
type: 'footer',
},
)
// delay the initialization so
// the nav element has time to render
setTimeout(() => {
tcUniNav(
'init',
navElementId,
{
type: 'footer',
},
)
}, 10)

return <div id={navElementId} />
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,24 @@ import { UserProfile } from '../../user-profile.model'

import { UserRole } from './user-role.enum'

export function create(profile: UserProfile, token: TokenModel): UserProfile {
// TODO: create the profile full name property
export function create(profile: UserProfile, token: TokenModel, hasDiceEnabled: boolean): UserProfile {

// Currently, the "Self-Service Customer" role is being set when a user is created
// during the self-service workflow. There are no other roles being set to distinguish
// between Customers and Members.
// Therefore, the only way to know if a user is a Member is if s/he is not a Customer.
// This is imperfect, bc a user could be both a Customer or a Member, but for now
// we are okay with this and will have a more in-depth initiave to properly assign
// rolees.
// roles.
profile.isCustomer = !!token.roles?.some(role => role === UserRole.customer)
profile.isMember = !profile.isCustomer

profile.isWipro = profile.email.endsWith('@wipro.com')
profile.diceEnabled = hasDiceEnabled

// store roles for custom capability checks
profile.roles = token.roles
profile.roles = token.roles || []

// TODO: create the profile full name property
return profile
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { userGetDiceStatusAsync } from '../../functions/user-functions'
import { tokenGetAsync, TokenModel } from '../../functions/token-functions'
import { EditNameRequest } from '../edit-name-request.model'
import { UserProfile } from '../user-profile.model'
Expand All @@ -11,19 +12,22 @@ export async function getAsync(handle?: string): Promise<UserProfile | undefined
const token: TokenModel = await tokenGetAsync()

// get the handle
const safeHandle: string | undefined = handle || token?.handle
if (!safeHandle) {
const safeHandle: string | undefined = handle || token.handle
if (!safeHandle || !token.userId) {
return Promise.resolve(undefined)
}

// get the profile
const profileResult: UserProfile = await profileStoreGet(safeHandle)
const profilePromise: Promise<UserProfile> = profileStoreGet(safeHandle)
const dicePromise: Promise<boolean> = userGetDiceStatusAsync(token.userId)

const [profileResult, diceEnabled]: [UserProfile, boolean] = await Promise.all([profilePromise, dicePromise])

// make the changes we need based on the token
const output: UserProfile = profileFactoryCreate(profileResult, token)
const output: UserProfile = profileFactoryCreate(profileResult, token, diceEnabled)
return output
}

export async function editNameAsync(handle: string, profile: EditNameRequest): Promise<any> {
export async function editNameAsync(handle: string, profile: EditNameRequest): Promise<UserProfile> {
return profileStorePatchName(handle, profile)
}
2 changes: 2 additions & 0 deletions src-ts/lib/profile-provider/user-profile.model.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
export interface UserProfile {
competitionCountryCode: string
createdAt: number
diceEnabled: boolean
email: string
firstName: string
handle: string
handleLower: string
homeCountryCode: string
isCustomer?: boolean
isMember?: boolean
isWipro: boolean
lastName: string
photoURL?: string
roles: Array<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 @@ -37,11 +38,14 @@ interface CourseCurriculumProps {
const CourseCurriculum: FC<CourseCurriculumProps> = (props: CourseCurriculumProps) => {

const navigate: NavigateFunction = useNavigate()
const [searchParams]: any = useSearchParams()
const [searchParams]: [URLSearchParams, unknown] = useSearchParams()

const isLoggedIn: boolean = !!props.profile

const [isTcAcademyPolicyModal, setIsTcAcademyPolicyModal]: [boolean, Dispatch<SetStateAction<boolean>>] = useState<boolean>(false)
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 @@ -76,6 +80,7 @@ const CourseCurriculum: FC<CourseCurriculumProps> = (props: CourseCurriculumProp
* Handle user click on start course/resume/login button
*/
const handleStartCourseClick: () => void = useCallback(() => {

// if user is not logged in, redirect to login page
if (!isLoggedIn) {
// add a flag to the return url to show the academic policy modal
Expand All @@ -84,6 +89,13 @@ const CourseCurriculum: FC<CourseCurriculumProps> = (props: CourseCurriculumProp
return
}

// if the user is wipro and s/he hasn't set up DICE,
// let the user know
if (props.profile?.isWipro && !props.profile.diceEnabled) {
setIsDiceModalOpen(true)
return
}

// Check if user accepted policy and resume(or start) the course
if (props.progress?.academicHonestyPolicyAcceptedAt) {
handleStartCourse()
Expand All @@ -92,6 +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
}, [
handleStartCourse,
isLoggedIn,
Expand Down Expand Up @@ -130,6 +143,7 @@ const CourseCurriculum: FC<CourseCurriculumProps> = (props: CourseCurriculumProp
}

handleStartCourse()
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [
handleStartCourse,
props.course.certificationId,
Expand All @@ -149,11 +163,20 @@ const CourseCurriculum: FC<CourseCurriculumProps> = (props: CourseCurriculumProp
* proceed as if the user just clicked "Start course" button
*/
useEffect(() => {
// eslint-disable-next-line no-null/no-null
if (props.progressReady && isLoggedIn && searchParams.get(LEARN_PATHS.startCourseRouteFlag) !== null) {
handleStartCourseClick()
}
}, [handleStartCourseClick, isLoggedIn, props.progressReady, searchParams])

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

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

return (
<>
<div className={styles.wrap}>
Expand Down Expand Up @@ -198,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;
}
}
}
Loading