diff --git a/frontends/api/package.json b/frontends/api/package.json index 2bbe941af1..0745ec8f54 100644 --- a/frontends/api/package.json +++ b/frontends/api/package.json @@ -29,7 +29,7 @@ "ol-test-utilities": "0.0.0" }, "dependencies": { - "@mitodl/mitxonline-api-axios": "^2026.3.26", + "@mitodl/mitxonline-api-axios": "^2026.4.23", "@tanstack/react-query": "^5.66.0", "axios": "^1.12.2", "tiny-invariant": "^1.3.3" diff --git a/frontends/api/src/mitxonline/test-utils/factories/enrollment.ts b/frontends/api/src/mitxonline/test-utils/factories/enrollment.ts index 1357679be5..b3d9baca88 100644 --- a/frontends/api/src/mitxonline/test-utils/factories/enrollment.ts +++ b/frontends/api/src/mitxonline/test-utils/factories/enrollment.ts @@ -88,7 +88,11 @@ const programEnrollmentV3: PartialFactory = ( link: `/certificate/program/${faker.string.uuid()}/`, } : null, - enrollment_mode: faker.helpers.arrayElement(["audit", "verified", null]), + enrollment_mode: faker.helpers.arrayElement([ + "audit", + "verified", + undefined, + ]), program: program, } return mergeOverrides(defaults, overrides) diff --git a/frontends/api/src/mitxonline/test-utils/factories/orders.ts b/frontends/api/src/mitxonline/test-utils/factories/orders.ts index dbab4806d3..89e5b66d9d 100644 --- a/frontends/api/src/mitxonline/test-utils/factories/orders.ts +++ b/frontends/api/src/mitxonline/test-utils/factories/orders.ts @@ -13,6 +13,7 @@ const transactionLine = ( total_paid: faker.commerce.price({ min: 50, max: 500 }), discount: "0.00", price: faker.commerce.price({ min: 50, max: 500 }), + content_type: "", ...overrides, }) diff --git a/frontends/api/src/mitxonline/test-utils/factories/pages.ts b/frontends/api/src/mitxonline/test-utils/factories/pages.ts index 6cba1acc51..e14b61a6ec 100644 --- a/frontends/api/src/mitxonline/test-utils/factories/pages.ts +++ b/frontends/api/src/mitxonline/test-utils/factories/pages.ts @@ -164,6 +164,32 @@ const coursePageItem: PartialFactory = (override) => { ], video_url: faker.internet.url(), what_you_learn: makeHTMLList(5), + how_youll_learn: [ + { + key: "learn_by_doing", + icon: "IconComputerBulb", + title: "Learn by doing", + text: "Apply what you learn with interactive exercises, projects, and real-world case studies.", + }, + { + key: "realworld_learning", + icon: "IconConnectedPeople", + title: "Real-world learning", + text: "Engage with real-world scenarios and examples to understand how concepts apply in practice.", + }, + { + key: "learn_from_others", + icon: "IconConnectedPeople", + title: "Learn from others", + text: "Collaborate with peers and learn from their perspectives through group projects and discussions.", + }, + { + key: "learn_on_demand", + icon: "IconComputerBulb", + title: "Learn on demand", + text: "Access course materials anytime, anywhere, and learn at your own pace.", + }, + ], } return mergeOverrides(defaults, override) } @@ -215,6 +241,32 @@ const programPageItem: PartialFactory = (override) => { faq_url: faker.internet.url(), about: makeHTMLParagraph(3), what_you_learn: makeHTMLList(5), + how_youll_learn: [ + { + key: "learn_by_doing", + icon: "IconComputerBulb", + title: "Learn by doing", + text: "Apply what you learn with interactive exercises, projects, and real-world case studies.", + }, + { + key: "realworld_learning", + icon: "IconConnectedPeople", + title: "Real-world learning", + text: "Engage with real-world scenarios and examples to understand how concepts apply in practice.", + }, + { + key: "learn_from_others", + icon: "IconConnectedPeople", + title: "Learn from others", + text: "Collaborate with peers and learn from their perspectives through group projects and discussions.", + }, + { + key: "learn_on_demand", + icon: "IconComputerBulb", + title: "Learn on demand", + text: "Access course materials anytime, anywhere, and learn at your own pace.", + }, + ], feature_image: featureImage(), video_url: faker.datatype.boolean() ? faker.internet.url() : null, faculty_section_title: "Meet your instructors", diff --git a/frontends/main/package.json b/frontends/main/package.json index a5c0c7c1e1..833ab6356c 100644 --- a/frontends/main/package.json +++ b/frontends/main/package.json @@ -14,7 +14,7 @@ "@emotion/styled": "^11.11.0", "@floating-ui/react": "^0.27.16", "@mitodl/course-search-utils": "^3.5.2", - "@mitodl/mitxonline-api-axios": "^2026.3.26", + "@mitodl/mitxonline-api-axios": "^2026.4.23", "@mitodl/smoot-design": "^6.24.0", "@mui/material": "^6.4.5", "@mui/material-nextjs": "^6.4.3", diff --git a/frontends/main/src/app-pages/DashboardPage/CoursewareDisplay/DashboardDialogs.test.tsx b/frontends/main/src/app-pages/DashboardPage/CoursewareDisplay/DashboardDialogs.test.tsx index 93687b1aff..db06027cb0 100644 --- a/frontends/main/src/app-pages/DashboardPage/CoursewareDisplay/DashboardDialogs.test.tsx +++ b/frontends/main/src/app-pages/DashboardPage/CoursewareDisplay/DashboardDialogs.test.tsx @@ -157,7 +157,7 @@ describe("UnenrollProgramDialog", () => { const programEnrollment = mitxonline.factories.enrollment.programEnrollmentV3({ - enrollment_mode: enrollmentMode, + enrollment_mode: enrollmentMode ? enrollmentMode : undefined, program: { display_mode: displayMode } as never, }) diff --git a/frontends/main/src/app-pages/ProductPages/CoursePage.tsx b/frontends/main/src/app-pages/ProductPages/CoursePage.tsx index d814ffbb86..0d64142ebf 100644 --- a/frontends/main/src/app-pages/ProductPages/CoursePage.tsx +++ b/frontends/main/src/app-pages/ProductPages/CoursePage.tsx @@ -16,7 +16,7 @@ import RawHTML from "./RawHTML" import AboutSection from "./AboutSection" import ProductPageTemplate from "./ProductPageTemplate" import WhatYoullLearnSection from "./WhatYoullLearnSection" -import HowYoullLearnSection, { DEFAULT_HOW_DATA } from "./HowYoullLearnSection" +import HowYoullLearnSection from "./HowYoullLearnSection" import { DEFAULT_RESOURCE_IMG } from "ol-utilities" import { isVerifiedEnrollmentMode } from "@/common/mitxonline" import { useFeatureFlagsLoaded } from "@/common/useFeatureFlagsLoaded" @@ -98,7 +98,7 @@ const CoursePage: React.FC = ({ readableId }) => { {page.what_you_learn ? ( ) : null} - + {page.prerequisites ? ( diff --git a/frontends/main/src/app-pages/ProductPages/HowYoullLearnSection.tsx b/frontends/main/src/app-pages/ProductPages/HowYoullLearnSection.tsx index a87adf9c72..4129b916f2 100644 --- a/frontends/main/src/app-pages/ProductPages/HowYoullLearnSection.tsx +++ b/frontends/main/src/app-pages/ProductPages/HowYoullLearnSection.tsx @@ -9,6 +9,11 @@ import IconCertificate from "@/public/images/product/icon_certificate.png" import IconComputerBulb from "@/public/images/product/icon_computer_lightbulb.png" import IconConnectedPeople from "@/public/images/product/icon_connected_people.png" +import type { + CoursePageItem, + ProgramPageItem, +} from "@mitodl/mitxonline-api-axios/v2" + const HowYoullLearnRoot = styled.section(({ theme }) => ({ display: "flex", flexDirection: "column", @@ -49,7 +54,7 @@ const HowYoullLearnHeader = styled.div({ alignItems: "center", }) -const HowYoullLearnIcon = styled(Image)({ +const HowYoullLearnIconImage = styled(Image)({ width: "80px", height: "50px", flexShrink: 0, @@ -69,64 +74,70 @@ const HowYoullLearnDescription = styled.p(({ theme }) => ({ }, })) -type HowYoullLearnItemData = { - icon: typeof IconComputerBulb - title: string - text: string -} +const HowYoullLearnIcon: React.FC<{ + src: string + width: number + height: number + alt: string +}> = ({ src, width, height, alt }) => { + let iconSrc: typeof IconBrains | null = null + switch (src) { + case "IconBrains": + iconSrc = IconBrains + break + case "IconCertificate": + iconSrc = IconCertificate + break + case "IconComputerBulb": + iconSrc = IconComputerBulb + break + case "IconConnectedPeople": + iconSrc = IconConnectedPeople + break + default: + console.warn(`Unknown how_youll_learn icon: ${src}`) + } -// Placeholder data — will be replaced by API-driven content. -const DEFAULT_HOW_DATA: HowYoullLearnItemData[] = [ - { - icon: IconConnectedPeople, - title: "Real-World Learning", - text: "Learn from MIT faculty and experts who ground their teaching in real-world cases rather than mathematical models, making the material approachable for all.", - }, - { - icon: IconBrains, - title: "Practical Application", - text: "Apply your new knowledge with hands-on, practical exercises drawn from healthcare, sports, finance, sustainability, and more.", - }, - { - icon: IconComputerBulb, - title: "AI-Enabled Support", - text: "Deepen your understanding of the course material and get help on assignments from AskTIM, the AI assistant built by MIT researchers.", - }, - { - icon: IconCertificate, - title: "Stackable Credentials", - text: "Earn an MIT Open Learning certificate at each milestone—module, course, and program—demonstrating your AI expertise. Available in paid courses only.", - }, -] + return ( + iconSrc && ( + + ) + ) +} const HowYoullLearnSection: React.FC<{ - data: HowYoullLearnItemData[] -}> = ({ data }) => { - return ( + page: CoursePageItem | ProgramPageItem +}> = ({ page }) => { + const filteredOptions = page.how_youll_learn ?? [] + + return filteredOptions.length > 0 ? ( How you'll learn - {data.map((item, index) => ( - + {filteredOptions.map((option) => ( + - {item.title} + {option.title} - {item.text} + {option.text} ))} - ) + ) : null } export default HowYoullLearnSection -export { DEFAULT_HOW_DATA } -export type { HowYoullLearnItemData } diff --git a/frontends/main/src/app-pages/ProductPages/ProgramAsCoursePage.tsx b/frontends/main/src/app-pages/ProductPages/ProgramAsCoursePage.tsx index 24cd3b1059..4bc11cd387 100644 --- a/frontends/main/src/app-pages/ProductPages/ProgramAsCoursePage.tsx +++ b/frontends/main/src/app-pages/ProductPages/ProgramAsCoursePage.tsx @@ -21,7 +21,7 @@ import UnstyledRawHTML from "@/components/UnstyledRawHTML/UnstyledRawHTML" import AboutSection from "./AboutSection" import ProductPageTemplate from "./ProductPageTemplate" import WhatYoullLearnSection from "./WhatYoullLearnSection" -import HowYoullLearnSection, { DEFAULT_HOW_DATA } from "./HowYoullLearnSection" +import HowYoullLearnSection from "./HowYoullLearnSection" import { DEFAULT_RESOURCE_IMG, pluralize } from "ol-utilities" import { useFeatureFlagsLoaded } from "@/common/useFeatureFlagsLoaded" import ProgramAsCourseInfoBox from "./InfoBoxProgramAsCourse" @@ -312,7 +312,7 @@ const ProgramAsCoursePage: React.FC = ({ childPrograms={childPrograms.data?.results} isLoading={dataLoading} /> - + {page.prerequisites ? ( diff --git a/frontends/main/src/app-pages/ProductPages/ProgramPage.tsx b/frontends/main/src/app-pages/ProductPages/ProgramPage.tsx index 4d380f7f10..946c9e4c4f 100644 --- a/frontends/main/src/app-pages/ProductPages/ProgramPage.tsx +++ b/frontends/main/src/app-pages/ProductPages/ProgramPage.tsx @@ -21,7 +21,7 @@ import UnstyledRawHTML from "@/components/UnstyledRawHTML/UnstyledRawHTML" import AboutSection from "./AboutSection" import ProductPageTemplate from "./ProductPageTemplate" import WhatYoullLearnSection from "./WhatYoullLearnSection" -import HowYoullLearnSection, { DEFAULT_HOW_DATA } from "./HowYoullLearnSection" +import HowYoullLearnSection from "./HowYoullLearnSection" import type { V2ProgramDetail, CourseWithCourseRunsSerializerV2, @@ -311,7 +311,7 @@ const ProgramPage: React.FC = ({ readableId }) => { // Use skeleton as fallback for loading OR error isLoading={dataLoading} /> - + {page.prerequisites ? ( diff --git a/yarn.lock b/yarn.lock index ce95838d84..0c4cc02084 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3521,13 +3521,13 @@ __metadata: languageName: node linkType: hard -"@mitodl/mitxonline-api-axios@npm:^2026.3.26": - version: 2026.3.26 - resolution: "@mitodl/mitxonline-api-axios@npm:2026.3.26" +"@mitodl/mitxonline-api-axios@npm:^2026.4.23": + version: 2026.4.23 + resolution: "@mitodl/mitxonline-api-axios@npm:2026.4.23" dependencies: "@types/node": "npm:^20.11.19" axios: "npm:^1.6.5" - checksum: 10/66d36280b62505ea4510db2376cf1887ade17286aa17b840d120dc88a48817097e9d194cd2a54c3479535d457031883f6b420995ee8909d9afb3debb3b520ab9 + checksum: 10/9580cb496742649450c0567b8b4be6ad19082af9d3ea1c2cd1f9dcec1c87746c8370c8af984b5012660d6e9a52f36dded227115da635a8efb7634df952499a25 languageName: node linkType: hard @@ -9141,7 +9141,7 @@ __metadata: resolution: "api@workspace:frontends/api" dependencies: "@faker-js/faker": "npm:^10.0.0" - "@mitodl/mitxonline-api-axios": "npm:^2026.3.26" + "@mitodl/mitxonline-api-axios": "npm:^2026.4.23" "@tanstack/react-query": "npm:^5.66.0" "@testing-library/react": "npm:^16.3.0" axios: "npm:^1.12.2" @@ -16380,7 +16380,7 @@ __metadata: "@floating-ui/react": "npm:^0.27.16" "@happy-dom/jest-environment": "npm:^20.1.0" "@mitodl/course-search-utils": "npm:^3.5.2" - "@mitodl/mitxonline-api-axios": "npm:^2026.3.26" + "@mitodl/mitxonline-api-axios": "npm:^2026.4.23" "@mitodl/smoot-design": "npm:^6.24.0" "@mui/material": "npm:^6.4.5" "@mui/material-nextjs": "npm:^6.4.3"