Skip to main content

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.

The Admin Dashboard is the central control plane for e-board members of the Beta Alpha Psi Beta Tau Chapter. It lives at the /admin route and is exclusively accessible to authenticated users whose role field equals "e-board" — any other role is implicitly excluded by the route guard. On mount, the page fires four parallel fetches: GET /users/summary (twice, split into admin and member lists), GET /sponsors/, and GET /announcements. All state is held inside the Admin component; child sections receive sliced props and callback functions rather than managing their own top-level data. The page renders a full-width Navbar (with role-aware nav links), then a two-column md:grid-cols-2 grid containing the eight dashboard sections described below, and ends with a Footer. All destructive and mutating actions are wrapped in either a ConfirmDialog (reusable, centralized) or a component-local ConfirmationModal, keeping confirmation logic consistent across the entire surface.

Layout

The dashboard uses a responsive CSS grid defined in Admin.tsx:
<div className="grid grid-cols-1 md:grid-cols-2 gap-12 w-full">
Sections are assigned order-N classes that control mobile vs. desktop stacking. The Resource Management and Eboard Management sections span the full two-column width (col-span-1 md:col-span-2) because they contain complex, accordion-style UIs that would be too cramped in a single column.

Dashboard Sections

Announcements

Create, view, edit, and delete rich-text announcements. Sorted pinned-first then newest-first. Uses TinyMCE for authoring new announcements.

Admin Users

List of current e-board email addresses. Add new admins by email; remove them with a single click backed by /users/delete-user.

Sponsors

Full CRUD for sponsor companies. Tier management (platinum → bronze), profile bio and links, passcode. See the dedicated Sponsors page.

General Members

Active general-member roster with search, rank filter, inline archive, and full profile edit. See the Members page.

Archived Members

Soft-deleted members. Restore with a single click; they re-appear in the active list immediately.

Alumni Members

Read-only list pulled from GET /member-info/alumni/summary. Search only — no editing from this panel.

Resource Management

Two-level hierarchy: categories (firm or chapter) containing file resources. Uploads go to Vercel Blob for files >4.5 MB.

Eboard Management

Create and edit eboard role entries with display rank ordering, role email, and member email linkage. See the Eboard page.

Access Control

The /admin route does not redirect unauthenticated users automatically within the component itself — the route guard is expected to be applied at the router level. Inside Admin.tsx, the useAuth() hook exposes role and session; all fetch calls use session.access_token as the Bearer token. Any request from a non-e-board account will be rejected at the API layer.
The role check happens at the Supabase JWT level on the backend. The frontend reads role from useAuth():
const { role, session, isAuthenticated } = useAuth();

Global Confirm Dialog

All mutating actions in Admin.tsx funnel through a single ConfirmDialog managed by the confirmDialogInfo state object:
interface ConfirmDialogInfo {
  isOpen: boolean;
  title: string;
  message: string;
  onConfirm: () => void;
  confirmText?: string;
  cancelText?: string;
}
The helper showConfirmationDialog(title, message, onConfirmAction, confirmText?, cancelText?) sets this state and is passed down to child components (e.g., SponsorList) that need to trigger confirmations but don’t own the dialog themselves.

Data Flow Summary

Admin.tsx (state owner)
├── fetchAdmins()       → GET /users/summary  → filter role === 'e-board'
├── fetchMembers()      → GET /users/summary  → filter role === 'general-member'
├── fetchSponsors()     → GET /sponsors/
├── fetchAnnouncements()→ GET /announcements

├── EmailList (admin)   ← adminEmails[]
├── EmailList (members) ← members[{email,name,rank}]  + memberDetails cache
├── SponsorList         ← sponsors[] + tiers[]
├── ArchivedMembersList ← self-fetches via memberArchiveService
├── AlumniMembersList   ← self-fetches GET /member-info/alumni/summary
├── AnnouncementListShort ← announcements[]
├── ResourceManagement  ← self-fetches GET /resources
└── EboardManagement    ← self-fetches GET /eboard

Announcements

Announcements are fetched from GET /announcements and sorted with pinned items first, then by created_at descending. The AnnouncementListShort component renders the list inline with Edit / View / Delete icon buttons per row.
ActionEndpointMethod
Create/announcements/add-announcementPOST
Edit/announcements/edit-announcementPOST
Delete/announcements/delete-announcementPOST
CreateAnnouncementModal uses TinyMCE (keyed by VITE_TINY_MCE_KEY) for rich-text description authoring. The EditAnnouncementModal uses a plain <textarea> for editing and persists draft edits to localStorage under the key modal-announcement-edit-{id}. On successful save or deliberate discard, that key is cleared.

Admin Users

The Admin Users panel re-uses the EmailList component with userType="admin" and clickable={false}. The add flow launches AddUserModal with role="e-board". Deletion calls POST /users/delete-user with { user_email } and immediately filters the email out of local state.
The same handleDelete function removes emails from all three lists (admins, sponsors, members) in one call. Ensure the email only appears in the intended list to avoid cross-contamination.

Resource Management

Resource Management is a self-contained ResourceManagement component that spans the full grid width. It organizes content into two levels:
  • Categories — each has a name, description, and resourceType ("firm" | "chapter")
  • Resources — belong to a category; have name, description, file_key, mime_type, and a signed_url for preview/download
Files larger than 4.5 MB are routed through Vercel Blob via @vercel/blob/client’s upload() function with the server-side handler at /blob-upload/resources/:categoryId. Smaller files are posted as multipart/form-data directly to /resources/:categoryId/resources.
The ResourcePreviewModal uses signed_url to render a preview in-browser. If signed_url is null, the Eye button is disabled — this happens when a resource record exists in the database but its blob has been deleted externally.

Build docs developers (and LLMs) love