From e2e1672c80f78409e2114f2f0efa1b8223625df1 Mon Sep 17 00:00:00 2001 From: nico Date: Mon, 28 Jul 2025 17:51:42 +0800 Subject: [PATCH] UI & API Pendidikan Non Formal --- .../jenis-program-yang-diselenggarakan.json | 7 + .../tempat-kegiatan.json | 7 + .../tujuan-program2.json | 7 + prisma/schema.prisma | 31 ++ prisma/seed.ts | 52 ++++ .../pendidikan/pendidikan-non-formal.ts | 267 ++++++++++++++++++ .../pendidikan-non-formal/_lib/layoutTabs.tsx | 68 +++++ .../_lib/pendidikanNonFormalTextEditor.tsx | 99 +++++++ .../edit/page.tsx | 86 ++++++ .../page.tsx | 54 ++++ .../pendidikan-non-formal/layout.tsx | 13 + .../pendidikan/pendidikan-non-formal/page.tsx | 74 ----- .../tempat-kegiatan/edit/page.tsx | 86 ++++++ .../tempat-kegiatan/page.tsx | 54 ++++ .../tujuan-program/edit/page.tsx | 86 ++++++ .../tujuan-program/page.tsx | 54 ++++ src/app/admin/_com/list_PageAdmin.tsx | 2 +- .../api/[[...slugs]]/_lib/pendidikan/index.ts | 2 + .../pendidikan/pendidikan-non-formal/index.ts | 14 + .../findUnique.ts | 37 +++ .../index.ts | 19 ++ .../updt.ts | 29 ++ .../tempat-kegiatan/findUnique.ts | 37 +++ .../tempat-kegiatan/index.ts | 18 ++ .../tempat-kegiatan/updt.ts | 29 ++ .../tujuan-program/findUnique.ts | 37 +++ .../tujuan-program/index.ts | 18 ++ .../tujuan-program/updt.ts | 29 ++ 28 files changed, 1241 insertions(+), 75 deletions(-) create mode 100644 prisma/data/pendidikan/pendidikan-non-formal/jenis-program-yang-diselenggarakan.json create mode 100644 prisma/data/pendidikan/pendidikan-non-formal/tempat-kegiatan.json create mode 100644 prisma/data/pendidikan/pendidikan-non-formal/tujuan-program2.json create mode 100644 src/app/admin/(dashboard)/_state/pendidikan/pendidikan-non-formal.ts create mode 100644 src/app/admin/(dashboard)/pendidikan/pendidikan-non-formal/_lib/layoutTabs.tsx create mode 100644 src/app/admin/(dashboard)/pendidikan/pendidikan-non-formal/_lib/pendidikanNonFormalTextEditor.tsx create mode 100644 src/app/admin/(dashboard)/pendidikan/pendidikan-non-formal/jenis-program-yang-diselenggarakan/edit/page.tsx create mode 100644 src/app/admin/(dashboard)/pendidikan/pendidikan-non-formal/jenis-program-yang-diselenggarakan/page.tsx create mode 100644 src/app/admin/(dashboard)/pendidikan/pendidikan-non-formal/layout.tsx delete mode 100644 src/app/admin/(dashboard)/pendidikan/pendidikan-non-formal/page.tsx create mode 100644 src/app/admin/(dashboard)/pendidikan/pendidikan-non-formal/tempat-kegiatan/edit/page.tsx create mode 100644 src/app/admin/(dashboard)/pendidikan/pendidikan-non-formal/tempat-kegiatan/page.tsx create mode 100644 src/app/admin/(dashboard)/pendidikan/pendidikan-non-formal/tujuan-program/edit/page.tsx create mode 100644 src/app/admin/(dashboard)/pendidikan/pendidikan-non-formal/tujuan-program/page.tsx create mode 100644 src/app/api/[[...slugs]]/_lib/pendidikan/pendidikan-non-formal/index.ts create mode 100644 src/app/api/[[...slugs]]/_lib/pendidikan/pendidikan-non-formal/jenis-program-yang-diselenggarakan/findUnique.ts create mode 100644 src/app/api/[[...slugs]]/_lib/pendidikan/pendidikan-non-formal/jenis-program-yang-diselenggarakan/index.ts create mode 100644 src/app/api/[[...slugs]]/_lib/pendidikan/pendidikan-non-formal/jenis-program-yang-diselenggarakan/updt.ts create mode 100644 src/app/api/[[...slugs]]/_lib/pendidikan/pendidikan-non-formal/tempat-kegiatan/findUnique.ts create mode 100644 src/app/api/[[...slugs]]/_lib/pendidikan/pendidikan-non-formal/tempat-kegiatan/index.ts create mode 100644 src/app/api/[[...slugs]]/_lib/pendidikan/pendidikan-non-formal/tempat-kegiatan/updt.ts create mode 100644 src/app/api/[[...slugs]]/_lib/pendidikan/pendidikan-non-formal/tujuan-program/findUnique.ts create mode 100644 src/app/api/[[...slugs]]/_lib/pendidikan/pendidikan-non-formal/tujuan-program/index.ts create mode 100644 src/app/api/[[...slugs]]/_lib/pendidikan/pendidikan-non-formal/tujuan-program/updt.ts diff --git a/prisma/data/pendidikan/pendidikan-non-formal/jenis-program-yang-diselenggarakan.json b/prisma/data/pendidikan/pendidikan-non-formal/jenis-program-yang-diselenggarakan.json new file mode 100644 index 00000000..84e33716 --- /dev/null +++ b/prisma/data/pendidikan/pendidikan-non-formal/jenis-program-yang-diselenggarakan.json @@ -0,0 +1,7 @@ +[ + { + "id": "edit", + "judul": "Tempat Kegiatan", + "deskripsi": "

Program Pendidikan Non Formal yang diselenggarakan di Desa Darmasaba meliputi:

1) Keaksaraan Fungsional

2) Pendidikan Kesetaraan (Paket A, B, C)

3) Pelatihan Keterampilan

