Add Layout Kontak Darurat - Admin Menu Keamanan

This commit is contained in:
2025-09-11 12:15:40 +08:00
parent a9d98895bb
commit 6a7bd386ae
42 changed files with 2973 additions and 1257 deletions

View File

@@ -6,6 +6,7 @@ import MenuTipsKeamanan from "./tips-keamanan";
import LaporanPublik from "./laporan-publik";
import LayananPolsek from "./layanan-polsek";
import KontakDaruratKeamanan from "./kontak-darurat-keamanan";
import KontakItem from "./kontak-darurat-keamanan/kontak-item";
const Keamanan = new Elysia({ prefix: "/api/keamanan", tags: ["Keamanan"] })
.use(KeamananLingkungan)
@@ -15,4 +16,5 @@ const Keamanan = new Elysia({ prefix: "/api/keamanan", tags: ["Keamanan"] })
.use(LaporanPublik)
.use(LayananPolsek)
.use(KontakDaruratKeamanan)
.use(KontakItem)
export default Keamanan;

View File

@@ -2,34 +2,55 @@ import prisma from "@/lib/prisma";
import { Context } from "elysia";
type FormCreate = {
nama: string,
imageId?: string,
kontakItems: {
nama: string,
nomorTelepon: string,
imageId?: string
}[]
}
export default async function kontakDaruratKeamananCreate(context: Context){
const body = context.body as FormCreate
nama: string;
imageId?: string;
kategoriId: string[];
};
export default async function kontakDaruratKeamananCreate(context: Context) {
const body = context.body as FormCreate;
await prisma.kontakDaruratKeamanan.create({
if (!body.kategoriId || body.kategoriId.length === 0) {
throw new Error("At least one kategoriId is required");
}
try {
const result = await prisma.$transaction(async (prisma) => {
const kontakDaruratKeamanan = await prisma.kontakDaruratKeamanan.create({
data: {
nama: body.nama,
imageId: body.imageId,
kontakItems: {
create: body.kontakItems,
},
nama: body.nama,
imageId: body.imageId,
kategoriId: body.kategoriId[0],
},
});
await prisma.kontakDaruratToItem.createMany({
data: body.kategoriId.map((kategoriId) => ({
kontakDaruratId: kontakDaruratKeamanan.id,
kontakItemId: kategoriId,
})),
});
return await prisma.kontakDaruratKeamanan.findUnique({
where: { id: kontakDaruratKeamanan.id },
include: {
kontakItems: true,
}
})
return {
success: true,
message: "Success create kontak darurat keamanan",
data: {
...body,
image: true,
kategori: true,
kontakItems: {
include: {
kontakItem: true,
},
},
},
}
}
});
});
return {
success: true,
message: "Success create kontak darurat keamanan",
data: result,
};
} catch (error) {
console.error("Error creating KontakDaruratKeamanan:", error);
throw new Error("Failed to create KontakDaruratKeamanan: " + (error as Error).message);
}
}

View File

@@ -3,56 +3,56 @@ import { Context } from "elysia";
import fs from "fs/promises";
import path from "path";
export default async function kontakDaruratKeamananDelete(context: Context){
const id = context.params?.id as string;
export default async function kontakDaruratKeamananDelete(context: Context) {
const { params } = context;
const id = params?.id as string;
if (!id) {
return {
status: 400,
body: "ID tidak diberikan",
};
}
if (!id) {
throw new Error("ID tidak diberikan");
}
await prisma.kontakItem.deleteMany({
where: {
kategoriId: id,
},
});
await prisma.kontakDaruratToItem.deleteMany({
where: {
kontakDaruratId: id,
},
});
const kontakDaruratKeamanan = await prisma.kontakDaruratKeamanan.findUnique({
where: { id },
include: {
kontakItems: true,
image: true,
}
});
if (!kontakDaruratKeamanan) {
return {
status: 404,
body: "Kontak darurat keamanan tidak ditemukan",
};
}
if (kontakDaruratKeamanan.image) {
try {
const filePath = path.join(kontakDaruratKeamanan.image.path, kontakDaruratKeamanan.image.name);
await fs.unlink(filePath);
await prisma.fileStorage.delete({
where: { id: kontakDaruratKeamanan.image.id },
});
} catch (err) {
console.error("Gagal hapus file image:", err);
}
}
await prisma.kontakDaruratKeamanan.delete({
where: { id },
});
const kontakDaruratKeamanan = await prisma.kontakDaruratKeamanan.findUnique({
where: { id },
include: {
image: true,
},
});
if (!kontakDaruratKeamanan) {
return {
status: 200,
success: true,
message: "Kontak darurat keamanan berhasil dihapus",
status: 404,
body: "Kontak darurat keamanan tidak ditemukan",
};
}
}
if (kontakDaruratKeamanan.image) {
try {
const filePath = path.join(
kontakDaruratKeamanan.image.path,
kontakDaruratKeamanan.image.name
);
await fs.unlink(filePath);
await prisma.fileStorage.delete({
where: { id: kontakDaruratKeamanan.image.id },
});
} catch (err) {
console.error("Gagal hapus file image:", err);
}
}
await prisma.kontakDaruratKeamanan.delete({
where: { id },
});
return {
status: 200,
success: true,
message: "Kontak darurat keamanan berhasil dihapus",
};
}

