Untitled

Anonymous
plain_text
02/09/2026 1:51 PM
18.4 KB
11
Indexable
generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}

// ================ ENUMS ================
enum Role {
  ADMIN
  TEACHER
  APPLICANT
  STUDENT
}

enum StatusPpdb {
  PENDING
  ACCEPTED
  REJECTED
}

enum AttendanceStatus {
  HADIR
  SAKIT
  IZIN
  ALPHA
}

enum RoomType {
  CLASSROOM
  LABORATORY
  PRACTICE
  LIBRARY
  OFFICE
  HALL
  TOILET
  OTHER
}

enum TimePreference {
  MORNING    // 07:00 - 10:00
  MIDDAY     // 10:00 - 13:00
  AFTERNOON  // 13:00 - 16:00
  ANY        // No preference
}

enum GradeLevel {
  GRADE_10  // Kelas X
  GRADE_11  // Kelas XI
  GRADE_12  // Kelas XII
}

// ================ MODEL UTAMA ================

model User {
  id        Int      @id @default(autoincrement())
  email     String   @unique
  password  String   // hashed
  role      Role
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt

  // Relasi ke role spesifik
  admin     Admin?
  teacher   Teacher?
  applicant Applicant?
  student   Student?
  staff     Staff?

  @@index([email])
  @@index([role])
}

// ================ ADMIN ================
model Admin {
  id        Int      @id @default(autoincrement())
  name      String
  nip       String   @unique
  phone     String?
  address   String?
  user      User     @relation(fields: [userId], references: [id], onDelete: Cascade)
  userId    Int      @unique
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt

  @@index([nip])
}

// ================ GURU ================
model Teacher {
  id        Int      @id @default(autoincrement())
  name      String
  nip       String   @unique
  phone     String?
  address   String?
  user      User     @relation(fields: [userId], references: [id], onDelete: Cascade)
  userId    Int      @unique
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt

  schedules        Schedule[]
  attendances      Attendance[]
  gradeClasses     GradeClass[] @relation("GradeClassHomeroom") // Wali kelas
  teacherSubjects  TeacherSubject[] // Many-to-many dengan Subject
  preference TeacherPreference?

  @@index([nip])
}

// ================ CALON SISWA (PPDB) ================
model Applicant {
  id           Int        @id @default(autoincrement())
  name         String
  nisn         String     @unique
  phone        String
  address      String
  birthDate    DateTime
  schoolOrigin String
  status       StatusPpdb @default(PENDING)
  registeredAt DateTime   @default(now())
  feedback     String?
  user         User?      @relation(fields: [userId], references: [id], onDelete: Cascade)
  userId       Int?       @unique
  
  // Profile Lengkap (nullable karena diisi setelah registrasi)
  gender              String?      // "MALE" / "FEMALE"
  birthPlace          String?
  fullAddress         String?
  postalCode          String?
  ijazahNumber        String?
  ijazahYear          Int?
  skhunNumber         String?
  desiredMajor        String?      // "IPA" / "IPS" / "Bahasa"
  bloodType           String?      // "A" / "B" / "AB" / "O"
  childPosition       Int?         // Anak ke-
  totalSiblings       Int?         // Dari berapa bersaudara
  hobby               String?
  height              Float?       // Tinggi badan (cm)
  weight              Float?       // Berat badan (kg)
  achievements        String?      @db.Text // JSON array
  
  // Data Orang Tua
  livingWith          String?      // "PARENTS" / "GUARDIAN"
  motherName          String?
  fatherName          String?
  motherOccupation    String?
  fatherOccupation    String?
  parentsPhone        String?
  fatherIncome        Int?      // Range: "< 1jt", "1-3jt", etc
  motherIncome        Int?
  
  // Data Wali (jika tinggal dengan wali)
  guardianName        String?
  guardianOccupation  String?
  guardianAddress     String?
  guardianPhone       String?
  guardianIncome      Int?
  
  // Upload Dokumen (store file paths)
  sttbFile            String?      // STTB SMP
  skhunFile           String?      // SKHUN SMP
  birthCertFile       String?      // Akte Kelahiran
  photoFile           String?      // Pas foto
  ijazahFile          String?      // Ijazah/Bukti Kelulusan
  achievementFiles    Json?        // Array of achievement file paths
  
  createdAt   DateTime   @default(now())
  updatedAt   DateTime   @updatedAt

  @@index([nisn])
  @@index([status])
  @@index([userId])
}
// ================ SISWA AKTIF ================
model Student {
  id            Int      @id @default(autoincrement())
  name          String
  nisn          String   @unique
  phone         String
  address       String
  birthDate     DateTime
  schoolOrigin  String   // Asal sekolah SMP
  enrollmentYear Int     // Tahun masuk (contoh: 2024)
  
  user          User?    @relation(fields: [userId], references: [id], onDelete: Cascade)
  userId        Int?     @unique
  
  // Relasi ke kelas
  gradeClass    GradeClass? @relation(fields: [gradeClassId], references: [id])
  gradeClassId  Int?
  
  createdAt     DateTime @default(now())
  updatedAt     DateTime @updatedAt

  // Relasi ke absensi
  attendances   Attendance[]

  @@index([nisn])
  @@index([gradeClassId])
  @@index([enrollmentYear])
}

// ================ STAFF ================
model Staff {
  id         Int      @id @default(autoincrement())
  name       String
  employeeId String   @unique
  position   String?
  phone      String?
  address    String?
  user       User?    @relation(fields: [userId], references: [id], onDelete: Cascade)
  userId     Int?     @unique
  createdAt  DateTime @default(now())
  updatedAt  DateTime @updatedAt

  @@index([employeeId])
}

// ================ MATA PELAJARAN ================
model Subject {
  id          Int        @id @default(autoincrement())
  name        String     // Nama mata pelajaran (e.g., "Matematika", "Bahasa Indonesia")
  code        String     @unique // Kode mata pelajaran (e.g., "MAT", "BIN")
  description String?    @db.Text
  category    String?    // Kategori: "Wajib", "Peminatan", "Lintas Minat"
  
  // Grade Level - untuk menentukan subject ini untuk kelas berapa
  gradeLevel  GradeLevel // GRADE_10, GRADE_11, atau GRADE_12
  
  createdAt   DateTime   @default(now())
  updatedAt   DateTime   @updatedAt

  // Relasi ke jadwal
  schedules        Schedule[]
  teacherSubjects  TeacherSubject[] // Many-to-many dengan Teacher
  subjectHours SubjectHours[]

  @@unique([code, gradeLevel]) // Kode + grade level harus unik (misal: MAT untuk Grade 10, MAT untuk Grade 11)
  @@index([code])
  @@index([category])
  @@index([gradeLevel])
  attendances Attendance[]
}

// ================ JUNCTION TABLE: GURU <-> MATA PELAJARAN ================
model TeacherSubject {
  id        Int      @id @default(autoincrement())
  
  teacher   Teacher  @relation(fields: [teacherId], references: [id], onDelete: Cascade)
  teacherId Int
  
  subject   Subject  @relation(fields: [subjectId], references: [id], onDelete: Cascade)
  subjectId Int
  
  // Metadata tambahan (opsional)
  isPrimary Boolean  @default(false) // Apakah guru ini pengampu utama untuk mata pelajaran ini
  assignedAt DateTime @default(now())
  
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt

  @@unique([teacherId, subjectId]) // Satu guru tidak bisa di-assign ke mata pelajaran yang sama 2x
  @@index([teacherId])
  @@index([subjectId])
}

model SchoolTimeConfig {
  id               Int      @id @default(autoincrement())
  academicYear     String   @unique
  schoolStartTime  String   // e.g., "07:00"
  schoolEndTime    String   // e.g., "16:00"
  periodDuration   Int      // dalam menit, e.g., 45
  breakDuration    Int      // dalam menit, e.g., 15
  maxPeriodsPerDay Int      // maksimal jam pelajaran per hari
  
  // Break times - JSON array of { afterPeriod: number, duration: number }
  // e.g., [{ afterPeriod: 2, duration: 20 }, { afterPeriod: 5, duration: 30 }]
  breakTimes       Json     
  
  createdAt        DateTime @default(now())
  updatedAt        DateTime @updatedAt
  
  @@index([academicYear])
}

// Jam pelajaran per subject
model SubjectHours {
  id               Int      @id @default(autoincrement())
  subject          Subject  @relation(fields: [subjectId], references: [id], onDelete: Cascade)
  subjectId        Int
  gradeClass       GradeClass @relation(fields: [gradeClassId], references: [id], onDelete: Cascade)
  gradeClassId     Int
  
  hoursPerWeek     Int      // Jam pelajaran per minggu untuk subject ini
  preferredDays    Json?    // Optional: [1,2,3] = prefer Monday, Tuesday, Wednesday
  
  createdAt        DateTime @default(now())
  updatedAt        DateTime @updatedAt
  
  @@unique([subjectId, gradeClassId])
  @@index([subjectId])
  @@index([gradeClassId])
}

model TeacherPreference {
  id              Int            @id @default(autoincrement())
  teacher         Teacher        @relation(fields: [teacherId], references: [id], onDelete: Cascade)
  teacherId       Int            @unique
  
  timePreference  TimePreference @default(ANY)
  maxHoursPerDay  Int            @default(8)
  maxHoursPerWeek Int            @default(40)
  unavailableDays Json?          // Array of days: [1,3] = Monday, Wednesday
  
  createdAt       DateTime       @default(now())
  updatedAt       DateTime       @updatedAt
}

