feat(landing): real social proof, featured-courses carousel, and reduced-motion support - #121
feat(landing): real social proof, featured-courses carousel, and reduced-motion support#121Whiznificent wants to merge 3 commits into
Conversation
…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
|
@Whiznificent is attempting to deploy a commit to the Deen Bridge Team on Vercel. A member of the Team first needs to authorize it. |
WalkthroughThe 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. ChangesLanding Page Social Proof
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
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
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
📒 Files selected for processing (7)
app/(pages)/(landingPage)/FeaturedCourses.jsxapp/(pages)/(landingPage)/FeaturedCoursesCarousel.jsxapp/(pages)/(landingPage)/LandingCourseCard.jsxapp/(pages)/(landingPage)/Stats.jsxapp/(pages)/(landingPage)/Testimonials.jsxapp/page.jsxstyles/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.
There was a problem hiding this comment.
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
📒 Files selected for processing (12)
app/(pages)/(landingPage)/About.jsxapp/(pages)/(landingPage)/CTA.jsxapp/(pages)/(landingPage)/Hero.jsxapp/(pages)/(landingPage)/LandingCourseCard.jsxapp/(pages)/(landingPage)/Partners.jsxapp/(pages)/(landingPage)/Stats.jsxapp/(pages)/(landingPage)/Testimonials.jsxapp/(pages)/(landingPage)/WhyDeenBridge.jsxapp/page.jsxcomponents/molecules/landingpage/PublicCourseCard.jsxlib/testimonials.jsstyles/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
|
@Whiznificent this PR has merge conflicts with the |
|
OK I will work on it
…On Sun, Aug 30, 2026, 1:33 PM Sakariyah Abdulhazeem < ***@***.***> wrote:
*zeemscript* left a comment (Deen-Bridge/dnb-frontend#121)
<#121 (comment)>
@Whiznificent <https://github.com/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!
—
Reply to this email directly, view it on GitHub
<#121?email_source=notifications&email_token=CAVMYC2AEATIBVOBFIPLQHD5MQNJVA5CNFSNUABFM5UWIORPF5TWS5BNNB2WEL2JONZXKZKDN5WW2ZLOOQXTKNBWHA3TAOBTGU32M4TFMFZW63VHNVSW45DJN5XKKZLWMVXHJLDGN5XXIZLSL5RWY2LDNM#issuecomment-5468708357>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/CAVMYC3RO2OUP5XCFCW6ENT5MQNJVAVCNFSNUABFKJSXA33TNF2G64TZHM4TONBUHE2DGOBUHNEXG43VMU5TIOJUHA4TSMZZGM32C5QC>
.
Triage notifications, keep track of coding agent tasks and review pull
requests on the go with GitHub Mobile for iOS
<https://github.com/notifications/mobile/ios/CAVMYC52KER3LZQ4G2UX5MT5MQNJVA5CNFSNUABFM5UWIORPF5TWS5BNNB2WEL2JONZXKZKDN5WW2ZLOOQXTKNBWHA3TAOBTGU32M4TFMFZW63VHNVSW45DJN5XKKZLWMVXHJKTGN5XXIZLSL5UW64Y>
and Android
<https://github.com/notifications/mobile/android/CAVMYCZMBRCGQLKQXO3OZET5MQNJVA5CNFSNUABFM5UWIORPF5TWS5BNNB2WEL2JONZXKZKDN5WW2ZLOOQXTKNBWHA3TAOBTGU32M4TFMFZW63VHNVSW45DJN5XKKZLWMVXHJLTGN5XXIZLSL5QW4ZDSN5UWI>.
Download it today!
You are receiving this because you were mentioned.Message ID:
***@***.***>
|
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 respectsprefers-reduced-motionfor accessibility.\n\n### What changed\n\n- NewFeaturedCoursessection (app/(pages)/(landingPage)/FeaturedCourses.jsx): async server component. Fetches/api/coursesvia the existingfetchCourses()action, ranks by enrollment then average rating then review count, and surfaces the top 8 in an embla carousel. Rendersnull(graceful degradation) when the API is unreachable or empty. ISR-cached for 5 minutes (export const revalidate = 300).\n- NewFeaturedCoursesCarousel(app/(pages)/(landingPage)/FeaturedCoursesCarousel.jsx,\"use client\"): thin wrapper around the existingcomponents/ui/carousel.jsxembla primitives. Keyboard-accessible (built-in ArrowLeft/Right handlers in the base primitive), loop disabled for finite curated sets, normalises children viaReact.Children.toArray.\n- NewLandingCourseCard(app/(pages)/(landingPage)/LandingCourseCard.jsx): lightweight server-renderable card with nouseAuth/useBookmarkhooks, so it renders correctly for unauthenticated landing visitors.\n- RewroteTestimonials(app/(pages)/(landingPage)/Testimonials.jsx): now an async server component. Pulls real reviews from courses, filters to entries with both an attributableuser.nameand a non-emptycomment, shows up to 6 with the actual student name + a link to the course the review was left on. Rendersnullwhen 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-rolledAnimatedCounternow branches onuseReducedMotion()to snap to the final value instead of ticking. Addedid+aria-labelledbypointing 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-outso the partners marquee stays static and the Hero CTA no longer tweens.\n- Cleanup: removed unusedHowItWorksimport + JSX, doubled-backaria-labels on<article>elements, deadmetadataconstant, redundantReactimport; renamed anonymouspagecomponent toPage.\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-motionhonored across Stats, Partners (CSS), Testimonials\n- [x] Semantic landmarks (H1 in Hero, H2 in each section,aria-labelledby)\n- [x] Passesnpm run lintandnpx next build\n\n### Notes\n\n- Both new server components useexport const revalidate = 300so the landing page does not hammer/api/courseson every visitor (the underlying axios call cannot tag its own request withnext.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 theprefers-reduced-motionemulation toreduce. 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
Accessibility
Enhancements