Skip to content

Commit

Permalink
feat: integrate new pathway section in academy detail page (#1068)
Browse files Browse the repository at this point in the history
  • Loading branch information
jajjibhai008 committed Apr 29, 2024
1 parent b1b9c02 commit 648b55e
Show file tree
Hide file tree
Showing 8 changed files with 291 additions and 102 deletions.
61 changes: 15 additions & 46 deletions src/components/academies/AcademyContentCard.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,8 @@ import { FormattedMessage, useIntl } from '@edx/frontend-platform/i18n';
import { camelCaseObject } from '@edx/frontend-platform/utils';
import { v4 as uuidv4 } from 'uuid';
import PropTypes from 'prop-types';
import { LEARNING_TYPE_COURSE, LEARNING_TYPE_EXECUTIVE_EDUCATION, LEARNING_TYPE_PATHWAY } from '@edx/frontend-enterprise-catalog-search/data/constants';
import { LEARNING_TYPE_COURSE, LEARNING_TYPE_EXECUTIVE_EDUCATION } from '@edx/frontend-enterprise-catalog-search/data/constants';
import SearchCourseCard from '../search/SearchCourseCard';
import SearchPathwayCard from '../pathway/SearchPathwayCard';
import { useEnterpriseCustomer } from '../app/data';

const AcademyContentCard = ({
Expand All @@ -26,7 +25,6 @@ const AcademyContentCard = ({
const intl = useIntl();
const ocmCourses = [];
const execEdCourses = [];
const pathways = [];
const maxCoursesToShow = 4;

useEffect(
Expand Down Expand Up @@ -66,8 +64,6 @@ const AcademyContentCard = ({
ocmCourses.push(course);
} else if (course.learningType === LEARNING_TYPE_EXECUTIVE_EDUCATION) {
execEdCourses.push(course);
} else if (course.learningType === LEARNING_TYPE_PATHWAY) {
pathways.push(course);
}
});

Expand Down Expand Up @@ -131,7 +127,7 @@ const AcademyContentCard = ({
<div className={additionalClass}>
<div className="d-flex flex-row align-items-center justify-content-between mt-5">
<h3 data-testid={titleTestId} className="font-weight-normal">{title}</h3>
{contentType !== LEARNING_TYPE_PATHWAY && contentLength > 4 && (
{contentLength > 4 && (
<Button
className=""
variant="link"
Expand All @@ -147,26 +143,17 @@ const AcademyContentCard = ({
xs: 12, md: 6, lg: 4, xl: 3,
}}
>
{contentType !== LEARNING_TYPE_PATHWAY
? content?.map(course => (
<SearchCourseCard
key={`academy-course-${uuidv4()}`}
data-testid="academy-course-card"
hit={course}
parentRoute={{
label: academyTitle,
to: academyURL,
}}
/>
))
: content?.map(pathway => (
<SearchPathwayCard
key={`academy-pathway-${uuidv4()}`}
data-testid="academy-pathways-card"
hit={pathway}
isAcademyPathway
/>
))}
{content?.map(course => (
<SearchCourseCard
key={`academy-course-${uuidv4()}`}
data-testid="academy-course-card"
hit={course}
parentRoute={{
label: academyTitle,
to: academyURL,
}}
/>
))}
</CardGrid>
</div>
);
Expand All @@ -179,7 +166,7 @@ const AcademyContentCard = ({
};
return (
<>
<div className="academy-tags mb-3">
<div className="academy-tags">
{tags.map(tag => (
<Button
className="academy-tag"
Expand Down Expand Up @@ -209,7 +196,7 @@ const AcademyContentCard = ({
{
isAlgoliaLoading ? (
<div className="d-flex justify-content-center align-items-center">
<Spinner animation="border" className="mie-3" screenReaderText="loading" />
<Spinner animation="border" className="mie-3 m-3" screenReaderText="loading" />
</div>
) : (
<>
Expand Down Expand Up @@ -249,24 +236,6 @@ const AcademyContentCard = ({
titleTestId: 'academy-ocm-courses-title',
subtitleTestId: 'academy-ocm-courses-subtitle',
})}
{renderableContent({
content: pathways,
contentLength: pathways?.length,
contentType: LEARNING_TYPE_PATHWAY,
title: intl.formatMessage({
id: 'academy.detail.page.pathways.section.title',
defaultMessage: 'Pathways',
description: 'Title for the pathways section on the academy detail page.',
}),
subtitle: intl.formatMessage({
id: 'academy.detail.page.pathways.section.subtitle',
defaultMessage: 'Not sure where to start? Try one of our recommended learning tracks.',
description: 'Subtitle for the pathways section on the academy detail page.',
}),
additionalClass: 'academy-pathways-container',
titleTestId: 'academy-pathway-title',
subtitleTestId: 'academy-pathway-subtitle',
})}
</>
)
}
Expand Down
71 changes: 40 additions & 31 deletions src/components/academies/AcademyDetailPage.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@ import { getConfig } from '@edx/frontend-platform/config';
import { ArrowDownward } from '@openedx/paragon/icons';
import NotFoundPage from '../NotFoundPage';
import './styles/Academy.scss';
import { isObjEmpty, useAcademyDetails, useEnterpriseCustomer } from '../app/data';
import PathwaysSection from './PathwaysSection';
import AcademyContentCard from './AcademyContentCard';
import { useAcademyDetails, useEnterpriseCustomer } from '../app/data';
import { useAcademyPathwayData } from './data/hooks';

const AcademyDetailPage = () => {
const config = getConfig();
Expand All @@ -34,6 +36,8 @@ const AcademyDetailPage = () => {
[config.ALGOLIA_APP_ID, config.ALGOLIA_INDEX_NAME, config.ALGOLIA_SEARCH_API_KEY],
);

const [pathway] = useAcademyPathwayData(academyUUID, courseIndex);

if (!academy) {
return (
<NotFoundPage
Expand All @@ -50,7 +54,6 @@ const AcademyDetailPage = () => {
/>
);
}

return (
<>
<Container size="lg" className="pt-3">
Expand All @@ -73,37 +76,41 @@ const AcademyDetailPage = () => {
values={{ academyTitle: academy?.title || 'Academy' }}
/>
</h1>
<div>
<h3 data-testid="academy-instruction-header">
<FormattedMessage
id="academy.detail.page.instruction.header"
defaultMessage="Follow a recommended pathway - or select individual courses"
description="Header for pathways and course selection instructions in a specific academy on the academy detail page"
/>
</h3>
<p data-testid="academy-instruction-description">
<FormattedMessage
id="academy.detail.page.instruction.description"
defaultMessage="Pathways are curated roadmaps through the academy’s content designed specifically for your learning goals. Or select a specific course from Executive Education or Self-paced courses in this Academy."
description="Description for pathways and course selection in a specific academy on the academy detail page"
/>
</p>
</div>
<div data-testid="academies-jump-link" className="mt-3 mb-4 text-right mr-5">
<Link to="#academy-all-courses">
<FormattedMessage
id="academy.detail.page.view.all.courses.link"
defaultMessage="View all {academyTitle} Academy Courses"
description="Link text to view all courses for a specific academy on the academy detail page"
values={{ academyTitle: academy?.title || '' }}
/>
<ArrowDownward />
</Link>
</div>
{!isObjEmpty(pathway) && (
<>
<div>
<h3 data-testid="academy-instruction-header">
<FormattedMessage
id="academy.detail.page.instruction.header"
defaultMessage="Follow a recommended pathway - or select individual courses"
description="Header for pathways and course selection instructions in a specific academy on the academy detail page"
/>
</h3>
<p data-testid="academy-instruction-description" className="mb-0">
<FormattedMessage
id="academy.detail.page.instruction.description"
defaultMessage="Pathways are curated roadmaps through the academy’s content designed specifically for your learning goals. Or select a specific course from Executive Education or Self-paced courses in this Academy."
description="Description for pathways and course selection in a specific academy on the academy detail page"
/>
</p>
</div>
<div data-testid="academies-jump-link" className="mb-4 text-right mr-5">
<Link to="#academy-all-courses">
<FormattedMessage
id="academy.detail.page.view.all.courses.link"
defaultMessage="View all {academyTitle} Academy Courses"
description="Link text to view all courses for a specific academy on the academy detail page"
values={{ academyTitle: academy?.title || '' }}
/>
<ArrowDownward />
</Link>
</div>
</>
)}
</div>
</Container>
{/* new pathway sectoin will come here */}
<Container size="lg" className="pb-4 mt-4">
{!isObjEmpty(pathway) && <PathwaysSection pathwayData={pathway} />}
<Container size="lg">
<h3 id="academy-all-courses" data-testid="academy-all-courses-title">
<FormattedMessage
id="academy.detail.page.all.courses.title"
Expand All @@ -112,6 +119,8 @@ const AcademyDetailPage = () => {
values={{ academyTitle: academy?.title }}
/>
</h3>
</Container>
<Container size="lg" className="pb-4">
<AcademyContentCard
courseIndex={courseIndex}
academyUUID={academyUUID}
Expand Down
83 changes: 63 additions & 20 deletions src/components/academies/PathwaysSection.jsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,67 @@
import { Container } from '@openedx/paragon';
import { Container, useToggle } from '@openedx/paragon';
import React from 'react';
import PropTypes from 'prop-types';
import DOMPurify from 'dompurify';
import { FormattedMessage } from '@edx/frontend-platform/i18n';
import PathwayModal from '../pathway/PathwayModal';

const PathwaysSection = () => (
<Container className="pathway-section mb-4">
<Container size="lg" className="inner-container pr-0">
<div className="row">
<div className="col">
<p className="eyebrow">Pathway</p>
<h1 className="pathway-title">Ai for Leaders</h1>
<p className="pathway-description">Lead with AI. This pathway will introduce you to basics of AI, as well as cover core
concepts for how to use and apply AI responsibly to support the strategy and growth
of your business.
</p>
</div>
<div className="col d-flex justify-content-center align-items-center">
<button className="launch-btn" type="button">Launch Pathway</button>
</div>
</div>
</Container>
</Container>
);
const PathwaysSection = ({ pathwayData }) => {
const [isLearnerPathwayModalOpen, openLearnerPathwayModal, onClose] = useToggle(false);
const handleCardClick = () => {
openLearnerPathwayModal();
};
return (
<>
<PathwayModal
learnerPathwayUuid={pathwayData?.pathwayUuid}
isOpen={isLearnerPathwayModalOpen}
onClose={onClose}
/>
<Container className="pathway-section mb-5">
<Container size="lg" className="inner-container pr-0">
<div className="row">
<div className="col">
<p className="eyebrow">
<FormattedMessage
id="academy.detail.page.pathway.section.heading"
defaultMessage="Pathway"
description="Heading for the pathway section on the academy detail page"
/>
</p>
<h1 className="pathway-title">{pathwayData.title}</h1>
<div
// eslint-disable-next-line react/no-danger
dangerouslySetInnerHTML={{
__html: DOMPurify.sanitize(
pathwayData.overview,
{ USE_PROFILES: { html: true } },
),
}}
className="pathway-description"
/>
</div>
<div className="col d-flex justify-content-center align-items-center">
<button className="launch-btn" type="button" onClick={handleCardClick}>
<FormattedMessage
id="academy.detail.page.pathway.section.launch.button"
defaultMessage="Launch Pathway"
description="Button to launch the pathway"
/>
</button>
</div>
</div>
</Container>
</Container>
</>
);
};

PathwaysSection.propTypes = {
pathwayData: PropTypes.shape({
title: PropTypes.string,
overview: PropTypes.string,
pathwayUuid: PropTypes.string,
}).isRequired,
};

export default PathwaysSection;
37 changes: 37 additions & 0 deletions src/components/academies/data/hooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { useEffect, useState } from 'react';
import { logError } from '@edx/frontend-platform/logging';

import { getAcademies, getAcademyMetadata } from './service';
import LearnerPathwayService from '../../pathway/data/service';

export function useAcademyMetadata(academyUUID) {
const [academyMetadata, setAcademyMetadata] = useState({});
Expand Down Expand Up @@ -51,3 +52,39 @@ export const useAcademies = (enterpriseCustomerUUID) => {

return [academies, isLoading, fetchError];
};

export const useAcademyPathwayData = (academyUUID, courseIndex) => {
const [pathway, setPathway] = useState({});
const [fetchError, setFetchError] = useState(null);
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
const fetchPathway = async () => {
setIsLoading(true);
try {
const { hits: pathwayHits, nbHits: nbPathwayHits } = await courseIndex.search('', {
filters: `(content_type:learnerpathway) AND academy_uuids:${academyUUID}`,
hitsPerPage: 1,
page: 0,
});
// for now we have only one pathway per academy
if (nbPathwayHits > 0 && pathwayHits[0]?.uuid) {
const learnerPathwayUuid = pathwayHits[0].uuid;
const learnerPathwayService = new LearnerPathwayService({ learnerPathwayUuid });
const data = await learnerPathwayService.fetchLearnerPathwayData();
setPathway({ title: data?.title, overview: data?.overview, pathwayUuid: learnerPathwayUuid });
} else {
setPathway({});
}
} catch (error) {
setFetchError(error);
logError(error);
} finally {
setIsLoading(false);
}
};

fetchPathway();
}, [academyUUID, courseIndex]);

return [pathway, isLoading, fetchError];
};
Loading

0 comments on commit 648b55e

Please sign in to comment.