From 0109886e00029917aa4604ae98d6f19f6016cb3f Mon Sep 17 00:00:00 2001 From: nico Date: Tue, 29 Jul 2025 12:29:25 +0800 Subject: [PATCH] UI & API Menu Pendidikan, Submenu Beasiswa tab beasiswa pendaftar --- prisma/schema.prisma | 62 ++++ .../_state/pendidikan/beasiswa-desa.ts | 282 ++++++++++++++++++ .../page.tsx | 2 +- .../beasiswa-desa/_lib/layoutTabs.tsx | 63 ++++ .../beasiswa-pendaftar/[id]/page.tsx | 137 +++++++++ .../beasiswa-desa/beasiswa-pendaftar/page.tsx | 107 +++++++ .../{ => keunggulan-program}/page.tsx | 5 +- .../pendidikan/beasiswa-desa/layout.tsx | 13 + src/app/admin/_com/list_PageAdmin.tsx | 2 +- .../beasiswa-pendaftar/create.ts | 51 ++++ .../beasiswa-desa/beasiswa-pendaftar/del.ts | 16 + .../beasiswa-pendaftar/findMany.ts | 11 + .../beasiswa-pendaftar/findUnique.ts | 46 +++ .../beasiswa-desa/beasiswa-pendaftar/index.ts | 63 ++++ .../beasiswa-desa/beasiswa-pendaftar/updt.ts | 79 +++++ .../_lib/pendidikan/beasiswa-desa/index.ts | 10 + .../api/[[...slugs]]/_lib/pendidikan/index.ts | 2 + .../(pages)/pendidikan/beasiswa-desa/page.tsx | 184 +++++++++++- 18 files changed, 1129 insertions(+), 6 deletions(-) create mode 100644 src/app/admin/(dashboard)/_state/pendidikan/beasiswa-desa.ts create mode 100644 src/app/admin/(dashboard)/pendidikan/beasiswa-desa/_lib/layoutTabs.tsx create mode 100644 src/app/admin/(dashboard)/pendidikan/beasiswa-desa/beasiswa-pendaftar/[id]/page.tsx create mode 100644 src/app/admin/(dashboard)/pendidikan/beasiswa-desa/beasiswa-pendaftar/page.tsx rename src/app/admin/(dashboard)/pendidikan/beasiswa-desa/{ => keunggulan-program}/page.tsx (96%) create mode 100644 src/app/admin/(dashboard)/pendidikan/beasiswa-desa/layout.tsx create mode 100644 src/app/api/[[...slugs]]/_lib/pendidikan/beasiswa-desa/beasiswa-pendaftar/create.ts create mode 100644 src/app/api/[[...slugs]]/_lib/pendidikan/beasiswa-desa/beasiswa-pendaftar/del.ts create mode 100644 src/app/api/[[...slugs]]/_lib/pendidikan/beasiswa-desa/beasiswa-pendaftar/findMany.ts create mode 100644 src/app/api/[[...slugs]]/_lib/pendidikan/beasiswa-desa/beasiswa-pendaftar/findUnique.ts create mode 100644 src/app/api/[[...slugs]]/_lib/pendidikan/beasiswa-desa/beasiswa-pendaftar/index.ts create mode 100644 src/app/api/[[...slugs]]/_lib/pendidikan/beasiswa-desa/beasiswa-pendaftar/updt.ts create mode 100644 src/app/api/[[...slugs]]/_lib/pendidikan/beasiswa-desa/index.ts diff --git a/prisma/schema.prisma b/prisma/schema.prisma index ca91104e..82e93d93 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -1839,6 +1839,68 @@ model Pengajar { isActive Boolean @default(true) } +// ========================================= BEASISWA DESA ========================================= // +model KeunggulanProgram { + id String @id @default(cuid()) + judul String + deskripsi String @db.Text + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + deletedAt DateTime @default(now()) + isActive Boolean @default(true) +} + +model BeasiswaPendaftar { + id String @id @default(cuid()) + namaLengkap String + nik String @unique + tempatLahir String + tanggalLahir DateTime + jenisKelamin JenisKelamin + kewarganegaraan String + agama Agama + alamatKTP String + alamatDomisili String? + noHp String + email String @unique + statusPernikahan StatusPernikahan + ukuranBaju UkuranBaju? + isActive Boolean @default(true) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} + +enum JenisKelamin { + LAKI_LAKI + PEREMPUAN +} + +enum Agama { + ISLAM + KRISTEN_PROTESTAN + KRISTEN_KATOLIK + HINDU + BUDDHA + KONGHUCU + LAINNYA +} + +enum StatusPernikahan { + BELUM_MENIKAH + MENIKAH + JANDA_DUDA +} + +enum UkuranBaju { + S + M + L + XL + XXL + LAINNYA +} + + // ========================================= PROGRAM PENDIDIKAN ANAK ========================================= // model TujuanProgram { id String @id @default(cuid()) diff --git a/src/app/admin/(dashboard)/_state/pendidikan/beasiswa-desa.ts b/src/app/admin/(dashboard)/_state/pendidikan/beasiswa-desa.ts new file mode 100644 index 00000000..b14d94c4 --- /dev/null +++ b/src/app/admin/(dashboard)/_state/pendidikan/beasiswa-desa.ts @@ -0,0 +1,282 @@ +import ApiFetch from "@/lib/api-fetch"; +import { Prisma } from "@prisma/client"; +import { toast } from "react-toastify"; +import { proxy } from "valtio"; +import { z } from "zod"; + +const templateBeasiswaPendaftar = z.object({ + namaLengkap: z.string().min(1, "Nama harus diisi"), + nik: z.string().min(1, "NIK harus diisi"), + tempatLahir: z.string().min(1, "Tempat lahir harus diisi"), + tanggalLahir: z.string().min(1, "Tanggal lahir harus diisi"), + jenisKelamin: z.string().min(1, "Jenis kelamin harus diisi"), + kewarganegaraan: z.string().min(1, "Kewarganegaraan harus diisi"), + agama: z.string().min(1, "Agama harus diisi"), + alamatKTP: z.string().min(1, "Alamat KTP harus diisi"), + alamatDomisili: z.string().min(1, "Alamat domisili harus diisi"), + noHp: z.string().min(1, "No HP harus diisi"), + email: z.string().min(1, "Email harus diisi"), + statusPernikahan: z.string().min(1, "Status pernikahan harus diisi"), + ukuranBaju: z.string().min(1, "Ukuran baju harus diisi"), +}); + +const defaultBeasiswaPendaftar = { + namaLengkap: "", + nik: "", + tempatLahir: "", + tanggalLahir: "", + jenisKelamin: "", + kewarganegaraan: "", + agama: "", + alamatKTP: "", + alamatDomisili: "", + noHp: "", + email: "", + statusPernikahan: "", + ukuranBaju: "", +}; + +const beasiswaPendaftar = proxy({ + create: { + form: { ...defaultBeasiswaPendaftar }, + loading: false, + async create() { + const cek = templateBeasiswaPendaftar.safeParse( + beasiswaPendaftar.create.form + ); + if (!cek.success) { + const err = `[${cek.error.issues + .map((v) => `${v.path.join(".")}`) + .join("\n")}] required`; + return toast.error(err); + } + + try { + beasiswaPendaftar.create.loading = true; + const res = await ApiFetch.api.pendidikan.beasiswa.beasiswapendaftar[ + "create" + ].post(beasiswaPendaftar.create.form); + if (res.status === 200) { + beasiswaPendaftar.findMany.load(); + return toast.success("Data Berhasil Dibuat, Silahkan Menunggu Konfirmasi dari Admin di WhatsApp"); + } + console.log(res); + return toast.error("failed create"); + } catch (error) { + console.log(error); + return toast.error("failed create"); + } finally { + beasiswaPendaftar.create.loading = false; + } + }, + }, + findMany: { + data: [] as Prisma.BeasiswaPendaftarGetPayload<{ + omit: { + isActive: true; + }; + }>[], + loading: false, + async load() { + const res = await ApiFetch.api.pendidikan.beasiswa.beasiswapendaftar[ + "findMany" + ].get(); + if (res.status === 200) { + beasiswaPendaftar.findMany.data = res.data?.data ?? []; + } + }, + }, + findUnique: { + data: null as Prisma.BeasiswaPendaftarGetPayload<{ + omit: { + isActive: true; + }; + }> | null, + loading: false, + async load(id: string) { + try { + const res = await fetch( + `/api/pendidikan/beasiswa/beasiswapendaftar/${id}` + ); + if (res.ok) { + const data = await res.json(); + beasiswaPendaftar.findUnique.data = data.data ?? null; + } else { + console.error("Failed to fetch data", res.status, res.statusText); + beasiswaPendaftar.findUnique.data = null; + } + } catch (error) { + console.error("Error fetching data:", error); + beasiswaPendaftar.findUnique.data = null; + } + }, + }, + delete: { + loading: false, + async delete(id: string) { + if (!id) return toast.warn("ID tidak valid"); + + try { + beasiswaPendaftar.delete.loading = true; + + const response = await fetch( + `/api/pendidikan/beasiswa/beasiswapendaftar/del/${id}`, + { + method: "DELETE", + headers: { + "Content-Type": "application/json", + }, + } + ); + + const result = await response.json(); + + if (response.ok && result?.success) { + toast.success(result.message || "Beasiswa berhasil dihapus"); + await beasiswaPendaftar.findMany.load(); // refresh list + } else { + toast.error(result?.message || "Gagal menghapus beasiswa"); + } + } catch (error) { + console.error("Gagal delete:", error); + toast.error("Terjadi kesalahan saat menghapus beasiswa"); + } finally { + beasiswaPendaftar.delete.loading = false; + } + }, + }, + update: { + id: "", + form: { ...defaultBeasiswaPendaftar }, + loading: false, + async load(id: string) { + if (!id) { + toast.warn("ID tidak valid"); + return null; + } + + try { + const response = await fetch( + `/api/pendidikan/beasiswa/beasiswapendaftar/${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 = { + namaLengkap: data.namaLengkap, + nik: data.nik, + tempatLahir: data.tempatLahir, + tanggalLahir: data.tanggalLahir, + jenisKelamin: data.jenisKelamin, + kewarganegaraan: data.kewarganegaraan, + agama: data.agama, + alamatKTP: data.alamatKTP, + alamatDomisili: data.alamatDomisili, + noHp: data.noHp, + email: data.email, + statusPernikahan: data.statusPernikahan, + ukuranBaju: data.ukuranBaju, + }; + return data; // Return the loaded data + } else { + throw new Error(result?.message || "Gagal memuat data"); + } + } catch (error) { + console.error("Error loading beasiswa pendaftar:", error); + toast.error( + error instanceof Error ? error.message : "Gagal memuat data" + ); + return null; + } + }, + async update() { + const cek = templateBeasiswaPendaftar.safeParse( + beasiswaPendaftar.update.form + ); + if (!cek.success) { + const err = `[${cek.error.issues + .map((v) => `${v.path.join(".")}`) + .join("\n")}] required`; + toast.error(err); + return false; + } + + try { + beasiswaPendaftar.update.loading = true; + + const response = await fetch( + `/api/pendidikan/beasiswa/beasiswapendaftar/${this.id}`, + { + method: "PUT", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + namaLengkap: this.form.namaLengkap, + nik: this.form.nik, + tanggalLahir: this.form.tanggalLahir, + jenisKelamin: this.form.jenisKelamin, + kewarganegaraan: this.form.kewarganegaraan, + agama: this.form.agama, + alamatKTP: this.form.alamatKTP, + alamatDomisili: this.form.alamatDomisili, + noHp: this.form.noHp, + email: this.form.email, + statusPernikahan: this.form.statusPernikahan, + ukuranBaju: this.form.ukuranBaju, + }), + } + ); + + if (!response.ok) { + const errorData = await response.json().catch(() => ({})); + throw new Error( + errorData.message || `HTTP error! status: ${response.status}` + ); + } + + const result = await response.json(); + + if (result.success) { + toast.success("Berhasil update beasiswa pendaftar"); + await beasiswaPendaftar.findMany.load(); // refresh list + return true; + } else { + throw new Error(result.message || "Gagal update beasiswa pendaftar"); + } + } catch (error) { + console.error("Error updating beasiswa pendaftar:", error); + toast.error( + error instanceof Error + ? error.message + : "Terjadi kesalahan saat update beasiswa pendaftar" + ); + return false; + } finally { + beasiswaPendaftar.update.loading = false; + } + }, + reset() { + beasiswaPendaftar.update.id = ""; + beasiswaPendaftar.update.form = { ...defaultBeasiswaPendaftar }; + }, + }, +}); + +const beasiswaDesaState = proxy({ + beasiswaPendaftar, +}); + +export default beasiswaDesaState; diff --git a/src/app/admin/(dashboard)/keamanan/keamanan-lingkungan-pecalang-patwal/page.tsx b/src/app/admin/(dashboard)/keamanan/keamanan-lingkungan-pecalang-patwal/page.tsx index 1c3f5d8e..0286429e 100644 --- a/src/app/admin/(dashboard)/keamanan/keamanan-lingkungan-pecalang-patwal/page.tsx +++ b/src/app/admin/(dashboard)/keamanan/keamanan-lingkungan-pecalang-patwal/page.tsx @@ -21,7 +21,7 @@ function KeamananLingkungan() { value={search} onChange={(e) => setSearch(e.currentTarget.value)} /> - + ); } diff --git a/src/app/admin/(dashboard)/pendidikan/beasiswa-desa/_lib/layoutTabs.tsx b/src/app/admin/(dashboard)/pendidikan/beasiswa-desa/_lib/layoutTabs.tsx new file mode 100644 index 00000000..6f05b13e --- /dev/null +++ b/src/app/admin/(dashboard)/pendidikan/beasiswa-desa/_lib/layoutTabs.tsx @@ -0,0 +1,63 @@ +/* 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: "Beasiswa Pendaftar", + value: "beasiswa-pendaftar", + href: "/admin/pendidikan/beasiswa-desa/beasiswa-pendaftar" + }, + { + label: "Keunggulan Program", + value: "keunggulan-program", + href: "/admin/pendidikan/beasiswa-desa/keunggulan-program" + }, + + ]; + 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 ( + + Beasiswa Desa + + + {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/beasiswa-desa/beasiswa-pendaftar/[id]/page.tsx b/src/app/admin/(dashboard)/pendidikan/beasiswa-desa/beasiswa-pendaftar/[id]/page.tsx new file mode 100644 index 00000000..61de1a37 --- /dev/null +++ b/src/app/admin/(dashboard)/pendidikan/beasiswa-desa/beasiswa-pendaftar/[id]/page.tsx @@ -0,0 +1,137 @@ +'use client' +import { ModalKonfirmasiHapus } from '@/app/admin/(dashboard)/_com/modalKonfirmasiHapus'; +import beasiswaDesaState from '@/app/admin/(dashboard)/_state/pendidikan/beasiswa-desa'; +import colors from '@/con/colors'; +import { Box, Button, Flex, Paper, Skeleton, Stack, Text } from '@mantine/core'; +import { useShallowEffect } from '@mantine/hooks'; +import { IconArrowBack, IconX } from '@tabler/icons-react'; +import { useParams, useRouter } from 'next/navigation'; +import { useState } from 'react'; +import { useProxy } from 'valtio/utils'; + + +function DetailBeasiswaPendaftar() { + const state = useProxy(beasiswaDesaState.beasiswaPendaftar) + const [modalHapus, setModalHapus] = useState(false); + const [selectedId, setSelectedId] = useState(null); + const router = useRouter() + const params = useParams() + + useShallowEffect(() => { + state.findUnique.load(params?.id as string) + }, []) + + const handleHapus = () => { + if (selectedId) { + state.delete.delete(selectedId) + setModalHapus(false) + setSelectedId(null) + router.push("/admin/pendidikan/beasiswa-desa/beasiswa-pendaftar") + } + } + + if (!state.findUnique.data) { + return ( + + + + ) + } + + return ( + + + + + + + + Detail Beasiswa Pendaftar + + + {state.findUnique.data ? ( + + + + Nama Lengkap + {state.findUnique.data?.namaLengkap} + + + NIK + {state.findUnique.data?.nik} + + + Tempat Lahir + {state.findUnique.data?.tempatLahir} + + + Tanggal Lahir + {state.findUnique.data?.tanggalLahir ? new Date(state.findUnique.data.tanggalLahir).toLocaleDateString() : '-'} + + + Jenis Kelamin + {state.findUnique.data?.jenisKelamin} + + + Kewarganegaraan + {state.findUnique.data?.kewarganegaraan} + + + Agama + {state.findUnique.data?.agama} + + + Alamat KTP + {state.findUnique.data?.alamatKTP} + + + Alamat Domisili + {state.findUnique.data?.alamatDomisili} + + + No HP + {state.findUnique.data?.noHp} + + + Email + {state.findUnique.data?.email} + + + Status Pernikahan + {state.findUnique.data?.statusPernikahan} + + + Ukuran Baju + {state.findUnique.data?.ukuranBaju} + + + + ) : null} + + + + {/* Modal Konfirmasi Hapus */} + setModalHapus(false)} + onConfirm={handleHapus} + text='Apakah anda yakin ingin menghapus beasiswa desa ini?' + /> + + ); +} + +export default DetailBeasiswaPendaftar; diff --git a/src/app/admin/(dashboard)/pendidikan/beasiswa-desa/beasiswa-pendaftar/page.tsx b/src/app/admin/(dashboard)/pendidikan/beasiswa-desa/beasiswa-pendaftar/page.tsx new file mode 100644 index 00000000..2d81dde6 --- /dev/null +++ b/src/app/admin/(dashboard)/pendidikan/beasiswa-desa/beasiswa-pendaftar/page.tsx @@ -0,0 +1,107 @@ +/* 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, Text, Title } from '@mantine/core'; +import { IconDeviceImacCog, IconSearch } from '@tabler/icons-react'; +import { useRouter } from 'next/navigation'; +import HeaderSearch from '../../../_com/header'; +import { useEffect, useState } from 'react'; +import { useProxy } from 'valtio/utils'; +import beasiswaDesaState from '../../../_state/pendidikan/beasiswa-desa'; + + +function BeasiswaPendaftar() { + const [search, setSearch] = useState(''); + return ( + + } + value={search} + onChange={(e) => setSearch(e.currentTarget.value)} + /> + + + ); +} + +function ListBeasiswaPendaftar({ search }: { search: string }) { + const listDataState = useProxy(beasiswaDesaState.beasiswaPendaftar) + const router = useRouter(); + + useEffect(() => { + listDataState.findMany.load() + }, []) + + const filteredData = (listDataState.findMany.data || []).filter(item => { + const keyword = search.toLowerCase(); + return ( + item.namaLengkap.toLowerCase().includes(keyword) || + item.alamatKTP.toLowerCase().includes(keyword) || + (item.tanggalLahir ? new Date(item.tanggalLahir).toLocaleDateString() : '').toLowerCase().includes(keyword) || + item.jenisKelamin.toLowerCase().includes(keyword) + ); + }); + + if (!listDataState.findMany.data) { + return ( + + + + ) + } + return ( + + + + List Beasiswa Pendaftar + + + + + No + Nama Lengkap + Alamat + Tanggal Lahir + Jenis Kelamin + Detail + + + + {filteredData.map((item, index) => ( + + + + {index + 1} + + + + {item.namaLengkap} + + + {item.alamatKTP} + + + {item.tanggalLahir ? new Date(item.tanggalLahir).toLocaleDateString() : '-'} + + + {item.jenisKelamin} + + + + + + ))} + +
+
+
+
+
+ ) +} + +export default BeasiswaPendaftar; diff --git a/src/app/admin/(dashboard)/pendidikan/beasiswa-desa/page.tsx b/src/app/admin/(dashboard)/pendidikan/beasiswa-desa/keunggulan-program/page.tsx similarity index 96% rename from src/app/admin/(dashboard)/pendidikan/beasiswa-desa/page.tsx rename to src/app/admin/(dashboard)/pendidikan/beasiswa-desa/keunggulan-program/page.tsx index c55b299b..5d8fc60f 100644 --- a/src/app/admin/(dashboard)/pendidikan/beasiswa-desa/page.tsx +++ b/src/app/admin/(dashboard)/pendidikan/beasiswa-desa/keunggulan-program/page.tsx @@ -3,13 +3,14 @@ import colors from '@/con/colors'; import { Box, Button, Paper, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text, Title } from '@mantine/core'; import { IconDeviceImacCog, IconSearch } from '@tabler/icons-react'; import { useRouter } from 'next/navigation'; -import HeaderSearch from '../../_com/header'; +import HeaderSearch from '../../../_com/header'; + function BeasiswaDesa() { return ( } /> diff --git a/src/app/admin/(dashboard)/pendidikan/beasiswa-desa/layout.tsx b/src/app/admin/(dashboard)/pendidikan/beasiswa-desa/layout.tsx new file mode 100644 index 00000000..4f25d331 --- /dev/null +++ b/src/app/admin/(dashboard)/pendidikan/beasiswa-desa/layout.tsx @@ -0,0 +1,13 @@ +'use client' +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/_com/list_PageAdmin.tsx b/src/app/admin/_com/list_PageAdmin.tsx index 957a697f..370feda9 100644 --- a/src/app/admin/_com/list_PageAdmin.tsx +++ b/src/app/admin/_com/list_PageAdmin.tsx @@ -349,7 +349,7 @@ export const navBar = [ { id: "Pendidikan_2", name: "Beasiswa Desa", - path: "/admin/pendidikan/beasiswa-desa" + path: "/admin/pendidikan/beasiswa-desa/beasiswa-pendaftar" }, { id: "Pendidikan_3", diff --git a/src/app/api/[[...slugs]]/_lib/pendidikan/beasiswa-desa/beasiswa-pendaftar/create.ts b/src/app/api/[[...slugs]]/_lib/pendidikan/beasiswa-desa/beasiswa-pendaftar/create.ts new file mode 100644 index 00000000..bb30acc5 --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/pendidikan/beasiswa-desa/beasiswa-pendaftar/create.ts @@ -0,0 +1,51 @@ +import prisma from "@/lib/prisma"; +import { Context } from "elysia"; + +type FormCreate = { + namaLengkap: string; + nik: string; + tempatLahir: string; + tanggalLahir: string; // ISO date string + jenisKelamin: "LAKI_LAKI" | "PEREMPUAN"; + kewarganegaraan: string; + agama: "ISLAM" | "KRISTEN_PROTESTAN" | "KRISTEN_KATOLIK" | "HINDU" | "BUDDHA" | "KONGHUCU" | "LAINNYA"; + alamatKTP: string; + alamatDomisili?: string; + noHp: string; + email: string; + statusPernikahan: "BELUM_MENIKAH" | "MENIKAH" | "JANDA_DUDA"; + ukuranBaju?: "S" | "M" | "L" | "XL" | "XXL" | "LAINNYA"; +}; + +export default async function beasiswaPendaftarCreate(context: Context) { + const body = (await context.body) as FormCreate; + + try { + const result = await prisma.beasiswaPendaftar.create({ + data: { + namaLengkap: body.namaLengkap, + nik: body.nik, + tempatLahir: body.tempatLahir, + tanggalLahir: new Date(body.tanggalLahir), + jenisKelamin: body.jenisKelamin, + kewarganegaraan: body.kewarganegaraan, + agama: body.agama, + alamatKTP: body.alamatKTP, + alamatDomisili: body.alamatDomisili, + noHp: body.noHp, + email: body.email, + statusPernikahan: body.statusPernikahan, + ukuranBaju: body.ukuranBaju, + }, + }); + + return { + success: true, + message: "Berhasil membuat data pendaftar beasiswa", + data: result, + }; + } catch (error) { + console.error("Gagal membuat data beasiswa pendaftar:", error); + throw new Error("Gagal membuat data beasiswa pendaftar: " + (error as Error).message); + } +} diff --git a/src/app/api/[[...slugs]]/_lib/pendidikan/beasiswa-desa/beasiswa-pendaftar/del.ts b/src/app/api/[[...slugs]]/_lib/pendidikan/beasiswa-desa/beasiswa-pendaftar/del.ts new file mode 100644 index 00000000..f3d8d05a --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/pendidikan/beasiswa-desa/beasiswa-pendaftar/del.ts @@ -0,0 +1,16 @@ +import prisma from "@/lib/prisma"; +import { Context } from "elysia"; + +export default async function beasiswaPendaftarDelete(context: Context) { + const id = context.params.id as string; + + await prisma.beasiswaPendaftar.delete({ + where: { id }, + }); + + return { + status: 200, + success: true, + message: "Success delete beasiswa pendaftar", + }; +} diff --git a/src/app/api/[[...slugs]]/_lib/pendidikan/beasiswa-desa/beasiswa-pendaftar/findMany.ts b/src/app/api/[[...slugs]]/_lib/pendidikan/beasiswa-desa/beasiswa-pendaftar/findMany.ts new file mode 100644 index 00000000..c7f7fd5a --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/pendidikan/beasiswa-desa/beasiswa-pendaftar/findMany.ts @@ -0,0 +1,11 @@ +import prisma from "@/lib/prisma"; + +export default async function beasiswaPendaftarFindMany() { + const data = await prisma.beasiswaPendaftar.findMany(); + + return { + success: true, + message: "Success get all beasiswa pendaftar", + data, + }; +} diff --git a/src/app/api/[[...slugs]]/_lib/pendidikan/beasiswa-desa/beasiswa-pendaftar/findUnique.ts b/src/app/api/[[...slugs]]/_lib/pendidikan/beasiswa-desa/beasiswa-pendaftar/findUnique.ts new file mode 100644 index 00000000..f6063097 --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/pendidikan/beasiswa-desa/beasiswa-pendaftar/findUnique.ts @@ -0,0 +1,46 @@ +import prisma from "@/lib/prisma"; + +export default async function beasiswaPendaftarFindUnique(request: Request) { + const url = new URL(request.url); + const pathSegments = url.pathname.split('/'); + const id = pathSegments[pathSegments.length - 1]; + + if (!id) { + return { + success: false, + message: "ID is required", + } + } + + try { + if (typeof id !== 'string') { + return { + success: false, + message: "ID is required", + } + } + + const data = await prisma.beasiswaPendaftar.findUnique({ + where: { id }, + }); + + if (!data) { + return { + success: false, + message: "Data not found", + } + } + + return { + success: true, + message: "Success get beasiswa pendaftar", + data, + } + } catch (error) { + console.error("Find by ID error:", error); + return { + success: false, + message: "Gagal mengambil data: " + (error instanceof Error ? error.message : 'Unknown error'), + } + } +} \ No newline at end of file diff --git a/src/app/api/[[...slugs]]/_lib/pendidikan/beasiswa-desa/beasiswa-pendaftar/index.ts b/src/app/api/[[...slugs]]/_lib/pendidikan/beasiswa-desa/beasiswa-pendaftar/index.ts new file mode 100644 index 00000000..fa3d5e29 --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/pendidikan/beasiswa-desa/beasiswa-pendaftar/index.ts @@ -0,0 +1,63 @@ +import Elysia, { t } from "elysia"; +import beasiswaPendaftarCreate from "./create"; +import beasiswaPendaftarFindMany from "./findMany"; +import beasiswaPendaftarFindUnique from "./findUnique"; +import beasiswaPendaftarUpdate from "./updt"; +import beasiswaPendaftarDelete from "./del"; + +const BeasiswaPendaftar = new Elysia({ + prefix: "/beasiswapendaftar", + tags: ["Pendidikan / Beasiswa Desa / Beasiswa Pendaftar"], +}) + + .post("/create", beasiswaPendaftarCreate, { + body: t.Object({ + namaLengkap: t.String(), + nik: t.String(), + tempatLahir: t.String(), + tanggalLahir: t.String(), + jenisKelamin: t.String(), + kewarganegaraan: t.String(), + agama: t.String(), + alamatKTP: t.String(), + alamatDomisili: t.String(), + noHp: t.String(), + email: t.String(), + statusPernikahan: t.String(), + ukuranBaju: t.String(), + }), + }) + .get("/findMany", beasiswaPendaftarFindMany) + .get("/:id", async (context) => { + const response = await beasiswaPendaftarFindUnique( + new Request(context.request) + ); + return response; + }) + .put( + "/:id", + async (context) => { + const response = await beasiswaPendaftarUpdate(context); + return response; + }, + { + body: t.Object({ + namaLengkap: t.String(), + nik: t.String(), + tempatLahir: t.String(), + tanggalLahir: t.String(), + jenisKelamin: t.String(), + kewarganegaraan: t.String(), + agama: t.String(), + alamatKTP: t.String(), + alamatDomisili: t.String(), + noHp: t.String(), + email: t.String(), + statusPernikahan: t.String(), + ukuranBaju: t.String(), + }), + } + ) + .delete("/del/:id", beasiswaPendaftarDelete); + +export default BeasiswaPendaftar; diff --git a/src/app/api/[[...slugs]]/_lib/pendidikan/beasiswa-desa/beasiswa-pendaftar/updt.ts b/src/app/api/[[...slugs]]/_lib/pendidikan/beasiswa-desa/beasiswa-pendaftar/updt.ts new file mode 100644 index 00000000..0175a79a --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/pendidikan/beasiswa-desa/beasiswa-pendaftar/updt.ts @@ -0,0 +1,79 @@ +import prisma from "@/lib/prisma"; +import { Context } from "elysia"; + +type FormUpdate = { + namaLengkap: string; + nik: string; + tempatLahir: string; + tanggalLahir: string; // ISO date string + jenisKelamin: "LAKI_LAKI" | "PEREMPUAN"; + kewarganegaraan: string; + agama: + | "ISLAM" + | "KRISTEN_PROTESTAN" + | "KRISTEN_KATOLIK" + | "HINDU" + | "BUDDHA" + | "KONGHUCU" + | "LAINNYA"; + alamatKTP: string; + alamatDomisili?: string; + noHp: string; + email: string; + statusPernikahan: "BELUM_MENIKAH" | "MENIKAH" | "JANDA_DUDA"; + ukuranBaju?: "S" | "M" | "L" | "XL" | "XXL" | "LAINNYA"; +}; + +export default async function beasiswaPendaftarUpdate(context: Context) { + const id = context.params.id as string; + const body = context.body as FormUpdate; + + try { + if (typeof id !== "string") { + return { + success: false, + message: "ID is required", + }; + } + + const data = await prisma.beasiswaPendaftar.update({ + where: { id }, + data: { + namaLengkap: body.namaLengkap, + nik: body.nik, + tempatLahir: body.tempatLahir, + tanggalLahir: new Date(body.tanggalLahir), + jenisKelamin: body.jenisKelamin, + kewarganegaraan: body.kewarganegaraan, + agama: body.agama, + alamatKTP: body.alamatKTP, + alamatDomisili: body.alamatDomisili, + noHp: body.noHp, + email: body.email, + statusPernikahan: body.statusPernikahan, + ukuranBaju: body.ukuranBaju, + }, + }); + + if (!data) { + return { + success: false, + message: "Data not found", + }; + } + + return { + success: true, + message: "Success update beasiswa pendaftar", + data, + }; + } catch (error) { + console.error("Update by ID error:", error); + return { + success: false, + message: + "Gagal mengupdate data: " + + (error instanceof Error ? error.message : "Unknown error"), + }; + } +} diff --git a/src/app/api/[[...slugs]]/_lib/pendidikan/beasiswa-desa/index.ts b/src/app/api/[[...slugs]]/_lib/pendidikan/beasiswa-desa/index.ts new file mode 100644 index 00000000..17ab9feb --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/pendidikan/beasiswa-desa/index.ts @@ -0,0 +1,10 @@ +import Elysia from "elysia"; +import BeasiswaPendaftar from "./beasiswa-pendaftar"; + +const Beasiswa = new Elysia({ + prefix: "/beasiswa", + tags: ["Pendidikan/Beasiswa Desa"] +}) +.use(BeasiswaPendaftar) + +export default Beasiswa \ No newline at end of file diff --git a/src/app/api/[[...slugs]]/_lib/pendidikan/index.ts b/src/app/api/[[...slugs]]/_lib/pendidikan/index.ts index eb39241d..ed4b40a6 100644 --- a/src/app/api/[[...slugs]]/_lib/pendidikan/index.ts +++ b/src/app/api/[[...slugs]]/_lib/pendidikan/index.ts @@ -4,6 +4,7 @@ import ProgramPendidikanAnak from "./program-pendidikan-anak"; import BimbinganBelajarDesa from "./bimbingan-belajar-desa"; import PendidikanNonFormal from "./pendidikan-non-formal"; import DataPendidikan from "./data-pendidikan"; +import Beasiswa from "./beasiswa-desa"; const Pendidikan = new Elysia({ prefix: "/api/pendidikan", @@ -15,5 +16,6 @@ const Pendidikan = new Elysia({ .use(BimbinganBelajarDesa) .use(PendidikanNonFormal) .use(DataPendidikan) +.use(Beasiswa) export default Pendidikan; \ No newline at end of file diff --git a/src/app/darmasaba/(pages)/pendidikan/beasiswa-desa/page.tsx b/src/app/darmasaba/(pages)/pendidikan/beasiswa-desa/page.tsx index 8b179d64..15f66c14 100644 --- a/src/app/darmasaba/(pages)/pendidikan/beasiswa-desa/page.tsx +++ b/src/app/darmasaba/(pages)/pendidikan/beasiswa-desa/page.tsx @@ -1,8 +1,11 @@ 'use client' import colors from '@/con/colors'; -import { Box, Button, Center, Group, Image, Paper, SimpleGrid, Stack, Stepper, StepperStep, Text, Title } from '@mantine/core'; +import { Box, Button, Center, Group, Image, Modal, Paper, Select, SimpleGrid, Stack, Stepper, StepperStep, Text, TextInput, Title } from '@mantine/core'; import { useState } from 'react'; import BackButton from '../../desa/layanan/_com/BackButto'; +import { useDisclosure } from '@mantine/hooks'; +import { useProxy } from 'valtio/utils'; +import beasiswaDesaState from '@/app/admin/(dashboard)/_state/pendidikan/beasiswa-desa'; const dataBeasiswa = [ { @@ -39,6 +42,32 @@ const dataProgram = [ } ] function Page() { + const beasiswaDesa = useProxy(beasiswaDesaState.beasiswaPendaftar) + const [opened, { open, close }] = useDisclosure(false); + const resetForm = () => { + beasiswaDesa.create.form = { + namaLengkap: "", + nik: "", + tempatLahir: "", + tanggalLahir: "", + jenisKelamin: "", + kewarganegaraan: "", + agama: "", + alamatKTP: "", + alamatDomisili: "", + noHp: "", + email: "", + statusPernikahan: "", + ukuranBaju: "", + }; + } + + const handleSubmit = async () => { + await beasiswaDesa.create.create(); + resetForm(); + close(); + } + const [active, setActive] = useState(1); const nextStep = () => setActive((current) => (current < 5 ? current + 1 : current)); const prevStep = () => setActive((current) => (current > 0 ? current - 1 : current)); @@ -69,7 +98,7 @@ function Page() { md: 2 }} > - + @@ -144,6 +173,157 @@ function Page() { + + + + + Ajukan Beasiswa + Nama} + placeholder="masukkan nama" + onChange={(val) => { + beasiswaDesa.create.form.namaLengkap = val.target.value + }} + /> + NIK} + placeholder="masukkan nik" + onChange={(val) => { + beasiswaDesa.create.form.nik = val.target.value + }} + /> + Tempat Lahir} + placeholder="masukkan tempat lahir" + onChange={(val) => { + beasiswaDesa.create.form.tempatLahir = val.target.value + }} + /> + Tanggal Lahir} + placeholder="masukkan tanggal lahir" + onChange={(val) => { + beasiswaDesa.create.form.tanggalLahir = val.target.value + }} + /> + Agama} + placeholder="Pilih agama" + data={[ + { value: "ISLAM", label: "Islam" }, + { value: "KRISTEN_PROTESTAN", label: "Kristen Protestan" }, + { value: "KRISTEN_KATOLIK", label: "Kristen Katolik" }, + { value: "HINDU", label: "Hindu" }, + { value: "BUDDHA", label: "Buddha" }, + { value: "KONGHUCU", label: "Konghucu" }, + { value: "LAINNYA", label: "Lainnya" }, + ]} + onChange={(val) => { + if (val) beasiswaDesa.create.form.agama = val as + "ISLAM" + | "KRISTEN_PROTESTAN" + | "KRISTEN_KATOLIK" + | "HINDU" + | "BUDDHA" + | "KONGHUCU" + | "LAINNYA"; + }} + /> + Alamat KTP} + placeholder="masukkan alamat ktp" + onChange={(val) => { + beasiswaDesa.create.form.alamatKTP = val.target.value + }} + /> + Alamat Domisili} + placeholder="masukkan alamat domisili" + onChange={(val) => { + beasiswaDesa.create.form.alamatDomisili = val.target.value + }} + /> + No Hp} + placeholder="masukkan no hp" + onChange={(val) => { + beasiswaDesa.create.form.noHp = val.target.value + }} + /> + Email} + placeholder="masukkan email" + onChange={(val) => { + beasiswaDesa.create.form.email = val.target.value + }} + /> + Ukuran Baju} + placeholder="Pilih ukuran baju" + data={[ + { value: "S", label: "S" }, + { value: "M", label: "M" }, + { value: "L", label: "L" }, + { value: "XL", label: "XL" }, + { value: "XXL", label: "XXL" }, + { value: "LAINNYA", label: "Lainnya" }, + ]} + onChange={(val) => { + if (val) beasiswaDesa.create.form.ukuranBaju = val as + "S" + | "M" + | "L" + | "XL" + | "XXL" + | "LAINNYA"; + }} + /> + + + + ); }