Merge pull request #39 from bipproduction/nico/4-jul-25

Nico/4 jul 25:
Fix UI & API Admin Menu Ekonomi Pasar Desa
This commit is contained in:
2025-07-04 11:20:15 +08:00
committed by GitHub
105 changed files with 6730 additions and 1162 deletions

View File

@@ -0,0 +1,16 @@
import Elysia from "elysia";
import PasarDesa from "./pasar-desa";
import LowonganKerja from "./lowongan-kerja";
import ProgramKemiskinan from "./program-kemiskinan";
import KategoriProduk from "./pasar-desa/kategori-produk";
const Ekonomi = new Elysia({
prefix: "/api/ekonomi",
tags: ["Ekonomi"],
})
.use(PasarDesa)
.use(KategoriProduk)
.use(LowonganKerja)
.use(ProgramKemiskinan)
export default Ekonomi

View File

@@ -0,0 +1,34 @@
import prisma from "@/lib/prisma";
import { Context } from "elysia";
type FormCreate = {
posisi: string;
namaPerusahaan: string;
lokasi: string;
tipePekerjaan: string;
gaji: string;
deskripsi: string;
kualifikasi: string;
}
export default async function lowonganKerjaCreate(context: Context) {
const body = context.body as FormCreate;
const lowonganKerja = await prisma.lowonganPekerjaan.create({
data: {
posisi: body.posisi,
namaPerusahaan: body.namaPerusahaan,
lokasi: body.lokasi,
tipePekerjaan: body.tipePekerjaan,
gaji: body.gaji,
deskripsi: body.deskripsi,
kualifikasi: body.kualifikasi,
},
});
return {
success: true,
message: "Success create lowongan kerja",
data: lowonganKerja,
};
}

View File

