feat(pendidikan): tambah state ringkasan pendidikan & beasiswa + expand seeder beasiswa 45 entry

- Tambah ringkasan-pendidikan.ts: state valtio fetch GET /api/pendidikan/ringkasan/stats
- Tambah ringkasan-beasiswa.ts: state valtio fetch ringkasan stats + beasiswaConfig find/update
- Expand beasiswa-pendaftar.json dari 3 → 45 entry (nama Bali, NIK unik, enum valid)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-06 11:03:11 +08:00
parent 8857853baf
commit d7e1192ab0
3 changed files with 1017 additions and 9 deletions

View File

@@ -0,0 +1,91 @@
import { toast } from "react-toastify";
import { proxy } from "valtio";
type StatsBeasiswa = {
jumlahPenerima: number;
danaTersalurkan: string;
tahunAjaran: string;
};
type BeasiswaConfig = {
id: string;
tahunAjaran: string;
danaTersalurkan: string;
};
const ringkasanBeasiswaState = proxy({
findStats: {
data: null as StatsBeasiswa | null,
loading: false,
async load() {
try {
ringkasanBeasiswaState.findStats.loading = true;
const res = await fetch(`/api/pendidikan/beasiswa/ringkasan/stats`);
if (res.ok) {
const result = await res.json();
ringkasanBeasiswaState.findStats.data = result?.data ?? null;
} else {
ringkasanBeasiswaState.findStats.data = null;
}
} catch (error) {
console.error("Error fetching ringkasan beasiswa:", error);
ringkasanBeasiswaState.findStats.data = null;
} finally {
ringkasanBeasiswaState.findStats.loading = false;
}
},
},
beasiswaConfig: {
data: null as BeasiswaConfig | null,
loading: false,
async find() {
try {
ringkasanBeasiswaState.beasiswaConfig.loading = true;
const res = await fetch(`/api/pendidikan/beasiswa/beasiswaconfig/find`);
if (res.ok) {
const result = await res.json();
ringkasanBeasiswaState.beasiswaConfig.data = result?.data ?? null;
} else {
ringkasanBeasiswaState.beasiswaConfig.data = null;
}
} catch (error) {
console.error("Error fetching beasiswa config:", error);
ringkasanBeasiswaState.beasiswaConfig.data = null;
} finally {
ringkasanBeasiswaState.beasiswaConfig.loading = false;
}
},
update: {
loading: false,
async submit(tahunAjaran: string, danaTersalurkan: string) {
try {
ringkasanBeasiswaState.beasiswaConfig.update.loading = true;
const res = await fetch(`/api/pendidikan/beasiswa/beasiswaconfig/update`, {
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ tahunAjaran, danaTersalurkan }),
});
const result = await res.json();
if (result.success) {
toast.success("Konfigurasi beasiswa berhasil disimpan");
await ringkasanBeasiswaState.beasiswaConfig.find();
await ringkasanBeasiswaState.findStats.load();
return true;
}
toast.error(result.message || "Gagal menyimpan konfigurasi");
return false;
} catch (error) {
console.error("Error updating beasiswa config:", error);
toast.error("Gagal menyimpan konfigurasi beasiswa");
return false;
} finally {
ringkasanBeasiswaState.beasiswaConfig.update.loading = false;
}
},
},
},
});
export default ringkasanBeasiswaState;

View File

@@ -0,0 +1,35 @@
import { proxy } from "valtio";
type PerJenjang = { nama: string; jumlahSiswa: number };
type StatsPendidikan = {
perJenjang: PerJenjang[];
jumlahLembaga: number;
jumlahPengajar: number;
};
const ringkasanPendidikanState = proxy({
findStats: {
data: null as StatsPendidikan | null,
loading: false,
async load() {
try {
ringkasanPendidikanState.findStats.loading = true;
const res = await fetch(`/api/pendidikan/ringkasan/stats`);
if (res.ok) {
const result = await res.json();
ringkasanPendidikanState.findStats.data = result?.data ?? null;
} else {
ringkasanPendidikanState.findStats.data = null;
}
} catch (error) {
console.error("Error fetching ringkasan pendidikan:", error);
ringkasanPendidikanState.findStats.data = null;
} finally {
ringkasanPendidikanState.findStats.loading = false;
}
},
},
});
export default ringkasanPendidikanState;