Skip to content

feat(landing): real social proof, featured-courses carousel, and reduced-motion support - #121

Open
Whiznificent wants to merge 3 commits into
Deen-Bridge:devfrom
Whiznificent:feat/landing-social-proof
Open

feat(landing): real social proof, featured-courses carousel, and reduced-motion support#121
Whiznificent wants to merge 3 commits into
Deen-Bridge:devfrom
Whiznificent:feat/landing-social-proof

Conversation

@Whiznificent

@Whiznificent Whiznificent commented Jul 22, 2026

Copy link
Copy Markdown

Summary\n\nCloses #121 n\nReplaces fabricated landing-page testimonials with real, attributable quotes pulled from course.reviews[], and adds a real Featured Courses carousel powered by /api/courses. The whole landing page now respects prefers-reduced-motion for accessibility.\n\n### What changed\n\n- New FeaturedCourses section (app/(pages)/(landingPage)/FeaturedCourses.jsx): async server component. Fetches /api/courses via the existing fetchCourses() action, ranks by enrollment then average rating then review count, and surfaces the top 8 in an embla carousel. Renders null (graceful degradation) when the API is unreachable or empty. ISR-cached for 5 minutes (export const revalidate = 300).\n- New FeaturedCoursesCarousel (app/(pages)/(landingPage)/FeaturedCoursesCarousel.jsx, \"use client\"): thin wrapper around the existing components/ui/carousel.jsx embla primitives. Keyboard-accessible (built-in ArrowLeft/Right handlers in the base primitive), loop disabled for finite curated sets, normalises children via React.Children.toArray.\n- New LandingCourseCard (app/(pages)/(landingPage)/LandingCourseCard.jsx): lightweight server-renderable card with no useAuth / useBookmark hooks, so it renders correctly for unauthenticated landing visitors.\n- Rewrote Testimonials (app/(pages)/(landingPage)/Testimonials.jsx): now an async server component. Pulls real reviews from courses, filters to entries with both an attributable user.name and a non-empty comment, shows up to 6 with the actual student name + a link to the course the review was left on. Renders null when no real reviews exist (acceptance criterion forbids fabricating quotes). Also ISR-cached.\n- Stats reduced-motion (app/(pages)/(landingPage)/Stats.jsx): wrapped the section in <MotionConfig reducedMotion=\"user\"> so framer-motion animations honor the OS preference, and the hand-rolled AnimatedCounter now branches on useReducedMotion() to snap to the final value instead of ticking. Added id + aria-labelledby pointing at the visible H2.\n- Global reduced-motion CSS (styles/globals.css): added @media (prefers-reduced-motion: reduce) block disabling .animate-scroll, .animate-marquee, .animate-marquee-reverse, .animate-spin-slow, .animate-gradient, .animate-fade-in, .animate-in-out so the partners marquee stays static and the Hero CTA no longer tweens.\n- Cleanup: removed unused HowItWorks import + JSX, doubled-back aria-labels on <article> elements, dead metadata constant, redundant React import; renamed anonymous page component to Page.\n\n### Acceptance criteria\n\n- [x] Carousel functional (keyboard, swipe, prev/next), hides when no data\n- [x] All fabricated testimonials removed; section renders only real, attributable quotes\n- [x] prefers-reduced-motion honored across Stats, Partners (CSS), Testimonials\n- [x] Semantic landmarks (H1 in Hero, H2 in each section, aria-labelledby)\n- [x] Passes npm run lint and npx next build\n\n### Notes\n\n- Both new server components use export const revalidate = 300 so the landing page does not hammer /api/courses on every visitor (the underlying axios call cannot tag its own request with next.revalidate).\n- Followup opportunities (out of scope here): capture student consent for public display of reviews; add a feature flag so the section can be toggled via env var; show the carousel autoplaying (and pause via reduced-motion) if richer discoverability is wanted.\n\n### How to test\n\n1. npm install && npm run lint && npm run build (build + lint both green).\n2. Visit / — Featured Courses carousel renders with up to 8 real courses. Testimonials render whatever real course reviews exist; if none, the section silently disappears (graceful degradation).\n3. In DevTools, toggle the prefers-reduced-motion emulation to reduce. The partners marquee should stop instantly and the AnimatedCounter should jump to its final value without ticking.\n4. Verify keyboard navigation on the carousel: Tab to focus the carousel, then arrow keys move between slides.