@@ -1,7 +1,7 @@
import prisma from "@/lib/prisma";
import { Context } from "elysia";
export default async function kontakDaruratKeamananDelete(context: Context){
const lowonganKerjaDelete = async (context: Context) => {
const id = context.params?.id as string;
if (!id) {
@@ -11,23 +11,24 @@ export default async function kontakDaruratKeamananDelete(context: Context){
};
}
const kontakDaruratKeamanan = await prisma.kontakDaruratKeamanan.findUnique({
const lowonganKerja = await prisma.lowonganPekerjaan.findUnique({
where: { id },
});
if (!kontakDaruratKeamanan) {
if (!lowonganKerja) {
return {
status: 404,
body: "Kontak darurat keamanan tidak ditemukan",
body: "Lowongan kerja tidak ditemukan",
};
}
await prisma.kontakDaruratKeamanan.delete({
await prisma.lowonganPekerjaan.delete({
where: { id },
});
return {
status: 200,
body: "Kontak darurat keamanan berhasil dihapus",
success: true,
message: "Lowongan kerja berhasil dihapus",
};
}
};
export default lowonganKerjaDelete;

View File

@@ -0,0 +1,21 @@
import prisma from "@/lib/prisma";
export default async function lowonganKerjaFindMany() {
try {
const data = await prisma.lowonganPekerjaan.findMany({
where: { isActive: true },
});
return {
success: true,
message: "Success fetch lowongan kerja",
data,
};
} catch (e) {
console.error("Find many error:", e);
return {
success: false,
message: "Failed fetch lowongan kerja",
};
}
}

View File

@@ -1,6 +1,6 @@
import prisma from "@/lib/prisma";
export default async function kontakDaruratKeamananFindUnique(request: Request){
export default async function lowonganKerjaFindUnique(request: Request){
const url = new URL(request.url);
const pathSegments = url.pathname.split('/');
const id = pathSegments[pathSegments.length - 1];
@@ -20,27 +20,31 @@ export default async function kontakDaruratKeamananFindUnique(request: Request){
}, { status: 400 });
}
const data = await prisma.kontakDaruratKeamanan.findUnique({
const data = await prisma.lowonganPekerjaan.findUnique({
where: { id },
});
if (!data) {
return Response.json({
success: false,
message: "Kontak darurat keamanan tidak ditemukan",
message: "Lowongan kerja tidak ditemukan",
}, { status: 404 });
}
return Response.json({
success: true,
message: "Success fetch kontak darurat keamanan by ID",
message: "Success fetch lowongan kerja by ID",
data,
}, { status: 200 });
} catch (error) {
console.error("Find by ID error:", error);
}, {
status: 200,
});
} catch (e) {
console.error("Find by ID error:", e);
return Response.json({
success: false,
message: "Gagal mengambil kontak darurat keamanan: " + (error instanceof Error ? error.message : 'Unknown error'),
}, { status: 500 });
message: "Gagal mengambil lowongan kerja: " + (e instanceof Error ? e.message : 'Unknown error'),
}, {
status: 500,
});
}
}

View File

@@ -0,0 +1,42 @@
import Elysia, { t } from "elysia";
import lowonganKerjaCreate from "./create";
import lowonganKerjaFindMany from "./findMany";
import lowonganKerjaFindUnique from "./findUnique";
import lowonganKerjaDelete from "./del";
import lowonganKerjaUpdate from "./updt";
const LowonganKerja = new Elysia({prefix: "/lowongankerja", tags: ["Ekonomi/Lowongan Kerja"]})
.post("/create", lowonganKerjaCreate, {
body: t.Object({
posisi: t.String(),
namaPerusahaan: t.String(),
lokasi: t.String(),
tipePekerjaan: t.String(),
gaji: t.String(),
deskripsi: t.String(),
kualifikasi: t.String(),
})
})
.get("/find-many", lowonganKerjaFindMany)
.get("/:id", async (context) => {
const response = await lowonganKerjaFindUnique(new Request(context.request));
return response;
})
.delete("/del/:id", lowonganKerjaDelete)
.put("/:id", async (context) => {
const response = await lowonganKerjaUpdate(context);
return response;
}, {
body: t.Object({
posisi: t.String(),
namaPerusahaan: t.String(),
lokasi: t.String(),
tipePekerjaan: t.String(),
gaji: t.String(),
deskripsi: t.String(),
kualifikasi: t.String(),
})
})
export default LowonganKerja;

View File

@@ -0,0 +1,64 @@
import prisma from "@/lib/prisma";
import { Context } from "elysia";
type FormUpdate = {
posisi: string;
namaPerusahaan: string;
lokasi: string;
tipePekerjaan: string;
gaji: string;
deskripsi: string;
kualifikasi: string;
}
export default async function lowonganKerjaUpdate(context: Context){
try {
const id = context.params?.id;
const body = context.body as FormUpdate;
const { posisi, namaPerusahaan, lokasi, tipePekerjaan, gaji, deskripsi, kualifikasi } = body;
if (!id) {
return Response.json({
success: false,
message: "ID tidak boleh kosong",
}, { status: 400 });
}
const existing = await prisma.lowonganPekerjaan.findUnique({
where: { id },
});
if (!existing) {
return Response.json({
success: false,
message: "Lowongan kerja tidak ditemukan",
}, { status: 404 });
}
const updated = await prisma.lowonganPekerjaan.update({
where: { id },
data: {
posisi,
namaPerusahaan,
lokasi,
tipePekerjaan,
gaji,
deskripsi,
kualifikasi,
},
});
return Response.json({
success: true,
message: "Success update lowongan kerja",
data: updated,
}, { status: 200 });
} catch (e) {
console.error("Update error:", e);
return Response.json({
success: false,
message: "Gagal mengupdate lowongan kerja: " + (e instanceof Error ? e.message : 'Unknown error'),
}, { status: 500 });
}
}

View File

@@ -0,0 +1,67 @@
import prisma from "@/lib/prisma";
import { Context } from "elysia";
type FormCreate = {
nama: string;
harga: number;
alamatUsaha: string;
imageId: string;
rating: number;
kategoriId: string[]; // Array of KategoriProduk IDs
};
export default async function pasarDesaCreate(context: Context) {
const body = context.body as FormCreate;
if (!body.kategoriId || body.kategoriId.length === 0) {
throw new Error("At least one kategoriId is required");
}
try {
// Start a transaction to ensure data consistency
const result = await prisma.$transaction(async (prisma) => {
// 1. Create PasarDesa with the first kategoriId as the main category
const pasarDesa = await prisma.pasarDesa.create({
data: {
nama: body.nama,
harga: Number(body.harga),
alamatUsaha: body.alamatUsaha,
imageId: body.imageId,
rating: Number(body.rating),
kategoriProdukId: body.kategoriId[0], // Use the first category as the main one
},
});
// 2. Create category relationships in KategoriToPasar for all categories
await prisma.kategoriToPasar.createMany({
data: body.kategoriId.map((kategoriId) => ({
pasarDesaId: pasarDesa.id,
kategoriId: kategoriId, // Note: The field is 'kategoriId' in the schema, not 'kategoriProdukId'
})),
});
// 3. Get the complete data with relationships
return await prisma.pasarDesa.findUnique({
where: { id: pasarDesa.id },
include: {
image: true,
kategoriProduk: true,
KategoriToPasar: {
include: {
kategori: true,
},
},
},
});
});
return {
success: true,
message: "Success create pasar desa",
data: result,
};
} catch (error) {
console.error("Error creating PasarDesa:", error);
throw new Error("Failed to create PasarDesa: " + (error as Error).message);
}
}

View File

@@ -0,0 +1,27 @@
import prisma from "@/lib/prisma";
import { Context } from "elysia";
export default async function pasarDesaDelete(context: Context) {
const { params } = context;
const id = params?.id as string;
if (!id) {
throw new Error("ID tidak ditemukan dalam parameter");
}
// 1. Hapus relasi dari pivot
await prisma.kategoriToPasar.deleteMany({
where: { pasarDesaId: id },
});
// 2. Hapus pasar desa utama
const deleted = await prisma.pasarDesa.delete({
where: { id },
});
return {
success: true,
message: "Berhasil menghapus pasar desa",
data: deleted,
};
}

View File

@@ -0,0 +1,26 @@
import prisma from "@/lib/prisma";
export default async function pasarDesaFindMany() {
const data = await prisma.pasarDesa.findMany({
where: {
isActive: true, // Opsional filter
},
orderBy: {
createdAt: "desc",
},
include: {
image: true,
KategoriToPasar: {
include: {
kategori: true,
},
},
},
});
return {
success: true,
message: "Berhasil mengambil semua data pasar desa",
data,
};
}

View File

@@ -0,0 +1,33 @@
import prisma from "@/lib/prisma";
import { Context } from "elysia";
export default async function pasarDesaFindUnique(context: Context) {
const { params } = context;
const id = params?.id as string;
if (!id) {
throw new Error("ID tidak ditemukan dalam parameter");
}
const data = await prisma.pasarDesa.findUnique({
where: { id },
include: {
image: true,
KategoriToPasar: {
include: {
kategori: true,
},
},
},
});
if (!data) {
throw new Error("Pasar desa tidak ditemukan");
}
return {
success: true,
message: "Data pasar desa ditemukan",
data,
};
}

View File

@@ -0,0 +1,86 @@
import Elysia, { t } from "elysia";
import pasarDesaCreate from "./create";
import pasarDesaDelete from "./del";
import pasarDesaFindMany from "./findMany";
import pasarDesaUpdate from "./updt";
import pasarDesaFindUnique from "./findUnique";
const PasarDesa = new Elysia({
prefix: "/pasardesa",
tags: ["Ekonomi/Pasar Desa"],
})
// GET all
.get("/find-many", pasarDesaFindMany)
// GET by ID
.get(
"/:id",
async (context) => {
return await pasarDesaFindUnique(context);
},
{
params: t.Object({
id: t.String(),
}),
}
)
// POST create
.post(
"/create",
pasarDesaCreate,
{
body: t.Object({
nama: t.String(),
harga: t.Number(),
alamatUsaha: t.String(),
imageId: t.String(),
rating: t.Number(),
kategoriId: t.Array(t.String()),
}),
}
)
// DELETE
.delete(
"/del/:id",
pasarDesaDelete,
{
params: t.Object({
id: t.String(),
}),
}
)
// PUT update
.put(
"/:id",
async (context) => {
const body = context.body;
const id = context.params.id;
// Gabungkan id ke body
return await pasarDesaUpdate({
...context,
body: {
...body,
id,
},
});
},
{
params: t.Object({
id: t.String(),
}),
body: t.Object({
nama: t.String(),
harga: t.Number(),
alamatUsaha: t.String(),
imageId: t.String(),
rating: t.Number(),
kategoriId: t.Array(t.String()),
}),
}
);
export default PasarDesa;

View File

@@ -0,0 +1,25 @@
import prisma from "@/lib/prisma";
import { Context } from "elysia";
export default async function kategoriProdukCreate(context: Context) {
const body = context.body as {nama: string};
if (!body.nama) {
return {
success: false,
message: "Nama is required",
};
}
const kategoriProduk = await prisma.kategoriProduk.create({
data: {
nama: body.nama,
},
});
return {
success: true,
message: "Success create kategori produk",
data: kategoriProduk
};
}

View File

@@ -0,0 +1,33 @@
import prisma from "@/lib/prisma";
import { Context } from "elysia";
const kategoriProdukDelete = async (context: Context) => {
const id = context.params.id;
if (!id) {
return {
success: false,
message: "ID is required",
}
}
const kategoriProduk = await prisma.kategoriProduk.delete({
where: {
id: id,
},
})
if(!kategoriProduk) {
return {
success: false,
message: "Kategori Produk tidak ditemukan",
}
}
return {
success: true,
message: "Success delete kategori produk",
data: kategoriProduk,
}
}
export default kategoriProdukDelete

View File

@@ -0,0 +1,15 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import prisma from "@/lib/prisma";
export default async function kategoriProdukFindMany() {
const data = await prisma.kategoriProduk.findMany();
return {
success: true,
data: data.map((item: any) => {
return {
id: item.id,
nama: item.nama,
}
}),
};
}

View File

@@ -0,0 +1,47 @@
import { Context } from "elysia";
import prisma from "@/lib/prisma";
export default async function kategoriProdukFindUnique(context: Context) {
const url = new URL(context.request.url);
const pathSegments = url.pathname.split('/');
const id = pathSegments[pathSegments.length - 1];
if (!id) {
return {
success: false,
message: "ID is required",
}
}
try {
if (typeof id !== 'string') {
return {
success: false,
message: "ID is required",
}
}
const data = await prisma.kategoriProduk.findUnique({
where: { id },
});
if (!data) {
return {
success: false,
message: "Kategori makanan tidak ditemukan",
}
}
return {
success: true,
message: "Success find kategori makanan",
data,
}
} catch (error) {
console.error("Find by ID error:", error);
return {
success: false,
message: "Gagal mengambil kategori makanan: " + (error instanceof Error ? error.message : 'Unknown error'),
}
}
}

View File

@@ -0,0 +1,30 @@
import Elysia from "elysia";
import kategoriProdukFindMany from "./findMany";
import kategoriProdukFindUnique from "./findUnique";
import kategoriProdukDelete from "./del";
import kategoriProdukCreate from "./create";
import kategoriProdukUpdate from "./updt";
import { t } from "elysia";
const KategoriProduk = new Elysia({
prefix: "/kategoriproduk",
tags: ["Ekonomi/Kategori Produk"],
})
.get("/find-many", kategoriProdukFindMany)
.get("/:id", async (context) => {
const response = await kategoriProdukFindUnique(context);
return response;
})
.delete("/del/:id", kategoriProdukDelete)
.post("/create", kategoriProdukCreate, {
body: t.Object({
nama: t.String(),
}),
})
.put("/:id", kategoriProdukUpdate, {
body: t.Object({
nama: t.String(),
}),
});
export default KategoriProduk;

View File

@@ -0,0 +1,44 @@
import prisma from "@/lib/prisma";
import { Context } from "elysia";
export default async function kategoriProdukUpdate(context: Context) {
const body = context.body as { nama: string };
const id = context.params?.id as string;
// Validasi ID dan nama
if (!id) {
return {
success: false,
message: "ID is required",
};
}
if (!body.nama) {
return {
success: false,
message: "Nama is required",
};
}
try {
const kategoriProduk = await prisma.kategoriProduk.update({
where: { id },
data: {
nama: body.nama,
},
});
return {
success: true,
message: "Success update kategori produk",
data: kategoriProduk,
};
} catch (error) {
console.error("Update error:", error);
return {
success: false,
message: "Gagal update kategori produk",
error: error instanceof Error ? error.message : String(error),
};
}
}

View File

@@ -0,0 +1,68 @@
import prisma from "@/lib/prisma";
import { Context } from "elysia";
type FormUpdate = {
id: string;
nama: string;
harga: number;
alamatUsaha: string;
imageId: string;
rating: number;
kategoriId: string[]; // Array of KategoriProduk IDs
};
export default async function pasarDesaUpdate(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");
}
// 1. Update data utama pasar desa
await prisma.pasarDesa.update({
where: { id: body.id },
data: {
nama: body.nama,
harga: Number(body.harga),
alamatUsaha: body.alamatUsaha,
imageId: body.imageId,
rating: Number(body.rating),
},
});
// 2. Hapus semua relasi kategori lama
await prisma.kategoriToPasar.deleteMany({
where: { pasarDesaId: body.id },
});
// 3. Tambah relasi kategori yang baru
await prisma.kategoriToPasar.createMany({
data: body.kategoriId.map((kategoriProdukId) => ({
pasarDesaId: body.id,
kategoriId: kategoriProdukId,
})),
});
// 4. Ambil data lengkap setelah update
const updated = await prisma.pasarDesa.findUnique({
where: { id: body.id },
include: {
image: true,
KategoriToPasar: {
include: {
kategori: true,
},
},
},
});
return {
success: true,
message: "Success update pasar desa",
data: updated,
};
}

