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.
"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
|
See all your assignments in one place. Switch between Kanban and List views. Drag-and-drop to update status. |
Day, week, and month views of your deadlines. Never miss a submission again. |
|
Visual progress bars for each course. See exactly how much you've completed. |
Browser extension syncs data automatically whenever you visit Moodle. |
|
Google OAuth + JWT tokens. Your data stays yours. |
Full right-to-left support. Built for Israeli students. |
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β 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 β β
β ββββββββββββββββββββββββββ β
ββββββββββββββββββββββββββββββββ
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
| 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 |
| 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 |
| 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 |
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(...)
}| 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 |
// 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
}// 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'
];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
- Node.js 18+
- PostgreSQL database (or Neon account)
- Google Cloud Console project (for OAuth)
git clone https://github.com/Noammandelbaum/semester-dash.git
cd semester-dash
npm installcp .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"npx prisma generate
npx prisma db pushnpm run dev
# Open http://localhost:3000cd extension
npm install
npm run buildThen load extension/dist/ as unpacked extension in Chrome.
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.
- 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
- 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
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.
Built with precision, synced with reliability, designed for students.
Turning Moodle chaos into academic clarity
