fix(header): fix missing Divider, Badge, IconUserShield and navigate

This commit is contained in:
2026-03-26 14:13:59 +08:00
parent ebc1242bee
commit aeedb17402
35 changed files with 2788 additions and 552 deletions

View File

@@ -0,0 +1,568 @@
-- CreateEnum
CREATE TYPE "ActivityStatus" AS ENUM ('BERJALAN', 'SELESAI', 'TERTUNDA', 'DIBATALKAN');
-- CreateEnum
CREATE TYPE "Priority" AS ENUM ('RENDAH', 'SEDANG', 'TINGGI', 'DARURAT');
-- CreateEnum
CREATE TYPE "DocumentCategory" AS ENUM ('SURAT_KEPUTUSAN', 'DOKUMENTASI', 'LAPORAN_KEUANGAN', 'NOTULENSI_RAPAT', 'UMUM');
-- CreateEnum
CREATE TYPE "EventType" AS ENUM ('RAPAT', 'KEGIATAN', 'UPACARA', 'SOSIAL', 'BUDAYA', 'LAINNYA');
-- CreateEnum
CREATE TYPE "ComplaintCategory" AS ENUM ('KETERTIBAN_UMUM', 'PELAYANAN_KESEHATAN', 'INFRASTRUKTUR', 'ADMINISTRASI', 'KEAMANAN', 'LAINNYA');
-- CreateEnum
CREATE TYPE "ComplaintStatus" AS ENUM ('BARU', 'DIPROSES', 'SELESAI', 'DITOLAK');
-- CreateEnum
CREATE TYPE "LetterType" AS ENUM ('KTP', 'KK', 'DOMISILI', 'USAHA', 'KETERANGAN_TIDAK_MAMPU', 'SURAT_PENGANTAR', 'LAINNYA');
-- CreateEnum
CREATE TYPE "ServiceStatus" AS ENUM ('BARU', 'DIPROSES', 'SELESAI', 'DIAMBIL');
-- CreateEnum
CREATE TYPE "IdeaStatus" AS ENUM ('BARU', 'DIKAJI', 'DISETUJUI', 'DITOLAK', 'DIIMPLEMENTASI');
-- CreateEnum
CREATE TYPE "Gender" AS ENUM ('LAKI_LAKI', 'PEREMPUAN');
-- CreateEnum
CREATE TYPE "Religion" AS ENUM ('HINDU', 'ISLAM', 'KRISTEN', 'KATOLIK', 'BUDDHA', 'KONGHUCU', 'LAINNYA');
-- CreateEnum
CREATE TYPE "MaritalStatus" AS ENUM ('BELUM_KAWIN', 'KAWIN', 'CERAI_HIDUP', 'CERAI_MATI');
-- CreateEnum
CREATE TYPE "EducationLevel" AS ENUM ('TIDAK_SEKOLAH', 'SD', 'SMP', 'SMA', 'D3', 'S1', 'S2', 'S3');
-- CreateTable
CREATE TABLE "user" (
"id" TEXT NOT NULL,
"email" TEXT NOT NULL,
"name" TEXT,
"emailVerified" BOOLEAN,
"image" TEXT,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
"role" TEXT DEFAULT 'user',
CONSTRAINT "user_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "division" (
"id" TEXT NOT NULL,
"name" TEXT NOT NULL,
"description" TEXT,
"color" TEXT NOT NULL DEFAULT '#1E3A5F',
"isActive" BOOLEAN NOT NULL DEFAULT true,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "division_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "activity" (
"id" TEXT NOT NULL,
"title" TEXT NOT NULL,
"description" TEXT,
"divisionId" TEXT NOT NULL,
"startDate" TIMESTAMP(3),
"endDate" TIMESTAMP(3),
"dueDate" TIMESTAMP(3),
"progress" INTEGER NOT NULL DEFAULT 0,
"status" "ActivityStatus" NOT NULL DEFAULT 'BERJALAN',
"priority" "Priority" NOT NULL DEFAULT 'SEDANG',
"assignedTo" TEXT,
"completedAt" TIMESTAMP(3),
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "activity_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "document" (
"id" TEXT NOT NULL,
"title" TEXT NOT NULL,
"category" "DocumentCategory" NOT NULL,
"type" TEXT NOT NULL,
"fileUrl" TEXT NOT NULL,
"fileSize" INTEGER,
"divisionId" TEXT,
"uploadedBy" TEXT NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "document_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "discussion" (
"id" TEXT NOT NULL,
"message" TEXT NOT NULL,
"senderId" TEXT NOT NULL,
"parentId" TEXT,
"divisionId" TEXT,
"isResolved" BOOLEAN NOT NULL DEFAULT false,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "discussion_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "event" (
"id" TEXT NOT NULL,
"title" TEXT NOT NULL,
"description" TEXT,
"eventType" "EventType" NOT NULL,
"startDate" TIMESTAMP(3) NOT NULL,
"endDate" TIMESTAMP(3),
"location" TEXT,
"isAllDay" BOOLEAN NOT NULL DEFAULT false,
"isRecurring" BOOLEAN NOT NULL DEFAULT false,
"createdBy" TEXT NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "event_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "division_metric" (
"id" TEXT NOT NULL,
"divisionId" TEXT NOT NULL,
"period" TEXT NOT NULL,
"activityCount" INTEGER NOT NULL DEFAULT 0,
"completionRate" DOUBLE PRECISION NOT NULL DEFAULT 0,
"avgProgress" DOUBLE PRECISION NOT NULL DEFAULT 0,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "division_metric_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "complaint" (
"id" TEXT NOT NULL,
"complaintNumber" TEXT NOT NULL,
"title" TEXT NOT NULL,
"description" TEXT NOT NULL,
"category" "ComplaintCategory" NOT NULL,
"status" "ComplaintStatus" NOT NULL DEFAULT 'BARU',
"priority" "Priority" NOT NULL DEFAULT 'SEDANG',
"reporterId" TEXT,
"reporterPhone" TEXT,
"reporterEmail" TEXT,
"isAnonymous" BOOLEAN NOT NULL DEFAULT false,
"assignedTo" TEXT,
"resolvedBy" TEXT,
"resolvedAt" TIMESTAMP(3),
"location" TEXT,
"imageUrl" TEXT[],
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "complaint_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "complaint_update" (
"id" TEXT NOT NULL,
"complaintId" TEXT NOT NULL,
"message" TEXT NOT NULL,
"status" "ComplaintStatus",
"updatedBy" TEXT NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "complaint_update_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "service_letter" (
"id" TEXT NOT NULL,
"letterNumber" TEXT NOT NULL,
"letterType" "LetterType" NOT NULL,
"applicantName" TEXT NOT NULL,
"applicantNik" TEXT NOT NULL,
"applicantAddress" TEXT NOT NULL,
"purpose" TEXT,
"status" "ServiceStatus" NOT NULL DEFAULT 'BARU',
"processedBy" TEXT,
"completedAt" TIMESTAMP(3),
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "service_letter_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "innovation_idea" (
"id" TEXT NOT NULL,
"title" TEXT NOT NULL,
"description" TEXT NOT NULL,
"category" TEXT NOT NULL,
"submitterName" TEXT NOT NULL,
"submitterContact" TEXT,
"status" "IdeaStatus" NOT NULL DEFAULT 'BARU',
"reviewedBy" TEXT,
"reviewedAt" TIMESTAMP(3),
"notes" TEXT,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "innovation_idea_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "resident" (
"id" TEXT NOT NULL,
"nik" TEXT NOT NULL,
"kk" TEXT NOT NULL,
"name" TEXT NOT NULL,
"birthDate" TIMESTAMP(3) NOT NULL,
"birthPlace" TEXT NOT NULL,
"gender" "Gender" NOT NULL,
"religion" "Religion" NOT NULL,
"maritalStatus" "MaritalStatus" NOT NULL DEFAULT 'BELUM_KAWIN',
"education" "EducationLevel",
"occupation" TEXT,
"banjarId" TEXT NOT NULL,
"rt" TEXT NOT NULL,
"rw" TEXT NOT NULL,
"address" TEXT NOT NULL,
"isHeadOfHousehold" BOOLEAN NOT NULL DEFAULT false,
"isPoor" BOOLEAN NOT NULL DEFAULT false,
"isStunting" BOOLEAN NOT NULL DEFAULT false,
"deathDate" TIMESTAMP(3),
"moveInDate" TIMESTAMP(3),
"moveOutDate" TIMESTAMP(3),
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "resident_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "banjar" (
"id" TEXT NOT NULL,
"name" TEXT NOT NULL,
"code" TEXT NOT NULL,
"description" TEXT,
"totalPopulation" INTEGER NOT NULL DEFAULT 0,
"totalKK" INTEGER NOT NULL DEFAULT 0,
"totalPoor" INTEGER NOT NULL DEFAULT 0,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "banjar_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "HealthRecord" (
"id" TEXT NOT NULL,
"residentId" TEXT NOT NULL,
"recordedBy" TEXT NOT NULL,
CONSTRAINT "HealthRecord_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "EmploymentRecord" (
"id" TEXT NOT NULL,
"residentId" TEXT NOT NULL,
CONSTRAINT "EmploymentRecord_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "PopulationDynamic" (
"id" TEXT NOT NULL,
"documentedBy" TEXT NOT NULL,
CONSTRAINT "PopulationDynamic_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "Budget" (
"id" TEXT NOT NULL,
"approvedBy" TEXT,
CONSTRAINT "Budget_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "BudgetTransaction" (
"id" TEXT NOT NULL,
"createdBy" TEXT NOT NULL,
CONSTRAINT "BudgetTransaction_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "Umkm" (
"id" TEXT NOT NULL,
"banjarId" TEXT,
CONSTRAINT "Umkm_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "Posyandu" (
"id" TEXT NOT NULL,
"coordinatorId" TEXT,
CONSTRAINT "Posyandu_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "SecurityReport" (
"id" TEXT NOT NULL,
"assignedTo" TEXT,
CONSTRAINT "SecurityReport_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "session" (
"id" TEXT NOT NULL,
"userId" TEXT NOT NULL,
"expiresAt" TIMESTAMP(3) NOT NULL,
"token" TEXT NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
"ipAddress" TEXT,
"userAgent" TEXT,
CONSTRAINT "session_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "account" (
"id" TEXT NOT NULL,
"userId" TEXT NOT NULL,
"accountId" TEXT NOT NULL,
"providerId" TEXT NOT NULL,
"accessToken" TEXT,
"refreshToken" TEXT,
"expiresAt" TIMESTAMP(3),
"password" TEXT,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
"idToken" TEXT,
"accessTokenExpiresAt" TIMESTAMP(3),
"refreshTokenExpiresAt" TIMESTAMP(3),
"scope" TEXT,
CONSTRAINT "account_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "verification" (
"id" TEXT NOT NULL,
"identifier" TEXT NOT NULL,
"value" TEXT NOT NULL,
"expiresAt" TIMESTAMP(3) NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "verification_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "api_key" (
"id" TEXT NOT NULL,
"name" TEXT NOT NULL,
"key" TEXT NOT NULL,
"userId" TEXT NOT NULL,
"isActive" BOOLEAN NOT NULL DEFAULT true,
"expiresAt" TIMESTAMP(3),
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "api_key_pkey" PRIMARY KEY ("id")
);
-- CreateIndex
CREATE UNIQUE INDEX "user_email_key" ON "user"("email");
-- CreateIndex
CREATE UNIQUE INDEX "division_name_key" ON "division"("name");
-- CreateIndex
CREATE INDEX "activity_divisionId_idx" ON "activity"("divisionId");
-- CreateIndex
CREATE INDEX "activity_status_idx" ON "activity"("status");
-- CreateIndex
CREATE INDEX "document_category_idx" ON "document"("category");
-- CreateIndex
CREATE INDEX "document_divisionId_idx" ON "document"("divisionId");
-- CreateIndex
CREATE INDEX "discussion_divisionId_idx" ON "discussion"("divisionId");
-- CreateIndex
CREATE INDEX "discussion_createdAt_idx" ON "discussion"("createdAt");
-- CreateIndex
CREATE INDEX "event_startDate_idx" ON "event"("startDate");
-- CreateIndex
CREATE INDEX "event_eventType_idx" ON "event"("eventType");
-- CreateIndex
CREATE UNIQUE INDEX "division_metric_divisionId_period_key" ON "division_metric"("divisionId", "period");
-- CreateIndex
CREATE UNIQUE INDEX "complaint_complaintNumber_key" ON "complaint"("complaintNumber");
-- CreateIndex
CREATE INDEX "complaint_status_idx" ON "complaint"("status");
-- CreateIndex
CREATE INDEX "complaint_category_idx" ON "complaint"("category");
-- CreateIndex
CREATE INDEX "complaint_createdAt_idx" ON "complaint"("createdAt");
-- CreateIndex
CREATE INDEX "complaint_update_complaintId_idx" ON "complaint_update"("complaintId");
-- CreateIndex
CREATE UNIQUE INDEX "service_letter_letterNumber_key" ON "service_letter"("letterNumber");
-- CreateIndex
CREATE INDEX "service_letter_letterType_idx" ON "service_letter"("letterType");
-- CreateIndex
CREATE INDEX "service_letter_status_idx" ON "service_letter"("status");
-- CreateIndex
CREATE INDEX "service_letter_createdAt_idx" ON "service_letter"("createdAt");
-- CreateIndex
CREATE INDEX "innovation_idea_category_idx" ON "innovation_idea"("category");
-- CreateIndex
CREATE INDEX "innovation_idea_status_idx" ON "innovation_idea"("status");
-- CreateIndex
CREATE UNIQUE INDEX "resident_nik_key" ON "resident"("nik");
-- CreateIndex
CREATE INDEX "resident_banjarId_idx" ON "resident"("banjarId");
-- CreateIndex
CREATE INDEX "resident_religion_idx" ON "resident"("religion");
-- CreateIndex
CREATE INDEX "resident_occupation_idx" ON "resident"("occupation");
-- CreateIndex
CREATE UNIQUE INDEX "banjar_name_key" ON "banjar"("name");
-- CreateIndex
CREATE UNIQUE INDEX "banjar_code_key" ON "banjar"("code");
-- CreateIndex
CREATE UNIQUE INDEX "session_token_key" ON "session"("token");
-- CreateIndex
CREATE INDEX "session_userId_idx" ON "session"("userId");
-- CreateIndex
CREATE INDEX "account_userId_idx" ON "account"("userId");
-- CreateIndex
CREATE INDEX "verification_identifier_idx" ON "verification"("identifier");
-- CreateIndex
CREATE UNIQUE INDEX "api_key_key_key" ON "api_key"("key");
-- CreateIndex
CREATE INDEX "api_key_userId_idx" ON "api_key"("userId");
-- AddForeignKey
ALTER TABLE "activity" ADD CONSTRAINT "activity_divisionId_fkey" FOREIGN KEY ("divisionId") REFERENCES "division"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "document" ADD CONSTRAINT "document_divisionId_fkey" FOREIGN KEY ("divisionId") REFERENCES "division"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "discussion" ADD CONSTRAINT "discussion_senderId_fkey" FOREIGN KEY ("senderId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "discussion" ADD CONSTRAINT "discussion_parentId_fkey" FOREIGN KEY ("parentId") REFERENCES "discussion"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "discussion" ADD CONSTRAINT "discussion_divisionId_fkey" FOREIGN KEY ("divisionId") REFERENCES "division"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "event" ADD CONSTRAINT "event_createdBy_fkey" FOREIGN KEY ("createdBy") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "division_metric" ADD CONSTRAINT "division_metric_divisionId_fkey" FOREIGN KEY ("divisionId") REFERENCES "division"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "complaint" ADD CONSTRAINT "complaint_reporterId_fkey" FOREIGN KEY ("reporterId") REFERENCES "user"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "complaint" ADD CONSTRAINT "complaint_assignedTo_fkey" FOREIGN KEY ("assignedTo") REFERENCES "user"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "complaint_update" ADD CONSTRAINT "complaint_update_complaintId_fkey" FOREIGN KEY ("complaintId") REFERENCES "complaint"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "complaint_update" ADD CONSTRAINT "complaint_update_updatedBy_fkey" FOREIGN KEY ("updatedBy") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "service_letter" ADD CONSTRAINT "service_letter_processedBy_fkey" FOREIGN KEY ("processedBy") REFERENCES "user"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "innovation_idea" ADD CONSTRAINT "innovation_idea_reviewedBy_fkey" FOREIGN KEY ("reviewedBy") REFERENCES "user"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "resident" ADD CONSTRAINT "resident_banjarId_fkey" FOREIGN KEY ("banjarId") REFERENCES "banjar"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "HealthRecord" ADD CONSTRAINT "HealthRecord_residentId_fkey" FOREIGN KEY ("residentId") REFERENCES "resident"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "HealthRecord" ADD CONSTRAINT "HealthRecord_recordedBy_fkey" FOREIGN KEY ("recordedBy") REFERENCES "user"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "EmploymentRecord" ADD CONSTRAINT "EmploymentRecord_residentId_fkey" FOREIGN KEY ("residentId") REFERENCES "resident"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "PopulationDynamic" ADD CONSTRAINT "PopulationDynamic_documentedBy_fkey" FOREIGN KEY ("documentedBy") REFERENCES "user"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "Budget" ADD CONSTRAINT "Budget_approvedBy_fkey" FOREIGN KEY ("approvedBy") REFERENCES "user"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "BudgetTransaction" ADD CONSTRAINT "BudgetTransaction_createdBy_fkey" FOREIGN KEY ("createdBy") REFERENCES "user"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "Umkm" ADD CONSTRAINT "Umkm_banjarId_fkey" FOREIGN KEY ("banjarId") REFERENCES "banjar"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "Posyandu" ADD CONSTRAINT "Posyandu_coordinatorId_fkey" FOREIGN KEY ("coordinatorId") REFERENCES "user"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "SecurityReport" ADD CONSTRAINT "SecurityReport_assignedTo_fkey" FOREIGN KEY ("assignedTo") REFERENCES "user"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "session" ADD CONSTRAINT "session_userId_fkey" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "account" ADD CONSTRAINT "account_userId_fkey" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "api_key" ADD CONSTRAINT "api_key_userId_fkey" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE CASCADE;

View File

@@ -0,0 +1,3 @@
# Please do not edit this file manually
# It should be added in your version-control system (e.g., Git)
provider = "postgresql"

View File

@@ -21,9 +21,459 @@ model User {
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[]
budgets Budget[]
budgetTransactions BudgetTransaction[]
posyandus Posyandu[]
securityReports SecurityReport[]
@@map("user")
}
// --- KATEGORI 1: KINERJA DIVISI & AKTIVITAS ---
model Division {
id String @id @default(cuid())
name String @unique
description String?
color String @default("#1E3A5F")
isActive Boolean @default(true)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
activities Activity[]
documents Document[]
discussions Discussion[]
divisionMetrics DivisionMetric[]
@@map("division")
}
model Activity {
id String @id @default(cuid())
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())
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())
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())
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")
}
// --- 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])
}
model EmploymentRecord {
id String @id @default(cuid())
residentId String
resident Resident @relation(fields: [residentId], references: [id])
}
model PopulationDynamic {
id String @id @default(cuid())
documentedBy String
documentor User @relation(fields: [documentedBy], references: [id])
}
model Budget {
id String @id @default(cuid())
approvedBy String?
approver User? @relation(fields: [approvedBy], references: [id])
}
model BudgetTransaction {
id String @id @default(cuid())
createdBy String
creator User @relation(fields: [createdBy], references: [id])
}
model Umkm {
id String @id @default(cuid())
banjarId String?
banjar Banjar? @relation(fields: [banjarId], references: [id])
}
model Posyandu {
id String @id @default(cuid())
coordinatorId String?
coordinator User? @relation(fields: [coordinatorId], references: [id])
}
model SecurityReport {
id String @id @default(cuid())
assignedTo String?
assignee User? @relation(fields: [assignedTo], references: [id])
}
// --- 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