4) Kursus & Pelatihan Soft Skill

5) Pendidikan Keluarga & Parenting

" + } +] \ No newline at end of file diff --git a/prisma/data/pendidikan/pendidikan-non-formal/tempat-kegiatan.json b/prisma/data/pendidikan/pendidikan-non-formal/tempat-kegiatan.json new file mode 100644 index 00000000..138a014b --- /dev/null +++ b/prisma/data/pendidikan/pendidikan-non-formal/tempat-kegiatan.json @@ -0,0 +1,7 @@ +[ + { + "id": "edit", + "judul": "Tempat Kegiatan", + "deskripsi": "" + } +] \ No newline at end of file diff --git a/prisma/data/pendidikan/pendidikan-non-formal/tujuan-program2.json b/prisma/data/pendidikan/pendidikan-non-formal/tujuan-program2.json new file mode 100644 index 00000000..c82c5c20 --- /dev/null +++ b/prisma/data/pendidikan/pendidikan-non-formal/tujuan-program2.json @@ -0,0 +1,7 @@ +[ + { + "id": "edit", + "judul": "Tujuan Program", + "deskripsi": "" + } +] \ No newline at end of file diff --git a/prisma/schema.prisma b/prisma/schema.prisma index a9aad026..c577c7cd 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -1890,3 +1890,34 @@ model FasilitasBimbinganBelajarDesa { deletedAt DateTime @default(now()) isActive Boolean @default(true) } + +// ========================================= PENDIDIKAN NON FORMAL ========================================= // +model TujuanPendidikanNonFormal { + 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 TempatKegiatan { + 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 JenisProgramYangDiselenggarakan { + 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) +} diff --git a/prisma/seed.ts b/prisma/seed.ts index 91bbdac3..74e37009 100644 --- a/prisma/seed.ts +++ b/prisma/seed.ts @@ -30,10 +30,14 @@ import bentukKonservasiBerdasarkanAdat from './data/lingkungan/konservasi-adat-b import filosofiTriHita from './data/lingkungan/konservasi-adat-bali/filosofi-tri-hita.json'; import profilePejabatDesa from './data/landing-page/profile.json'; import tujuanProgram from './data/pendidikan/program-pendidikan-anak/tujuan-program.json'; +import tujuanProgram2 from './data/pendidikan/pendidikan-non-formal/tujuan-program2.json'; import programUnggulan from './data/pendidikan/program-pendidikan-anak/program-unggulan.json'; import tujuanBimbinganBelajarDesa from './data/pendidikan/bimbingan-belajar-desa/tujuan-bimbingan-belajar-desa.json'; import lokasiJadwalBimbinganBelajarDesa from './data/pendidikan/bimbingan-belajar-desa/lokasi-dan-jadwal.json'; import fasilitasBimbinganBelajarDesa from './data/pendidikan/bimbingan-belajar-desa/fasilitas-yang-disediakan.json'; +import tempatKegiatan from './data/pendidikan/pendidikan-non-formal/tempat-kegiatan.json'; +import jenisProgramYangDiselenggarakan from './data/pendidikan/pendidikan-non-formal/jenis-program-yang-diselenggarakan.json'; + (async () => { for (const l of layanan) { @@ -679,6 +683,54 @@ import fasilitasBimbinganBelajarDesa from './data/pendidikan/bimbingan-belajar-d } console.log("✅ fasilitas bimbingan belajar desa seeded (editable later via UI)"); + for (const t of tujuanProgram2) { + await prisma.tujuanPendidikanNonFormal.upsert({ + where: { id: t.id }, + update: { + judul: t.judul, + deskripsi: t.deskripsi, + }, + create: { + id: t.id, + judul: t.judul, + deskripsi: t.deskripsi, + }, + }); + } + console.log("✅ fasilitas bimbingan belajar desa seeded (editable later via UI)"); + + for (const t of tempatKegiatan) { + await prisma.tempatKegiatan.upsert({ + where: { id: t.id }, + update: { + judul: t.judul, + deskripsi: t.deskripsi, + }, + create: { + id: t.id, + judul: t.judul, + deskripsi: t.deskripsi, + }, + }); + } + console.log("✅ fasilitas bimbingan belajar desa seeded (editable later via UI)"); + + for (const t of jenisProgramYangDiselenggarakan) { + await prisma.jenisProgramYangDiselenggarakan.upsert({ + where: { id: t.id }, + update: { + judul: t.judul, + deskripsi: t.deskripsi, + }, + create: { + id: t.id, + judul: t.judul, + deskripsi: t.deskripsi, + }, + }); + } + console.log("✅ fasilitas bimbingan belajar desa seeded (editable later via UI)"); + })() .then(() => prisma.$disconnect()) .catch((e) => { diff --git a/src/app/admin/(dashboard)/_state/pendidikan/pendidikan-non-formal.ts b/src/app/admin/(dashboard)/_state/pendidikan/pendidikan-non-formal.ts new file mode 100644 index 00000000..d9dbce3d --- /dev/null +++ b/src/app/admin/(dashboard)/_state/pendidikan/pendidikan-non-formal.ts @@ -0,0 +1,267 @@ +import ApiFetch from "@/lib/api-fetch"; +import { Prisma } from "@prisma/client"; +import { toast } from "react-toastify"; +import { proxy } from "valtio"; +import { z } from "zod"; + +// ========================================= TUJUAN PENDIDIKAN NON FORMAL ========================================= // + +const templateTujuanPendidikanNonFormalForm = z.object({ + judul: z.string().min(3, "Judul minimal 3 karakter"), + deskripsi: z.string().min(3, "Deskripsi minimal 3 karakter"), +}); + +type TujuanPendidikanNonFormalForm = + Prisma.TujuanPendidikanNonFormalGetPayload<{ + select: { + id: true; + judul: true; + deskripsi: true; + }; + }>; + +const stateTujuanPendidikanNonFormal = proxy({ + findById: { + data: null as TujuanPendidikanNonFormalForm | null, + loading: false, + initialize() { + stateTujuanPendidikanNonFormal.findById.data = { + id: "", + judul: "", + deskripsi: "", + } as TujuanPendidikanNonFormalForm; + }, + async load(id: string) { + try { + stateTujuanPendidikanNonFormal.findById.loading = true; + const res = + await ApiFetch.api.pendidikan.pendidikannonformal.tujuanpendidikannonformal[ + "find-by-id" + ].get({ + query: { id }, + }); + if (res.status === 200) { + stateTujuanPendidikanNonFormal.findById.data = res.data?.data ?? null; + } else { + toast.error("Gagal mengambil data tujuan pendidikan non formal"); + } + } catch (error) { + console.error((error as Error).message); + toast.error( + "Terjadi kesalahan saat mengambil data tujuan pendidikan non formal" + ); + } finally { + stateTujuanPendidikanNonFormal.findById.loading = false; + } + }, + }, + + update: { + loading: false, + async save(data: TujuanPendidikanNonFormalForm) { + const cek = templateTujuanPendidikanNonFormalForm.safeParse(data); + if (!cek.success) { + const errors = cek.error.issues + .map((issue) => `${issue.path.join(".")}: ${issue.message}`) + .join(", "); + toast.error(`Form tidak valid: ${errors}`); + return; + } + + try { + stateTujuanPendidikanNonFormal.update.loading = true; + const res = + await ApiFetch.api.pendidikan.pendidikannonformal.tujuanpendidikannonformal[ + "update" + ].post(data); + if (res.status === 200) { + toast.success("Data tujuan pendidikan non formal berhasil diubah"); + await stateTujuanPendidikanNonFormal.findById.load(data.id); + } else { + toast.error("Gagal mengubah data tujuan pendidikan non formal"); + } + } catch (error) { + console.error((error as Error).message); + toast.error( + "Terjadi kesalahan saat mengubah data tujuan pendidikan non formal" + ); + } finally { + stateTujuanPendidikanNonFormal.update.loading = false; + } + }, + }, +}); + +// ========================================= TEMPAT KEGIATAN ========================================= // + +const templateTempatKegiatanForm = z.object({ + judul: z.string().min(3, "Judul minimal 3 karakter"), + deskripsi: z.string().min(3, "Deskripsi minimal 3 karakter"), +}); + +type TempatKegiatanForm = Prisma.TempatKegiatanGetPayload<{ + select: { + id: true; + judul: true; + deskripsi: true; + }; +}>; + +const stateTempatKegiatan = proxy({ + findById: { + data: null as TempatKegiatanForm | null, + loading: false, + initialize() { + stateTempatKegiatan.findById.data = { + id: "", + judul: "", + deskripsi: "", + } as TempatKegiatanForm; + }, + async load(id: string) { + try { + stateTempatKegiatan.findById.loading = true; + const res = + await ApiFetch.api.pendidikan.pendidikannonformal.tempatkegiatan[ + "find-by-id" + ].get({ + query: { id }, + }); + if (res.status === 200) { + stateTempatKegiatan.findById.data = res.data?.data ?? null; + } else { + toast.error("Gagal mengambil data tempat kegiatan"); + } + } catch (error) { + console.error((error as Error).message); + toast.error("Terjadi kesalahan saat mengambil data tempat kegiatan"); + } finally { + stateTempatKegiatan.findById.loading = false; + } + }, + }, + + update: { + loading: false, + async save(data: TempatKegiatanForm) { + const cek = templateTempatKegiatanForm.safeParse(data); + if (!cek.success) { + const errors = cek.error.issues + .map((issue) => `${issue.path.join(".")}: ${issue.message}`) + .join(", "); + toast.error(`Form tidak valid: ${errors}`); + return; + } + + try { + stateTempatKegiatan.update.loading = true; + const res = + await ApiFetch.api.pendidikan.pendidikannonformal.tempatkegiatan[ + "update" + ].post(data); + if (res.status === 200) { + toast.success("Data tempat kegiatan berhasil diubah"); + await stateTempatKegiatan.findById.load(data.id); + } else { + toast.error("Gagal mengubah data tempat kegiatan"); + } + } catch (error) { + console.error((error as Error).message); + toast.error("Terjadi kesalahan saat mengubah data tempat kegiatan"); + } finally { + stateTempatKegiatan.update.loading = false; + } + }, + }, +}); + +// ========================================= JENIS PROGRAM YANG DISELENGGARAKAN ========================================= // + +const templateJenisProgramForm = z.object({ + judul: z.string().min(3, "Judul minimal 3 karakter"), + deskripsi: z.string().min(3, "Deskripsi minimal 3 karakter"), +}); + +type JenisProgramForm = Prisma.JenisProgramYangDiselenggarakanGetPayload<{ + select: { + id: true; + judul: true; + deskripsi: true; + }; +}>; + +const stateJenisProgram = proxy({ + findById: { + data: null as JenisProgramForm | null, + loading: false, + initialize() { + stateJenisProgram.findById.data = { + id: "", + judul: "", + deskripsi: "", + } as JenisProgramForm; + }, + async load(id: string) { + try { + stateJenisProgram.findById.loading = true; + const res = + await ApiFetch.api.pendidikan.pendidikannonformal.jenisprogramyangdiselenggarakan[ + "find-by-id" + ].get({ + query: { id }, + }); + if (res.status === 200) { + stateJenisProgram.findById.data = res.data?.data ?? null; + } else { + toast.error("Gagal mengambil data jenis program"); + } + } catch (error) { + console.error((error as Error).message); + toast.error("Terjadi kesalahan saat mengambil data jenis program"); + } finally { + stateJenisProgram.findById.loading = false; + } + }, + }, + + update: { + loading: false, + async save(data: JenisProgramForm) { + const cek = templateJenisProgramForm.safeParse(data); + if (!cek.success) { + const errors = cek.error.issues + .map((issue) => `${issue.path.join(".")}: ${issue.message}`) + .join(", "); + toast.error(`Form tidak valid: ${errors}`); + return; + } + + try { + stateJenisProgram.update.loading = true; + const res = + await ApiFetch.api.pendidikan.pendidikannonformal.jenisprogramyangdiselenggarakan[ + "update" + ].post(data); + if (res.status === 200) { + toast.success("Data jenis program berhasil diubah"); + await stateJenisProgram.findById.load(data.id); + } else { + toast.error("Gagal mengubah data jenis program"); + } + } catch (error) { + console.error((error as Error).message); + toast.error("Terjadi kesalahan saat mengubah data jenis program"); + } finally { + stateJenisProgram.update.loading = false; + } + }, + }, +}); + +const pendidikanNonFormalState = proxy({ + stateTujuanPendidikanNonFormal, + stateTempatKegiatan, + stateJenisProgram, +}); + +export default pendidikanNonFormalState; diff --git a/src/app/admin/(dashboard)/pendidikan/pendidikan-non-formal/_lib/layoutTabs.tsx b/src/app/admin/(dashboard)/pendidikan/pendidikan-non-formal/_lib/layoutTabs.tsx new file mode 100644 index 00000000..8b64a065 --- /dev/null +++ b/src/app/admin/(dashboard)/pendidikan/pendidikan-non-formal/_lib/layoutTabs.tsx @@ -0,0 +1,68 @@ +/* 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: "Tujuan Program", + value: "tujuan-program", + href: "/admin/pendidikan/pendidikan-non-formal/tujuan-program" + }, + { + label: "Tempat Kegiatan", + value: "tempat-kegiatan", + href: "/admin/pendidikan/pendidikan-non-formal/tempat-kegiatan" + }, + { + label: "Jenis Program yang Diselenggarakan", + value: "jenis-program-yang-diselenggarakan", + href: "/admin/pendidikan/pendidikan-non-formal/jenis-program-yang-diselenggarakan" + }, + + ]; + 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 ( + + Pendidikan Non Formal + + + {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/pendidikan-non-formal/_lib/pendidikanNonFormalTextEditor.tsx b/src/app/admin/(dashboard)/pendidikan/pendidikan-non-formal/_lib/pendidikanNonFormalTextEditor.tsx new file mode 100644 index 00000000..67a90635 --- /dev/null +++ b/src/app/admin/(dashboard)/pendidikan/pendidikan-non-formal/_lib/pendidikanNonFormalTextEditor.tsx @@ -0,0 +1,99 @@ +'use client' +import { Button, Stack } from '@mantine/core'; +import { Link, RichTextEditor } from '@mantine/tiptap'; +import Highlight from '@tiptap/extension-highlight'; +import SubScript from '@tiptap/extension-subscript'; +import Superscript from '@tiptap/extension-superscript'; +import TextAlign from '@tiptap/extension-text-align'; +import Underline from '@tiptap/extension-underline'; +import { useEditor } from '@tiptap/react'; +import StarterKit from '@tiptap/starter-kit'; +import { useEffect } from 'react'; + +export function PendidikanNonFormalTextEditor({ onSubmit, onChange, showSubmit = true, initialContent = '', }: { + onSubmit?: (val: string) => void, + onChange: (val: string) => void, + showSubmit?: boolean, + initialContent?: string }) { + const editor = useEditor({ + extensions: [ + StarterKit, + Underline, + Link, + Superscript, + SubScript, + Highlight, + TextAlign.configure({ types: ['heading', 'paragraph'] }), + ], + immediatelyRender: false, + content: initialContent, + onUpdate : ({editor}) => { + onChange(editor.getHTML()) + } + }); + + useEffect(() => { + if (editor && initialContent !== editor.getHTML()) { + editor.commands.setContent(initialContent || '

