diff --git a/app/Http/Controllers/Admin/BookController.php b/app/Http/Controllers/Admin/BookController.php
new file mode 100644
index 0000000..685e06f
--- /dev/null
+++ b/app/Http/Controllers/Admin/BookController.php
@@ -0,0 +1,39 @@
+only(['search']);
+ $semuaBuku = DummyDataService::getKatalogBuku($filters);
+
+ // Memisahkan buku menjadi dua koleksi: online dan offline
+ [$bukuOnline, $bukuOffline] = $semuaBuku->partition(function ($buku) {
+ $tipe = $buku['tipe_akses'];
+ return $tipe === 'online' || (is_array($tipe) && in_array('online', $tipe));
+ });
+
+ return view('admin.buku.index', [
+ 'pageTitle' => 'Manajemen Buku',
+ 'bukuOnline' => $bukuOnline,
+ 'bukuOffline' => $bukuOffline,
+ 'input' => $filters
+ ]);
+ }
+
+ /**
+ * Menampilkan halaman form untuk menambah buku baru.
+ */
+ public function create()
+ {
+ return view('admin.buku.create', [
+ 'pageTitle' => 'Tambah Buku Baru'
+ ]);
+ }
+}
\ No newline at end of file
diff --git a/app/Http/Controllers/Admin/DashboardController.php b/app/Http/Controllers/Admin/DashboardController.php
new file mode 100644
index 0000000..fa10625
--- /dev/null
+++ b/app/Http/Controllers/Admin/DashboardController.php
@@ -0,0 +1,23 @@
+ 'Dashboard Admin',
+ 'user' => auth()->user(),
+ 'greeting' => 'Selamat Datang',
+ 'stats' => DummyDataService::getAdminDashboardStats(),
+ 'statistikBulanan' => DummyDataService::getStatistikPeminjamanAdmin(),
+ 'komposisiBuku' => DummyDataService::getKomposisiBukuAdmin(),
+ 'pengumuman' => DummyDataService::getPengumuman(),
+ 'aktivitasTerakhir' => DummyDataService::getAktivitasTerakhir(),
+ ]);
+ }
+}
diff --git a/app/Http/Controllers/Admin/PengumumanController.php b/app/Http/Controllers/Admin/PengumumanController.php
new file mode 100644
index 0000000..8ceca56
--- /dev/null
+++ b/app/Http/Controllers/Admin/PengumumanController.php
@@ -0,0 +1,19 @@
+ 'Manajemen Pengumuman',
+ 'semuaPengumuman' => $semuaPengumuman,
+ ]);
+ }
+}
\ No newline at end of file
diff --git a/app/Http/Controllers/Admin/UserController.php b/app/Http/Controllers/Admin/UserController.php
new file mode 100644
index 0000000..944eb9c
--- /dev/null
+++ b/app/Http/Controllers/Admin/UserController.php
@@ -0,0 +1,15 @@
+ 'Manajemen Pengguna',
+ 'semuaSiswa' => $semuaSiswa
+ ]);
+ }
+}
\ No newline at end of file
diff --git a/app/Http/Controllers/Auth/RegisteredUserController.php b/app/Http/Controllers/Auth/RegisteredUserController.php
index 0739e2e..3a959cc 100644
--- a/app/Http/Controllers/Auth/RegisteredUserController.php
+++ b/app/Http/Controllers/Auth/RegisteredUserController.php
@@ -15,7 +15,7 @@
class RegisteredUserController extends Controller
{
/**
- * Display the registration view.
+ * Menampilkan halaman registrasi.
*/
public function create(): View
{
@@ -23,28 +23,35 @@ public function create(): View
}
/**
- * Handle an incoming registration request.
- *
- * @throws \Illuminate\Validation\ValidationException
+ * Menangani permintaan registrasi yang masuk.
*/
public function store(Request $request): RedirectResponse
{
$request->validate([
'name' => ['required', 'string', 'max:255'],
- 'email' => ['required', 'string', 'lowercase', 'email', 'max:255', 'unique:'.User::class],
+ 'email' => ['required', 'string', 'lowercase', 'email', 'max:255'],
'password' => ['required', 'confirmed', Rules\Password::defaults()],
+ 'role' => ['required', 'in:penjaga perpus,siswa'],
]);
- $user = User::create([
+ $user = new User();
+ $user->forceFill([
+ 'id' => rand(100, 999),
+ 'nama_lengkap' => $request->name,
'name' => $request->name,
'email' => $request->email,
- 'password' => Hash::make($request->password),
+ 'password' => $request->password,
+ 'role' => $request->role,
]);
event(new Registered($user));
Auth::login($user);
- return redirect(route('dashboard', absolute: false));
+ if ($user->role === 'penjaga perpus') {
+ return redirect()->route('admin.dashboard');
+ } else {
+ return redirect()->route('dashboard');
+ }
}
-}
+}
\ No newline at end of file
diff --git a/app/Http/Controllers/RiwayatController.php b/app/Http/Controllers/RiwayatController.php
new file mode 100644
index 0000000..472cb4e
--- /dev/null
+++ b/app/Http/Controllers/RiwayatController.php
@@ -0,0 +1,35 @@
+ 'Riwayat Peminjaman Offline',
+ 'riwayatOffline' => $riwayatOffline,
+ ]);
+ }
+
+ /**
+ * Menampilkan halaman riwayat baca online.
+ */
+ public function onlineIndex()
+ {
+ $riwayatOnline = DummyDataService::getRiwayatOnline();
+
+ return view('riwayat.online', [
+ 'pageTitle' => 'Riwayat Baca Online',
+ 'riwayatOnline' => $riwayatOnline,
+ ]);
+ }
+}
\ No newline at end of file
diff --git a/app/Http/Middleware/CheckRole.php b/app/Http/Middleware/CheckRole.php
new file mode 100644
index 0000000..9db1672
--- /dev/null
+++ b/app/Http/Middleware/CheckRole.php
@@ -0,0 +1,35 @@
+role == $role) {
+ return $next($request);
+ }
+ }
+
+ // Jika role tidak cocok, tolak akses
+ abort(403, 'AKSES DITOLAK: Anda tidak memiliki hak untuk mengakses halaman ini.');
+ }
+}
\ No newline at end of file
diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php
index e52e3fa..ec5adfd 100644
--- a/app/Providers/AppServiceProvider.php
+++ b/app/Providers/AppServiceProvider.php
@@ -2,8 +2,10 @@
namespace App\Providers;
+use App\Services\DummyDataService;
use Illuminate\Support\Facades\URL;
use Illuminate\Support\ServiceProvider;
+use Illuminate\Support\Facades\View;
class AppServiceProvider extends ServiceProvider
{
@@ -19,9 +21,16 @@ public function register(): void
* Bootstrap any application services.
*/
public function boot(): void
-{
- if ($this->app->environment('production')) {
- URL::forceScheme('https');
+ {
+ if ($this->app->environment('production')) {
+ URL::forceScheme('https');
+ }
+
+ View::composer('*', function ($view) {
+ $notifikasi = collect(DummyDataService::getNotifikasi());
+ $unreadNotificationsCount = $notifikasi->where('read', false)->count();
+
+ $view->with(compact('notifikasi', 'unreadNotificationsCount'));
+ });
}
}
-}
diff --git a/app/Services/DummyDataService.php b/app/Services/DummyDataService.php
index cd18a84..ffce8c1 100644
--- a/app/Services/DummyDataService.php
+++ b/app/Services/DummyDataService.php
@@ -25,7 +25,7 @@ public static function getAllSiswa(): array
'email' => 'budi.santoso@smkn1perpus.sch.id',
'nomor_hp' => '081122334455',
'password' => 'password',
- 'role' => 'guru',
+ 'role' => 'penjaga perpus',
],
[
'id' => 3,
@@ -56,11 +56,59 @@ public static function getAllSiswa(): array
'email' => 'rina.marlina@smkn1perpus.sch.id',
'nomor_hp' => '081223344556',
'password' => 'password',
- 'role' => 'guru',
+ 'role' => 'penjaga perpus',
],
];
}
+ public static function getAdminDashboardStats(): array
+ {
+ $allBooks = self::getAllBooks();
+ $allUsers = self::getAllSiswa();
+ $bukuDipinjam = $allBooks->filter(fn($buku) => $buku['status'] === 'Dipinjam')->count();
+ return [
+ ['label' => 'Total Buku', 'value' => $allBooks->count(), 'icon' => 'bi-journal-bookmark-fill', 'color' => 'primary'],
+ ['label' => 'Total Anggota', 'value' => count($allUsers), 'icon' => 'bi-people-fill', 'color' => 'success'],
+ ['label' => 'Buku Dipinjam', 'value' => $bukuDipinjam, 'icon' => 'bi-arrow-up-right-circle-fill', 'color' => 'warning'],
+ ['label' => 'Denda Menunggu', 'value' => 0, 'icon' => 'bi-cash-coin', 'color' => 'danger'],
+ ];
+ }
+ /**
+ * Data untuk bar chart di dashboard admin (total peminjaman per bulan).
+ */
+ public static function getStatistikPeminjamanAdmin(): array
+ {
+ return [
+ 'labels' => ['Jan', 'Feb', 'Mar', 'Apr', 'Mei', 'Jun', 'Jul'],
+ 'data' => [65, 59, 80, 81, 56, 55, 70],
+ ];
+ }
+
+ /**
+ * Data untuk donut chart di dashboard admin (komposisi buku).
+ */
+ public static function getKomposisiBukuAdmin(): array
+ {
+ $allBooks = self::getAllBooks();
+ return [
+ 'tersedia' => $allBooks->where('status', 'Tersedia')->count(),
+ 'dipinjam' => $allBooks->where('status', 'Dipinjam')->count(),
+ ];
+ }
+
+ /**
+ * Data untuk tabel aktivitas terakhir di dashboard admin.
+ */
+ public static function getAktivitasTerakhir(): array
+ {
+ return [
+ ['nama' => 'Silvi Rahmawati', 'judul_buku' => 'Perahu Kertas', 'tipe' => 'Peminjaman', 'waktu' => '5 menit yang lalu', 'status' => 'Dipinjam'],
+ ['nama' => 'Andi Pratama', 'judul_buku' => 'The Last Spell Breather', 'tipe' => 'Pengembalian', 'waktu' => '1 jam yang lalu', 'status' => 'Dikembalikan'],
+ ['nama' => 'Siti Nurhaliza', 'judul_buku' => 'Ayah', 'tipe' => 'Baca Online', 'waktu' => '3 jam yang lalu', 'status' => 'Selesai'],
+ ['nama' => 'Rina Marlina', 'judul_buku' => 'Modul Ajar IPAS', 'tipe' => 'Peminjaman', 'waktu' => 'Kemarin', 'status' => 'Dipinjam'],
+ ];
+ }
+
/**
* Data untuk 4 kartu statistik
*/
@@ -133,7 +181,7 @@ public static function getStatistikBulanan(): array
* Master list untuk semua buku
* @return \Illuminate\Support\Collection
*/
- private static function getAllBooks()
+ public static function getAllBooks()
{
return collect([
[
@@ -145,8 +193,8 @@ private static function getAllBooks()
'tahun' => 2022,
'status' => 'Tersedia',
'is_new' => true,
- 'tipe_akses' => ['online','offline'],
- 'file_pdf'=>'ipas.pdf',
+ 'tipe_akses' => ['online', 'offline'],
+ 'file_pdf' => 'ipas.pdf',
'progress' => 75,
'sisa_hari' => 14,
'user_id' => 1,
@@ -213,8 +261,8 @@ private static function getAllBooks()
'tahun' => 2023,
'status' => 'Tersedia',
'is_new' => true,
- 'tipe_akses' => ['online','offline'],
- 'file_pdf'=>'mtk.pdf',
+ 'tipe_akses' => ['online', 'offline'],
+ 'file_pdf' => 'mtk.pdf',
'sisa_hari' => 7,
'progress' => 40,
'user_id' => [1, 4, 5],
@@ -243,9 +291,9 @@ private static function getAllBooks()
'status' => 'Tersedia',
'is_new' => true,
'tipe_akses' => 'online',
- 'file_pdf' => 'ayah.pdf',
+ 'file_pdf' => 'ayah.pdf',
'progress' => 0,
- 'user_id' => [1, 2, 3],
+ 'user_id' => [1, 2, 3],
],
[
'id' => 9,
@@ -256,8 +304,8 @@ private static function getAllBooks()
'tahun' => 2015,
'status' => 'Tersedia',
'is_new' => true,
- 'tipe_akses' => ['online','offline'],
- 'file_pdf' => 'senja.pdf',
+ 'tipe_akses' => ['online', 'offline'],
+ 'file_pdf' => 'senja.pdf',
'progress' => 0,
'sisa_hari' => 14,
'user_id' => [1, 3],
@@ -274,7 +322,7 @@ private static function getAllBooks()
'tipe_akses' => 'online',
'file_pdf' => 'hijrah.pdf',
'progress' => 0,
- 'user_id' => [2, 3],
+ 'user_id' => [2, 3],
]
]);
}
@@ -377,4 +425,105 @@ public static function getFilterOptions(): array
'penulis' => $buku->pluck('penulis')->unique()->sort()->values(),
];
}
+
+ /**
+ * Data untuk riwayat peminjaman offline.
+ * Setiap item mewakili satu transaksi peminjaman.
+ */
+ public static function getRiwayatOffline(): array
+ {
+ return [
+ [
+ 'id' => 1,
+ 'id_peminjaman' => 'PIN-20240520-001',
+ 'judul_utama' => 'Yuk, Mari Sekolah',
+ 'tanggal_pinjam' => '20/05/2024',
+ 'tanggal_kembali' => '27/05/2024',
+ 'status' => 'Dikembalikan',
+ 'books' => [
+ [
+ 'id' => 1,
+ 'judul' => 'Yuk, Mari Sekolah',
+ 'cover' => 'images/covers/ipas.jpg',
+ 'deskripsi' => 'Buku ini berisi ajakan kepada anak-anak untuk semangat pergi ke sekolah dan menuntut ilmu.',
+ 'kategori' => 'Pendidikan',
+ 'tahun' => 2022,
+ 'keterangan' => 'Buku dikembalikan dalam kondisi baik.'
+ ]
+ ]
+ ],
+ [
+ 'id' => 2,
+ 'id_peminjaman' => 'PIN-20240527-002',
+ 'judul_utama' => 'Perahu Kertas & 1 lainnya',
+ 'tanggal_pinjam' => '27/05/2024',
+ 'tanggal_kembali' => '04/06/2024',
+ 'status' => 'Dipinjam',
+ 'books' => [
+ [
+ 'id' => 8,
+ 'judul' => 'Perahu Kertas',
+ 'cover' => 'images/covers/ayah.png',
+ 'deskripsi' => 'Cerita penggambaran pasang surut hubungan dua anak manusia, yaitu Kugy dan Keenan.',
+ 'kategori' => 'Fiksi',
+ 'tahun' => 2022,
+ 'keterangan' => null,
+ ],
+ [
+ 'id' => 7,
+ 'judul' => 'The Last Spell Breather',
+ 'cover' => 'images/covers/thelastspellbreather.jpg',
+ 'deskripsi' => 'Sebuah petualangan fantasi di dunia sihir yang menakjubkan.',
+ 'kategori' => 'Fantasi',
+ 'tahun' => 2024,
+ 'keterangan' => null,
+ ]
+ ]
+ ],
+ ];
+ }
+
+ /**
+ * Data untuk riwayat baca buku online.
+ */
+ public static function getRiwayatOnline(): array
+ {
+ return [
+ [
+ 'id' => 1,
+ 'id_baca' => 'BCO-20240527-002',
+ 'judul_buku' => 'Ayah',
+ 'tanggal_akses' => '27/05/2024',
+ 'status' => 'Selesai',
+ 'books' => [
+ [
+ 'id' => 9,
+ 'judul' => 'Ayah',
+ 'cover' => 'images/covers/ayah.png',
+ 'deskripsi' => 'Novel yang mengisahkan perjuangan dan kasih sayang seorang ayah.',
+ 'kategori' => 'Novel',
+ 'tahun' => 2015,
+ 'keterangan' => null
+ ]
+ ]
+ ],
+ ];
+ }
+
+ /**
+ * Data untuk fitur notifikasi.
+ */
+ public static function getNotifikasi(): array
+ {
+ return [
+ ['icon' => 'bi-check2-circle', 'color' => 'success', 'title' => 'Buku "Perahu Kertas" berhasil dipinjam.', 'time' => '5 menit yang lalu', 'read' => false],
+ ['icon' => 'bi-exclamation-triangle', 'color' => 'danger', 'title' => 'Buku "Sosiologi" akan jatuh tempo besok!', 'time' => '1 jam yang lalu', 'read' => false],
+ ['icon' => 'bi-book-half', 'color' => 'info', 'title' => '5 buku baru ditambahkan ke kategori Fiksi.', 'time' => '3 jam yang lalu', 'read' => false],
+ ['icon' => 'bi-arrow-repeat', 'color' => 'primary', 'title' => 'Peminjaman buku "Modul IPAS" telah diperpanjang.', 'time' => 'Kemarin', 'read' => true],
+ ['icon' => 'bi-check-circle', 'color' => 'success', 'title' => 'Anda telah mengembalikan buku "The Last Spell Breather".', 'time' => 'Kemarin', 'read' => true],
+ ['icon' => 'bi-info-circle', 'color' => 'info', 'title' => 'Perpustakaan akan mengadakan acara baca buku bersama.', 'time' => '2 hari yang lalu', 'read' => true],
+ ['icon' => 'bi-person-check', 'color' => 'primary', 'title' => 'Profil Anda berhasil diperbarui.', 'time' => '2 hari yang lalu', 'read' => true],
+ ['icon' => 'bi-exclamation-triangle', 'color' => 'warning', 'title' => 'Sistem akan maintenance pada pukul 23:00.', 'time' => '3 hari yang lalu', 'read' => true],
+ ];
+ }
}
diff --git a/bootstrap/app.php b/bootstrap/app.php
index a240aa5..9bc6f90 100644
--- a/bootstrap/app.php
+++ b/bootstrap/app.php
@@ -14,10 +14,13 @@
$middleware->web(append: [
\App\Http\Middleware\AuthenticateFromSessionData::class,
]);
+ $middleware->alias([
+ 'role' => \App\Http\Middleware\CheckRole::class,
+ ]);
})
->withProviders([
App\Providers\AuthServiceProvider::class,
])
->withExceptions(function (Exceptions $exceptions): void {
//
- })->create();
+ })->create();
\ No newline at end of file
diff --git a/resources/js/app.js b/resources/js/app.js
index 396c712..aa5c306 100644
--- a/resources/js/app.js
+++ b/resources/js/app.js
@@ -6,4 +6,6 @@ window.bootstrap = bootstrap;
import Alpine from 'alpinejs';
window.Alpine = Alpine;
-Alpine.start();
\ No newline at end of file
+document.addEventListener('DOMContentLoaded', () => {
+ Alpine.start();
+});
\ No newline at end of file
diff --git a/resources/scss/_variables.scss b/resources/scss/_variables.scss
index d46ee7c..78b2707 100644
--- a/resources/scss/_variables.scss
+++ b/resources/scss/_variables.scss
@@ -1,3 +1,5 @@
+@use "sass:color";
+
// ===================================
// VARIABLES & MAPS
// ===================================
@@ -5,17 +7,17 @@
// Theme Colors Map
$theme-colors: (
"primary": #435ebe,
- "secondary": mix(#6c757d, #ffffff, 80%),
- "success": mix(#198754, #ffffff, 85%),
- "info": mix(#0dcaf0, #ffffff, 80%),
- "warning": mix(#ffc107, #ffffff, 80%),
- "danger": mix(#dc3545, #ffffff, 80%),
+ "secondary": color.mix(#6c757d, #ffffff, 80%),
+ "success": color.mix(#198754, #ffffff, 85%),
+ "info": color.mix(#0dcaf0, #ffffff, 80%),
+ "warning": color.mix(#ffc107, #ffffff, 80%),
+ "danger": color.mix(#dc3545, #ffffff, 80%),
);
// Gray Colors Map
$grays: (
- "light": #f4f7f8,
- "dark": #4c5053
+ "light": #f4f7f8,
+ "dark": #4c5053,
);
// Spacing & Sizing
@@ -43,7 +45,7 @@ $transition: all 0.3s ease;
}
.alert-#{$color} {
background-color: rgba($value, 0.2);
- color: darken($value, 25%);
+ color: color.adjust($value, $lightness: -25%);
border-color: rgba($value, 0.3);
}
}
@@ -56,7 +58,7 @@ $transition: all 0.3s ease;
box-shadow: $card-box-shadow;
.card-header {
background-color: #fff;
- border-bottom: 1px solid rgba(map-get($grays, "dark"), 0.1);
+ border-bottom: 1px solid rgba(map-get($grays, "dark"), 0.1);
}
}
@@ -78,17 +80,17 @@ $transition: all 0.3s ease;
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.15);
.modal-header,
.modal-footer {
- border-color: rgba(map-get($grays, "dark"), 0.1);
+ border-color: rgba(map-get($grays, "dark"), 0.1);
padding: 1.5rem;
}
.modal-title {
- color: map-get($grays, "dark");
+ color: map-get($grays, "dark");
font-weight: 700;
}
}
}
-.btn{
+.btn {
padding: 0.5rem 1.5rem;
font-weight: 500;
&.btn-sm {
@@ -106,7 +108,8 @@ $transition: all 0.3s ease;
// CUSTOM COMPONENTS
// ===================================
-.icon-circle, .icon-box {
+.icon-circle,
+.icon-box {
display: flex;
align-items: center;
justify-content: center;
@@ -128,26 +131,14 @@ $transition: all 0.3s ease;
}
}
-.book-card {
- transition: $transition;
- box-shadow: $card-box-shadow;
- &:hover {
- transform: translateY(-2px);
- box-shadow: $shadow-md;
- .book-cover {
- transform: scale(1.03);
- }
- }
-}
-
.book-cover {
transition: $transition;
border-radius: $border-radius-sm;
&-container {
background: linear-gradient(
135deg,
- rgba(map-get($grays, "light"), 0.5) 0%,
- rgba(map-get($grays, "light"), 0.8) 100%
+ rgba(map-get($grays, "light"), 0.5) 0%,
+ rgba(map-get($grays, "light"), 0.8) 100%
);
border-radius: $border-radius-sm 0 0 $border-radius-sm;
}
@@ -155,9 +146,9 @@ $transition: all 0.3s ease;
.section-header {
padding-bottom: 15px;
- border-bottom: 2px solid map-get($grays, "light");
+ border-bottom: 2px solid map-get($grays, "light");
h5 {
- color: map-get($grays, "dark");
+ color: map-get($grays, "dark");
font-weight: 700;
}
}
@@ -167,12 +158,12 @@ $transition: all 0.3s ease;
padding: 3rem 1rem;
i {
font-size: 4rem;
- color: map-get($grays, "dark");
+ color: map-get($grays, "dark");
margin-bottom: 1rem;
opacity: 0.25;
}
p {
- color: map-get($grays, "dark");
+ color: map-get($grays, "dark");
opacity: 0.7;
font-size: 1rem;
margin: 0;
@@ -180,12 +171,18 @@ $transition: all 0.3s ease;
}
.btn-primary-soft {
- --bs-btn-color: #{map-get($grays, "dark")};
- --bs-btn-bg: #{map-get($theme-colors, "primary")};
- --bs-btn-border-color: #{map-get($theme-colors, "primary")};
- --bs-btn-hover-color: #{map-get($grays, "dark")};
- --bs-btn-hover-bg: #{darken(map-get($theme-colors, "primary"), 5%)};
- --bs-btn-hover-border-color: #{darken(map-get($theme-colors, "primary"), 7.5%)};
+ --bs-btn-color: #{map-get($grays, "dark")};
+ --bs-btn-bg: #{map-get($theme-colors, "primary")};
+ --bs-btn-border-color: #{map-get($theme-colors, "primary")};
+ --bs-btn-hover-color: #{map-get($grays, "dark")};
+ --bs-btn-hover-bg: #{color.adjust(
+ map-get($theme-colors, "primary"),
+ $lightness: -5%
+ )};
+ --bs-btn-hover-border-color: #{color.adjust(
+ map-get($theme-colors, "primary"),
+ $lightness: -7.5%
+ )};
}
// ===================================
@@ -199,22 +196,26 @@ $transition: all 0.3s ease;
overflow: hidden;
line-height: 1.4;
}
-.line-clamp-2 { -webkit-line-clamp: 2; }
-.line-clamp-3 { -webkit-line-clamp: 3; }
+.line-clamp-2 {
+ -webkit-line-clamp: 2;
+}
+.line-clamp-3 {
+ -webkit-line-clamp: 3;
+}
// Custom Scrollbar
::-webkit-scrollbar {
width: 6px;
}
::-webkit-scrollbar-track {
- background: map-get($grays, "light");
+ background: map-get($grays, "light");
border-radius: 3px;
}
::-webkit-scrollbar-thumb {
- background: rgba(map-get($grays, "dark"), 0.25);
+ background: rgba(map-get($grays, "dark"), 0.25);
border-radius: 3px;
&:hover {
- background: rgba(map-get($grays, "dark"), 0.4);
+ background: rgba(map-get($grays, "dark"), 0.4);
}
}
@@ -225,7 +226,7 @@ $transition: all 0.3s ease;
.card-book-select {
cursor: pointer;
position: relative;
- transition: $transition;
+ transition: $transition;
&:hover {
transform: translateY(-5px);
@@ -234,7 +235,10 @@ $transition: all 0.3s ease;
.card-select-overlay {
position: absolute;
- top: 0; left: 0; right: 0; bottom: 0;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
background-color: rgba(#435ebe, 0.7);
display: flex;
align-items: center;
@@ -242,15 +246,15 @@ $transition: all 0.3s ease;
opacity: 0;
transition: $transition;
border-radius: $border-radius-sm;
-
+
i {
- text-shadow: 0 1px 3px rgba(0,0,0,0.3);
+ text-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
}
}
&.selected {
border: 2px solid map-get($theme-colors, "primary");
-
+
.card-select-overlay {
opacity: 1;
}
@@ -262,7 +266,6 @@ $transition: all 0.3s ease;
}
}
-
// ===================================
// Styling untuk Form Peminjaman
// ===================================
@@ -283,10 +286,15 @@ $transition: all 0.3s ease;
cursor: pointer;
transition: $transition;
border: 2px solid transparent;
+ box-shadow: $card-box-shadow;
&:hover {
+ transform: translateY(-2px);
border-color: map-get($theme-colors, "primary");
box-shadow: $shadow-md;
+ .book-cover {
+ transform: scale(1.03);
+ }
}
// Style khusus ketika checkbox di dalamnya terpilih
@@ -295,6 +303,7 @@ $transition: all 0.3s ease;
background-color: rgba(map-get($theme-colors, "success"), 0.05);
}
}
+
.remove-book {
padding: 2px 6px;
font-size: 12px;
@@ -305,7 +314,6 @@ $transition: all 0.3s ease;
opacity: 0.65;
}
-
.book-option[style*="display: none"] {
display: none !important;
-}
\ No newline at end of file
+}
diff --git a/resources/scss/app.scss b/resources/scss/app.scss
index 4e502dd..1b0f6a3 100644
--- a/resources/scss/app.scss
+++ b/resources/scss/app.scss
@@ -1,7 +1,217 @@
@import "bootstrap/scss/functions";
-@import "variables";
+@import "variables";
@import "bootstrap/scss/bootstrap";
+// =========================================
+// Custom Styling untuk Navbar dan Sidebar
+// =========================================
+
body {
- background-color: $light;
-}
\ No newline at end of file
+ background-color: map-get($grays, "light");
+}
+
+.sidebar {
+ width: 240px;
+ position: fixed;
+ top: 0;
+ bottom: 0;
+ z-index: 1050;
+ transition: all 0.3s ease;
+}
+
+.main-wrapper {
+ transition: margin-left 0.3s ease;
+}
+
+.overlay {
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background: rgba(0, 0, 0, 0.5);
+ z-index: 1049;
+ display: none;
+}
+
+.overlay.active {
+ display: block;
+}
+
+@media (min-width: 992px) {
+ .sidebar {
+ left: 0;
+ }
+
+ .main-wrapper {
+ margin-left: 250px;
+ }
+
+ .sidebar.minimized {
+ width: 70px;
+ }
+
+ .sidebar.minimized .nav-text,
+ .sidebar.minimized .sidebar-title {
+ display: none;
+ }
+
+ .sidebar.minimized .nav-link {
+ justify-content: center;
+ }
+
+ .main-wrapper.sidebar-minimized {
+ margin-left: 80px;
+ }
+}
+
+@media (max-width: 991.98px) {
+ .sidebar {
+ left: -250px;
+ }
+
+ .sidebar.active {
+ left: 0;
+ }
+}
+
+.sidebar .nav-link {
+ font-weight: 500;
+ color: #555;
+ margin: 0.3rem 0;
+ border-radius: 0.5rem;
+ display: flex;
+ align-items: center;
+}
+
+.sidebar .nav-link.active {
+ background: rgba(67, 94, 190, 0.1);
+ color: #435ebe;
+ font-weight: 600;
+}
+
+.sidebar .nav-link:hover:not(.active) {
+ background: #f8f9fa;
+}
+
+.sidebar.minimized .nav-link .ms-auto {
+ display: none;
+}
+
+// ===================================
+// NAVBAR & NOTIFICATIONS
+// ===================================
+
+nav {
+ border-bottom-left-radius: 5px;
+ border-bottom-right-radius: 5px;
+}
+
+.navbar {
+ border-bottom: none;
+}
+
+.navbar-brand {
+ color: var(--bs-dark-text-emphasis) !important;
+}
+
+.navbar .nav-link,
+.navbar .btn-light {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 40px;
+ height: 40px;
+ border-radius: 50%;
+ background-color: transparent;
+ border: none;
+ transition: background-color 0.2s ease-in-out;
+
+ &:hover,
+ &:focus {
+ background-color: var(--bs-light-bg-subtle);
+ }
+}
+
+.profile-avatar {
+ width: 38px;
+ height: 38px;
+ border-radius: 50%;
+ object-fit: cover;
+ cursor: pointer;
+}
+
+.dropdown-item {
+ display: flex;
+ align-items: center;
+ gap: 0.75rem;
+ padding: 0.5rem 1rem;
+}
+
+.dropdown-item i {
+ width: 1.25em;
+}
+
+.notification-dropdown .dropdown-menu {
+ width: 390px;
+ border-radius: 0.75rem;
+ padding: 0.5rem !important;
+ box-shadow: 0 0.5rem 1.5rem rgba(0, 0, 0, 0.12);
+}
+
+.notification-wrapper {
+ max-height: 450px;
+}
+
+.notification-item {
+ display: flex;
+ align-items: center;
+ gap: 0.8rem;
+ padding: 0.8rem 1rem;
+ transition: background-color 0.2s ease;
+ text-decoration: none !important;
+}
+
+.notification-item:hover {
+ background-color: var(--bs-tertiary-bg);
+}
+
+.notification-item.unread {
+ background-color: rgba(var(--bs-primary-rgb), 0.1);
+}
+
+.notification-icon {
+ width: 42px;
+ height: 42px;
+ flex-shrink: 0;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ border-radius: 50%;
+}
+
+.notification-icon i {
+ text-align: center;
+ font-size: 1.25rem;
+}
+
+.notification-content p {
+ margin-bottom: 0.1rem;
+ font-weight: 600;
+ color: var(--bs-body-color);
+ font-size: 0.925rem;
+ line-height: 1.4;
+}
+
+.notification-content small {
+ color: var(--bs-secondary-color);
+ font-size: 0.8rem;
+}
+
+.unread-dot {
+ width: 8px;
+ height: 8px;
+ border-radius: 50%;
+ background-color: var(--bs-primary);
+ margin-left: auto;
+}
diff --git a/resources/views/admin/buku/create.blade.php b/resources/views/admin/buku/create.blade.php
new file mode 100644
index 0000000..6072106
--- /dev/null
+++ b/resources/views/admin/buku/create.blade.php
@@ -0,0 +1,66 @@
+
+ @section('page-title', $pageTitle)
+
+
+
diff --git a/resources/views/admin/buku/index.blade.php b/resources/views/admin/buku/index.blade.php
new file mode 100644
index 0000000..1b1e78a
--- /dev/null
+++ b/resources/views/admin/buku/index.blade.php
@@ -0,0 +1,171 @@
+
+ @section('page-title', $pageTitle)
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+ {{-- TAB UNTUK BUKU OFFLINE --}}
+
+
+
+
+ | No | Cover | Judul | Penulis | Status | Aksi |
+
+
+ @forelse($bukuOffline as $buku)
+
+ | {{ $loop->iteration }} |
+ ![{{ $buku['judul'] }}]({{ asset($buku['cover']) }}) |
+ {{ $buku['judul'] }} |
+ {{ $buku['penulis'] }} |
+
+ @if($buku['status'] == 'Tersedia')
+ Tersedia
+ @else
+ Dipinjam
+ @endif
+ |
+
+
+ |
+
+ @empty
+ | Tidak ada data buku offline. |
+ @endforelse
+
+
+
+
+
+ {{-- TAB UNTUK BUKU ONLINE --}}
+
+
+
+
+ | No | Cover | Judul | Penulis | File PDF | Aksi |
+
+
+ @forelse($bukuOnline as $buku)
+
+ | {{ $loop->iteration }} |
+ ![{{ $buku['judul'] }}]({{ asset($buku['cover']) }}) |
+ {{ $buku['judul'] }} |
+ {{ $buku['penulis'] }} |
+ {{ $buku['file_pdf'] ?? 'N/A' }} |
+
+
+ |
+
+ @empty
+ | Tidak ada data buku online. |
+ @endforelse
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
![Cover Buku]()
+
+
+
+
+
+ | Kategori | |
+ | Tahun | |
+ | Status | |
+
+
+
+
+
+
+
+
+ @push('scripts')
+
+ @endpush
+
\ No newline at end of file
diff --git a/resources/views/admin/dashboard.blade.php b/resources/views/admin/dashboard.blade.php
new file mode 100644
index 0000000..ddf6fe6
--- /dev/null
+++ b/resources/views/admin/dashboard.blade.php
@@ -0,0 +1,172 @@
+
+ @section('page-title', 'Dashboard Admin')
+
+
+
Selamat Datang, {{ $user->nama_lengkap }}!
+
Berikut adalah ringkasan aktivitas perpustakaan hari ini.
+
+
+ {{-- Kartu Statistik --}}
+
+ @foreach ($stats as $stat)
+
+
+
+
+
+
{{ $stat['label'] }}
+ {{ $stat['value'] }}
+
+
+
+
+
+
+ @endforeach
+
+
+
+
+
+
+
+
+
+
+ Tersedia
+ Dipinjam
+
+
+
+
+
+
+
+
+
+
+
+ @forelse(collect($pengumuman)->take(2) as $item)
+
+
+
+
{{ $item['title'] }}!
+
{{ $item['content'] }}
+
+
+ @empty
+
Tidak ada pengumuman baru.
+ @endforelse
+
+
+
+
+
+
+
+
+
+
+ @forelse($aktivitasTerakhir as $aktivitas)
+
+ |
+ {{ $aktivitas['nama'] }}
+ {{ $aktivitas['tipe'] }} buku "{{ $aktivitas['judul_buku'] }}"
+ |
+
+ @if($aktivitas['status'] == 'Dikembalikan' || $aktivitas['status'] == 'Selesai')
+ {{$aktivitas['status']}}
+ @else
+ {{$aktivitas['status']}}
+ @endif
+ {{ $aktivitas['waktu'] }}
+ |
+
+ @empty
+ Tidak ada aktivitas terkini. |
+ @endforelse
+
+
+
+
+
+
+
+
+
+ @push('scripts')
+
+
+ @endpush
+
\ No newline at end of file
diff --git a/resources/views/admin/pengguna/index.blade.php b/resources/views/admin/pengguna/index.blade.php
new file mode 100644
index 0000000..49e13d0
--- /dev/null
+++ b/resources/views/admin/pengguna/index.blade.php
@@ -0,0 +1,28 @@
+
+ @section('page-title', $pageTitle)
+
+
+
+
+
+
+ | No | Nama Lengkap | Email | Role | Aksi |
+
+
+ @forelse($semuaSiswa as $siswa)
+
+ | {{ $loop->iteration }} |
+ {{ $siswa['nama_lengkap'] }} |
+ {{ $siswa['email'] }} |
+ {{ Str::title($siswa['role']) }} |
+ |
+
+ @empty
+ | Tidak ada data pengguna. |
+ @endforelse
+
+
+
+
+
+
\ No newline at end of file
diff --git a/resources/views/admin/pengumuman/index.blade.php b/resources/views/admin/pengumuman/index.blade.php
new file mode 100644
index 0000000..7df3a68
--- /dev/null
+++ b/resources/views/admin/pengumuman/index.blade.php
@@ -0,0 +1,42 @@
+
+ @section('page-title', $pageTitle)
+
+
+
+
+
+
+
+ | No |
+ Tipe |
+ Judul |
+ Isi |
+ Aksi |
+
+
+
+ @foreach ($semuaPengumuman as $item)
+
+ | {{ $loop->iteration }} |
+ {{ Str::title($item['type']) }}
+ |
+ {{ $item['title'] }} |
+ {{ $item['content'] }} |
+
+
+
+ |
+
+ @endforeach
+
+
+
+
+
+
diff --git a/resources/views/layouts/app.blade.php b/resources/views/layouts/app.blade.php
index c04d08a..9e67040 100644
--- a/resources/views/layouts/app.blade.php
+++ b/resources/views/layouts/app.blade.php
@@ -6,102 +6,14 @@
{{ config('app.name', 'Perpus') }}
-
-
@vite(['resources/scss/app.scss', 'resources/js/app.js'])
-
-
@@ -120,6 +32,45 @@
+
+
+
@stack('scripts')
diff --git a/resources/views/layouts/navigation.blade.php b/resources/views/layouts/navigation.blade.php
index b4be40b..ef37d57 100644
--- a/resources/views/layouts/navigation.blade.php
+++ b/resources/views/layouts/navigation.blade.php
@@ -1,28 +1,108 @@