View File

@@ -0,0 +1,41 @@
import prisma from "@/lib/prisma";
import { Context } from "elysia";
type FormCreate = {
nama: string;
deskripsi: string;
ikonUrl?: string; // optional karena boleh null
statistik?: {
tahun: number;
jumlah: number;
};
};
export default async function programKemiskinanCreate(context: Context) {
const body = context.body as FormCreate;
const program = await prisma.programKemiskinan.create({
data: {
nama: body.nama,
deskripsi: body.deskripsi,
ikonUrl: body.ikonUrl,
statistik: body.statistik
? {
create: {
tahun: Number(body.statistik.tahun),
jumlah: Number(body.statistik.jumlah),
},
}
: undefined,
},
include: {
statistik: true, // untuk menampilkan data relasinya juga
},
});
return {
success: true,
message: "Success create program kemiskinan dengan relasi statistik",
data: program,
};
}

View File

@@ -0,0 +1,19 @@
import prisma from "@/lib/prisma";
import { Context } from "elysia";
export default async function programKemiskinanDelete(context: Context) {
const id = context.params.id as string;
await prisma.programKemiskinan.delete({
where: { id },
include: {
statistik: true,
}
});
return {
status: 200,
success: true,
message: "Success delete program kemiskinan",
};
}

View File

