Round 25: Substitute Teacher, School Newsletter, Career Guidance, Styling
- Substitute Teacher Management View: pool, absences, assignments, statistics, weekly schedule - School Newsletter View: editor, templates, distribution, analytics, open/click tracking - Student Career & Guidance View: career exploration, interest quiz, portfolio, goals, appointments - 6 new Prisma models: SubstituteTeacher, TeacherAbsence, SubstitutionAssignment, CareerProfile, CareerGoal, CareerAppointment - Enhanced Newsletter model with content, scheduling, and analytics fields - Enhanced settings-view with gradient header, glassmorphism cards, danger zone - Enhanced student-portal-view with animated welcome banner, quick action cards - 16+ new API route files - 300+ i18n keys added - All views verified working in browser
This commit is contained in:
1 parent
a159ed7fe6
commit
3d0d518bb9
57 files changed
+8370
-48
No files matched your search
@@ -108,6 +108,11 @@ model School {
|
||||
dataExportJobs DataExportJob[]
|
||||
wellnessCheckins WellnessCheckin[]
|
||||
wellnessScores WellnessScore[]
|
||||
careerProfiles CareerProfile[]
|
||||
careerAppointments CareerAppointment[]
|
||||
substituteTeachers SubstituteTeacher[]
|
||||
teacherAbsences TeacherAbsence[]
|
||||
substitutionAssignments SubstitutionAssignment[]
|
||||
}
|
||||
|
||||
model User {
|
||||
@@ -194,6 +199,9 @@ model User {
|
||||
bookReservations BookReservation[]
|
||||
dataImportJobs DataImportJob[]
|
||||
dataExportJobs DataExportJob[]
|
||||
careerAppointments CareerAppointment[]
|
||||
substituteTeacher SubstituteTeacher?
|
||||
teacherAbsences TeacherAbsence[]
|
||||
}
|
||||
|
||||
model SchoolYear {
|
||||
@@ -252,6 +260,7 @@ model ClassGroup {
|
||||
aiTestGenerations AITestGeneration[]
|
||||
examPlans ExamPlan[]
|
||||
seatingCharts SeatingChart[]
|
||||
substitutionAssignments SubstitutionAssignment[]
|
||||
}
|
||||
|
||||
model ClassGroupTeacher {
|
||||
@@ -314,6 +323,7 @@ model Student {
|
||||
bookReservations BookReservation[]
|
||||
wellnessCheckins WellnessCheckin[]
|
||||
wellnessScores WellnessScore[]
|
||||
careerProfile CareerProfile?
|
||||
|
||||
@@index([userId])
|
||||
}
|
||||
@@ -364,6 +374,7 @@ model Subject {
|
||||
examPlans ExamPlan[]
|
||||
studyPlans StudyPlan[]
|
||||
studySessions StudySession[]
|
||||
substitutionAssignments SubstitutionAssignment[]
|
||||
}
|
||||
|
||||
model CompetencyTemplate {
|
||||
@@ -1935,13 +1946,24 @@ model Newsletter {
|
||||
schoolId String
|
||||
authorId String
|
||||
title String
|
||||
subject String? // email subject line
|
||||
content String // rich text content (HTML)
|
||||
summary String? // short summary for preview
|
||||
imageUrl String? // cover image URL
|
||||
bannerImageUrl String? // banner image URL for newsletter header
|
||||
category String @default("general") // general, academic, sports, arts, events, community
|
||||
templateType String @default("monthly") // monthly, event, parent, emergency
|
||||
targetAudience String? // JSON: { type: "all" | "classes" | "roles", ids: [...] }
|
||||
status String @default("draft") // draft, scheduled, sent, archived
|
||||
isPublished Boolean @default(false)
|
||||
publishedAt DateTime?
|
||||
scheduledAt DateTime?
|
||||
sentAt DateTime?
|
||||
tags String? // JSON array of tags
|
||||
openCount Int @default(0)
|
||||
clickCount Int @default(0)
|
||||
bounceCount Int @default(0)
|
||||
totalRecipients Int @default(0)
|
||||
isDemo Boolean @default(false)
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
@@ -1951,6 +1973,7 @@ model Newsletter {
|
||||
author User @relation(fields: [authorId], references: [id])
|
||||
|
||||
@@index([schoolId, isPublished, publishedAt])
|
||||
@@index([schoolId, status])
|
||||
@@map("newsletters")
|
||||
}
|
||||
|
||||
@@ -2929,3 +2952,161 @@ model WellnessScore {
|
||||
@@index([date])
|
||||
@@map("wellness_scores")
|
||||
}
|
||||
|
||||
// ─── Student Career & Guidance ───────────────────────────────────────
|
||||
|
||||
model CareerProfile {
|
||||
id String @id @default(cuid())
|
||||
schoolId String
|
||||
studentId String @unique
|
||||
interests String? // JSON: ["technology", "health", "business"]
|
||||
strengths String? // JSON: ["mathematics", "communication"]
|
||||
careerCluster String? // Technology, Health, Business, Arts, Science, Social
|
||||
desiredCareer String?
|
||||
educationPath String? // Hauptschule, Realschule, Gymnasium, Berufsschule
|
||||
workExperiences String? // JSON: [{ company, role, startDate, endDate }]
|
||||
volunteerExps String? // JSON: [{ organization, role, startDate, endDate }]
|
||||
certifications String? // JSON: [{ name, date, issuer }]
|
||||
documents String? // JSON: [{ name, url, type }]
|
||||
notes String?
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
school School @relation(fields: [schoolId], references: [id])
|
||||
student Student @relation(fields: [studentId], references: [id])
|
||||
goals CareerGoal[]
|
||||
appointments CareerAppointment[]
|
||||
|
||||
@@index([schoolId])
|
||||
@@index([studentId])
|
||||
@@index([careerCluster])
|
||||
@@map("career_profiles")
|
||||
}
|
||||
|
||||
model CareerGoal {
|
||||
id String @id @default(cuid())
|
||||
profileId String
|
||||
title String
|
||||
description String?
|
||||
category String // short_term, long_term, academic, professional
|
||||
targetDate DateTime?
|
||||
progress Int @default(0) // 0-100
|
||||
status String @default("active") // active, completed, abandoned
|
||||
milestones String? // JSON: [{ title, completed, date }]
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
profile CareerProfile @relation(fields: [profileId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@index([profileId])
|
||||
@@index([category])
|
||||
@@index([status])
|
||||
@@map("career_goals")
|
||||
}
|
||||
|
||||
model CareerAppointment {
|
||||
id String @id @default(cuid())
|
||||
schoolId String
|
||||
profileId String
|
||||
counselorId String
|
||||
date DateTime
|
||||
duration Int @default(30) // minutes
|
||||
type String @default("guidance") // guidance, career, academic, college
|
||||
status String @default("scheduled") // scheduled, completed, cancelled
|
||||
notes String?
|
||||
actionItems String? // JSON: [{ item, completed }]
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
school School @relation(fields: [schoolId], references: [id])
|
||||
profile CareerProfile @relation(fields: [profileId], references: [id], onDelete: Cascade)
|
||||
counselor User @relation(fields: [counselorId], references: [id])
|
||||
|
||||
@@index([schoolId])
|
||||
@@index([profileId])
|
||||
@@index([counselorId])
|
||||
@@index([date])
|
||||
@@index([status])
|
||||
@@map("career_appointments")
|
||||
}
|
||||
|
||||
model SubstituteTeacher {
|
||||
id String @id @default(cuid())
|
||||
schoolId String
|
||||
userId String? @unique // optional link to User account
|
||||
firstName String
|
||||
lastName String
|
||||
email String?
|
||||
phone String?
|
||||
qualifications String? // JSON: ["Mathematics", "Physics", "German"]
|
||||
subjects String? // JSON: subject IDs they can teach
|
||||
gradeLevels String? // JSON: ["1-4", "5-6", "7-10"]
|
||||
availability String? // JSON: { "monday": ["08:00-12:00"], "tuesday": [...] }
|
||||
maxDaysPerWeek Int @default(5)
|
||||
rating Float @default(0) // 0-5 average rating
|
||||
totalAssignments Int @default(0)
|
||||
notes String?
|
||||
isActive Boolean @default(true)
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
deletedAt DateTime?
|
||||
|
||||
school School @relation(fields: [schoolId], references: [id])
|
||||
user User? @relation(fields: [userId], references: [id])
|
||||
assignments SubstitutionAssignment[]
|
||||
|
||||
@@index([schoolId])
|
||||
@@index([userId])
|
||||
@@map("substitute_teachers")
|
||||
}
|
||||
|
||||
model TeacherAbsence {
|
||||
id String @id @default(cuid())
|
||||
schoolId String
|
||||
teacherId String
|
||||
type String // SICK, PERSONAL, TRAINING, OTHER
|
||||
startDate DateTime
|
||||
endDate DateTime
|
||||
reason String?
|
||||
status String @default("reported") // reported, covered, cancelled
|
||||
notes String?
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
school School @relation(fields: [schoolId], references: [id])
|
||||
teacher User @relation(fields: [teacherId], references: [id])
|
||||
assignments SubstitutionAssignment[]
|
||||
|
||||
@@index([schoolId])
|
||||
@@index([teacherId])
|
||||
@@index([startDate])
|
||||
@@map("teacher_absences")
|
||||
}
|
||||
|
||||
model SubstitutionAssignment {
|
||||
id String @id @default(cuid())
|
||||
schoolId String
|
||||
absenceId String
|
||||
substituteId String
|
||||
classGroupId String?
|
||||
subjectId String?
|
||||
date DateTime
|
||||
period Int? // lesson period number
|
||||
room String?
|
||||
status String @default("pending") // pending, confirmed, completed, cancelled
|
||||
notes String?
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
school School @relation(fields: [schoolId], references: [id])
|
||||
absence TeacherAbsence @relation(fields: [absenceId], references: [id])
|
||||
substitute SubstituteTeacher @relation(fields: [substituteId], references: [id])
|
||||
classGroup ClassGroup? @relation(fields: [classGroupId], references: [id])
|
||||
subject Subject? @relation(fields: [subjectId], references: [id])
|
||||
|
||||
@@index([schoolId])
|
||||
@@index([absenceId])
|
||||
@@index([substituteId])
|
||||
@@index([date])
|
||||
@@map("substitution_assignments")
|
||||
}
|
||||
Reference in new issue
Block a user