View File

@@ -1,27 +1,56 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import prisma from "@/lib/prisma";
import { Context } from "elysia";
export default async function kontakDaruratKeamananFindMany() {
try {
const data = await prisma.kontakDaruratKeamanan.findMany({
export default async function kontakDaruratKeamananFindMany(context: Context) {
// Ambil parameter dari query
const page = Number(context.query.page) || 1;
const limit = Number(context.query.limit) || 10;
const search = (context.query.search as string) || "";
const skip = (page - 1) * limit;
// Buat where clause
const where: any = { isActive: true };
// Tambahkan pencarian (jika ada)
if (search) {
where.OR = [{ nama: { contains: search, mode: "insensitive" } }];
}
try {
const [data, total] = await Promise.all([
prisma.kontakDaruratKeamanan.findMany({
include: {
kontakItems: {
include: {
kontakItems: true,
image: true,
kontakItem: true,
},
orderBy: {
createdAt: "desc",
}
});
},
image: true,
kategori: true,
},
skip,
take: limit,
orderBy: { createdAt: "desc" },
}),
prisma.kontakDaruratKeamanan.count({ where }),
]);
return {
success: true,
message: "Success fetch kontak darurat keamanan",
data,
};
} catch (e) {
console.error("Find many error:", e);
return {
success: false,
message: "Failed fetch kontak darurat keamanan",
};
}
}
return {
success: true,
message: "Berhasil ambil kontak darurat keamanan dengan pagination",
data,
page,
limit,
total,
totalPages: Math.ceil(total / limit),
};
} catch (e) {
console.error("Find many error:", e);
return {
success: false,
message: "Failed fetch kontak darurat keamanan",
};
}
}

View File

