From 112aef235c706a065ce8cc575d43d24bf9a63ce7 Mon Sep 17 00:00:00 2001 From: mahamakifdar19 Date: Tue, 9 Jan 2024 18:35:40 +0500 Subject: [PATCH] feat: skills quiz v2 enhancements --- .../data/tests/utils.test.js | 7 ++ .../data/hooks/hooks.test.jsx | 18 +++++ .../skills-quiz-v2/JobCardComponent.jsx | 56 +++++++++++--- src/components/skills-quiz-v2/ProgramCard.jsx | 35 --------- src/components/skills-quiz-v2/SkillsQuiz.jsx | 33 +++++---- .../skills-quiz-v2/SkillsQuizForm.jsx | 3 +- src/components/skills-quiz-v2/constants.js | 18 ----- .../skills-quiz-v2/styles/_Body.scss | 69 ++++++++++-------- .../skills-quiz-v2/styles/_Card.scss | 73 ++++++++++--------- .../skills-quiz-v2/styles/_Header.scss | 2 - .../skills-quiz/SearchCourseCard.jsx | 2 +- .../skills-quiz/TopSkillsOverview.jsx | 28 ++++--- .../skills-quiz/tests/SkillsQuiz.test.jsx | 19 ++++- src/index.scss | 1 + 14 files changed, 200 insertions(+), 164 deletions(-) delete mode 100644 src/components/skills-quiz-v2/ProgramCard.jsx diff --git a/src/components/dashboard/main-content/course-enrollments/data/tests/utils.test.js b/src/components/dashboard/main-content/course-enrollments/data/tests/utils.test.js index f1f88bbef..a8f584da6 100644 --- a/src/components/dashboard/main-content/course-enrollments/data/tests/utils.test.js +++ b/src/components/dashboard/main-content/course-enrollments/data/tests/utils.test.js @@ -233,4 +233,11 @@ describe('sortAssignmentsByAssignmentStatus', () => { expect(sortedAssignments).toEqual(expectedSortedAssignments); }); + + it('returns empty array for null assignments', () => { + const assignments = null; + const expectedAssignments = []; + const sortedAssignments = sortAssignmentsByAssignmentStatus(assignments); + expect(sortedAssignments).toEqual(expectedAssignments); + }); }); diff --git a/src/components/enterprise-user-subsidy/data/hooks/hooks.test.jsx b/src/components/enterprise-user-subsidy/data/hooks/hooks.test.jsx index b035fff7b..8ad9e537f 100644 --- a/src/components/enterprise-user-subsidy/data/hooks/hooks.test.jsx +++ b/src/components/enterprise-user-subsidy/data/hooks/hooks.test.jsx @@ -2,6 +2,7 @@ import { renderHook } from '@testing-library/react-hooks'; import * as logging from '@edx/frontend-platform/logging'; import { camelCaseObject } from '@edx/frontend-platform/utils'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; +import { logError } from '@edx/frontend-platform/logging'; import { useCouponCodes, @@ -349,6 +350,9 @@ describe('useCustomerAgreementData', () => { }); it('handles no customer agreement data for enterprise', async () => { + jest.mock('@edx/frontend-platform/logging', () => ({ + logError: jest.fn(), + })); fetchCustomerAgreementData.mockResolvedValueOnce({ data: { results: [] }, }); @@ -363,6 +367,20 @@ describe('useCustomerAgreementData', () => { false, // isLoading ]); }); + + it('handles errors in fetching customer agreement data', async () => { + const mockError = new Error('error'); + fetchCustomerAgreementData.mockRejectedValueOnce(mockError); + + const { result, waitForNextUpdate } = renderHook(() => useCustomerAgreementData(TEST_ENTERPRISE_UUID)); + + expect(result.current).toEqual([undefined, true]); + + await waitForNextUpdate(); + + expect(result.current).toEqual([null, false]); + expect(logError).toHaveBeenCalledWith(new Error(mockError)); + }); }); const Wrapper = ({ children }) => ( diff --git a/src/components/skills-quiz-v2/JobCardComponent.jsx b/src/components/skills-quiz-v2/JobCardComponent.jsx index d16f36353..8d8eedc9d 100644 --- a/src/components/skills-quiz-v2/JobCardComponent.jsx +++ b/src/components/skills-quiz-v2/JobCardComponent.jsx @@ -2,42 +2,76 @@ import React, { useContext, useState, useEffect } from 'react'; import { SelectableBox, Chip, Spinner, Stack, Button, } from '@edx/paragon'; +import { camelCaseObject } from '@edx/frontend-platform/utils'; +import { logError } from '@edx/frontend-platform/logging'; import PropTypes from 'prop-types'; import { SkillsContext } from '../skills-quiz/SkillsContextProvider'; import { SET_KEY_VALUE } from '../skills-quiz/data/constants'; -import { DROPDOWN_OPTION_IMPROVE_CURRENT_ROLE } from '../skills-quiz/constants'; import TopSkillsOverview from '../skills-quiz/TopSkillsOverview'; import SearchCourseCard from '../skills-quiz/SearchCourseCard'; import SearchProgramCard from '../skills-quiz/SearchProgramCard'; import SearchPathways from '../skills-quiz/SearchPathways'; import SkillsCourses from '../skills-quiz/SkillsCourses'; +import { fetchCourseEnrollments } from '../skills-quiz/data/service'; +import { saveSkillsGoalsAndJobsUserSelected } from '../skills-quiz/data/utils'; const JobCardComponent = ({ jobs, isLoading, jobIndex, courseIndex, }) => { const { dispatch, state } = useContext(SkillsContext); - const { goal } = state; + const { goal, currentJobRole, interestedJobs } = state; const [jobSelected, setJobSelected] = useState(undefined); const [showMoreRecommendedCourses, setShowMoreRecommendedCourses] = useState(false); useEffect(() => { - if (jobs?.length === 1) { + if (jobs?.length > 0) { setJobSelected(jobs[0]?.name); - dispatch({ type: SET_KEY_VALUE, key: 'selectedJob', value: jobSelected }); - } else if (jobs?.length === 0) { - setJobSelected(undefined); - dispatch({ type: SET_KEY_VALUE, key: 'selectedJob', value: undefined }); + dispatch({ + type: SET_KEY_VALUE, + key: 'selectedJob', + value: jobs[0]?.name, + }); } - }, [jobs, dispatch, jobSelected]); + }, [jobs, dispatch]); + + useEffect(() => { + if (goal && (currentJobRole || interestedJobs)) { + saveSkillsGoalsAndJobsUserSelected(goal, currentJobRole, interestedJobs); + } + }, [goal, currentJobRole, interestedJobs]); + + useEffect(() => { + const fetchLearnerCourseEnrollments = async () => { + try { + const response = await fetchCourseEnrollments(); + const enrolledCourses = camelCaseObject(response.data); + const enrolledCourseIds = enrolledCourses.map( + (course) => course.courseDetails.courseId, + ); + dispatch({ + type: SET_KEY_VALUE, + key: 'enrolledCourseIds', + value: enrolledCourseIds, + }); + } catch (error) { + logError(error); + } + }; + + if (jobs?.length > 0) { + fetchLearnerCourseEnrollments(); + } + }, [dispatch, jobs]); const handleChange = (e) => { + e.preventDefault(); setJobSelected(e.target.value); dispatch({ type: SET_KEY_VALUE, key: 'selectedJob', value: e.target.value }); setShowMoreRecommendedCourses(false); }; return !isLoading ? ( - <> +
))} - {(jobSelected || goal === DROPDOWN_OPTION_IMPROVE_CURRENT_ROLE) && ( + {jobs?.length > 0 && ( <> @@ -89,7 +123,7 @@ const JobCardComponent = ({ { showMoreRecommendedCourses && } )} - +
) : ( { - const isExtraSmall = useMediaQuery({ maxWidth: breakpoints.small.maxWidth }); - return ( - - - - - ); -}; - -ProgramCard.propTypes = { - title: PropTypes.string, - subtitle: PropTypes.string, - mainImg: PropTypes.string, - logoImg: PropTypes.string, -}; - -ProgramCard.defaultProps = { - title: null, - subtitle: null, - mainImg: null, - logoImg: null, -}; - -export default ProgramCard; diff --git a/src/components/skills-quiz-v2/SkillsQuiz.jsx b/src/components/skills-quiz-v2/SkillsQuiz.jsx index c32547ec5..ea7d74220 100644 --- a/src/components/skills-quiz-v2/SkillsQuiz.jsx +++ b/src/components/skills-quiz-v2/SkillsQuiz.jsx @@ -1,32 +1,43 @@ import { Helmet } from 'react-helmet'; -import './styles/index.scss'; import { AppContext } from '@edx/frontend-platform/react'; +import { sendEnterpriseTrackEvent } from '@edx/frontend-enterprise-utils'; import PropTypes from 'prop-types'; import { ModalDialog, useToggle, ActionRow, Button, } from '@edx/paragon'; import { useNavigate } from 'react-router-dom'; -import { useContext } from 'react'; +import { useContext, useEffect } from 'react'; import { SKILL_BUILDER_TITLE, text, - webTechBootCamps, closeModalText, } from './constants'; -import ProgramCard from './ProgramCard'; import SkillsQuizHeader from './SkillsQuizHeader'; import SkillQuizForm from './SkillsQuizForm'; import headerImage from '../skills-quiz/images/headerImage.png'; const SkillsQuizV2 = ({ isStyleAutoSuggest }) => { - const { enterpriseConfig } = useContext(AppContext); + const { enterpriseConfig, authenticatedUser: { userId } } = useContext(AppContext); const navigate = useNavigate(); const [isOpen, open, close] = useToggle(false); const handleExit = () => { navigate(`/${enterpriseConfig.slug}/search`); + sendEnterpriseTrackEvent( + enterpriseConfig.uuid, + 'edx.ui.enterprise.learner_portal.skills_quiz.done.clicked', + { userId, enterprise: enterpriseConfig.slug }, + ); }; + useEffect(() => { + sendEnterpriseTrackEvent( + enterpriseConfig.uuid, + 'edx.ui.enterprise.learner_portal.skills_quiz.started', + { userId, enterprise: enterpriseConfig.slug }, + ); + }, [enterpriseConfig.slug, enterpriseConfig.uuid, userId]); + const TITLE = `edx - ${SKILL_BUILDER_TITLE}`; return ( <> @@ -58,7 +69,7 @@ const SkillsQuizV2 = ({ isStyleAutoSuggest }) => { @@ -74,16 +85,6 @@ const SkillsQuizV2 = ({ isStyleAutoSuggest }) => {

{text}

-
-

- Boot camps for a web technology specialist -

-
- {webTechBootCamps.map((bootcamp) => ( - - ))} -
-
diff --git a/src/components/skills-quiz-v2/SkillsQuizForm.jsx b/src/components/skills-quiz-v2/SkillsQuizForm.jsx index f8c0b7514..26be274f7 100644 --- a/src/components/skills-quiz-v2/SkillsQuizForm.jsx +++ b/src/components/skills-quiz-v2/SkillsQuizForm.jsx @@ -41,8 +41,7 @@ const SkillQuizForm = ({ isStyleAutoSuggest }) => {