diff --git a/prisma/schema.prisma b/prisma/schema.prisma index c6c47265..db494111 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -1791,3 +1791,50 @@ model NilaiKonservasiAdat { deletedAt DateTime @default(now()) isActive Boolean @default(true) } + +// ========================================= MENU PENDIDIKAN ========================================= // +// ========================================= INFO SEKOLAH & PAUD ========================================= // +model JenjangPendidikan { + id String @id @default(cuid()) + nama String // TK/PAUD, SD, SMP, SMA/SMK + lembagas Lembaga[] // Relasi ke lembaga + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + deletedAt DateTime @default(now()) + isActive Boolean @default(true) +} + +model Lembaga { + id String @id @default(cuid()) + nama String + jenjangPendidikan JenjangPendidikan @relation(fields: [jenjangId], references: [id]) + jenjangId String + siswa Siswa[] + pengajar Pengajar[] + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + deletedAt DateTime @default(now()) + isActive Boolean @default(true) +} + +model Siswa { + id String @id @default(cuid()) + nama String + lembaga Lembaga @relation(fields: [lembagaId], references: [id]) + lembagaId String + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + deletedAt DateTime @default(now()) + isActive Boolean @default(true) +} + +model Pengajar { + id String @id @default(cuid()) + nama String + lembaga Lembaga @relation(fields: [lembagaId], references: [id]) + lembagaId String + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + deletedAt DateTime @default(now()) + isActive Boolean @default(true) +} diff --git a/src/app/admin/(dashboard)/_state/pendidikan/info-sekolah-paud.ts b/src/app/admin/(dashboard)/_state/pendidikan/info-sekolah-paud.ts new file mode 100644 index 00000000..c0bd624f --- /dev/null +++ b/src/app/admin/(dashboard)/_state/pendidikan/info-sekolah-paud.ts @@ -0,0 +1,998 @@ +import ApiFetch from "@/lib/api-fetch"; +import { Prisma } from "@prisma/client"; +import { toast } from "react-toastify"; +import { proxy } from "valtio"; +import { z } from "zod"; + +// ========================================= JENJANG PENDIDIKAN ========================================= // +const jenjangPendidikanForm = z.object({ + nama: z.string().min(1, "Nama minimal 1 karakter"), +}); + +const jenjangPendidikanDefaultForm = { + nama: "", +}; + +const jenjangPendidikan = proxy({ + create: { + form: { ...jenjangPendidikanDefaultForm }, + loading: false, + async create() { + const cek = jenjangPendidikanForm.safeParse( + jenjangPendidikan.create.form + ); + if (!cek.success) { + const err = `[${cek.error.issues + .map((v) => `${v.path.join(".")}`) + .join("\n")}] required`; + return toast.error(err); + } + try { + jenjangPendidikan.create.loading = true; + const res = + await ApiFetch.api.pendidikan.infosekolahpaud.jenjangpendidikan[ + "create" + ].post(jenjangPendidikan.create.form); + if (res.status === 200) { + jenjangPendidikan.findMany.load(); + return toast.success("Data berhasil ditambahkan"); + } + return toast.error("Gagal menambahkan data"); + } catch (error) { + console.log(error); + toast.error("Gagal menambahkan data"); + } finally { + jenjangPendidikan.create.loading = false; + } + }, + }, + findMany: { + data: null as Array<{ + id: string; + nama: string; + }> | null, + async load() { + const res = + await ApiFetch.api.pendidikan.infosekolahpaud.jenjangpendidikan[ + "find-many" + ].get(); + if (res.status === 200) { + jenjangPendidikan.findMany.data = res.data?.data ?? []; + } + }, + }, + findUnique: { + data: null as Prisma.JenjangPendidikanGetPayload<{ + omit: { isActive: true }; + }> | null, + async load(id: string) { + try { + const res = await fetch( + `/api/pendidikan/infosekolahpaud/jenjangpendidikan/${id}` + ); + if (res.ok) { + const data = await res.json(); + jenjangPendidikan.findUnique.data = data.data ?? null; + } else { + console.error("Failed to fetch data", res.status, res.statusText); + jenjangPendidikan.findUnique.data = null; + } + } catch (error) { + console.error("Error fetching data:", error); + jenjangPendidikan.findUnique.data = null; + } + }, + }, + delete: { + loading: false, + async byId(id: string) { + if (!id) return toast.warn("ID tidak valid"); + + try { + jenjangPendidikan.delete.loading = true; + + const response = await fetch( + `/api/pendidikan/infosekolahpaud/jenjangpendidikan/del/${id}`, + { + method: "DELETE", + headers: { + "Content-Type": "application/json", + }, + } + ); + + const result = await response.json(); + + if (response.ok && result?.success) { + toast.success( + result.message || "jenjang pendidikan berhasil dihapus" + ); + await jenjangPendidikan.findMany.load(); // refresh list + } else { + toast.error(result?.message || "Gagal menghapus jenjang pendidikan"); + } + } catch (error) { + console.error("Gagal delete:", error); + toast.error("Terjadi kesalahan saat menghapus jenjang pendidikan"); + } finally { + jenjangPendidikan.delete.loading = false; + } + }, + }, + edit: { + id: "", + form: { ...jenjangPendidikanDefaultForm }, + loading: false, + + async load(id: string) { + if (!id) { + toast.warn("ID tidak valid"); + return null; + } + + try { + const response = await fetch( + `/api/pendidikan/infosekolahpaud/jenjangpendidikan/${id}`, + { + method: "GET", + headers: { + "Content-Type": "application/json", + }, + } + ); + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + const result = await response.json(); + if (result?.success) { + const data = result.data; + this.id = data.id; + this.form = { + nama: data.nama, + }; + return data; + } else { + throw new Error(result?.message || "Gagal memuat data"); + } + } catch (error) { + console.error("Error loading jenjang pendidikan:", error); + toast.error( + error instanceof Error ? error.message : "Gagal memuat data" + ); + return null; + } + }, + + async update() { + const cek = jenjangPendidikanForm.safeParse(jenjangPendidikan.edit.form); + if (!cek.success) { + const err = `[${cek.error.issues + .map((v) => `${v.path.join(".")}`) + .join("\n")}] required`; + toast.error(err); + return false; + } + + try { + jenjangPendidikan.edit.loading = true; + const response = await fetch( + `/api/pendidikan/infosekolahpaud/jenjangpendidikan/${jenjangPendidikan.edit.id}`, + { + method: "PUT", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + nama: jenjangPendidikan.edit.form.nama, + }), + } + ); + + // Clone the response to avoid 'body already read' error + const responseClone = response.clone(); + + try { + const result = await response.json(); + + if (!response.ok) { + console.error( + "Update failed with status:", + response.status, + "Response:", + result + ); + throw new Error( + result?.message || + `Gagal mengupdate jenjang pendidikan (${response.status})` + ); + } + + if (result.success) { + toast.success( + result.message || "Berhasil memperbarui jenjang pendidikan" + ); + await jenjangPendidikan.findMany.load(); // refresh list + return true; + } else { + throw new Error( + result.message || "Gagal mengupdate jenjang pendidikan" + ); + } + } catch (error) { + // If JSON parsing fails, try to get the response text for better error messages + try { + const text = await responseClone.text(); + console.error("Error response text:", text); + throw new Error(`Gagal memproses respons dari server: ${text}`); + } catch (textError) { + console.error("Error parsing response as text:", textError); + console.error("Original error:", error); + throw new Error("Gagal memproses respons dari server"); + } + } + } catch (error) { + console.error("Error updating jenjang pendidikan:", error); + toast.error( + error instanceof Error + ? error.message + : "Gagal mengupdate jenjang pendidikan" + ); + return false; + } finally { + jenjangPendidikan.edit.loading = false; + } + }, + reset() { + jenjangPendidikan.edit.id = ""; + jenjangPendidikan.edit.form = { ...jenjangPendidikanDefaultForm }; + }, + }, +}); + +// ========================================= LEMBAGA PENDIDIKAN ========================================= // + +const lembagaPendidikanForm = z.object({ + nama: z.string().min(1, "Nama minimal 1 karakter"), + jenjangId: z.string().min(1, "Jenjang pendidikan minimal 1"), +}); + +const lembagaPendidikanDefaultForm = { + nama: "", + jenjangId: "", +}; + +const lembagaPendidikan = proxy({ + create: { + form: { ...lembagaPendidikanDefaultForm }, + loading: false, + async create() { + const cek = lembagaPendidikanForm.safeParse( + lembagaPendidikan.create.form + ); + if (!cek.success) { + const err = `[${cek.error.issues + .map((v) => `${v.path.join(".")}`) + .join("\n")}] required`; + return toast.error(err); + } + try { + lembagaPendidikan.create.loading = true; + const res = + await ApiFetch.api.pendidikan.infosekolahpaud.lembagapendidikan[ + "create" + ].post(lembagaPendidikan.create.form); + if (res.status === 200) { + lembagaPendidikan.findMany.load(); + return toast.success("Data berhasil ditambahkan"); + } + return toast.error("Gagal menambahkan data"); + } catch (error) { + console.log(error); + toast.error("Gagal menambahkan data"); + } finally { + lembagaPendidikan.create.loading = false; + } + }, + }, + findMany: { + data: null as Array< + Prisma.LembagaGetPayload<{ + include: { + jenjangPendidikan: true; + siswa: true; + pengajar: true; + }; + }> + > | null, + async load() { + const res = + await ApiFetch.api.pendidikan.infosekolahpaud.lembagapendidikan[ + "find-many" + ].get(); + if (res.status === 200) { + lembagaPendidikan.findMany.data = res.data?.data ?? []; + } + }, + }, + findUnique: { + data: null as Prisma.LembagaGetPayload<{ + include: { + jenjangPendidikan: true; + siswa: true; + pengajar: true; + }; + }> | null, + async load(id: string) { + try { + const res = await fetch( + `/api/pendidikan/infosekolahpaud/lembagapendidikan/${id}` + ); + if (res.ok) { + const data = await res.json(); + lembagaPendidikan.findUnique.data = data.data ?? null; + } else { + console.error("Failed to fetch data", res.status, res.statusText); + lembagaPendidikan.findUnique.data = null; + } + } catch (error) { + console.error("Error fetching data:", error); + lembagaPendidikan.findUnique.data = null; + } + }, + }, + delete: { + loading: false, + async byId(id: string) { + if (!id) return toast.warn("ID tidak valid"); + + try { + lembagaPendidikan.delete.loading = true; + + const response = await fetch( + `/api/pendidikan/infosekolahpaud/lembagapendidikan/del/${id}`, + { + method: "DELETE", + headers: { + "Content-Type": "application/json", + }, + } + ); + + const result = await response.json(); + + if (response.ok && result?.success) { + toast.success( + result.message || "lembaga pendidikan berhasil dihapus" + ); + await lembagaPendidikan.findMany.load(); // refresh list + } else { + toast.error(result?.message || "Gagal menghapus lembaga pendidikan"); + } + } catch (error) { + console.error("Gagal delete:", error); + toast.error("Terjadi kesalahan saat menghapus lembaga pendidikan"); + } finally { + lembagaPendidikan.delete.loading = false; + } + }, + }, + edit: { + id: "", + form: { ...lembagaPendidikanDefaultForm }, + loading: false, + + async load(id: string) { + if (!id) { + toast.warn("ID tidak valid"); + return null; + } + + try { + const response = await fetch( + `/api/pendidikan/infosekolahpaud/lembagapendidikan/${id}`, + { + method: "GET", + headers: { + "Content-Type": "application/json", + }, + } + ); + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + const result = await response.json(); + if (result?.success) { + const data = result.data; + this.id = data.id; + this.form = { + nama: data.nama, + jenjangId: data.jenjangId, + }; + return data; + } else { + throw new Error(result?.message || "Gagal memuat data"); + } + } catch (error) { + console.error("Error loading lembaga pendidikan:", error); + toast.error( + error instanceof Error ? error.message : "Gagal memuat data" + ); + return null; + } + }, + + async update() { + const cek = lembagaPendidikanForm.safeParse(lembagaPendidikan.edit.form); + if (!cek.success) { + const err = `[${cek.error.issues + .map((v) => `${v.path.join(".")}`) + .join("\n")}] required`; + toast.error(err); + return false; + } + + try { + lembagaPendidikan.edit.loading = true; + const response = await fetch( + `/api/pendidikan/infosekolahpaud/lembagapendidikan/${lembagaPendidikan.edit.id}`, + { + method: "PUT", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + nama: lembagaPendidikan.edit.form.nama, + jenjangId: lembagaPendidikan.edit.form.jenjangId, + }), + } + ); + + // Clone the response to avoid 'body already read' error + const responseClone = response.clone(); + + try { + const result = await response.json(); + + if (!response.ok) { + console.error( + "Update failed with status:", + response.status, + "Response:", + result + ); + throw new Error( + result?.message || + `Gagal mengupdate lembaga pendidikan (${response.status})` + ); + } + + if (result.success) { + toast.success( + result.message || "Berhasil memperbarui lembaga pendidikan" + ); + await lembagaPendidikan.findMany.load(); // refresh list + return true; + } else { + throw new Error( + result.message || "Gagal mengupdate lembaga pendidikan" + ); + } + } catch (error) { + // If JSON parsing fails, try to get the response text for better error messages + try { + const text = await responseClone.text(); + console.error("Error response text:", text); + throw new Error(`Gagal memproses respons dari server: ${text}`); + } catch (textError) { + console.error("Error parsing response as text:", textError); + console.error("Original error:", error); + throw new Error("Gagal memproses respons dari server"); + } + } + } catch (error) { + console.error("Error updating lembaga pendidikan:", error); + toast.error( + error instanceof Error + ? error.message + : "Gagal mengupdate lembaga pendidikan" + ); + return false; + } finally { + lembagaPendidikan.edit.loading = false; + } + }, + reset() { + lembagaPendidikan.edit.id = ""; + lembagaPendidikan.edit.form = { ...lembagaPendidikanDefaultForm }; + }, + }, +}); + +// ========================================= SISWA ========================================= // + +const siswaForm = z.object({ + nama: z.string().min(1, "Nama minimal 1 karakter"), + lembagaId: z.string().min(1, "lembaga pendidikan minimal 1"), +}); + +const siswaDefaultForm = { + nama: "", + lembagaId: "", +}; + +const siswa = proxy({ + create: { + form: { ...siswaDefaultForm }, + loading: false, + async create() { + const cek = siswaForm.safeParse(siswa.create.form); + if (!cek.success) { + const err = `[${cek.error.issues + .map((v) => `${v.path.join(".")}`) + .join("\n")}] required`; + return toast.error(err); + } + try { + siswa.create.loading = true; + const res = await ApiFetch.api.pendidikan.infosekolahpaud.siswa[ + "create" + ].post(siswa.create.form); + if (res.status === 200) { + siswa.findMany.load(); + return toast.success("Data berhasil ditambahkan"); + } + return toast.error("Gagal menambahkan data"); + } catch (error) { + console.log(error); + toast.error("Gagal menambahkan data"); + } finally { + siswa.create.loading = false; + } + }, + }, + findMany: { + data: null as Array< + Prisma.SiswaGetPayload<{ + include: { + lembaga: true; + }; + }> + > | null, + async load() { + const res = await ApiFetch.api.pendidikan.infosekolahpaud.siswa[ + "find-many" + ].get(); + if (res.status === 200) { + siswa.findMany.data = res.data?.data ?? []; + } + }, + }, + findUnique: { + data: null as Prisma.SiswaGetPayload<{ + include: { + lembaga: true; + }; + }> | null, + async load(id: string) { + try { + const res = await fetch(`/api/pendidikan/infosekolahpaud/siswa/${id}`); + if (res.ok) { + const data = await res.json(); + siswa.findUnique.data = data.data ?? null; + } else { + console.error("Failed to fetch data", res.status, res.statusText); + siswa.findUnique.data = null; + } + } catch (error) { + console.error("Error fetching data:", error); + siswa.findUnique.data = null; + } + }, + }, + delete: { + loading: false, + async byId(id: string) { + if (!id) return toast.warn("ID tidak valid"); + + try { + siswa.delete.loading = true; + + const response = await fetch( + `/api/pendidikan/infosekolahpaud/siswa/del/${id}`, + { + method: "DELETE", + headers: { + "Content-Type": "application/json", + }, + } + ); + + const result = await response.json(); + + if (response.ok && result?.success) { + toast.success(result.message || "siswa berhasil dihapus"); + await siswa.findMany.load(); // refresh list + } else { + toast.error(result?.message || "Gagal menghapus siswa"); + } + } catch (error) { + console.error("Gagal delete:", error); + toast.error("Terjadi kesalahan saat menghapus siswa"); + } finally { + siswa.delete.loading = false; + } + }, + }, + edit: { + id: "", + form: { ...siswaDefaultForm }, + loading: false, + + async load(id: string) { + if (!id) { + toast.warn("ID tidak valid"); + return null; + } + + try { + const response = await fetch( + `/api/pendidikan/infosekolahpaud/siswa/${id}`, + { + method: "GET", + headers: { + "Content-Type": "application/json", + }, + } + ); + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + const result = await response.json(); + if (result?.success) { + const data = result.data; + this.id = data.id; + this.form = { + nama: data.nama, + lembagaId: data.lembagaId, + }; + return data; + } else { + throw new Error(result?.message || "Gagal memuat data"); + } + } catch (error) { + console.error("Error loading siswa:", error); + toast.error( + error instanceof Error ? error.message : "Gagal memuat data" + ); + return null; + } + }, + + async update() { + const cek = siswaForm.safeParse(siswa.edit.form); + if (!cek.success) { + const err = `[${cek.error.issues + .map((v) => `${v.path.join(".")}`) + .join("\n")}] required`; + toast.error(err); + return false; + } + + try { + siswa.edit.loading = true; + const response = await fetch( + `/api/pendidikan/infosekolahpaud/siswa/${siswa.edit.id}`, + { + method: "PUT", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + nama: siswa.edit.form.nama, + lembagaId: siswa.edit.form.lembagaId, + }), + } + ); + + // Clone the response to avoid 'body already read' error + const responseClone = response.clone(); + + try { + const result = await response.json(); + + if (!response.ok) { + console.error( + "Update failed with status:", + response.status, + "Response:", + result + ); + throw new Error( + result?.message || `Gagal mengupdate siswa (${response.status})` + ); + } + + if (result.success) { + toast.success(result.message || "Berhasil memperbarui siswa"); + await siswa.findMany.load(); // refresh list + return true; + } else { + throw new Error(result.message || "Gagal mengupdate siswa"); + } + } catch (error) { + // If JSON parsing fails, try to get the response text for better error messages + try { + const text = await responseClone.text(); + console.error("Error response text:", text); + throw new Error(`Gagal memproses respons dari server: ${text}`); + } catch (textError) { + console.error("Error parsing response as text:", textError); + console.error("Original error:", error); + throw new Error("Gagal memproses respons dari server"); + } + } + } catch (error) { + console.error("Error updating siswa:", error); + toast.error( + error instanceof Error ? error.message : "Gagal mengupdate siswa" + ); + return false; + } finally { + siswa.edit.loading = false; + } + }, + reset() { + siswa.edit.id = ""; + siswa.edit.form = { ...siswaDefaultForm }; + }, + }, +}); + +// ========================================= PENGAJAR ========================================= // + +const pengajarForm = z.object({ + nama: z.string().min(1, "Nama minimal 1 karakter"), + lembagaId: z.string().min(1, "lembaga pendidikan minimal 1"), +}); + +const pengajarDefaultForm = { + nama: "", + lembagaId: "", +}; + +const pengajar = proxy({ + create: { + form: { ...pengajarDefaultForm }, + loading: false, + async create() { + const cek = pengajarForm.safeParse(pengajar.create.form); + if (!cek.success) { + const err = `[${cek.error.issues + .map((v) => `${v.path.join(".")}`) + .join("\n")}] required`; + return toast.error(err); + } + try { + pengajar.create.loading = true; + const res = await ApiFetch.api.pendidikan.infosekolahpaud.pengajar[ + "create" + ].post(pengajar.create.form); + if (res.status === 200) { + pengajar.findMany.load(); + return toast.success("Data berhasil ditambahkan"); + } + return toast.error("Gagal menambahkan data"); + } catch (error) { + console.log(error); + toast.error("Gagal menambahkan data"); + } finally { + pengajar.create.loading = false; + } + }, + }, + findMany: { + data: null as Array< + Prisma.PengajarGetPayload<{ + include: { + lembaga: true; + }; + }> + > | null, + async load() { + const res = await ApiFetch.api.pendidikan.infosekolahpaud.pengajar[ + "find-many" + ].get(); + if (res.status === 200) { + pengajar.findMany.data = res.data?.data ?? []; + } + }, + }, + findUnique: { + data: null as Prisma.PengajarGetPayload<{ + include: { + lembaga: true; + }; + }> | null, + async load(id: string) { + try { + const res = await fetch(`/api/pendidikan/infosekolahpaud/pengajar/${id}`); + if (res.ok) { + const data = await res.json(); + pengajar.findUnique.data = data.data ?? null; + } else { + console.error("Failed to fetch data", res.status, res.statusText); + pengajar.findUnique.data = null; + } + } catch (error) { + console.error("Error fetching data:", error); + pengajar.findUnique.data = null; + } + }, + }, + delete: { + loading: false, + async byId(id: string) { + if (!id) return toast.warn("ID tidak valid"); + + try { + pengajar.delete.loading = true; + + const response = await fetch( + `/api/pendidikan/infosekolahpaud/pengajar/del/${id}`, + { + method: "DELETE", + headers: { + "Content-Type": "application/json", + }, + } + ); + + const result = await response.json(); + + if (response.ok && result?.success) { + toast.success(result.message || "pengajar berhasil dihapus"); + await pengajar.findMany.load(); // refresh list + } else { + toast.error(result?.message || "Gagal menghapus pengajar"); + } + } catch (error) { + console.error("Gagal delete:", error); + toast.error("Terjadi kesalahan saat menghapus pengajar"); + } finally { + pengajar.delete.loading = false; + } + }, + }, + edit: { + id: "", + form: { ...pengajarDefaultForm }, + loading: false, + + async load(id: string) { + if (!id) { + toast.warn("ID tidak valid"); + return null; + } + + try { + const response = await fetch( + `/api/pendidikan/infosekolahpaud/pengajar/${id}`, + { + method: "GET", + headers: { + "Content-Type": "application/json", + }, + } + ); + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + const result = await response.json(); + if (result?.success) { + const data = result.data; + this.id = data.id; + this.form = { + nama: data.nama, + lembagaId: data.lembagaId, + }; + return data; + } else { + throw new Error(result?.message || "Gagal memuat data"); + } + } catch (error) { + console.error("Error loading siswa:", error); + toast.error( + error instanceof Error ? error.message : "Gagal memuat data" + ); + return null; + } + }, + + async update() { + const cek = pengajarForm.safeParse(pengajar.edit.form); + if (!cek.success) { + const err = `[${cek.error.issues + .map((v) => `${v.path.join(".")}`) + .join("\n")}] required`; + toast.error(err); + return false; + } + + try { + pengajar.edit.loading = true; + const response = await fetch( + `/api/pendidikan/infosekolahpaud/pengajar/${pengajar.edit.id}`, + { + method: "PUT", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + nama: pengajar.edit.form.nama, + lembagaId: pengajar.edit.form.lembagaId, + }), + } + ); + + // Clone the response to avoid 'body already read' error + const responseClone = response.clone(); + + try { + const result = await response.json(); + + if (!response.ok) { + console.error( + "Update failed with status:", + response.status, + "Response:", + result + ); + throw new Error( + result?.message || `Gagal mengupdate pengajar (${response.status})` + ); + } + + if (result.success) { + toast.success(result.message || "Berhasil memperbarui pengajar"); + await pengajar.findMany.load(); // refresh list + return true; + } else { + throw new Error(result.message || "Gagal mengupdate pengajar"); + } + } catch (error) { + // If JSON parsing fails, try to get the response text for better error messages + try { + const text = await responseClone.text(); + console.error("Error response text:", text); + throw new Error(`Gagal memproses respons dari server: ${text}`); + } catch (textError) { + console.error("Error parsing response as text:", textError); + console.error("Original error:", error); + throw new Error("Gagal memproses respons dari server"); + } + } + } catch (error) { + console.error("Error updating pengajar:", error); + toast.error( + error instanceof Error ? error.message : "Gagal mengupdate pengajar" + ); + return false; + } finally { + pengajar.edit.loading = false; + } + }, + reset() { + pengajar.edit.id = ""; + pengajar.edit.form = { ...pengajarDefaultForm }; + }, + }, +}); + +const infoSekolahPaud = proxy({ + jenjangPendidikan, + lembagaPendidikan, + siswa, + pengajar, +}); + +export default infoSekolahPaud; diff --git a/src/app/admin/(dashboard)/pendidikan/info-sekolah-paud/_lib/layoutTabs.tsx b/src/app/admin/(dashboard)/pendidikan/info-sekolah-paud/_lib/layoutTabs.tsx new file mode 100644 index 00000000..84fa4515 --- /dev/null +++ b/src/app/admin/(dashboard)/pendidikan/info-sekolah-paud/_lib/layoutTabs.tsx @@ -0,0 +1,72 @@ +/* eslint-disable react-hooks/exhaustive-deps */ +'use client' +import colors from '@/con/colors'; +import { Stack, Tabs, TabsList, TabsPanel, TabsTab, Title } from '@mantine/core'; +import { usePathname, useRouter } from 'next/navigation'; +import React, { useEffect, useState } from 'react'; + +function LayoutTabs({ children }: { children: React.ReactNode }) { + const router = useRouter() + const pathname = usePathname() + const tabs = [ + { + label: "Jenjang Pendidikan", + value: "jenjangPendidikan", + href: "/admin/pendidikan/info-sekolah-paud/jenjang-pendidikan" + }, + { + label: "Lembaga", + value: "lembaga", + href: "/admin/pendidikan/info-sekolah-paud/lembaga" + }, + { + label: "Siswa", + value: "siswa", + href: "/admin/pendidikan/info-sekolah-paud/siswa" + }, + { + label: "Pengajar", + value: "pengajar", + href: "/admin/pendidikan/info-sekolah-paud/pengajar" + }, + ]; + const curentTab = tabs.find(tab => tab.href === pathname) + const [activeTab, setActiveTab] = useState(curentTab?.value || tabs[0].value); + + const handleTabChange = (value: string | null) => { + const tab = tabs.find(t => t.value === value) + if (tab) { + router.push(tab.href) + } + setActiveTab(value) + } + + useEffect(() => { + const match = tabs.find(tab => tab.href === pathname) + if (match) { + setActiveTab(match.value) + } + }, [pathname]) + + return ( + + Info Sekolah & PAUD + + + {tabs.map((e, i) => ( + {e.label} + ))} + + {tabs.map((e, i) => ( + + {/* Konten dummy, bisa diganti tergantung routing */} + <> + + ))} + + {children} + + ); +} + +export default LayoutTabs; \ No newline at end of file diff --git a/src/app/admin/(dashboard)/pendidikan/info-sekolah-paud/jenjang-pendidikan/[id]/page.tsx b/src/app/admin/(dashboard)/pendidikan/info-sekolah-paud/jenjang-pendidikan/[id]/page.tsx new file mode 100644 index 00000000..acf48fc8 --- /dev/null +++ b/src/app/admin/(dashboard)/pendidikan/info-sekolah-paud/jenjang-pendidikan/[id]/page.tsx @@ -0,0 +1,98 @@ +/* eslint-disable react-hooks/exhaustive-deps */ +'use client' +import infoSekolahPaud from '@/app/admin/(dashboard)/_state/pendidikan/info-sekolah-paud'; +import colors from '@/con/colors'; +import { Box, Button, Group, Paper, Stack, Text, TextInput, Title } from '@mantine/core'; +import { IconArrowBack } from '@tabler/icons-react'; +import { useParams, useRouter } from 'next/navigation'; +import { useEffect, useState } from 'react'; +import { toast } from 'react-toastify'; +import { useProxy } from 'valtio/utils'; + +function EditJenjangPendidikan() { + const router = useRouter(); + const params = useParams(); + const id = params?.id as string; + const stateJenjang = useProxy(infoSekolahPaud.jenjangPendidikan); + + const [formData, setFormData] = useState({ + nama: "", + }); + + useEffect(() => { + const loadJenjangPendidikan = async () => { + if (!id) return; + + try { + const data = await stateJenjang.edit.load(id); + + if (data) { + // pastikan id-nya masuk ke state edit + stateJenjang.edit.id = id; + setFormData({ + nama: data.nama || '', + }); + } + } catch (error) { + console.error("Error loading jenjang pendidikan:", error); + toast.error("Gagal memuat data jenjang pendidikan"); + } + }; + + loadJenjangPendidikan(); + }, [id]); + + const handleSubmit = async () => { + try { + if (!formData.nama.trim()) { + toast.error('Nama jenjang pendidikan tidak boleh kosong'); + return; + } + + stateJenjang.edit.form = { + nama: formData.nama.trim(), + }; + + // Safety check tambahan: pastikan ID tidak kosong + if (!stateJenjang.edit.id) { + stateJenjang.edit.id = id; // fallback + } + + const success = await stateJenjang.edit.update(); + + if (success) { + router.push("/admin/pendidikan/info-sekolah-paud/jenjang-pendidikan"); + } + } catch (error) { + console.error("Error updating jenjang pendidikan:", error); + // toast akan ditampilkan dari fungsi update + } + }; + + return ( + + + + + + + + Edit Jenjang Pendidikan + setFormData({ ...formData, nama: e.target.value })} + label={Nama Jenjang Pendidikan} + placeholder='Masukkan nama jenjang pendidikan' + /> + + + + + + + ); +} + +export default EditJenjangPendidikan; diff --git a/src/app/admin/(dashboard)/pendidikan/info-sekolah-paud/jenjang-pendidikan/create/page.tsx b/src/app/admin/(dashboard)/pendidikan/info-sekolah-paud/jenjang-pendidikan/create/page.tsx new file mode 100644 index 00000000..28aa5a0d --- /dev/null +++ b/src/app/admin/(dashboard)/pendidikan/info-sekolah-paud/jenjang-pendidikan/create/page.tsx @@ -0,0 +1,61 @@ +/* eslint-disable react-hooks/exhaustive-deps */ +'use client' +import colors from '@/con/colors'; +import { Box, Button, Group, Paper, Stack, Text, TextInput, Title } from '@mantine/core'; +import { IconArrowBack } from '@tabler/icons-react'; +import { useRouter } from 'next/navigation'; +import { useEffect } from 'react'; +import { useProxy } from 'valtio/utils'; +import infoSekolahPaud from '../../../../_state/pendidikan/info-sekolah-paud'; + +function CreateJenjangPendidikan() { + const router = useRouter(); + const stateJenjang = useProxy(infoSekolahPaud.jenjangPendidikan) + + useEffect(() => { + stateJenjang.findMany.load(); + }, []); + + const resetForm = () => { + stateJenjang.create.form = { + nama: "", + }; + } + + const handleSubmit = async () => { + await stateJenjang.create.create(); + resetForm(); + router.push("/admin/pendidikan/info-sekolah-paud/jenjang-pendidikan") + } + + return ( + + + + + + + + + Create Jenjang Pendidikan + { + stateJenjang.create.form.nama = val.target.value; + }} + label={Nama Jenjang Pendidikan} + placeholder='Masukkan nama jenjang pendidikan' + /> + + + + + + + + ); +} + +export default CreateJenjangPendidikan; diff --git a/src/app/admin/(dashboard)/pendidikan/info-sekolah-paud/jenjang-pendidikan/page.tsx b/src/app/admin/(dashboard)/pendidikan/info-sekolah-paud/jenjang-pendidikan/page.tsx new file mode 100644 index 00000000..ce6acd9e --- /dev/null +++ b/src/app/admin/(dashboard)/pendidikan/info-sekolah-paud/jenjang-pendidikan/page.tsx @@ -0,0 +1,112 @@ +'use client' +import colors from '@/con/colors'; +import { Box, Button, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr } from '@mantine/core'; +import { useShallowEffect } from '@mantine/hooks'; +import { IconEdit, IconSearch, IconX } from '@tabler/icons-react'; +import { useRouter } from 'next/navigation'; +import { useState } from 'react'; +import { useProxy } from 'valtio/utils'; +import HeaderSearch from '../../../_com/header'; +import JudulList from '../../../_com/judulList'; +import { ModalKonfirmasiHapus } from '../../../_com/modalKonfirmasiHapus'; +import infoSekolahPaud from '../../../_state/pendidikan/info-sekolah-paud'; + + +function JenjangPendidikan() { + const [search, setSearch] = useState("") + return ( + + } + value={search} + onChange={(e) => setSearch(e.currentTarget.value)} + /> + + + ); +} + +function ListJenjangPendidikan({ search }: { search: string }) { + const stateList = useProxy(infoSekolahPaud.jenjangPendidikan) + const [modalHapus, setModalHapus] = useState(false) + const [selectedId, setSelectedId] = useState(null) + const router = useRouter() + + const handleHapus = () => { + if (selectedId) { + stateList.delete.byId(selectedId) + setModalHapus(false) + setSelectedId(null) + } + } + + useShallowEffect(() => { + stateList.findMany.load() + }, []) + + const filteredData = (stateList.findMany.data || []).filter(item => { + const keyword = search.toLowerCase(); + return ( + item.nama.toLowerCase().includes(keyword) + ); + }); + + if (!stateList.findMany.data) { + return ( + + + + ) + } + + return ( + + + + + + + Nama Jenjang Pendidikan + Edit + Delete + + + + {filteredData.map((item) => ( + + {item.nama} + + + + + + + + ))} + +
+
+ {/* Modal Konfirmasi Hapus */} + setModalHapus(false)} + onConfirm={handleHapus} + text='Apakah anda yakin ingin menghapus jenjang pendidikan ini?' + /> +
+ ); +} + +export default JenjangPendidikan; diff --git a/src/app/admin/(dashboard)/pendidikan/info-sekolah-paud/layout.tsx b/src/app/admin/(dashboard)/pendidikan/info-sekolah-paud/layout.tsx new file mode 100644 index 00000000..3a414c77 --- /dev/null +++ b/src/app/admin/(dashboard)/pendidikan/info-sekolah-paud/layout.tsx @@ -0,0 +1,12 @@ +import React from 'react'; +import LayoutTabs from './_lib/layoutTabs'; + +function Layout({ children }: { children: React.ReactNode }) { + return ( + + {children} + + ); +} + +export default Layout; diff --git a/src/app/admin/(dashboard)/pendidikan/info-sekolah-paud/lembaga/[id]/edit/page.tsx b/src/app/admin/(dashboard)/pendidikan/info-sekolah-paud/lembaga/[id]/edit/page.tsx new file mode 100644 index 00000000..406cc46c --- /dev/null +++ b/src/app/admin/(dashboard)/pendidikan/info-sekolah-paud/lembaga/[id]/edit/page.tsx @@ -0,0 +1,89 @@ +/* eslint-disable react-hooks/exhaustive-deps */ +'use client'; + +import { useEffect, useState } from 'react'; +import { useParams, useRouter } from 'next/navigation'; +import { Box, Button, Paper, Select, Stack, TextInput, Title } from '@mantine/core'; +import { toast } from 'react-toastify'; +import { useProxy } from 'valtio/utils'; +import infoSekolahPaud from '@/app/admin/(dashboard)/_state/pendidikan/info-sekolah-paud'; +import colors from '@/con/colors'; +import { IconArrowBack } from '@tabler/icons-react'; + + +export default function EditLembaga() { + const router = useRouter(); + const { id } = useParams<{ id: string }>(); + const state = useProxy(infoSekolahPaud.lembagaPendidikan); + const jenjangPendidikanList = infoSekolahPaud.jenjangPendidikan.findMany.data; + + const [form, setForm] = useState({ + nama: '', + jenjangId: '', + }); + + useEffect(() => { + infoSekolahPaud.jenjangPendidikan.findMany.load(); + + if (id) { + state.edit.load(id).then(data => { + if (data) { + setForm({ + nama: data.nama, + jenjangId: data.jenjangId, + }); + } + }); + } + }, [id]); + + const handleSubmit = async () => { + if (!form.nama || !form.jenjangId) { + toast.warn("Nama dan jenjang pendidikan harus diisi"); + return; + } + + state.edit.id = id; + state.edit.form = form; + + const result = await state.edit.update(); + + if (result) { + toast.success("Data berhasil diperbarui"); + router.push('/admin/pendidikan/info-sekolah-paud/lembaga'); + } + }; + + return ( + + + + + + + Edit Lembaga + setForm({ ...form, nama: e.currentTarget.value })} + /> + ({ + value: j.id, + label: j.nama, + })) || []} + value={stateLembaga.create.form.jenjangId} + onChange={(val) => { + stateLembaga.create.form.jenjangId = val || ''; + }} + /> + + + + + + + + ); +} + +export default CreateLembaga; diff --git a/src/app/admin/(dashboard)/pendidikan/info-sekolah-paud/lembaga/page.tsx b/src/app/admin/(dashboard)/pendidikan/info-sekolah-paud/lembaga/page.tsx new file mode 100644 index 00000000..ec956a3d --- /dev/null +++ b/src/app/admin/(dashboard)/pendidikan/info-sekolah-paud/lembaga/page.tsx @@ -0,0 +1,89 @@ +'use client' +import colors from '@/con/colors'; +import { Box, Button, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr } from '@mantine/core'; +import { useShallowEffect } from '@mantine/hooks'; +import { IconDeviceImac, IconSearch } from '@tabler/icons-react'; +import { useRouter } from 'next/navigation'; +import { useState } from 'react'; +import { useProxy } from 'valtio/utils'; +import HeaderSearch from '../../../_com/header'; +import JudulList from '../../../_com/judulList'; +import infoSekolahPaud from '../../../_state/pendidikan/info-sekolah-paud'; + + +function Lembaga() { + const [search, setSearch] = useState("") + return ( + + } + value={search} + onChange={(e) => setSearch(e.currentTarget.value)} + /> + + + ); +} + +function ListLembaga({ search }: { search: string }) { + const stateList = useProxy(infoSekolahPaud.lembagaPendidikan) + const router = useRouter() + + + useShallowEffect(() => { + stateList.findMany.load() + }, []) + + const filteredData = (stateList.findMany.data || []).filter(item => { + const keyword = search.toLowerCase(); + return ( + item.nama.toLowerCase().includes(keyword) || + item.jenjangPendidikan?.nama.toLowerCase().includes(keyword) + ); + }); + + if (!stateList.findMany.data) { + return ( + + + + ) + } + + return ( + + + + + + + Nama Lembaga + Jenjang Pendidikan + Detail + + + + {filteredData.map((item) => ( + + {item.nama} + {item.jenjangPendidikan?.nama} + + + + + ))} + +
+
+
+ ); +} + +export default Lembaga; diff --git a/src/app/admin/(dashboard)/pendidikan/info-sekolah-paud/page.tsx b/src/app/admin/(dashboard)/pendidikan/info-sekolah-paud/page.tsx deleted file mode 100644 index d20a948c..00000000 --- a/src/app/admin/(dashboard)/pendidikan/info-sekolah-paud/page.tsx +++ /dev/null @@ -1,69 +0,0 @@ -'use client' -import { Box, Button, Image, Paper, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core'; -import React from 'react'; -import HeaderSearch from '../../_com/header'; -import { IconDeviceImacCog, IconSearch } from '@tabler/icons-react'; -import colors from '@/con/colors'; -import JudulList from '../../_com/judulList'; -import { useRouter } from 'next/navigation'; - -function InfoSekolahPaud() { - return ( - - } - /> - - - ); -} - -function ListInfoSekolahPaud() { - const router = useRouter(); - return ( - - - - - - - - - Nama Sekolah PAUD - Gambar - Deskripsi - Detail - - - - - - - Sekolah PAUD - - - - - - Deskripsi - - - - - -
-
-
-
-
- ) -} - -export default InfoSekolahPaud; diff --git a/src/app/admin/(dashboard)/pendidikan/info-sekolah-paud/pengajar/[id]/edit/page.tsx b/src/app/admin/(dashboard)/pendidikan/info-sekolah-paud/pengajar/[id]/edit/page.tsx new file mode 100644 index 00000000..2dec3709 --- /dev/null +++ b/src/app/admin/(dashboard)/pendidikan/info-sekolah-paud/pengajar/[id]/edit/page.tsx @@ -0,0 +1,100 @@ +/* eslint-disable react-hooks/exhaustive-deps */ +'use client' +import infoSekolahPaud from '@/app/admin/(dashboard)/_state/pendidikan/info-sekolah-paud'; +import colors from '@/con/colors'; +import { Box, Button, Paper, Select, Stack, Text, TextInput, Title } from '@mantine/core'; +import { IconArrowBack } from '@tabler/icons-react'; +import { useParams, useRouter } from 'next/navigation'; +import { useEffect, useState } from 'react'; +import { toast } from 'react-toastify'; +import { useProxy } from 'valtio/utils'; + + +interface FormPengajar { + nama: string; + lembagaId: string; +} + +function EditPengajar() { + const pengajarState = useProxy(infoSekolahPaud.pengajar) + const params = useParams() + const router = useRouter() + + const [formData, setFormData] = useState({ + nama: '', + lembagaId: '' + }) + + useEffect(() => { + const loadPengajar = async () => { + const id = params?.id as string; + if (!id) return; + + try { + const data = await pengajarState.edit.load(id); + if (data) { + setFormData({ + nama: data.nama || '', + lembagaId: data.lembagaId || '', + }); + } + } catch (error) { + console.error("Error loading pengajar:", error); + toast.error("Gagal memuat data pengajar"); + } + } + infoSekolahPaud.lembagaPendidikan.findMany.load() + loadPengajar(); + }, [params?.id]); + + const handleSubmit = async () => { + try { + pengajarState.edit.form = { + ...pengajarState.edit.form, + nama: formData.nama.trim(), + lembagaId: formData.lembagaId.trim(), + } + await pengajarState.edit.update() + router.push("/admin/pendidikan/info-sekolah-paud/pengajar"); + } catch (error) { + console.error("Error updating pengajar:", error); + toast.error("Terjadi kesalahan saat memperbarui pengajar"); + } + } + + return ( + + + + + + + + Edit Pengajar + Nama Pengajar} + placeholder="masukkan nama siswa" + onChange={(e) => setFormData({ ...formData, nama: e.target.value })} + /> + { + stateCreate.create.form.lembagaId = val ?? ""; + }} + label={Lembaga} + placeholder="Pilih lembaga" + data={ + infoSekolahPaud.lembagaPendidikan.findMany.data?.map((v) => ({ + value: v.id, + label: v.nama, + })) || [] + } + /> + + + + + + + ); +} + +export default CreatePengajar; diff --git a/src/app/admin/(dashboard)/pendidikan/info-sekolah-paud/pengajar/page.tsx b/src/app/admin/(dashboard)/pendidikan/info-sekolah-paud/pengajar/page.tsx new file mode 100644 index 00000000..16c8539e --- /dev/null +++ b/src/app/admin/(dashboard)/pendidikan/info-sekolah-paud/pengajar/page.tsx @@ -0,0 +1,90 @@ +/* eslint-disable react-hooks/exhaustive-deps */ +'use client' +import colors from '@/con/colors'; +import { Box, Button, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr } from '@mantine/core'; +import { IconDeviceImacCog, IconSearch } from '@tabler/icons-react'; +import { useRouter } from 'next/navigation'; +import { useEffect, useState } from 'react'; +import { useProxy } from 'valtio/utils'; +import HeaderSearch from '../../../_com/header'; +import JudulList from '../../../_com/judulList'; +import infoSekolahPaud from '../../../_state/pendidikan/info-sekolah-paud'; + +function Pengajar() { + const [search, setSearch] = useState(''); + return ( + + } + value={search} + onChange={(e) => setSearch(e.currentTarget.value)} + /> + + + ); +} + +function ListPengajar({ search }: { search: string }) { + const listState = useProxy(infoSekolahPaud.pengajar) + const router = useRouter(); + useEffect(() => { + listState.findMany.load() + }, []) + + const filteredData = (listState.findMany.data || []).filter(item => { + const keyword = search.toLowerCase(); + return ( + item.nama.toLowerCase().includes(keyword) || + item.lembaga.nama.toLowerCase().includes(keyword) + ); + }); + + if (!listState.findMany.data) { + return ( + + + + ) + } + + return ( + + + + + + + + + Nama Pengajar + Lembaga + Detail + + + + {filteredData.map((item) => ( + + {item.nama} + {item.lembaga.nama} + + + + + ))} + +
+
+
+
+
+ ) +} + +export default Pengajar; diff --git a/src/app/admin/(dashboard)/pendidikan/info-sekolah-paud/siswa/[id]/edit/page.tsx b/src/app/admin/(dashboard)/pendidikan/info-sekolah-paud/siswa/[id]/edit/page.tsx new file mode 100644 index 00000000..5769f8c1 --- /dev/null +++ b/src/app/admin/(dashboard)/pendidikan/info-sekolah-paud/siswa/[id]/edit/page.tsx @@ -0,0 +1,100 @@ +/* eslint-disable react-hooks/exhaustive-deps */ +'use client' +import infoSekolahPaud from '@/app/admin/(dashboard)/_state/pendidikan/info-sekolah-paud'; +import colors from '@/con/colors'; +import { Box, Button, Paper, Select, Stack, Text, TextInput, Title } from '@mantine/core'; +import { IconArrowBack } from '@tabler/icons-react'; +import { useParams, useRouter } from 'next/navigation'; +import { useEffect, useState } from 'react'; +import { toast } from 'react-toastify'; +import { useProxy } from 'valtio/utils'; + + +interface FormSiswa { + nama: string; + lembagaId: string; +} + +function EditSiswa() { + const siswaState = useProxy(infoSekolahPaud.siswa) + const params = useParams() + const router = useRouter() + + const [formData, setFormData] = useState({ + nama: '', + lembagaId: '' + }) + + useEffect(() => { + const loadSiswa = async () => { + const id = params?.id as string; + if (!id) return; + + try { + const data = await siswaState.edit.load(id); + if (data) { + setFormData({ + nama: data.nama || '', + lembagaId: data.lembagaId || '', + }); + } + } catch (error) { + console.error("Error loading siswa:", error); + toast.error("Gagal memuat data siswa"); + } + } + infoSekolahPaud.lembagaPendidikan.findMany.load() + loadSiswa(); + }, [params?.id]); + + const handleSubmit = async () => { + try { + siswaState.edit.form = { + ...siswaState.edit.form, + nama: formData.nama.trim(), + lembagaId: formData.lembagaId.trim(), + } + await siswaState.edit.update() + router.push("/admin/pendidikan/info-sekolah-paud/siswa"); + } catch (error) { + console.error("Error updating siswa:", error); + toast.error("Terjadi kesalahan saat memperbarui siswa"); + } + } + + return ( + + + + + + + + Edit Siswa + Nama Siswa} + placeholder="masukkan nama siswa" + onChange={(e) => setFormData({ ...formData, nama: e.target.value })} + /> + { + stateCreate.create.form.lembagaId = val ?? ""; + }} + label={Lembaga} + placeholder="Pilih lembaga" + data={ + infoSekolahPaud.lembagaPendidikan.findMany.data?.map((v) => ({ + value: v.id, + label: v.nama, + })) || [] + } + /> + + + + + + + ); +} + +export default CreateSiswa; diff --git a/src/app/admin/(dashboard)/pendidikan/info-sekolah-paud/siswa/page.tsx b/src/app/admin/(dashboard)/pendidikan/info-sekolah-paud/siswa/page.tsx new file mode 100644 index 00000000..a94ef631 --- /dev/null +++ b/src/app/admin/(dashboard)/pendidikan/info-sekolah-paud/siswa/page.tsx @@ -0,0 +1,90 @@ +/* eslint-disable react-hooks/exhaustive-deps */ +'use client' +import colors from '@/con/colors'; +import { Box, Button, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr } from '@mantine/core'; +import { IconDeviceImacCog, IconSearch } from '@tabler/icons-react'; +import { useRouter } from 'next/navigation'; +import { useEffect, useState } from 'react'; +import { useProxy } from 'valtio/utils'; +import HeaderSearch from '../../../_com/header'; +import JudulList from '../../../_com/judulList'; +import infoSekolahPaud from '../../../_state/pendidikan/info-sekolah-paud'; + +function Siswa() { + const [search, setSearch] = useState(''); + return ( + + } + value={search} + onChange={(e) => setSearch(e.currentTarget.value)} + /> + + + ); +} + +function ListSiswa({ search }: { search: string }) { + const listState = useProxy(infoSekolahPaud.siswa) + const router = useRouter(); + useEffect(() => { + listState.findMany.load() + }, []) + + const filteredData = (listState.findMany.data || []).filter(item => { + const keyword = search.toLowerCase(); + return ( + item.nama.toLowerCase().includes(keyword) || + item.lembaga.nama.toLowerCase().includes(keyword) + ); + }); + + if (!listState.findMany.data) { + return ( + + + + ) + } + + return ( + + + + + + + + + Nama Siswa + Lembaga + Detail + + + + {filteredData.map((item) => ( + + {item.nama} + {item.lembaga.nama} + + + + + ))} + +
+
+
+
+
+ ) +} + +export default Siswa; diff --git a/src/app/admin/_com/list_PageAdmin.tsx b/src/app/admin/_com/list_PageAdmin.tsx index 763ff658..98e2444b 100644 --- a/src/app/admin/_com/list_PageAdmin.tsx +++ b/src/app/admin/_com/list_PageAdmin.tsx @@ -344,7 +344,7 @@ export const navBar = [ { id: "Pendidikan_1", name: "Info Sekolah & PAUD", - path: "/admin/pendidikan/info-sekolah-paud" + path: "/admin/pendidikan/info-sekolah-paud/jenjang-pendidikan" }, { id: "Pendidikan_2", diff --git a/src/app/api/[[...slugs]]/_lib/pendidikan/index.ts b/src/app/api/[[...slugs]]/_lib/pendidikan/index.ts new file mode 100644 index 00000000..5b5ee45f --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/pendidikan/index.ts @@ -0,0 +1,11 @@ +import Elysia from "elysia"; +import InfoSekolahPAUD from "./info-sekolah-paud"; + +const Pendidikan = new Elysia({ + prefix: "/api/pendidikan", + tags: ["Pendidikan"] +}) + +.use(InfoSekolahPAUD) + +export default Pendidikan; \ No newline at end of file diff --git a/src/app/api/[[...slugs]]/_lib/pendidikan/info-sekolah-paud/index.ts b/src/app/api/[[...slugs]]/_lib/pendidikan/info-sekolah-paud/index.ts new file mode 100644 index 00000000..9a185da9 --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/pendidikan/info-sekolah-paud/index.ts @@ -0,0 +1,17 @@ +import Elysia from "elysia"; +import JenjangPendidikan from "./jenjang-pendidikan"; +import Pengajar from "./pengajar"; +import Siswa from "./siswa"; +import LembagaPendidikan from "./lembaga"; + +const InfoSekolahPAUD = new Elysia({ + prefix: "/infosekolahpaud", + tags: ["Pendidikan/Info Sekolah PAUD"], +}) + +.use(JenjangPendidikan) +.use(Siswa) +.use(Pengajar) +.use(LembagaPendidikan) + +export default InfoSekolahPAUD; diff --git a/src/app/api/[[...slugs]]/_lib/pendidikan/info-sekolah-paud/jenjang-pendidikan/create.ts b/src/app/api/[[...slugs]]/_lib/pendidikan/info-sekolah-paud/jenjang-pendidikan/create.ts new file mode 100644 index 00000000..6bcaddfb --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/pendidikan/info-sekolah-paud/jenjang-pendidikan/create.ts @@ -0,0 +1,27 @@ +import prisma from "@/lib/prisma"; +import { Context } from "elysia"; + +type FormCreate = { + nama: string; +}; + +export default async function jenjangPendidikanCreate(context: Context) { + const body = (await context.body) as FormCreate; + + try { + const result = await prisma.jenjangPendidikan.create({ + data: { + nama: body.nama, + }, + }); + + return { + success: true, + message: "Berhasil membuat jenjang pendidikan", + data: result, + }; + } catch (error) { + console.error("Error creating jenjang pendidikan:", error); + throw new Error("Gagal membuat jenjang pendidikan: " + (error as Error).message); + } +} diff --git a/src/app/api/[[...slugs]]/_lib/pendidikan/info-sekolah-paud/jenjang-pendidikan/del.ts b/src/app/api/[[...slugs]]/_lib/pendidikan/info-sekolah-paud/jenjang-pendidikan/del.ts new file mode 100644 index 00000000..e0cc1665 --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/pendidikan/info-sekolah-paud/jenjang-pendidikan/del.ts @@ -0,0 +1,31 @@ +import prisma from "@/lib/prisma"; +import { Context } from "elysia"; + +export default async function jenjangPendidikanDelete(context: Context) { + const id = context.params.id; + if (!id) { + return { + success: false, + message: "ID is required", + } + } + + const jenjangPendidikan = await prisma.jenjangPendidikan.delete({ + where: { + id: id, + }, + }) + + if(!jenjangPendidikan) { + return { + success: false, + message: "Jenjang Pendidikan tidak ditemukan", + } + } + + return { + success: true, + message: "Success delete jenjang pendidikan", + data: jenjangPendidikan, + } +} diff --git a/src/app/api/[[...slugs]]/_lib/pendidikan/info-sekolah-paud/jenjang-pendidikan/findMany.ts b/src/app/api/[[...slugs]]/_lib/pendidikan/info-sekolah-paud/jenjang-pendidikan/findMany.ts new file mode 100644 index 00000000..c8733fe0 --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/pendidikan/info-sekolah-paud/jenjang-pendidikan/findMany.ts @@ -0,0 +1,15 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import prisma from "@/lib/prisma"; + +export default async function jenjangPendidikanFindMany() { + const data = await prisma.jenjangPendidikan.findMany(); + return { + success: true, + data: data.map((item: any) => { + return { + id: item.id, + nama: item.nama, + } + }), + }; +} \ No newline at end of file diff --git a/src/app/api/[[...slugs]]/_lib/pendidikan/info-sekolah-paud/jenjang-pendidikan/findUnique.ts b/src/app/api/[[...slugs]]/_lib/pendidikan/info-sekolah-paud/jenjang-pendidikan/findUnique.ts new file mode 100644 index 00000000..59058a0c --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/pendidikan/info-sekolah-paud/jenjang-pendidikan/findUnique.ts @@ -0,0 +1,47 @@ +import { Context } from "elysia"; +import prisma from "@/lib/prisma"; + +export default async function jenjangPendidikanFindUnique(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.jenjangPendidikan.findUnique({ + where: { id }, + }); + + if (!data) { + return { + success: false, + message: "Jenjang Pendidikan tidak ditemukan", + } + } + + return { + success: true, + message: "Success find jenjang pendidikan", + data, + } + } catch (error) { + console.error("Find by ID error:", error); + return { + success: false, + message: "Gagal mengambil jenjang pendidikan: " + (error instanceof Error ? error.message : 'Unknown error'), + } + } +} \ No newline at end of file diff --git a/src/app/api/[[...slugs]]/_lib/pendidikan/info-sekolah-paud/jenjang-pendidikan/index.ts b/src/app/api/[[...slugs]]/_lib/pendidikan/info-sekolah-paud/jenjang-pendidikan/index.ts new file mode 100644 index 00000000..8a7e62c3 --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/pendidikan/info-sekolah-paud/jenjang-pendidikan/index.ts @@ -0,0 +1,29 @@ +import Elysia, { t } from "elysia"; +import jenjangPendidikanCreate from "./create"; +import jenjangPendidikanDelete from "./del"; +import jenjangPendidikanFindMany from "./findMany"; +import jenjangPendidikanFindUnique from "./findUnique"; +import jenjangPendidikanUpdate from "./updt"; + +const JenjangPendidikan = new Elysia({ + prefix: "/jenjangpendidikan", + tags: ["Pendidikan/Jenjang Pendidikan"], +}) + .get("/find-many", jenjangPendidikanFindMany) + .get("/:id", async (context) => { + const response = await jenjangPendidikanFindUnique(context); + return response; + }) + .delete("/del/:id", jenjangPendidikanDelete) + .post("/create", jenjangPendidikanCreate, { + body: t.Object({ + nama: t.String(), + }), + }) + .put("/:id", jenjangPendidikanUpdate, { + body: t.Object({ + nama: t.String(), + }), + }); + +export default JenjangPendidikan; diff --git a/src/app/api/[[...slugs]]/_lib/pendidikan/info-sekolah-paud/jenjang-pendidikan/updt.ts b/src/app/api/[[...slugs]]/_lib/pendidikan/info-sekolah-paud/jenjang-pendidikan/updt.ts new file mode 100644 index 00000000..27de2750 --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/pendidikan/info-sekolah-paud/jenjang-pendidikan/updt.ts @@ -0,0 +1,44 @@ +import prisma from "@/lib/prisma"; +import { Context } from "elysia"; + +export default async function jenjangPendidikanUpdate(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 jenjangPendidikan = await prisma.jenjangPendidikan.update({ + where: { id }, + data: { + nama: body.nama, + }, + }); + + return { + success: true, + message: "Success update jenjang pendidikan", + data: jenjangPendidikan, + }; + } catch (error) { + console.error("Update error:", error); + return { + success: false, + message: "Gagal update jenjang pendidikan", + error: error instanceof Error ? error.message : String(error), + }; + } +} diff --git a/src/app/api/[[...slugs]]/_lib/pendidikan/info-sekolah-paud/lembaga/create.ts b/src/app/api/[[...slugs]]/_lib/pendidikan/info-sekolah-paud/lembaga/create.ts new file mode 100644 index 00000000..0872beb4 --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/pendidikan/info-sekolah-paud/lembaga/create.ts @@ -0,0 +1,38 @@ +import prisma from "@/lib/prisma"; +import { Context } from "elysia"; + +type FormCreate = { + nama: string; + jenjangId: string; +}; + +export default async function lembagaPendidikanCreate(context: Context) { + const body = (await context.body) as FormCreate; + + if (!body.jenjangId) { + throw new Error("jenjangId wajib diisi"); + } + + try { + const result = await prisma.lembaga.create({ + data: { + nama: body.nama, + jenjangId: body.jenjangId, + }, + include: { + jenjangPendidikan: true, + siswa: true, + pengajar: true, + }, + }); + + return { + success: true, + message: "Berhasil membuat lembaga", + data: result, + }; + } catch (error) { + console.error("Error creating lembaga:", error); + throw new Error("Gagal membuat lembaga: " + (error as Error).message); + } +} diff --git a/src/app/api/[[...slugs]]/_lib/pendidikan/info-sekolah-paud/lembaga/del.ts b/src/app/api/[[...slugs]]/_lib/pendidikan/info-sekolah-paud/lembaga/del.ts new file mode 100644 index 00000000..e56c906b --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/pendidikan/info-sekolah-paud/lembaga/del.ts @@ -0,0 +1,21 @@ +import prisma from "@/lib/prisma"; +import { Context } from "elysia"; + +export default async function lembagaPendidikanDelete(context: Context) { + const { params } = context; + const id = params?.id as string; + + if (!id) { + throw new Error("ID lembaga pendidikan wajib diisi"); + } + + const deleted = await prisma.lembaga.delete({ + where: { id }, + }); + + return { + success: true, + message: "Berhasil menghapus lembaga pendidikan", + data: deleted, + }; +} diff --git a/src/app/api/[[...slugs]]/_lib/pendidikan/info-sekolah-paud/lembaga/findMany.ts b/src/app/api/[[...slugs]]/_lib/pendidikan/info-sekolah-paud/lembaga/findMany.ts new file mode 100644 index 00000000..6e37a51b --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/pendidikan/info-sekolah-paud/lembaga/findMany.ts @@ -0,0 +1,45 @@ +// /api/berita/findManyPaginated.ts +import prisma from "@/lib/prisma"; +import { Context } from "elysia"; + +async function lembagaPendidikanFindMany(context: Context) { + const page = Number(context.query.page) || 1; + const limit = Number(context.query.limit) || 10; + const skip = (page - 1) * limit; + + try { + const [data, total] = await Promise.all([ + prisma.lembaga.findMany({ + where: { isActive: true }, + include: { + jenjangPendidikan: true, + siswa: true, + pengajar: true, + }, + skip, + take: limit, + orderBy: { createdAt: 'desc' }, // opsional, kalau mau urut berdasarkan waktu + }), + prisma.kegiatanDesa.count({ + where: { isActive: true } + }) + ]); + + return { + success: true, + message: "Success fetch lembaga pendidikan with pagination", + data, + page, + totalPages: Math.ceil(total / limit), + total, + }; + } catch (e) { + console.error("Find many paginated error:", e); + return { + success: false, + message: "Failed fetch lembaga pendidikan with pagination", + }; + } +} + +export default lembagaPendidikanFindMany; diff --git a/src/app/api/[[...slugs]]/_lib/pendidikan/info-sekolah-paud/lembaga/findUnique.ts b/src/app/api/[[...slugs]]/_lib/pendidikan/info-sekolah-paud/lembaga/findUnique.ts new file mode 100644 index 00000000..84e93a8c --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/pendidikan/info-sekolah-paud/lembaga/findUnique.ts @@ -0,0 +1,30 @@ +import prisma from "@/lib/prisma"; +import { Context } from "elysia"; + +export default async function lembagaPendidikanFindUnique(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.lembaga.findUnique({ + where: { id }, + include: { + jenjangPendidikan: true, + siswa: true, + pengajar: true, + }, + }); + + if (!data) { + throw new Error("Lembaga pendidikan tidak ditemukan"); + } + + return { + success: true, + message: "Data lembaga pendidikan ditemukan", + data, + }; +} diff --git a/src/app/api/[[...slugs]]/_lib/pendidikan/info-sekolah-paud/lembaga/index.ts b/src/app/api/[[...slugs]]/_lib/pendidikan/info-sekolah-paud/lembaga/index.ts new file mode 100644 index 00000000..f7725749 --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/pendidikan/info-sekolah-paud/lembaga/index.ts @@ -0,0 +1,37 @@ +import Elysia, { t } from "elysia"; +import lembagaPendidikanCreate from "./create"; +import lembagaPendidikanDelete from "./del"; +import lembagaPendidikanFindMany from "./findMany"; +import lembagaPendidikanFindUnique from "./findUnique"; +import lembagaPendidikanUpdate from "./updt"; + +const LembagaPendidikan = new Elysia({ + prefix: "/lembagapendidikan", + tags: ["Pendidikan/Info Sekolah PAUD/Lembaga Pendidikan"], +}) + + // ✅ Find all + .get("/find-many", lembagaPendidikanFindMany) + + // ✅ Find by ID + .get("/:id", lembagaPendidikanFindUnique) + + // ✅ Create + .post("/create", lembagaPendidikanCreate, { + body: t.Object({ + nama: t.String(), + jenjangId: t.String(), + }), + }) + + // ✅ Update + .put("/:id", lembagaPendidikanUpdate, { + body: t.Object({ + nama: t.Optional(t.String()), + jenjangId: t.Optional(t.String()), + }), + }) + // ✅ Delete + .delete("/del/:id", lembagaPendidikanDelete); + +export default LembagaPendidikan; diff --git a/src/app/api/[[...slugs]]/_lib/pendidikan/info-sekolah-paud/lembaga/updt.ts b/src/app/api/[[...slugs]]/_lib/pendidikan/info-sekolah-paud/lembaga/updt.ts new file mode 100644 index 00000000..d8533248 --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/pendidikan/info-sekolah-paud/lembaga/updt.ts @@ -0,0 +1,49 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import prisma from "@/lib/prisma"; +import { Context } from "elysia"; + +type FormUpdate = { + nama?: string; + jenjangId?: string; + }; + + export default async function lembagaPendidikanUpdate(context: Context) { + const body = context.body as FormUpdate; + + const id = context.params.id; + + if (!id) { + return { + success: false, + message: "ID lembaga pendidikan wajib diisi", + }; + } + + try { + const updated = await prisma.lembaga.update({ + where: { id }, + data: { + nama: body.nama, + jenjangId: body.jenjangId, + }, + include: { + jenjangPendidikan: true, + siswa: true, + pengajar: true, + }, + }); + + return { + success: true, + message: "Lembaga pendidikan berhasil diperbarui", + data: updated, + }; + } catch (error: any) { + console.error("❌ Error update lembaga pendidikan:", error); + return { + success: false, + message: "Gagal memperbarui lembaga pendidikan", + error: error.message, + }; + } + } diff --git a/src/app/api/[[...slugs]]/_lib/pendidikan/info-sekolah-paud/pengajar/create.ts b/src/app/api/[[...slugs]]/_lib/pendidikan/info-sekolah-paud/pengajar/create.ts new file mode 100644 index 00000000..35f1cdb3 --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/pendidikan/info-sekolah-paud/pengajar/create.ts @@ -0,0 +1,36 @@ +import prisma from "@/lib/prisma"; +import { Context } from "elysia"; + +type FormCreate = { + nama: string; + lembagaId: string; +}; + +export default async function pengajarCreate(context: Context) { + const body = (await context.body) as FormCreate; + + if (!body.lembagaId) { + throw new Error("lembagaId wajib diisi"); + } + + try { + const result = await prisma.pengajar.create({ + data: { + nama: body.nama, + lembagaId: body.lembagaId, + }, + include: { + lembaga: true, + }, + }); + + return { + success: true, + message: "Berhasil menambahkan pengajar", + data: result, + }; + } catch (error) { + console.error("Error creating pengajar:", error); + throw new Error("Gagal membuat pengajar: " + (error as Error).message); + } +} diff --git a/src/app/api/[[...slugs]]/_lib/pendidikan/info-sekolah-paud/pengajar/del.ts b/src/app/api/[[...slugs]]/_lib/pendidikan/info-sekolah-paud/pengajar/del.ts new file mode 100644 index 00000000..09936481 --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/pendidikan/info-sekolah-paud/pengajar/del.ts @@ -0,0 +1,21 @@ +import prisma from "@/lib/prisma"; +import { Context } from "elysia"; + +export default async function pengajarDelete(context: Context) { + const { params } = context; + const id = params?.id as string; + + if (!id) { + throw new Error("ID tidak ditemukan dalam parameter"); + } + + const deleted = await prisma.pengajar.delete({ + where: { id }, + }); + + return { + success: true, + message: "Berhasil menghapus pengajar", + data: deleted, + }; +} diff --git a/src/app/api/[[...slugs]]/_lib/pendidikan/info-sekolah-paud/pengajar/findMany.ts b/src/app/api/[[...slugs]]/_lib/pendidikan/info-sekolah-paud/pengajar/findMany.ts new file mode 100644 index 00000000..e1f99710 --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/pendidikan/info-sekolah-paud/pengajar/findMany.ts @@ -0,0 +1,43 @@ +// /api/berita/findManyPaginated.ts +import prisma from "@/lib/prisma"; +import { Context } from "elysia"; + +async function pengajarFindMany(context: Context) { + const page = Number(context.query.page) || 1; + const limit = Number(context.query.limit) || 10; + const skip = (page - 1) * limit; + + try { + const [data, total] = await Promise.all([ + prisma.pengajar.findMany({ + where: { isActive: true }, + include: { + lembaga: true, + }, + skip, + take: limit, + orderBy: { createdAt: 'desc' }, // opsional, kalau mau urut berdasarkan waktu + }), + prisma.pengajar.count({ + where: { isActive: true } + }) + ]); + + return { + success: true, + message: "Success fetch pengajar with pagination", + data, + page, + totalPages: Math.ceil(total / limit), + total, + }; + } catch (e) { + console.error("Find many paginated error:", e); + return { + success: false, + message: "Failed fetch pengajar with pagination", + }; + } +} + +export default pengajarFindMany; diff --git a/src/app/api/[[...slugs]]/_lib/pendidikan/info-sekolah-paud/pengajar/findUnique.ts b/src/app/api/[[...slugs]]/_lib/pendidikan/info-sekolah-paud/pengajar/findUnique.ts new file mode 100644 index 00000000..da6df441 --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/pendidikan/info-sekolah-paud/pengajar/findUnique.ts @@ -0,0 +1,28 @@ +import prisma from "@/lib/prisma"; +import { Context } from "elysia"; + +export default async function pengajarFindUnique(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.pengajar.findUnique({ + where: { id }, + include: { + lembaga: true, + }, + }); + + if (!data) { + throw new Error("Pengajar tidak ditemukan"); + } + + return { + success: true, + message: "Data pengajar ditemukan", + data, + }; +} diff --git a/src/app/api/[[...slugs]]/_lib/pendidikan/info-sekolah-paud/pengajar/index.ts b/src/app/api/[[...slugs]]/_lib/pendidikan/info-sekolah-paud/pengajar/index.ts new file mode 100644 index 00000000..adecaa63 --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/pendidikan/info-sekolah-paud/pengajar/index.ts @@ -0,0 +1,37 @@ +import Elysia, { t } from "elysia"; +import PengajarCreate from "./create"; +import PengajarDelete from "./del"; +import PengajarFindMany from "./findMany"; +import PengajarFindUnique from "./findUnique"; +import PengajarUpdate from "./updt"; + +const Pengajar = new Elysia({ + prefix: "/pengajar", + tags: ["Pendidikan/Info Sekolah PAUD/Pengajar"], +}) + + // ✅ Find all + .get("/find-many", PengajarFindMany) + + // ✅ Find by ID + .get("/:id", PengajarFindUnique) + + // ✅ Create + .post("/create", PengajarCreate, { + body: t.Object({ + nama: t.String(), + lembagaId: t.String(), + }), + }) + + // ✅ Update + .put("/:id", PengajarUpdate, { + body: t.Object({ + nama: t.Optional(t.String()), + lembagaId: t.Optional(t.String()), + }), + }) + // ✅ Delete + .delete("/del/:id", PengajarDelete); + +export default Pengajar; diff --git a/src/app/api/[[...slugs]]/_lib/pendidikan/info-sekolah-paud/pengajar/updt.ts b/src/app/api/[[...slugs]]/_lib/pendidikan/info-sekolah-paud/pengajar/updt.ts new file mode 100644 index 00000000..61271788 --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/pendidikan/info-sekolah-paud/pengajar/updt.ts @@ -0,0 +1,47 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import prisma from "@/lib/prisma"; +import { Context } from "elysia"; + +type FormUpdatePengajar = { + nama?: string; + lembagaId?: string; +}; + +export default async function pengajarUpdate(context: Context) { + const body = context.body as FormUpdatePengajar; + + const id = context.params.id; + + if (!id) { + return { + success: false, + message: "ID pengajar wajib diisi", + }; + } + + try { + const updated = await prisma.pengajar.update({ + where: { id }, + data: { + nama: body.nama, + lembagaId: body.lembagaId, + }, + include: { + lembaga: true, + }, + }); + + return { + success: true, + message: "Pengajar berhasil diperbarui", + data: updated, + }; + } catch (error: any) { + console.error("❌ Error update pengajar:", error); + return { + success: false, + message: "Gagal memperbarui data pengajar", + error: error.message, + }; + } +} diff --git a/src/app/api/[[...slugs]]/_lib/pendidikan/info-sekolah-paud/siswa/create.ts b/src/app/api/[[...slugs]]/_lib/pendidikan/info-sekolah-paud/siswa/create.ts new file mode 100644 index 00000000..6bbcc6fb --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/pendidikan/info-sekolah-paud/siswa/create.ts @@ -0,0 +1,36 @@ +import prisma from "@/lib/prisma"; +import { Context } from "elysia"; + +type FormCreate = { + nama: string; + lembagaId: string; +}; + +export default async function siswaCreate(context: Context) { + const body = (await context.body) as FormCreate; + + if (!body.lembagaId) { + throw new Error("lembagaId wajib diisi"); + } + + try { + const result = await prisma.siswa.create({ + data: { + nama: body.nama, + lembagaId: body.lembagaId, + }, + include: { + lembaga: true, + }, + }); + + return { + success: true, + message: "Berhasil menambahkan siswa", + data: result, + }; + } catch (error) { + console.error("Error creating siswa:", error); + throw new Error("Gagal membuat siswa: " + (error as Error).message); + } +} diff --git a/src/app/api/[[...slugs]]/_lib/pendidikan/info-sekolah-paud/siswa/del.ts b/src/app/api/[[...slugs]]/_lib/pendidikan/info-sekolah-paud/siswa/del.ts new file mode 100644 index 00000000..ec3f06ca --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/pendidikan/info-sekolah-paud/siswa/del.ts @@ -0,0 +1,21 @@ +import prisma from "@/lib/prisma"; +import { Context } from "elysia"; + +export default async function siswaDelete(context: Context) { + const { params } = context; + const id = params?.id as string; + + if (!id) { + throw new Error("ID tidak ditemukan dalam parameter"); + } + + const deleted = await prisma.siswa.delete({ + where: { id }, + }); + + return { + success: true, + message: "Berhasil menghapus siswa", + data: deleted, + }; +} diff --git a/src/app/api/[[...slugs]]/_lib/pendidikan/info-sekolah-paud/siswa/findMany.ts b/src/app/api/[[...slugs]]/_lib/pendidikan/info-sekolah-paud/siswa/findMany.ts new file mode 100644 index 00000000..346cdfd9 --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/pendidikan/info-sekolah-paud/siswa/findMany.ts @@ -0,0 +1,43 @@ +// /api/berita/findManyPaginated.ts +import prisma from "@/lib/prisma"; +import { Context } from "elysia"; + +async function siswaFindMany(context: Context) { + const page = Number(context.query.page) || 1; + const limit = Number(context.query.limit) || 10; + const skip = (page - 1) * limit; + + try { + const [data, total] = await Promise.all([ + prisma.siswa.findMany({ + where: { isActive: true }, + include: { + lembaga: true, + }, + skip, + take: limit, + orderBy: { createdAt: 'desc' }, // opsional, kalau mau urut berdasarkan waktu + }), + prisma.siswa.count({ + where: { isActive: true } + }) + ]); + + return { + success: true, + message: "Success fetch siswa with pagination", + data, + page, + totalPages: Math.ceil(total / limit), + total, + }; + } catch (e) { + console.error("Find many paginated error:", e); + return { + success: false, + message: "Failed fetch siswa with pagination", + }; + } +} + +export default siswaFindMany; diff --git a/src/app/api/[[...slugs]]/_lib/pendidikan/info-sekolah-paud/siswa/findUnique.ts b/src/app/api/[[...slugs]]/_lib/pendidikan/info-sekolah-paud/siswa/findUnique.ts new file mode 100644 index 00000000..c99bf3ef --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/pendidikan/info-sekolah-paud/siswa/findUnique.ts @@ -0,0 +1,28 @@ +import prisma from "@/lib/prisma"; +import { Context } from "elysia"; + +export default async function siswaFindUnique(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.siswa.findUnique({ + where: { id }, + include: { + lembaga: true, + }, + }); + + if (!data) { + throw new Error("Siswa tidak ditemukan"); + } + + return { + success: true, + message: "Data siswa ditemukan", + data, + }; +} diff --git a/src/app/api/[[...slugs]]/_lib/pendidikan/info-sekolah-paud/siswa/index.ts b/src/app/api/[[...slugs]]/_lib/pendidikan/info-sekolah-paud/siswa/index.ts new file mode 100644 index 00000000..4e6a1d4a --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/pendidikan/info-sekolah-paud/siswa/index.ts @@ -0,0 +1,37 @@ +import Elysia, { t } from "elysia"; +import SiswaCreate from "./create"; +import SiswaDelete from "./del"; +import SiswaFindMany from "./findMany"; +import SiswaFindUnique from "./findUnique"; +import SiswaUpdate from "./updt"; + +const Siswa = new Elysia({ + prefix: "/siswa", + tags: ["Pendidikan/Info Sekolah PAUD/Siswa"], +}) + + // ✅ Find all + .get("/find-many", SiswaFindMany) + + // ✅ Find by ID + .get("/:id", SiswaFindUnique) + + // ✅ Create + .post("/create", SiswaCreate, { + body: t.Object({ + nama: t.String(), + lembagaId: t.String(), + }), + }) + + // ✅ Update + .put("/:id", SiswaUpdate, { + body: t.Object({ + nama: t.Optional(t.String()), + lembagaId: t.Optional(t.String()), + }), + }) + // ✅ Delete + .delete("/del/:id", SiswaDelete); + +export default Siswa; diff --git a/src/app/api/[[...slugs]]/_lib/pendidikan/info-sekolah-paud/siswa/updt.ts b/src/app/api/[[...slugs]]/_lib/pendidikan/info-sekolah-paud/siswa/updt.ts new file mode 100644 index 00000000..c23ffbd8 --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/pendidikan/info-sekolah-paud/siswa/updt.ts @@ -0,0 +1,48 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import prisma from "@/lib/prisma"; +import { Context } from "elysia"; + +type FormUpdateSiswaDesa = { + nama?: string; + lembagaId?: string; +}; + +export default async function siswaUpdate(context: Context) { + const body = context.body as FormUpdateSiswaDesa; + + const id = context.params.id; + + if (!id) { + return { + success: false, + message: "ID siswa wajib diisi", + }; + } + + try { + const updated = await prisma.siswa.update({ + where: { id }, + data: { + nama: body.nama, + lembagaId: body.lembagaId, + updatedAt: new Date(), + }, + include: { + lembaga: true, + }, + }); + + return { + success: true, + message: "Siswa berhasil diperbarui", + data: updated, + }; + } catch (error: any) { + console.error("❌ Error update siswa:", error); + return { + success: false, + message: "Gagal memperbarui data siswa", + error: error.message, + }; + } +} diff --git a/src/app/api/[[...slugs]]/route.ts b/src/app/api/[[...slugs]]/route.ts index 73bc58ba..d4000feb 100644 --- a/src/app/api/[[...slugs]]/route.ts +++ b/src/app/api/[[...slugs]]/route.ts @@ -22,6 +22,7 @@ import Ekonomi from "./_lib/ekonomi"; import Inovasi from "./_lib/inovasi"; import Lingkungan from "./_lib/lingkungan"; import LandingPage from "./_lib/landing_page"; +import Pendidikan from "./_lib/pendidikan"; const ROOT = process.cwd(); @@ -77,16 +78,17 @@ const ApiServer = new Elysia() }) ) .use(cors(corsConfig)) - .use(PPID) - .use(Kesehatan) - .use(Desa) - .use(Keamanan) .use(Utils) .use(FileStorage) + .use(LandingPage) + .use(PPID) + .use(Desa) + .use(Kesehatan) + .use(Keamanan) .use(Ekonomi) .use(Inovasi) .use(Lingkungan) - .use(LandingPage) + .use(Pendidikan) .onError(({ code }) => { if (code === "NOT_FOUND") { return {