@@ -0,0 +1,18 @@
import prisma from "@/lib/prisma";
export default async function programKemiskinanFindMany() {
const data = await prisma.programKemiskinan.findMany({
include: {
statistik: true, // ikut sertakan relasinya
},
orderBy: {
createdAt: "desc",
},
});
return {
success: true,
message: "Success get all program layanan",
data,
};
}

View File

@@ -0,0 +1,49 @@
import prisma from "@/lib/prisma";
export default async function programKemiskinanFindUnique(request: Request) {
const url = new URL(request.url);
const pathSegments = url.pathname.split('/');
const id = pathSegments[pathSegments.length - 1];
if (!id) {
return {
success: false,
message: "ID is required",
}
}
try {
if (typeof id !== 'string') {
return {
success: false,
message: "ID is required",
}
}
const data = await prisma.programKemiskinan.findUnique({
where: { id },
include: {
statistik: true,
}
});
if (!data) {
return {
success: false,
message: "Data not found",
}
}
return {
success: true,
message: "Success get program layanan",
data,
}
} catch (error) {
console.error("Find by ID error:", error);
return {
success: false,
message: "Gagal mengambil data: " + (error instanceof Error ? error.message : 'Unknown error'),
}
}
}

View File

@@ -0,0 +1,42 @@
import Elysia, { t } from "elysia";
import programKemiskinanCreate from "./create";
import programKemiskinanDelete from "./del";
import programKemiskinanFindUnique from "./findUnique";
import programKemiskinanUpdate from "./updt";
import programKemiskinanFindMany from "./findMany";
const ProgramKemiskinan = new Elysia({
prefix: '/programkemiskinan', tags: ['Ekonomi / Program Kemiskinan']
})
.post("/create", programKemiskinanCreate, {
body: t.Object({
nama: t.String(),
deskripsi: t.String(),
ikonUrl: t.String(),
statistik: t.Object({
tahun: t.String(),
jumlah: t.String(),
}),
}),
})
.delete("/del/:id", programKemiskinanDelete)
.get("/find-many", programKemiskinanFindMany)
.get("/:id", async (context) => {
const response = await programKemiskinanFindUnique(new Request(context.request));
return response;
})
.put("/:id", async (context) => {
const response = await programKemiskinanUpdate(context);
return response;
}, {
body: t.Object({
nama: t.String(),
deskripsi: t.String(),
ikonUrl: t.String(),
statistik: t.Object({
tahun: t.String(),
jumlah: t.String(),
}),
}),
})
export default ProgramKemiskinan;

