feat(sosial): add schema fields + API endpoints for Sosial dashboard - bump to 0.1.45

- Add persentase field to ProgramKesehatan (for health stats bar chart)
- Add BeasiswaConfig model (dana tersalurkan + tahun ajaran)
- Add RingkasanKesehatanDesa model (ibu hamil, balita, alert stunting)
- Add KegiatanDesa CRUD API (GET /api/desa/kegiatandesa/find-many?kategori=Budaya)
- Add BeasiswaConfig API (GET/PUT /api/pendidikan/beasiswa/beasiswaconfig/...)
- Add RingkasanKesehatan API (GET/PUT /api/kesehatan/ringkasankesehatan/...)
- Migration: 20260430000000_add_sosial_fields

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-30 00:16:25 +08:00
parent 6041cdf552
commit b39f9b39da
17 changed files with 366 additions and 1 deletions

View File

@@ -13,6 +13,7 @@ import KategoriPengumuman from "./pengumuman/kategori-pengumuman";
import MantanPerbekel from "./profile/profile-mantan-perbekel";
import AjukanPermohonan from "./layanan/ajukan_permohonan";
import Musik from "./musik";
import KegiatanDesa from "./kegiatan-desa";
const Desa = new Elysia({ prefix: "/desa", tags: ["Desa"] })
@@ -30,6 +31,7 @@ const Desa = new Elysia({ prefix: "/desa", tags: ["Desa"] })
.use(KategoriPengumuman)
.use(AjukanPermohonan)
.use(Musik)
.use(KegiatanDesa)
export default Desa;

View File

@@ -0,0 +1,30 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import prisma from "@/lib/prisma";
import { Context } from "elysia";
async function kegiatanDesaCreate(context: Context) {
const body = context.body as any;
try {
const data = await prisma.kegiatanDesa.create({
data: {
judul: body.judul,
deskripsiSingkat: body.deskripsiSingkat,
deskripsiLengkap: body.deskripsiLengkap,
tanggal: new Date(body.tanggal),
lokasi: body.lokasi,
partisipan: Number(body.partisipan) || 0,
kategoriKegiatanId: body.kategoriKegiatanId,
imageId: body.imageId || null,
},
include: { kategoriKegiatan: true, image: true },
});
return { success: true, message: "Kegiatan desa berhasil dibuat", data };
} catch (e) {
console.error("Error di kegiatanDesaCreate:", e);
return { success: false, message: "Gagal membuat kegiatan desa" };
}
}
export default kegiatanDesaCreate;

View File

@@ -0,0 +1,19 @@
import prisma from "@/lib/prisma";
import { Context } from "elysia";
async function kegiatanDesaDelete(context: Context) {
const { id } = context.params as { id: string };
try {
await prisma.kegiatanDesa.update({
where: { id },
data: { isActive: false },
});
return { success: true, message: "Kegiatan desa berhasil dihapus" };
} catch (e) {
console.error("Error di kegiatanDesaDelete:", e);
return { success: false, message: "Gagal menghapus kegiatan desa" };
}
}
export default kegiatanDesaDelete;

View File

@@ -0,0 +1,57 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import prisma from "@/lib/prisma";
import { Context } from "elysia";
async function kegiatanDesaFindMany(context: Context) {
const page = Number(context.query.page) || 1;
const limit = Number(context.query.limit) || 10;
const search = (context.query.search as string) || '';
const kategori = (context.query.kategori as string) || '';
const skip = (page - 1) * limit;
const where: any = { isActive: true };
if (search) {
where.OR = [
{ judul: { contains: search, mode: 'insensitive' } },
{ lokasi: { contains: search, mode: 'insensitive' } },
];
}
if (kategori) {
where.kategoriKegiatan = {
nama: { contains: kategori, mode: 'insensitive' },
};
}
try {
const [data, total] = await Promise.all([
prisma.kegiatanDesa.findMany({
where,
include: {
kategoriKegiatan: true,
image: true,
},
skip,
take: limit,
orderBy: { tanggal: 'asc' },
}),
prisma.kegiatanDesa.count({ where }),
]);
return {
success: true,
message: "Berhasil ambil kegiatan desa",
data,
page,
limit,
total,
totalPages: Math.ceil(total / limit),
};
} catch (e) {
console.error("Error di kegiatanDesaFindMany:", e);
return { success: false, message: "Gagal mengambil data kegiatan desa" };
}
}
export default kegiatanDesaFindMany;