Summary by CodeRabbit

  • New Features

    • Added a “Featured Courses” landing section with ranked selection and a client-side carousel.
    • Replaced static testimonials with real course reviews displayed in a responsive grid.
    • Introduced landing course cards for course browsing, including ratings, pricing (“Free” when applicable), and course navigation.
  • Accessibility

    • Improved reduced-motion support across landing animations and partner scrolling behavior.
    • Added/linked headings and sections for assistive technologies (Stats, About, Why, CTA), plus improved carousel control labeling.
  • Enhancements

    • Enabled previously disabled landing sections and improved graceful fallbacks when course/review data is unavailable.

…ews, and reduced-motion support

- Added FeaturedCourses server section: fetches /api/courses, ranks by enrollment
  then rating, surfaces top items in an embla carousel (loop-free, keyboard
  + swipe accessible).
- Added LandingCourseCard: lightweight, server-renderable card without auth
  or bookmark hooks so it works for unauthenticated landing visitors.
- Refactored Testimonials into a server component that pulls real quotes
  from course.reviews and renders up to 6 attributable reviews; renders
  null if none exist (no fabrication).
- Implemented prefers-reduced-motion across the landing page:
  - Stats now wrapped in <MotionConfig reducedMotion="user"> and the
    AnimatedCounter respects the OS preference immediately.
  - Added a global @media (prefers-reduced-motion: reduce) block disabling
    marquee, scroll, gradient, spin, fade-in, and animate-in-out animations.
- Polish: aria-labelledby on each section pointing at visible H2, redundant
  aria-labels removed from article elements, PascalCase Page component.

Closes Deen-Bridge#114
@vercel

vercel Bot commented Jul 22, 2026

Copy link
Copy Markdown

@Whiznificent is attempting to deploy a commit to the Deen Bridge Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai

coderabbitai Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

The landing page now displays API-backed featured courses and real review-backed testimonials, activates both sections, and adds reduced-motion and semantic accessibility handling across landing-page content.

Changes

Landing Page Social Proof

Layer / File(s) Summary
Featured courses carousel
app/(pages)/(landingPage)/FeaturedCourses.jsx, app/(pages)/(landingPage)/FeaturedCoursesCarousel.jsx, app/(pages)/(landingPage)/LandingCourseCard.jsx, components/molecules/landingpage/PublicCourseCard.jsx
Courses are fetched, ranked, limited to eight, rendered with course metadata, and exposed through responsive carousel navigation.
Review-backed testimonials
app/(pages)/(landingPage)/Testimonials.jsx, lib/testimonials.js
Real course reviews are filtered for attributable content, limited to six, and rendered with ratings, avatars, and course links; the placeholder testimonial module is documented for future content.
Landing page section integration
app/page.jsx
Featured courses and testimonials are rendered between the stats and partners sections.
Motion and landmark accessibility
app/(pages)/(landingPage)/About.jsx, app/(pages)/(landingPage)/CTA.jsx, app/(pages)/(landingPage)/Hero.jsx, app/(pages)/(landingPage)/Partners.jsx, app/(pages)/(landingPage)/Stats.jsx, app/(pages)/(landingPage)/WhyDeenBridge.jsx, styles/globals.css
Sections gain heading relationships and decorative-element labels, while counters, marquees, and related animations respond to reduced-motion preferences.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant LandingPage
  participant FeaturedCourses
  participant Testimonials
  participant fetchCourses
  participant FeaturedCoursesCarousel
  LandingPage->>FeaturedCourses: render featured section
  FeaturedCourses->>fetchCourses: fetch courses
  fetchCourses-->>FeaturedCourses: return course data
  FeaturedCourses->>FeaturedCoursesCarousel: render ranked course cards
  LandingPage->>Testimonials: render testimonials section
  Testimonials->>fetchCourses: fetch courses and reviews
  fetchCourses-->>Testimonials: return review data
  Testimonials-->>LandingPage: render testimonial grid
Loading