@@ -1,39 +1,27 @@
import prisma from "@/lib/prisma";
import { Context } from "elysia";
export default async function kontakDaruratKeamananFindUnique(
request: Request
context: Context
) {
const url = new URL(request.url);
const pathSegments = url.pathname.split("/");
const id = pathSegments[pathSegments.length - 1];
const { params } = context;
const id = params?.id as string;
if (!id) {
return Response.json(
{
success: false,
message: "ID tidak boleh kosong",
},
{ status: 400 }
);
throw new Error("ID tidak ditemukan dalam parameter");
}
try {
if (typeof id !== "string") {
return Response.json(
{
success: false,
message: "ID tidak valid",
},
{ status: 400 }
);
}
const data = await prisma.kontakDaruratKeamanan.findUnique({
where: { id },
include: {
kontakItems: {
include: {
image: true,
kontakItem: {
include: {
image: true
}
},
},
},
image: true,
@@ -41,33 +29,12 @@ export default async function kontakDaruratKeamananFindUnique(
});
if (!data) {
return Response.json(
{
success: false,
message: "Kontak darurat keamanan tidak ditemukan",
},
{ status: 404 }
);
throw new Error("Pasar desa tidak ditemukan");
}
return Response.json(
{
success: true,
message: "Success fetch kontak darurat keamanan by ID",
data,
},
{ status: 200 }
);
} catch (error) {
console.error("Find by ID error:", error);
return Response.json(
{
success: false,
message:
"Gagal mengambil kontak darurat keamanan: " +
(error instanceof Error ? error.message : "Unknown error"),
},
{ status: 500 }
);
}
return {
success: true,
message: "Data kontak darurat keamanan ditemukan",
data,
};
}

View File

@@ -9,24 +9,23 @@ const KontakDaruratKeamanan = new Elysia({
prefix: "/kontakdaruratkeamanan",
tags: ["Keamanan/Kontak Darurat"],
})
.get("/find-many", kontakDaruratKeamananFindMany)
.get("/:id", async (context) => {
const response = await kontakDaruratKeamananFindUnique(
new Request(context.request)
);
return response;
})
.get("/findMany", kontakDaruratKeamananFindMany)
.get(
"/:id",
async (context) => {
return await kontakDaruratKeamananFindUnique(context);
},
{
params: t.Object({
id: t.String(),
}),
}
)
.post("/create", kontakDaruratKeamananCreate, {
body: t.Object({
nama: t.String(),
imageId: t.Optional(t.String()),
kontakItems: t.Array(
t.Object({
nama: t.String(),
nomorTelepon: t.String(),
imageId: t.Optional(t.String()),
})
),
kategoriId: t.Array(t.String()),
}),
})
.delete("/del/:id", kontakDaruratKeamananDelete)
@@ -37,16 +36,13 @@ const KontakDaruratKeamanan = new Elysia({
return response;
},
{
params: t.Object({
id: t.String(),
}),
body: t.Object({
nama: t.String(),
imageId: t.Optional(t.String()),
kontakItems: t.Array(
t.Object({
nama: t.String(),
nomorTelepon: t.String(),
imageId: t.Optional(t.String()),
})
),
kategoriId: t.Array(t.String()),
}),
}
);

View File

@@ -0,0 +1,26 @@
import prisma from "@/lib/prisma";
import { Context } from "elysia";
type FormCreate = {
nama: string;
nomorTelepon: string;
imageId?: string;
};
export default async function kontakItemCreate(context: Context){
const body = context.body as FormCreate
await prisma.kontakItem.create({
data: {
nama: body.nama,
nomorTelepon: body.nomorTelepon,
imageId: body.imageId,
},
})
return {
success: true,
message: "Success create kontak item",
data: {
...body,
},
}
}

View File

@@ -0,0 +1,51 @@
import prisma from "@/lib/prisma";
import { Context } from "elysia";
import fs from "fs/promises";
import path from "path";
export default async function kontakItemDelete(context: Context){
const id = context.params?.id as string;
if (!id) {
return {
status: 400,
body: "ID tidak diberikan",
};
}
const kontakItem = await prisma.kontakItem.findUnique({
where: { id },
include: {
image: true
}
});
if (!kontakItem) {
return {
status: 404,
body: "Kontak darurat keamanan tidak ditemukan",
};
}
if (kontakItem.image) {
try {
const filePath = path.join(kontakItem.image.path, kontakItem.image.name);
await fs.unlink(filePath);
await prisma.fileStorage.delete({
where: { id: kontakItem.image.id },
});
} catch (err) {
console.error("Gagal hapus file image:", err);
}
}
await prisma.kontakItem.delete({
where: { id },
});
return {
status: 200,
success: true,
message: "Kontak item berhasil dihapus",
};
}

View File

@@ -0,0 +1,51 @@
import prisma from "@/lib/prisma";
import { Context } from "elysia";
import { Prisma } from "@prisma/client";
export default async function kontakItemFindMany(context: Context) {
// Ambil parameter dari query
const page = Number(context.query.page) || 1;
const limit = Number(context.query.limit) || 10;
const search = (context.query.search as string) || "";
const skip = (page - 1) * limit;
// Buat where clause dengan tipe yang benar
const where: Prisma.KontakItemWhereInput = { isActive: true };
// Tambahkan pencarian (jika ada)
if (search) {
where.OR = [
{ nama: { contains: search, mode: "insensitive" as const } },
];
}
try {
const [data, total] = await Promise.all([
prisma.kontakItem.findMany({
include: {
image: true,
},
skip,
take: limit,
orderBy: { createdAt: "desc" },
}),
prisma.kontakItem.count({ where }),
]);
return {
success: true,
message: "Berhasil ambil kontak item dengan pagination",
data,
page,
limit,
total,
totalPages: Math.ceil(total / limit),
};
} catch (e) {
console.error("Find many error:", e);
return {
success: false,
message: "Failed fetch kontak item",
};
}
}

View File

@@ -0,0 +1,68 @@
import prisma from "@/lib/prisma";
export default async function kontakItemFindUnique(
request: Request
) {
const url = new URL(request.url);
const pathSegments = url.pathname.split("/");
const id = pathSegments[pathSegments.length - 1];
if (!id) {
return Response.json(
{
success: false,
message: "ID tidak boleh kosong",
},
{ status: 400 }
);
}
try {
if (typeof id !== "string") {
return Response.json(
{
success: false,
message: "ID tidak valid",
},
{ status: 400 }
);
}
const data = await prisma.kontakItem.findUnique({
where: { id },
include: {
image: true,
},
});
if (!data) {
return Response.json(
{
success: false,
message: "Kontak item tidak ditemukan",
},
{ status: 404 }
);
}
return Response.json(
{
success: true,
message: "Success fetch kontak item by ID",
data,
},
{ status: 200 }
);
} catch (error) {
console.error("Find by ID error:", error);
return Response.json(
{
success: false,
message:
"Gagal mengambil kontak item: " +
(error instanceof Error ? error.message : "Unknown error"),
},
{ status: 500 }
);
}
}

View File

@@ -0,0 +1,40 @@
import Elysia, { t } from "elysia";
import kontakItemCreate from "./create";
import kontakItemDelete from "./del";
import kontakItemFindMany from "./findMany";
import kontakItemFindUnique from "./findUnique";
import kontakItemUpdate from "./updt";
const KontakItem = new Elysia({
prefix: "/kontakitem",
tags: ["Keamanan/Kontak Item"],
})
.get("/find-many", kontakItemFindMany)
.get("/:id", async (context) => {
const response = await kontakItemFindUnique(new Request(context.request));
return response;
})
.post("/create", kontakItemCreate, {
body: t.Object({
nama: t.String(),
imageId: t.Optional(t.String()),
nomorTelepon: t.String(),
}),
})
.delete("/del/:id", kontakItemDelete)
.put(
"/:id",
async (context) => {
const response = await kontakItemUpdate(context);
return response;
},
{
body: t.Object({
nama: t.String(),
imageId: t.Optional(t.String()),
nomorTelepon: t.String(),
}),
}
);
export default KontakItem;

View File

@@ -0,0 +1,44 @@
import prisma from "@/lib/prisma";
import { Context } from "elysia";
type FormUpdate = {
nama: string;
nomorTelepon: string;
imageId?: string;
};
export default async function kontakItemUpdate(context: Context){
try {
const { id } = context.params as { id: string };
const body = context.body as FormUpdate;
// Update utama
const updated = await prisma.kontakItem.update({
where: { id },
data: {
nama: body.nama,
nomorTelepon: body.nomorTelepon,
imageId: body.imageId,
},
include: {
image: true,
},
});
return {
success: true,
message: "Success update kontak item",
data: updated,
};
} catch (error) {
console.error("Error updating kontak item:", error);
return Response.json({
success: false,
message: "Terjadi kesalahan saat mengupdate kontak item",
}, { status: 500 ,
headers: {
"Content-Type": "application/json",
},
});
}
}

View File

@@ -2,56 +2,59 @@ import prisma from "@/lib/prisma";
import { Context } from "elysia";
type FormUpdate = {
nama: string;
imageId?: string;
kontakItems: {
nama: string;
nomorTelepon: string;
imageId?: string;
}[];
};
id: string;
nama: string;
imageId?: string;
kategoriId: string[];
};
export default async function kontakDaruratKeamananUpdate(context: Context){
try {
const { id } = context.params as { id: string };
const body = context.body as FormUpdate;
// Hapus kontakItems lama
await prisma.kontakItem.deleteMany({
where: {
kategoriId: id,
},
});
// Update utama
const updated = await prisma.kontakDaruratKeamanan.update({
where: { id },
data: {
nama: body.nama,
imageId: body.imageId,
kontakItems: {
create: body.kontakItems,
},
},
include: {
kontakItems: true,
},
});
return {
success: true,
message: "Success update kontak darurat keamanan",
data: updated,
};
} catch (error) {
console.error("Error updating kontak darurat keamanan:", error);
return Response.json({
success: false,
message: "Terjadi kesalahan saat mengupdate kontak darurat keamanan",
}, { status: 500 ,
headers: {
"Content-Type": "application/json",
},
});
}
}
export default async function kontakDaruratKeamananUpdate(context: Context) {
const body = context.body as FormUpdate;
if (!body.id) {
throw new Error("ID pasar desa tidak boleh kosong");
}
if (!body.kategoriId || body.kategoriId.length === 0) {
throw new Error("Minimal 1 kategori harus dipilih");
}
await prisma.kontakDaruratKeamanan.update({
where: { id: body.id },
data: {
nama: body.nama,
imageId: body.imageId,
kategoriId: body.kategoriId[0],
},
});
await prisma.kontakDaruratToItem.deleteMany({
where: { kontakDaruratId: body.id },
});
await prisma.kontakDaruratToItem.createMany({
data: body.kategoriId.map((kategoriId) => ({
kontakDaruratId: body.id,
kontakItemId: kategoriId,
})),
});
const updated = await prisma.kontakDaruratKeamanan.findUnique({
where: { id: body.id },
include: {
image: true,
kategori: true,
kontakItems: {
include: {
kontakItem: true,
},
},
},
});
return {
success: true,
message: "Success update kontak darurat keamanan",
data: updated,
};
}

View File

@@ -1,27 +1,66 @@
import prisma from "@/lib/prisma";
import { Prisma } from "@prisma/client";
import { Context } from "elysia";
export default async function pencegahanKriminalitasFindMany() {
try {
const data = await prisma.pencegahanKriminalitas.findMany({
where: {
isActive: true,
},
include: {
programKeamanan: true,
tipsKeamanan: true,
videoKeamanan: true,
}
})
return {
success: true,
message: "Success fetch pencegahan kriminalitas",
data,
}
} catch (error) {
console.error("Find many error:", error);
return {
success: false,
message: "Failed fetch pencegahan kriminalitas",
}
}
}
export default async function pencegahanKriminalitasFindMany(context: Context) {
// Ambil parameter dari query
const page = Number(context.query.page) || 1;
const limit = Number(context.query.limit) || 10;
const search = (context.query.search as string) || "";
const skip = (page - 1) * limit;
// Buat where clause dengan tipe yang benar
const where: Prisma.PencegahanKriminalitasWhereInput = { isActive: true };
// Tambahkan pencarian (jika ada)
if (search) {
where.OR = [
{
programKeamanan: {
nama: { contains: search, mode: "insensitive" as const },
},
},
{
tipsKeamanan: {
judul: { contains: search, mode: "insensitive" as const },
},
},
{
videoKeamanan: {
judul: { contains: search, mode: "insensitive" as const },
},
},
];
}
try {
const [data, total] = await Promise.all([
prisma.pencegahanKriminalitas.findMany({
where,
include: {
programKeamanan: true,
tipsKeamanan: true,
videoKeamanan: true,
},
skip,
take: limit,
orderBy: { createdAt: "desc" },
}),
prisma.pencegahanKriminalitas.count({ where }),
]);
return {
success: true,
message: "Success fetch pencegahan kriminalitas",
data,
page,
limit,
total,
totalPages: Math.ceil(total / limit),
};
} catch (error) {
console.error("Find many error:", error);
return {
success: false,
message: "Failed fetch pencegahan kriminalitas",
};
}
}

View File

@@ -28,8 +28,8 @@ async function desaAntiKorupsiFindMany(context: Context) {
kategori: true,
file: true,
},
skip,
take: limit,
skip: limit === 0 ? undefined : skip,
take: limit === 0 ? undefined : limit,
orderBy: { name: "asc" }, // opsional, kalau mau urut berdasarkan waktu
}),
prisma.desaAntiKorupsi.count({