[#336] Blade/alumni dashboard#406
Conversation
📝 WalkthroughWalkthroughAdds an alumni-specific dashboard path and three new alumni UI components; fetches past hackathons for alumni; refactors the donation UI into tiered options with a details modal; standardizes dashboard header and consolidates payment/points rendering. (49 words) Changes
Sequence DiagramsequenceDiagram
participant User as User
participant Dashboard as MemberDashboard
participant Fetcher as DataFetcher
participant AlumniView as AlumniDashboard
participant NonAlumniView as NonAlumniDashboard
participant Recap as AlumniRecap
User->>Dashboard: Request dashboard
Dashboard->>Dashboard: Determine isAlumni from member.gradDate
Dashboard->>Fetcher: Fetch events, dues
alt isAlumni
Dashboard->>Fetcher: Fetch past hackathons
Fetcher-->>Dashboard: Return events, dues, hackathons
Dashboard->>AlumniView: Render alumni layout
AlumniView->>Recap: Provide member, events, hackathons, boardPosition
Recap->>Recap: Compute class year, tenure, counts, most active year
AlumniView-->>User: Display alumni dashboard (Discord, EarlyAccessVolunteer, MemberInfo, Donate, DayInHistory, AlumniRecap)
else not alumni
Fetcher-->>Dashboard: Return events, dues
Dashboard->>NonAlumniView: Render standard layout
NonAlumniView-->>User: Display member dashboard (Payment, Points, EventNumber)
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Suggested reviewers
🚥 Pre-merge checks | ✅ 7 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (7 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
apps/blade/src/app/_components/dashboard/member-dashboard/member-dashboard.tsx (1)
29-63:⚠️ Potential issue | 🟠 MajorCompare full graduation dates here, not just years.
This keeps some graduates in the member flow after they should flip to alumni. For example, a member with
gradDate = 2026-05-01still returnsfalseon October 1, 2026 becauseyearsUntilGradis0, and the graduate/Post Doctorate branch never flips at all. Reuse an actualhasGraduatedcheck for those branches.Suggested fix
const currentDate = new Date(); + const hasGraduated = gradDateObj < currentDate; // Check if dates are valid if (isNaN(gradDateObj.getTime())) return false; @@ if ( member.levelOfStudy === "Graduate University (Masters, Professional, Doctoral, etc)" || member.levelOfStudy === "Post Doctorate" ) { - return false; + return hasGraduated; } - // If graduation date has passed, they are alumni - if (yearsUntilGrad < 0) return true; - - // Current year graduates are still seniors until they actually graduate - return false; + return hasGraduated;🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/blade/src/app/_components/dashboard/member-dashboard/member-dashboard.tsx` around lines 29 - 63, calcAlumniStatus currently compares only years; change it to compare full dates: first parse gradDate into gradDateObj and compute hasGraduated = gradDateObj.getTime() <= currentDate.getTime() (and return false if gradDateObj is invalid). For the "Less than Secondary / High School" and "Secondary / High School" branches, replace the yearsUntilGrad check with a comparison to a cutoffDate 4 years before today (e.g. cutoffDate.setFullYear(currentYear - 4)) and return true if gradDateObj <= cutoffDate. For the graduate/Post Doctorate branch (member.levelOfStudy === "Graduate University..." or "Post Doctorate") return true only when hasGraduated is true (otherwise false). Finally, the general fall-through should return hasGraduated (true if gradDateObj is in the past, false otherwise). Use the function name calcAlumniStatus and member.levelOfStudy to locate the changes.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@apps/blade/src/app/_components/dashboard/member-dashboard/AlumniRecap.tsx`:
- Around line 47-70: getMostActiveYear currently counts both events and
hackathons but returns a label that says "event(s)"; update the returned string
in getMostActiveYear to use a neutral term like "activity/activities" (or
"activity(ies)") instead of "event(s)" so the summary accurately reflects
combined events and hackathons — locate the template literal in
getMostActiveYear that builds `${bestYear} (${bestCount} event${...})` and
change the word "event" to "activity" and adjust pluralization accordingly.
In
`@apps/blade/src/app/_components/dashboard/member-dashboard/day-in-history.tsx`:
- Around line 19-22: The current dailyImage selection uses Math.random so it
changes on every render; replace that with a deterministic index derived from
the current date (e.g., use a date string or numeric combination of
getFullYear/getMonth/getDate, or a small hash of new
Date().toISOString().slice(0,10)) and compute index = dateValue %
ALUMNI_PHOTOS.length, then pick ALUMNI_PHOTOS[index] for dailyImage; update the
code that defines dailyImage to remove Math.random and use this date-derived
index so the same image is shown for the entire day.
In
`@apps/blade/src/app/_components/dashboard/member-dashboard/payment/donate.tsx`:
- Around line 99-103: The "Donate" links are not uniquely announced; update the
modal checkout link rendered with Button (asChild) and Link so each has a
distinct accessible name by including the tier/amount in the visible text or an
aria-label derived from the option object (e.g., use o.label or o.amount).
Locate the link generation that uses Link href={o.href} and change the Link
content or add aria-label={`Donate — ${o.label} (${o.amount})`} (or similar) so
screen readers announce the specific tier/amount for each Donate action.
---
Outside diff comments:
In
`@apps/blade/src/app/_components/dashboard/member-dashboard/member-dashboard.tsx`:
- Around line 29-63: calcAlumniStatus currently compares only years; change it
to compare full dates: first parse gradDate into gradDateObj and compute
hasGraduated = gradDateObj.getTime() <= currentDate.getTime() (and return false
if gradDateObj is invalid). For the "Less than Secondary / High School" and
"Secondary / High School" branches, replace the yearsUntilGrad check with a
comparison to a cutoffDate 4 years before today (e.g.
cutoffDate.setFullYear(currentYear - 4)) and return true if gradDateObj <=
cutoffDate. For the graduate/Post Doctorate branch (member.levelOfStudy ===
"Graduate University..." or "Post Doctorate") return true only when hasGraduated
is true (otherwise false). Finally, the general fall-through should return
hasGraduated (true if gradDateObj is in the past, false otherwise). Use the
function name calcAlumniStatus and member.levelOfStudy to locate the changes.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yml
Review profile: CHILL
Plan: Pro
Run ID: f281f832-4516-4358-bce4-1191aad14920
⛔ Files ignored due to path filters (7)
apps/blade/public/alumni-recap/2019-crowd.jpgis excluded by!**/*.jpgapps/blade/public/alumni-recap/DSC_0125.JPGis excluded by!**/*.jpgapps/blade/public/alumni-recap/IMG_4699.JPGis excluded by!**/*.jpgapps/blade/public/alumni-recap/IMG_6763.JPGis excluded by!**/*.jpgapps/blade/public/alumni-recap/IMG_8131.JPGis excluded by!**/*.jpgapps/blade/public/alumni-recap/KnightHacks.JPGis excluded by!**/*.jpgapps/blade/public/alumni-recap/ThankYouAllForAttendingYouAllMeanTheWorldToUs.pngis excluded by!**/*.png
📒 Files selected for processing (5)
apps/blade/src/app/_components/dashboard/member-dashboard/AlumniRecap.tsxapps/blade/src/app/_components/dashboard/member-dashboard/day-in-history.tsxapps/blade/src/app/_components/dashboard/member-dashboard/early-access-volunteer.tsxapps/blade/src/app/_components/dashboard/member-dashboard/member-dashboard.tsxapps/blade/src/app/_components/dashboard/member-dashboard/payment/donate.tsx
There was a problem hiding this comment.
🧹 Nitpick comments (2)
apps/blade/src/app/_components/dashboard/member-dashboard/member-dashboard.tsx (1)
29-56: Consider handling "I'm not currently a student" explicitly.The
calcAlumniStatusfunction handles 4 of 11 education levels explicitly and falls through tohasGraduatedfor the rest. This means someone who selected "I'm not currently a student" with any past graduation date would be marked as alumni.Per the schema in
packages/consts/src/forms/index.ts, other unhandled levels include:
- "I'm not currently a student"
- "Code School / Bootcamp"
- "Undergraduate University (2 year...)" and "(3+ year)"
If this fallthrough behavior is intentional, a brief comment explaining the logic would help future maintainers.
🔧 Optional: Add comment clarifying intent
if ( member.levelOfStudy === "Graduate University (Masters, Professional, Doctoral, etc)" || member.levelOfStudy === "Post Doctorate" ) { return hasGraduated; } + // All other education levels (undergraduate, bootcamp, vocational, etc.) + // are considered alumni if their graduation date has passed return hasGraduated; };🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/blade/src/app/_components/dashboard/member-dashboard/member-dashboard.tsx` around lines 29 - 56, The calcAlumniStatus function currently falls through to hasGraduated for unhandled levelOfStudy values; explicitly handle the "I'm not currently a student" option by detecting member.levelOfStudy === "I'm not currently a student" and return false (not alumni), and either also add explicit branches or a clarifying comment for other levels like "Code School / Bootcamp" and the two "Undergraduate University" variants to document intended fallthrough behavior; update the calcAlumniStatus function (and its levelOfStudy checks) accordingly so the logic is explicit for "I'm not currently a student" and maintainers see the rationale.apps/blade/src/app/_components/dashboard/member-dashboard/payment/donate.tsx (1)
25-56: Stripe payment links are functional and safe to hardcode. All four links return HTTP 200, confirming they work as expected. Since these are public Stripe checkout URLs (not API keys or secrets), hardcoding them complies with the codebase guidelines. If donation URLs differ across environments (dev/staging/prod), consider moving them to environment variables for easier management—this is optional but helps with environment-specific configuration.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/blade/src/app/_components/dashboard/member-dashboard/payment/donate.tsx` around lines 25 - 56, The hardcoded Stripe checkout URLs in DONATION_OPTIONS should either remain as-is (they are public and safe) or be made environment-configurable; update the DONATION_OPTIONS initializer in donate.tsx to source each option.href from environment variables (e.g., NEXT_PUBLIC_STRIPE_SUPPORTER_URL, NEXT_PUBLIC_STRIPE_CONTRIBUTOR_URL, NEXT_PUBLIC_STRIPE_PARTNER_URL, NEXT_PUBLIC_STRIPE_CUSTOM_URL) with the current hardcoded URL as the fallback, so the app supports differing dev/staging/prod links while keeping the existing values if env vars are not provided.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In
`@apps/blade/src/app/_components/dashboard/member-dashboard/member-dashboard.tsx`:
- Around line 29-56: The calcAlumniStatus function currently falls through to
hasGraduated for unhandled levelOfStudy values; explicitly handle the "I'm not
currently a student" option by detecting member.levelOfStudy === "I'm not
currently a student" and return false (not alumni), and either also add explicit
branches or a clarifying comment for other levels like "Code School / Bootcamp"
and the two "Undergraduate University" variants to document intended fallthrough
behavior; update the calcAlumniStatus function (and its levelOfStudy checks)
accordingly so the logic is explicit for "I'm not currently a student" and
maintainers see the rationale.
In
`@apps/blade/src/app/_components/dashboard/member-dashboard/payment/donate.tsx`:
- Around line 25-56: The hardcoded Stripe checkout URLs in DONATION_OPTIONS
should either remain as-is (they are public and safe) or be made
environment-configurable; update the DONATION_OPTIONS initializer in donate.tsx
to source each option.href from environment variables (e.g.,
NEXT_PUBLIC_STRIPE_SUPPORTER_URL, NEXT_PUBLIC_STRIPE_CONTRIBUTOR_URL,
NEXT_PUBLIC_STRIPE_PARTNER_URL, NEXT_PUBLIC_STRIPE_CUSTOM_URL) with the current
hardcoded URL as the fallback, so the app supports differing dev/staging/prod
links while keeping the existing values if env vars are not provided.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yml
Review profile: CHILL
Plan: Pro
Run ID: 1f3dd851-a3c2-4225-a2f8-07ff20c29a2e
📒 Files selected for processing (4)
apps/blade/src/app/_components/dashboard/member-dashboard/day-in-history.tsxapps/blade/src/app/_components/dashboard/member-dashboard/early-access-volunteer.tsxapps/blade/src/app/_components/dashboard/member-dashboard/member-dashboard.tsxapps/blade/src/app/_components/dashboard/member-dashboard/payment/donate.tsx
🚧 Files skipped from review as they are similar to previous changes (2)
- apps/blade/src/app/_components/dashboard/member-dashboard/day-in-history.tsx
- apps/blade/src/app/_components/dashboard/member-dashboard/early-access-volunteer.tsx
Implemented the simple AlumniRecap into the member dashboard
27cfd61 to
c9d2d53
Compare
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (3)
apps/blade/src/app/_components/dashboard/member-dashboard/payment/donate.tsx (1)
130-154: Consider addingaria-labelto the grid donation buttons for accessibility parity.The modal links have
aria-label={...}(line 102), but the main grid buttons only show{opt.priceLabel}and{opt.name}as visible text. Screen readers will announce both spans, which may suffice—but for consistency with the modal and to explicitly label the action, consider adding anaria-labelhere too.♿ Optional: add aria-label for parity
<Button key={opt.name} asChild variant="outline" className="flex min-h-[88px] w-full flex-col items-center justify-center text-center transition-all hover:-translate-y-0.5 hover:shadow-md" > <Link href={opt.href} target="_blank" rel="noopener noreferrer" + aria-label={`Donate — ${opt.name} (${opt.priceLabel})`} className="flex w-full min-w-0 flex-col items-center justify-center text-center" >As per coding guidelines,
apps/blade/**: "Accessibility (alt text, ARIA, semantic HTML)".🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/blade/src/app/_components/dashboard/member-dashboard/payment/donate.tsx` around lines 130 - 154, The donation grid buttons rendered in the DONATION_OPTIONS.map lack an explicit aria-label for screen readers; add an aria-label to the interactive element (the Link or the surrounding Button) using the option data (e.g., combine opt.priceLabel and opt.name into a single descriptive string like "Donate {opt.name} — {opt.priceLabel}") so it matches the modal links' accessibility behavior and ensures consistent announcement by assistive technologies; update the Link/Button in the map to include aria-label={...} accordingly.apps/blade/src/app/_components/dashboard/member-dashboard/early-access-volunteer.tsx (1)
47-59: Links to forms lack accessible names describing the destination.Each button links to
/forms/${f.slugName}, but screen readers only hear the formatted slug. Consider adding context about the action (e.g., "Fill out [Form Name]").♿ Optional: improve link accessibility
<Button key={f.slugName} asChild variant="outline" className="h-auto w-full justify-between py-3" > - <Link href={`/forms/${f.slugName}`} className="min-w-0"> + <Link + href={`/forms/${f.slugName}`} + className="min-w-0" + aria-label={`Fill out ${f.slugName.split("-").map(w => w.charAt(0).toUpperCase() + w.slice(1)).join(" ")} form`} + >As per coding guidelines,
apps/blade/**: "Accessibility (alt text, ARIA, semantic HTML)".🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/blade/src/app/_components/dashboard/member-dashboard/early-access-volunteer.tsx` around lines 47 - 59, The link text currently renders the formatted slug from f.slugName but lacks an accessible name describing the action; update the Link (and its inner content) to include a descriptive accessible label such as aria-label={`Fill out ${formattedName} form`} or add a visually hidden element with the text "Fill out [Form Name]" alongside the displayed slug (use the same formattedName computed from f.slugName). Ensure the aria-label or sr-only text uses the same capitalization logic (the existing .split("-").map(...).join(" ")) so screen readers announce "Fill out [Form Name]" while visual output remains unchanged.apps/blade/src/app/_components/dashboard/member-dashboard/AlumniRecap.tsx (1)
94-116: Hardcoded officer list will require manual updates each term.Adrian, Carlos, Jason, and Nat are hardcoded. When leadership changes, this file needs a code change and redeploy. Consider fetching officers dynamically or extracting to a config file that non-developers can update.
Would you like me to open an issue to track making the officers list dynamic or configurable?
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/blade/src/app/_components/dashboard/member-dashboard/AlumniRecap.tsx` around lines 94 - 116, The AlumniRecap component currently renders a hardcoded officers list (Adrian, Carlos, Jason, Nat); change it to render dynamically by sourcing data instead of static JSX: add an officers prop or a data fetch (e.g., getOfficers / useEffect or pass in an imported config) and replace the four hardcoded <li> entries with a map over officers (use unique keys and render officer.name and officer.role), and ensure AlumniRecap and its callers are updated to provide the officers array or the fetching mechanism so future leadership changes don’t require code edits.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@apps/blade/src/app/_components/dashboard/member-dashboard/AlumniRecap.tsx`:
- Around line 33-45: The getYearsAsMember function can return negative values
when dateCreated is in the future; after computing years and the anniversary
adjustment (the local variables years and hasAnniversaryPassed inside
getYearsAsMember), clamp the final computed value to a minimum of 0 before
returning it (e.g., compute adjustedYears = hasAnniversaryPassed ? years : years
- 1; if adjustedYears < 0 return 0 else return adjustedYears) so the function
never returns negatives even with future dates.
- Around line 73-78: The AlumniRecap component declares a boardPosition?: string
| null prop in AlumniRecapProps but it is never passed, so the "Board History"
section in AlumniRecap will never render; either remove boardPosition from
AlumniRecapProps and delete the Board History rendering in the AlumniRecap
function (and clean up any JSX referencing boardPosition), or add boardPosition
to the Member data model and populate it at every call site that renders
<AlumniRecap> (or derive it from your Board/Position table) so AlumniRecap
receives a real value; update the AlumniRecap signature and all callers
accordingly and ensure types and tests reflect the chosen approach.
---
Nitpick comments:
In `@apps/blade/src/app/_components/dashboard/member-dashboard/AlumniRecap.tsx`:
- Around line 94-116: The AlumniRecap component currently renders a hardcoded
officers list (Adrian, Carlos, Jason, Nat); change it to render dynamically by
sourcing data instead of static JSX: add an officers prop or a data fetch (e.g.,
getOfficers / useEffect or pass in an imported config) and replace the four
hardcoded <li> entries with a map over officers (use unique keys and render
officer.name and officer.role), and ensure AlumniRecap and its callers are
updated to provide the officers array or the fetching mechanism so future
leadership changes don’t require code edits.
In
`@apps/blade/src/app/_components/dashboard/member-dashboard/early-access-volunteer.tsx`:
- Around line 47-59: The link text currently renders the formatted slug from
f.slugName but lacks an accessible name describing the action; update the Link
(and its inner content) to include a descriptive accessible label such as
aria-label={`Fill out ${formattedName} form`} or add a visually hidden element
with the text "Fill out [Form Name]" alongside the displayed slug (use the same
formattedName computed from f.slugName). Ensure the aria-label or sr-only text
uses the same capitalization logic (the existing .split("-").map(...).join(" "))
so screen readers announce "Fill out [Form Name]" while visual output remains
unchanged.
In
`@apps/blade/src/app/_components/dashboard/member-dashboard/payment/donate.tsx`:
- Around line 130-154: The donation grid buttons rendered in the
DONATION_OPTIONS.map lack an explicit aria-label for screen readers; add an
aria-label to the interactive element (the Link or the surrounding Button) using
the option data (e.g., combine opt.priceLabel and opt.name into a single
descriptive string like "Donate {opt.name} — {opt.priceLabel}") so it matches
the modal links' accessibility behavior and ensures consistent announcement by
assistive technologies; update the Link/Button in the map to include
aria-label={...} accordingly.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yml
Review profile: CHILL
Plan: Pro
Run ID: bbd63f66-9c4f-4d44-9407-18eb4aa110a9
⛔ Files ignored due to path filters (7)
apps/blade/public/alumni-recap/2019-crowd.jpgis excluded by!**/*.jpgapps/blade/public/alumni-recap/DSC_0125.JPGis excluded by!**/*.jpgapps/blade/public/alumni-recap/IMG_4699.JPGis excluded by!**/*.jpgapps/blade/public/alumni-recap/IMG_6763.JPGis excluded by!**/*.jpgapps/blade/public/alumni-recap/IMG_8131.JPGis excluded by!**/*.jpgapps/blade/public/alumni-recap/KnightHacks.JPGis excluded by!**/*.jpgapps/blade/public/alumni-recap/ThankYouAllForAttendingYouAllMeanTheWorldToUs.pngis excluded by!**/*.png
📒 Files selected for processing (5)
apps/blade/src/app/_components/dashboard/member-dashboard/AlumniRecap.tsxapps/blade/src/app/_components/dashboard/member-dashboard/day-in-history.tsxapps/blade/src/app/_components/dashboard/member-dashboard/early-access-volunteer.tsxapps/blade/src/app/_components/dashboard/member-dashboard/member-dashboard.tsxapps/blade/src/app/_components/dashboard/member-dashboard/payment/donate.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
- apps/blade/src/app/_components/dashboard/member-dashboard/day-in-history.tsx
Why
We needed an updated alumni dashboard with new donation options and stats
What
Closes: #336
Test Plan
Tested ui on mobile and large devices. Tested stripe links.
Checklist
db:pushbefore mergingSummary by CodeRabbit
New Features
Improvements