feat: add dynamic highlights carousel to Community homepage - #34
Closed
Bjjj834 wants to merge 2 commits into
Closed
Conversation
Adds a CommunityHighlightsCarousel section to the community homepage, between the hero and the Impact Dashboard. It surfaces three dynamic slides so the homepage reflects what is actually happening: - Upcoming Event: nearest future event from the merged Google Calendar feed + hardcoded events list (same merge rule as the Events page), with date/time/location and a CTA into the event page or Events list - Latest News: newest article by real publication date from the News sheet, reusing ArmorNews's CSV fallback loader - Featured Course: the catalog entry explicitly marked featured: true. Courses carry no date metadata, so the slide is labeled 'Featured Course', never 'New Course' Behavior: auto-advance every 7s; pauses on hover, keyboard focus, and hidden tab; disabled entirely under prefers-reduced-motion. Prev/next buttons, pagination dots, ArrowLeft/ArrowRight navigation, touch swipe, ARIA carousel/slide roles. Focus is never moved automatically. Data handling: pure selectors in communityHighlights.js; module-level promise cache avoids refetching on remount; each source loads independently so one failure only drops its slide; when nothing valid exists the section renders nothing rather than an empty shell. Existing calendar/sheet config is exported from ArmorEvents/ArmorNews and imported here - no key or id duplicated into new files. Analytics: homepage_highlight_view / _next / _previous / _cta_click GA4 events with slide_type, via the ReactGA4 instance initialized in App.js. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- communityHighlights.test.js: nearest-future-event selection, past and malformed dates excluded, same-day and in-progress events included, latest-news pick ignores undated articles, featured-course selection, and a guard asserting the course catalog has no date metadata (so the 'Featured Course' label stays honest) - CommunityHighlightsCarousel.test.js: loading skeleton, slide rendering, next/prev buttons, keyboard arrows, one failed source keeps other slides, all-empty renders nothing, ARIA attributes, autoplay advance, prefers-reduced-motion disables autoplay, hover pauses autoplay Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Contributor
Author
|
Note on CI: the CI workflow triggers only on pull requests targeting |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
prefers-reduced-motion, with independent loading/error/empty handling per data sourceProduct rationale
The homepage was entirely static — nothing on it changed between visits, so returning members had no reason to land there. This section makes the page reflect what is actually happening in the community right now: the event you can still register for, the newest AI news item, and a concrete course to start. Each slide has a meaningful CTA that routes into an existing flow (event page/registration, article, course), so the carousel is a discovery surface rather than a decoration.
Implementation
New files
frontend/src/landing_page/landing_page_armor/communityHighlights.js— pure, unit-tested selectors (selectNextUpcomingEvent,selectLatestNews,selectFeaturedCourse,parseEventStart) plus fetch helpers with a module-level promise cache (no refetch on remount)frontend/src/landing_page/landing_page_armor/CommunityHighlightsCarousel.js— the carousel component (custom, no new dependencies)Modified files (minimal extractions, no behavior change)
ArmorEvents.js—exportadded to the existingGCAL_ID,GCAL_API_KEY,eventsData,parseCalendarEventso the carousel imports the existing config instead of duplicating itArmorNews.js—exportadded toDEFAULT_SHEET_ID,DEFAULT_NEWS_GID,tryCsvFallbacks,csvToMatrix,normalizeRowsArmorCourses.js— catalog exported; one course markedfeatured: truewith a comment documenting the selection ruleArmorMain.js— renders<CommunityHighlightsCarousel />between hero and<ImpactDashboard />Behavior details
prefers-reduced-motion(slide transition animation is also dropped)aria-current), ArrowLeft/ArrowRight keys, touch swipe (40px threshold) — all without any third-party carousel libraryaria-roledescription="carousel"region,role="group"/aria-roledescription="slide"slides, labeled controls; focus is never moved programmatically and never trappedpublishedAt/createdAt/addedAtmetadata, so no "New Course" claim is made; a test guards thisnull(no empty shell)homepage_highlight_view/homepage_highlight_next/homepage_highlight_previous/homepage_highlight_cta_clickwithslide_type(event/news/course) via the existing ReactGA4 setupScreenshots
Verified at desktop (1440px), tablet (768px), and mobile (375px) viewports — screenshots captured locally during review (
home-desktop.png,home-tablet.png,home-mobile.png; can be attached on request). Desktop shows image-left/content-right slides; mobile stacks media above text with tappable buttons and no horizontal overflow (scrollWidth === clientWidthverified at 375px).Testing
npm run lintCI=true npx react-scripts test --watchAll=falsenpm run buildNew tests:
communityHighlights.test.js(nearest-future selection, past/malformed dates excluded, same-day + in-progress events included, latest-news date logic, featured-course rule + metadata guard) andCommunityHighlightsCarousel.test.js(skeleton, navigation via buttons/keyboard, failed-source isolation, all-empty renders nothing, ARIA attributes, autoplay, reduced-motion, hover pause).Manual verification against the running app: live calendar picked the nearest event (World Cup Party, Jul 19) over a later one; news slide matched the top item on the News page; keyboard arrows change slides; reduced-motion override showed no auto-advance after 9s and no animation class; remounting the homepage reused cached data (no duplicate calendar request); quiz and newsletter modals open and dismiss exactly as before.
Risks and limitations
startISOrely on display-string date parsing (handled, with malformed dates skipped).featured: trueinArmorCourses.js(documented in-code). If date metadata is added later, a guard test flags the opportunity to switch to a genuine "New Course".ImpactDashboardrefetches members/calendar on every homepage mount and embeds the shared Google API key (added in Upgrade Impact Dashboard with live stats and social share (closes #1) #32);classnamesandreact-iconsare imported but not declared inpackage.json. None are touched by this PR.Checklist
🤖 Generated with Claude Code