diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 0a998e2f..261cf123 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -324,8 +324,8 @@ model PegawaiPPID { model StrukturOrganisasiPPID { id String @id @default(uuid()) posisiOrganisasiId String @db.VarChar(50) - pegawaiId String - hubunganOrganisasiId String + pegawaiId String + hubunganOrganisasiId String posisiOrganisasi PosisiOrganisasiPPID @relation(fields: [posisiOrganisasiId], references: [id]) pegawai PegawaiPPID @relation(fields: [pegawaiId], references: [id]) createdAt DateTime @default(now()) @@ -1450,8 +1450,8 @@ model PegawaiBumDes { model StrukturOrganisasiBumDes { id String @id @default(uuid()) posisiOrganisasiId String @db.VarChar(50) - pegawaiId String - hubunganOrganisasiId String + pegawaiId String + hubunganOrganisasiId String posisiOrganisasi PosisiOrganisasiBumDes @relation(fields: [posisiOrganisasiId], references: [id]) pegawai PegawaiBumDes @relation(fields: [pegawaiId], references: [id]) createdAt DateTime @default(now()) @@ -1820,7 +1820,7 @@ model KategoriKegiatan { isActive Boolean @default(true) KegiatanDesa KegiatanDesa[] } - + // ========================================= EDUKASI LINGKUNGAN ========================================= // model TujuanEdukasiLingkungan { id String @id @default(cuid()) @@ -2087,6 +2087,9 @@ model DataPerpustakaan { updatedAt DateTime @updatedAt deletedAt DateTime @default(now()) isActive Boolean @default(true) + + // relasi baru ke peminjaman + peminjamanBuku PeminjamanBuku[] } model KategoriBuku { @@ -2099,6 +2102,31 @@ model KategoriBuku { DataPerpustakaan DataPerpustakaan[] } +model PeminjamanBuku { + id String @id @default(cuid()) + nama String + noTelp String + alamat String + bukuId String + tanggalPinjam DateTime @default(now()) + batasKembali DateTime // tenggat waktu pengembalian + tanggalKembali DateTime? // diisi saat dikembalikan + status StatusPeminjaman @default(Dipinjam) + catatan String? // opsional, misal: kondisi buku + buku DataPerpustakaan @relation(fields: [bukuId], references: [id]) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + deletedAt DateTime @default(now()) + isActive Boolean @default(true) +} + +enum StatusPeminjaman { + Dipinjam + Dikembalikan + Terlambat + Dibatalkan +} + // ========================================= USER ========================================= // model User { diff --git a/src/app/admin/(dashboard)/_state/pendidikan/perpustakaan-digital.ts b/src/app/admin/(dashboard)/_state/pendidikan/perpustakaan-digital.ts index da6411e9..6fee556c 100644 --- a/src/app/admin/(dashboard)/_state/pendidikan/perpustakaan-digital.ts +++ b/src/app/admin/(dashboard)/_state/pendidikan/perpustakaan-digital.ts @@ -514,9 +514,312 @@ const kategoriBuku = proxy({ }, }); +const templatePeminjamanBuku = z.object({ + nama: z.string().min(1, "Nama harus diisi"), + noTelp: z.string().min(1, "No Telp harus diisi"), + alamat: z.string().min(1, "Alamat harus diisi"), + bukuId: z.string().min(1, "Buku ID harus diisi"), + tanggalPinjam: z.string().min(1, "Tanggal Pinjam harus diisi"), + batasKembali: z.string().min(1, "Batas Kembali harus diisi"), + tanggalKembali: z.string().min(1, "Tanggal Kembali harus diisi"), + catatan: z.string().min(1, "Catatan harus diisi") +}); + +const defaultPeminjamanBuku = { + nama: "", + noTelp: "", + alamat: "", + bukuId: "", + tanggalPinjam: "", + batasKembali: "", + tanggalKembali: "", + catatan: "" +}; + +interface FormEditData { + nama: string; + noTelp: string; + alamat: string; + bukuId: string; + buku?: { + id: string; + judul: string; + }; + tanggalPinjam: string; + batasKembali: string; + tanggalKembali: string; + catatan: string; + status: 'Dipinjam' | 'Dikembalikan' | 'Terlambat' | 'Dibatalkan'; +} + +const editForm: FormEditData = { + nama: "", + noTelp: "", + alamat: "", + bukuId: "", + tanggalPinjam: "", + batasKembali: "", + tanggalKembali: "", + catatan: "", + status: "Dipinjam" +} + +const peminjamanBuku = proxy({ + create: { + form: { ...defaultPeminjamanBuku }, + loading: false, + async create() { + const cek = templatePeminjamanBuku.safeParse(peminjamanBuku.create.form); + if (!cek.success) { + const err = `[${cek.error.issues + .map((v) => `${v.path.join(".")}`) + .join("\n")}] required`; + return toast.error(err); + } + + try { + peminjamanBuku.create.loading = true; + const res = + await ApiFetch.api.pendidikan.perpustakaandigital.peminjamanbuku[ + "create" + ].post(peminjamanBuku.create.form); + if (res.status === 200) { + peminjamanBuku.findMany.load(); + return toast.success("Data Peminjaman Buku Berhasil Dibuat"); + } + console.log(res); + return toast.error("failed create"); + } catch (error) { + console.log(error); + return toast.error("failed create"); + } finally { + peminjamanBuku.create.loading = false; + } + }, + }, + findMany: { + data: [] as Prisma.PeminjamanBukuGetPayload<{ + include: { + buku: true; + }; + }>[], + page: 1, + totalPages: 1, + loading: false, + search: "", + load: async (page = 1, limit = 10, search = "") => { + peminjamanBuku.findMany.loading = true; // ✅ Akses langsung via nama path + peminjamanBuku.findMany.page = page; + peminjamanBuku.findMany.search = search; + + try { + const query: any = { page, limit }; + if (search) query.search = search; + + const res = await ApiFetch.api.pendidikan.perpustakaandigital.peminjamanbuku["findMany"].get({ query }); + + if (res.status === 200 && res.data?.success) { + peminjamanBuku.findMany.data = res.data.data ?? []; + peminjamanBuku.findMany.totalPages = res.data.totalPages ?? 1; + } else { + peminjamanBuku.findMany.data = []; + peminjamanBuku.findMany.totalPages = 1; + } + } catch (err) { + console.error("Gagal fetch data peminjaman buku paginated:", err); + peminjamanBuku.findMany.data = []; + peminjamanBuku.findMany.totalPages = 1; + } finally { + peminjamanBuku.findMany.loading = false; + } + }, + }, + findUnique: { + data: null as Prisma.PeminjamanBukuGetPayload<{ + include: { + buku: true; + }; + }> | null, + loading: false, + async load(id: string) { + try { + const res = await fetch( + `/api/pendidikan/perpustakaandigital/peminjamanbuku/${id}` + ); + if (res.ok) { + const data = await res.json(); + peminjamanBuku.findUnique.data = data.data ?? null; + } else { + console.error("Failed to fetch data", res.status, res.statusText); + peminjamanBuku.findUnique.data = null; + } + } catch (error) { + console.error("Error fetching data:", error); + peminjamanBuku.findUnique.data = null; + } + }, + }, + delete: { + loading: false, + async delete(id: string) { + if (!id) return toast.warn("ID tidak valid"); + + try { + peminjamanBuku.delete.loading = true; + + const response = await fetch( + `/api/pendidikan/perpustakaandigital/peminjamanbuku/del/${id}`, + { + method: "DELETE", + headers: { + "Content-Type": "application/json", + }, + } + ); + + const result = await response.json(); + + if (response.ok && result?.success) { + toast.success( + result.message || "Data Peminjaman Buku berhasil dihapus" + ); + await peminjamanBuku.findMany.load(); // refresh list + } else { + toast.error(result?.message || "Gagal menghapus Data Peminjaman Buku"); + } + } catch (error) { + console.error("Gagal delete:", error); + toast.error("Terjadi kesalahan saat menghapus Data Peminjaman Buku"); + } finally { + peminjamanBuku.delete.loading = false; + } + }, + }, + update: { + id: "", + form: { ...editForm }, + loading: false, + async load(id: string) { + if (!id) { + toast.warn("ID tidak valid"); + return null; + } + + try { + const response = await fetch( + `/api/pendidikan/perpustakaandigital/peminjamanbuku/${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, + noTelp: data.noTelp, + alamat: data.alamat, + bukuId: data.bukuId, + tanggalPinjam: data.tanggalPinjam, + batasKembali: data.batasKembali, + tanggalKembali: data.tanggalKembali, + catatan: data.catatan, + status: data.status + }; + return data; // Return the loaded data + } else { + throw new Error(result?.message || "Gagal memuat data"); + } + } catch (error) { + console.error("Error loading peminjaman buku:", error); + toast.error( + error instanceof Error ? error.message : "Gagal memuat data" + ); + return null; + } + }, + async update() { + const cek = templatePeminjamanBuku.safeParse(peminjamanBuku.update.form); + if (!cek.success) { + const err = `[${cek.error.issues + .map((v) => `${v.path.join(".")}`) + .join("\n")}] required`; + toast.error(err); + return false; + } + + try { + peminjamanBuku.update.loading = true; + + const response = await fetch( + `/api/pendidikan/perpustakaandigital/peminjamanbuku/${this.id}`, + { + method: "PUT", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + nama: this.form.nama, + noTelp: this.form.noTelp, + alamat: this.form.alamat, + bukuId: this.form.bukuId, + tanggalPinjam: this.form.tanggalPinjam, + batasKembali: this.form.batasKembali, + tanggalKembali: this.form.tanggalKembali, + catatan: this.form.catatan, + status: this.form.status + }), + } + ); + + 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 data peminjaman buku"); + await peminjamanBuku.findMany.load(); // refresh list + return true; + } else { + throw new Error(result.message || "Gagal update data peminjaman buku"); + } + } catch (error) { + console.error("Error updating data peminjaman buku:", error); + toast.error( + error instanceof Error + ? error.message + : "Terjadi kesalahan saat update data peminjaman buku" + ); + return false; + } finally { + peminjamanBuku.update.loading = false; + } + }, + reset() { + peminjamanBuku.update.id = ""; + peminjamanBuku.update.form = { ...editForm }; + }, + }, +}) + const perpustakaanDigitalState = proxy({ dataPerpustakaan, kategoriBuku, + peminjamanBuku, }); export default perpustakaanDigitalState; diff --git a/src/app/admin/(dashboard)/pendidikan/perpustakaan-digital/_lib/layoutTabs.tsx b/src/app/admin/(dashboard)/pendidikan/perpustakaan-digital/_lib/layoutTabs.tsx index b3ead298..40848d2f 100644 --- a/src/app/admin/(dashboard)/pendidikan/perpustakaan-digital/_lib/layoutTabs.tsx +++ b/src/app/admin/(dashboard)/pendidikan/perpustakaan-digital/_lib/layoutTabs.tsx @@ -4,7 +4,7 @@ import colors from '@/con/colors'; import { ScrollArea, Stack, Tabs, TabsList, TabsPanel, TabsTab, Title, Tooltip } from '@mantine/core'; import { usePathname, useRouter } from 'next/navigation'; import React, { useEffect, useState } from 'react'; -import { IconBook2, IconCategory } from '@tabler/icons-react'; +import { IconBook2, IconCategory, IconUser } from '@tabler/icons-react'; function LayoutTabs({ children }: { children: React.ReactNode }) { const router = useRouter(); @@ -25,6 +25,13 @@ function LayoutTabs({ children }: { children: React.ReactNode }) { icon: , tooltip: "Atur kategori untuk buku digital", }, + { + label: "Peminjam", + value: "peminjam", + href: "/admin/pendidikan/perpustakaan-digital/peminjam", + icon: , + tooltip: "Data Peminjam Buku", + }, ]; const currentTab = tabs.find(tab => tab.href === pathname); diff --git a/src/app/admin/(dashboard)/pendidikan/perpustakaan-digital/peminjam/[id]/edit/page.tsx b/src/app/admin/(dashboard)/pendidikan/perpustakaan-digital/peminjam/[id]/edit/page.tsx new file mode 100644 index 00000000..20acc5ec --- /dev/null +++ b/src/app/admin/(dashboard)/pendidikan/perpustakaan-digital/peminjam/[id]/edit/page.tsx @@ -0,0 +1,245 @@ +/* eslint-disable react-hooks/exhaustive-deps */ +'use client' +import EditEditor from '@/app/admin/(dashboard)/_com/editEditor'; +import perpustakaanDigitalState from '@/app/admin/(dashboard)/_state/pendidikan/perpustakaan-digital'; + +import colors from '@/con/colors'; +import { + Box, + Button, + Group, + Paper, + Select, + Stack, + Text, + TextInput, + Title, + Tooltip, +} from '@mantine/core'; +import { DateInput } from '@mantine/dates'; +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'; + +export type Status = "Dipinjam" | "Dikembalikan" | "Terlambat" | "Dibatalkan"; + +function EditPeminjam() { + const stateEdit = useProxy(perpustakaanDigitalState.peminjamanBuku); + const router = useRouter(); + const params = useParams(); + + const [formData, setFormData] = useState<{ + nama: string; + noTelp: string; + alamat: string; + bukuId: string; + buku?: { + id: string; + judul: string; + }; + tanggalPinjam: string; + batasKembali: string; + tanggalKembali: string; + status: Status; + catatan: string; + }>({ + nama: '', + noTelp: '', + alamat: '', + bukuId: '', + tanggalPinjam: '', + batasKembali: '', + tanggalKembali: '', + status: 'Dipinjam', // Default status + catatan: '', + }); + + useEffect(() => { + const loadPeminjam = async () => { + const id = params?.id as string; + if (!id) return; + + try { + const data = await stateEdit.update.load(id); + if (data) { + setFormData((prev) => ({ + ...prev, + nama: data.nama ?? prev.nama, + noTelp: data.noTelp ?? prev.noTelp, + alamat: data.alamat ?? prev.alamat, + bukuId: data.bukuId ?? prev.bukuId, + buku: data.buku ? { + id: data.buku.id, + judul: data.buku.judul + } : undefined, + tanggalPinjam: data.tanggalPinjam ?? prev.tanggalPinjam, + batasKembali: data.batasKembali ?? prev.batasKembali, + tanggalKembali: data.tanggalKembali ?? prev.tanggalKembali, + status: (data.status as Status) ?? prev.status, + catatan: data.catatan ?? prev.catatan, + })); + } + } catch (error) { + console.error("Error loading peminjam:", error); + toast.error("Gagal mengambil data peminjam"); + } + }; + + loadPeminjam(); + }, [params?.id]); + + + const handleChange = (field: string, value: string | Status) => { + setFormData((prev) => ({ ...prev, [field]: value })); + }; + + const handleSubmit = async () => { + try { + stateEdit.update.form = { + ...stateEdit.update.form, + ...formData, + }; + + await stateEdit.update.update(); + toast.success("Peminjam berhasil diperbarui!"); + router.push("/admin/pendidikan/perpustakaan-digital/peminjam"); + } catch (error) { + console.error("Error updating peminjam:", error); + toast.error("Terjadi kesalahan saat memperbarui peminjam"); + } + }; + + return ( + + {/* Header */} + + + + + + Edit Peminjam Buku + + + + {/* Card */} + + + handleChange('nama', e.target.value)} + label={Nama Peminjam} + placeholder="Masukkan nama peminjam" + required + /> + + handleChange('noTelp', e.target.value)} + label={No Telp Peminjam} + placeholder="Masukkan no telp peminjam" + required + /> + + handleChange('alamat', e.target.value)} + label={Alamat Peminjam} + placeholder="Masukkan alamat peminjam" + required + /> + + Buku} + placeholder="Buku" + required + readOnly + /> + + { + perpustakaanDigitalState.peminjamanBuku.update.form.tanggalPinjam = + date ? new Date(date).toISOString() : ''; + }} + label={Tanggal Pinjam} + placeholder="Masukkan tanggal pinjam" + required + /> + + { + perpustakaanDigitalState.peminjamanBuku.update.form.tanggalKembali = + date ? new Date(date).toISOString() : ''; + }} + label={Tanggal Kembali} + placeholder="Masukkan tanggal kembali" + required + /> + + { + perpustakaanDigitalState.peminjamanBuku.update.form.batasKembali = + date ? new Date(date).toISOString() : ''; + }} + label={Batas Kembali} + placeholder="Masukkan batas kembali" + required + /> + +