View File

@@ -0,0 +1,71 @@
import prisma from "@/lib/prisma";
import { Context } from "elysia";
type FormUpdate = {
nama: string;
deskripsi: string;
ikonUrl?: string;
statistik?: {
tahun: number;
jumlah: number;
};
};
export default async function programKemiskinanUpdate(context: Context) {
const id = context.params.id as string;
const body = context.body as FormUpdate;
try {
// cari ID statistik yang terkait
const existing = await prisma.programKemiskinan.findUnique({
where: { id },
include: { statistik: true },
});
if (!existing) {
return new Response(JSON.stringify({ success: false, message: "Program tidak ditemukan" }), { status: 404 });
}
// update statistik (bisa update atau create kalau belum ada)
let statistikUpdate;
if (existing.statistikId) {
statistikUpdate = await prisma.statistikKemiskinan.update({
where: { id: existing.statistikId },
data: {
tahun: Number(body.statistik?.tahun),
jumlah: Number(body.statistik?.jumlah),
},
});
} else {
statistikUpdate = await prisma.statistikKemiskinan.create({
data: {
tahun: Number(body.statistik?.tahun),
jumlah: Number(body.statistik?.jumlah),
},
});
}
const program = await prisma.programKemiskinan.update({
where: { id },
data: {
nama: body.nama,
deskripsi: body.deskripsi,
ikonUrl: body.ikonUrl,
statistik: {
connect: { id: statistikUpdate.id }, // konek ke statistik baru atau yang diperbarui
},
},
include: { statistik: true },
});
return new Response(JSON.stringify({ success: true, data: program }), {
status: 200,
});
} catch (err) {
console.error("Gagal update:", err);
return new Response(JSON.stringify({ success: false, message: "Gagal update program" }), {
status: 500,
});
}
}

View File

@@ -1,18 +1,18 @@
import Elysia from "elysia";
import KeamananLingkungan from "./keamanan-lingkungan";
import PolsekTerdekat from "./polsek-terdekat";
import KontakDarurat from "./kontak-darurat";
import PencegahanKriminalitas from "./pencegahan-kriminalitas";
import MenuTipsKeamanan from "./tips-keamanan";
import LaporanPublik from "./laporan-publik";
import LayananPolsek from "./layanan-polsek";
import KontakDaruratKeamanan from "./kontak-darurat-keamanan";
const Keamanan = new Elysia({ prefix: "/api/keamanan", tags: ["Keamanan"] })
.use(KeamananLingkungan)
.use(PolsekTerdekat)
.use(KontakDarurat)
.use(PencegahanKriminalitas)
.use(MenuTipsKeamanan)
.use(LaporanPublik)
.use(LayananPolsek)
.use(KontakDaruratKeamanan)
export default Keamanan;

View File

@@ -1,22 +1,28 @@
import prisma from "@/lib/prisma";
import { Prisma } from "@prisma/client";
import { Context } from "elysia";
type FormCreate = Prisma.KontakDaruratKeamananGetPayload<{
select: {
nama: true,
kontak: true,
icon: true,
}
}>
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
await prisma.kontakDaruratKeamanan.create({
data: {
nama: body.nama,
kontak: body.kontak,
icon: body.icon,
imageId: body.imageId,
kontakItems: {
create: body.kontakItems,
},
},
include: {
kontakItems: true,
}
})
return {

View File

@@ -0,0 +1,58 @@
import prisma from "@/lib/prisma";
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;
if (!id) {
return {
status: 400,
body: "ID tidak diberikan",
};
}
await prisma.kontakItem.deleteMany({
where: {
kategoriId: 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 },
});
return {
status: 200,
success: true,
message: "Kontak darurat keamanan berhasil dihapus",
};
}

