Skip to content

Program Infobox Updates#3589

Open
ChristopherChudzicki wants to merge 19 commits into
mainfrom
cc/hq-11787-program-infobox
Open

Program Infobox Updates#3589
ChristopherChudzicki wants to merge 19 commits into
mainfrom
cc/hq-11787-program-infobox

Conversation

@ChristopherChudzicki

@ChristopherChudzicki ChristopherChudzicki commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

What are the relevant tickets?

For

Part 2 of the InfoBox redesign: the Program and Program-as-Course infoboxes. PR1 (#3523) shipped the Course infobox and its building blocks.

Description (What does it do?)

Brings Program and Program-as-Course pages onto the redesigned building blocks from PR1 — it's mostly composition. The one genuinely new piece is the savings price framing.

  1. Choose Your Path: price and financial aid move out of the metadata summary into a Certificate Track card; free enrollment gets a Learn for Free card. Both paths → two cards with buttons inside; a single path → one card with a large button below. The old price row and "Audit for free or upgrade to certificate" box are gone.
  2. Savings framing: when the member-course bundle price exceeds the program price, the card shows the program price ("full program") beside the struck bundle price ("purchased separately") plus a green "Save $X compared to purchasing N courses separately" line.
  3. Financial aid is text, never a reduced price: the displayed price is always the full price; an approved flexible price only switches the link copy to "Financial assistance approved (applied at checkout)".
    • Old per-user price display was nice, but we're using strikethrough text for a different purpose now. May do financial aid esign updates later, depending on priorities.
  4. Enrollment indicated: enrolled users see a single "Enrolled ✔️" link to the program dashboard (/dashboard/program/[id]).
  5. Program-as-Course: the same blocks with course vocabulary ("Enroll", course bullet copy), never the savings block; keeps the "Part of a Program" upsell.
  6. Page header mirrors the infobox's primary action (paid first), shows "Enrolled" when enrolled, a disabled "Enroll" when nothing is enrollable, and now surfaces enrollment errors below the button.

Changes at a glance

The diff is large, but the net change (about +700) is program-related tests. Production code shrank slightly. Changes basically fall in two buckets:

  • New code (smaller bucket)
  • code moved out of course-specific files to be shared with programs (bigger bucket)
New file Lines Origin
programOffering.ts +30 new — program → offering (paid/free/both/none)
enrollTypes.ts +70 new — shared types + offeringTracks/buildEnrollState/offeringBoxCount
enrollAnalytics.ts +41 newfireEnrollCta (PostHog enroll_cta_clicked)
useProgramEnrollment.ts +138 new — enrollment state machine (absorbs the deleted dialog/button logic, reshaped)
useProgramCertificatePrice.ts +100 relocated from ProductSummary price/savings/finaid, reshaped
useProgramIsEnrolled.ts +44 new (small)
ProgramSavingsBlock.tsx +160 new — the savings framing (the one genuinely-new UI)
EnrollOfferingBoxes.tsx +185 relocation — enroll-area body lifted out of CourseEnrollArea
EnrollAreaParts.tsx +97 relocation — EnrollButton / cells / Choose Your Path
HeaderEnrollButton.tsx +106 relocation — shared header button
InfoBoxLayout.tsx / InfoBoxGrid.tsx +71 / +60 relocation — shared card shell + responsive grid
ProgramEnrollArea.tsx / ProgramHeaderEnrollButton.tsx +49 / +39 thin wiring (containers)

Screenshots (if appropriate):

Each pair is main (before) on the left, this PR (after) on the right, at the desktop sidebar width where the InfoBox renders (tablet shots at the end). Based on the QA programs in the setup script linked under testing.

Savings price framing (anon)

S1 · Program price vs. purchasing courses separately

The new savings block: program price ("full program") beside the struck bundle price ("purchased separately"), with a green "Save $X …" line. On main this is the old single price row + "Audit for free or upgrade" box.

Before (main) After (this PR)
S1 savings — main S1 savings — this PR
Program enrollment paths (anon)

P1 · Both paths — free audit + paid certificate

Before (main) After (this PR)
P1 both — main P1 both — this PR

P2 · Paid certificate only

Before (main) After (this PR)
P2 paid only — main P2 paid only — this PR

P3 · Free only — audit, no certificate

Before (main) After (this PR)
P3 free only — main P3 free only — this PR
Program-as-Course (anon)

C2 · Both paths, course vocabulary + "Part of a Program" upsell

Same blocks as a program, but with course copy ("Access to this course & course materials") and no savings block; the "Part of a Program" upsell renders as a flush separator inside the card.

Before (main) After (this PR)
C2 program-as-course — main C2 program-as-course — this PR
Logged-in — enrolled & financial aid

P1 · Enrolled — collapses to a single link

Before (main) After (this PR)
P1 enrolled — main P1 enrolled — this PR

P2 · Financial assistance approved — full price, applied at checkout

Before (main) After (this PR)
P2 finaid approved — main P2 finaid approved — this PR
Tablet (anon)

Tablet — P1 · Both paths

Before (main) After (this PR)
P1 both tablet — main P1 both tablet — this PR

Tablet — P2 · Paid only

Before (main) After (this PR)
P2 paid only tablet — main P2 paid only tablet — this PR

How can this be tested?

Prerequisites: MITxOnline and Learn integrated locally; the MitxOnlineProductPages feature flag ON.

Test Data: setup script at https://gist.github.com/ChristopherChudzicki/78e77f73139b8fdd59e91f522b0bb8b2 — it provisions the QA programs / program-as-course pages (the P/S/C scenarios above) plus the student_infobox@odl.local user (enrolled in some, financial-aid-approved on others).

Manual Testing (screenshots cover appearance; the flows below are the important part):

  1. Check the program and program-as-course pages match the screenshots above (anon), at desktop and tablet widths.
  2. Enrollment flows:
    • "Start Learning" (free) enrolls you in the program's audit path (fires the program-enrolled GTM event).
    • "Earn Certificate" / "Enroll in Program" (paid) starts checkout for the program certificate (use fake cybersource cards to finish).
    • The header enroll button's action always matches the infobox primary-styled button; on failure, both the header and the infobox show an error alert.
  3. Enrolled state: when enrolled, the enroll area collapses to a single "Enrolled ✔️" link to /dashboard/program/[id] — no cards or buttons.
  4. Financial aid: an approved user sees "Financial assistance approved (applied at checkout)" and the full price (no discount, no strike-through); checkout applies the reduction.
  5. Program-as-Course: course vocabulary throughout, the "Part of a Program" upsell links to the parent program, and the savings block never appears.
  6. Un-enrollable paid: a paid-only program with no purchasable product shows a disabled "Enroll" (header) and no card; a paid+free program in that state degrades to free-only.

Additional Context

Alongside the composition, the course and program variants were pulled onto shared internals (the presentational components above + enrollAnalytics.ts, the required tabletColumns prop, an auth-gate on the course price hook) rather than parallel copies, so the two can't drift. Course test suites are unchanged and green as the regression oracle. Full ProductPages suite: 30 suites / 359 tests green; typecheck + lint clean.

Follow-ups / out of scope: visual QA of the savings block against Figma (100%-width block, green "Save $X" line without a check icon — confirm intent); splitting the large ProductSummary.tsx; program discounts (deferred backend work); a few a11y refinements; and the smoot-design Alert contrast on the dark header banner (tracked separately in smoot-design).

@github-actions

github-actions Bot commented Jul 8, 2026

Copy link
Copy Markdown

OpenAPI Changes

No changes detected

View full changelog

Unexpected changes? Ensure your branch is up-to-date with main (consider rebasing).

@ChristopherChudzicki ChristopherChudzicki changed the title Cc/hq 11787 program infobox Program Infobox Updates Jul 8, 2026
@ChristopherChudzicki ChristopherChudzicki marked this pull request as ready for review July 9, 2026 12:57
Copilot AI review requested due to automatic review settings July 9, 2026 12:57

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Updates MITxOnline Program and Program-as-Course product pages to use the redesigned InfoBox building blocks introduced for courses, adding program-specific offering logic and a new “savings” price framing while keeping course/program variants aligned through shared presentational components and hooks.

Changes:

  • Replaces legacy program enrollment dialog/button flows with shared, hook-driven enrollment state + shared header/infobox CTA components (mirroring behavior across page header and InfoBox).
  • Adds program pricing/savings + financial-aid text handling via a dedicated data hook and a new full-width savings price block.
  • Refactors InfoBox composition into a shared layout/grid + shared offering-box renderer used by both course and program variants.

Reviewed changes

Copilot reviewed 54 out of 55 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
frontends/main/src/page-components/EnrollmentDialogs/ProgramEnrollmentDialog.tsx Removes legacy program enrollment modal UI (superseded by new infobox/header enrollment system).
frontends/main/src/page-components/EnrollmentDialogs/ProgramEnrollmentDialog.test.tsx Removes tests for the deleted legacy program enrollment modal.
frontends/main/src/app-pages/ProductPages/util.ts Adds getTotalRequiredCourses helper for program requirements-derived counts.
frontends/main/src/app-pages/ProductPages/useProgramIsEnrolled.ts New hook to determine whether the authenticated user is enrolled in a specific program (auth-gated).
frontends/main/src/app-pages/ProductPages/useProgramEnrollment.ts New hook implementing the program enrollment “state machine” + CTA actions + analytics.
frontends/main/src/app-pages/ProductPages/useProgramEnrollment.test.tsx Tests for program enrollment hook (offering logic, auth gating, analytics, actions).
frontends/main/src/app-pages/ProductPages/useProgramCertificatePrice.ts New hook providing full program price, savings framing inputs, and financial-aid link/applied state.
frontends/main/src/app-pages/ProductPages/useProgramCertificatePrice.test.tsx Tests for program certificate price hook, including savings and financial-aid behavior.
frontends/main/src/app-pages/ProductPages/useCourseEnrollment.ts Refactors course enrollment hook to use shared enroll analytics/types helpers.
frontends/main/src/app-pages/ProductPages/useCourseCertificatePrice.ts Renames/refines course certificate pricing hook and auth-gates flexible-price lookup.
frontends/main/src/app-pages/ProductPages/TrackCard.tsx Adds priceBlock support and introduces AccessFeatureRow to keep shared bullet copy in sync.
frontends/main/src/app-pages/ProductPages/ProgramSavingsBlock.tsx New component rendering “savings” price framing (full program vs purchased separately + save line).
frontends/main/src/app-pages/ProductPages/ProgramSavingsBlock.test.tsx Tests for savings block rendering + pluralization semantics.
frontends/main/src/app-pages/ProductPages/ProgramPage.tsx Switches program page header CTA to the new shared program header enroll button.
frontends/main/src/app-pages/ProductPages/ProgramPage.test.tsx Stabilizes program page tests by pinning offering modes to avoid CTA/heading flakiness.
frontends/main/src/app-pages/ProductPages/programOffering.ts New pure function mapping program enrollment modes + product purchasability to actionable offering.
frontends/main/src/app-pages/ProductPages/programOffering.test.ts Tests for actionable program offering/demotion logic.
frontends/main/src/app-pages/ProductPages/ProgramHeaderEnrollButton.tsx New program-specific wrapper around shared HeaderEnrollButton using useProgramEnrollment.
frontends/main/src/app-pages/ProductPages/ProgramHeaderEnrollButton.test.tsx Tests header CTA labels/disabled/enrolled/signup/error behaviors for programs.
frontends/main/src/app-pages/ProductPages/ProgramEnrollmentButton.tsx Removes legacy program enrollment CTA component.
frontends/main/src/app-pages/ProductPages/ProgramEnrollmentButton.test.tsx Removes tests for deleted legacy program enrollment CTA.
frontends/main/src/app-pages/ProductPages/ProgramEnrollArea.tsx New program infobox enroll-area container wiring enrollment + pricing + savings into offering boxes.
frontends/main/src/app-pages/ProductPages/ProgramEnrollArea.test.tsx Tests infobox program enroll-area structure (both/paid/free/enrolled/none, savings, financial-aid).
frontends/main/src/app-pages/ProductPages/ProgramBundleUpsell.tsx Adjusts upsell styling to sit flush within the InfoBox card across widths.
frontends/main/src/app-pages/ProductPages/ProgramAsCourseSummary.test.tsx Updates program-as-course summary tests for new required props and new dates-row behavior.
frontends/main/src/app-pages/ProductPages/ProgramAsCoursePage.tsx Switches program-as-course page header CTA to the new shared program header enroll button.
frontends/main/src/app-pages/ProductPages/ProgramAsCoursePage.test.tsx Updates assertions for new CTA labels and card rendering rules under the redesigned composition.
frontends/main/src/app-pages/ProductPages/ProgramAsCourseCertificateTrackCard.tsx Removes bespoke program-as-course certificate track card (replaced by shared TrackCard-based components).
frontends/main/src/app-pages/ProductPages/ProductSummary.tsx Removes legacy program price row; adds certificate row; adds program dates row for program-as-course; makes tabletColumns required.
frontends/main/src/app-pages/ProductPages/ProductSummary.test.tsx Updates product summary tests for required tabletColumns and new program summary row ordering/content.
frontends/main/src/app-pages/ProductPages/LearnForFreeCard.tsx Uses shared AccessFeatureRow for consistent access bullet copy.
frontends/main/src/app-pages/ProductPages/LearnForFreeCard.test.tsx Updates expected access bullet copy for program noun.
frontends/main/src/app-pages/ProductPages/InfoBoxProgramAsCourse.tsx Converts program-as-course infobox into a thin wrapper around InfoBoxProgram (displayAsCourse).
frontends/main/src/app-pages/ProductPages/InfoBoxProgramAsCourse.test.tsx Adds coverage for program-as-course infobox behaviors (labels, finaid link, no savings, upsell, a11y heading).
frontends/main/src/app-pages/ProductPages/InfoBoxProgram.tsx Rebuilds program infobox using shared InfoBox layout + program enroll area + optional program-as-course upsell.
frontends/main/src/app-pages/ProductPages/InfoBoxProgram.test.tsx Adds coverage for program infobox layouts (box counts, structure, savings, enrolled, none).
frontends/main/src/app-pages/ProductPages/InfoBoxParts.tsx Removes now-obsolete infobox padding/action stack wrappers (handled by shared grid/layout).
frontends/main/src/app-pages/ProductPages/InfoBoxLayout.tsx New shared InfoBox shell coordinating heading, grid, tablet column logic, upsell, and Ask TIM section.
frontends/main/src/app-pages/ProductPages/InfoBoxGrid.tsx New shared responsive grid + divider used by course and program infoboxes.
frontends/main/src/app-pages/ProductPages/InfoBoxCourse.tsx Migrates course infobox to InfoBoxLayout and shared grid/count logic.
frontends/main/src/app-pages/ProductPages/HeaderEnrollButton.tsx New shared page-header enroll CTA component (enrolled/options/none + signup + error alert).
frontends/main/src/app-pages/ProductPages/enrollTypes.ts New shared enrollment types + offeringBoxCount helper for consistent grid counts.
frontends/main/src/app-pages/ProductPages/enrollTypes.test.ts Tests for shared offering-box count logic.
frontends/main/src/app-pages/ProductPages/EnrollOfferingBoxes.tsx New shared infobox enroll-area presentational component (cards, buttons, error alert, signup popover).
frontends/main/src/app-pages/ProductPages/EnrollAreaParts.tsx New shared low-level UI primitives for offering cell layout + enroll button behavior/spinner a11y.
frontends/main/src/app-pages/ProductPages/enrollAnalytics.ts New shared PostHog analytics helper for enroll CTA clicks (course + program).
frontends/main/src/app-pages/ProductPages/courseRun.ts Reuses shared Offering type and removes duplicated offering box count logic.
frontends/main/src/app-pages/ProductPages/courseRun.test.ts Removes tests for the deleted duplicated offeringBoxCount in courseRun.ts.
frontends/main/src/app-pages/ProductPages/CoursePage.tsx Swaps course page header CTA implementation to shared HeaderEnrollButton and adds header error surfacing.
frontends/main/src/app-pages/ProductPages/CoursePage.test.tsx Adds test ensuring header enrollment failures surface an error alert beneath the header CTA.
frontends/main/src/app-pages/ProductPages/CourseEnrollArea.tsx Refactors course infobox enroll area to use shared EnrollOfferingBoxes + renamed pricing hook.
frontends/main/src/app-pages/ProductPages/CourseEnrollArea.test.tsx Updates inline comment to reflect renamed pricing hook.
frontends/main/src/app-pages/ProductPages/CertificateTrackCard.tsx Adds priceBlock passthrough and uses shared AccessFeatureRow for consistent access bullet copy.
frontends/main/src/app-pages/ProductPages/CertificateTrackCard.test.tsx Updates expected access bullet copy and adds a test for priceBlock suppressing top-right price.

Comment on lines +54 to +58
const ProgramVerticalDivider = styled.div(() => ({
width: "1px",
height: "48px",
backgroundColor: theme.custom.colors.lightGray2,
flexShrink: 0,
@ChristopherChudzicki ChristopherChudzicki force-pushed the cc/hq-11787-program-infobox branch from d08bcae to 0d2f395 Compare July 9, 2026 15:54
@ChristopherChudzicki ChristopherChudzicki added the Needs Review An open Pull Request that is ready for review label Jul 10, 2026

@daniellefrappier18 daniellefrappier18 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Scenario Viewer URL Screenshot
P1 · Both paths — free audit + paid certificate anon /programs/program-v1:qa-prog-both Image
P2 · Paid certificate only anon /programs/program-v1:qa-prog-paid Image
P3 · Free only — audit, no certificate anon /programs/program-v1:qa-prog-free Image
C2 · Both paths, course vocabulary + "Part of a Program" upsell anon /courses/p/program-v1:qa-pac-both Image
P1 · Enrolled — collapses to a single link logged in (student_infobox@odl.local) /programs/program-v1:qa-prog-both Image
P2 · Financial assistance approved — full price, applied at checkout logged in (student_infobox@odl.local) /programs/program-v1:qa-prog-paid Image

I was able to verify the screenshot and the manual testing path but I did find an issue that seems like it’s a bug. When logged out and viewing a program with no enrollment modes or no purchasable product, the InfoBox probably should not display “Certificate: Program certificate on completion” since there's no certificate available to earn.

Image Image

@ChristopherChudzicki ChristopherChudzicki force-pushed the cc/hq-11787-program-infobox branch from 3aef496 to dc0f77e Compare July 10, 2026 18:25
ChristopherChudzicki and others added 19 commits July 10, 2026 14:46
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…card

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Composes useProgramEnrollment + useProgramCertificatePrice with the
shared EnrollAreaParts/CertificateTrackCard/LearnForFreeCard/EnrolledLink
building blocks, mirroring CourseEnrollArea's paid/free box composition
for programs (no degraded run states, no certificateDeadlineNote).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Add a program-level dates row (Start/End) to ProgramAsCourseSummary,
using program.start_date/end_date with the shared LocalDate/InfoLabelValue
treatment. Reorder rows to Format, Estimated, Dates per spec; the row is
omitted entirely when both dates are unset.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…nrollArea

Rewrite InfoBoxProgram to mirror InfoBoxCourse's structure: BoxGrid with a
data-boxes count derived from getProgramOffering/programOfferingBoxCount and
useProgramIsEnrolled (the same sources useProgramEnrollment uses, so the grid
can't disagree with what ProgramEnrollArea renders), a conditional
SectionDivider, and ProgramEnrollArea in place of the old fixed
ProgramEnrollmentButton. Adds InfoBoxProgram.test.tsx pinning the both/paid-
only/savings behaviors, and fixes two ProgramPage.test.tsx assertions that
implicitly relied on the old button's fixed "Enroll in Program" label,
which is now offering-dependent.
…drop bespoke card

Rewrites InfoBoxProgramAsCourse onto the same BoxGrid/ProgramEnrollArea
composition as InfoBoxProgram (Task 10), passing displayAsCourse for
course-labeled buttons and ProgramAsCourseSummary for meta. Deletes the
bespoke ProgramAsCourseCertificateTrackCard in favor of the shared
CertificateTrackCard rendered via ProgramEnrollArea, dropping the old
certificate_available-gated logic in favor of enrollment-mode-driven
offering (two deliberate behavior changes, per spec). Retains the "Part
of a Program" upsell for programs with parent programs.

Updates ProgramAsCoursePage.test.tsx assertions that drove the old
"Enroll" label to the new "Start Learning"/"Enroll" labels, and pins an
explicit paid enrollment mode where the headings test relied on
certificate_available alone. Adds InfoBoxProgramAsCourse.test.tsx
covering paid-only, both, financial aid, no-savings, parent-program
upsell, and the a11y heading.
Tasks 1-11 recomposed program InfoBoxes and page headers onto shared
blocks, leaving ProgramEnrollmentButton, ProductSummary's ProgramPriceRow,
the empty CourseSignupButton stub, and three consumer-free InfoBox
primitives dead. Verified via grep that nothing outside these files still
imports them before deleting.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…e, finaid query gate)

Deletes the now-orphaned ProgramEnrollmentDialog, drops redundant/dead
tests, restores the enrollment-type gate on the flexible-price query
dropped during refactoring, and tightens/trims a couple of test overrides.
…acing)

- Free program enrollment fires GTM program-enrolled (was course-enrolled;
  trackProgramEnrolled had lost its last caller with the dialog's removal)
- Header enroll button surfaces mutation failures again with an inline
  alert below the button, matching the pre-redesign header CTA
- Enrollment error alerts span the full BoxGrid row on tablet instead of
  auto-flowing into a half-width column slot (shared FullRowCell, applied
  on both course and program enroll areas)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The Save-$X and comparison text were separate flex items, so each wrapped
inside its own box at card width; inline spans in one block wrap as text.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…rt-price hooks

Consolidate duplicated enrollment logic into shared modules:
- enrollTypes.ts owns Offering + canonical offeringBoxCount
- enrollAnalytics.ts owns EnrollCtaPlacement + fireEnrollCta
- getTotalRequiredCourses moved to util.ts
- rename useCertificatePrice -> useCourseCertificatePrice (.tsx -> .ts),
  gate the course price query on useUserIsAuthenticated so anon course
  visitors no longer fire the user-scoped flexible-price query
- rename useProgramCertificatePrice .tsx -> .ts, data-only shape

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Introduce reusable presentation blocks shared by course and program:
- InfoBoxLayout.tsx: card shell (labelled section, BoxGrid, meta grid,
  divider gating, AskTim) with boxCount/tabletColumns derivations
- EnrollOfferingBoxes.tsx: offering boxes, enrolled collapse, error alert
- HeaderEnrollButton.tsx: HeaderButtonSlot + HeaderAlertSizer (width fix)
  + three-state render; program header adapts onto it
- ProgramSavingsBlock.tsx (+ test): styled savings JSX, price row wraps
- TrackCard.tsx: shared AccessFeatureRow used by cert + free cards
- ProgramBundleUpsell.tsx: flush top-border separator on tablet+desktop
  (was doubling the card border on tablet)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… blocks

Wire the course and program InfoBoxes onto the shared shell + primitives:
- InfoBoxProgram.tsx is now the single program component with optional
  displayAsCourse; InfoBoxProgramAsCourse is a thin wrapper
- InfoBoxCourse.tsx adopts InfoBoxLayout (keeps session/enrolled logic)
- CourseEnrollArea/ProgramEnrollArea are thin adapters over the shared
  EnrollOfferingBoxes presenter; course header now surfaces enroll errors
- tabletColumns is now required on the summary components + SummaryRows
- summary direct-render tests updated for required tabletColumns; cloned
  course structural + enrolled data-boxes tests into InfoBoxProgram.test

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…able

The program InfoBox rendered "Certificate: Program certificate on
completion" unconditionally, claiming a certificate could be earned even
for a program with no paid enrollment mode (free-only, or no modes). Gate
the row on `program.certificate_available` — the API's own signal, computed
server-side as `any(mode.requires_payment)` — the same flag
MitxOnlineResourceCard already trusts.

Visibility stays independent of product data: a paid mode with no
purchasable product still shows its (odd) certificate card. That's a
bad-data combination we deliberately don't police in the frontend.

Pin the existing cert-row tests to certificate_available (the factory
default is a random boolean).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@ChristopherChudzicki ChristopherChudzicki force-pushed the cc/hq-11787-program-infobox branch from dc0f77e to b18c9d6 Compare July 10, 2026 18:46
@ChristopherChudzicki

Copy link
Copy Markdown
Contributor Author

@daniellefrappier18

I was able to verify the screenshot and the manual testing path but I did find an issue that seems like it’s a bug.

Thanks for catching this. The exact mode you mentioned—no enrollment types / no product—is kind of bad data, but the cert row was definitely showing even if enrollment_mode was free-only. That's fixed. (So is the one your mentioned.)

Ready for another look.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Needs Review An open Pull Request that is ready for review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants