← Back to projects

Project 05 · Full-Stack Web App

YUPlanner

A full-stack academic-planning web app for York University students. A React single-page app talks to a Spring Boot REST API backed by MySQL, unifying course search, conflict-free scheduling, professor ratings, progress tracking and PDF export — with dedicated Student, Professor and Advisor roles.

EECS 3311 · Software Design · York University — Group 8

Overview

YUPlanner is a classic three-tier web application: a JavaScript SPA in the browser, a Java REST service, and a relational database. Rather than juggling several disconnected tools, a student can search the catalogue, check prerequisites, build a conflict-free timetable, review professor ratings, track progress, and export a schedule to PDF — all from one interface. The system serves three roles, each with a dedicated dashboard and navigation.

Student

  • Browse / search / filter the course catalogue & prerequisites
  • Add & drop courses; weekly schedule view
  • Academic-progress tracking; professor ratings & reviews
  • Export schedule to PDF (jsPDF)

Professor

  • View & manage assigned course sections
  • Edit professor profile
  • Read aggregated student reviews & average rating

Advisor

  • View all catalogue courses
  • Oversee advised students and their records
3Tiers
5JPA entities
3User roles
~15REST endpoints
16Client-side routes
3Ports · 3000 / 8080 / 3306

Technology Stack

BackendTechnologyNotes
LanguageJava 17maven.compiler 17
FrameworkSpring Boot 3.3.5spring-boot-starter-parent
Web / RESTSpring Web (MVC)embedded Tomcat
PersistenceSpring Data JPA · HibernateMySQLDialect
BuildApache Mavenmvnw wrapper included
FrontendTechnologyNotes
LibraryReact 18.3.1Create React App (react-scripts 5)
Routingreact-router-dom 6.28client-side routing
UI kitMaterial-UI 6.1.9+ Emotion 11 styling
HTTPAxios 1.7.8 + Fetch APIREST calls to backend
PDF exportjsPDF 2.5.2+ autotable / html2canvas
DatabaseValue
EngineMySQL 8.0.40 (InnoDB, utf8mb4)
Schemayuplanner
JDBC URLjdbc:mysql://localhost:3306/yuplanner
Schema strategyspring.jpa.hibernate.ddl-auto=update
Seed databackend/db/yuplanner.sql

System Architecture

A three-tier architecture with a clear separation between presentation, application, and data. Presentation ↔ application is stateless HTTP with JSON bodies; application ↔ data is JDBC mediated by Hibernate.

Presentation Tier — React 18 SPA Pages & Components · React Router v6 · UserContext · Material-UI · Axios / Fetch · jsPDF — runs at localhost:3000
HTTP · REST · JSON — CORS (@CrossOrigin)
Application Tier — Spring Boot 3.3.5 REST API (Java 17) Controllers → Services → Repositories · embedded Tomcat — localhost:8080
Spring Data JPA / Hibernate · JDBC — maps entities ↔ tables
Data Tier — MySQL 8 database yuplanner · :3306

Deployment topology

In the documented development configuration, all three tiers run on the developer's workstation.

Developer Workstation Web Browser CRA dev server · React SPA localhost:3000 JVM (Java 17) Spring Boot + Tomcat localhost:8080 MySQL 8 localhost:3306 schema yuplanner HTTP/JSONREST JDBCSQL

A request flows straight down the stack and back: a React component calls the API (Axios/Fetch), Spring MVC routes it to the matching @RestController, which delegates to a service and then a Spring Data repository; Hibernate turns the entity operations into SQL over JDBC, and the JSON result propagates back up to re-render the component. The login and rating sequence diagrams (below) trace this concretely.

CORS: courseController restricts access to http://localhost:3000; the other controllers use a bare @CrossOrigin (all origins) — flagged in Limitations.

Backend Design

The backend is organised into four cooperating layers. A request enters at the controller layer and descends through service and repository layers to the entity/database layer; results return along the same path.

