From 032fab758d58464adc5db46280602f7079631935 Mon Sep 17 00:00:00 2001 From: irfanuddinahmad <34648393+irfanuddinahmad@users.noreply.github.com> Date: Thu, 4 Jan 2024 11:35:27 +0500 Subject: [PATCH] feat: ENT-7367 Added skills quiz v2 (#911) * feat: Added skills quiz v2 * fix: Fixed duplicate components * feat: unfold top skills and recommendations on card click --------- Co-authored-by: IrfanUddinAhmad Co-authored-by: mahamakifdar19 --- .env.development | 1 + .../skills-quiz-v2/JobCardComponent.jsx | 124 +++++++++++++ src/components/skills-quiz-v2/ProgramCard.jsx | 35 ++++ src/components/skills-quiz-v2/SkillsQuiz.jsx | 102 +++++++++++ .../skills-quiz-v2/SkillsQuizForm.jsx | 84 +++++++++ .../skills-quiz-v2/SkillsQuizHeader.jsx | 15 ++ src/components/skills-quiz-v2/constants.js | 23 +++ .../skills-quiz-v2/styles/_Body.scss | 37 ++++ .../skills-quiz-v2/styles/_Card.scss | 43 +++++ .../skills-quiz-v2/styles/_Header.scss | 25 +++ .../skills-quiz-v2/styles/index.scss | 3 + .../skills-quiz/CurrentJobDropdown.jsx | 29 ++- .../skills-quiz/IndustryDropdown.jsx | 23 ++- src/components/skills-quiz/SearchJobCard.jsx | 21 ++- .../skills-quiz/SearchJobDropdown.jsx | 23 ++- src/components/skills-quiz/SkillsQuiz.jsx | 14 +- .../skills-quiz/SkillsQuizStepper.jsx | 170 +++++++++++------- .../skills-quiz/tests/SkillsQuiz.test.jsx | 3 +- .../skills-quiz/tests/SkillsQuizPage.test.jsx | 2 +- 19 files changed, 695 insertions(+), 82 deletions(-) create mode 100644 src/components/skills-quiz-v2/JobCardComponent.jsx create mode 100644 src/components/skills-quiz-v2/ProgramCard.jsx create mode 100644 src/components/skills-quiz-v2/SkillsQuiz.jsx create mode 100644 src/components/skills-quiz-v2/SkillsQuizForm.jsx create mode 100644 src/components/skills-quiz-v2/SkillsQuizHeader.jsx create mode 100644 src/components/skills-quiz-v2/constants.js create mode 100644 src/components/skills-quiz-v2/styles/_Body.scss create mode 100644 src/components/skills-quiz-v2/styles/_Card.scss create mode 100644 src/components/skills-quiz-v2/styles/_Header.scss create mode 100644 src/components/skills-quiz-v2/styles/index.scss diff --git a/.env.development b/.env.development index 444be43740..320e9b7008 100644 --- a/.env.development +++ b/.env.development @@ -23,6 +23,7 @@ SITE_NAME='edX' ALGOLIA_APP_ID='' ALGOLIA_SEARCH_API_KEY='' ALGOLIA_INDEX_NAME='' +ALGOLIA_INDEX_NAME_JOBS='' FULLSTORY_ORG_ID='' USE_API_CACHE='' LOGO_URL=https://edx-cdn.org/v3/default/logo.svg diff --git a/src/components/skills-quiz-v2/JobCardComponent.jsx b/src/components/skills-quiz-v2/JobCardComponent.jsx new file mode 100644 index 0000000000..d16f36353c --- /dev/null +++ b/src/components/skills-quiz-v2/JobCardComponent.jsx @@ -0,0 +1,124 @@ +import React, { useContext, useState, useEffect } from 'react'; +import { + SelectableBox, Chip, Spinner, Stack, Button, +} from '@edx/paragon'; +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'; + +const JobCardComponent = ({ + jobs, isLoading, jobIndex, courseIndex, +}) => { + const { dispatch, state } = useContext(SkillsContext); + const { goal } = state; + const [jobSelected, setJobSelected] = useState(undefined); + const [showMoreRecommendedCourses, setShowMoreRecommendedCourses] = useState(false); + + useEffect(() => { + if (jobs?.length === 1) { + 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 }); + } + }, [jobs, dispatch, jobSelected]); + + const handleChange = (e) => { + setJobSelected(e.target.value); + dispatch({ type: SET_KEY_VALUE, key: 'selectedJob', value: e.target.value }); + setShowMoreRecommendedCourses(false); + }; + + return !isLoading ? ( + <> + + {jobs.map((job) => ( + +
+
{job.name}
+
Related skills
+ {job.skills.slice(0, 5).map((skill) => ( +
+ {skill.name} +
+ ))} +
+
+ ))} +
+ {(jobSelected || goal === DROPDOWN_OPTION_IMPROVE_CURRENT_ROLE) && ( + <> + + + + + + +
+ { !showMoreRecommendedCourses && ( + + ) } +
+ { showMoreRecommendedCourses && } + + )} + + ) : ( + + ); +}; + +JobCardComponent.defaultProps = { + jobs: undefined, + isLoading: false, + jobIndex: undefined, + courseIndex: undefined, +}; + +JobCardComponent.propTypes = { + isLoading: PropTypes.bool, + jobs: PropTypes.arrayOf(PropTypes.shape()), + jobIndex: PropTypes.shape({ + appId: PropTypes.string, + indexName: PropTypes.string, + search: PropTypes.func.isRequired, + }), + courseIndex: PropTypes.shape({ + appId: PropTypes.string, + indexName: PropTypes.string, + search: PropTypes.func.isRequired, + }), +}; + +export default JobCardComponent; diff --git a/src/components/skills-quiz-v2/ProgramCard.jsx b/src/components/skills-quiz-v2/ProgramCard.jsx new file mode 100644 index 0000000000..a3b2101527 --- /dev/null +++ b/src/components/skills-quiz-v2/ProgramCard.jsx @@ -0,0 +1,35 @@ +import { Card, useMediaQuery, breakpoints } from '@edx/paragon'; +import PropTypes from 'prop-types'; + +const ProgramCard = ({ + mainImg, logoImg, title, subtitle, +}) => { + 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 new file mode 100644 index 0000000000..6ea4293ca8 --- /dev/null +++ b/src/components/skills-quiz-v2/SkillsQuiz.jsx @@ -0,0 +1,102 @@ +import { Helmet } from 'react-helmet'; +import './styles/index.scss'; +import { AppContext } from '@edx/frontend-platform/react'; +import PropTypes from 'prop-types'; +import { + ModalDialog, useToggle, ActionRow, Button, +} from '@edx/paragon'; +import { useHistory } from 'react-router-dom'; +import { useContext } 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 history = useHistory(); + const [isOpen, open, close] = useToggle(false); + + const handleExit = () => { + history.push(`/${enterpriseConfig.slug}/search`); + }; + + const TITLE = `edx - ${SKILL_BUILDER_TITLE}`; + return ( + <> + + + + Exit Skill Builder? + + +

{closeModalText}

+ + + + +
+
+ + + + + + + + + +
+
+

{text}

+
+ +
+

+ Boot camps for a web technology specialist +

+
+ {webTechBootCamps.map((bootcamp) => ( + + ))} +
+
+
+
+
+ + ); +}; + +SkillsQuizV2.propTypes = { + isStyleAutoSuggest: PropTypes.bool, +}; + +SkillsQuizV2.defaultProps = { + isStyleAutoSuggest: false, +}; + +export default SkillsQuizV2; diff --git a/src/components/skills-quiz-v2/SkillsQuizForm.jsx b/src/components/skills-quiz-v2/SkillsQuizForm.jsx new file mode 100644 index 0000000000..f8c0b7514f --- /dev/null +++ b/src/components/skills-quiz-v2/SkillsQuizForm.jsx @@ -0,0 +1,84 @@ +import { Button } from '@edx/paragon'; +import { useState, useMemo } from 'react'; +import { getConfig } from '@edx/frontend-platform/config'; +import algoliasearch from 'algoliasearch/lite'; +import { InstantSearch } from 'react-instantsearch-dom'; +import PropTypes from 'prop-types'; +import SearchJobDropdown from '../skills-quiz/SearchJobDropdown'; +import CurrentJobDropdown from '../skills-quiz/CurrentJobDropdown'; +import IndustryDropdown from '../skills-quiz/IndustryDropdown'; +import GoalDropdown from '../skills-quiz/GoalDropdown'; +import SearchJobCard from '../skills-quiz/SearchJobCard'; + +const SkillQuizForm = ({ isStyleAutoSuggest }) => { + const config = getConfig(); + + const [searchClient, courseIndex, jobIndex] = useMemo(() => { + const client = algoliasearch( + config.ALGOLIA_APP_ID, + config.ALGOLIA_SEARCH_API_KEY, + ); + const cIndex = client.initIndex(config.ALGOLIA_INDEX_NAME); + const jIndex = client.initIndex(config.ALGOLIA_INDEX_NAME_JOBS); + return [client, cIndex, jIndex]; + }, [ + config.ALGOLIA_APP_ID, + config.ALGOLIA_INDEX_NAME, + config.ALGOLIA_INDEX_NAME_JOBS, + config.ALGOLIA_SEARCH_API_KEY, + ]); + const [hide, setHide] = useState(true); + + return ( +
+ +

+ What roles are you interested in ? +

+ + + {!hide && ( +
+

+ Tell us about what you want to achieve ? +

+ +

+ Search and select your current job title +

+ + +

+ What industry are you interested in ? +

+ +
+ )} + +
+
+ ); +}; + +SkillQuizForm.propTypes = { + isStyleAutoSuggest: PropTypes.bool, +}; + +SkillQuizForm.defaultProps = { + isStyleAutoSuggest: false, +}; + +export default SkillQuizForm; diff --git a/src/components/skills-quiz-v2/SkillsQuizHeader.jsx b/src/components/skills-quiz-v2/SkillsQuizHeader.jsx new file mode 100644 index 0000000000..488e413e64 --- /dev/null +++ b/src/components/skills-quiz-v2/SkillsQuizHeader.jsx @@ -0,0 +1,15 @@ +import React from 'react'; +import edxLogo from '../skills-quiz/images/edx-logo.svg'; + +const SkillsQuizHeader = () => ( +
+ edx-logo +
+
+

Skills builder

+

Let edX be your guide

+
+
+); + +export default SkillsQuizHeader; diff --git a/src/components/skills-quiz-v2/constants.js b/src/components/skills-quiz-v2/constants.js new file mode 100644 index 0000000000..362a64bcf0 --- /dev/null +++ b/src/components/skills-quiz-v2/constants.js @@ -0,0 +1,23 @@ +export const SKILL_BUILDER_TITLE = 'Skill Builder'; + +export const text = 'We combine the educational expertise with labor market data to help you reach your learning and professional goals. Whether you are looking to grow in your career, change careers, or just learn new skills, this tool can help you find a relevant course. Your role selection and recommendations are private and are not visible to your edX administrator'; + +export const webTechBootCamps = [ + { + mainImg: + 'https://prod-discovery.edx-cdn.org/media/course/image/8e119d15-b484-4de6-a795-b6d9be101233-19d5867a8db3.small.png', + logoImg: + 'https://prod-discovery.edx-cdn.org/organization/logos/eac96c61-1462-4084-a0b2-12525b74a9e1-8377159ff774.png', + title: 'Engineering for Your Classroom K – 3', + subtitle: 'University of British Columbia', + }, + { + mainImg: + 'https://prod-discovery.edx-cdn.org/media/course/image/7868fb19-176b-4d98-b1a0-4d1e2029fdb8-b302dd3a98d1.jpg', + logoImg: + 'https://prod-discovery.edx-cdn.org/organization/logos/eac96c61-1462-4084-a0b2-12525b74a9e1-8377159ff774.png', + title: 'Software Engineering : Introduction', + subtitle: 'University of British Columbia', + }, +]; +export const closeModalText = 'Learners who enroll in courses that align with their career goals are more likely to complete the course'; diff --git a/src/components/skills-quiz-v2/styles/_Body.scss b/src/components/skills-quiz-v2/styles/_Body.scss new file mode 100644 index 0000000000..e6a71ab883 --- /dev/null +++ b/src/components/skills-quiz-v2/styles/_Body.scss @@ -0,0 +1,37 @@ +$gray-500: #707070; +$max-sm-modal-width: 28rem !important; +$min-sm-modal-height: 16rem !important; + +.page-body { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} +.form { + width: 65%; +} +.pgn__form-group { + width: 101%; +} +.text { + width: 65%; +} +.pgn__form-label { + font-weight: bold; +} + +.btn { + margin: 7px -5px; +} + +.form-auto-suggest { + .form-control { + color: $gray-500; + } +} + +.modal-small { + max-width: $max-sm-modal-width; + min-height: $min-sm-modal-height; +} diff --git a/src/components/skills-quiz-v2/styles/_Card.scss b/src/components/skills-quiz-v2/styles/_Card.scss new file mode 100644 index 0000000000..f0f362c625 --- /dev/null +++ b/src/components/skills-quiz-v2/styles/_Card.scss @@ -0,0 +1,43 @@ +$dark-500: #00262b !default; + +.selectable-box { + .box { + display: flex; + flex-direction: row-reverse; + justify-content: space-between; + min-height: 400px; + } + .pgn__form-radio-input { + min-width: 21px; + min-height: 21px; + } + .lead { + display: flex; + max-width: 250px; + min-height: 5rem; + } +} + +.cards-display { + margin: 33px 0; + display: flex; + flex-direction: column; + width: 65%; + height: 400px; +} + +.card-container { + border: none; + display: flex; + flex-direction: row; + height: 100%; +} + +.card { + margin-right: 10px; + min-height: 10em; +} + +.pgn__searchfield { + height: 45px; +} diff --git a/src/components/skills-quiz-v2/styles/_Header.scss b/src/components/skills-quiz-v2/styles/_Header.scss new file mode 100644 index 0000000000..ac82d0781a --- /dev/null +++ b/src/components/skills-quiz-v2/styles/_Header.scss @@ -0,0 +1,25 @@ +$accent-b: #f0cc00 !default; + +.header { + display: flex; + background: $dark-500; + width: 100%; + min-height: 100px; + max-height: 150px; + color: $accent-b; +} + +.sticky { + z-index: 1; +} + +.heading-text { + display: flex; + padding: 10px; + justify-content: center; + flex-direction: column; +} + +h2 { + color: $accent-b; +} diff --git a/src/components/skills-quiz-v2/styles/index.scss b/src/components/skills-quiz-v2/styles/index.scss new file mode 100644 index 0000000000..cfc1124567 --- /dev/null +++ b/src/components/skills-quiz-v2/styles/index.scss @@ -0,0 +1,3 @@ +@import "./Card"; +@import "./Header"; +@import "./Body"; diff --git a/src/components/skills-quiz/CurrentJobDropdown.jsx b/src/components/skills-quiz/CurrentJobDropdown.jsx index a13533f581..06cd72ade3 100644 --- a/src/components/skills-quiz/CurrentJobDropdown.jsx +++ b/src/components/skills-quiz/CurrentJobDropdown.jsx @@ -1,19 +1,28 @@ import React, { useContext } from 'react'; import { SearchContext } from '@edx/frontend-enterprise-catalog-search'; import FacetListRefinement from '@edx/frontend-enterprise-catalog-search/FacetListRefinement'; - +import PropTypes from 'prop-types'; import { CURRENT_JOB_FACET } from './constants'; -const CurrentJobDropdown = () => { +const CurrentJobDropdown = ({ isStyleAutoSuggest, isChip }) => { const { refinements } = useContext(SearchContext); const { - title, attribute, typeaheadOptions, facetValueType, customAttribute, + title, + attribute, + typeaheadOptions, + facetValueType, + customAttribute, } = CURRENT_JOB_FACET; return ( 0 ? refinements[customAttribute][0] : title} + title={ + refinements[customAttribute]?.length > 0 + ? refinements[customAttribute][0] + : title + } + label={title} attribute={attribute} limit={300} // this is replicating the B2C search experience refinements={refinements} @@ -24,8 +33,20 @@ const CurrentJobDropdown = () => { customAttribute={customAttribute} showBadge={false} variant="default" + isStyleAutoSuggest={isStyleAutoSuggest} + isChip={isChip} /> ); }; +CurrentJobDropdown.propTypes = { + isStyleAutoSuggest: PropTypes.bool, + isChip: PropTypes.bool, +}; + +CurrentJobDropdown.defaultProps = { + isStyleAutoSuggest: false, + isChip: false, +}; + export default CurrentJobDropdown; diff --git a/src/components/skills-quiz/IndustryDropdown.jsx b/src/components/skills-quiz/IndustryDropdown.jsx index 52a3c6094e..3c92192d98 100644 --- a/src/components/skills-quiz/IndustryDropdown.jsx +++ b/src/components/skills-quiz/IndustryDropdown.jsx @@ -1,10 +1,10 @@ import React, { useContext } from 'react'; import { SearchContext } from '@edx/frontend-enterprise-catalog-search'; import FacetListRefinement from '@edx/frontend-enterprise-catalog-search/FacetListRefinement'; - +import PropTypes from 'prop-types'; import { INDUSTRY_ATTRIBUTE_NAME, INDUSTRY_FACET } from './constants'; -const IndustryDropdown = () => { +const IndustryDropdown = ({ isStyleAutoSuggest, isStyleSearchBox }) => { const { refinements } = useContext(SearchContext); const { title, attribute, typeaheadOptions, facetValueType, @@ -13,7 +13,12 @@ const IndustryDropdown = () => { return ( 0 ? refinements[INDUSTRY_ATTRIBUTE_NAME][0] : title} + title={ + refinements[INDUSTRY_ATTRIBUTE_NAME]?.length > 0 + ? refinements[INDUSTRY_ATTRIBUTE_NAME][0] + : title + } + label={title} attribute={attribute} defaultRefinement={refinements[INDUSTRY_ATTRIBUTE_NAME]} limit={300} // this is replicating the B2C search experience @@ -22,8 +27,20 @@ const IndustryDropdown = () => { typeaheadOptions={typeaheadOptions} searchable={!!typeaheadOptions} showBadge={false} + isStyleAutoSuggest={isStyleAutoSuggest} + isStyleSearchBox={isStyleSearchBox} /> ); }; +IndustryDropdown.propTypes = { + isStyleAutoSuggest: PropTypes.bool, + isStyleSearchBox: PropTypes.bool, +}; + +IndustryDropdown.defaultProps = { + isStyleAutoSuggest: false, + isStyleSearchBox: false, +}; + export default IndustryDropdown; diff --git a/src/components/skills-quiz/SearchJobCard.jsx b/src/components/skills-quiz/SearchJobCard.jsx index 8216314ebd..4bbd6b22b0 100644 --- a/src/components/skills-quiz/SearchJobCard.jsx +++ b/src/components/skills-quiz/SearchJobCard.jsx @@ -5,15 +5,17 @@ import PropTypes from 'prop-types'; import { SearchContext } from '@edx/frontend-enterprise-catalog-search'; import { SkillsContext } from './SkillsContextProvider'; import { SET_KEY_VALUE } from './data/constants'; +import JobCardComponentV2 from '../skills-quiz-v2/JobCardComponent'; import JobCardComponent from './JobCardComponent'; import { JOB_FILTERS } from './constants'; -const SearchJobCard = ({ index }) => { +const SearchJobCard = ({ index, isSkillQuizV2, courseIndex }) => { const { refinements } = useContext(SearchContext); const { name: jobs, current_job: currentJob } = refinements; const [isLoading, setIsLoading] = useState(true); const { dispatch, state } = useContext(SkillsContext); const { interestedJobs } = state; + const jobsToFetch = useMemo(() => { const jobsArray = []; if (jobs) { @@ -68,7 +70,11 @@ const SearchJobCard = ({ index }) => { } }, [dispatch, index, currentJob, jobToFetch]); - return ; + return ( + isSkillQuizV2 + ? + : + ); }; SearchJobCard.propTypes = { @@ -77,6 +83,17 @@ SearchJobCard.propTypes = { indexName: PropTypes.string, search: PropTypes.func.isRequired, }).isRequired, + courseIndex: PropTypes.shape({ + appId: PropTypes.string, + indexName: PropTypes.string, + search: PropTypes.func.isRequired, + }), + isSkillQuizV2: PropTypes.bool, +}; + +SearchJobCard.defaultProps = { + courseIndex: undefined, + isSkillQuizV2: false, }; export default SearchJobCard; diff --git a/src/components/skills-quiz/SearchJobDropdown.jsx b/src/components/skills-quiz/SearchJobDropdown.jsx index b38f5a77fe..d781f86365 100644 --- a/src/components/skills-quiz/SearchJobDropdown.jsx +++ b/src/components/skills-quiz/SearchJobDropdown.jsx @@ -1,10 +1,14 @@ import React, { useContext } from 'react'; import { SearchContext } from '@edx/frontend-enterprise-catalog-search'; import FacetListRefinement from '@edx/frontend-enterprise-catalog-search/FacetListRefinement'; - +import PropTypes from 'prop-types'; import { DESIRED_JOB_FACET } from './constants'; -const SearchJobDropdown = () => { +const SearchJobDropdown = ({ + isStyleAutoSuggest, + isChip, + isStyleSearchBox, +}) => { const { refinements } = useContext(SearchContext); const { title, attribute, typeaheadOptions } = DESIRED_JOB_FACET; @@ -12,6 +16,7 @@ const SearchJobDropdown = () => { { doRefinement={false} showBadge={false} variant="default" + isStyleAutoSuggest={isStyleAutoSuggest} + isChip={isChip} + isStyleSearchBox={isStyleSearchBox} /> ); }; +SearchJobDropdown.propTypes = { + isStyleAutoSuggest: PropTypes.bool, + isChip: PropTypes.bool, + isStyleSearchBox: PropTypes.bool, +}; + +SearchJobDropdown.defaultProps = { + isStyleAutoSuggest: false, + isChip: false, + isStyleSearchBox: false, +}; export default SearchJobDropdown; diff --git a/src/components/skills-quiz/SkillsQuiz.jsx b/src/components/skills-quiz/SkillsQuiz.jsx index d2f6c34e4a..cf81e3846b 100644 --- a/src/components/skills-quiz/SkillsQuiz.jsx +++ b/src/components/skills-quiz/SkillsQuiz.jsx @@ -4,18 +4,17 @@ import { SearchData } from '@edx/frontend-enterprise-catalog-search'; import { AppContext } from '@edx/frontend-platform/react'; -import { - Container, Row, -} from '@edx/paragon'; +import { Container, Row } from '@edx/paragon'; import { MainContent } from '../layout'; import SkillsQuizStepper from './SkillsQuizStepper'; import { SkillsContextProvider } from './SkillsContextProvider'; +import SkillsQuizV2 from '../skills-quiz-v2/SkillsQuiz'; const SkillsQuiz = () => { const { enterpriseConfig } = useContext(AppContext); const PAGE_TITLE = `Skills Quiz - ${enterpriseConfig.name}`; - + const v1 = false; return ( <> @@ -24,12 +23,15 @@ const SkillsQuiz = () => { - + {v1 ? ( + + ) : ( + + )} - ); diff --git a/src/components/skills-quiz/SkillsQuizStepper.jsx b/src/components/skills-quiz/SkillsQuizStepper.jsx index 8de02b35d2..67711e4b5c 100644 --- a/src/components/skills-quiz/SkillsQuizStepper.jsx +++ b/src/components/skills-quiz/SkillsQuizStepper.jsx @@ -1,10 +1,16 @@ /* eslint-disable object-curly-newline */ import React, { useEffect, useState, useContext, useMemo } from 'react'; +import PropTypes from 'prop-types'; import { - Button, Stepper, ModalDialog, Container, Form, Stack, + Button, + Stepper, + ModalDialog, + Container, + Form, + Stack, } from '@edx/paragon'; import algoliasearch from 'algoliasearch/lite'; -import { Configure, InstantSearch } from 'react-instantsearch-dom'; +import { InstantSearch } from 'react-instantsearch-dom'; import { getConfig } from '@edx/frontend-platform/config'; import { SearchContext } from '@edx/frontend-enterprise-catalog-search'; import { useHistory } from 'react-router-dom'; @@ -33,7 +39,6 @@ import { STEP3, SKILLS_QUIZ_SEARCH_PAGE_MESSAGE, GOAL_DROPDOWN_DEFAULT_OPTION, - JOB_FILTERS, } from './constants'; import { SkillsContext } from './SkillsContextProvider'; import { SET_KEY_VALUE } from './data/constants'; @@ -42,27 +47,29 @@ import TopSkillsOverview from './TopSkillsOverview'; import SkillsQuizHeader from './SkillsQuizHeader'; import headerImage from './images/headerImage.png'; -import { saveDesiredRoleForCareerChart, saveSkillsGoalsAndJobsUserSelected } from './data/utils'; +import { saveSkillsGoalsAndJobsUserSelected } from './data/utils'; import { fetchCourseEnrollments } from './data/service'; -const SkillsQuizStepper = () => { +const SkillsQuizStepper = ({ isStyleAutoSuggest }) => { const config = getConfig(); const { userId } = getAuthenticatedUser(); - const [searchClient, courseIndex, jobIndex] = useMemo( - () => { - const client = algoliasearch( - config.ALGOLIA_APP_ID, - config.ALGOLIA_SEARCH_API_KEY, - ); - const cIndex = client.initIndex(config.ALGOLIA_INDEX_NAME); - const jIndex = client.initIndex(config.ALGOLIA_INDEX_NAME_JOBS); - return [client, cIndex, jIndex]; - }, - [config.ALGOLIA_APP_ID, config.ALGOLIA_INDEX_NAME, config.ALGOLIA_INDEX_NAME_JOBS, config.ALGOLIA_SEARCH_API_KEY], - ); + const [searchClient, courseIndex, jobIndex] = useMemo(() => { + const client = algoliasearch( + config.ALGOLIA_APP_ID, + config.ALGOLIA_SEARCH_API_KEY, + ); + const cIndex = client.initIndex(config.ALGOLIA_INDEX_NAME); + const jIndex = client.initIndex(config.ALGOLIA_INDEX_NAME_JOBS); + return [client, cIndex, jIndex]; + }, [ + config.ALGOLIA_APP_ID, + config.ALGOLIA_INDEX_NAME, + config.ALGOLIA_INDEX_NAME_JOBS, + config.ALGOLIA_SEARCH_API_KEY, + ]); const [currentStep, setCurrentStep] = useState(STEP1); const [isStudentChecked, setIsStudentChecked] = useState(false); - const handleIsStudentCheckedChange = e => setIsStudentChecked(e.target.checked); + const handleIsStudentCheckedChange = (e) => setIsStudentChecked(e.target.checked); const { state: { selectedJob, goal, currentJobRole, interestedJobs }, @@ -90,12 +97,14 @@ const SkillsQuizStepper = () => { const flipToRecommendedCourses = () => { saveSkillsGoalsAndJobsUserSelected(goal, currentJobRole, interestedJobs); - saveDesiredRoleForCareerChart(goal, currentJobRole, interestedJobs); // show courses if learner has selected skills or jobs. if (goalExceptImproveAndJobSelected) { // verify if selectedJob is still checked and within first 3 jobs else // set first job as selected by default to show courses. - if (jobs?.length > 0 && ((selectedJob && !jobs?.includes(selectedJob)) || !selectedJob)) { + if ( + jobs?.length > 0 + && ((selectedJob && !jobs?.includes(selectedJob)) || !selectedJob) + ) { sendEnterpriseTrackEvent( enterpriseConfig.uuid, 'edx.ui.enterprise.learner_portal.skills_quiz.continue.clicked', @@ -112,7 +121,11 @@ const SkillsQuizStepper = () => { sendEnterpriseTrackEvent( enterpriseConfig.uuid, 'edx.ui.enterprise.learner_portal.skills_quiz.continue.clicked', - { userId, enterprise: enterpriseConfig.slug, selectedJob: currentJob[0] }, + { + userId, + enterprise: enterpriseConfig.slug, + selectedJob: currentJob[0], + }, ); skillsDispatch({ type: SET_KEY_VALUE, @@ -132,7 +145,10 @@ const SkillsQuizStepper = () => { }, [enterpriseConfig.slug, enterpriseConfig.uuid, userId]); // will be true if goal changed not because of first render. - const [industryAndJobsDropdownsVisible, setIndustryAndJobsDropdownsVisible] = useState(false); + const [ + industryAndJobsDropdownsVisible, + setIndustryAndJobsDropdownsVisible, + ] = useState(false); useEffect(() => { if (goal !== GOAL_DROPDOWN_DEFAULT_OPTION) { @@ -150,7 +166,9 @@ const SkillsQuizStepper = () => { try { const response = await fetchCourseEnrollments(); const enrolledCourses = camelCaseObject(response.data); - const enrolledCourseIds = enrolledCourses.map((course) => course.courseDetails.courseId); + const enrolledCourseIds = enrolledCourses.map( + (course) => course.courseDetails.courseId, + ); skillsDispatch({ type: SET_KEY_VALUE, key: 'enrolledCourseIds', @@ -175,9 +193,7 @@ const SkillsQuizStepper = () => { onClose={closeSkillsQuiz} > - + @@ -186,9 +202,7 @@ const SkillsQuizStepper = () => {
-

- {SKILLS_QUIZ_SEARCH_PAGE_MESSAGE} -

+

{SKILLS_QUIZ_SEARCH_PAGE_MESSAGE}

First, tell us a bit more about what you want to achieve.

@@ -201,26 +215,29 @@ const SkillsQuizStepper = () => { indexName={config.ALGOLIA_INDEX_NAME_JOBS} searchClient={searchClient} > -
- Second, which industry describes where you work? (select one, or leave blank) + Second, which industry describes where you work? (select + one, or leave blank)
- +
-
-

- Next, tell us about your current job title. -

- +

+ Next, tell us about your current job title. +

+
+ I am currently a student @@ -228,32 +245,40 @@ const SkillsQuizStepper = () => {
- {goal !== DROPDOWN_OPTION_IMPROVE_CURRENT_ROLE - ? ( - <> -

- Lastly, tell us about career paths you're interested in (select up to three) -

-
- -
- - ) : null} + {goal !== DROPDOWN_OPTION_IMPROVE_CURRENT_ROLE ? ( + <> +

+ Lastly, tell us about career paths you're + interested in (select up to three) +

+
+ +
+ + ) : null}
)} {industryAndJobsDropdownsVisible && ( <> - {goalExceptImproveAndJobSelected - ? : null} - {improveGoalAndCurrentJobSelected - ? : null} + {goalExceptImproveAndJobSelected ? ( + + ) : null} + {improveGoalAndCurrentJobSelected ? ( + + ) : null} )}
- +

