The Member Dashboard (Documentation Index
Fetch the complete documentation index at: https://mintlify.com/asubap/website/llms.txt
Use this file to discover all available pages before exploring further.
/member) is the primary landing destination for any authenticated general-member. It is wrapped in a ProtectedRoute and immediately calls POST /member-info/ to hydrate the member’s profile from the backend. The page is anchored by MemberDescription, which renders the full left-column profile card and also mounts EventMember (the right-column event panel) and ProfileEditModal within its JSX tree.
Page Architecture
MemberView composes one top-level component that owns the full authenticated layout:
MemberDescription
Renders the profile card (photo, name, major, email, phone), the hours breakdown, the About section, and the Edit Profile button. Also owns the announcements bell, the Slack shortcut, and mounts
EventMember and ProfileEditModal internally.EventMember
Rendered inside
MemberDescription. Independently fetches GET /events and categorises events into In-Session, Upcoming, and Attended. Uses EventCard for each row.ProfileEditModal
A modal layered on top of
MemberDescription that handles form state, profile-picture upload/delete, and the POST /member-info/edit-member-info/ save call.Data Flow
Session check
MemberView reads the Supabase session from useAuth(). If no session exists, ProtectedRoute redirects to login before this page mounts.Fetch member info
fetchUserDetails() is called on mount. It calls:userDetails state.Render MemberDescription
All user fields are forwarded as props.
onRefreshUserDetails (a reference to fetchUserDetails(false)) is also passed so child components can trigger a silent refresh — for example, after a successful check-in.Visibility-change refresh
MemberDescription attaches a visibilitychange listener and calls onRefreshUserDetails() whenever the browser tab regains focus, keeping hours and rank current after tab switches.Profile Fields
MemberView maintains a typed userDetails state object whose keys match the backend response from POST /member-info/:
MemberDescription, which maps them to the MemberDetail shape (camelCase) for internal state and the ProfileEditModal.
rank drives the isAlumni() check throughout the dashboard. When rank === "alumni", the hours breakdown row, the announcements bell, and the Slack button are all removed from the DOM.Hours Breakdown
Non-alumni members see five numeric values displayed below their basic info:| Field | Backend key | Description |
|---|---|---|
| Total Hours | total_hours | Sum of all categories |
| Social Hours | social_hours | Social event attendance |
| Professional Hours | professional_hours | Professional development events |
| Service Hours | service_hours | Community service events |
| Development Hours | development_hours | Chapter development events |
Edit Profile Modal
Clicking Edit Profile opensProfileEditModal. The modal uses a useRef snapshot of the initial data to detect unsaved changes and prompts a discard-confirmation dialog on close if any field was modified.
Editable fields
- Profile fields
- Profile picture
| Field | Input type | Required | Notes |
|---|---|---|---|
| Name | text | ✅ | Cannot be empty or "N/A" |
email | ✅ | Disabled — cannot change | |
| Phone Number | tel | ✅ | |
| Major(s) | text | ✅ | Free-text; multiple majors as one string |
| Graduation Year | text | ✅ | Maps to graduationDate in MemberDetail |
| Status | select | ✅ | Looking for Internship / Looking for Full-time / Not Looking |
| About | textarea | ✅ | Minimum 1 non-"N/A" character |
Save endpoint
onSave callback updates local profileData state, and a toast confirmation is shown. On failure the error detail from the response body is surfaced via showToast.
Event Panels
EventMember renders three scrollable lists (max-height 400px on sm screens, 600px on mobile):
Events In-Session
Events In-Session
Filtered by
isEventInSession(event_date, event_time, event_hours). The helper computes start and end = start + event_hours × 3600 seconds; the event is in-session when now ∈ [start, end]. Each card shows hideRSVP={true} (RSVP is hidden during the session). A successful check-in calls fetchEvents() and onRefreshUserDetails?.() to refresh both the event list and the hours totals.Upcoming Events
Upcoming Events
All non-in-session events where
event_date/time >= now, sorted ascending by date. Renders EventCard with full RSVP and check-in buttons (subject to alumni restrictions).Attended Events
Attended Events
Sourced from the
eventAttendance prop (the event_attendance array from POST /member-info/), passed from MemberView through MemberDescription into EventMember. Items are sorted descending by event_date (most recent first). Each item is adapted to the Event shape before passing to EventCard:Announcements Bell
The bell icon (fromlucide-react) in the top-right of the profile column is only visible to non-alumni. It tracks unread count using localStorage under the key readAnnouncementIds.
addAnnouncementsToReadStorage(allFetchedAnnouncementIds) to mark all currently fetched announcements as read, then recalculates the badge count. The badge caps display at 9+ if there are more than nine unread items.
Announcements are fetched from:
MemberAnnouncementsListModal.
Slack Shortcut
A second icon button next to the bell openshttps://beta-alpha-psi-space.slack.com/ in a new tab. Like the bell, it is hidden for alumni.
Alumni Restrictions Summary
isAlumni(rank) returns true:
- Hours section is removed
- Announcements bell is removed
- Slack button is removed
EventCardhides the RSVP and Check-In buttons (handled inEventCarditself via the sameisAlumniUserflag)
Extending the Dashboard
To add a new hours category:- Add the backend key to the
userDetailsstate object inMemberView. - Pass it as a new prop from
MemberView → MemberDescription → EventMemberas needed. - Add a corresponding entry to the
MemberDetailtype intypes/index.ts. - Render it alongside the existing hour rows in
MemberDescription.