Skip to content

Repository files navigation

SemesterHub

Next.js TypeScript PostgreSQL Prisma

SemesterHub Dashboard

Your semester, at a glance

Live Demo β€’ Features β€’ Architecture β€’ Getting Started


🎯 The Story Behind the Code

The Problem: Every student knows the frustration. Moodle shows assignments scattered across multiple pages. No clear overview of deadlines. Progress tracking is non-existent. You click through 10 pages just to see what's due this week.

The Question: What if your semester data was actually... organized?

This project started from personal pain. As a student at JCT, I spent more time navigating Moodle than actually studying. So I built the tool I wished existed: a clean dashboard that shows everything at a glance.

The Challenge

"Build a system that talks to a university platform you don't control, syncs data reliably, and presents it beautifully."

This required solving real engineering problems:

  • Browser Extension Development – Manifest v3, content scripts, service workers
  • Authentication Complexity – Google OAuth for the webapp + JWT tokens for the extension
  • Data Synchronization – Reliable sync between Moodle β†’ Extension β†’ Backend β†’ Dashboard
  • Real-time Updates – Keep data fresh without overwhelming the server

✨ Features

πŸ“‹ Assignment Tracking

See all your assignments in one place. Switch between Kanban and List views. Drag-and-drop to update status.

πŸ“… Calendar View

Day, week, and month views of your deadlines. Never miss a submission again.

πŸ“Š Progress Tracking

Visual progress bars for each course. See exactly how much you've completed.

πŸ”„ Real-time Sync

Browser extension syncs data automatically whenever you visit Moodle.

πŸ” Secure Authentication

Google OAuth + JWT tokens. Your data stays yours.

🌐 Hebrew RTL Support

Full right-to-left support. Built for Israeli students.


πŸ—οΈ Architecture

