Pull#14
Merged
Merged
Conversation
PDF Upload with Free OCR
feat: implement admin dashboard with student metrics and management r…
Added a features section to the README.
Updated project description and features section.
…dmin upload interface
fixed upload issues
❌ Deploy Preview for gpacal-cis failed.
|
There was a problem hiding this comment.
Pull request overview
This PR introduces major new functionality for a GPA Calculator Portal: student/admin dashboards, admin-side result ingestion, and schema support for GPA vs non-GPA subjects, along with deployment/docs updates.
Changes:
- Added student GPA dashboard UI + API (
/api/student/gpa) with recommendations and semester/year breakdowns. - Added admin dashboard + student browsing/detail views, plus batch-oriented result saving and subject creation updates.
- Introduced
subjects.isGpato exclude non-GPA subjects from GPA calculations, and added a Markdown-based upload flow for results.
Reviewed changes
Copilot reviewed 34 out of 34 changed files in this pull request and generated 12 comments.
Show a summary per file
| File | Description |
|---|---|
| README.md | New project documentation and setup/deploy guidance. |
| netlify.toml | Netlify build configuration for Next.js. |
| lib/studentAuth.ts | Shared helper to authenticate student requests via JWT cookie. |
| lib/adminAuth.ts | Shared helper to authenticate admin requests via JWT cookie. |
| lib/schema.ts | Adds subjects.isGpa column with default true. |
| lib/parsePDF.ts | More robust OCR text parsing and normalization logic. |
| lib/parseMD.ts | New Markdown/text results parser for admin uploads. |
| lib/hooks/usePDFOCR.ts | Removes client-side PDF OCR hook implementation. |
| components/ui/accordion.tsx | Adds accordion UI component for result grouping displays. |
| components/admin/AdminSidebar.tsx | Adds admin navigation sidebar (mobile + desktop). |
| app/student/login/page.tsx | Redirects first-time students to change-password flow. |
| app/student/dashboard/page.tsx | New student dashboard with GPA, class tier, recommendations, and results table. |
| app/student/dashboard/loading.tsx | Loading skeleton for student dashboard route. |
| app/student/change-password/page.tsx | Enhanced password change UX + first-login flow handling. |
| app/not-found.tsx | Custom 404 page. |
| app/error.tsx | App-level error boundary UI. |
| app/api/student/gpa/route.ts | Student GPA API: aggregates results and computes GPAs/FGPA/classification. |
| app/api/admin/subjects/route.ts | Admin subjects API now supports isGpa. |
| app/api/admin/students/route.ts | Admin list endpoint for computed FGPA/class/pass status with filtering/pagination. |
| app/api/admin/students/[index]/route.ts | Admin student detail endpoint with year/semester drilldown. |
| app/api/admin/results/save/route.ts | Batch-oriented save/upsert of results + student creation; supports non-GPA subjects. |
| app/api/admin/results/analyze/route.ts | Server-side PDF analysis path (Gemini/pdf-parse + parseOCRText fallback). |
| app/api/admin/dashboard/stats/route.ts | Admin dashboard stats endpoint (totals, recent uploads, at-risk count). |
| app/admin/upload/StepIndicator.tsx | Updates wizard step labeling for markdown upload flow. |
| app/admin/upload/Step1SubjectDetails.tsx | Adds GPA vs non-GPA subject type selection. |
| app/admin/upload/Step2UploadOCR.tsx | Reworked step 2 into Markdown/text upload + parsing UX. |
| app/admin/upload/Step3ReviewEdit.tsx | Displays non-GPA status and adjusts grade point display accordingly. |
| app/admin/upload/page.tsx | Wires new upload flow (MD parsing → review/edit → save). |
| app/admin/students/page.tsx | New admin students browsing UI with filtering and pagination. |
| app/admin/students/loading.tsx | Loading skeleton for admin students route. |
| app/admin/students/[index]/page.tsx | New admin student detail UI with FGPA breakdown and record accordion. |
| app/admin/dashboard/page.tsx | New admin dashboard UI (stats + recent uploads). |
| app/admin/dashboard/loading.tsx | Loading skeleton for admin dashboard route. |
| .env.example | Expanded environment variable template (DB/JWT/AI keys). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+69
to
+74
| // Split by pipe, remove first/last empty elements | ||
| const cells = trimmed | ||
| .split("|") | ||
| .map((c) => c.trim()) | ||
| .filter((_, i, arr) => i > 0 && i < arr.length); // skip first/last empty from leading/trailing | | ||
|
|
Comment on lines
+104
to
+106
| <Icon | ||
| className={`h-4.5 w-4.5 flex-shrink-0 transition-colors ${ | ||
| active |
Comment on lines
+73
to
+77
| The database seeder initializes: | ||
| - **Default Admin Account**: | ||
| - **Username**: `admin` | ||
| - **Password**: `admin123` | ||
| - **Academic Semesters**: Years 1-4, Semesters 1-2. |
Comment on lines
136
to
+142
| const res = await fetch("/api/auth/student/change-password", { | ||
| method: "POST", | ||
| headers: { "Content-Type": "application/json" }, | ||
| body: JSON.stringify({ currentPassword, newPassword }), | ||
| body: JSON.stringify({ | ||
| currentPassword: isFirstLogin ? "123456789" : currentPassword, | ||
| newPassword, | ||
| }), |
Comment on lines
+45
to
+51
| const allResults = await db | ||
| .select({ | ||
| studentIndex: results.studentIndex, | ||
| gradePoint: results.gradePoint, | ||
| creditPoints: subjects.creditPoints, | ||
| yearNumber: semesters.yearNumber, | ||
| }) |
Comment on lines
+36
to
+46
| // ── 1. Fetch matching students (with pagination) ──────────────────── | ||
| // First get total count for pagination | ||
| const countQuery = search | ||
| ? db | ||
| .select({ value: count() }) | ||
| .from(students) | ||
| .where(ilike(students.indexNumber, `%${search}%`)) | ||
| : db.select({ value: count() }).from(students); | ||
|
|
||
| const [totalRow] = await countQuery; | ||
|
|
Comment on lines
+62
to
+70
| const allResults = await db | ||
| .select({ | ||
| studentIndex: results.studentIndex, | ||
| grade: results.grade, | ||
| gradePoint: results.gradePoint, | ||
| creditPoints: subjects.creditPoints, | ||
| yearNumber: semesters.yearNumber, | ||
| semesterId: subjects.semesterId, | ||
| }) |
Comment on lines
+97
to
+103
| if (!studentData.byYear.has(row.yearNumber)) { | ||
| studentData.byYear.set(row.yearNumber, []); | ||
| } | ||
| studentData.byYear.get(row.yearNumber)!.push({ | ||
| gp: Number(row.gradePoint), | ||
| cp: row.creditPoints, | ||
| }); |
Comment on lines
+245
to
+247
| <th className="px-5 py-3 text-left text-xs font-medium text-slate-400 uppercase tracking-wider"> | ||
| Subjects Done | ||
| </th> |
Comment on lines
+1
to
+14
| import { NextRequest, NextResponse } from "next/server"; | ||
| import { verifyToken } from "@/lib/auth"; | ||
| import { parseOCRText } from "@/lib/parsePDF"; | ||
| import pdf from "pdf-parse"; | ||
|
|
||
| // ─── Helper: Verify Admin JWT ─────────────────────────────────────────────── | ||
|
|
||
| function getAdminFromRequest(request: NextRequest) { | ||
| const token = request.cookies.get("auth-token")?.value; | ||
| if (!token) return null; | ||
| const payload = verifyToken(token); | ||
| if (!payload || payload.role !== "admin") return null; | ||
| return payload; | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.