Suggested reviewers: zeemscript

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The PR aligns with #114 by adding featured courses, review-backed testimonials, reduced-motion handling, and section accessibility.
Out of Scope Changes check ✅ Passed The described changes all support the landing-page accessibility, motion, or social-proof goals; no unrelated additions stand out.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main landing-page changes: real testimonials, featured courses, and reduced-motion support.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 8

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@app/`(pages)/(landingPage)/FeaturedCourses.jsx:
- Around line 39-49: Address the missing loading state for the async fetch used
by FeaturedCourses by adding the loading fallback at the consolidated
app/page.jsx location, covering the direct FeaturedCourses render with the
appropriate loading mechanism. Keep FeaturedCourses’s existing fetch and ranking
behavior unchanged.
- Around line 8-12: The revalidate setting in FeaturedCourses is ignored because
it is a plain component. Move the 300-second revalidation configuration to the
app/page.jsx route, and ensure Testimonials does not define the same ineffective
setting; alternatively, cache the shared fetchCourses helper so both sections
reuse one courses result.

In `@app/`(pages)/(landingPage)/LandingCourseCard.jsx:
- Around line 11-23: Update LandingCourseCard’s identifier handling to support
courses that provide only id, using the available id as the route identifier
when _id is absent. Preserve the existing _id behavior and ensure the Link
destination never uses undefined for id-only courses.

In `@app/`(pages)/(landingPage)/Stats.jsx:
- Around line 58-64: Update the animation effect in the stats counter so that
when prefersReducedMotion is false and the animation restarts, count is reset to
0 before starting the interval, keeping it consistent with current = 0; preserve
the reduced-motion path that immediately sets the final numericValue.

In `@app/`(pages)/(landingPage)/Testimonials.jsx:
- Around line 52-67: Update the Testimonials async rendering flow around the
Testimonials component and its fetchCourses call to provide a loading state
through the app’s established loading.jsx or Suspense pattern, and add the
corresponding error-state handling required for app/** data fetching. Ensure the
page can render its loading fallback while the request is pending and an
appropriate error UI if the fetch fails.
- Around line 6-8: Move the revalidate export from the Testimonials component
module to its owning landing route page.jsx or layout.jsx so the five-minute
cache is applied. Apply the same change to FeaturedCourses.jsx, preserving the
existing revalidate value and removing the ineffective component-level exports.

In `@app/page.jsx`:
- Around line 11-25: Wrap the async FeaturedCourses and Testimonials sections in
independent Suspense boundaries with appropriate loading skeleton fallbacks, and
add the corresponding error handling for fetch failures. Keep the remaining Page
sections rendered normally so each data-dependent section can load or fail
independently without blocking the entire landing page.

In `@styles/globals.css`:
- Around line 284-299: Extend the reduced-motion handling in the `@media`
(prefers-reduced-motion: reduce) block to cover the Stats card icon’s
group-hover scale and transition behavior. Use a scoped selector targeting the
Stats markup, or update Stats.jsx to omit group-hover:scale-110 and
transition-all for reduced-motion users, while preserving the effect for other
users.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: d6f23150-4248-4150-947d-9bfb4d7e7b23

📥 Commits

Reviewing files that changed from the base of the PR and between 37c25df and eae9e67.

📒 Files selected for processing (7)
  • app/(pages)/(landingPage)/FeaturedCourses.jsx
  • app/(pages)/(landingPage)/FeaturedCoursesCarousel.jsx
  • app/(pages)/(landingPage)/LandingCourseCard.jsx
  • app/(pages)/(landingPage)/Stats.jsx
  • app/(pages)/(landingPage)/Testimonials.jsx
  • app/page.jsx
  • styles/globals.css

Comment thread app/(pages)/(landingPage)/FeaturedCourses.jsx
Comment thread app/(pages)/(landingPage)/FeaturedCourses.jsx
Comment thread app/(pages)/(landingPage)/LandingCourseCard.jsx
Comment thread app/(pages)/(landingPage)/Stats.jsx Outdated
Comment thread app/(pages)/(landingPage)/Testimonials.jsx Outdated
Comment thread app/(pages)/(landingPage)/Testimonials.jsx
Comment thread app/page.jsx
Comment thread styles/globals.css
…testimonials (Deen-Bridge#114)

- Add FeaturedCourses server component: fetches GET /api/courses with
  1-hour revalidation, ranks by rating×log(reviewCount), renders top 8
  in existing embla Carousel (keyboard/swipe navigable). Section hides
  cleanly when API is unreachable or returns no data.

- Add PublicCourseCard: lightweight card for unauthenticated visitors —
  no useAuth/bookmark hooks. Shows thumbnail (lazy-loaded), title,
  category, star rating, review count, instructor avatar+name, USDC
  price. Links to /dashboard/courses/[courseId].

- Replace fabricated Testimonials: remove all 6 invented hardcoded
  quotes. Add lib/testimonials.js as the maintainer-managed source of
  real, consent-confirmed reviews. Testimonials.jsx reads from that
  file and renders nothing if the array is empty, so the section stays
  hidden until real quotes are added.

- Rebuild Testimonials.jsx: initials avatar (consistent with Partners),
  quote attribution to course title, marquee pauses on hover and
  keyboard focus (onFocus/onBlur + CSS paused class). Re-enabled in
  app/page.jsx.

- Reduced-motion across the landing page (useReducedMotion from
  framer-motion + CSS @media prefers-reduced-motion):
    * Stats.jsx: AnimatedCounter shows final value immediately; all
      motion.div entrance animations are skipped.
    * Partners.jsx: marquee replaced with static flex-wrap grid.
    * Testimonials.jsx: marquees replaced with static flex-wrap grid.
    * globals.css: @media rule stops animate-marquee, animate-marquee-
      reverse, animate-scroll, animate-spin-slow, animate-gradient,
      animate-fade-in.

- Heading/landmark audit:
    * One h1 in Hero (inside <header>).
    * Every section now has aria-labelledby pointing to its h2:
      About (#about-heading), WhyDeenBridge (#why-heading),
      Stats (#stats-heading), FeaturedCourses (#featured-courses-heading),
      Testimonials (#testimonials-heading), Partners (#partners-heading),
      CTA (#cta-heading).
    * Decorative divs marked aria-hidden=true throughout.

- Below-the-fold images use loading=lazy in PublicCourseCard.

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@app/`(pages)/(landingPage)/Hero.jsx:
- Around line 9-10: Change the root element in the Hero component from a header
to a main landmark, preserving its existing classes and child content so the
landing page provides the primary accessible landmark.