View File

@@ -3,7 +3,13 @@ import prisma from "@/lib/prisma";
export default async function kontakDaruratKeamananFindMany() {
try {
const data = await prisma.kontakDaruratKeamanan.findMany({
where: { isActive: true },
include: {
kontakItems: true,
image: true,
},
orderBy: {
createdAt: "desc",
}
});
return {

View File

@@ -0,0 +1,73 @@
import prisma from "@/lib/prisma";
export default async function kontakDaruratKeamananFindUnique(
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.kontakDaruratKeamanan.findUnique({
where: { id },
include: {
kontakItems: {
include: {
image: true,
},
},
image: true,
},
});
if (!data) {
return Response.json(
{
success: false,
message: "Kontak darurat keamanan tidak ditemukan",
},
{ status: 404 }
);
}
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 }
);
}
}

View File

@@ -0,0 +1,53 @@
import Elysia, { t } from "elysia";
import kontakDaruratKeamananCreate from "./create";
import kontakDaruratKeamananDelete from "./del";
import kontakDaruratKeamananFindMany from "./findMany";
import kontakDaruratKeamananFindUnique from "./findUnique";
import kontakDaruratKeamananUpdate from "./updt";
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;
})
.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()),
})
),
}),
})
.delete("/del/:id", kontakDaruratKeamananDelete)
.put(
"/:id",
async (context) => {
const response = await kontakDaruratKeamananUpdate(context);
return response;
},
{
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()),
})
),
}),
}
);
export default KontakDaruratKeamanan;

View File

@@ -0,0 +1,57 @@
import prisma from "@/lib/prisma";
import { Context } from "elysia";
type FormUpdate = {
nama: string;
imageId?: string;
kontakItems: {
nama: string;
nomorTelepon: string;
imageId?: 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",
},
});
}
}

View File

@@ -1,32 +0,0 @@
import Elysia, { t } from "elysia";
import kontakDaruratKeamananFindMany from "./findMany";
import kontakDaruratKeamananFindUnique from "./findUnique";
import kontakDaruratKeamananCreate from "./create";
import kontakDaruratKeamananDelete from "./del";
const kontakDaruratKeamanan = new Elysia({ prefix: "/kontak-darurat", tags: ["Keamanan/Kontak Darurat"] })
.get("/find-many", kontakDaruratKeamananFindMany)
.get("/:id", async (context) => {
const response = await kontakDaruratKeamananFindUnique(new Request(context.request));
return response;
})
.post("/create", kontakDaruratKeamananCreate, {
body: t.Object({
nama: t.String(),
kontak: t.String(),
icon: t.String(),
}),
})
.delete("/del/:id", kontakDaruratKeamananDelete)
.put("/:id", async (context) => {
const response = await kontakDaruratKeamananCreate(context);
return response;
},
{
body: t.Object({
nama: t.String(),
kontak: t.String(),
icon: t.String(),
}),
})
export default kontakDaruratKeamanan;

View File

@@ -1,61 +0,0 @@
import prisma from "@/lib/prisma";
import { Prisma } from "@prisma/client";
import { Context } from "elysia";
type FormUpdate = Prisma.KontakDaruratKeamananGetPayload<{
select: {
nama: true;
kontak: true;
icon: true;
}
}>
export default async function kontakDaruratUpdate(context: Context){
try {
const id = context.params?.id;
const body = (await context.body) as Omit<FormUpdate, "id">;
const {nama, kontak, icon} = body;
if(!id){
return Response.json({
success: false,
message: "ID tidak diberikan",
}, { status: 400 });
}
const existing = await prisma.kontakDaruratKeamanan.findUnique({
where: { id },
});
if (!existing) {
return Response.json({
success: false,
message: "Kontak darurat keamanan tidak ditemukan",
}, { status: 404 });
}
const updated = await prisma.kontakDaruratKeamanan.update({
where: { id },
data: {
nama,
kontak,
icon,
},
});
return Response.json({
success: true,
message: "Success update kontak darurat keamanan",
data: updated,
}, { status: 200 });
} 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",
},
});
}
}

