A full-stack degree planning app for CUNY students. DegreeMap helps students explore CUNY campuses, build a visual semester-by-semester roadmap, track progress toward graduation, share plans, and lets advisors review their students' roadmaps.
- Interactive Leaflet map of all 25 CUNY campuses
- Search and filter by name, borough, and campus type (Senior / Community / Graduate)
- Per-campus detail page with programs, stats, and student reviews
- Rate campuses (1β5) with written reviews and category breakdowns
- Reviews stored in Supabase and visible to everyone
- Visual drag-and-drop canvas with four node types: Course, Milestone, Elective, Career Goal
- Per-node editing: label, credits, structured term + year, status (Planned / In Progress / Complete), notes
- Auto-save with a saved/unsaved indicator, undo/redo (Ctrl/Cmd+Z, Ctrl/Cmd+Shift+Z)
- Credit-total validation against the 120-credit degree target
- Start from prebuilt templates (CS @ Hunter, Nursing @ Hunter, BBA @ Baruch)
- Live degree-progress bar and credit breakdown (Recharts pie + chronological per-semester bars)
- Milestones derived from real earned credits (30/60/90/120 tiers)
- Expected graduation and major pulled from your profile
- Stat cards (credits earned, GPA, roadmap count, next milestone) computed from real data
- Editable profile: name, home campus, major, and GPA
- Roadmap list with progress bars + quick links
- Role-gated student roster backed by the database (search, status, credits, GPA)
- Per-student detail view with notes/comments
- Advisor accounts get an Advisor badge and nav link
- Generate read-only share links; no auth required to view
- Class-based dark mode (Tailwind v4
@custom-variant), persisted to localStorage - Mobile-first responsive layouts
- Supabase Auth (JWT); protected routes; graceful demo-mode fallback when offline
Frontend: React 19 + TypeScript, Vite 5, Tailwind CSS 4, React Router 6, React Flow 11, React Leaflet, Recharts, lucide-react, react-hot-toast
Backend: Express 4 + TypeScript (ES modules, ts-node), Supabase JS
Database: Supabase PostgreSQL with Row Level Security
degreemap-cuny/
βββ client/ # React + Vite frontend
β βββ src/
β β βββ components/ # Navbar, CampusMap, roadmap/, ui/, ...
β β βββ pages/ # Landing, Explore, SchoolDetail, Dashboard,
β β β # RoadmapBuilder, Journey, Advisor, SharedRoadmap, ...
β β βββ services/ # apiClient, roadmapService, advisorService, supabase
β β βββ store/ # AuthContext, DarkModeContext
β β βββ data/ # cunyCampuses, roadmapTemplates, semester, constants
β β βββ App.tsx
β βββ vercel.json # SPA rewrites for Vercel
β βββ package.json
β
βββ server/ # Express backend
β βββ src/
β β βββ routes/ # auth, schools(reviews), users, roadmaps, advisor
β β βββ controllers/ # request handlers
β β βββ services/ # Supabase data access
β β βββ middleware/ # JWT auth
β β βββ index.ts
β βββ DATABASE_SETUP.sql # canonical fresh-install schema
β βββ MIGRATION_2026-05-30.sql # additive: roadmaps cols, role, comments
β βββ MIGRATION_2026-06-01.sql # additive: profiles.gpa
β βββ package.json
βββ README.md
- Node.js 18+ and npm
- A Supabase project (free tier is fine)
cd client && npm install
cd ../server && npm installclient/.env:
VITE_SUPABASE_URL=https://your-project.supabase.co
VITE_SUPABASE_ANON_KEY=your-anon-key
VITE_API_URL=http://localhost:5000
server/.env:
PORT=5000
SUPABASE_URL=https://your-project.supabase.co
SUPABASE_SERVICE_ROLE_KEY=your-service-role-key
FRONTEND_URL=http://localhost:5173
SUPABASE_SERVICE_ROLE_KEYis secret β keep it server-side only. The anon key is safe to expose in the client.
In the Supabase SQL editor:
- Run
server/DATABASE_SETUP.sqlfor a fresh project (it includesprofiles,roadmaps,roadmap_comments,journey_milestones,campus_reviews,profiles.gpa,profiles.role, and RLS policies).- For an existing project, run the additive migrations instead:
MIGRATION_2026-05-30.sqlthenMIGRATION_2026-06-01.sql.
- For an existing project, run the additive migrations instead:
- Add the new-user trigger so each signup automatically gets a
profilesrow:create or replace function public.handle_new_user() returns trigger language plpgsql security definer set search_path = public as $$ begin insert into public.profiles (id, name, school, major) values (new.id, new.raw_user_meta_data->>'name', new.raw_user_meta_data->>'school', new.raw_user_meta_data->>'major') on conflict (id) do nothing; return new; end; $$; drop trigger if exists on_auth_user_created on auth.users; create trigger on_auth_user_created after insert on auth.users for each row execute function public.handle_new_user();
Campus catalog data lives in the frontend (
client/src/data/cunyCampuses.ts) β there is noschoolstable.
# terminal 1
cd server && npm run dev # http://localhost:5000
# terminal 2
cd client && npm run dev # http://localhost:5173New accounts default to role = 'student'. To promote one (after it has registered):
UPDATE profiles
SET role = 'advisor'
WHERE id = (SELECT id FROM auth.users WHERE email = 'advisor@example.com');Once promoted, that account sees an Advisor badge + nav link and can open /advisor.
Auth: POST /api/auth/register, POST /api/auth/login
Users: GET /api/users/me, PUT /api/users/me (name, school, major, graduation_year, gpa)
Roadmaps (auth): POST /api/roadmaps, GET /api/roadmaps/mine, GET /api/roadmaps/detail/:id, PUT /api/roadmaps/:id, DELETE /api/roadmaps/:id Β· public: GET /api/roadmaps/templates, GET /api/roadmaps/public/:shareId
Reviews: POST / GET /api/schools/:schoolId/reviews, DELETE /api/schools/:schoolId/reviews/:reviewId
Advisor (role-gated): GET /api/advisor/students, GET /api/advisor/students/:studentId/roadmaps, GET/POST /api/advisor/roadmaps/:roadmapId/comments
DegreeMap is two apps: a static Vite frontend and a long-running Express backend. Vercel hosts the frontend; deploy the Express server to a Node host (e.g. Render or Railway).
- New Web Service from this repo, root directory
server. - Build:
npm install && npm run buildΒ· Start:npm start. - Env vars:
SUPABASE_URL,SUPABASE_SERVICE_ROLE_KEY,FRONTEND_URL(your Vercel URL, for CORS).PORTis provided by the host and read automatically. - Note the public URL (e.g.
https://degreemap-api.onrender.com).
- Import the repo; set Root Directory to
client. - Framework preset: Vite Β· Build:
npm run buildΒ· Output:dist. - Environment variables:
VITE_SUPABASE_URLVITE_SUPABASE_ANON_KEYVITE_API_URL= your backend URL from above (notlocalhost)
- SPA routing is handled by
client/vercel.json(rewrites all paths toindex.html).
- Apply
DATABASE_SETUP.sql(or the migrations) and thehandle_new_usertrigger to the Supabase project the deployment uses. - Set the backend's
FRONTEND_URLto the deployed Vercel domain so CORS allows it. - Without a deployed backend the app still runs (Supabase auth + localStorage roadmaps), but server persistence, reviews, and the advisor view require it.
column ... does not existerrors from the API β your Supabase project is behind on migrations; runDATABASE_SETUP.sqlor the migration files.- "Couldn't load your roadmaps" β the backend isn't running /
VITE_API_URLis wrong. - Advisor view shows an access error β the account isn't
role = 'advisor', or it's a demo-mode session (no auth token). - No profile row after signup β add the
handle_new_usertrigger above.
- Server-side input validation (zod) and rate limiting
- Automated test suite
- Export roadmap as PDF
- Email notifications
MIT License β see LICENSE file.
Last Updated: June 2026