System Overview

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                      Student's Browser                           β”‚
β”‚                                                                  β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”              β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚
β”‚  β”‚   Moodle Tab    β”‚              β”‚  SemesterHub Dashboard  β”‚   β”‚
β”‚  β”‚   (ac.il)       β”‚              β”‚  (semester-dash.vercel) β”‚   β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜              β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚
β”‚           β”‚                                    β”‚                 β”‚
β”‚           β”‚         β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”       β”‚                 β”‚
β”‚           └────────►│    Extension     β”‚β—„β”€β”€β”€β”€β”€β”€β”˜                 β”‚
β”‚                     β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚                         β”‚
β”‚                     β”‚  β”‚  Content   β”‚  β”‚                         β”‚
β”‚                     β”‚  β”‚  Scripts   β”‚  β”‚                         β”‚
β”‚                     β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚                         β”‚
β”‚                     β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚                         β”‚
β”‚                     β”‚  β”‚  Service   β”‚  β”‚                         β”‚
β”‚                     β”‚  β”‚  Worker    β”‚  β”‚                         β”‚
β”‚                     β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚                         β”‚
β”‚                     β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                         β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                               β”‚
                               β”‚ HTTPS + JWT
                               β–Ό
                β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                β”‚     Next.js API (Vercel)     β”‚
                β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
                β”‚  β”‚   /api/extension/*     β”‚  β”‚  Token generation
                β”‚  β”‚   /api/sync/moodle     β”‚  β”‚  Data sync endpoint
                β”‚  β”‚   /api/courses/*       β”‚  β”‚  CRUD operations
                β”‚  β”‚   /api/assignments/*   β”‚  β”‚  CRUD operations
                β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
                β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
                β”‚  β”‚   NextAuth.js v5       β”‚  β”‚  Google OAuth
                β”‚  β”‚   Rate Limiter         β”‚  β”‚  10 req/min
                β”‚  β”‚   CORS Whitelist       β”‚  β”‚  Security
                β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
                β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                               β”‚
                               β”‚ Prisma ORM
                               β–Ό
                β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                β”‚    PostgreSQL (Neon Cloud)   β”‚
                β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
                β”‚  β”‚  Users                 β”‚  β”‚
                β”‚  β”‚  Semesters             β”‚  β”‚
                β”‚  β”‚  Courses               β”‚  β”‚
                β”‚  β”‚  Assignments           β”‚  β”‚
                β”‚  β”‚  UserPreferences       β”‚  β”‚
                β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
                β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Data Flow: How Sync Works

1. Student visits Moodle
           β”‚
           β–Ό
2. Extension detects Moodle page
           β”‚
           β–Ό
3. Content script scrapes course/assignment data
           β”‚
           β–Ό
4. Extension checks for valid JWT token
           β”‚
     β”Œβ”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”
     β”‚           β”‚
   Valid      Invalid
     β”‚           β”‚
     β”‚           β–Ό
     β”‚    5a. Request new token
     β”‚        (using session cookie)
     β”‚           β”‚
     β”‚           β–Ό
     β”‚    5b. Store token in
     β”‚        chrome.storage.local
     β”‚           β”‚
     β””β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜
           β”‚
           β–Ό
6. POST /api/sync/moodle with Bearer token
           β”‚
           β–Ό
7. Server validates JWT + rate limit
           β”‚
           β–Ό
8. Upsert courses/assignments (by moodleId)
           β”‚
           β–Ό
9. Dashboard shows updated data

πŸ› οΈ Tech Stack

Web Application

Layer Technology Why
Framework Next.js 16 (App Router) Server components, API routes, great DX
Language TypeScript (strict) Type safety across the entire stack
Database PostgreSQL (Neon) Reliable, scalable, serverless
ORM Prisma 7 Type-safe queries, migrations, studio
Auth NextAuth.js v5 Google OAuth, session management
Styling Tailwind CSS 4 Utility-first, RTL support
UI Components Radix UI Accessible, unstyled primitives
Icons Lucide Clean, consistent iconography
Drag & Drop DnD Kit Accessible drag-and-drop

Browser Extension

Layer Technology Why
Manifest V3 Latest Chrome extension standard
Build Vite 6 Fast builds, HMR for development
Language TypeScript Same type safety as webapp
Auth JWT Bearer Tokens Secure, stateless authentication
Storage chrome.storage.local Persistent token storage

Infrastructure

Layer Technology Why
Hosting Vercel Zero-config Next.js deployment
Database Neon Serverless PostgreSQL
CI/CD GitHub Actions Automated lint + build
Security CORS + Rate Limiting Protection against abuse

πŸ“Š Database Schema

model User {
  id            String    @id @default(cuid())
  name          String?
  email         String    @unique
  image         String?
  semesters     Semester[]
  preferences   UserPreferences?
}

model Semester {
  id        String   @id @default(cuid())
  name      String   // "A", "B", "Summer"
  year      Int
  startDate DateTime
  endDate   DateTime
  courses   Course[]
  user      User     @relation(...)
}

model Course {
  id           String       @id @default(cuid())
  name         String
  courseCode   String?
  moodleId     String?      @unique
  moodleUrl    String?
  color        String?
  lastSyncedAt DateTime?
  assignments  Assignment[]
  semester     Semester     @relation(...)
}

model Assignment {
  id                     String   @id @default(cuid())
  title                  String
  description            String?
  dueDate                DateTime?
  type                   AssignmentType  // ASSIGNMENT, QUIZ, FORUM
  status                 AssignmentStatus // NOT_STARTED, IN_PROGRESS, COMPLETED
  moodleId               String?  @unique
  moodleSubmissionStatus String?
  course                 Course   @relation(...)
}

πŸ” Security Implementation

Authentication Flow

Component Method Details
Web App Google OAuth NextAuth.js handles the entire flow
Extension JWT Tokens 30-day expiry, stored in chrome.storage
API Bearer Token Validated on every request

Token Generation (Extension)

// Extension requests token using existing session
const response = await fetch('/api/extension/token', {
  credentials: 'include'  // Sends session cookie
});

const { token, expiresAt } = await response.json();

// Token payload
{
  userId: "cuid_xxx",
  email: "student@mail.com",
  name: "Student Name",
  aud: "extension",
  iss: "semesterhub",
  exp: 1234567890  // 30 days
}

API Protection

// Rate limiting: 10 requests per minute per user
const rateLimiter = new RateLimiter({
  interval: 60 * 1000,
  uniqueTokenPerInterval: 500
});

// CORS whitelist
const allowedOrigins = [
  'chrome-extension://*',
  'moz-extension://*',
  'https://semester-dash.vercel.app'
];

πŸ“ Project Structure

semester-dash/
β”‚
β”œβ”€β”€ src/
β”‚   β”œβ”€β”€ app/                      # Next.js App Router
β”‚   β”‚   β”œβ”€β”€ api/
β”‚   β”‚   β”‚   β”œβ”€β”€ extension/        # Token endpoints
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ token/        # GET - Generate JWT
β”‚   β”‚   β”‚   β”‚   └── verify/       # GET - Validate JWT
β”‚   β”‚   β”‚   β”œβ”€β”€ sync/
β”‚   β”‚   β”‚   β”‚   └── moodle/       # POST - Sync data
β”‚   β”‚   β”‚   β”œβ”€β”€ courses/          # CRUD
β”‚   β”‚   β”‚   β”œβ”€β”€ assignments/      # CRUD
β”‚   β”‚   β”‚   └── semesters/        # CRUD
β”‚   β”‚   β”‚
β”‚   β”‚   β”œβ”€β”€ dashboard/            # Protected pages
β”‚   β”‚   β”‚   β”œβ”€β”€ page.tsx          # Main dashboard
β”‚   β”‚   β”‚   β”œβ”€β”€ assignments/      # Assignment views
β”‚   β”‚   β”‚   β”œβ”€β”€ calendar/         # Calendar view
β”‚   β”‚   β”‚   β”œβ”€β”€ courses/          # Course management
β”‚   β”‚   β”‚   └── semesters/        # Semester management
β”‚   β”‚   β”‚
β”‚   β”‚   β”œβ”€β”€ login/                # Auth pages
β”‚   β”‚   └── onboarding/           # New user flow
β”‚   β”‚
β”‚   β”œβ”€β”€ components/
β”‚   β”‚   β”œβ”€β”€ ui/                   # Base components
β”‚   β”‚   β”œβ”€β”€ assignments/          # Assignment components
β”‚   β”‚   β”œβ”€β”€ courses/              # Course components
β”‚   β”‚   └── layout/               # Navigation, sidebar
β”‚   β”‚
β”‚   β”œβ”€β”€ lib/
β”‚   β”‚   β”œβ”€β”€ auth.ts               # NextAuth config
β”‚   β”‚   β”œβ”€β”€ prisma.ts             # Prisma client
β”‚   β”‚   └── rate-limit.ts         # Rate limiter
β”‚   β”‚
β”‚   └── types/                    # TypeScript types
β”‚
β”œβ”€β”€ extension/                    # Browser Extension
β”‚   β”œβ”€β”€ src/
β”‚   β”‚   β”œβ”€β”€ background/           # Service worker
β”‚   β”‚   β”‚   └── index.ts
β”‚   β”‚   β”œβ”€β”€ content/              # Content scripts
β”‚   β”‚   β”‚   β”œβ”€β”€ moodle.ts         # Moodle scraper
β”‚   β”‚   β”‚   └── webapp.ts         # Dashboard integration
β”‚   β”‚   β”œβ”€β”€ popup/                # Extension popup
β”‚   β”‚   └── shared/               # Shared utilities
β”‚   β”‚       β”œβ”€β”€ api-client.ts     # API wrapper
β”‚   β”‚       └── token-manager.ts  # JWT handling
β”‚   β”‚
β”‚   β”œβ”€β”€ manifest.json             # Extension manifest
β”‚   └── vite.config.ts            # Build config
β”‚
β”œβ”€β”€ prisma/
β”‚   └── schema.prisma             # Database schema
β”‚
└── package.json

πŸš€ Getting Started

Prerequisites

  • Node.js 18+
  • PostgreSQL database (or Neon account)
  • Google Cloud Console project (for OAuth)

1. Clone & Install

git clone https://github.com/Noammandelbaum/semester-dash.git
cd semester-dash
npm install

2. Environment Setup

cp .env.example .env.local
# Database
DATABASE_URL="postgresql://user:pass@host:5432/db"

# NextAuth
NEXTAUTH_URL="http://localhost:3000"
NEXTAUTH_SECRET="your-secret-here"

# Google OAuth
GOOGLE_CLIENT_ID="xxx.apps.googleusercontent.com"
GOOGLE_CLIENT_SECRET="xxx"

# JWT
JWT_SECRET="your-jwt-secret"

3. Database Setup

npx prisma generate
npx prisma db push

4. Run Development Server

npm run dev
# Open http://localhost:3000

5. Build Extension

cd extension
npm install
npm run build

Then load extension/dist/ as unpacked extension in Chrome.


πŸ€– Built with AI

This entire project was developed using Claude Code - an AI-assisted development workflow.

From database schema design to browser extension architecture, every component was built collaboratively with AI agents. This approach enabled:

  • Rapid prototyping – Ideas to working code in hours, not days
  • Consistent code quality – AI maintains patterns across the codebase
  • Complex problem solving – JWT auth, content scripts, real-time sync
  • Learning acceleration – Understanding new technologies faster

The future of development isn't AI replacing developers – it's developers and AI building together.


πŸ“š What I Learned

Technical Skills

  • Browser Extension Development – Manifest v3, content scripts, service workers, chrome.storage API
  • Authentication Complexity – OAuth flows, JWT tokens, secure token storage
  • Full-Stack TypeScript – End-to-end type safety from database to UI
  • Real-time Sync – Designing reliable data synchronization between systems

Software Engineering

  • API Design – RESTful endpoints with proper error handling and rate limiting
  • Security First – CORS, token validation, input sanitization
  • Database Design – Normalized schemas, efficient queries, proper indexing
  • Developer Experience – Clear project structure, comprehensive types

Key Insight

"The best tools are the ones that solve your own problems first."

I built SemesterHub because I needed it. That personal investment drove every feature decision and kept the scope focused on what actually matters to students.


πŸ“¬ Contact

Email LinkedIn GitHub


Built with precision, synced with reliability, designed for students.

Turning Moodle chaos into academic clarity

About

Student dashboard platform with browser extension for Moodle data sync. Built with Next.js, PostgreSQL, Prisma, and TypeScript.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages