generator client { provider = "prisma-client-js" output = "../generated/prisma" } datasource db { provider = "postgresql" url = env("DATABASE_URL") } model User { id String @id @default(cuid()) email String @unique name String? emailVerified Boolean? image String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt role String? @default("user") accounts Account[] sessions Session[] apiKeys ApiKey[] // Relations discussions Discussion[] events Event[] complaints Complaint[] @relation("ComplaintReporter") assignedComplaints Complaint[] @relation("ComplaintAssignee") complaintUpdates ComplaintUpdate[] serviceLetters ServiceLetter[] innovationIdeas InnovationIdea[] @relation("IdeaReviewer") healthRecords HealthRecord[] populationDynamics PopulationDynamic[] budgetTransactions BudgetTransaction[] posyandus Posyandu[] securityReports SecurityReport[] @@map("user") } // --- KATEGORI 1: KINERJA DIVISI & AKTIVITAS --- model Division { id String @id @default(cuid()) externalId String? @unique // ID asli dari server NOC villageId String? @default("desa1") // ID Desa dari sistem NOC name String @unique description String? color String @default("#1E3A5F") isActive Boolean @default(true) lastSyncedAt DateTime? // Terakhir kali sinkronisasi dilakukan createdAt DateTime @default(now()) updatedAt DateTime @updatedAt activities Activity[] documents Document[] discussions Discussion[] divisionMetrics DivisionMetric[] @@map("division") } model Activity { id String @id @default(cuid()) externalId String? @unique // ID asli dari server NOC villageId String? @default("desa1") title String description String? divisionId String startDate DateTime? endDate DateTime? dueDate DateTime? progress Int @default(0) // 0-100 status ActivityStatus @default(BERJALAN) priority Priority @default(SEDANG) assignedTo String? // JSON array of user IDs completedAt DateTime? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt division Division @relation(fields: [divisionId], references: [id], onDelete: Cascade) @@index([divisionId]) @@index([status]) @@map("activity") } model Document { id String @id @default(cuid()) externalId String? @unique // ID asli dari server NOC villageId String? @default("desa1") title String category DocumentCategory type String // "Gambar", "Dokumen", "PDF", etc fileUrl String fileSize Int? // in bytes divisionId String? uploadedBy String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt division Division? @relation(fields: [divisionId], references: [id], onDelete: SetNull) @@index([category]) @@index([divisionId]) @@map("document") } model Discussion { id String @id @default(cuid()) externalId String? @unique // ID asli dari server NOC villageId String? @default("desa1") message String senderId String parentId String? // For threaded discussions divisionId String? isResolved Boolean @default(false) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt sender User @relation(fields: [senderId], references: [id], onDelete: Cascade) parent Discussion? @relation("DiscussionThread", fields: [parentId], references: [id], onDelete: SetNull) replies Discussion[] @relation("DiscussionThread") division Division? @relation(fields: [divisionId], references: [id], onDelete: SetNull) @@index([divisionId]) @@index([createdAt]) @@map("discussion") } model Event { id String @id @default(cuid()) externalId String? @unique // ID asli dari server NOC villageId String? @default("desa1") title String description String? eventType EventType startDate DateTime endDate DateTime? location String? isAllDay Boolean @default(false) isRecurring Boolean @default(false) createdBy String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt creator User @relation(fields: [createdBy], references: [id], onDelete: Cascade) @@index([startDate]) @@index([eventType]) @@map("event") } model DivisionMetric { id String @id @default(cuid()) divisionId String period String // "2025-Q1", "2025-01" activityCount Int @default(0) completionRate Float @default(0) avgProgress Float @default(0) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt division Division @relation(fields: [divisionId], references: [id], onDelete: Cascade) @@unique([divisionId, period]) @@map("division_metric") } // --- KATEGORI 2: PENGADUAN & LAYANAN PUBLIK --- model Complaint { id String @id @default(cuid()) complaintNumber String @unique // Auto-generated: COMPLAINT-YYYYMMDD-XXX title String description String category ComplaintCategory status ComplaintStatus @default(BARU) priority Priority @default(SEDANG) reporterId String? reporterPhone String? reporterEmail String? isAnonymous Boolean @default(false) assignedTo String? // User ID resolvedBy String? // User ID resolvedAt DateTime? location String? imageUrl String[] // Array of image URLs createdAt DateTime @default(now()) updatedAt DateTime @updatedAt reporter User? @relation("ComplaintReporter", fields: [reporterId], references: [id], onDelete: SetNull) assignee User? @relation("ComplaintAssignee", fields: [assignedTo], references: [id], onDelete: SetNull) complaintUpdates ComplaintUpdate[] @@index([status]) @@index([category]) @@index([createdAt]) @@map("complaint") } model ComplaintUpdate { id String @id @default(cuid()) complaintId String message String status ComplaintStatus? updatedBy String createdAt DateTime @default(now()) complaint Complaint @relation(fields: [complaintId], references: [id], onDelete: Cascade) updater User @relation(fields: [updatedBy], references: [id], onDelete: Cascade) @@index([complaintId]) @@map("complaint_update") } model ServiceLetter { id String @id @default(cuid()) letterNumber String @unique letterType LetterType applicantName String applicantNik String applicantAddress String purpose String? status ServiceStatus @default(BARU) processedBy String? completedAt DateTime? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt processor User? @relation(fields: [processedBy], references: [id], onDelete: SetNull) @@index([letterType]) @@index([status]) @@index([createdAt]) @@map("service_letter") } model InnovationIdea { id String @id @default(cuid()) title String description String category String // "Teknologi", "Ekonomi", "Kesehatan", "Pendidikan" submitterName String submitterContact String? status IdeaStatus @default(BARU) reviewedBy String? reviewedAt DateTime? notes String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt reviewer User? @relation("IdeaReviewer", fields: [reviewedBy], references: [id], onDelete: SetNull) @@index([category]) @@index([status]) @@map("innovation_idea") } // --- KATEGORI 3: DEMOGRAFI & KEPENDUDUKAN --- model Resident { id String @id @default(cuid()) nik String @unique kk String name String birthDate DateTime birthPlace String gender Gender religion Religion maritalStatus MaritalStatus @default(BELUM_KAWIN) education EducationLevel? occupation String? banjarId String rt String rw String address String isHeadOfHousehold Boolean @default(false) isPoor Boolean @default(false) isStunting Boolean @default(false) deathDate DateTime? moveInDate DateTime? moveOutDate DateTime? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt banjar Banjar @relation(fields: [banjarId], references: [id], onDelete: Cascade) healthRecords HealthRecord[] employmentRecords EmploymentRecord[] @@index([banjarId]) @@index([religion]) @@index([occupation]) @@map("resident") } model Banjar { id String @id @default(cuid()) name String @unique code String @unique description String? totalPopulation Int @default(0) totalKK Int @default(0) totalPoor Int @default(0) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt residents Resident[] umkms Umkm[] @@map("banjar") } // --- KATEGORI 4: KEUANGAN & ANGGARAN --- model Budget { id String @id @default(cuid()) category String // "Belanja", "Pangan", "Pembiayaan", "Pendapatan" amount Float @default(0) percentage Float @default(0) color String @default("#3B82F6") fiscalYear Int @default(2025) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@unique([category, fiscalYear]) @@map("budget") } // --- KATEGORI 5: METRIK DASHBOARD & SDGS --- model SdgsScore { id String @id @default(cuid()) title String @unique score Float @default(0) image String? // filename in public folder createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@map("sdgs_score") } model SatisfactionRating { id String @id @default(cuid()) category String @unique // "Sangat Puas", "Puas", "Cukup", "Kurang" value Int @default(0) color String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@map("satisfaction_rating") } // --- STUBS FOR PHASE 2+ (To maintain relations) --- model HealthRecord { id String @id @default(cuid()) residentId String resident Resident @relation(fields: [residentId], references: [id]) recordedBy String recorder User @relation(fields: [recordedBy], references: [id]) type String // "Pemeriksaan", "Imunisasi", "Ibu Hamil" notes String? createdAt DateTime @default(now()) } model EmploymentRecord { id String @id @default(cuid()) residentId String resident Resident @relation(fields: [residentId], references: [id]) companyName String position String startDate DateTime endDate DateTime? isActive Boolean @default(true) createdAt DateTime @default(now()) } model PopulationDynamic { id String @id @default(cuid()) documentedBy String documentor User @relation(fields: [documentedBy], references: [id]) type String // "KELAHIRAN", "KEMATIAN", "KEDATANGAN", "KEPERGIAN" residentName String eventDate DateTime description String? createdAt DateTime @default(now()) } model BudgetTransaction { id String @id @default(cuid()) createdBy String creator User @relation(fields: [createdBy], references: [id]) transactionNumber String @unique type String // "PENGELUARAN", "PENDAPATAN" category String amount Float description String? date DateTime createdAt DateTime @default(now()) } model Umkm { id String @id @default(cuid()) banjarId String? banjar Banjar? @relation(fields: [banjarId], references: [id]) name String owner String productType String? description String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt } model Posyandu { id String @id @default(cuid()) coordinatorId String? coordinator User? @relation(fields: [coordinatorId], references: [id]) name String location String schedule String type String // "Ibu dan Anak", "Lansia", etc. createdAt DateTime @default(now()) updatedAt DateTime @updatedAt } model SecurityReport { id String @id @default(cuid()) assignedTo String? assignee User? @relation(fields: [assignedTo], references: [id]) reportNumber String @unique title String description String location String? reportedBy String status String @default("BARU") // BARU, DIPROSES, SELESAI createdAt DateTime @default(now()) updatedAt DateTime @updatedAt } // --- ENUMS --- enum ActivityStatus { BERJALAN SELESAI TERTUNDA DIBATALKAN } enum Priority { RENDAH SEDANG TINGGI DARURAT } enum DocumentCategory { SURAT_KEPUTUSAN DOKUMENTASI LAPORAN_KEUANGAN NOTULENSI_RAPAT UMUM } enum EventType { RAPAT KEGIATAN UPACARA SOSIAL BUDAYA LAINNYA } enum ComplaintCategory { KETERTIBAN_UMUM PELAYANAN_KESEHATAN INFRASTRUKTUR ADMINISTRASI KEAMANAN LAINNYA } enum ComplaintStatus { BARU DIPROSES SELESAI DITOLAK } enum LetterType { KTP KK DOMISILI USAHA KETERANGAN_TIDAK_MAMPU SURAT_PENGANTAR LAINNYA } enum ServiceStatus { BARU DIPROSES SELESAI DIAMBIL } enum IdeaStatus { BARU DIKAJI DISETUJUI DITOLAK DIIMPLEMENTASI } enum Gender { LAKI_LAKI PEREMPUAN } enum Religion { HINDU ISLAM KRISTEN KATOLIK BUDDHA KONGHUCU LAINNYA } enum MaritalStatus { BELUM_KAWIN KAWIN CERAI_HIDUP CERAI_MATI } enum EducationLevel { TIDAK_SEKOLAH SD SMP SMA D3 S1 S2 S3 } model Session { id String @id @default(cuid()) userId String expiresAt DateTime token String @unique createdAt DateTime @default(now()) updatedAt DateTime @updatedAt ipAddress String? userAgent String? user User @relation(fields: [userId], references: [id], onDelete: Cascade) @@index([userId]) @@map("session") } model Account { id String @id @default(cuid()) userId String accountId String providerId String accessToken String? refreshToken String? expiresAt DateTime? password String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt idToken String? accessTokenExpiresAt DateTime? refreshTokenExpiresAt DateTime? scope String? user User @relation(fields: [userId], references: [id], onDelete: Cascade) @@index([userId]) @@map("account") } model Verification { id String @id @default(cuid()) identifier String value String expiresAt DateTime createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@index([identifier]) @@map("verification") } model ApiKey { id String @id @default(cuid()) name String key String @unique userId String isActive Boolean @default(true) expiresAt DateTime? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt user User @relation(fields: [userId], references: [id], onDelete: Cascade) @@index([userId]) @@map("api_key") }