Controller Layer — @RestController studentController · courseController · professorController · advisorController · RatingController
call
Service Layer — business logic (interface + Impl) studentService · courseService · professorService · advisorService · RatingService
delegate
Repository Layer — Spring Data JPA extends JpaRepository<T, Integer> · derived queries
persist / fetch
Model / Entity Layer — @Entity student · course · professor · advisor · Rating
Hibernate / JDBC
MySQL 8 database yuplanner
eecs3311.app.yuplanner · package layout
eecs3311.app.yuplanner
|-- YUPlannerApplication.java   // @SpringBootApplication entry point, port 8080
|-- controller/                 // @RestController REST endpoints
|-- service/                    // business logic: interface + Impl
|-- repository/                  // Spring Data JPA repositories
+-- model/                      // @Entity JPA domain objects
RepositoryDerived query method
studentRepositorystudent findByUsernameAndPassword(String, String)
professorRepositoryprofessor findByUsernameAndPassword(String, String)
courseRepositorycourse findByCourseCode(int)
RatingRepositoryList<Rating> findByProfessor(professor)
advisorRepository(none — inherited CRUD only)
application.properties
spring.datasource.url=jdbc:mysql://localhost:3306/yuplanner
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
spring.jpa.database-platform=org.hibernate.dialect.MySQLDialect

REST API Reference

All endpoints are served at http://localhost:8080; request/response bodies are JSON. Each controller declares its own CORS policy.

MethodPathRequestResponse
POST/student/addstudent JSON"New student is added"
GET/student/getAllList<student>
POST/student/findusername, passwordmatching student, or null
GET/course/getAllList<course>
GET/course/get/{courseCode}path: intmatching course
POST/professor/findusername, passwordmatching professor, or null
GET/advisor/getAllList<advisor>
GET/rating/professor-reviews/{id}path: intList<Rating> · 404 if absent
POST/rating/addRating JSON"New rating is added"
GET/rating/average/{id}path: intdouble average · 404 if absent
POST /student/find · request
{ "username": "mhart",
  "password": "password" }
200 OK · response (null if no match)
{ "id": 1, "userid": 123456789,
  "firstname": "Maya", "lastname": "Hart",
  "username": "mhart",
  "courses": ["EECS 3311"] }

Two representative flows — the stateless login credential lookup, and the average-rating fetch (which raises a 404 via ResponseStatusException when the professor or reviews are absent):

Login.jsstudentControllerstudentServicestudentRepositoryMySQL POST /student/find {u, p} findStudent(student) findByUsernameAndPassword(u, p) SELECT … WHERE username=? AND password=? row or ∅ student or null student or null 200 OK · JSON student (null if not found)
Sequence — student login / credential lookup
StudentsReviews.jsRatingController professorService /RatingService RatingRepositoryMySQL GET /rating/average/{professorId} findProfessorById(id) findByProfessor(professor) SELECT * FROM rating WHERE professor_id=? List<Rating>List<Rating> compute average (Java stream) double average200 OK · average rating 404 if professor / reviews absent (ResponseStatusException)
Sequence — fetch a professor's average rating

Authentication is a stateless credential lookup — the SPA posts a username/password to /student/find or /professor/find and receives the matching record (or null). There is no token, session, or password hashing (see Limitations).

Data Model & Database

The domain is modelled by five JPA entities, each mapped to a MySQL table. The only database-enforced association is Rating → professor (@ManyToOne); every other cross-reference is stored as a plain string rather than a foreign key.

student id : int PKstudentid : intfirstname, lastnameusername, passwordcourses : List<String> course id : int PKcoursecode : intdept, coursenamecourseday, coursetimeterm, type, campuslocation, prerequisitescourseinstructor advisor id : int PKemployeeid : intfirstname, lastnameusername, passwordstudents : String professor id : int PKemployeeid : intfirstname, lastnameusername, passwordcourses : String Rating id : int PKprofessor_id : int FKrating : doublereview : varchar(500)studentName : String @ManyToOne * 1 enforced FK (@ManyToOne) - - -soft link (string)
EntityKey fieldsSeed rows
studentid PK · studentid · name · username/password · courses : List<String> (@ElementCollection)5
courseid PK · courseCode · dept · coursename · day/time/duration · term/section/type · campus · prerequisites · instructor6
professorid PK · employeeid · name · username/password · courses : String4
advisorid PK · employeeid · name · username/password · students : String4
Ratingid PK · professor (@ManyToOne, @JoinColumn professor_id) · rating : double · review : varchar(500) · studentName

