feat(keamanan): tambah modul CCTV — schema, API, admin UI, seeder

- Tambah model CctvKeamanan + enum StatusCctv ke prisma schema
- Tambah status Baru ke enum StatusLaporan
- Migration: add_cctv_keamanan_model
- API CRUD + stats endpoint di /api/keamanan/cctv/...
- Admin state (valtio proxy) dengan create/findMany/edit/delete/stats
- Admin pages: list, create, detail (peta Leaflet), edit (peta picker)
- Seeder 8 data CCTV lokasi Darmasaba
- Tambah submenu CCTV di sidebar nav keamanan
- Bump version 0.1.57 → 0.1.58

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-06 16:40:31 +08:00
parent 60841039dd
commit 936dd14ca9
20 changed files with 1398 additions and 1 deletions

View File

@@ -0,0 +1,35 @@
import prisma from "@/lib/prisma";
import { loadJsonData } from "../../load-json";
const cctvData = loadJsonData("keamanan/cctv/cctv.json");
export async function seedCctv() {
console.log("🔄 Seeding CCTV Keamanan...");
for (const c of cctvData) {
await prisma.cctvKeamanan.upsert({
where: { id: c.id },
update: {
kode: c.kode,
nama: c.nama,
lokasi: c.lokasi,
latitude: c.latitude ?? null,
longitude: c.longitude ?? null,
status: c.status,
lastActive: new Date(c.lastActive),
},
create: {
id: c.id,
kode: c.kode,
nama: c.nama,
lokasi: c.lokasi,
latitude: c.latitude ?? null,
longitude: c.longitude ?? null,
status: c.status,
lastActive: new Date(c.lastActive),
},
});
}
console.log(`✅ CCTV Keamanan seeded: ${cctvData.length} data`);
}

View File

@@ -0,0 +1,82 @@
[
{
"id": "cctv_darmasaba_01",
"kode": "CCTV-01",
"nama": "Balai Desa",
"lokasi": "Jl. Raya Darmasaba, Depan Balai Desa",
"latitude": -8.5712,
"longitude": 115.1923,
"status": "Online",
"lastActive": "2026-02-12T14:30:00.000Z"
},
{
"id": "cctv_darmasaba_02",
"kode": "CCTV-02",
"nama": "Pintu Masuk Desa Utara",
"lokasi": "Jl. Raya Darmasaba, Pintu Masuk Utara",
"latitude": -8.5685,
"longitude": 115.1917,
"status": "Online",
"lastActive": "2026-02-12T13:45:00.000Z"
},
{
"id": "cctv_darmasaba_03",
"kode": "CCTV-03",
"nama": "Taman Desa",
"lokasi": "Area Taman Desa Darmasaba",
"latitude": -8.5730,
"longitude": 115.1935,
"status": "Offline",
"lastActive": "2026-02-11T09:00:00.000Z"
},
{
"id": "cctv_darmasaba_04",
"kode": "CCTV-04",
"nama": "Pasar Desa",
"lokasi": "Pasar Tradisional Darmasaba",
"latitude": -8.5698,
"longitude": 115.1945,
"status": "Online",
"lastActive": "2026-02-12T15:00:00.000Z"
},
{
"id": "cctv_darmasaba_05",
"kode": "CCTV-05",
"nama": "Pintu Masuk Desa Selatan",
"lokasi": "Jl. Raya Darmasaba, Pintu Masuk Selatan",
"latitude": -8.5755,
"longitude": 115.1920,
"status": "Online",
"lastActive": "2026-02-12T14:55:00.000Z"
},
{
"id": "cctv_darmasaba_06",
"kode": "CCTV-06",
"nama": "SD Negeri Darmasaba",
"lokasi": "Depan SD Negeri 1 Darmasaba",
"latitude": -8.5720,
"longitude": 115.1910,
"status": "Online",
"lastActive": "2026-02-12T12:30:00.000Z"
},
{
"id": "cctv_darmasaba_07",
"kode": "CCTV-07",
"nama": "Pura Desa",
"lokasi": "Area Pura Desa Darmasaba",
"latitude": -8.5708,
"longitude": 115.1950,
"status": "Offline",
"lastActive": "2026-02-10T18:00:00.000Z"
},
{
"id": "cctv_darmasaba_08",
"kode": "CCTV-08",
"nama": "Persimpangan Utama",
"lokasi": "Persimpangan Jl. Raya Darmasaba - Jl. Abiansemal",
"latitude": -8.5695,
"longitude": 115.1930,
"status": "Online",
"lastActive": "2026-02-12T15:10:00.000Z"
}
]

View File

@@ -0,0 +1,23 @@
-- CreateEnum
CREATE TYPE "StatusCctv" AS ENUM ('Online', 'Offline');
-- AlterEnum
ALTER TYPE "StatusLaporan" ADD VALUE 'Baru';
-- CreateTable
CREATE TABLE "CctvKeamanan" (
"id" TEXT NOT NULL,
"kode" TEXT NOT NULL,
"nama" TEXT NOT NULL,
"lokasi" TEXT NOT NULL,
"latitude" DOUBLE PRECISION,
"longitude" DOUBLE PRECISION,
"status" "StatusCctv" NOT NULL DEFAULT 'Online',
"lastActive" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
"deletedAt" TIMESTAMP(3),
"isActive" BOOLEAN NOT NULL DEFAULT true,
CONSTRAINT "CctvKeamanan_pkey" PRIMARY KEY ("id")
);

View File

@@ -1395,11 +1395,33 @@ model PenangananLaporanPublik {
}
enum StatusLaporan {
Baru
Selesai
Proses
Gagal
}
// ========================================= CCTV KEAMANAN ========================================= //
enum StatusCctv {
Online
Offline
}
model CctvKeamanan {
id String @id @default(cuid())
kode String // e.g. "CCTV-01"
nama String // e.g. "Balai Desa"
lokasi String
latitude Float?
longitude Float?
status StatusCctv @default(Online)
lastActive DateTime @default(now())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime?
isActive Boolean @default(true)
}
model Pelapor {
id String @id @default(cuid())
nama String

View File

@@ -32,6 +32,7 @@ import { seedInfoTeknologi } from "./_seeder_list/inovasi/seed_info_teknologi";
import { seedKolaborasiInovasi } from "./_seeder_list/inovasi/seed_kolaborasi_inovasi";
import { seedLayananOnlineDesa } from "./_seeder_list/inovasi/seed_layanan_online_desa";
import { seedProgramKreatifDesa } from "./_seeder_list/inovasi/seed_program_kreatif_desa";
import { seedCctv } from "./_seeder_list/keamanan/seed_cctv";
import { seedKeamananLingkungan } from "./_seeder_list/keamanan/seed_keamanan_lingkungan";
import { seedKontakDaruratKeamanan } from "./_seeder_list/keamanan/seed_kontak_darurat";
import { seedLaporanPublik } from "./_seeder_list/keamanan/seed_laporan_publik";
@@ -280,6 +281,8 @@ import seedAssets from "./seed_assets";
await seedPencegahanKriminalitas();
// // ==================== SUBMENU LAPORAN PUBLIK =================
await seedLaporanPublik();
// // ==================== SUBMENU CCTV KEAMANAN ==================
await seedCctv();
// // ==================== SUBMENU TIPS KEAMANAN ==================
await seedKeamananLingkungan();