Learn. Teach. Earn.
CampusHustle is a university-verified peer-to-peer academic marketplace where students can find tutors, buy and sell study notes, and get AI-powered study help — all within a single trusted campus community. Think of it as an academic creator economy: students earn from knowledge they already have, and learners get affordable, relevant help from peers who recently took the same courses.
- Tech Stack
- Getting Started
- Available Scripts
- Environment Variables
- Project Structure
- Features
- Application Routes
- Architecture Notes
- Testing
- CI/CD Pipeline
- Contributing
| Technology | Version | Role |
|---|---|---|
| React | 19 | Component-based UI library |
| Vite | 8 | Build tool and dev server |
| Tailwind CSS | 4 | Utility-first styling with custom design tokens |
| React Router | 7 | Client-side routing |
| Socket.IO Client | 4 | Real-time chat connection |
| Motion | 13 | Animation library |
| Tabler Icons | 3 | Icon set |
| Vitest | 4 | Unit and component test runner |
| Testing Library | 16 | DOM assertions and user interaction testing |
- Node.js
v18or higher - npm
v9or higher
# 1. Clone the repo
git clone git@github.com:CampusHustle/frontend.git
cd frontend
# 2. Install dependencies
npm install
# 3. Copy the environment file and fill in your values
cp .env.example .env
# 4. Start the dev server
npm run devThe app runs at http://localhost:5173.
| Command | What it does |
|---|---|
npm run dev |
Starts the dev server with hot reload |
npm run build |
Builds the production bundle into dist/ |
npm run preview |
Serves the production build locally |
npm run lint |
Runs ESLint across the whole project |
npm test |
Runs the full Vitest test suite once |
npm run test:watch |
Runs Vitest in watch mode |
Copy .env.example to .env and set the following:
| Variable | Description | Example |
|---|---|---|
VITE_API_URL |
Base URL for the backend API | https://api.campushustle.com |
VITE_SOCKET_URL |
WebSocket server URL (defaults to VITE_API_URL if not set) |
wss://api.campushustle.com |
In development with Vite's proxy configured, you can leave
VITE_API_URLempty.
frontend/
├── public/ # Static assets
├── src/
│ ├── admin/ # Admin dashboard (separate section)
│ │ ├── components/ # AdminLayout, InteractiveCharts, ModalsAndDrawers
│ │ ├── context/ # AdminThemeContext (dark/light toggle)
│ │ ├── screens/ # Dashboard, UserManagement, VerificationQueue,
│ │ │ # ReportsModeration, ReportDetail
│ │ └── mockData.js # Seed data for admin screens
│ │
│ ├── api/ # API layer
│ │ ├── client.js # Axios/fetch base client with auth headers
│ │ ├── authApi.js # Login, signup, profile, token refresh
│ │ ├── tutorApi.js # Tutor search, profile, availability
│ │ ├── bookingApi.js # Create and manage bookings
│ │ ├── chatApi.js # Conversations and messages
│ │ ├── noteApi.js # Note listings and purchases
│ │ ├── aiApi.js # AI study assistant
│ │ ├── reviewApi.js # Ratings and reviews
│ │ ├── notificationApi.js # User notifications
│ │ ├── mockAuthApi.js # Mock auth (used in offline/test mode)
│ │ ├── mockBookingApi.js # Mock booking state
│ │ ├── mockChatApi.js # Seed chat data and mock subscription
│ │ └── mockUsers.js # Seed tutor/user data
│ │
│ ├── components/ # Reusable UI components
│ │ ├── AppNavbar.jsx # Top navigation bar (search, profile, logout)
│ │ ├── BookingCard.jsx # Single booking row with status badge and actions
│ │ ├── BookingStatusBadge.jsx # Pending/confirmed/completed/cancelled pill
│ │ ├── ConsentModal.jsx # Contact-sharing consent dialog
│ │ ├── FloatingAiAssistant.jsx # Floating chat bubble for AI assistant
│ │ ├── Footer.jsx # Site-wide footer
│ │ ├── LogoutWarningModal.jsx # Logout confirmation dialog
│ │ ├── PurchaseCard.jsx # Note purchase summary card
│ │ └── ... # Landing page sections (Hero, Bento, Stats, etc.)
│ │
│ ├── hooks/
│ │ └── useSocket.js # Socket lifecycle hook (connect/disconnect/status)
│ │
│ ├── pages/ # Thin re-exports of screens (for test imports)
│ │
│ ├── screens/ # Full-page screen components
│ │ ├── HomeScreen.jsx # Landing page
│ │ ├── LoginScreen.jsx # Sign in
│ │ ├── SignupScreen.jsx # Sign up with .edu email
│ │ ├── VerifyEmailScreen.jsx
│ │ ├── CompleteProfileScreen.jsx
│ │ ├── FindTutorScreen.jsx # Search and filter tutors
│ │ ├── TutorDetailScreen.jsx # Tutor profile, availability, booking
│ │ ├── MarketplaceScreen.jsx # Study notes marketplace
│ │ ├── NoteDetailScreen.jsx # Single note with purchase flow
│ │ ├── ProfileScreen.jsx # Logged-in user profile
│ │ ├── PostListingScreen.jsx # Create a new note listing
│ │ ├── BookingScreen.jsx # Student's booking history and status
│ │ ├── TutorBookingRequestScreen.jsx # Tutor's incoming requests
│ │ ├── ChatScreen.jsx # Real-time peer messaging
│ │ ├── AiChatScreen.jsx # AI study assistant
│ │ ├── TermsScreen.jsx
│ │ └── PrivacyScreen.jsx
│ │
│ ├── services/
│ │ └── socket.js # Socket.IO factory (auth token, reconnection config)
│ │
│ ├── utils/
│ │ ├── sanitize.js # Message and display text sanitization
│ │ ├── session.js # localStorage session management (tokens, user)
│ │ ├── user.js # Profile helpers (hasCompletedProfile, profileFromForm)
│ │ ├── validators.js # Email, name, and input validation
│ │ └── theme.js # Theme utilities
│ │
│ ├── __tests__/ # 26 test files covering pages, components, API, session
│ ├── App.jsx # Root component — all routes and global state
│ ├── main.jsx # App entry point
│ ├── index.css # Global styles and Tailwind v4 design tokens
│ └── setupTests.js # Vitest global setup (jest-dom, observer stubs)
│
├── .env.example # Environment variable template
├── .github/workflows/ci.yml # GitHub Actions CI/CD pipeline
├── eslint.config.js
├── vite.config.js
└── package.json
- Find a tutor — search by subject, price range, rating, and department with live filtering
- Book a session — pick an available time slot from a tutor's weekly grid and request a booking
- Buy study notes — browse peer-created PDFs and exam prep material, pay via Telebirr or bank transfer
- Real-time chat — message tutors directly once a booking is confirmed
- Share contact info — send verified contact details through the chat with explicit consent
- AI study assistant — ask questions and get answers grounded in tutor-uploaded material
- Set availability — configure fixed weekly time slots for booking
- Manage requests — accept or decline incoming booking requests from a dedicated dashboard
- Sell notes — upload study materials and earn from every purchase
- Live chat — communicate with students after accepting their booking
- Booking status flow — pending → confirmed → completed (or cancelled at any stage)
- Input sanitization — all user-submitted text is stripped of control characters and length-capped before display
- Session persistence — access/refresh tokens and last-visited page survive page reloads
- Admin panel — moderation dashboard for verifying accounts, managing users, and resolving reports
| Route | Screen | Notes |
|---|---|---|
/ |
HomeScreen | Public landing page |
/login |
LoginScreen | Email + password sign in |
/signup |
SignupScreen | Student email registration |
/verify-email |
VerifyEmailScreen | Token-based email verification |
/complete-profile |
CompleteProfileScreen | Onboarding after signup |
/tutor |
FindTutorScreen | Search and filter tutors |
/tutor/:id |
TutorDetailScreen | Tutor profile, availability, booking panel |
/market |
MarketplaceScreen | Browse all study notes |
/notes/:id |
NoteDetailPage | Note preview and purchase |
/notes/:id/payment |
NotePaymentPage | Upload payment receipt |
/profile |
ProfileScreen | Logged-in user's profile |
/post-listing |
PostListingScreen | Create a note listing |
/bookings |
BookingScreen | Student's booking history |
/tutor-requests |
TutorBookingRequestScreen | Tutor's incoming requests |
/chat |
ChatScreen | Peer messaging inbox |
/chat/:id |
ChatScreen | Direct conversation with a specific user |
/assistant |
AiChatScreen | AI study assistant |
/admin |
AdminLayout → DashboardOverviewScreen | Admin dashboard |
/admin/users |
UserManagementScreen | User list and actions |
/admin/verification |
VerificationQueueScreen | ID verification queue |
/admin/reports |
ReportsModerationScreen | Abuse report list |
/admin/reports/:id |
ReportDetailScreen | Individual report detail |
/terms |
TermsScreen | Terms of service |
/privacy |
PrivacyScreen | Privacy policy |
All routes live in src/App.jsx. Global state (current user, logout modal, tutorial list) is managed at the AppRoutes level and passed down as props. There is no Redux or Zustand — the app uses React's built-in useState and useEffect for state management.
On login, the backend returns an accessToken and refreshToken. Both are stored in localStorage via src/utils/session.js. The current user object is also persisted so the app can restore session state across page reloads. The getAccessToken() helper is called by the API client to attach the Authorization header to every request.
The useSocket hook in src/hooks/useSocket.js manages the Socket.IO lifecycle. It creates a socket via src/services/socket.js, attaches the access token in the auth handshake, handles connect / disconnect / connect_error / reconnect_attempt events, and cleans up all listeners on unmount. The ChatScreen uses the socket directly to send and receive messages.
src/utils/sanitize.js exports two helpers used across every chat input in the app:
sanitizeMessage(text)— strips Unicode control characters and caps the string at 2000 characterssanitizeDisplayText(text)— same logic, capped at 200 characters for names and labels
React's JSX renderer escapes HTML by default, so there is no innerHTML injection risk — the sanitizer handles invisible control characters and length abuse.
src/api/client.js is a shared fetch/axios base that attaches the access token. Every other file in src/api/ uses this client. The mock API files (mockAuthApi.js, mockBookingApi.js, mockChatApi.js) are used in tests and offline development — swap them out for the real endpoints by changing the import in the consuming screen.
The project has 26 test files in src/__tests__/ covering:
- Page rendering and user interactions (login, signup, profile, tutor detail, marketplace, booking, chat)
- API layer (auth integration, booking status transitions)
- Utility functions (session management, validators)
- UI state (all four booking statuses, connection status badge, consent modal flow)
Run all tests:
npm testRun a specific file:
npm test -- BookingStatusWatch mode during development:
npm run test:watchTests use Vitest + Testing Library. Mocks for the socket, API calls, and browser APIs (localStorage, clipboard, IntersectionObserver) are set up in src/setupTests.js and per-test via vi.mock().
Every push to main or develop, and every pull request targeting those branches, runs the following pipeline via GitHub Actions (.github/workflows/ci.yml):
CI job:
- Check out source code
- Set up Node.js 20 with npm cache
- Install dependencies (
npm ci) - Run ESLint (
npm run lint) - Run Vitest test suite (
npm test) - Build production bundle (
npm run build)
CD job (runs only on push to main, after CI passes):
- Triggers a Vercel or Netlify deployment webhook configured as a repository secret (
VERCEL_DEPLOY_HOOK_URLorNETLIFY_BUILD_HOOK_URL)
To configure deployment, add one of those secrets in your GitHub repository settings under Settings → Secrets and variables → Actions.
- Branch off
develop— never commit directly tomain
git checkout develop
git checkout -b feature/your-feature-name- Make your changes, then verify everything passes before pushing:
npm run lint
npm test
npm run build- Push your branch and open a pull request targeting
develop
git push -u origin feature/your-feature-name-
The CI pipeline runs automatically. Address any failures before requesting review.
-
Once approved, the branch is merged into
develop. Merges tomaintrigger the production deployment.
Private and proprietary — CampusHustle, INSA CTC Summer Camp Project.