All other conceptual links — a student's courses, a professor's courses, an advisor's students — are stored as human-readable strings, so they are neither validated nor joined by the database.

Frontend Design

The frontend is a React 18 SPA scaffolded with Create React App. The root App.js wraps the tree in a <UserProvider> (React Context) and a <BrowserRouter>, then declares all client-side routes. Cross-cutting user state (identity + role) is shared through Context rather than prop-drilling.

App.js <UserProvider> · <BrowserRouter> · <Routes> UserContext.js global user state (Context API) Public /login · Login/signup · Signup Student /student-profile· SearchCourses· DropCourse · YourProgress· ViewFullSchedule· CourseDetails/:id Professor /professor-profile· ManageCourses· EditProfessorProfile· StudentsReviews Advisor /advisor-profile· AdvisorViewCourses· AdvisorViewStudents Shared components AppBar · Sidebar / ProfessorSidebar / AdvisorSidebar · AddCourseModal · EditCourseModal · ManageCoursesTable · Login&Signup fields · buttons
RouteComponent / purpose
/login · /signupLogin / Signup — authentication
/student-profileStudentProfile — student dashboard
/student-profile/search-coursesSearchCourses — browse / filter catalogue
/student-profile/view-scheduleViewFullSchedule — weekly schedule
/professor-profileProfessorProfile + ManageCourses / StudentsReviews
/advisor-profileAdvisorProfile + AdvisorViewCourses / ViewStudents

UserContext stores the authenticated user + role after login. Components reach the API at localhost:8080 via Axios and Fetch; the schedule view uses jsPDF (with autotable / html2canvas) to export a downloadable weekly timetable.

UI mockups

The UI uses York University's brand red (#E31837) as the primary accent, most visibly on the login screen.

localhost:3000/login

(a) Login screen

localhost:3000/student-profile
YUPlanner · Student Dashboard
Search CoursesDrop CourseYour ProgressView ScheduleSign Out
Weekly Schedule

(b) Student dashboard

Setup, Build & Deployment

Prerequisites: Java 17 + Maven (wrapper included), MySQL 8.0.40 on localhost:3306, and Node.js 22.11 LTS with npm 10.9.

run locally
# 1 · seed the database
mysql -u root -p < backend/db/yuplanner.sql

# 2 · backend → http://localhost:8080
cd backend/yuplanner && mvn spring-boot:run

# 3 · frontend → http://localhost:3000
cd frontend/yuplanner && npm install && npm start
ComponentPortURL
React SPA (CRA dev server)3000http://localhost:3000
Spring Boot REST API8080http://localhost:8080
MySQL3306jdbc:mysql://localhost:3306/yuplanner

Known Limitations & Future Work

Characteristic of an educational, sprint-scoped project — not broken features, but the items that matter most for security, correctness and maintainability.

Security

  • Passwords stored / compared in plaintext → hash with BCrypt
  • No Spring Security, tokens or sessions → add JWT auth + login filter
  • No role-based access control → restrict actions by role
  • Permissive CORS & login returns the full record → tighten + return a DTO

Data Model

  • String-based associations → model as proper JPA relations
  • Column-name divergence (studentid/employeeid vs userid)
  • ddl-auto=update everywhere → versioned migrations (Flyway)

API Surface

  • Commented-out course /find, update, delete handlers → implement or remove
  • String responses ("New course is added") → typed JSON + status codes
  • Inconsistent error handling → standardise across controllers

Tooling

  • No containerisation → docker-compose for DB + backend + frontend
  • No CI/CD → GitHub Actions build/test on PRs
  • Lowercase class names → adopt Java PascalCase