In `@app/`(pages)/(landingPage)/Stats.jsx:
- Line 180: Update the stats icon class string in the relevant Stats component
markup to add motion-reduce:transition-none and
motion-reduce:group-hover:scale-100, preserving the existing hover scale and
transition behavior for users without reduced-motion preferences.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: a47aa889-bbed-46a2-9b5f-6e53193c9116

📥 Commits

Reviewing files that changed from the base of the PR and between eae9e67 and 27c8f38.

📒 Files selected for processing (12)
  • app/(pages)/(landingPage)/About.jsx
  • app/(pages)/(landingPage)/CTA.jsx
  • app/(pages)/(landingPage)/Hero.jsx
  • app/(pages)/(landingPage)/LandingCourseCard.jsx
  • app/(pages)/(landingPage)/Partners.jsx
  • app/(pages)/(landingPage)/Stats.jsx
  • app/(pages)/(landingPage)/Testimonials.jsx
  • app/(pages)/(landingPage)/WhyDeenBridge.jsx
  • app/page.jsx
  • components/molecules/landingpage/PublicCourseCard.jsx
  • lib/testimonials.js
  • styles/globals.css
🚧 Files skipped from review as they are similar to previous changes (4)
  • styles/globals.css
  • app/(pages)/(landingPage)/LandingCourseCard.jsx
  • app/page.jsx
  • app/(pages)/(landingPage)/Testimonials.jsx

Comment thread app/(pages)/(landingPage)/Hero.jsx Outdated
Comment thread app/(pages)/(landingPage)/Stats.jsx
@zeemscript

Copy link
Copy Markdown
Collaborator

@Whiznificent this PR has merge conflicts with the main branch. Please resolve the conflicts (merge main in or rebase) and push the fix so it can be merged. Thanks!

@Whiznificent

Whiznificent commented Aug 30, 2026 via email

Copy link
Copy Markdown
Author

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants