Skip to content
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

feat: skills quiz v2 enhancements #919

Merged
merged 2 commits into from
Jan 15, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Original file line number Diff line number Diff line change
Expand Up @@ -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);
});
});
18 changes: 18 additions & 0 deletions src/components/enterprise-user-subsidy/data/hooks/hooks.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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: [] },
});
Expand All @@ -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 }) => (
Expand Down
56 changes: 45 additions & 11 deletions src/components/skills-quiz-v2/JobCardComponent.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[curious/clarification] Is this fetchCourseEnrollments function returning all enrollments for the learner across all enterprises? Should we be referring to only the course enrollments relevant for the currently viewed enterprise customer, instead of all enrollments?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@adamstankiewicz Yes, the fetchCourseEnrollments returns a list of all course enrollments for a user.
In Skills Quiz V1, we utilized this function to fetch the list of all enrollments and for consistency, I replicated the same approach in V2 as well.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mahamakifdar19 While this isn't blocking feedback, I still might recommend verifying with the team / Product whether this should be using all course enrollments vs. just the course enrollments associated with the specific enterprise customer. The Enterprise Learner Portal generally isn't supposed to be aware of B2C enrollments.

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 ? (
<>
<div className="skills-quiz-v2-job-card">
<SelectableBox.Set
type="radio"
value={jobSelected}
Expand Down Expand Up @@ -68,7 +102,7 @@ const JobCardComponent = ({
</SelectableBox>
))}
</SelectableBox.Set>
{(jobSelected || goal === DROPDOWN_OPTION_IMPROVE_CURRENT_ROLE) && (
{jobs?.length > 0 && (
<>
<TopSkillsOverview index={jobIndex} />
<Stack gap={4}>
Expand All @@ -89,7 +123,7 @@ const JobCardComponent = ({
{ showMoreRecommendedCourses && <SkillsCourses index={courseIndex} />}
</>
)}
</>
</div>
) : (
<Spinner
animation="border"
Expand Down
35 changes: 0 additions & 35 deletions src/components/skills-quiz-v2/ProgramCard.jsx

This file was deleted.

33 changes: 17 additions & 16 deletions src/components/skills-quiz-v2/SkillsQuiz.jsx
Original file line number Diff line number Diff line change
@@ -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 (
<>
Expand Down Expand Up @@ -58,7 +69,7 @@ const SkillsQuizV2 = ({ isStyleAutoSuggest }) => {
<ModalDialog
title="Skills Quiz"
size="fullscreen"
className="bg-light-200 skills-quiz-modal"
className="bg-light-200 skills-quiz-modal skills-quiz-v2"
isOpen
onClose={open}
>
Expand All @@ -74,16 +85,6 @@ const SkillsQuizV2 = ({ isStyleAutoSuggest }) => {
<p className="text-gray-600 text-justify">{text}</p>
</div>
<SkillQuizForm isStyleAutoSuggest={isStyleAutoSuggest} />
<div className="cards-display">
<p className="pgn__form-label">
Boot camps for a web technology specialist
</p>
<div className="card-container">
{webTechBootCamps.map((bootcamp) => (
<ProgramCard {...bootcamp} />
))}
</div>
</div>
</div>
</ModalDialog.Body>
</ModalDialog>
Expand Down
3 changes: 1 addition & 2 deletions src/components/skills-quiz-v2/SkillsQuizForm.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,7 @@ const SkillQuizForm = ({ isStyleAutoSuggest }) => {
<SearchJobDropdown key="search" isStyleSearchBox isChip />
<Button
variant="link"
size="inline"
className="mb-2 mb-sm-0 btn"
className="mb-2 mb-sm-0 advanced-options-btn my-2 mx-n3"
onClick={() => setHide(!hide)}
>
{!hide ? 'Hide advanced options' : 'Show advanced options'}
Expand Down
18 changes: 0 additions & 18 deletions src/components/skills-quiz-v2/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,4 @@ 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';
69 changes: 37 additions & 32 deletions src/components/skills-quiz-v2/styles/_Body.scss
Original file line number Diff line number Diff line change
@@ -1,37 +1,42 @@
$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;
}
.skills-quiz-v2 {
$max-sm-modal-width: 28rem !important;
$min-sm-modal-height: 16rem !important;

.btn {
margin: 7px -5px;
}
.page-body {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}

.form-auto-suggest {
.form-control {
color: $gray-500;
.form {
width: 65%;
}

.pgn__form-group {
width: 101%;
}
}

.modal-small {
max-width: $max-sm-modal-width;
min-height: $min-sm-modal-height;
.text {
width: 65%;
}

.pgn__form-label {
font-weight: bold;
}

.advanced-options-btn {
text-decoration: none;
mahamakifdar19 marked this conversation as resolved.
Show resolved Hide resolved
}

.form-auto-suggest {
.form-control {
color: $gray-500;
}
}

.modal-small {
max-width: $max-sm-modal-width;
min-height: $min-sm-modal-height;
}
}
Loading