diff --git a/resources/js/Layouts/MainLayout.jsx b/resources/js/Layouts/MainLayout.jsx
new file mode 100644
index 0000000..88db98d
--- /dev/null
+++ b/resources/js/Layouts/MainLayout.jsx
@@ -0,0 +1,81 @@
+import { Link, usePage } from '@inertiajs/react';
+import { useState } from 'react';
+
+export default function MainLayout({ children }) {
+ const [sidebarOpen, setSidebarOpen] = useState(true);
+ const { url } = usePage();
+
+ const menus = [
+ { name: 'Dashboard', href: '/', icon: '🏠' },
+ { name: 'Buku', href: '/buku', icon: '📚' },
+ { name: 'Kategori', href: '/kategori', icon: '🏷️' },
+ { name: 'Anggota', href: '/anggota', icon: '👥' },
+ { name: 'Peminjaman', href: '/peminjaman', icon: '📋' },
+ ];
+
+ return (
+
+ {/* Sidebar */}
+
+
+ {/* Main Content */}
+
+ {/* Header */}
+
+ Sistem Perpustakaanku
+ 📅 {new Date().toLocaleDateString('id-ID', {
+ weekday: 'long', year: 'numeric', month: 'long', day: 'numeric'
+ })}
+
+
+ {/* Flash Message */}
+
+
+ {/* Content */}
+
+ {children}
+
+
+
+ );
+}
+
+function FlashMessage() {
+ const { flash } = usePage().props;
+ if (!flash?.success) return null;
+
+ return (
+
+ ✅
+ {flash.success}
+
+ );
+}
\ No newline at end of file
diff --git a/resources/js/Pages/Anggota/Create.jsx b/resources/js/Pages/Anggota/Create.jsx
new file mode 100644
index 0000000..315dcc1
--- /dev/null
+++ b/resources/js/Pages/Anggota/Create.jsx
@@ -0,0 +1,158 @@
+import MainLayout from "@/Layouts/MainLayout";
+import { Head, useForm, Link } from "@inertiajs/react";
+
+export default function AnggotaCreate() {
+ const { data, setData, post, processing, errors } = useForm({
+ nama: "",
+ email: "",
+ no_telepon: "",
+ alamat: "",
+ tanggal_bergabung: "",
+ status: "aktif",
+ });
+
+ const handleSubmit = (e) => {
+ e.preventDefault();
+ post("/anggota");
+ };
+
+ return (
+
+
+
+
+
+
+ ← Kembali
+
+ /
+ Tambah Anggota
+
+
+
+
+ 👥 Tambah Anggota Baru
+
+
+
+
+
+
+ );
+}
+
+function FormField({ label, error, children }) {
+ return (
+
+
+ {children}
+ {error &&
{error}
}
+
+ );
+}
diff --git a/resources/js/Pages/Anggota/Edit.jsx b/resources/js/Pages/Anggota/Edit.jsx
new file mode 100644
index 0000000..ab5d731
--- /dev/null
+++ b/resources/js/Pages/Anggota/Edit.jsx
@@ -0,0 +1,154 @@
+import MainLayout from "@/Layouts/MainLayout";
+import { Head, useForm, Link } from "@inertiajs/react";
+
+export default function AnggotaEdit({ anggota }) {
+ const { data, setData, put, processing, errors } = useForm({
+ nama: anggota.nama,
+ email: anggota.email,
+ no_telepon: anggota.no_telepon ?? "",
+ alamat: anggota.alamat ?? "",
+ tanggal_bergabung: anggota.tanggal_bergabung,
+ status: anggota.status,
+ });
+
+ const handleSubmit = (e) => {
+ e.preventDefault();
+ put(`/anggota/${anggota.id}`);
+ };
+
+ return (
+
+
+
+
+
+
+ ← Kembali
+
+ /
+ Edit Anggota
+
+
+
+
+
+ );
+}
+
+function FormField({ label, error, children }) {
+ return (
+
+
+ {children}
+ {error &&
{error}
}
+
+ );
+}
diff --git a/resources/js/Pages/Anggota/Index.jsx b/resources/js/Pages/Anggota/Index.jsx
new file mode 100644
index 0000000..c7c009e
--- /dev/null
+++ b/resources/js/Pages/Anggota/Index.jsx
@@ -0,0 +1,142 @@
+import MainLayout from "@/Layouts/MainLayout";
+import { Head, Link, router } from "@inertiajs/react";
+
+export default function AnggotaIndex({ anggotas }) {
+ const handleDelete = (id) => {
+ if (confirm("Yakin ingin menghapus anggota ini?")) {
+ router.delete(`/anggota/${id}`);
+ }
+ };
+
+ return (
+
+
+
+
+
+
+ 👥 Data Anggota
+
+
+ + Tambah Anggota
+
+
+
+
+
+
+
+
+ |
+ No
+ |
+
+ Nama
+ |
+
+ Email
+ |
+
+ No Telepon
+ |
+
+ Tgl Bergabung
+ |
+
+ Status
+ |
+
+ Aksi
+ |
+
+
+
+ {anggotas.data.map((anggota, index) => (
+
+ |
+ {(anggotas.current_page - 1) *
+ anggotas.per_page +
+ index +
+ 1}
+ |
+
+ {anggota.nama}
+ |
+
+ {anggota.email}
+ |
+
+ {anggota.no_telepon ?? "-"}
+ |
+
+ {anggota.tanggal_bergabung}
+ |
+
+
+ {anggota.status}
+
+ |
+
+
+
+ Edit
+
+
+
+ |
+
+ ))}
+
+
+
+
+ {/* Pagination */}
+
+
+ Menampilkan {anggotas.from} - {anggotas.to} dari{" "}
+ {anggotas.total} data
+
+
+ {anggotas.links.map((link, i) => (
+
+ ))}
+
+
+
+
+
+ );
+}
diff --git a/resources/js/Pages/Buku/Create.jsx b/resources/js/Pages/Buku/Create.jsx
new file mode 100644
index 0000000..fa588d2
--- /dev/null
+++ b/resources/js/Pages/Buku/Create.jsx
@@ -0,0 +1,188 @@
+import MainLayout from "@/Layouts/MainLayout";
+import { Head, useForm, Link } from "@inertiajs/react";
+
+export default function BukuCreate({ kategoris }) {
+ const { data, setData, post, processing, errors } = useForm({
+ kategori_id: "",
+ judul: "",
+ pengarang: "",
+ penerbit: "",
+ tahun_terbit: "",
+ isbn: "",
+ stok: 0,
+ sinopsis: "",
+ });
+
+ const handleSubmit = (e) => {
+ e.preventDefault();
+ post("/buku");
+ };
+
+ return (
+
+
+
+
+
+
+ ← Kembali
+
+ /
+ Tambah Buku
+
+
+
+
+
+ );
+}
+
+function FormField({ label, error, children }) {
+ return (
+
+
+ {children}
+ {error &&
{error}
}
+
+ );
+}
diff --git a/resources/js/Pages/Buku/Edit.jsx b/resources/js/Pages/Buku/Edit.jsx
new file mode 100644
index 0000000..9451316
--- /dev/null
+++ b/resources/js/Pages/Buku/Edit.jsx
@@ -0,0 +1,182 @@
+import MainLayout from "@/Layouts/MainLayout";
+import { Head, useForm, Link } from "@inertiajs/react";
+
+export default function BukuEdit({ buku, kategoris }) {
+ const { data, setData, put, processing, errors } = useForm({
+ kategori_id: buku.kategori_id,
+ judul: buku.judul,
+ pengarang: buku.pengarang,
+ penerbit: buku.penerbit,
+ tahun_terbit: buku.tahun_terbit,
+ isbn: buku.isbn,
+ stok: buku.stok,
+ sinopsis: buku.sinopsis ?? "",
+ });
+
+ const handleSubmit = (e) => {
+ e.preventDefault();
+ put(`/buku/${buku.id}`);
+ };
+
+ return (
+
+
+
+
+
+
+ ← Kembali
+
+ /
+ Edit Buku
+
+
+
+
+
+ );
+}
+
+function FormField({ label, error, children }) {
+ return (
+
+
+ {children}
+ {error &&
{error}
}
+
+ );
+}
diff --git a/resources/js/Pages/Buku/Index.jsx b/resources/js/Pages/Buku/Index.jsx
new file mode 100644
index 0000000..07b688c
--- /dev/null
+++ b/resources/js/Pages/Buku/Index.jsx
@@ -0,0 +1,142 @@
+import MainLayout from "@/Layouts/MainLayout";
+import { Head, Link, router } from "@inertiajs/react";
+
+export default function BukuIndex({ bukus }) {
+ const handleDelete = (id) => {
+ if (confirm("Yakin ingin menghapus buku ini?")) {
+ router.delete(`/buku/${id}`);
+ }
+ };
+
+ return (
+
+
+
+
+
+
+ 📚 Data Buku
+
+
+ + Tambah Buku
+
+
+
+
+
+
+
+
+ |
+ No
+ |
+
+ Judul
+ |
+
+ Pengarang
+ |
+
+ Kategori
+ |
+
+ Stok
+ |
+
+ Tersedia
+ |
+
+ Aksi
+ |
+
+
+
+ {bukus.data.map((buku, index) => (
+
+ |
+ {(bukus.current_page - 1) *
+ bukus.per_page +
+ index +
+ 1}
+ |
+
+ {buku.judul}
+ |
+
+ {buku.pengarang}
+ |
+
+ {buku.kategori?.nama_kategori}
+ |
+
+ {buku.stok}
+ |
+
+ 0
+ ? "bg-green-100 text-green-700"
+ : "bg-red-100 text-red-700"
+ }`}
+ >
+ {buku.stok_tersedia}
+
+ |
+
+
+
+ Edit
+
+
+
+ |
+
+ ))}
+
+
+
+
+ {/* Pagination */}
+
+
+ Menampilkan {bukus.from} - {bukus.to} dari{" "}
+ {bukus.total} data
+
+
+ {bukus.links.map((link, i) => (
+
+ ))}
+
+
+
+
+
+ );
+}
diff --git a/resources/js/Pages/Dashboard.jsx b/resources/js/Pages/Dashboard.jsx
new file mode 100644
index 0000000..4799f0b
--- /dev/null
+++ b/resources/js/Pages/Dashboard.jsx
@@ -0,0 +1,133 @@
+import MainLayout from "@/Layouts/MainLayout";
+import { Head, Link } from "@inertiajs/react";
+
+export default function Dashboard({ stats, peminjaman_terbaru }) {
+ const statCards = [
+ {
+ label: "Total Buku",
+ value: stats.total_buku,
+ icon: "📚",
+ color: "bg-blue-500",
+ },
+ {
+ label: "Anggota Aktif",
+ value: stats.total_anggota,
+ icon: "👥",
+ color: "bg-green-500",
+ },
+ {
+ label: "Sedang Dipinjam",
+ value: stats.total_peminjaman,
+ icon: "📋",
+ color: "bg-yellow-500",
+ },
+ {
+ label: "Terlambat Kembali",
+ value: stats.total_terlambat,
+ icon: "⚠️",
+ color: "bg-red-500",
+ },
+ ];
+
+ return (
+
+
+
+
+
Dashboard
+
+ {/* Stat Cards */}
+
+ {statCards.map((card) => (
+
+
+ {card.icon}
+
+
+
+ {card.label}
+
+
+ {card.value}
+
+
+
+ ))}
+
+
+ {/* Tabel Peminjaman Terbaru */}
+
+
+
+ Peminjaman Terbaru
+
+
+ Lihat Semua →
+
+
+
+
+
+
+ |
+ Anggota
+ |
+
+ Buku
+ |
+
+ Tgl Pinjam
+ |
+
+ Status
+ |
+
+
+
+ {peminjaman_terbaru.map((p) => (
+
+ |
+ {p.anggota?.nama}
+ |
+
+ {p.buku?.judul}
+ |
+
+ {p.tanggal_pinjam}
+ |
+
+
+ |
+
+ ))}
+
+
+
+
+
+
+ );
+}
+
+function StatusBadge({ status }) {
+ const colors = {
+ dipinjam: "bg-yellow-100 text-yellow-800",
+ dikembalikan: "bg-green-100 text-green-800",
+ terlambat: "bg-red-100 text-red-800",
+ };
+ return (
+
+ {status}
+
+ );
+}
diff --git a/resources/js/Pages/Kategori/Create.jsx b/resources/js/Pages/Kategori/Create.jsx
new file mode 100644
index 0000000..eda007c
--- /dev/null
+++ b/resources/js/Pages/Kategori/Create.jsx
@@ -0,0 +1,98 @@
+import MainLayout from "@/Layouts/MainLayout";
+import { Head, useForm, Link } from "@inertiajs/react";
+
+export default function KategoriCreate() {
+ const { data, setData, post, processing, errors } = useForm({
+ nama_kategori: "",
+ deskripsi: "",
+ });
+
+ const handleSubmit = (e) => {
+ e.preventDefault();
+ post("/kategori");
+ };
+
+ return (
+
+
+
+
+
+
+ ← Kembali
+
+ /
+ Tambah Kategori
+
+
+
+
+ 🏷️ Tambah Kategori Baru
+
+
+
+
+
+
+ );
+}
+
+function FormField({ label, error, children }) {
+ return (
+
+
+ {children}
+ {error &&
{error}
}
+
+ );
+}
diff --git a/resources/js/Pages/Kategori/Edit.jsx b/resources/js/Pages/Kategori/Edit.jsx
new file mode 100644
index 0000000..7b5fd89
--- /dev/null
+++ b/resources/js/Pages/Kategori/Edit.jsx
@@ -0,0 +1,96 @@
+import MainLayout from "@/Layouts/MainLayout";
+import { Head, useForm, Link } from "@inertiajs/react";
+
+export default function KategoriEdit({ kategori }) {
+ const { data, setData, put, processing, errors } = useForm({
+ nama_kategori: kategori.nama_kategori,
+ deskripsi: kategori.deskripsi ?? "",
+ });
+
+ const handleSubmit = (e) => {
+ e.preventDefault();
+ put(`/kategori/${kategori.id}`);
+ };
+
+ return (
+
+
+
+
+
+
+ ← Kembali
+
+ /
+ Edit Kategori
+
+
+
+
+ ✏️ Edit Kategori
+
+
+
+
+
+
+ );
+}
+
+function FormField({ label, error, children }) {
+ return (
+
+
+ {children}
+ {error &&
{error}
}
+
+ );
+}
diff --git a/resources/js/Pages/Kategori/Index.jsx b/resources/js/Pages/Kategori/Index.jsx
new file mode 100644
index 0000000..6818907
--- /dev/null
+++ b/resources/js/Pages/Kategori/Index.jsx
@@ -0,0 +1,122 @@
+import MainLayout from "@/Layouts/MainLayout";
+import { Head, Link, router } from "@inertiajs/react";
+
+export default function KategoriIndex({ kategoris }) {
+ const handleDelete = (id) => {
+ if (confirm("Yakin ingin menghapus kategori ini?")) {
+ router.delete(`/kategori/${id}`);
+ }
+ };
+
+ return (
+
+
+
+
+
+
+ 🏷️ Data Kategori
+
+
+ + Tambah Kategori
+
+
+
+
+
+
+
+ |
+ No
+ |
+
+ Nama Kategori
+ |
+
+ Deskripsi
+ |
+
+ Jumlah Buku
+ |
+
+ Aksi
+ |
+
+
+
+ {kategoris.data.map((kategori, index) => (
+
+ |
+ {(kategoris.current_page - 1) *
+ kategoris.per_page +
+ index +
+ 1}
+ |
+
+ {kategori.nama_kategori}
+ |
+
+ {kategori.deskripsi ?? "-"}
+ |
+
+
+ {kategori.bukus_count ?? 0} Buku
+
+ |
+
+
+
+ Edit
+
+
+
+ |
+
+ ))}
+
+
+
+ {/* Pagination */}
+
+
+ Menampilkan {kategoris.from} - {kategoris.to} dari{" "}
+ {kategoris.total} data
+
+
+ {kategoris.links.map((link, i) => (
+
+ ))}
+
+
+
+
+
+ );
+}
diff --git a/resources/js/Pages/Peminjaman/Create.jsx b/resources/js/Pages/Peminjaman/Create.jsx
new file mode 100644
index 0000000..08df3a7
--- /dev/null
+++ b/resources/js/Pages/Peminjaman/Create.jsx
@@ -0,0 +1,156 @@
+import MainLayout from "@/Layouts/MainLayout";
+import { Head, useForm, Link } from "@inertiajs/react";
+
+export default function PeminjamanCreate({ anggotas, bukus }) {
+ const { data, setData, post, processing, errors } = useForm({
+ anggota_id: "",
+ buku_id: "",
+ tanggal_pinjam: "",
+ tanggal_kembali_rencana: "",
+ catatan: "",
+ });
+
+ const handleSubmit = (e) => {
+ e.preventDefault();
+ post("/peminjaman");
+ };
+
+ return (
+
+
+
+
+
+
+ ← Kembali
+
+ /
+ Tambah Peminjaman
+
+
+
+
+
+ );
+}
+
+function FormField({ label, error, children }) {
+ return (
+
+
+ {children}
+ {error &&
{error}
}
+
+ );
+}
diff --git a/resources/js/Pages/Peminjaman/Index.jsx b/resources/js/Pages/Peminjaman/Index.jsx
new file mode 100644
index 0000000..a878b8b
--- /dev/null
+++ b/resources/js/Pages/Peminjaman/Index.jsx
@@ -0,0 +1,161 @@
+import MainLayout from "@/Layouts/MainLayout";
+import { Head, Link, router } from "@inertiajs/react";
+
+export default function PeminjamanIndex({ peminjamans }) {
+ const handleKembalikan = (id) => {
+ if (confirm("Konfirmasi pengembalian buku ini?")) {
+ router.post(`/peminjaman/${id}/kembalikan`);
+ }
+ };
+
+ const handleDelete = (id) => {
+ if (confirm("Yakin ingin menghapus data ini?")) {
+ router.delete(`/peminjaman/${id}`);
+ }
+ };
+
+ const statusColor = {
+ dipinjam: "bg-yellow-100 text-yellow-800",
+ dikembalikan: "bg-green-100 text-green-800",
+ terlambat: "bg-red-100 text-red-800",
+ };
+
+ return (
+
+
+
+
+
+
+ 📋 Data Peminjaman
+
+
+ + Tambah Peminjaman
+
+
+
+
+
+
+
+
+ |
+ No
+ |
+
+ Anggota
+ |
+
+ Buku
+ |
+
+ Tgl Pinjam
+ |
+
+ Tgl Kembali
+ |
+
+ Status
+ |
+
+ Denda
+ |
+
+ Aksi
+ |
+
+
+
+ {peminjamans.data.map((p, index) => (
+
+ |
+ {(peminjamans.current_page - 1) *
+ peminjamans.per_page +
+ index +
+ 1}
+ |
+
+ {p.anggota?.nama}
+ |
+
+ {p.buku?.judul}
+ |
+
+ {p.tanggal_pinjam}
+ |
+
+ {p.tanggal_kembali_rencana}
+ |
+
+
+ {p.status}
+
+ |
+
+ {p.denda > 0
+ ? `Rp ${p.denda.toLocaleString("id-ID")}`
+ : "-"}
+ |
+
+
+ {p.status === "dipinjam" && (
+
+ )}
+
+
+ |
+
+ ))}
+
+
+
+
+ {/* Pagination */}
+
+
+ Menampilkan {peminjamans.from} - {peminjamans.to}{" "}
+ dari {peminjamans.total} data
+
+
+ {peminjamans.links.map((link, i) => (
+
+ ))}
+
+
+
+
+
+ );
+}