diff --git a/src/widgets/ProductRecommendations/components/ProductCard.jsx b/src/widgets/ProductRecommendations/components/ProductCard.jsx index e69de29..b58498b 100644 --- a/src/widgets/ProductRecommendations/components/ProductCard.jsx +++ b/src/widgets/ProductRecommendations/components/ProductCard.jsx @@ -0,0 +1,78 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { + Badge, + Card, + Truncate, + Hyperlink, +} from '@edx/paragon'; + +const ProductCard = ({ + title, + subtitle, + headerImage, + schoolLogo, + courseType, + url, +}) => { + const courseTypeToProductTypeMap = { + course: 'Course', + 'verified-audit': 'Course', + verified: 'Course', + audit: 'Course', + 'credit-verified-audit': 'Course', + 'spoc-verified-audit': 'Course', + professional: 'Professional Certificate', + 'bootcamp-2u': 'Boot Camp', + 'executive-education-2u': 'Executive Education', + 'executive-education': 'Executive Education', + masters: "Master's", + 'masters-verified-audit': "Master's", + }; + + const productType = courseTypeToProductTypeMap[courseType]; + + return ( +
+ + + + + {title} + + )} + subtitle={( + + {subtitle} + + )} + /> + +
+ {productType} +
+
+
+
+
+ ); +}; + +ProductCard.propTypes = { + title: PropTypes.string.isRequired, + subtitle: PropTypes.string.isRequired, + headerImage: PropTypes.string.isRequired, + schoolLogo: PropTypes.string.isRequired, + courseType: PropTypes.string.isRequired, + url: PropTypes.string.isRequired, +}; + +export default ProductCard; diff --git a/src/widgets/ProductRecommendations/components/ProductCardContainer.jsx b/src/widgets/ProductRecommendations/components/ProductCardContainer.jsx index e69de29..126f124 100644 --- a/src/widgets/ProductRecommendations/components/ProductCardContainer.jsx +++ b/src/widgets/ProductRecommendations/components/ProductCardContainer.jsx @@ -0,0 +1,67 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import classNames from 'classnames'; +import ProductCard from './ProductCard'; +import ProductCardHeader from './ProductCardHeader'; + +const ProductCardContainer = ({ courses }) => { + const courseTypes = [...new Set(courses.map((item) => item.courseType))]; + + return ( +
+ {courses + && courseTypes.map((type) => ( +
+ +
+ {courses + .filter((course) => course.courseType === type) + .map((item) => ( + + ))} +
+
+ ))} +
+ ); +}; + +ProductCardContainer.propTypes = { + courses: PropTypes.arrayOf( + PropTypes.shape({ + key: PropTypes.string, + uuid: PropTypes.string, + title: PropTypes.string, + image: PropTypes.shape({ + src: PropTypes.string, + }), + prospectusPath: PropTypes.string, + owners: PropTypes.arrayOf( + PropTypes.shape({ + key: PropTypes.string, + name: PropTypes.string, + logoImageUrl: PropTypes.string, + }), + ), + activeCourseRun: PropTypes.shape({ + key: PropTypes.string, + marketingUrl: PropTypes.string, + }), + courseType: PropTypes.string, + }), + ).isRequired, +}; + +export default ProductCardContainer; diff --git a/src/widgets/ProductRecommendations/components/ProductRecommendationsContainer.jsx b/src/widgets/ProductRecommendations/components/ProductRecommendationsContainer.jsx index ae8cea7..96c2d20 100644 --- a/src/widgets/ProductRecommendations/components/ProductRecommendationsContainer.jsx +++ b/src/widgets/ProductRecommendations/components/ProductRecommendationsContainer.jsx @@ -2,27 +2,25 @@ import React from 'react'; import { Container } from '@edx/paragon'; import { useIntl } from '@edx/frontend-platform/i18n'; -import messages from '../messages'; -import ProductCardHeader from './ProductCardHeader'; import './index.scss'; +import messages from '../messages'; +import ProductCardContainer from './ProductCardContainer'; +import mockCrossProductRecommendations from '../mockData'; const ProductRecommendationsContainer = () => { const { formatMessage } = useIntl(); - const mockRecommendations = ['executive-education', 'bootcamp-2u', 'course']; + const mockRecommendations = mockCrossProductRecommendations.courses; return (
-

{formatMessage(messages.recommendationsHeading)}

- {/* {mockRecommendations.forEach((recommendation) => ( - - ))} */} - {mockRecommendations.map((courseType) => ( - - ))} +

+ {formatMessage(messages.recommendationsHeading)} +

+
); diff --git a/src/widgets/ProductRecommendations/components/index.scss b/src/widgets/ProductRecommendations/components/index.scss index 3d848ca..d5cb491 100644 --- a/src/widgets/ProductRecommendations/components/index.scss +++ b/src/widgets/ProductRecommendations/components/index.scss @@ -1,8 +1,153 @@ +@import "@edx/paragon/scss/core/core"; + +$horizontal-card-gap: 20px; +$vertical-card-gap: 24px; +$card-height: 332px; +$header-height: 104px; +$card-width: 270px; .recommendations-container { - border: solid + border: solid; +} + +.base-card { + height: $card-height; + width: $card-width !important; + + p { + margin-bottom: 0; + } + + .pgn__card-image-cap { + height: $header-height; + object: { + fit: cover; + position: top center; + } + } + + .pgn__card-logo-cap { + bottom: -1.5rem; + object: { + fit: scale-down; + position: center center; + } + } + + .product-card-title { + font: { + size: 1.125rem; + weight: 700; + } + + line-height: 24px ; + } + + .product-card-subtitle { + font: { + size: 0.875rem; + weight: 400; + } + + line-height: 24px; + } + + .product-badge { + position: absolute; + bottom: 2.75rem; + } + + .footer-content { + position: absolute; + bottom: 1rem; + } + + &.light { + background-color: $white; + + .title { + color: $black; + } + + .subtitle { + color: $gray-700; + } + + .badge { + background-color: $light-500; + color: $black; + } + + .footer-content { + color: $gray-700; + } + } + + &.dark { + background-color: $primary-500; + + .pgn__card-header-title-md { + color: $white; + } + + .pgn__card-header-subtitle-md { + color: $light-200; + } + + .title { + color: $white; + } + + .subtitle { + color: $light-200; + } + + .badge { + background-color: $dark-200; + color: $white; + } + + .footer-content { + color: $light-200; + } + } } .base-card-link:hover { text-decoration: none; +} + +.base-card:hover { + box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.15), 0 0.125rem 0.5rem rgba(0, 0, 0, 0.15); +} + +.base-card-link .base-card { + display: flex; +} + +.product-card-container { + gap: $vertical-card-gap $horizontal-card-gap; + margin: 0 (-$horizontal-card-gap); + padding: 0 $horizontal-card-gap; + + .course-subcontainer { + display: flex; + gap: $vertical-card-gap $horizontal-card-gap; + } + + // .base-card-wrapper { + // flex: 0 1 calc(100% - #{$horizontal-card-gap}); + + // @include media-breakpoint-up(sm) { + // flex: 0 1 calc(50% - #{$horizontal-card-gap}); + // } + + // @include media-breakpoint-up(md) { + // flex: 0 1 calc(33.333% - #{$horizontal-card-gap}); + // } + + // @include media-breakpoint-up(xl) { + // flex: 0 1 calc(25% - #{$horizontal-card-gap}); + // } + // } } \ No newline at end of file diff --git a/src/widgets/ProductRecommendations/mockData.js b/src/widgets/ProductRecommendations/mockData.js new file mode 100644 index 0000000..550db97 --- /dev/null +++ b/src/widgets/ProductRecommendations/mockData.js @@ -0,0 +1,100 @@ +const mockCrossProductRecommendations = { + courses: [ + { + key: 'HarvardX+CRS', + uuid: 'fee0ed87-a122-46a8-b233-3e5c75c755d1', + title: 'CRISPR: Gene-editing Applications', + image: { + src: 'https://prod-discovery.edx-cdn.org/media/course/image/ed79a49b-64c1-48d2-afdc-054bf921e38d-6a76ceb47dea.small.jpg', + }, + prospectusPath: + 'course/harvard-vpal-crispr-gene-editing-applications-online-short-course', + owners: [ + { + key: 'HarvardX', + name: 'Harvard University', + logoImageUrl: + 'http://localhost:18381/media/organization/logos/ef72daf3-c9a1-4c00-ba37-b3514392bdcf-8839c516815a.png', + }, + ], + activeCourseRun: { + key: 'course-v1:HarvardX+CRS+1T2023', + marketingUrl: + 'course/crispr-gene-editing-applications-course-v1-harvardx-crs-1t2023?utm_source=discovery_worker&utm_medium=affiliate_partner', + }, + courseType: 'executive-education-2u', + }, + { + key: 'AdelaideX+BC24CYB', + uuid: 'dacb53da-35c7-4c6b-b8d0-d7d8c0c78cef', + title: 'Cybersecurity Boot Camp', + image: { + src: 'https://prod-discovery.edx-cdn.org/media/course/image/fbb62b99-9f85-4563-a67c-34ac827560bd-e3e6263b98fd.small.png', + }, + prospectusPath: + 'course/the-university-of-adelaide-cybersecurity-boot-camp', + owners: [ + { + key: 'AdelaideX', + name: 'University of Adelaide', + logoImageUrl: + 'http://localhost:18381/media/organization/logos/51d054b4-9589-4376-9e9f-15656f3c8d0e-f10fb90c3ff1.png', + }, + ], + activeCourseRun: { + key: 'course-v1:AdelaideX+BC24CYB+1T2023', + marketingUrl: + 'course/cybersecurity-boot-camp-course-v1-adelaidex-bc24cyb-1t2023?utm_source=discovery_worker&utm_medium=affiliate_partner', + }, + courseType: 'bootcamp-2u', + }, + { + key: 'AA+AA101', + uuid: '1e2cae8c-1c67-4067-a3c0-360543e6a9b8', + title: 'Data Analytics for Business', + image: { + src: 'https://prod-discovery.edx-cdn.org/media/course/image/1e2cae8c-1c67-4067-a3c0-360543e6a9b8-50beebb61f2e.small.png', + }, + prospectusPath: '/course/data-analytics-for-business', + owners: [ + { + key: 'GTx', + name: 'The Georgia Institute of Technology', + logoImageUrl: + 'https://prod-discovery.edx-cdn.org/organization/logos/8537d31f-01b4-40fd-b652-e17b38eefe41-7956b2a3cd04.png', + }, + ], + activeCourseRun: { + key: 'course-v1:GTx+MGT6203x+2T2023', + marketingUrl: + 'https://www.edx.org/course/data-analytics-for-business-course-v1gtxmgt6203x2t2023?utm_source=prospectus_worker&utm_medium=affiliate_partner', + }, + courseType: 'course', + }, + { + key: 'AA+AA101', + uuid: '876a65e7-425b-437b-bced-bdf8059fec81', + title: 'Western and Chinese Art: Masters and Classics', + image: { + src: 'https://prod-discovery.edx-cdn.org/media/course/image/876a65e7-425b-437b-bced-bdf8059fec81.small.jpg', + }, + prospectusPath: '/course/western-and-chinese-art-masters-and-classics', + owners: [ + { + key: 'TsinghuaX', + name: 'Tsinghua University', + logoImageUrl: + 'https://prod-discovery.edx-cdn.org/organization/logos/b5714409-b5f4-4c9d-9348-b0fecbaaddd6-780fbb6c72c7.png', + }, + ], + activeCourseRun: { + key: 'course-v1:TsinghuaX+00691153.x+1T2015', + marketingUrl: + 'https://www.edx.org/course/western-chinese-art-masters-classics-tsinghuax-00691153x?utm_source=prospectus_worker&utm_medium=affiliate_partner', + }, + courseType: 'course', + }, + ], +}; + +export default mockCrossProductRecommendations; diff --git a/src/widgets/RecommendationsPanel/index.jsx b/src/widgets/RecommendationsPanel/index.jsx index c091a8d..b6c68b5 100644 --- a/src/widgets/RecommendationsPanel/index.jsx +++ b/src/widgets/RecommendationsPanel/index.jsx @@ -4,6 +4,7 @@ import LookingForChallengeWidget from 'widgets/LookingForChallengeWidget'; import LoadingView from './LoadingView'; import LoadedView from './LoadedView'; import hooks from './hooks'; +import recommendedCoursesData from "../RecommendationsPanel/mockData"; export const RecommendationsPanel = () => { const { @@ -17,14 +18,17 @@ export const RecommendationsPanel = () => { if (isLoading) { return (); } - if (isLoaded && courses.length > 0) { - return ( - - ); - } - if (isFailed) { - return (); - } + + const newCourses = recommendedCoursesData.courses; + + // if (newCourses.length > 0) { + // return ( + // + // ); + // } + // if (isFailed) { + // return (); + // } // default fallback return (); };