View File

@@ -0,0 +1,35 @@
import Elysia, { t } from "elysia";
import kegiatanDesaFindMany from "./find-many";
import kegiatanDesaCreate from "./create";
import kegiatanDesaDelete from "./del";
import kegiatanDesaUpdate from "./updt";
const KegiatanDesa = new Elysia({ prefix: "/kegiatandesa", tags: ["Desa/Kegiatan Desa"] })
.get("/find-many", kegiatanDesaFindMany)
.post("/create", kegiatanDesaCreate, {
body: t.Object({
judul: t.String(),
deskripsiSingkat: t.String(),
deskripsiLengkap: t.String(),
tanggal: t.String(),
lokasi: t.String(),
partisipan: t.Optional(t.Number()),
kategoriKegiatanId: t.String(),
imageId: t.Optional(t.String()),
}),
})
.put("/:id", kegiatanDesaUpdate, {
body: t.Object({
judul: t.String(),
deskripsiSingkat: t.String(),
deskripsiLengkap: t.String(),
tanggal: t.String(),
lokasi: t.String(),
partisipan: t.Optional(t.Number()),
kategoriKegiatanId: t.String(),
imageId: t.Optional(t.String()),
}),
})
.delete("/del/:id", kegiatanDesaDelete);
export default KegiatanDesa;

View File

@@ -0,0 +1,32 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import prisma from "@/lib/prisma";
import { Context } from "elysia";
async function kegiatanDesaUpdate(context: Context) {
const { id } = context.params as { id: string };
const body = context.body as any;
try {
const data = await prisma.kegiatanDesa.update({
where: { id },
data: {
judul: body.judul,
deskripsiSingkat: body.deskripsiSingkat,
deskripsiLengkap: body.deskripsiLengkap,
tanggal: new Date(body.tanggal),
lokasi: body.lokasi,
partisipan: Number(body.partisipan) || 0,
kategoriKegiatanId: body.kategoriKegiatanId,
imageId: body.imageId || null,
},
include: { kategoriKegiatan: true, image: true },
});
return { success: true, message: "Kegiatan desa berhasil diupdate", data };
} catch (e) {
console.error("Error di kegiatanDesaUpdate:", e);
return { success: false, message: "Gagal mengupdate kegiatan desa" };
}
}
export default kegiatanDesaUpdate;

View File

