feat(sosial-dashboard): tambah API ringkasan pendidikan & beasiswa + CRUD event budaya - bump 0.1.55
- API GET /api/pendidikan/ringkasan/stats: siswa per jenjang, jumlah lembaga & pengajar - API GET /api/pendidikan/beasiswa/ringkasan/stats: jumlah penerima, dana, tahun ajaran - Schema + migration: model EventBudaya (nama, tanggal, lokasi, deskripsi) - API CRUD /api/desa/eventbudaya: create, find-many, findUnique, updt, del - State admin: eventBudaya.ts (valtio proxy, create/findMany/edit/delete) - Admin CMS: /admin/desa/event-budaya (list, create, edit) - Navbar: tambah entry Desa_9 Event Budaya di semua role - Seeder: 8 event budaya Bali untuk Desa Darmasaba Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
29
src/app/api/[[...slugs]]/_lib/desa/event-budaya/create.ts
Normal file
29
src/app/api/[[...slugs]]/_lib/desa/event-budaya/create.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import prisma from "@/lib/prisma";
|
||||
import { Context } from "elysia";
|
||||
|
||||
async function eventBudayaCreate(context: Context) {
|
||||
const body = context.body as {
|
||||
nama: string;
|
||||
tanggal: string;
|
||||
lokasi: string;
|
||||
deskripsi?: string;
|
||||
};
|
||||
|
||||
try {
|
||||
const data = await prisma.eventBudaya.create({
|
||||
data: {
|
||||
nama: body.nama,
|
||||
tanggal: new Date(body.tanggal),
|
||||
lokasi: body.lokasi,
|
||||
deskripsi: body.deskripsi || null,
|
||||
},
|
||||
});
|
||||
|
||||
return { success: true, message: "Event budaya berhasil dibuat", data };
|
||||
} catch (e) {
|
||||
console.error("Error di eventBudayaCreate:", e);
|
||||
return { success: false, message: "Gagal membuat event budaya" };
|
||||
}
|
||||
}
|
||||
|
||||
export default eventBudayaCreate;
|
||||
20
src/app/api/[[...slugs]]/_lib/desa/event-budaya/del.ts
Normal file
20
src/app/api/[[...slugs]]/_lib/desa/event-budaya/del.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import prisma from "@/lib/prisma";
|
||||
import { Context } from "elysia";
|
||||
|
||||
async function eventBudayaDelete(context: Context) {
|
||||
const { id } = context.params as { id: string };
|
||||
|
||||
try {
|
||||
await prisma.eventBudaya.update({
|
||||
where: { id },
|
||||
data: { isActive: false },
|
||||
});
|
||||
|
||||
return { success: true, message: "Event budaya berhasil dihapus" };
|
||||
} catch (e) {
|
||||
console.error("Error di eventBudayaDelete:", e);
|
||||
return { success: false, message: "Gagal menghapus event budaya" };
|
||||
}
|
||||
}
|
||||
|
||||
export default eventBudayaDelete;
|
||||
46
src/app/api/[[...slugs]]/_lib/desa/event-budaya/find-many.ts
Normal file
46
src/app/api/[[...slugs]]/_lib/desa/event-budaya/find-many.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import prisma from "@/lib/prisma";
|
||||
import { Context } from "elysia";
|
||||
|
||||
async function eventBudayaFindMany(context: Context) {
|
||||
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;
|
||||
|
||||
const where: any = { isActive: true };
|
||||
|
||||
if (search) {
|
||||
where.OR = [
|
||||
{ nama: { contains: search, mode: "insensitive" } },
|
||||
{ lokasi: { contains: search, mode: "insensitive" } },
|
||||
];
|
||||
}
|
||||
|
||||
try {
|
||||
const [data, total] = await Promise.all([
|
||||
prisma.eventBudaya.findMany({
|
||||
where,
|
||||
skip,
|
||||
take: limit,
|
||||
orderBy: { tanggal: "asc" },
|
||||
}),
|
||||
prisma.eventBudaya.count({ where }),
|
||||
]);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: "Berhasil mengambil event budaya",
|
||||
data,
|
||||
page,
|
||||
limit,
|
||||
total,
|
||||
totalPages: Math.ceil(total / limit),
|
||||
};
|
||||
} catch (e) {
|
||||
console.error("Error di eventBudayaFindMany:", e);
|
||||
return { success: false, message: "Gagal mengambil data event budaya" };
|
||||
}
|
||||
}
|
||||
|
||||
export default eventBudayaFindMany;
|
||||
@@ -0,0 +1,23 @@
|
||||
import prisma from "@/lib/prisma";
|
||||
import { Context } from "elysia";
|
||||
|
||||
async function eventBudayaFindUnique(context: Context) {
|
||||
const { id } = context.params as { id: string };
|
||||
|
||||
try {
|
||||
const data = await prisma.eventBudaya.findFirst({
|
||||
where: { id, isActive: true },
|
||||
});
|
||||
|
||||
if (!data) {
|
||||
return { success: false, message: "Event budaya tidak ditemukan" };
|
||||
}
|
||||
|
||||
return { success: true, data };
|
||||
} catch (e) {
|
||||
console.error("Error di eventBudayaFindUnique:", e);
|
||||
return { success: false, message: "Gagal mengambil data event budaya" };
|
||||
}
|
||||
}
|
||||
|
||||
export default eventBudayaFindUnique;
|
||||
29
src/app/api/[[...slugs]]/_lib/desa/event-budaya/index.ts
Normal file
29
src/app/api/[[...slugs]]/_lib/desa/event-budaya/index.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import Elysia, { t } from "elysia";
|
||||
import eventBudayaFindMany from "./find-many";
|
||||
import eventBudayaFindUnique from "./findUnique";
|
||||
import eventBudayaCreate from "./create";
|
||||
import eventBudayaDelete from "./del";
|
||||
import eventBudayaUpdate from "./updt";
|
||||
|
||||
const EventBudaya = new Elysia({ prefix: "/eventbudaya", tags: ["Desa/Event Budaya"] })
|
||||
.get("/find-many", eventBudayaFindMany)
|
||||
.get("/:id", eventBudayaFindUnique)
|
||||
.post("/create", eventBudayaCreate, {
|
||||
body: t.Object({
|
||||
nama: t.String(),
|
||||
tanggal: t.String(),
|
||||
lokasi: t.String(),
|
||||
deskripsi: t.Optional(t.String()),
|
||||
}),
|
||||
})
|
||||
.put("/:id", eventBudayaUpdate, {
|
||||
body: t.Object({
|
||||
nama: t.String(),
|
||||
tanggal: t.String(),
|
||||
lokasi: t.String(),
|
||||
deskripsi: t.Optional(t.String()),
|
||||
}),
|
||||
})
|
||||
.delete("/del/:id", eventBudayaDelete);
|
||||
|
||||
export default EventBudaya;
|
||||
31
src/app/api/[[...slugs]]/_lib/desa/event-budaya/updt.ts
Normal file
31
src/app/api/[[...slugs]]/_lib/desa/event-budaya/updt.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import prisma from "@/lib/prisma";
|
||||
import { Context } from "elysia";
|
||||
|
||||
async function eventBudayaUpdate(context: Context) {
|
||||
const { id } = context.params as { id: string };
|
||||
const body = context.body as {
|
||||
nama: string;
|
||||
tanggal: string;
|
||||
lokasi: string;
|
||||
deskripsi?: string;
|
||||
};
|
||||
|
||||
try {
|
||||
const data = await prisma.eventBudaya.update({
|
||||
where: { id },
|
||||
data: {
|
||||
nama: body.nama,
|
||||
tanggal: new Date(body.tanggal),
|
||||
lokasi: body.lokasi,
|
||||
deskripsi: body.deskripsi || null,
|
||||
},
|
||||
});
|
||||
|
||||
return { success: true, message: "Event budaya berhasil diupdate", data };
|
||||
} catch (e) {
|
||||
console.error("Error di eventBudayaUpdate:", e);
|
||||
return { success: false, message: "Gagal mengupdate event budaya" };
|
||||
}
|
||||
}
|
||||
|
||||
export default eventBudayaUpdate;
|
||||
@@ -15,6 +15,7 @@ import AjukanPermohonan from "./layanan/ajukan_permohonan";
|
||||
import Musik from "./musik";
|
||||
import KegiatanDesa from "./kegiatan-desa";
|
||||
import KategoriKegiatan from "./kegiatan-desa/kategori-kegiatan";
|
||||
import EventBudaya from "./event-budaya";
|
||||
|
||||
|
||||
const Desa = new Elysia({ prefix: "/desa", tags: ["Desa"] })
|
||||
@@ -34,6 +35,7 @@ const Desa = new Elysia({ prefix: "/desa", tags: ["Desa"] })
|
||||
.use(Musik)
|
||||
.use(KegiatanDesa)
|
||||
.use(KategoriKegiatan)
|
||||
.use(EventBudaya)
|
||||
|
||||
|
||||
export default Desa;
|
||||
|
||||
@@ -2,6 +2,7 @@ import Elysia from "elysia";
|
||||
import BeasiswaPendaftar from "./beasiswa-pendaftar";
|
||||
import KeunggulanProgram from "./keunggulan-program";
|
||||
import BeasiswaConfig from "./beasiswa-config";
|
||||
import RingkasanBeasiswa from "./ringkasan";
|
||||
|
||||
const Beasiswa = new Elysia({
|
||||
prefix: "/beasiswa",
|
||||
@@ -10,5 +11,6 @@ const Beasiswa = new Elysia({
|
||||
.use(BeasiswaPendaftar)
|
||||
.use(KeunggulanProgram)
|
||||
.use(BeasiswaConfig)
|
||||
.use(RingkasanBeasiswa)
|
||||
|
||||
export default Beasiswa
|
||||
@@ -0,0 +1,9 @@
|
||||
import Elysia from "elysia";
|
||||
import beasiswaRingkasanStats from "./stats";
|
||||
|
||||
const RingkasanBeasiswa = new Elysia({
|
||||
prefix: "/ringkasan",
|
||||
tags: ["Pendidikan/Beasiswa Desa"],
|
||||
}).get("/stats", beasiswaRingkasanStats);
|
||||
|
||||
export default RingkasanBeasiswa;
|
||||
@@ -0,0 +1,35 @@
|
||||
import prisma from "@/lib/prisma";
|
||||
|
||||
type StatsResult = {
|
||||
jumlahPenerima: number;
|
||||
danaTersalurkan: bigint;
|
||||
tahunAjaran: string;
|
||||
};
|
||||
|
||||
export default async function beasiswaRingkasanStats(): Promise<{
|
||||
success: boolean;
|
||||
data?: StatsResult;
|
||||
message?: string;
|
||||
}> {
|
||||
try {
|
||||
const [jumlahPenerima, config] = await Promise.all([
|
||||
prisma.beasiswaPendaftar.count({ where: { isActive: true } }),
|
||||
prisma.beasiswaConfig.findFirst({
|
||||
where: { isActive: true },
|
||||
orderBy: { createdAt: "desc" },
|
||||
}),
|
||||
]);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
data: {
|
||||
jumlahPenerima,
|
||||
danaTersalurkan: config?.danaTersalurkan ?? BigInt(0),
|
||||
tahunAjaran: config?.tahunAjaran ?? "-",
|
||||
},
|
||||
};
|
||||
} catch (e) {
|
||||
console.error("beasiswaRingkasanStats error:", e);
|
||||
return { success: false, message: "Gagal menghitung statistik beasiswa" };
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import PendidikanNonFormal from "./pendidikan-non-formal";
|
||||
import DataPendidikan from "./data-pendidikan";
|
||||
import Beasiswa from "./beasiswa-desa";
|
||||
import PerpustakaanDigital from "./perpustakaan-digital";
|
||||
import RingkasanPendidikan from "./ringkasan";
|
||||
|
||||
const Pendidikan = new Elysia({
|
||||
prefix: "/pendidikan",
|
||||
@@ -19,5 +20,6 @@ const Pendidikan = new Elysia({
|
||||
.use(DataPendidikan)
|
||||
.use(Beasiswa)
|
||||
.use(PerpustakaanDigital)
|
||||
.use(RingkasanPendidikan)
|
||||
|
||||
export default Pendidikan;
|
||||
@@ -0,0 +1,9 @@
|
||||
import Elysia from "elysia";
|
||||
import pendidikanRingkasanStats from "./stats";
|
||||
|
||||
const RingkasanPendidikan = new Elysia({
|
||||
prefix: "/ringkasan",
|
||||
tags: ["Pendidikan/Ringkasan"],
|
||||
}).get("/stats", pendidikanRingkasanStats);
|
||||
|
||||
export default RingkasanPendidikan;
|
||||
46
src/app/api/[[...slugs]]/_lib/pendidikan/ringkasan/stats.ts
Normal file
46
src/app/api/[[...slugs]]/_lib/pendidikan/ringkasan/stats.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import prisma from "@/lib/prisma";
|
||||
|
||||
type PerJenjang = { nama: string; jumlahSiswa: number };
|
||||
|
||||
type StatsResult = {
|
||||
perJenjang: PerJenjang[];
|
||||
jumlahLembaga: number;
|
||||
jumlahPengajar: number;
|
||||
};
|
||||
|
||||
export default async function pendidikanRingkasanStats(): Promise<{
|
||||
success: boolean;
|
||||
data?: StatsResult;
|
||||
message?: string;
|
||||
}> {
|
||||
try {
|
||||
const [jenjangList, jumlahLembaga, jumlahPengajar] = await Promise.all([
|
||||
prisma.jenjangPendidikan.findMany({
|
||||
where: { isActive: true },
|
||||
include: {
|
||||
lembagas: {
|
||||
where: { isActive: true },
|
||||
include: {
|
||||
_count: { select: { siswa: { where: { isActive: true } } } },
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
prisma.lembaga.count({ where: { isActive: true } }),
|
||||
prisma.pengajar.count({ where: { isActive: true } }),
|
||||
]);
|
||||
|
||||
const perJenjang = jenjangList.map((j) => ({
|
||||
nama: j.nama,
|
||||
jumlahSiswa: j.lembagas.reduce((acc, l) => acc + l._count.siswa, 0),
|
||||
}));
|
||||
|
||||
return {
|
||||
success: true,
|
||||
data: { perJenjang, jumlahLembaga, jumlahPengajar },
|
||||
};
|
||||
} catch (e) {
|
||||
console.error("pendidikanRingkasanStats error:", e);
|
||||
return { success: false, message: "Gagal menghitung statistik pendidikan" };
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user