Created by Ruhaan Sahi and Adham Eldalil
React frontend for the BC Study Guide Club platform, built with Vite and React Router. It serves study guides and resources across the High School, AP, and IB diplomas, and handles club sign-up, contribution, and question submissions through a connected backend API.
This site was hosted on Microsoft Azure and reached 630+ unique users internationally. It's no longer live, as hosting for the server and domain was discontinued following the graduation of the class of 2026.
- React 18 with React Router DOM v7
- Vite
- ESLint
- Cloudinary SDK
- js-cookie
Routing is handled in src/Routes.jsx using React Router. Static routes cover the main pages (/, /AboutUs, /ContactUs, /StudyGuides, /IB, /AP, /Highschool, /Form), with a catch-all * route to a 404 page.
The IB, AP, and High School sections each pull their class list from the backend rather than hardcoding it in the frontend. src/Pages/IB.jsx fetches /r/classes/ib on mount, renders a card per class, and on click calls navigate(/IB/${class_id}). That resolves against a dynamic route:
<Route path="/IB/:class_id" element={<ClassResources />} />
ClassResources.jsx reads class_id out of the URL with useParams, then fetches /r/class/${class_id} for that class's resource list. So a single component and a single route handle every IB class rather than one page per class, and the resource list stays entirely driven by whatever the backend returns.
The frontend holds no study guide content itself. Every resource, class list, and submission goes through REST calls to a backend (Django, based on the CSRF token flow) that's backed by a SQL database, as noted in the resume for this project.
The API base URL comes from an environment variable, VITE_API_URL, read via import.meta.env.VITE_API_URL, so the same build can point at a local or deployed backend without code changes.
Endpoints called from the frontend:
GET /r/classes/ib(and equivalent for AP/High School) — list of classes for a diplomaGET /r/class/:class_id— resources for a specific class, used by the dynamic route aboveGET /getCSRFToken/— issues a CSRF tokenPOST /contributeRequest/requestform/— submits the contribute/question/join form
src/Pages/dynamicform.jsx is a single form component that changes its fields depending on what the user picks from a "What would you like to do?" dropdown:
- Contribute a Study Guide — adds diploma, GPA, school, subject, and a PDF file upload
- Ask a Question — adds school and a free-text question
- Join the Club — embeds a Google Form via iframe instead of using the custom form
Submission flow for the first two options:
- On submit, form fields are packed into a
FormDataobject (needed for the PDF upload alongside the text fields) - A CSRF token is fetched from
/getCSRFToken/withcredentials: "include"so the session cookie is sent - The
FormDatais POSTed to/contributeRequest/requestform/with the CSRF token in theX-CSRFTokenheader - On success, the form resets and a confirmation message shows for a few seconds; on failure, an error message shows and the user can retry
There's also client-side rate limiting on submission: a canSubmit flag disables the submit button after a successful send and re-enables it after 45 seconds, so the same session can't spam the endpoint. The submit button shows a loading state while the request is in flight.
- Shared nav pattern — Most pages implement the same hamburger menu (open/close state, click-outside-to-close via refs and a document click listener) rather than sharing one nav component, so the same logic is duplicated across
main.jsx,study_guides.jsx,contact_us.jsx,about_us.jsx, andnot_found.jsx - SPA fallback routing —
public/routes.jsonrewrites all paths toindex.htmlwith a 200 status, which is what makes client-side routes like/IB/ap-calculuswork correctly on a static host instead of 404ing at the server level
npm install
npm run devRequires a .env file with:
VITE_API_URL=<backend base URL>