View File

@@ -5,8 +5,8 @@ type LaporanPublikInput = {
judul: string;
lokasi: string;
tanggalWaktu: string;
status: "SELESAI" | "PROSES" | "GAGAL";
penanganan: string[];
status: "Selesai" | "Proses" | "Gagal";
penanganan: string;
kronologi?: string;
};
@@ -21,9 +21,9 @@ const laporanPublikCreate = async (context: Context) => {
tanggalWaktu: new Date(tanggalWaktu),
status,
penanganan: {
create: penanganan.map((item) => ({
deskripsi: item,
})),
create: {
deskripsi: penanganan,
},
},
kronologi,
},

View File

@@ -15,11 +15,11 @@ const LaporanPublik = new Elysia({
lokasi: t.String(),
tanggalWaktu: t.String(), // ISO string
status: t.Union([
t.Literal("SELESAI"),
t.Literal("PROSES"),
t.Literal("GAGAL"),
t.Literal("Selesai"),
t.Literal("Proses"),
t.Literal("Gagal"),
]),
penanganan: t.Array(t.String()), // 🛠️ ARRAY of strings
penanganan: t.String(), // 🛠️ ARRAY of strings
kronologi: t.Optional(t.String()),
}),
})
@@ -31,11 +31,11 @@ const LaporanPublik = new Elysia({
lokasi: t.String(),
tanggalWaktu: t.String(), // ISO string
status: t.Union([
t.Literal("SELESAI"),
t.Literal("PROSES"),
t.Literal("GAGAL"),
t.Literal("Selesai"),
t.Literal("Proses"),
t.Literal("Gagal"),
]),
penanganan: t.Array(t.String()), // 🛠️ ARRAY of strings
penanganan: t.String(), // 🛠️ ARRAY of strings
kronologi: t.Optional(t.String()),
}),
})

View File

@@ -5,8 +5,8 @@ type LaporanPublikUpdateInput = {
judul: string;
lokasi: string;
tanggalWaktu: string;
status: "SELESAI" | "PROSES" | "GAGAL";
penanganan: string[];
status: "Selesai" | "Proses" | "Gagal";
penanganan: string;
kronologi?: string;
};
@@ -32,9 +32,9 @@ const LaporanPublikUpdate = async (context: Context) => {
status,
kronologi,
penanganan: {
create: penanganan.map((item) => ({
deskripsi: item,
})),
create: {
deskripsi: penanganan,
},
},
},
include: {

View File

@@ -23,7 +23,8 @@ const pencegahanKriminalitasDelete = async (context: Context) => {
if (!pencegahanKriminalitas) {
return {
status: 404,
body: "Pencegahan kriminalitas tidak ditemukan",
success: false,
message: "Pencegahan kriminalitas tidak ditemukan",
};
}
@@ -33,7 +34,8 @@ const pencegahanKriminalitasDelete = async (context: Context) => {
return {
status: 200,
body: "Pencegahan kriminalitas berhasil dihapus",
success: true,
message: "Pencegahan kriminalitas berhasil dihapus",
};
};
export default pencegahanKriminalitasDelete;

View File

@@ -46,8 +46,9 @@ const menuTipsKeamananDelete = async (context: Context) => {
});
return {
status: 200,
body: deleted,
success: true,
message: "Success delete menu tips keamanan",
data: deleted,
};
};
export default menuTipsKeamananDelete;

View File

@@ -18,6 +18,7 @@ import uplImg from "./_lib/upl-img";
import { uplImgSingle } from "./_lib/upl-img-single";
import FileStorage from "./_lib/fileStorage";
import Keamanan from "./_lib/keamanan";
import Ekonomi from "./_lib/ekonomi";
const ROOT = process.cwd();
@@ -79,6 +80,7 @@ const ApiServer = new Elysia()
.use(Keamanan)
.use(Utils)
.use(FileStorage)
.use(Ekonomi)
.onError(({ code }) => {
if (code === "NOT_FOUND") {
return {