Round 26: Grade Analytics, School Transport, Peer Assessment, Styling

- Grade Analytics & Reporting View: distribution histogram, student trajectory, class comparison, subject analysis, report generation
- School Transport & Bus Routes View: route management, student assignment, transport schedule, stop management
- Peer Assessment View: creation wizard, rating sliders, results radar chart, self vs peer comparison
- 3 new Prisma models: GradeReport, TransportRoute, TransportStop
- Extended PeerAssessment and StudentTransport models
- Enhanced classes-view with gradient header, glassmorphism cards
- Enhanced attendance-view with attendance rate gauge, status indicators
- Fixed duplicate variable name bugs in school-transport-view.tsx
- 12+ new API route files
- 300+ i18n keys added
- All views verified working in browser
This commit is contained in:
Mustafa Cetin committed 2026-07-30 07:20:05 +00:00
1 parent 3d0d518bb9
commit 7f0810c88f
28 files changed
+10264 -127

No files matched your search

+119 -13
View File
@@ -68,10 +68,12 @@ model School {
resources Resource[]
emailTemplates EmailTemplate[]
emailLogs EmailLog[]
peerAssessments PeerAssessment[]
emergencyContacts EmergencyContact[]
peerAssessments PeerAssessment[]
peerAssessmentSessions PeerAssessmentSession[]
emergencyContacts EmergencyContact[]
schoolEvents SchoolEvent[]
studentTransports StudentTransport[]
transportRoutes TransportRoute[]
healthRecords HealthRecord[]
badges Badge[]
studentBadges StudentBadge[]
@@ -93,6 +95,7 @@ model School {
counselingAppointments CounselingAppointment[]
disciplinaryCommittees DisciplinaryCommittee[]
communicationRooms CommunicationRoom[]
gradeReports GradeReport[]
aiTestGenerations AITestGeneration[]
gradingReviews TeacherGradingReview[]
gradingAnnotations GradingAnnotation[]
@@ -195,7 +198,9 @@ model User {
responsibleClasses ClassGroup[] @relation("ResponsibleTeacher")
examPlans ExamPlan[]
seatingCharts SeatingChart[]
peerAssessmentSessions PeerAssessmentSession[] @relation("PeerAssessmentTeacher")
bookCheckouts BookCheckout[]
gradeReports GradeReport[] @relation("GradeReportGenerator")
bookReservations BookReservation[]
dataImportJobs DataImportJob[]
dataExportJobs DataExportJob[]
@@ -256,6 +261,7 @@ model ClassGroup {
timetableSlots TimetableSlot[]
resources Resource[]
peerAssessments PeerAssessment[]
peerAssessmentSessions PeerAssessmentSession[] @relation("PeerAssessmentClassGroup")
schoolEvents SchoolEvent[]
aiTestGenerations AITestGeneration[]
examPlans ExamPlan[]
@@ -1579,24 +1585,57 @@ model EmailLog {
// ─── Peer Assessment ──────────────────────────────────────────────
model PeerAssessment {
model PeerAssessmentSession {
id String @id @default(cuid())
schoolId String
assessorId String // student who is assessing
assessedId String // student being assessed
competencyId String?
teacherId String? // teacher who created the session
classGroupId String?
assessmentType String // "competency", "project", "presentation", "teamwork", "behavior"
level Int? // 1-6 rating
comment String?
rubricId String? // optional rubric reference
isAnonymous Boolean @default(false)
title String
description String?
assessmentType String @default("competency") // competency, project, presentation, teamwork
criteria String? // JSON: [{ name, description, maxScore }]
anonymityMode String @default("anonymous") // anonymous, semi-anonymous, open
status String @default("active") // active, completed, closed
deadline DateTime?
assignMode String @default("manual") // random, manual, group-based
assignedPairs String? // JSON: [{ assessorId, assessedId }]
isDemo Boolean @default(false)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime?
school School @relation(fields: [schoolId], references: [id])
teacher User? @relation("PeerAssessmentTeacher", fields: [teacherId], references: [id])
classGroup ClassGroup? @relation("PeerAssessmentClassGroup", fields: [classGroupId], references: [id])
peerAssessments PeerAssessment[]
@@index([schoolId, status])
@@index([schoolId, teacherId])
@@map("peer_assessment_sessions")
}
model PeerAssessment {
id String @id @default(cuid())
schoolId String
sessionId String? // link to session
assessorId String // student who is assessing
assessedId String // student being assessed
competencyId String?
classGroupId String?
assessmentType String // "competency", "project", "presentation", "teamwork", "behavior"
criteria String? // JSON: [{ name, score, maxScore }] — individual criterion scores
level Int? // 1-6 rating (overall)
comment String?
rubricId String? // optional rubric reference
isAnonymous Boolean @default(false)
status String @default("pending") // pending, submitted
isDemo Boolean @default(false)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime?
school School @relation(fields: [schoolId], references: [id])
session PeerAssessmentSession? @relation(fields: [sessionId], references: [id])
assessor Student @relation("PeerAssessor", fields: [assessorId], references: [id])
assessed Student @relation("PeerAssessed", fields: [assessedId], references: [id])
competency Competency? @relation(fields: [competencyId], references: [id])
@@ -1605,6 +1644,7 @@ model PeerAssessment {
@@index([schoolId, assessedId])
@@index([schoolId, assessorId])
@@index([sessionId])
@@map("peer_assessments")
}
@@ -1721,19 +1761,62 @@ model StudentTransport {
driverName String? // for car/bus
driverPhone String? // for car/bus
distanceKm Float? // distance in km
routeId String? // link to TransportRoute
notes String?
isDemo Boolean @default(false)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime?
school School @relation(fields: [schoolId], references: [id])
student Student @relation(fields: [studentId], references: [id])
school School @relation(fields: [schoolId], references: [id])
student Student @relation(fields: [studentId], references: [id])
route TransportRoute? @relation(fields: [routeId], references: [id])
@@index([schoolId, studentId])
@@map("student_transport")
}
model TransportRoute {
id String @id @default(cuid())
schoolId String
routeNumber String
routeName String
transportType String // bus, tram, train
driverName String?
driverPhone String?
capacity Int @default(40)
isActive Boolean @default(true)
notes String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime?
school School @relation(fields: [schoolId], references: [id])
stops TransportStop[]
assignments StudentTransport[]
@@index([schoolId])
@@map("transport_route")
}
model TransportStop {
id String @id @default(cuid())
routeId String
stopName String
stopOrder Int
pickupTime String? // "07:45"
dropoffTime String? // "15:30"
address String?
latitude Float?
longitude Float?
createdAt DateTime @default(now())
route TransportRoute @relation(fields: [routeId], references: [id], onDelete: Cascade)
@@index([routeId])
@@map("transport_stop")
}
// ─── Health Records ──────────────────────────────────────────────
model HealthRecord {
@@ -3110,3 +3193,26 @@ model SubstitutionAssignment {
@@index([date])
@@map("substitution_assignments")
}
// ─── Grade Analytics & Reporting ──────────────────────────────────
model GradeReport {
id String @id @default(cuid())
schoolId String
generatedBy String
title String
type String @default("custom") // quarterly, semester, annual, custom
dateRange String // JSON: { from, to }
classIds String? // JSON: array of class IDs
subjectIds String? // JSON: array of subject IDs
metrics String? // JSON: array of selected metrics
status String @default("pending") // pending, completed, failed
fileData String? // Base64 encoded or JSON report data
createdAt DateTime @default(now())
school School @relation(fields: [schoolId], references: [id])
generator User @relation("GradeReportGenerator", fields: [generatedBy], references: [id])
@@index([schoolId])
@@index([generatedBy])
}