@@ -21,6 +21,7 @@ import Kematian from "./data_kesehatan_warga/persentase_kelahiran_kematian/kemat
import DokterTenagaMedis from "./data_kesehatan_warga/fasilitas_kesehatan/dokter-tenaga-medis";
import PendaftaranJadwalKegiatan from "./data_kesehatan_warga/jadwal_kegiatan/pendaftaran";
import TarifLayanan from "./data_kesehatan_warga/fasilitas_kesehatan/tarif-layanan";
import RingkasanKesehatan from "./ringkasan-kesehatan";
const Kesehatan = new Elysia({
@@ -49,4 +50,5 @@ const Kesehatan = new Elysia({
.use(DokterTenagaMedis)
.use(TarifLayanan)
.use(PendaftaranJadwalKegiatan)
.use(RingkasanKesehatan)
export default Kesehatan;

View File

@@ -0,0 +1,17 @@
import prisma from "@/lib/prisma";
async function ringkasanKesehatanFindUnique() {
try {
const data = await prisma.ringkasanKesehatanDesa.findFirst({
where: { isActive: true },
orderBy: { createdAt: 'desc' },
});
return { success: true, data };
} catch (e) {
console.error("Error di ringkasanKesehatanFindUnique:", e);
return { success: false, message: "Gagal mengambil ringkasan kesehatan" };
}
}
export default ringkasanKesehatanFindUnique;

View File

@@ -0,0 +1,15 @@
import Elysia, { t } from "elysia";
import ringkasanKesehatanFindUnique from "./findUnique";
import ringkasanKesehatanUpdate from "./updt";
const RingkasanKesehatan = new Elysia({ prefix: "/ringkasankesehatan", tags: ["Kesehatan/Ringkasan"] })
.get("/find", ringkasanKesehatanFindUnique)
.put("/update", ringkasanKesehatanUpdate, {
body: t.Object({
ibuHamilAkh: t.Number(),
balitaTerdaftar: t.Number(),
alertStunting: t.Number(),
}),
});
export default RingkasanKesehatan;

View File

@@ -0,0 +1,38 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import prisma from "@/lib/prisma";
import { Context } from "elysia";
async function ringkasanKesehatanUpdate(context: Context) {
const body = context.body as any;
try {
const existing = await prisma.ringkasanKesehatanDesa.findFirst({
where: { isActive: true },
orderBy: { createdAt: 'desc' },
});
const data = existing
? await prisma.ringkasanKesehatanDesa.update({
where: { id: existing.id },
data: {
ibuHamilAkh: Number(body.ibuHamilAkh),
balitaTerdaftar: Number(body.balitaTerdaftar),
alertStunting: Number(body.alertStunting),
},
})
: await prisma.ringkasanKesehatanDesa.create({
data: {
ibuHamilAkh: Number(body.ibuHamilAkh),
balitaTerdaftar: Number(body.balitaTerdaftar),
alertStunting: Number(body.alertStunting),
},
});
return { success: true, message: "Ringkasan kesehatan berhasil disimpan", data };
} catch (e) {
console.error("Error di ringkasanKesehatanUpdate:", e);
return { success: false, message: "Gagal menyimpan ringkasan kesehatan" };
}
}
export default ringkasanKesehatanUpdate;

View File

@@ -0,0 +1,17 @@
import prisma from "@/lib/prisma";
async function beasiswaConfigFindUnique() {
try {
const data = await prisma.beasiswaConfig.findFirst({
where: { isActive: true },
orderBy: { createdAt: 'desc' },
});
return { success: true, data };
} catch (e) {
console.error("Error di beasiswaConfigFindUnique:", e);
return { success: false, message: "Gagal mengambil konfigurasi beasiswa" };
}
}
export default beasiswaConfigFindUnique;

View File

@@ -0,0 +1,14 @@
import Elysia, { t } from "elysia";
import beasiswaConfigFindUnique from "./findUnique";
import beasiswaConfigUpdate from "./updt";
const BeasiswaConfig = new Elysia({ prefix: "/beasiswaconfig", tags: ["Pendidikan/Beasiswa Desa/Config"] })
.get("/find", beasiswaConfigFindUnique)
.put("/update", beasiswaConfigUpdate, {
body: t.Object({
tahunAjaran: t.String(),
danaTersalurkan: t.String(),
}),
});
export default BeasiswaConfig;

View File

@@ -0,0 +1,36 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import prisma from "@/lib/prisma";
import { Context } from "elysia";
async function beasiswaConfigUpdate(context: Context) {
const body = context.body as any;
try {
const existing = await prisma.beasiswaConfig.findFirst({
where: { isActive: true },
orderBy: { createdAt: 'desc' },
});
const data = existing
? await prisma.beasiswaConfig.update({
where: { id: existing.id },
data: {
tahunAjaran: body.tahunAjaran,
danaTersalurkan: BigInt(body.danaTersalurkan),
},
})
: await prisma.beasiswaConfig.create({
data: {
tahunAjaran: body.tahunAjaran,
danaTersalurkan: BigInt(body.danaTersalurkan),
},
});
return { success: true, message: "Konfigurasi beasiswa berhasil disimpan", data: { ...data, danaTersalurkan: data.danaTersalurkan.toString() } };
} catch (e) {
console.error("Error di beasiswaConfigUpdate:", e);
return { success: false, message: "Gagal menyimpan konfigurasi beasiswa" };
}
}
export default beasiswaConfigUpdate;

View File

@@ -1,6 +1,7 @@
import Elysia from "elysia";
import BeasiswaPendaftar from "./beasiswa-pendaftar";
import KeunggulanProgram from "./keunggulan-program";
import BeasiswaConfig from "./beasiswa-config";
const Beasiswa = new Elysia({
prefix: "/beasiswa",
@@ -8,5 +9,6 @@ const Beasiswa = new Elysia({
})
.use(BeasiswaPendaftar)
.use(KeunggulanProgram)
.use(BeasiswaConfig)
export default Beasiswa