// ================ KELAS (GRADE) ================
model GradeClass {
  id          Int        @id @default(autoincrement())
  name        String     // Contoh: "X-1", "XI IPA 2", "XII IPS 1"
  gradeLevel  GradeLevel // GRADE_10, GRADE_11, GRADE_12
  academicYear String    // Contoh: "2024/2025"
  capacity    Int        @default(30)
  
  // Wali kelas
  homeroom    Teacher?   @relation("GradeClassHomeroom", fields: [homeroomId], references: [id])
  homeroomId  Int?
  
  // Ruang kelas
  classroom   MapRoom?   @relation(fields: [classroomId], references: [id])
  classroomId Int?
  
  createdAt   DateTime   @default(now())
  updatedAt   DateTime   @updatedAt

  // Relasi
  students    Student[]
  schedules   Schedule[]
  subjectHours SubjectHours[]

  @@unique([name, academicYear]) // Nama kelas + tahun ajaran harus unik
  @@index([gradeLevel])
  @@index([academicYear])
  @@index([homeroomId])
  @@index([classroomId])
  attendances Attendance[]
}

// ================ ABSENSI ================
model Attendance {
  id        Int              @id @default(autoincrement())
  date      DateTime         @default(now())
  status    AttendanceStatus @default(HADIR)
  notes     String?          // Catatan tambahan (alasan izin/sakit, dll)

  // Relasi ke siswa
  student   Student  @relation(fields: [studentId], references: [id], onDelete: Cascade)
  studentId Int

  // Relasi ke guru yang mencatat
  teacher   Teacher  @relation(fields: [teacherId], references: [id])
  teacherId Int

  // Relasi ke jadwal (opsional - untuk absensi per mata pelajaran)
  schedule   Schedule? @relation(fields: [scheduleId], references: [id])
  scheduleId Int?

    subjectId   Int
  subject     Subject  @relation(fields: [subjectId], references: [id])
  
  gradeClassId Int
  gradeClass   GradeClass @relation(fields: [gradeClassId], references: [id])

  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt

  @@unique([studentId, date, subjectId]) // Satu siswa hanya bisa punya 1 absensi per hari per jadwal
  @@index([date])
  @@index([studentId])
  @@index([teacherId])
  @@index([status])
}

// ================ JADWAL ================
model Schedule {
  id      Int    @id @default(autoincrement())
  day     Int    // 1=Monday, ..., 6=Saturday
  period  Int    // Period number (1st hour, 2nd hour, etc.)
  
  // Waktu mulai dan selesai
  startTime String // Format: "07:00"
  endTime   String // Format: "08:30"

  // Relasi ke mata pelajaran
  subject   Subject @relation(fields: [subjectId], references: [id], onDelete: Cascade)
  subjectId Int

  // Relasi ke kelas
  gradeClass   GradeClass @relation(fields: [gradeClassId], references: [id], onDelete: Cascade)
  gradeClassId Int

  // Relasi ke ruangan
  room   MapRoom? @relation(fields: [roomId], references: [id])
  roomId Int?

  // Relasi ke guru
  teacher   Teacher @relation(fields: [teacherId], references: [id])
  teacherId Int

  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt

  // Relasi ke absensi
  attendances Attendance[]

  @@unique([gradeClassId, day, period]) // Satu kelas tidak bisa punya 2 jadwal di waktu yang sama
  @@index([day])
  @@index([teacherId])
  @@index([gradeClassId])
  @@index([subjectId])
}

// ================ FEEDBACK (dari publik) ================
model Feedback {
  id        Int      @id @default(autoincrement())
  name      String
  email     String?
  message   String   @db.Text
  isRead    Boolean  @default(false)
  createdAt DateTime @default(now())

  @@index([isRead])
  @@index([createdAt])
}

// ================ DENAH SEKOLAH (CAMPUS MAP) ================
model CampusMap {
  id         Int      @id @default(autoincrement())
  name       String   @default("Denah Utama")
  svgPath    String   // e.g., "/uploads/maps/school.svg"
  uploadedAt DateTime @default(now())
  isActive   Boolean  @default(true)
  createdAt  DateTime @default(now())
  updatedAt  DateTime @updatedAt

  rooms MapRoom[]

  @@index([isActive])
}

// ================ RUANGAN DI DENAH ================
model MapRoom {
  id          Int      @id @default(autoincrement())
  roomId      String   // Harus sesuai dengan `id` elemen di SVG (e.g., "class-101")
  name        String
  type        RoomType @default(OTHER)
  capacity    Int      @default(0)
  floor       Int      @default(1) // Lantai berapa
  building    String?  // Gedung (contoh: "Gedung A", "Gedung B")
  description String?  @db.Text
  metadata    Json?    // Optional: { x: 120, y: 200, color: "#3b82f6" }

  campusMap   CampusMap @relation(fields: [campusMapId], references: [id], onDelete: Cascade)
  campusMapId Int

  schedules    Schedule[]
  gradeClasses GradeClass[]

  @@unique([campusMapId, roomId]) // roomId harus unik per denah
  @@index([type])
  @@index([campusMapId])
}
Editor is loading...
Leave a Comment