View File

@@ -1,150 +1,325 @@
import "dotenv/config";
import { hash } from "bcryptjs";
import { generateId } from "better-auth";
import { prisma } from "@/utils/db";
import {
ActivityStatus,
ComplaintCategory,
ComplaintStatus,
EventType,
Gender,
Priority,
PrismaClient,
Religion,
} from "../generated/prisma";
const prisma = new PrismaClient();
async function seedAdminUser() {
// Load environment variables
const adminEmail = process.env.ADMIN_EMAIL;
const adminEmail = process.env.ADMIN_EMAIL || "admin@example.com";
const adminPassword = process.env.ADMIN_PASSWORD || "admin123";
if (!adminEmail) {
console.log(
"No ADMIN_EMAIL environment variable found. Skipping admin user creation.",
);
return;
console.log(`Checking admin user: ${adminEmail}`);
const existingUser = await prisma.user.findUnique({
where: { email: adminEmail },
});
if (existingUser) {
if (existingUser.role !== "admin") {
await prisma.user.update({
where: { email: adminEmail },
data: { role: "admin" },
});
console.log("Updated existing user to admin role.");
}
return existingUser.id;
}
try {
// Check if admin user already exists
const existingUser = await prisma.user.findUnique({
where: { email: adminEmail },
});
const hashedPassword = await hash(adminPassword, 12);
const userId = generateId();
if (existingUser) {
// Update existing user to have admin role if they don't already
if (existingUser.role !== "admin") {
await prisma.user.update({
where: { email: adminEmail },
data: { role: "admin" },
});
console.log(`User with email ${adminEmail} updated to admin role.`);
} else {
console.log(`User with email ${adminEmail} already has admin role.`);
}
} else {
// Create new admin user
const hashedPassword = await hash(adminPassword, 12);
const userId = generateId();
await prisma.user.create({
data: {
id: userId,
email: adminEmail,
name: "Admin User",
role: "admin",
emailVerified: true,
createdAt: new Date(),
updatedAt: new Date(),
},
});
await prisma.account.create({
data: {
await prisma.user.create({
data: {
id: userId,
email: adminEmail,
name: "Admin Desa Darmasaba",
role: "admin",
emailVerified: true,
accounts: {
create: {
id: generateId(),
userId,
accountId: userId,
providerId: "credential",
password: hashedPassword,
createdAt: new Date(),
updatedAt: new Date(),
},
});
},
},
});
console.log(`Admin user created with email: ${adminEmail}`);
}
} catch (error) {
console.error("Error seeding admin user:", error);
throw error;
}
console.log(`Admin user created: ${adminEmail}`);
return userId;
}
async function seedDemoUsers() {
const demoUsers = [
{ email: "demo1@example.com", name: "Demo User 1", role: "user" },
{ email: "demo2@example.com", name: "Demo User 2", role: "user" },
async function seedBanjars() {
const banjars = [
{
email: "moderator@example.com",
name: "Moderator User",
role: "moderator",
name: "Darmasaba",
code: "DSB",
totalPopulation: 1200,
totalKK: 300,
totalPoor: 45,
},
{
name: "Manesa",
code: "MNS",
totalPopulation: 950,
totalKK: 240,
totalPoor: 32,
},
{
name: "Cabe",
code: "CBE",
totalPopulation: 800,
totalKK: 200,
totalPoor: 28,
},
{
name: "Penenjoan",
code: "PNJ",
totalPopulation: 1100,
totalKK: 280,
totalPoor: 50,
},
{
name: "Baler Pasar",
code: "BPS",
totalPopulation: 850,
totalKK: 210,
totalPoor: 35,
},
{
name: "Bucu",
code: "BCU",
totalPopulation: 734,
totalKK: 184,
totalPoor: 24,
},
];
for (const userData of demoUsers) {
try {
const existingUser = await prisma.user.findUnique({
where: { email: userData.email },
});
console.log("Seeding Banjars...");
for (const banjar of banjars) {
await prisma.banjar.upsert({
where: { name: banjar.name },
update: banjar,
create: banjar,
});
}
}
if (!existingUser) {
const userId = generateId();
const hashedPassword = await hash("demo123", 12);
async function seedDivisions() {
const divisions = [
{
name: "Pemerintahan",
description: "Urusan administrasi dan tata kelola desa",
color: "#1E3A5F",
},
{
name: "Pembangunan",
description: "Infrastruktur dan sarana prasarana desa",
color: "#2E7D32",
},
{
name: "Pemberdayaan",
description: "Pemberdayaan ekonomi dan masyarakat",
color: "#EF6C00",
},
{
name: "Kesejahteraan",
description: "Kesehatan, pendidikan, dan sosial",
color: "#C62828",
},
];
await prisma.user.create({
data: {
id: userId,
email: userData.email,
name: userData.name,
role: userData.role,
emailVerified: true,
createdAt: new Date(),
updatedAt: new Date(),
},
});
console.log("Seeding Divisions...");
const createdDivisions = [];
for (const div of divisions) {
const d = await prisma.division.upsert({
where: { name: div.name },
update: div,
create: div,
});
createdDivisions.push(d);
}
return createdDivisions;
}
await prisma.account.create({
data: {
id: generateId(),
userId,
accountId: userId,
providerId: "credential",
password: hashedPassword,
createdAt: new Date(),
updatedAt: new Date(),
},
});
async function seedResidents(banjarIds: string[]) {
console.log("Seeding Residents...");
const residents = [
{
nik: "5103010101700001",
kk: "5103010101700000",
name: "I Wayan Sudarsana",
birthDate: new Date("1970-05-15"),
birthPlace: "Badung",
gender: Gender.LAKI_LAKI,
religion: Religion.HINDU,
occupation: "Wiraswasta",
banjarId: banjarIds[0],
rt: "001",
rw: "000",
address: "Jl. Raya Darmasaba No. 1",
isHeadOfHousehold: true,
},
{
nik: "5103010101850002",
kk: "5103010101850000",
name: "Ni Made Arianti",
birthDate: new Date("1985-08-20"),
birthPlace: "Denpasar",
gender: Gender.PEREMPUAN,
religion: Religion.HINDU,
occupation: "Guru",
banjarId: banjarIds[1],
rt: "002",
rw: "000",
address: "Gg. Manesa No. 5",
isPoor: true,
},
];
console.log(`Demo user created: ${userData.email}`);
} else {
console.log(`Demo user already exists: ${userData.email}`);
}
} catch (error) {
console.error(`Error seeding user ${userData.email}:`, error);
}
for (const res of residents) {
await prisma.resident.upsert({
where: { nik: res.nik },
update: res,
create: res,
});
}
}
async function seedActivities(divisionIds: string[]) {
console.log("Seeding Activities...");
const activities = [
{
title: "Rapat Koordinasi 2025",
description: "Penyusunan rencana kerja tahunan",
divisionId: divisionIds[0],
progress: 100,
status: ActivityStatus.SELESAI,
priority: Priority.TINGGI,
},
{
title: "Pemutakhiran Indeks Desa",
description: "Pendataan SDG's Desa 2025",
divisionId: divisionIds[0],
progress: 65,
status: ActivityStatus.BERJALAN,
priority: Priority.SEDANG,
},
{
title: "Pembangunan Jalan Banjar Cabe",
description: "Pengaspalan jalan utama",
divisionId: divisionIds[1],
progress: 40,
status: ActivityStatus.BERJALAN,
priority: Priority.DARURAT,
},
];
for (const act of activities) {
await prisma.activity.create({
data: act,
});
}
}
async function seedComplaints(adminId: string) {
console.log("Seeding Complaints...");
const complaints = [
{
complaintNumber: `COMP-20250326-001`,
title: "Lampu Jalan Mati",
description:
"Lampu jalan di depan Balai Banjar Manesa mati sejak 3 hari lalu.",
category: ComplaintCategory.INFRASTRUKTUR,
status: ComplaintStatus.BARU,
priority: Priority.SEDANG,
location: "Banjar Manesa",
reporterId: adminId,
},
{
complaintNumber: `COMP-20250326-002`,
title: "Sampah Menumpuk",
description: "Tumpukan sampah di area pasar Darmasaba belum diangkut.",
category: ComplaintCategory.KETERTIBAN_UMUM,
status: ComplaintStatus.DIPROSES,
priority: Priority.TINGGI,
location: "Pasar Darmasaba",
assignedTo: adminId,
},
];
for (const comp of complaints) {
await prisma.complaint.upsert({
where: { complaintNumber: comp.complaintNumber },
update: comp,
create: comp,
});
}
}
async function seedEvents(adminId: string) {
console.log("Seeding Events...");
const events = [
{
title: "Rapat Pleno Desa",
description: "Pembahasan anggaran belanja desa",
eventType: EventType.RAPAT,
startDate: new Date(),
location: "Balai Desa Darmasaba",
createdBy: adminId,
},
{
title: "Gotong Royong Kebersihan",
description: "Kegiatan rutin mingguan",
eventType: EventType.SOSIAL,
startDate: new Date(Date.now() + 86400000), // Besok
location: "Seluruh Banjar",
createdBy: adminId,
},
];
for (const event of events) {
await prisma.event.create({
data: event,
});
}
}
async function main() {
console.log("Seeding database...");
console.log("Starting seed...");
await seedAdminUser();
await seedDemoUsers();
const adminId = await seedAdminUser();
await seedBanjars();
const banjars = await prisma.banjar.findMany();
const banjarIds = banjars.map((b) => b.id);
console.log("Database seeding completed.");
const divisions = await seedDivisions();
const divisionIds = divisions.map((d) => d.id);
await seedResidents(banjarIds);
await seedActivities(divisionIds);
await seedComplaints(adminId);
await seedEvents(adminId);
console.log("Seed finished successfully!");
}
// Only auto-execute when run directly (not when imported)
const isMainModule =
typeof require !== "undefined"
? require.main === module
: import.meta.path.endsWith("seed.ts");
if (isMainModule) {
main().catch((error) => {
console.error("Error during seeding:", error);
main()
.catch((e) => {
console.error(e);
process.exit(1);
})
.finally(async () => {
await prisma.$disconnect();
});
}
// Export for programmatic use
export { seedAdminUser, seedDemoUsers, main as runSeed };