Start Exploring Courses!

@@ -263,7 +288,8 @@ const SkillsQuizStepper = () => {
- {(selectedJob || goal === DROPDOWN_OPTION_IMPROVE_CURRENT_ROLE) && ( + {(selectedJob + || goal === DROPDOWN_OPTION_IMPROVE_CURRENT_ROLE) && ( @@ -273,12 +299,18 @@ const SkillsQuizStepper = () => {
-
- +
@@ -297,14 +329,20 @@ const SkillsQuizStepper = () => { - - @@ -316,4 +354,12 @@ const SkillsQuizStepper = () => { ); }; +SkillsQuizStepper.propTypes = { + isStyleAutoSuggest: PropTypes.bool, +}; + +SkillsQuizStepper.defaultProps = { + isStyleAutoSuggest: false, +}; + export default SkillsQuizStepper; diff --git a/src/components/skills-quiz/tests/SkillsQuiz.test.jsx b/src/components/skills-quiz/tests/SkillsQuiz.test.jsx index 616a11e538..41de65ba91 100644 --- a/src/components/skills-quiz/tests/SkillsQuiz.test.jsx +++ b/src/components/skills-quiz/tests/SkillsQuiz.test.jsx @@ -4,7 +4,6 @@ import { screen } from '@testing-library/react'; import { AppContext } from '@edx/frontend-platform/react'; import { SearchData } from '@edx/frontend-enterprise-catalog-search'; import { UserSubsidyContext } from '../../enterprise-user-subsidy'; -import { SKILLS_QUIZ_SEARCH_PAGE_MESSAGE } from '../constants'; import { renderWithRouter, @@ -86,6 +85,6 @@ describe('', () => { , { route: '/test/skills-quiz/' }, ); - expect(screen.getByText(SKILLS_QUIZ_SEARCH_PAGE_MESSAGE)).toBeTruthy(); + expect(screen.getByText('Skills builder')).toBeTruthy(); }); }); diff --git a/src/components/skills-quiz/tests/SkillsQuizPage.test.jsx b/src/components/skills-quiz/tests/SkillsQuizPage.test.jsx index eb72f54891..63afa9f848 100644 --- a/src/components/skills-quiz/tests/SkillsQuizPage.test.jsx +++ b/src/components/skills-quiz/tests/SkillsQuizPage.test.jsx @@ -91,7 +91,7 @@ describe('SkillsQuizPage', () => { , { route: '/test/skills-quiz/' }, ); - expect(screen.getByText(SKILLS_QUIZ_SEARCH_PAGE_MESSAGE)).toBeInTheDocument(); + expect(screen.getByText('Let edX be your guide')).toBeInTheDocument(); }); it('should render null', async () => { renderWithRouter(