'); + } + }, [initialContent, editor]); + + return ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {showSubmit && ( + + )} + + ); +} \ No newline at end of file diff --git a/src/app/admin/(dashboard)/pendidikan/pendidikan-non-formal/jenis-program-yang-diselenggarakan/edit/page.tsx b/src/app/admin/(dashboard)/pendidikan/pendidikan-non-formal/jenis-program-yang-diselenggarakan/edit/page.tsx new file mode 100644 index 00000000..4f64794f --- /dev/null +++ b/src/app/admin/(dashboard)/pendidikan/pendidikan-non-formal/jenis-program-yang-diselenggarakan/edit/page.tsx @@ -0,0 +1,86 @@ +'use client' +import pendidikanNonFormalState from '@/app/admin/(dashboard)/_state/pendidikan/pendidikan-non-formal'; +import colors from '@/con/colors'; +import { Box, Button, Group, Paper, Stack, Text, TextInput, Title } from '@mantine/core'; +import { useShallowEffect } from '@mantine/hooks'; +import { IconArrowBack } from '@tabler/icons-react'; +import dynamic from 'next/dynamic'; +import { useRouter } from 'next/navigation'; +import { useEffect, useState } from 'react'; +import { useProxy } from 'valtio/utils'; + +const JenisProgramYangDiselenggarakanTextEditor = dynamic(() => import('../../_lib/pendidikanNonFormalTextEditor').then(mod => mod.PendidikanNonFormalTextEditor), { + ssr: false, +}); + +function EditJenisProgramYangDiselenggarakan() { + const router = useRouter() + const editState = useProxy(pendidikanNonFormalState.stateJenisProgram) + const [judul, setJudul] = useState(''); + const [content, setContent] = useState(''); + + useShallowEffect(() => { + if (!editState.findById.data) { + editState.findById.initialize(); // biar masuk ke `findFirst` route kamu + } + }, []); + + useEffect(() => { + if (editState.findById.data) { + setJudul(editState.findById.data.judul ?? '') + setContent(editState.findById.data.deskripsi ?? '') + } + }, [editState.findById.data]) + + const submit = () => { + if (editState.findById.data) { + editState.findById.data.judul = judul; + editState.findById.data.deskripsi = content; + editState.update.save(editState.findById.data) + } + router.push('/admin/pendidikan/pendidikan-non-formal/jenis-program-yang-diselenggarakan') + } + return ( + + + + + + + + + Edit Jenis Program Yang Diselenggarakan + Judul} + value={judul} + onChange={(e) => setJudul(e.target.value)} + /> + Deskripsi + + + + + + + + + + ); +} + +export default EditJenisProgramYangDiselenggarakan; diff --git a/src/app/admin/(dashboard)/pendidikan/pendidikan-non-formal/jenis-program-yang-diselenggarakan/page.tsx b/src/app/admin/(dashboard)/pendidikan/pendidikan-non-formal/jenis-program-yang-diselenggarakan/page.tsx new file mode 100644 index 00000000..918462a1 --- /dev/null +++ b/src/app/admin/(dashboard)/pendidikan/pendidikan-non-formal/jenis-program-yang-diselenggarakan/page.tsx @@ -0,0 +1,54 @@ +'use client' +import colors from '@/con/colors'; +import { Box, Button, Grid, GridCol, Paper, Skeleton, Stack, Text, Title } from '@mantine/core'; +import { useShallowEffect } from '@mantine/hooks'; +import { IconEdit } from '@tabler/icons-react'; +import { useRouter } from 'next/navigation'; +import { useProxy } from 'valtio/utils'; +import pendidikanNonFormalState from '../../../_state/pendidikan/pendidikan-non-formal'; + +function Page() { + const router = useRouter() + const listPreview = useProxy(pendidikanNonFormalState.stateJenisProgram) + useShallowEffect(() => { + listPreview.findById.load('edit') + }, []) + + if (!listPreview.findById.data) { + return ( + + + + ) + } + return ( + + + + + Preview Jenis Program Yang Diselenggarakan + + + + + + + + + + + + + + + + + + + + ) +} + +export default Page; diff --git a/src/app/admin/(dashboard)/pendidikan/pendidikan-non-formal/layout.tsx b/src/app/admin/(dashboard)/pendidikan/pendidikan-non-formal/layout.tsx new file mode 100644 index 00000000..4f25d331 --- /dev/null +++ b/src/app/admin/(dashboard)/pendidikan/pendidikan-non-formal/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/(dashboard)/pendidikan/pendidikan-non-formal/page.tsx b/src/app/admin/(dashboard)/pendidikan/pendidikan-non-formal/page.tsx deleted file mode 100644 index a7451ab4..00000000 --- a/src/app/admin/(dashboard)/pendidikan/pendidikan-non-formal/page.tsx +++ /dev/null @@ -1,74 +0,0 @@ -'use client' -import colors from '@/con/colors'; -import { Box, Button, Paper, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core'; -import { IconDeviceImacCog, IconSearch } from '@tabler/icons-react'; -import { useRouter } from 'next/navigation'; -import HeaderSearch from '../../_com/header'; -import JudulList from '../../_com/judulList'; - -function PendidikanNonFormal() { - return ( - - } - /> - - - ); -} - -function ListPendidikanNonFormal() { - const router = useRouter(); - return ( - - - - - - - - - Nomor - Nama Lengkap - Nomor Telepon - Email - Detail - - - - - - - 1 - - - - Nama Lengkap - - - Nomor Telepon - - - Email - - - - - - -
-
-
-
-
- ) -} - -export default PendidikanNonFormal; diff --git a/src/app/admin/(dashboard)/pendidikan/pendidikan-non-formal/tempat-kegiatan/edit/page.tsx b/src/app/admin/(dashboard)/pendidikan/pendidikan-non-formal/tempat-kegiatan/edit/page.tsx new file mode 100644 index 00000000..3d710c51 --- /dev/null +++ b/src/app/admin/(dashboard)/pendidikan/pendidikan-non-formal/tempat-kegiatan/edit/page.tsx @@ -0,0 +1,86 @@ +'use client' +import pendidikanNonFormalState from '@/app/admin/(dashboard)/_state/pendidikan/pendidikan-non-formal'; +import colors from '@/con/colors'; +import { Box, Button, Group, Paper, Stack, Text, TextInput, Title } from '@mantine/core'; +import { useShallowEffect } from '@mantine/hooks'; +import { IconArrowBack } from '@tabler/icons-react'; +import dynamic from 'next/dynamic'; +import { useRouter } from 'next/navigation'; +import { useEffect, useState } from 'react'; +import { useProxy } from 'valtio/utils'; + +const TempatKegiatanTextEditor = dynamic(() => import('../../_lib/pendidikanNonFormalTextEditor').then(mod => mod.PendidikanNonFormalTextEditor), { + ssr: false, +}); + +function EditTempatKegiatan() { + const router = useRouter() + const editState = useProxy(pendidikanNonFormalState.stateTempatKegiatan) + const [judul, setJudul] = useState(''); + const [content, setContent] = useState(''); + + useShallowEffect(() => { + if (!editState.findById.data) { + editState.findById.initialize(); // biar masuk ke `findFirst` route kamu + } + }, []); + + useEffect(() => { + if (editState.findById.data) { + setJudul(editState.findById.data.judul ?? '') + setContent(editState.findById.data.deskripsi ?? '') + } + }, [editState.findById.data]) + + const submit = () => { + if (editState.findById.data) { + editState.findById.data.judul = judul; + editState.findById.data.deskripsi = content; + editState.update.save(editState.findById.data) + } + router.push('/admin/pendidikan/pendidikan-non-formal/tempat-kegiatan') + } + return ( + + + + + + + + + Edit Tempat Kegiatan + Judul} + value={judul} + onChange={(e) => setJudul(e.target.value)} + /> + Deskripsi + + + + + + + + + + ); +} + +export default EditTempatKegiatan; diff --git a/src/app/admin/(dashboard)/pendidikan/pendidikan-non-formal/tempat-kegiatan/page.tsx b/src/app/admin/(dashboard)/pendidikan/pendidikan-non-formal/tempat-kegiatan/page.tsx new file mode 100644 index 00000000..d04f719f --- /dev/null +++ b/src/app/admin/(dashboard)/pendidikan/pendidikan-non-formal/tempat-kegiatan/page.tsx @@ -0,0 +1,54 @@ +'use client' +import colors from '@/con/colors'; +import { Box, Button, Grid, GridCol, Paper, Skeleton, Stack, Text, Title } from '@mantine/core'; +import { useShallowEffect } from '@mantine/hooks'; +import { IconEdit } from '@tabler/icons-react'; +import { useRouter } from 'next/navigation'; +import { useProxy } from 'valtio/utils'; +import statePendidikanNonFormal from '../../../_state/pendidikan/pendidikan-non-formal'; + +function Page() { + const router = useRouter() + const listPreview = useProxy(statePendidikanNonFormal.stateTempatKegiatan) + useShallowEffect(() => { + listPreview.findById.load('edit') + }, []) + + if (!listPreview.findById.data) { + return ( + + + + ) + } + return ( + + + + + Preview Tempat Kegiatan + + + + + + + + + + + + + + + + + + + + ) +} + +export default Page; diff --git a/src/app/admin/(dashboard)/pendidikan/pendidikan-non-formal/tujuan-program/edit/page.tsx b/src/app/admin/(dashboard)/pendidikan/pendidikan-non-formal/tujuan-program/edit/page.tsx new file mode 100644 index 00000000..e7140d91 --- /dev/null +++ b/src/app/admin/(dashboard)/pendidikan/pendidikan-non-formal/tujuan-program/edit/page.tsx @@ -0,0 +1,86 @@ +'use client' +import pendidikanNonFormalState from '@/app/admin/(dashboard)/_state/pendidikan/pendidikan-non-formal'; +import colors from '@/con/colors'; +import { Box, Button, Group, Paper, Stack, Text, TextInput, Title } from '@mantine/core'; +import { useShallowEffect } from '@mantine/hooks'; +import { IconArrowBack } from '@tabler/icons-react'; +import dynamic from 'next/dynamic'; +import { useRouter } from 'next/navigation'; +import { useEffect, useState } from 'react'; +import { useProxy } from 'valtio/utils'; + +const PendidikanNonFormalTextEditor = dynamic(() => import('../../_lib/pendidikanNonFormalTextEditor').then(mod => mod.PendidikanNonFormalTextEditor), { + ssr: false, +}); + +function EditTujuanProgram() { + const router = useRouter() + const editState = useProxy(pendidikanNonFormalState.stateTujuanPendidikanNonFormal) + const [judul, setJudul] = useState(''); + const [content, setContent] = useState(''); + + useShallowEffect(() => { + if (!editState.findById.data) { + editState.findById.initialize(); // biar masuk ke `findFirst` route kamu + } + }, []); + + useEffect(() => { + if (editState.findById.data) { + setJudul(editState.findById.data.judul ?? '') + setContent(editState.findById.data.deskripsi ?? '') + } + }, [editState.findById.data]) + + const submit = () => { + if (editState.findById.data) { + editState.findById.data.judul = judul; + editState.findById.data.deskripsi = content; + editState.update.save(editState.findById.data) + } + router.push('/admin/pendidikan/pendidikan-non-formal/tujuan-program') + } + return ( + + + + + + + + + Edit Tujuan Program + Judul} + value={judul} + onChange={(e) => setJudul(e.target.value)} + /> + Deskripsi + + + + + + + + + + ); +} + +export default EditTujuanProgram; diff --git a/src/app/admin/(dashboard)/pendidikan/pendidikan-non-formal/tujuan-program/page.tsx b/src/app/admin/(dashboard)/pendidikan/pendidikan-non-formal/tujuan-program/page.tsx new file mode 100644 index 00000000..e5282ddf --- /dev/null +++ b/src/app/admin/(dashboard)/pendidikan/pendidikan-non-formal/tujuan-program/page.tsx @@ -0,0 +1,54 @@ +'use client' +import colors from '@/con/colors'; +import { Box, Button, Grid, GridCol, Paper, Skeleton, Stack, Text, Title } from '@mantine/core'; +import { useShallowEffect } from '@mantine/hooks'; +import { IconEdit } from '@tabler/icons-react'; +import { useRouter } from 'next/navigation'; +import { useProxy } from 'valtio/utils'; +import statePendidikanNonFormal from '../../../_state/pendidikan/pendidikan-non-formal'; + +function Page() { + const router = useRouter() + const listPreview = useProxy(statePendidikanNonFormal.stateTujuanPendidikanNonFormal) + useShallowEffect(() => { + listPreview.findById.load('edit') + }, []) + + if (!listPreview.findById.data) { + return ( + + + + ) + } + return ( + + + + + Preview Pendidikan Non Formal + + + + + + + + + + + + + + + + + + + + ) +} + +export default Page; diff --git a/src/app/admin/_com/list_PageAdmin.tsx b/src/app/admin/_com/list_PageAdmin.tsx index 2aa7563a..957a697f 100644 --- a/src/app/admin/_com/list_PageAdmin.tsx +++ b/src/app/admin/_com/list_PageAdmin.tsx @@ -364,7 +364,7 @@ export const navBar = [ { id: "Pendidikan_5", name: "Pendidikan Non Formal", - path: "/admin/pendidikan/pendidikan-non-formal" + path: "/admin/pendidikan/pendidikan-non-formal/tujuan-program" }, { id: "Pendidikan_6", diff --git a/src/app/api/[[...slugs]]/_lib/pendidikan/index.ts b/src/app/api/[[...slugs]]/_lib/pendidikan/index.ts index 307b4908..141e6757 100644 --- a/src/app/api/[[...slugs]]/_lib/pendidikan/index.ts +++ b/src/app/api/[[...slugs]]/_lib/pendidikan/index.ts @@ -2,6 +2,7 @@ import Elysia from "elysia"; import InfoSekolahPAUD from "./info-sekolah-paud"; import ProgramPendidikanAnak from "./program-pendidikan-anak"; import BimbinganBelajarDesa from "./bimbingan-belajar-desa"; +import PendidikanNonFormal from "./pendidikan-non-formal"; const Pendidikan = new Elysia({ prefix: "/api/pendidikan", @@ -11,5 +12,6 @@ const Pendidikan = new Elysia({ .use(InfoSekolahPAUD) .use(ProgramPendidikanAnak) .use(BimbinganBelajarDesa) +.use(PendidikanNonFormal) export default Pendidikan; \ No newline at end of file diff --git a/src/app/api/[[...slugs]]/_lib/pendidikan/pendidikan-non-formal/index.ts b/src/app/api/[[...slugs]]/_lib/pendidikan/pendidikan-non-formal/index.ts new file mode 100644 index 00000000..5dfc0954 --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/pendidikan/pendidikan-non-formal/index.ts @@ -0,0 +1,14 @@ +import Elysia from "elysia"; +import JenisProgramYangDiselenggarakan from "./jenis-program-yang-diselenggarakan"; +import TujuanPendidikanNonFormal from "./tujuan-program"; +import TempatKegiatan from "./tempat-kegiatan"; + +const PendidikanNonFormal = new Elysia({ + prefix: "/pendidikannonformal", + tags: ["Pendidikan/Pendidikan Non Formal"] +}) +.use(JenisProgramYangDiselenggarakan) +.use(TujuanPendidikanNonFormal) +.use(TempatKegiatan) + +export default PendidikanNonFormal diff --git a/src/app/api/[[...slugs]]/_lib/pendidikan/pendidikan-non-formal/jenis-program-yang-diselenggarakan/findUnique.ts b/src/app/api/[[...slugs]]/_lib/pendidikan/pendidikan-non-formal/jenis-program-yang-diselenggarakan/findUnique.ts new file mode 100644 index 00000000..01301806 --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/pendidikan/pendidikan-non-formal/jenis-program-yang-diselenggarakan/findUnique.ts @@ -0,0 +1,37 @@ +import prisma from "@/lib/prisma"; +import { Context } from "elysia"; + +export default async function jenisProgramYangDiselenggarakanFindUnique( + context: Context +) { + try { + const id = context?.params?.slugs?.[0]; + + // If no ID provided, get the first profile + if (!id) { + const data = await prisma.jenisProgramYangDiselenggarakan.findFirst(); + return { + success: true, + data, + }; + } + + const data = await prisma.jenisProgramYangDiselenggarakan.findUniqueOrThrow( + { + where: { id }, + } + ); + + return { + success: true, + data, + }; + } catch (error) { + console.error("Error fetching jenis program yang diselenggarakan:", error); + + return { + success: false, + message: error instanceof Error ? error.message : "Unknown error", + }; + } +} diff --git a/src/app/api/[[...slugs]]/_lib/pendidikan/pendidikan-non-formal/jenis-program-yang-diselenggarakan/index.ts b/src/app/api/[[...slugs]]/_lib/pendidikan/pendidikan-non-formal/jenis-program-yang-diselenggarakan/index.ts new file mode 100644 index 00000000..57b50928 --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/pendidikan/pendidikan-non-formal/jenis-program-yang-diselenggarakan/index.ts @@ -0,0 +1,19 @@ +import Elysia, { t } from "elysia"; +import jenisProgramYangDiselenggarakanFindUnique from "./findUnique"; +import jenisProgramYangDiselenggarakanUpdate from "./updt"; + + +const JenisProgramYangDiselenggarakan = new Elysia({ + prefix: "/jenisprogramyangdiselenggarakan", + tags: ["Pendidikan/Pendidikan Non Formal/Jenis Program Yang Diselenggarakan"] +}) + .get("/find-by-id", jenisProgramYangDiselenggarakanFindUnique) + .post("/update", jenisProgramYangDiselenggarakanUpdate, { + body: t.Object({ + id: t.String(), + judul: t.String(), + deskripsi: t.String(), + }) + }) + +export default JenisProgramYangDiselenggarakan \ No newline at end of file diff --git a/src/app/api/[[...slugs]]/_lib/pendidikan/pendidikan-non-formal/jenis-program-yang-diselenggarakan/updt.ts b/src/app/api/[[...slugs]]/_lib/pendidikan/pendidikan-non-formal/jenis-program-yang-diselenggarakan/updt.ts new file mode 100644 index 00000000..e4869dbb --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/pendidikan/pendidikan-non-formal/jenis-program-yang-diselenggarakan/updt.ts @@ -0,0 +1,29 @@ +import prisma from "@/lib/prisma"; +import { Prisma } from "@prisma/client"; +import { Context } from "elysia"; + +type FormUpdate = Prisma.JenisProgramYangDiselenggarakanGetPayload<{ + select: { + id: true; + judul: true; + deskripsi: true; + } +}> +export default async function jenisProgramYangDiselenggarakanUpdate(context: Context) { + const body = context.body as FormUpdate; + + await prisma.jenisProgramYangDiselenggarakan.update({ + where: { + id: body.id + }, + data: { + judul: body.judul, + deskripsi: body.deskripsi, + } + }) + + return { + success: true, + message: "Jenis program yang diselenggarakan berhasil diupdate", + } +} \ No newline at end of file diff --git a/src/app/api/[[...slugs]]/_lib/pendidikan/pendidikan-non-formal/tempat-kegiatan/findUnique.ts b/src/app/api/[[...slugs]]/_lib/pendidikan/pendidikan-non-formal/tempat-kegiatan/findUnique.ts new file mode 100644 index 00000000..eb67135c --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/pendidikan/pendidikan-non-formal/tempat-kegiatan/findUnique.ts @@ -0,0 +1,37 @@ +import prisma from "@/lib/prisma"; +import { Context } from "elysia"; + +export default async function tempatKegiatanFindUnique( + context: Context +) { + try { + const id = context?.params?.slugs?.[0]; + + // If no ID provided, get the first profile + if (!id) { + const data = await prisma.tempatKegiatan.findFirst(); + return { + success: true, + data, + }; + } + + const data = await prisma.tempatKegiatan.findUniqueOrThrow( + { + where: { id }, + } + ); + + return { + success: true, + data, + }; + } catch (error) { + console.error("Error fetching tempat kegiatan:", error); + + return { + success: false, + message: error instanceof Error ? error.message : "Unknown error", + }; + } +} diff --git a/src/app/api/[[...slugs]]/_lib/pendidikan/pendidikan-non-formal/tempat-kegiatan/index.ts b/src/app/api/[[...slugs]]/_lib/pendidikan/pendidikan-non-formal/tempat-kegiatan/index.ts new file mode 100644 index 00000000..c3942953 --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/pendidikan/pendidikan-non-formal/tempat-kegiatan/index.ts @@ -0,0 +1,18 @@ +import Elysia, { t } from "elysia"; +import tempatKegiatanFindUnique from "./findUnique"; +import tempatKegiatanUpdate from "./updt"; + +const TempatKegiatan = new Elysia({ + prefix: "/tempatkegiatan", + tags: ["Pendidikan/Pendidikan Non Formal/Tempat Kegiatan"] +}) + .get("/find-by-id", tempatKegiatanFindUnique) + .post("/update", tempatKegiatanUpdate, { + body: t.Object({ + id: t.String(), + judul: t.String(), + deskripsi: t.String(), + }) + }) + +export default TempatKegiatan \ No newline at end of file diff --git a/src/app/api/[[...slugs]]/_lib/pendidikan/pendidikan-non-formal/tempat-kegiatan/updt.ts b/src/app/api/[[...slugs]]/_lib/pendidikan/pendidikan-non-formal/tempat-kegiatan/updt.ts new file mode 100644 index 00000000..578aa759 --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/pendidikan/pendidikan-non-formal/tempat-kegiatan/updt.ts @@ -0,0 +1,29 @@ +import prisma from "@/lib/prisma"; +import { Prisma } from "@prisma/client"; +import { Context } from "elysia"; + +type FormUpdate = Prisma.TempatKegiatanGetPayload<{ + select: { + id: true; + judul: true; + deskripsi: true; + } +}> +export default async function tempatKegiatanUpdate(context: Context) { + const body = context.body as FormUpdate; + + await prisma.tempatKegiatan.update({ + where: { + id: body.id + }, + data: { + judul: body.judul, + deskripsi: body.deskripsi, + } + }) + + return { + success: true, + message: "Tempat kegiatan berhasil diupdate", + } +} \ No newline at end of file diff --git a/src/app/api/[[...slugs]]/_lib/pendidikan/pendidikan-non-formal/tujuan-program/findUnique.ts b/src/app/api/[[...slugs]]/_lib/pendidikan/pendidikan-non-formal/tujuan-program/findUnique.ts new file mode 100644 index 00000000..661c6611 --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/pendidikan/pendidikan-non-formal/tujuan-program/findUnique.ts @@ -0,0 +1,37 @@ +import prisma from "@/lib/prisma"; +import { Context } from "elysia"; + +export default async function tujuanPendidikanNonFormalFindUnique( + context: Context +) { + try { + const id = context?.params?.slugs?.[0]; + + // If no ID provided, get the first profile + if (!id) { + const data = await prisma.tujuanPendidikanNonFormal.findFirst(); + return { + success: true, + data, + }; + } + + const data = await prisma.tujuanPendidikanNonFormal.findUniqueOrThrow( + { + where: { id }, + } + ); + + return { + success: true, + data, + }; + } catch (error) { + console.error("Error fetching tujuan pendidikan non formal:", error); + + return { + success: false, + message: error instanceof Error ? error.message : "Unknown error", + }; + } +} diff --git a/src/app/api/[[...slugs]]/_lib/pendidikan/pendidikan-non-formal/tujuan-program/index.ts b/src/app/api/[[...slugs]]/_lib/pendidikan/pendidikan-non-formal/tujuan-program/index.ts new file mode 100644 index 00000000..d67413c0 --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/pendidikan/pendidikan-non-formal/tujuan-program/index.ts @@ -0,0 +1,18 @@ +import Elysia, { t } from "elysia"; +import tujuanPendidikanNonFormalFindUnique from "./findUnique"; +import tujuanPendidikanNonFormalUpdate from "./updt"; + +const TujuanPendidikanNonFormal = new Elysia({ + prefix: "/tujuanpendidikannonformal", + tags: ["Pendidikan/Pendidikan Non Formal/Tujuan Pendidikan Non Formal"] +}) + .get("/find-by-id", tujuanPendidikanNonFormalFindUnique) + .post("/update", tujuanPendidikanNonFormalUpdate, { + body: t.Object({ + id: t.String(), + judul: t.String(), + deskripsi: t.String(), + }) + }) + +export default TujuanPendidikanNonFormal \ No newline at end of file diff --git a/src/app/api/[[...slugs]]/_lib/pendidikan/pendidikan-non-formal/tujuan-program/updt.ts b/src/app/api/[[...slugs]]/_lib/pendidikan/pendidikan-non-formal/tujuan-program/updt.ts new file mode 100644 index 00000000..b301884f --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/pendidikan/pendidikan-non-formal/tujuan-program/updt.ts @@ -0,0 +1,29 @@ +import prisma from "@/lib/prisma"; +import { Prisma } from "@prisma/client"; +import { Context } from "elysia"; + +type FormCreate = Prisma.TujuanPendidikanNonFormalGetPayload<{ + select: { + id: true; + judul: true; + deskripsi: true; + } +}> +export default async function tujuanPendidikanNonFormalUpdate(context: Context) { + const body = context.body as FormCreate; + + await prisma.tujuanPendidikanNonFormal.update({ + where: { + id: body.id + }, + data: { + judul: body.judul, + deskripsi: body.deskripsi, + } + }) + + return { + success: true, + message: "Tujuan pendidikan non formal berhasil diupdate", + } +} \ No newline at end of file