feat: migrate dummy data to database and refactor all controllers
This commit is contained in:
parent
779ef38952
commit
1764c5f9f4
|
|
@ -3,42 +3,41 @@
|
|||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Services\DummyDataService;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Models\Book;
|
||||
use App\Models\Loan;
|
||||
use App\Models\User;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class AdminPeminjamanController extends Controller
|
||||
{
|
||||
public function index(Request $request)
|
||||
{
|
||||
$peminjamanAktif = DummyDataService::getAdminPeminjamanAktif();
|
||||
$loans = Loan::with(['user', 'book'])
|
||||
->whereIn('status', ['Dipinjam', 'Terlambat'])
|
||||
->get();
|
||||
|
||||
// LOGIC DENDA & WA LINK (Update Request Client)
|
||||
$peminjamanAktif = $peminjamanAktif->map(function ($item) {
|
||||
// Hitung Denda Flat 1000/hari
|
||||
$tenggat = Carbon::parse($item['tenggat_kembali']);
|
||||
$now = Carbon::now();
|
||||
$groupedLoans = $loans->groupBy('user_id');
|
||||
|
||||
$item['hari_terlambat'] = 0;
|
||||
$item['total_denda'] = 0;
|
||||
|
||||
if ($now->greaterThan($tenggat)) {
|
||||
$hariTelat = $tenggat->diffInDays($now);
|
||||
$item['hari_terlambat'] = $hariTelat;
|
||||
$item['total_denda'] = $hariTelat * 1000;
|
||||
$item['denda_per_hari'] = 1000;
|
||||
}
|
||||
// Generate WA Link
|
||||
$hp = $item['nomor_hp'];
|
||||
if (substr($hp, 0, 1) == '0') {
|
||||
$hp = '62' . substr($hp, 1);
|
||||
}
|
||||
|
||||
$pesan = "Halo kak {$item['peminjam']}, buku anda sudah terlambat {$item['hari_terlambat']} hari dengan denda Rp " . number_format($item['total_denda'], 0, ',', '.') . ". Mohon segera dikembalikan ya.";
|
||||
$item['wa_link'] = "https://wa.me/{$hp}?text=" . urlencode($pesan);
|
||||
|
||||
return $item;
|
||||
});
|
||||
$peminjamanAktif = $groupedLoans->map(function ($userLoans, $userId) {
|
||||
$user = $userLoans->first()->user;
|
||||
$firstLoan = $userLoans->first();
|
||||
|
||||
return [
|
||||
'id_peminjaman' => 'PIN-ADM-' . sprintf('%03d', $userId),
|
||||
'user_id' => $userId,
|
||||
'peminjam' => $user->nama_lengkap ?? 'Unknown',
|
||||
'nomor_hp' => $user->phone ?? '-',
|
||||
'tanggal_pinjam' => $firstLoan->borrowed_at,
|
||||
'tenggat_kembali' => $firstLoan->due_at,
|
||||
'status' => $firstLoan->status,
|
||||
'books' => $userLoans->map(fn($l) => [
|
||||
'id' => $l->book->id,
|
||||
'judul' => $l->book->judul,
|
||||
'cover' => $l->book->cover,
|
||||
])->toArray(),
|
||||
];
|
||||
})->values();
|
||||
|
||||
$daftarPeminjam = $peminjamanAktif->pluck('peminjam')->unique();
|
||||
|
||||
|
|
@ -51,44 +50,31 @@ public function index(Request $request)
|
|||
|
||||
public function create()
|
||||
{
|
||||
$allUsers = collect(DummyDataService::getAllSiswa());
|
||||
$peminjamanAktif = DummyDataService::getAdminPeminjamanAktif();
|
||||
$users = User::whereIn('role', ['siswa', 'guru'])->get();
|
||||
|
||||
$groupedUsers = $users->map(function ($user) {
|
||||
$jumlahPinjam = Loan::where('user_id', $user->id)
|
||||
->whereIn('status', ['Dipinjam', 'Terlambat'])
|
||||
->count();
|
||||
|
||||
$groupedUsers = $allUsers
|
||||
->whereIn('role', ['siswa', 'guru'])
|
||||
->map(function ($user) use ($peminjamanAktif) {
|
||||
$user->jumlah_pinjam = $jumlahPinjam;
|
||||
$user->kena_limit = $jumlahPinjam >= 2;
|
||||
$user->disabled = $user->kena_limit || $user->is_banned;
|
||||
|
||||
// Hitung berapa buku yang sedang dipinjam
|
||||
$jumlahPinjam = $peminjamanAktif->where('peminjam', $user['nama_lengkap'])->count();
|
||||
if ($user->is_banned) {
|
||||
$user->status_text = "(Akun Dibekukan)";
|
||||
} elseif ($user->kena_limit) {
|
||||
$user->status_text = "(Limit Penuh: 2/2)";
|
||||
} else {
|
||||
$user->status_text = "";
|
||||
}
|
||||
|
||||
// Cek Status
|
||||
$user['jumlah_pinjam'] = $jumlahPinjam;
|
||||
$user['kena_limit'] = $jumlahPinjam >= 2;
|
||||
return $user;
|
||||
})->groupBy('role');
|
||||
|
||||
// Cek apakah user di-banned (Nonaktif Manual)
|
||||
$user['is_banned'] = $user['is_banned'] ?? false;
|
||||
$user['disabled'] = $user['kena_limit'] || $user['is_banned'];
|
||||
|
||||
if ($user['is_banned']) {
|
||||
$user['status_text'] = "(Akun Dibekukan)";
|
||||
} elseif ($user['kena_limit']) {
|
||||
$user['status_text'] = "(Limit Penuh: 2/2)";
|
||||
} else {
|
||||
$user['status_text'] = "";
|
||||
}
|
||||
|
||||
return $user;
|
||||
})
|
||||
->groupBy('role');
|
||||
|
||||
$daftarBuku = DummyDataService::getAllBooks()
|
||||
->where('status', 'Tersedia')
|
||||
->filter(function ($buku) {
|
||||
if (is_array($buku['tipe_akses'])) {
|
||||
return in_array('offline', $buku['tipe_akses']);
|
||||
}
|
||||
return $buku['tipe_akses'] === 'offline';
|
||||
});
|
||||
$daftarBuku = Book::where('status', 'Tersedia')
|
||||
->whereJsonContains('tipe_akses', 'offline')
|
||||
->get();
|
||||
|
||||
return view('admin.peminjaman.create', [
|
||||
'pageTitle' => 'Buat Peminjaman Manual',
|
||||
|
|
@ -97,80 +83,58 @@ public function create()
|
|||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Menampilkan halaman KHUSUS Manajemen Denda (Siswa & Guru Telat).
|
||||
*/
|
||||
public function dendaIndex()
|
||||
{
|
||||
$allData = DummyDataService::getAdminPeminjamanAktif();
|
||||
$allSiswaRaw = collect(DummyDataService::getAllSiswa());
|
||||
$now = \Carbon\Carbon::now();
|
||||
$now = Carbon::now();
|
||||
|
||||
// Fetch all loans that are overdue or users that are banned
|
||||
$loans = Loan::with(['user', 'book'])
|
||||
->where(function($query) use ($now) {
|
||||
$query->where('due_at', '<', $now)
|
||||
->whereIn('status', ['Dipinjam', 'Terlambat']);
|
||||
})
|
||||
->orWhereHas('user', function($query) {
|
||||
$query->where('is_banned', true);
|
||||
})
|
||||
->get();
|
||||
|
||||
// LOGIC AUTO-BAN
|
||||
$allSiswa = $allSiswaRaw->map(function ($siswa) use ($allData, $now) {
|
||||
$groupedLoans = $loans->groupBy('user_id');
|
||||
|
||||
// Cek siswa punya pinjaman yang telat
|
||||
$pinjamanUser = $allData->firstWhere('user_id', $siswa['id']);
|
||||
|
||||
$isTelat = false;
|
||||
if ($pinjamanUser) {
|
||||
$isTelat = $pinjamanUser['tenggat_kembali']->startOfDay()->lt($now->startOfDay());
|
||||
}
|
||||
|
||||
// Jika Role SISWA dan TELAT -> Wajib AUTO BAN (Override true)
|
||||
// ika Role GURU -> Ikut status asli database (Manual Ban)
|
||||
if ($siswa['role'] === 'siswa' && $isTelat) {
|
||||
$siswa['is_banned'] = true;
|
||||
}
|
||||
|
||||
return $siswa;
|
||||
});
|
||||
|
||||
$siswaTelat = $allData->filter(function ($item) use ($now, $allSiswa) {
|
||||
$userData = $allSiswa->firstWhere('id', $item['user_id']);
|
||||
|
||||
if (!$userData)
|
||||
return false;
|
||||
|
||||
$isTelat = $item['tenggat_kembali']->startOfDay()->lt($now->startOfDay());
|
||||
$isBanned = $userData['is_banned'];
|
||||
$isTargetRole = in_array($userData['role'], ['siswa', 'guru']);
|
||||
|
||||
return ($isTelat || $isBanned) && $isTargetRole;
|
||||
})->map(function ($item) use ($now, $allSiswa) {
|
||||
|
||||
// Hitungan Denda
|
||||
$tenggat = $item['tenggat_kembali']->startOfDay();
|
||||
|
||||
// Jika belum telat (tapi banned/manual), hari telat 0
|
||||
$hariTelat = ($tenggat->lt($now->startOfDay()))
|
||||
? $tenggat->diffInDays($now->startOfDay())
|
||||
: 0;
|
||||
|
||||
$totalDenda = $hariTelat * $item['denda_per_hari'];
|
||||
|
||||
$item['hari_terlambat'] = $hariTelat;
|
||||
$item['total_denda'] = $totalDenda;
|
||||
|
||||
// Ambil status is_banned TERBARU dari $allSiswa
|
||||
$dataSiswa = $allSiswa->firstWhere('id', $item['user_id']);
|
||||
$item['is_banned'] = $dataSiswa['is_banned'] ?? false;
|
||||
$item['kelas'] = $dataSiswa['kelas'] ?? 'Guru';
|
||||
$siswaTelat = $groupedLoans->map(function ($userLoans, $userId) use ($now) {
|
||||
$user = $userLoans->first()->user;
|
||||
$firstLoan = $userLoans->first();
|
||||
|
||||
$tenggat = Carbon::parse($firstLoan->due_at);
|
||||
$hariTelat = $now->greaterThan($tenggat) ? (int) $tenggat->diffInDays($now) : 0;
|
||||
$totalDenda = $hariTelat * 1000;
|
||||
|
||||
// Link WA
|
||||
$hp = $item['nomor_hp'];
|
||||
if (substr($hp, 0, 1) == '0')
|
||||
$hp = '62' . substr($hp, 1);
|
||||
|
||||
if ($hariTelat > 0) {
|
||||
$pesan = "Halo {$item['peminjam']}, anda terlambat pengembalian buku. Total Denda: Rp " . number_format($totalDenda, 0, ',', '.');
|
||||
} else {
|
||||
$pesan = "Halo {$item['peminjam']}, akun anda sedang dinonaktifkan sementara. Mohon hubungi petugas.";
|
||||
$hp = $user->phone ?? '';
|
||||
$waLink = '#';
|
||||
if ($hp) {
|
||||
if (substr($hp, 0, 1) == '0') $hp = '62' . substr($hp, 1);
|
||||
$pesan = $hariTelat > 0
|
||||
? "Halo {$user->nama_lengkap}, anda terlambat pengembalian buku. Total Denda: Rp " . number_format($totalDenda, 0, ',', '.')
|
||||
: "Halo {$user->nama_lengkap}, akun anda sedang dinonaktifkan sementara. Mohon hubungi petugas.";
|
||||
$waLink = "https://wa.me/{$hp}?text=" . urlencode($pesan);
|
||||
}
|
||||
$item['wa_link'] = "https://wa.me/{$hp}?text=" . urlencode($pesan);
|
||||
|
||||
return $item;
|
||||
});
|
||||
return [
|
||||
'id' => $firstLoan->id,
|
||||
'peminjam' => $user->nama_lengkap,
|
||||
'nomor_hp' => $user->phone ?? '-',
|
||||
'kelas' => $user->kelas ?? 'Guru',
|
||||
'hari_terlambat' => $hariTelat,
|
||||
'total_denda' => $totalDenda,
|
||||
'wa_link' => $waLink,
|
||||
'is_banned' => $user->is_banned,
|
||||
'tenggat_kembali' => $firstLoan->due_at,
|
||||
'books' => $userLoans->map(fn($l) => [
|
||||
'id' => $l->book->id,
|
||||
'judul' => $l->book->judul,
|
||||
])->toArray(),
|
||||
];
|
||||
})->values();
|
||||
|
||||
$listKelas = $siswaTelat->pluck('kelas')->unique()->values();
|
||||
|
||||
|
|
@ -181,11 +145,9 @@ public function dendaIndex()
|
|||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Dummy function untuk tombol Sanksi
|
||||
*/
|
||||
public function berikanSanksi(Request $request)
|
||||
{
|
||||
// Actually implement banning logic here if needed
|
||||
return response()->json(['status' => 'success']);
|
||||
}
|
||||
}
|
||||
|
|
@ -3,7 +3,7 @@
|
|||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Services\DummyDataService;
|
||||
use App\Models\Recommendation;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class AdminRekomendasiController extends Controller
|
||||
|
|
@ -19,17 +19,19 @@ private function extractYouTubeId(string $url): ?string
|
|||
|
||||
public function index()
|
||||
{
|
||||
$rekomendasiMentah = DummyDataService::getRekomendasiPembelajaran();
|
||||
$rekomendasiMentah = Recommendation::latest()->get();
|
||||
|
||||
// Menambahkan thumbnail YouTube ke setiap rekomendasi
|
||||
$semuaRekomendasi = $rekomendasiMentah->map(function ($item) {
|
||||
$videoId = $this->extractYouTubeId($item['youtube_link']);
|
||||
if ($videoId) {
|
||||
$item['thumbnail'] = "https://img.youtube.com/vi/{$videoId}/hqdefault.jpg";
|
||||
} else {
|
||||
$item['thumbnail'] = 'https://via.placeholder.com/150?text=No+Preview';
|
||||
}
|
||||
return $item;
|
||||
$videoId = $this->extractYouTubeId($item->youtube_link);
|
||||
return [
|
||||
'id' => $item->id,
|
||||
'judul' => $item->judul,
|
||||
'kategori' => $item->kategori,
|
||||
'youtube_link' => $item->youtube_link,
|
||||
'thumbnail' => $videoId ? "https://img.youtube.com/vi/{$videoId}/hqdefault.jpg" : 'https://via.placeholder.com/150?text=No+Preview',
|
||||
'deskripsi' => $item->deskripsi,
|
||||
];
|
||||
});
|
||||
|
||||
return view('admin.rekomendasi.index', [
|
||||
|
|
@ -45,8 +47,11 @@ public function create()
|
|||
|
||||
public function edit($id)
|
||||
{
|
||||
$rekomendasi = DummyDataService::getRekomendasiPembelajaran()->firstWhere('id', (int)$id);
|
||||
abort_if(!$rekomendasi, 404);
|
||||
return view('admin.rekomendasi.edit', ['pageTitle' => 'Edit Rekomendasi', 'rekomendasi' => $rekomendasi]);
|
||||
$rekomendasi = Recommendation::findOrFail($id);
|
||||
|
||||
return view('admin.rekomendasi.edit', [
|
||||
'pageTitle' => 'Edit Rekomendasi: ' . $rekomendasi->judul,
|
||||
'rekomendasi' => $rekomendasi
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
@ -3,7 +3,8 @@
|
|||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Services\DummyDataService;
|
||||
use App\Models\Book;
|
||||
use App\Models\Category;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class BookController extends Controller
|
||||
|
|
@ -11,12 +12,19 @@ class BookController extends Controller
|
|||
public function index(Request $request)
|
||||
{
|
||||
$filters = $request->only(['search']);
|
||||
$semuaBuku = DummyDataService::getKatalogBuku($filters);
|
||||
|
||||
$query = Book::with('category');
|
||||
|
||||
if ($request->filled('search')) {
|
||||
$query->where('judul', 'like', '%' . $request->search . '%');
|
||||
}
|
||||
|
||||
$semuaBuku = $query->latest()->get();
|
||||
|
||||
// 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));
|
||||
$tipe = $buku->tipe_akses;
|
||||
return in_array('online', $tipe ?? []);
|
||||
});
|
||||
|
||||
return view('admin.buku.index', [
|
||||
|
|
@ -33,18 +41,21 @@ public function index(Request $request)
|
|||
public function create()
|
||||
{
|
||||
return view('admin.buku.create', [
|
||||
'pageTitle' => 'Tambah Buku Baru'
|
||||
'pageTitle' => 'Tambah Buku Baru',
|
||||
'categories' => Category::all()
|
||||
]);
|
||||
}
|
||||
|
||||
public function edit($id)
|
||||
{
|
||||
$buku = DummyDataService::getKatalogBuku()->firstWhere('id', (int)$id);
|
||||
abort_if(!$buku, 404);
|
||||
{
|
||||
$buku = Book::with('category')->findOrFail($id);
|
||||
|
||||
return view('admin.buku.edit', [
|
||||
'pageTitle' => 'Edit Buku: ' . $buku['judul'],
|
||||
'buku' => $buku
|
||||
]);
|
||||
}
|
||||
return view('admin.buku.edit', [
|
||||
'pageTitle' => 'Edit Buku: ' . $buku->judul,
|
||||
'buku' => $buku,
|
||||
'categories' => Category::all()
|
||||
]);
|
||||
}
|
||||
|
||||
// You might also want to implement store, update, destroy here later
|
||||
}
|
||||
|
|
@ -3,21 +3,78 @@
|
|||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Services\DummyDataService;
|
||||
use App\Models\Announcement;
|
||||
use App\Models\Book;
|
||||
use App\Models\Loan;
|
||||
use App\Models\User;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
class DashboardController extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
$allBooks = Book::count();
|
||||
$allUsers = User::count();
|
||||
$bukuDipinjam = Loan::whereIn('status', ['Dipinjam', 'Terlambat'])->count();
|
||||
|
||||
$stats = [
|
||||
['label' => 'Total Buku', 'value' => $allBooks, 'icon' => 'bi-journal-bookmark-fill', 'color' => 'primary'],
|
||||
['label' => 'Total Anggota', 'value' => $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' => Loan::where('status', 'Terlambat')->count(), 'icon' => 'bi-cash-coin', 'color' => 'danger'],
|
||||
];
|
||||
|
||||
// Monthly stats (last 7 months)
|
||||
$labels = [];
|
||||
$data = [];
|
||||
for ($i = 6; $i >= 0; $i--) {
|
||||
$month = Carbon::now()->subMonths($i);
|
||||
$labels[] = $month->translatedFormat('M');
|
||||
$data[] = Loan::whereMonth('borrowed_at', $month->month)
|
||||
->whereYear('borrowed_at', $month->year)
|
||||
->count();
|
||||
}
|
||||
|
||||
$statistikBulanan = [
|
||||
'labels' => $labels,
|
||||
'data' => $data,
|
||||
];
|
||||
|
||||
$komposisiBuku = [
|
||||
'tersedia' => Book::where('status', 'Tersedia')->count(),
|
||||
'dipinjam' => Book::where('status', 'Dipinjam')->count(),
|
||||
];
|
||||
|
||||
$pengumuman = Announcement::latest()->take(5)->get();
|
||||
|
||||
$aktivitasTerakhir = Loan::with(['user', 'book'])
|
||||
->latest()
|
||||
->take(4)
|
||||
->get()
|
||||
->map(fn($loan) => [
|
||||
'nama' => $loan->user->nama_lengkap ?? 'Unknown',
|
||||
'judul_buku' => $loan->book->judul ?? 'Unknown',
|
||||
'tipe' => $loan->status === 'Dikembalikan' ? 'Pengembalian' : 'Peminjaman',
|
||||
'waktu' => $loan->created_at->diffForHumans(),
|
||||
'status' => $loan->status,
|
||||
]);
|
||||
|
||||
$hour = date('H');
|
||||
$greeting = "Selamat Pagi";
|
||||
if ($hour >= 12 && $hour < 15) $greeting = "Selamat Siang";
|
||||
elseif ($hour >= 15 && $hour < 18) $greeting = "Selamat Sore";
|
||||
elseif ($hour >= 18) $greeting = "Selamat Malam";
|
||||
|
||||
return view('admin.dashboard', [
|
||||
'pageTitle' => 'Beranda',
|
||||
'user' => auth()->user(),
|
||||
'greeting' => 'Selamat Datang',
|
||||
'stats' => DummyDataService::getAdminDashboardStats(),
|
||||
'statistikBulanan' => DummyDataService::getStatistikPeminjamanAdmin(),
|
||||
'komposisiBuku' => DummyDataService::getKomposisiBukuAdmin(),
|
||||
'pengumuman' => DummyDataService::getPengumuman(),
|
||||
'aktivitasTerakhir' => DummyDataService::getAktivitasTerakhir(),
|
||||
'user' => Auth::user(),
|
||||
'greeting' => $greeting,
|
||||
'stats' => $stats,
|
||||
'statistikBulanan' => $statistikBulanan,
|
||||
'komposisiBuku' => $komposisiBuku,
|
||||
'pengumuman' => $pengumuman,
|
||||
'aktivitasTerakhir' => $aktivitasTerakhir,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Services\DummyDataService;
|
||||
use App\Models\Announcement;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class PengumumanController extends Controller
|
||||
|
|
@ -13,7 +13,7 @@ class PengumumanController extends Controller
|
|||
*/
|
||||
public function index()
|
||||
{
|
||||
$semuaPengumuman = DummyDataService::getPengumuman();
|
||||
$semuaPengumuman = Announcement::latest()->get();
|
||||
return view('admin.pengumuman.index', [
|
||||
'pageTitle' => 'Manajemen Pengumuman',
|
||||
'semuaPengumuman' => $semuaPengumuman,
|
||||
|
|
@ -35,13 +35,10 @@ public function create()
|
|||
*/
|
||||
public function edit($id)
|
||||
{
|
||||
$pengumuman = collect(DummyDataService::getPengumuman())->firstWhere('id', (int)$id);
|
||||
|
||||
// Hentikan jika pengumuman tidak ditemukan
|
||||
abort_if(!$pengumuman, 404);
|
||||
$pengumuman = Announcement::findOrFail($id);
|
||||
|
||||
return view('admin.pengumuman.edit', [
|
||||
'pageTitle' => 'Edit Pengumuman',
|
||||
'pageTitle' => 'Edit Pengumuman: ' . $pengumuman->title,
|
||||
'pengumuman' => $pengumuman,
|
||||
]);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,13 +3,14 @@
|
|||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Services\DummyDataService;
|
||||
use App\Models\User;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class UserController extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
$semuaSiswa = DummyDataService::getAllSiswa();
|
||||
$semuaSiswa = User::whereIn('role', ['siswa', 'guru', 'penjaga perpus'])->latest()->get();
|
||||
return view('admin.pengguna.index', [
|
||||
'pageTitle' => 'Manajemen Pengguna',
|
||||
'semuaSiswa' => $semuaSiswa
|
||||
|
|
@ -31,12 +32,13 @@ public function create()
|
|||
*/
|
||||
public function edit($id)
|
||||
{
|
||||
$pengguna = collect(DummyDataService::getAllSiswa())->firstWhere('id', (int)$id);
|
||||
abort_if(!$pengguna, 404);
|
||||
$pengguna = User::findOrFail($id);
|
||||
|
||||
return view('admin.pengguna.edit', [
|
||||
'pageTitle' => 'Edit Pengguna',
|
||||
'pageTitle' => 'Edit Pengguna: ' . $pengguna->nama_lengkap,
|
||||
'pengguna' => $pengguna,
|
||||
]);
|
||||
}
|
||||
|
||||
// You might also want to implement store, update, destroy here later
|
||||
}
|
||||
|
|
@ -31,52 +31,35 @@ public function create(Request $request): View
|
|||
*/
|
||||
public function store(Request $request): RedirectResponse
|
||||
{
|
||||
// Bagian Validasi Dinamis
|
||||
$role = $request->input('role');
|
||||
|
||||
$rules = [
|
||||
'name' => ['required', 'string', 'max:255'],
|
||||
'password' => ['required', 'confirmed', Rules\Password::defaults()],
|
||||
'role' => ['required', 'in:siswa,guru'], // Sesuaikan dengan role yang diizinkan
|
||||
'role' => ['required', 'in:siswa,guru'],
|
||||
];
|
||||
|
||||
// Tambahkan validasi NISN atau NIP berdasarkan role
|
||||
if ($role === 'siswa') {
|
||||
$rules['nisn'] = ['required', 'string', 'max:255']; // Tambahkan 'unique:users' jika perlu
|
||||
} else { // Asumsi 'guru'
|
||||
$rules['nip'] = ['required', 'string', 'max:255']; // Tambahkan 'unique:users' jika perlu
|
||||
$rules['nisn'] = ['required', 'string', 'max:255', 'unique:users,nomor_induk'];
|
||||
} else {
|
||||
$rules['nip'] = ['required', 'string', 'max:255', 'unique:users,nomor_induk'];
|
||||
}
|
||||
|
||||
$request->validate($rules);
|
||||
|
||||
// Bagian Pembuatan User
|
||||
$userArray = [
|
||||
'id' => rand(100, 999), // ID unik sementara
|
||||
'nama_lengkap' => $request->name,
|
||||
$user = User::create([
|
||||
'name' => $request->name,
|
||||
'password' => Hash::make($request->password), // Gunakan Hash jika login Anda sudah pakai Hash
|
||||
// 'password' => $request->password, // Gunakan ini jika login (LoginRequest) masih cek teks biasa
|
||||
'nama_lengkap' => $request->name,
|
||||
'email' => ($request->nisn ?: $request->nip) . '@smkn1perpus.sch.id',
|
||||
'password' => Hash::make($request->password),
|
||||
'role' => $request->role,
|
||||
];
|
||||
|
||||
// Tambahkan field dinamis (NISN/NIP) dan buat email unik palsu
|
||||
if ($role === 'siswa') {
|
||||
$userArray['nisn'] = $request->nisn;
|
||||
$userArray['email'] = $request->nisn . '@smkn1perpus.sch.id'; // Email unik sementara
|
||||
} else {
|
||||
$userArray['nip'] = $request->nip;
|
||||
$userArray['email'] = $request->nip . '@smkn1perpus.sch.id'; // Email unik sementara
|
||||
}
|
||||
|
||||
$user = new User();
|
||||
$user->forceFill($userArray);
|
||||
// $user->save(); // Aktifkan ini jika menggunakan database
|
||||
'nomor_induk' => $request->nisn ?: $request->nip,
|
||||
]);
|
||||
|
||||
event(new Registered($user));
|
||||
|
||||
Auth::login($user);
|
||||
|
||||
// Bagian Redirect
|
||||
if ($user->role === 'penjaga perpus') {
|
||||
return redirect()->route('admin.dashboard');
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -2,10 +2,13 @@
|
|||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Services\DummyDataService;
|
||||
use App\Models\Book;
|
||||
use App\Models\Category;
|
||||
use App\Models\Loan;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Response;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\View\View;
|
||||
use Symfony\Component\HttpFoundation\BinaryFileResponse;
|
||||
|
|
@ -15,9 +18,34 @@ class BacaOnlineController extends Controller
|
|||
public function index(Request $request): View
|
||||
{
|
||||
$filters = $request->only(['search', 'kategori', 'tahun', 'penulis']);
|
||||
$filters['tipe_akses'] = 'online';
|
||||
$semuaBuku = DummyDataService::getKatalogBuku($filters);
|
||||
$filterOptions = DummyDataService::getFilterOptions();
|
||||
|
||||
$query = Book::with('category')->whereJsonContains('tipe_akses', 'online');
|
||||
|
||||
if ($request->filled('search')) {
|
||||
$query->where('judul', 'like', '%' . $request->search . '%');
|
||||
}
|
||||
|
||||
if ($request->filled('kategori')) {
|
||||
$query->whereHas('category', function($q) use ($request) {
|
||||
$q->where('name', $request->kategori);
|
||||
});
|
||||
}
|
||||
|
||||
if ($request->filled('tahun')) {
|
||||
$query->where('tahun', $request->tahun);
|
||||
}
|
||||
|
||||
if ($request->filled('penulis')) {
|
||||
$query->where('penulis', $request->penulis);
|
||||
}
|
||||
|
||||
$semuaBuku = $query->latest()->get();
|
||||
|
||||
$filterOptions = [
|
||||
'kategori' => Category::pluck('name')->unique()->sort()->values(),
|
||||
'tahun' => Book::pluck('tahun')->unique()->sortDesc()->values(),
|
||||
'penulis' => Book::pluck('penulis')->unique()->sort()->values(),
|
||||
];
|
||||
|
||||
return view('katalog.index', [
|
||||
'semuaBuku' => $semuaBuku,
|
||||
|
|
@ -30,7 +58,7 @@ public function index(Request $request): View
|
|||
|
||||
public function ringkasan(int $id): View
|
||||
{
|
||||
$book = $this->getBookOrFail($id);
|
||||
$book = Book::with('category')->findOrFail($id);
|
||||
|
||||
return view('katalog.ringkasan', [
|
||||
'buku' => $book,
|
||||
|
|
@ -44,13 +72,13 @@ public function ringkasan(int $id): View
|
|||
|
||||
public function showCodeRequestPage(int $id): View
|
||||
{
|
||||
$book = $this->getBookOrFail($id);
|
||||
$book = Book::findOrFail($id);
|
||||
$sessionKey = 'access_code_for_book_' . $id;
|
||||
|
||||
if (session()->has($sessionKey)) {
|
||||
$accessCode = session($sessionKey);
|
||||
} else {
|
||||
$accessCode = 'BCO-' . date('Ymd') . '-' . $book['id'] . '-' . Str::upper(Str::random(4));
|
||||
$accessCode = 'BCO-' . date('Ymd') . '-' . $book->id . '-' . Str::upper(Str::random(4));
|
||||
session([$sessionKey => $accessCode]);
|
||||
}
|
||||
|
||||
|
|
@ -69,6 +97,20 @@ public function verifyCode(Request $request, int $id): RedirectResponse
|
|||
|
||||
if ($request->input('kode_akses') === $correctCode) {
|
||||
session(['book_verified_' . $id => true]);
|
||||
|
||||
// Track history
|
||||
Loan::updateOrCreate(
|
||||
[
|
||||
'user_id' => Auth::id(),
|
||||
'book_id' => $id,
|
||||
'status' => 'Online',
|
||||
],
|
||||
[
|
||||
'loan_code' => $correctCode,
|
||||
'borrowed_at' => now(),
|
||||
]
|
||||
);
|
||||
|
||||
session()->forget('access_code_for_book_' . $id);
|
||||
return redirect()->route('baca.view_book', ['id' => $id]);
|
||||
}
|
||||
|
|
@ -82,7 +124,7 @@ public function viewBook(int $id): View|RedirectResponse
|
|||
return redirect()->route('baca.request_code', ['id' => $id])
|
||||
->with('error', 'Silakan masukkan kode akses terlebih dahulu.');
|
||||
}
|
||||
$book = $this->getBookOrFail($id);
|
||||
$book = Book::findOrFail($id);
|
||||
return view('baca.view_book', ['book' => $book]);
|
||||
}
|
||||
|
||||
|
|
@ -91,23 +133,16 @@ public function streamPdf(int $id): BinaryFileResponse|Response
|
|||
if (!session('book_verified_' . $id, false)) {
|
||||
abort(403, 'Akses Ditolak.');
|
||||
}
|
||||
$book = $this->getBookOrFail($id);
|
||||
$filePath = 'books/' . $book['file_pdf'];
|
||||
$book = Book::findOrFail($id);
|
||||
$filePath = 'books/' . ($book->file_pdf ?? 'sample.pdf');
|
||||
$absolutePath = storage_path('app/' . $filePath);
|
||||
|
||||
// For demo purposes, if file doesn't exist, we might want to use a placeholder or handle it gracefully
|
||||
if (!file_exists($absolutePath)) {
|
||||
abort(404, 'GAGAL - PHP tidak dapat menemukan file di: ' . $absolutePath);
|
||||
// Create a dummy file for testing if it doesn't exist? (Optional, maybe just abort)
|
||||
abort(404, 'File PDF tidak ditemukan di server.');
|
||||
}
|
||||
|
||||
return response()->file($absolutePath);
|
||||
}
|
||||
|
||||
private function getBookOrFail(int $id): array
|
||||
{
|
||||
$book = DummyDataService::getKatalogBuku()->firstWhere('id', $id);
|
||||
if (!$book) {
|
||||
abort(404, 'Buku tidak ditemukan.');
|
||||
}
|
||||
return $book;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,11 @@
|
|||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Services\DummyDataService;
|
||||
use App\Models\Announcement;
|
||||
use App\Models\Book;
|
||||
use App\Models\Loan;
|
||||
use App\Models\Recommendation;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
|
|
@ -11,50 +15,122 @@ class DashboardController extends Controller
|
|||
public function index()
|
||||
{
|
||||
$user = Auth::user();
|
||||
$bukuPinjam = DummyDataService::getBukuPinjamOffline($user);
|
||||
|
||||
// Fetch active loans for the user
|
||||
$loans = Loan::with('book')
|
||||
->where('user_id', $user->id)
|
||||
->whereIn('status', ['Dipinjam', 'Terlambat'])
|
||||
->get();
|
||||
|
||||
$isTelat = collect($bukuPinjam)->contains(function ($buku) {
|
||||
return $buku['sisa_hari'] < 0;
|
||||
$bukuPinjamOffline = $loans->map(function ($loan) {
|
||||
$dueAt = Carbon::parse($loan->due_at);
|
||||
$sisaHari = (int) now()->diffInDays($dueAt, false);
|
||||
|
||||
return [
|
||||
'id' => $loan->book->id,
|
||||
'judul' => $loan->book->judul,
|
||||
'penulis' => $loan->book->penulis,
|
||||
'sisa_hari' => $sisaHari,
|
||||
'cover' => $loan->book->cover,
|
||||
];
|
||||
});
|
||||
|
||||
// Check if user has overdue books
|
||||
$isTelat = $bukuPinjamOffline->contains(fn($b) => $b['sisa_hari'] < 0);
|
||||
|
||||
if ($isTelat && $user->role === 'siswa') {
|
||||
$user->is_banned = true;
|
||||
}
|
||||
$user->update(['is_banned' => true]);
|
||||
}
|
||||
|
||||
$stats = DummyDataService::getDashboardStats();
|
||||
$pengumuman = DummyDataService::getPengumuman();
|
||||
$pemberitahuan = DummyDataService::getPemberitahuan();
|
||||
$progressMembaca = DummyDataService::getProgressMembaca();
|
||||
$statistikBulanan = DummyDataService::getStatistikBulanan();
|
||||
$bukuPinjamOffline = $bukuPinjam;
|
||||
$bacaBukuOnline = DummyDataService::getBacaBukuOnline($user);
|
||||
$rekomendasiPembelajaran = DummyDataService::getRekomendasiPembelajaran();
|
||||
$personalNotif = DummyDataService::getNotifikasiForUser($user);
|
||||
// Stats calculation
|
||||
$stats = [
|
||||
['label' => 'Buku yang dipinjam', 'value' => $loans->count(), 'icon' => 'bi-book-half', 'color' => 'primary'],
|
||||
['label' => 'Tenggat Waktu', 'value' => $bukuPinjamOffline->where('sisa_hari', '<=', 3)->where('sisa_hari', '>=', 0)->count(), 'icon' => 'bi-clock-history', 'color' => 'danger'],
|
||||
['label' => 'Buku dikembalikan', 'value' => Loan::where('user_id', $user->id)->where('status', 'Dikembalikan')->count(), 'icon' => 'bi-check-circle', 'color' => 'success'],
|
||||
['label' => 'History Baca', 'value' => Loan::where('user_id', $user->id)->count(), 'icon' => 'bi-hourglass-split', 'color' => 'warning'],
|
||||
];
|
||||
|
||||
// Cek apakah ada notifikasi denda aktif
|
||||
$dendaAlert = collect($personalNotif)->where('type', 'denda_active');
|
||||
$pengumuman = Announcement::latest()->take(5)->get();
|
||||
|
||||
// Placeholder for pemberitahuan (system notifications)
|
||||
$pemberitahuan = collect([
|
||||
['type' => 'info', 'icon' => 'bi-bell-fill', 'title' => 'Selamat Datang', 'content' => 'Selamat datang di perpustakaan digital SMKN 1.', 'badge' => 'Baru']
|
||||
]);
|
||||
|
||||
// Menambahkan thumbnail YouTube ke setiap rekomendasi
|
||||
$rekomendasiPembelajaran = $rekomendasiPembelajaran->map(function ($item) {
|
||||
$videoId = $this->extractYouTubeId($item['youtube_link']);
|
||||
if ($videoId) {
|
||||
$item['thumbnail'] = "https://img.youtube.com/vi/{$videoId}/hqdefault.jpg";
|
||||
} else {
|
||||
$item['thumbnail'] = 'https://via.placeholder.com/150?text=No+Preview';
|
||||
}
|
||||
return $item;
|
||||
$progressMembaca = ['selesai' => 70, 'sisa' => 30]; // Still dummy as we don't track pages yet
|
||||
$statistikBulanan = [
|
||||
'labels' => ['Jan', 'Feb', 'Mar', 'Apr', 'Mei', 'Jun', 'Jul'],
|
||||
'data' => [10, 15, 8, 20, 18, 25, 22],
|
||||
];
|
||||
|
||||
// Online books (books with 'online' in tipe_akses)
|
||||
$bacaBukuOnline = $loans->filter(function ($loan) {
|
||||
return in_array('online', $loan->book->tipe_akses ?? []);
|
||||
})->map(fn($loan) => [
|
||||
'judul' => $loan->book->judul,
|
||||
'penulis' => $loan->book->penulis,
|
||||
'progress' => 0, // Placeholder
|
||||
'cover' => $loan->book->cover,
|
||||
]);
|
||||
|
||||
$recommendations = Recommendation::all();
|
||||
$rekomendasiPembelajaran = $recommendations->map(function ($item) {
|
||||
$videoId = $this->extractYouTubeId($item->youtube_link);
|
||||
return [
|
||||
'id' => $item->id,
|
||||
'judul' => $item->judul,
|
||||
'kategori' => $item->kategori,
|
||||
'thumbnail' => $videoId ? "https://img.youtube.com/vi/{$videoId}/hqdefault.jpg" : 'https://via.placeholder.com/150?text=No+Preview',
|
||||
'youtube_link' => $item->youtube_link,
|
||||
'deskripsi' => $item->deskripsi,
|
||||
];
|
||||
});
|
||||
|
||||
$dendaAlert = collect($personalNotif)->where('type', 'denda_active');
|
||||
// Dynamic notifications based on loans
|
||||
$personalNotif = collect();
|
||||
foreach ($bukuPinjamOffline as $buku) {
|
||||
if ($buku['sisa_hari'] < 0) {
|
||||
$hariTelat = abs($buku['sisa_hari']);
|
||||
$denda = $hariTelat * 1000;
|
||||
$personalNotif->push([
|
||||
'icon' => 'bi-exclamation-octagon-fill',
|
||||
'color' => 'danger',
|
||||
'title' => 'TERLAMBAT: ' . $buku['judul'],
|
||||
'content' => "Telat {$hariTelat} hari. Denda: Rp " . number_format($denda, 0, ',', '.'),
|
||||
'time' => 'Sekarang',
|
||||
'read' => false,
|
||||
'type' => 'denda_active',
|
||||
]);
|
||||
} elseif ($buku['sisa_hari'] <= 3) {
|
||||
$personalNotif->push([
|
||||
'icon' => 'bi-exclamation-triangle-fill',
|
||||
'color' => 'warning',
|
||||
'title' => 'Jatuh Tempo: ' . $buku['judul'],
|
||||
'content' => "Sisa waktu tinggal " . $buku['sisa_hari'] . " hari lagi.",
|
||||
'time' => 'Segera',
|
||||
'read' => false,
|
||||
'type' => 'warning_jatuh_tempo',
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
$personalNotif->push([
|
||||
'icon' => 'bi-info-circle-fill',
|
||||
'color' => 'primary',
|
||||
'title' => 'Selamat Datang!',
|
||||
'content' => 'Jelajahi koleksi buku terbaru kami.',
|
||||
'time' => 'Baru saja',
|
||||
'read' => true,
|
||||
'type' => 'info',
|
||||
]);
|
||||
|
||||
$dendaAlert = $personalNotif->where('type', 'denda_active');
|
||||
|
||||
$hour = date('H');
|
||||
$greeting = "Selamat Pagi";
|
||||
if ($hour >= 12 && $hour < 15)
|
||||
$greeting = "Selamat Siang";
|
||||
elseif ($hour >= 15 && $hour < 18)
|
||||
$greeting = "Selamat Sore";
|
||||
elseif ($hour >= 18)
|
||||
$greeting = "Selamat Malam";
|
||||
if ($hour >= 12 && $hour < 15) $greeting = "Selamat Siang";
|
||||
elseif ($hour >= 15 && $hour < 18) $greeting = "Selamat Sore";
|
||||
elseif ($hour >= 18) $greeting = "Selamat Malam";
|
||||
|
||||
return view('dashboard', compact(
|
||||
'user',
|
||||
|
|
@ -70,10 +146,7 @@ public function index()
|
|||
'rekomendasiPembelajaran'
|
||||
))->with('notifikasi', $personalNotif);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function untuk mengekstrak ID video dari URL YouTube.
|
||||
*/
|
||||
|
||||
private function extractYouTubeId(string $url): ?string
|
||||
{
|
||||
preg_match('/(v=|vi=|youtu.be\/|embed\/|\/v\/|\?v=|\&v=)(.+?)\b/i', $url, $matches);
|
||||
|
|
|
|||
|
|
@ -3,16 +3,74 @@
|
|||
namespace App\Http\Controllers\Guru;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Services\DummyDataService;
|
||||
use App\Models\Book;
|
||||
use App\Models\Category;
|
||||
use App\Models\Loan;
|
||||
use App\Models\User;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class LaporanController extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
$laporan = DummyDataService::getLaporanMinatBaca();
|
||||
$siswaTeraktif = DummyDataService::getSiswaTeraktif();
|
||||
$aktivitasMingguan = DummyDataService::getAktivitasMingguan();
|
||||
// 1. Laporan Minat Baca (Buku Terpopuler & Kategori Populer)
|
||||
$bukuTerpopuler = Book::withCount('loans')
|
||||
->orderBy('loans_count', 'desc')
|
||||
->take(3)
|
||||
->get()
|
||||
->map(fn($book) => [
|
||||
'judul' => $book->judul,
|
||||
'penulis' => $book->penulis,
|
||||
'total_pembaca' => $book->loans_count,
|
||||
'cover' => $book->cover,
|
||||
]);
|
||||
|
||||
$kategoriPopuler = Category::withCount(['books as total_pembaca' => function ($query) {
|
||||
$query->join('loans', 'books.id', '=', 'loans.book_id');
|
||||
}])
|
||||
->orderBy('total_pembaca', 'desc')
|
||||
->take(4)
|
||||
->get()
|
||||
->map(fn($cat) => [
|
||||
'nama' => $cat->name,
|
||||
'total_pembaca' => $cat->total_pembaca,
|
||||
'trend' => 'naik', // Placeholder
|
||||
'icon' => 'bi-arrow-up-right',
|
||||
]);
|
||||
|
||||
$laporan = [
|
||||
'buku_terpopuler' => $bukuTerpopuler,
|
||||
'kategori_populer' => $kategoriPopuler,
|
||||
'insight' => 'Siswa menunjukkan minat baca yang dinamis. Kategori ' . ($kategoriPopuler->first()['nama'] ?? 'Populer') . ' menjadi favorit saat ini.',
|
||||
];
|
||||
|
||||
// 2. Siswa Teraktif
|
||||
$siswaTeraktif = User::where('role', 'siswa')
|
||||
->withCount('loans')
|
||||
->orderBy('loans_count', 'desc')
|
||||
->take(10)
|
||||
->get()
|
||||
->map(fn($user) => [
|
||||
'nama' => $user->nama_lengkap,
|
||||
'total_buku' => $user->loans_count,
|
||||
'kelas' => $user->kelas ?? 'N/A',
|
||||
]);
|
||||
|
||||
// 3. Aktivitas Mingguan (7 hari terakhir)
|
||||
$labels = [];
|
||||
$data = [];
|
||||
for ($i = 6; $i >= 0; $i--) {
|
||||
$date = Carbon::now()->subDays($i);
|
||||
$labels[] = $date->translatedFormat('l');
|
||||
$data[] = Loan::whereDate('borrowed_at', $date->toDateString())->count();
|
||||
}
|
||||
|
||||
$aktivitasMingguan = [
|
||||
'labels' => $labels,
|
||||
'data' => $data,
|
||||
];
|
||||
|
||||
return view('guru.laporan.index', [
|
||||
'pageTitle' => 'Laporan Minat Baca Siswa',
|
||||
|
|
|
|||
|
|
@ -2,7 +2,10 @@
|
|||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Services\DummyDataService;
|
||||
use App\Models\Book;
|
||||
use App\Models\Category;
|
||||
use App\Models\Loan;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
|
|
@ -14,15 +17,46 @@ public function index(Request $request)
|
|||
|
||||
$isBanned = false;
|
||||
if ($user && $user->role === 'siswa') {
|
||||
$bukuPinjam = DummyDataService::getBukuPinjamOffline($user);
|
||||
$isTelat = collect($bukuPinjam)->contains(fn($b) => $b['sisa_hari'] < 0);
|
||||
$isBannedManual = $user->is_banned ?? false;
|
||||
$isBanned = $isTelat || $isBannedManual;
|
||||
// Check for overdue loans
|
||||
$hasOverdue = Loan::where('user_id', $user->id)
|
||||
->whereIn('status', ['Dipinjam', 'Terlambat'])
|
||||
->where('due_at', '<', now())
|
||||
->exists();
|
||||
|
||||
$isBanned = $hasOverdue || ($user->is_banned ?? false);
|
||||
}
|
||||
|
||||
$filters = $request->only(['search', 'kategori', 'tahun', 'penulis']);
|
||||
$semuaBuku = DummyDataService::getKatalogBuku($filters);
|
||||
$filterOptions = DummyDataService::getFilterOptions();
|
||||
|
||||
// Query books with filters
|
||||
$query = Book::with('category');
|
||||
|
||||
if ($request->filled('search')) {
|
||||
$query->where('judul', 'like', '%' . $request->search . '%');
|
||||
}
|
||||
|
||||
if ($request->filled('kategori')) {
|
||||
$query->whereHas('category', function($q) use ($request) {
|
||||
$q->where('name', $request->kategori);
|
||||
});
|
||||
}
|
||||
|
||||
if ($request->filled('tahun')) {
|
||||
$query->where('tahun', $request->tahun);
|
||||
}
|
||||
|
||||
if ($request->filled('penulis')) {
|
||||
$query->where('penulis', $request->penulis);
|
||||
}
|
||||
|
||||
$semuaBuku = $query->latest()->get();
|
||||
|
||||
// Get filter options dynamically
|
||||
$filterOptions = [
|
||||
'kategori' => Category::pluck('name')->unique()->sort()->values(),
|
||||
'tahun' => Book::pluck('tahun')->unique()->sortDesc()->values(),
|
||||
'penulis' => Book::pluck('penulis')->unique()->sort()->values(),
|
||||
];
|
||||
|
||||
return view('katalog.index', [
|
||||
'semuaBuku' => $semuaBuku,
|
||||
|
|
|
|||
|
|
@ -2,30 +2,60 @@
|
|||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Services\DummyDataService;
|
||||
use App\Models\Book;
|
||||
use App\Models\Category;
|
||||
use App\Models\Loan;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class PeminjamanController extends Controller
|
||||
{
|
||||
public function index(Request $request)
|
||||
{
|
||||
$user = \Illuminate\Support\Facades\Auth::user();
|
||||
$bukuPinjam = \App\Services\DummyDataService::getBukuPinjamOffline($user);
|
||||
$isTelat = collect($bukuPinjam)->contains(function ($buku) {
|
||||
return $buku['sisa_hari'] < 0;
|
||||
});
|
||||
$user = Auth::user();
|
||||
|
||||
$isBannedManual = $user->is_banned ?? false;
|
||||
// Check for overdue loans
|
||||
$hasOverdue = Loan::where('user_id', $user->id)
|
||||
->whereIn('status', ['Dipinjam', 'Terlambat'])
|
||||
->where('due_at', '<', now())
|
||||
->exists();
|
||||
|
||||
if (($isTelat || $isBannedManual) && $user->role === 'siswa') {
|
||||
if (($hasOverdue || $user->is_banned) && $user->role === 'siswa') {
|
||||
return redirect()->route('dashboard')->with('error', 'AKSES DITOLAK: Akun Anda sedang dibekukan karena ada buku terlambat!');
|
||||
}
|
||||
|
||||
$filters = $request->only(['search', 'kategori', 'tahun', 'penulis']);
|
||||
$filters['tipe_akses'] = 'offline';
|
||||
$semuaBuku = DummyDataService::getKatalogBuku($filters);
|
||||
$filterOptions = DummyDataService::getFilterOptions();
|
||||
|
||||
$query = Book::with('category')->whereJsonContains('tipe_akses', 'offline');
|
||||
|
||||
if ($request->filled('search')) {
|
||||
$query->where('judul', 'like', '%' . $request->search . '%');
|
||||
}
|
||||
|
||||
if ($request->filled('kategori')) {
|
||||
$query->whereHas('category', function($q) use ($request) {
|
||||
$q->where('name', $request->kategori);
|
||||
});
|
||||
}
|
||||
|
||||
if ($request->filled('tahun')) {
|
||||
$query->where('tahun', $request->tahun);
|
||||
}
|
||||
|
||||
if ($request->filled('penulis')) {
|
||||
$query->where('penulis', $request->penulis);
|
||||
}
|
||||
|
||||
$semuaBuku = $query->latest()->get();
|
||||
|
||||
$filterOptions = [
|
||||
'kategori' => Category::pluck('name')->unique()->sort()->values(),
|
||||
'tahun' => Book::pluck('tahun')->unique()->sortDesc()->values(),
|
||||
'penulis' => Book::pluck('penulis')->unique()->sort()->values(),
|
||||
];
|
||||
|
||||
return view('katalog.index', [
|
||||
'semuaBuku' => $semuaBuku,
|
||||
|
|
@ -39,7 +69,7 @@ public function index(Request $request)
|
|||
public function ringkasan($id)
|
||||
{
|
||||
$user = Auth::user();
|
||||
$buku = DummyDataService::getKatalogBuku()->firstWhere('id', $id);
|
||||
$buku = Book::with('category')->findOrFail($id);
|
||||
|
||||
return view('katalog.ringkasan', [
|
||||
'user' => $user,
|
||||
|
|
@ -52,13 +82,14 @@ public function ringkasan($id)
|
|||
]);
|
||||
}
|
||||
|
||||
|
||||
public function form($id)
|
||||
{
|
||||
$user = Auth::user();
|
||||
$buku = DummyDataService::getKatalogBuku()->firstWhere('id', $id);
|
||||
$filters = ['tipe_akses' => 'offline'];
|
||||
$semuaBuku = DummyDataService::getKatalogBuku($filters);
|
||||
$buku = Book::with('category')->findOrFail($id);
|
||||
|
||||
$semuaBuku = Book::whereJsonContains('tipe_akses', 'offline')
|
||||
->where('status', 'Tersedia')
|
||||
->get();
|
||||
|
||||
return view('peminjaman.form', compact('user', 'buku', 'semuaBuku'));
|
||||
}
|
||||
|
|
@ -67,21 +98,31 @@ public function store(Request $request)
|
|||
{
|
||||
$request->validate([
|
||||
'buku_ids' => 'required|array|min:1|max:3',
|
||||
'buku_ids.*' => 'integer'
|
||||
'buku_ids.*' => 'exists:books,id'
|
||||
]);
|
||||
|
||||
$bukuIds = $request->input('buku_ids');
|
||||
|
||||
foreach ($bukuIds as $bukuId) {
|
||||
// Di backend nanti kayak gini
|
||||
// Peminjaman::create([
|
||||
// 'user_id' => auth()->id(),
|
||||
// 'book_id' => $bukuId,
|
||||
// 'tanggal_pinjam' => now(),
|
||||
// 'tanggal_kembali' => now()->addDays(7),
|
||||
// 'status' => 'dipinjam'
|
||||
// ]);
|
||||
}
|
||||
DB::transaction(function () use ($bukuIds) {
|
||||
foreach ($bukuIds as $bukuId) {
|
||||
$book = Book::lockForUpdate()->find($bukuId);
|
||||
|
||||
if ($book->status !== 'Tersedia') {
|
||||
throw new \Exception("Buku '{$book->judul}' sudah tidak tersedia.");
|
||||
}
|
||||
|
||||
Loan::create([
|
||||
'user_id' => Auth::id(),
|
||||
'book_id' => $bukuId,
|
||||
'loan_code' => 'PIN-' . date('Ym') . '-' . strtoupper(Str::random(4)) . '-' . $bukuId,
|
||||
'borrowed_at' => now(),
|
||||
'due_at' => now()->addDays(7),
|
||||
'status' => 'Dipinjam',
|
||||
]);
|
||||
|
||||
$book->update(['status' => 'Dipinjam']);
|
||||
}
|
||||
});
|
||||
|
||||
return redirect()->route('dashboard')
|
||||
->with('success', 'Berhasil meminjam ' . count($bukuIds) . ' buku!');
|
||||
|
|
|
|||
|
|
@ -3,12 +3,15 @@
|
|||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Http\Requests\ProfileUpdateRequest;
|
||||
use App\Models\Book;
|
||||
use App\Models\Loan;
|
||||
use App\Models\User;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Redirect;
|
||||
use Illuminate\View\View;
|
||||
use App\Services\DummyDataService;
|
||||
|
||||
class ProfileController extends Controller
|
||||
{
|
||||
|
|
@ -19,7 +22,7 @@ public function index(): RedirectResponse|View
|
|||
{
|
||||
$user = Auth::user();
|
||||
if (!$user) {
|
||||
return redirect()->route(route: 'login');
|
||||
return redirect()->route('login');
|
||||
}
|
||||
|
||||
$viewData = ['user' => $user];
|
||||
|
|
@ -27,20 +30,68 @@ public function index(): RedirectResponse|View
|
|||
// Menyiapkan data berdasarkan role pengguna
|
||||
if ($user->role === 'penjaga perpus') {
|
||||
// Data untuk Penjaga Perpus: Statistik global & aktivitas terkini
|
||||
$viewData['statistik'] = DummyDataService::getAdminDashboardStats();
|
||||
$viewData['aktivitasTerakhir'] = DummyDataService::getAktivitasTerakhir();
|
||||
$viewData['statistik'] = [
|
||||
['label' => 'Total Buku', 'value' => Book::count(), 'icon' => 'bi-journal-bookmark-fill', 'color' => 'primary'],
|
||||
['label' => 'Total Anggota', 'value' => User::count(), 'icon' => 'bi-people-fill', 'color' => 'success'],
|
||||
['label' => 'Buku Dipinjam', 'value' => Loan::whereIn('status', ['Dipinjam', 'Terlambat'])->count(), 'icon' => 'bi-arrow-up-right-circle-fill', 'color' => 'warning'],
|
||||
['label' => 'Denda Menunggu', 'value' => Loan::where('status', 'Terlambat')->count(), 'icon' => 'bi-cash-coin', 'color' => 'danger'],
|
||||
];
|
||||
$viewData['aktivitasTerakhir'] = Loan::with(['user', 'book'])->latest()->take(4)->get()->map(fn($loan) => [
|
||||
'nama' => $loan->user->nama_lengkap ?? 'Unknown',
|
||||
'judul_buku' => $loan->book->judul ?? 'Unknown',
|
||||
'tipe' => $loan->status === 'Dikembalikan' ? 'Pengembalian' : 'Peminjaman',
|
||||
'waktu' => $loan->created_at->diffForHumans(),
|
||||
'status' => $loan->status,
|
||||
]);
|
||||
|
||||
} elseif ($user->role === 'guru') {
|
||||
// Data untuk Guru: Data personal + ringkasan laporan minat baca
|
||||
$viewData['bukuOffline'] = DummyDataService::getBukuPinjamOffline($user);
|
||||
$viewData['bukuOnline'] = DummyDataService::getBacaBukuOnline($user);
|
||||
$viewData['laporan'] = DummyDataService::getLaporanMinatBaca();
|
||||
// Data untuk Guru
|
||||
$loans = Loan::with('book')->where('user_id', $user->id)->whereIn('status', ['Dipinjam', 'Terlambat'])->get();
|
||||
|
||||
$viewData['bukuOffline'] = $loans->map(fn($loan) => [
|
||||
'judul' => $loan->book->judul,
|
||||
'penulis' => $loan->book->penulis,
|
||||
'sisa_hari' => (int) now()->diffInDays(Carbon::parse($loan->due_at), false),
|
||||
'cover' => $loan->book->cover,
|
||||
]);
|
||||
|
||||
$viewData['bukuOnline'] = $loans->filter(fn($loan) => in_array('online', $loan->book->tipe_akses ?? []))->map(fn($loan) => [
|
||||
'judul' => $loan->book->judul,
|
||||
'penulis' => $loan->book->penulis,
|
||||
'progress' => 0,
|
||||
'cover' => $loan->book->cover,
|
||||
]);
|
||||
|
||||
// Analytics for Guru (simplified for profile view)
|
||||
$viewData['laporan'] = [
|
||||
'buku_terpopuler' => Book::withCount('loans')->orderBy('loans_count', 'desc')->take(3)->get(),
|
||||
'insight' => 'Siswa aktif meminjam buku kategori Sains.'
|
||||
];
|
||||
|
||||
} else {
|
||||
// Data default untuk Siswa
|
||||
$viewData['bukuOffline'] = DummyDataService::getBukuPinjamOffline($user);
|
||||
$viewData['bukuOnline'] = DummyDataService::getBacaBukuOnline($user);
|
||||
$viewData['statistik'] = DummyDataService::getDashboardStats();
|
||||
// Data untuk Siswa
|
||||
$loans = Loan::with('book')->where('user_id', $user->id)->whereIn('status', ['Dipinjam', 'Terlambat'])->get();
|
||||
|
||||
$viewData['bukuOffline'] = $loans->map(fn($loan) => [
|
||||
'judul' => $loan->book->judul,
|
||||
'penulis' => $loan->book->penulis,
|
||||
'sisa_hari' => (int) now()->diffInDays(Carbon::parse($loan->due_at), false),
|
||||
'cover' => $loan->book->cover,
|
||||
]);
|
||||
|
||||
$viewData['bukuOnline'] = $loans->filter(fn($loan) => in_array('online', $loan->book->tipe_akses ?? []))->map(fn($loan) => [
|
||||
'judul' => $loan->book->judul,
|
||||
'penulis' => $loan->book->penulis,
|
||||
'progress' => 0,
|
||||
'cover' => $loan->book->cover,
|
||||
]);
|
||||
|
||||
$viewData['statistik'] = [
|
||||
['label' => 'Buku dipinjam', 'value' => $loans->count(), 'icon' => 'bi-book-half', 'color' => 'primary'],
|
||||
['label' => 'Tenggat Waktu', 'value' => $viewData['bukuOffline']->where('sisa_hari', '<=', 3)->where('sisa_hari', '>=', 0)->count(), 'icon' => 'bi-clock-history', 'color' => 'danger'],
|
||||
['label' => 'Buku dikembalikan', 'value' => Loan::where('user_id', $user->id)->where('status', 'Dikembalikan')->count(), 'icon' => 'bi-check-circle', 'color' => 'success'],
|
||||
['label' => 'History Baca', 'value' => Loan::where('user_id', $user->id)->count(), 'icon' => 'bi-hourglass-split', 'color' => 'warning'],
|
||||
];
|
||||
}
|
||||
|
||||
return view('profile.index', $viewData);
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Services\DummyDataService;
|
||||
use App\Models\Recommendation;
|
||||
|
||||
class RekomendasiController extends Controller
|
||||
{
|
||||
|
|
@ -17,20 +17,20 @@ private function extractYouTubeId(string $url): ?string
|
|||
|
||||
public function show($id)
|
||||
{
|
||||
$rekomendasi = DummyDataService::getRekomendasiPembelajaran()->firstWhere('id', (int)$id);
|
||||
abort_if(!$rekomendasi, 404);
|
||||
$rekomendasi = Recommendation::findOrFail($id);
|
||||
|
||||
// Menambahkan thumbnail YouTube ke setiap rekomendasi
|
||||
$embedLink = null;
|
||||
$videoId = $this->extractYouTubeId($rekomendasi['youtube_link']);
|
||||
$videoId = $this->extractYouTubeId($rekomendasi->youtube_link);
|
||||
if ($videoId) {
|
||||
$embedLink = "https://www.youtube.com/embed/" . $videoId;
|
||||
}
|
||||
$rekomendasi['youtube_embed_link'] = $embedLink;
|
||||
|
||||
$data = $rekomendasi->toArray();
|
||||
$data['youtube_embed_link'] = $embedLink;
|
||||
|
||||
return view('rekomendasiShow', [
|
||||
'pageTitle' => $rekomendasi['judul'],
|
||||
'rekomendasi' => $rekomendasi,
|
||||
'pageTitle' => $rekomendasi->judul,
|
||||
'rekomendasi' => $data,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
@ -2,18 +2,43 @@
|
|||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Services\DummyDataService;
|
||||
use App\Models\Loan;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth; // Tambahkan ini
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
class RiwayatController extends Controller
|
||||
{
|
||||
public function offlineIndex()
|
||||
{
|
||||
$user = \Illuminate\Support\Facades\Auth::user();
|
||||
if (!$user) $user = (object) ['id' => 1];
|
||||
$user = Auth::user();
|
||||
|
||||
$loans = Loan::with('book.category')
|
||||
->where('user_id', $user->id)
|
||||
->whereIn('status', ['Dipinjam', 'Dikembalikan', 'Terlambat'])
|
||||
->latest()
|
||||
->get();
|
||||
|
||||
$riwayatOffline = DummyDataService::getRiwayatOffline($user);
|
||||
$riwayatOffline = $loans->map(fn($loan) => [
|
||||
'id' => $loan->id,
|
||||
'id_peminjaman' => $loan->loan_code,
|
||||
'kode_buku' => $loan->book->kode_buku,
|
||||
'judul_utama' => $loan->book->judul,
|
||||
'tanggal_pinjam' => $loan->borrowed_at->format('d/m/Y'),
|
||||
'tanggal_kembali' => $loan->due_at ? $loan->due_at->format('d/m/Y') : '-',
|
||||
'status' => $loan->status,
|
||||
'books' => [
|
||||
[
|
||||
'id' => $loan->book->id,
|
||||
'judul' => $loan->book->judul,
|
||||
'kode_buku' => $loan->book->kode_buku,
|
||||
'cover' => $loan->book->cover,
|
||||
'deskripsi' => 'Buku ' . $loan->book->judul,
|
||||
'kategori' => $loan->book->category->name ?? 'Tanpa Kategori',
|
||||
'tahun' => $loan->book->tahun,
|
||||
'keterangan' => $loan->status === 'Terlambat' ? 'Buku Terlambat' : null,
|
||||
]
|
||||
]
|
||||
]);
|
||||
|
||||
return view('riwayat.offline', [
|
||||
'pageTitle' => 'Riwayat Peminjaman Offline',
|
||||
|
|
@ -23,7 +48,32 @@ public function offlineIndex()
|
|||
|
||||
public function onlineIndex()
|
||||
{
|
||||
$riwayatOnline = DummyDataService::getRiwayatOnline();
|
||||
$user = Auth::user();
|
||||
|
||||
$loans = Loan::with('book.category')
|
||||
->where('user_id', $user->id)
|
||||
->where('status', 'Online')
|
||||
->latest()
|
||||
->get();
|
||||
|
||||
$riwayatOnline = $loans->map(fn($loan) => [
|
||||
'id' => $loan->id,
|
||||
'id_baca' => $loan->loan_code,
|
||||
'judul_buku' => $loan->book->judul,
|
||||
'tanggal_akses' => $loan->borrowed_at->format('d/m/Y'),
|
||||
'status' => 'Selesai',
|
||||
'books' => [
|
||||
[
|
||||
'id' => $loan->book->id,
|
||||
'judul' => $loan->book->judul,
|
||||
'cover' => $loan->book->cover,
|
||||
'deskripsi' => 'Buku ' . $loan->book->judul,
|
||||
'kategori' => $loan->book->category->name ?? 'Tanpa Kategori',
|
||||
'tahun' => $loan->book->tahun,
|
||||
'keterangan' => null
|
||||
]
|
||||
]
|
||||
]);
|
||||
|
||||
return view('riwayat.online', [
|
||||
'pageTitle' => 'Riwayat Baca Online',
|
||||
|
|
|
|||
|
|
@ -52,64 +52,50 @@ public function rules(): array
|
|||
*/
|
||||
public function authenticate(): void
|
||||
{
|
||||
// Pastikan pengguna tidak mencoba login terlalu sering (mencegah brute-force).
|
||||
$this->ensureIsNotRateLimited();
|
||||
|
||||
// Ambil data yang dikirim dari form login.
|
||||
$roleDariForm = $this->input('role');
|
||||
$allUsers = DummyDataService::getAllSiswa();
|
||||
$inputPassword = $this->input('password');
|
||||
$userArray = null;
|
||||
|
||||
// Tentukan field mana yang akan menerima pesan error jika gagal (nisn atau email).
|
||||
$loginIdentifier = $this->input('nisn') ?: $this->input('nip');
|
||||
$password = $this->input('password');
|
||||
|
||||
$errorField = $this->filled('nisn') ? 'nisn' : 'nip';
|
||||
|
||||
// Cari data pengguna berdasarkan input yang diberikan.
|
||||
if ($this->filled('nisn')) {
|
||||
// Jika form diisi dengan 'nisn', cari pengguna berdasarkan 'nisn'.
|
||||
$userArray = collect($allUsers)->firstWhere('nisn', $this->input('nisn'));
|
||||
} else {
|
||||
// Jika tidak, cari pengguna berdasarkan 'nip'.
|
||||
$userArray = collect($allUsers)->firstWhere('nip', $this->input('nip'));
|
||||
if (!Auth::attempt(['nomor_induk' => $loginIdentifier, 'password' => $password], $this->boolean('remember'))) {
|
||||
RateLimiter::hit($this->throttleKey());
|
||||
|
||||
throw ValidationException::withMessages([
|
||||
$errorField => trans('auth.failed'),
|
||||
]);
|
||||
}
|
||||
|
||||
// Lakukan Pengecekan Kredensial dan Role.
|
||||
if ($userArray && $userArray['password'] === $inputPassword) {
|
||||
|
||||
// Cek #2: Jika kredensial benar, apakah role pengguna sesuai dengan form yang digunakan?
|
||||
if (isset($userArray['role']) && $userArray['role'] === $roleDariForm) {
|
||||
|
||||
// Buat objek User dari data dummy.
|
||||
$userModel = new User();
|
||||
$userArray['name'] = $userArray['nama_lengkap'];
|
||||
$userModel->forceFill($userArray);
|
||||
$user = Auth::user();
|
||||
|
||||
// Loginkan pengguna secara resmi ke dalam sistem.
|
||||
Auth::login($userModel);
|
||||
// Cek jika role sesuai
|
||||
if ($user->role !== $roleDariForm) {
|
||||
Auth::logout();
|
||||
$this->session()->invalidate();
|
||||
$this->session()->regenerateToken();
|
||||
|
||||
// Reset hitungan percobaan login yang gagal.
|
||||
RateLimiter::clear($this->throttleKey());
|
||||
return; // Proses autentikasi berhasil.
|
||||
RateLimiter::hit($this->throttleKey());
|
||||
|
||||
} else {
|
||||
// Tambah hitungan percobaan login yang gagal.
|
||||
RateLimiter::hit($this->throttleKey());
|
||||
|
||||
// Ambil nama role asli pengguna untuk ditampilkan di pesan error.
|
||||
$actualRole = Str::title($userArray['role'] ?? 'Tidak Dikenal');
|
||||
|
||||
// Lemparkan error validasi khusus 'forbidden' dengan pesan yang jelas.
|
||||
throw ValidationException::withMessages([
|
||||
'forbidden' => "Akses ditolak. Akun ini terdaftar sebagai {$actualRole}.",
|
||||
]);
|
||||
}
|
||||
$actualRole = Str::title($user->role ?? 'Tidak Dikenal');
|
||||
throw ValidationException::withMessages([
|
||||
'forbidden' => "Akses ditolak. Akun ini terdaftar sebagai {$actualRole}.",
|
||||
]);
|
||||
}
|
||||
|
||||
// Jika pengguna tidak ditemukan atau password salah.
|
||||
RateLimiter::hit($this->throttleKey());
|
||||
throw ValidationException::withMessages([
|
||||
$errorField => trans('auth.failed'), // Pesan error umum "These credentials do not match...".
|
||||
]);
|
||||
// Cek jika akun di-banned
|
||||
if ($user->is_banned) {
|
||||
Auth::logout();
|
||||
$this->session()->invalidate();
|
||||
$this->session()->regenerateToken();
|
||||
|
||||
throw ValidationException::withMessages([
|
||||
'forbidden' => "Akun Anda telah dinonaktifkan. Silakan hubungi admin.",
|
||||
]);
|
||||
}
|
||||
|
||||
RateLimiter::clear($this->throttleKey());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Announcement extends Model
|
||||
{
|
||||
protected $fillable = ['type', 'icon', 'title', 'content'];
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Book extends Model
|
||||
{
|
||||
protected $fillable = [
|
||||
'judul', 'penulis', 'cover', 'kode_buku',
|
||||
'category_id', 'tahun', 'status', 'is_new', 'tipe_akses'
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'tipe_akses' => 'array',
|
||||
'is_new' => 'boolean',
|
||||
];
|
||||
|
||||
public function category()
|
||||
{
|
||||
return $this->belongsTo(Category::class);
|
||||
}
|
||||
|
||||
public function loans()
|
||||
{
|
||||
return $this->hasMany(Loan::class);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Category extends Model
|
||||
{
|
||||
protected $fillable = ['name', 'slug'];
|
||||
|
||||
public function books()
|
||||
{
|
||||
return $this->hasMany(Book::class);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Loan extends Model
|
||||
{
|
||||
protected $fillable = [
|
||||
'user_id', 'book_id', 'loan_code',
|
||||
'borrowed_at', 'due_at', 'returned_at', 'status'
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'borrowed_at' => 'datetime',
|
||||
'due_at' => 'datetime',
|
||||
'returned_at' => 'datetime',
|
||||
];
|
||||
|
||||
public function user()
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
}
|
||||
|
||||
public function book()
|
||||
{
|
||||
return $this->belongsTo(Book::class);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Recommendation extends Model
|
||||
{
|
||||
protected $fillable = ['judul', 'kategori', 'youtube_link', 'deskripsi'];
|
||||
}
|
||||
|
|
@ -19,8 +19,16 @@ class User extends Authenticatable
|
|||
*/
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'nama_lengkap',
|
||||
'email',
|
||||
'password',
|
||||
'nomor_induk',
|
||||
'phone',
|
||||
'nuptk',
|
||||
'role',
|
||||
'is_banned',
|
||||
'kelas',
|
||||
'golongan',
|
||||
];
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class classes extends Model
|
||||
{
|
||||
//
|
||||
}
|
||||
|
|
@ -2,7 +2,8 @@
|
|||
|
||||
namespace App\Providers;
|
||||
|
||||
use App\Services\DummyDataService;
|
||||
use App\Models\Loan;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\URL;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
|
|
@ -31,7 +32,42 @@ public function boot(): void
|
|||
View::composer('*', function ($view) {
|
||||
if (Auth::check()) {
|
||||
$user = Auth::user();
|
||||
$notifikasi = collect(DummyDataService::getNotifikasiForUser($user));
|
||||
|
||||
// Fetch active loans to derive notifications
|
||||
$loans = Loan::with('book')
|
||||
->where('user_id', $user->id)
|
||||
->whereIn('status', ['Dipinjam', 'Terlambat'])
|
||||
->get();
|
||||
|
||||
$notifikasi = collect();
|
||||
|
||||
foreach ($loans as $loan) {
|
||||
$dueAt = Carbon::parse($loan->due_at);
|
||||
$sisaHari = (int) now()->diffInDays($dueAt, false);
|
||||
|
||||
if ($sisaHari < 0) {
|
||||
$notifikasi->push([
|
||||
'icon' => 'bi-exclamation-octagon-fill',
|
||||
'color' => 'danger',
|
||||
'title' => 'TERLAMBAT: ' . $loan->book->judul,
|
||||
'content' => "Segera kembalikan buku. Denda berlaku.",
|
||||
'time' => 'Sekarang',
|
||||
'read' => false,
|
||||
'type' => 'denda_active',
|
||||
]);
|
||||
} elseif ($sisaHari <= 3) {
|
||||
$notifikasi->push([
|
||||
'icon' => 'bi-exclamation-triangle-fill',
|
||||
'color' => 'warning',
|
||||
'title' => 'Jatuh Tempo: ' . $loan->book->judul,
|
||||
'content' => "Tersisa {$sisaHari} hari lagi.",
|
||||
'time' => 'Segera',
|
||||
'read' => false,
|
||||
'type' => 'warning_jatuh_tempo',
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
$unreadCount = $notifikasi->where('read', false)->count();
|
||||
$view->with('notifikasi', $notifikasi);
|
||||
$view->with('unreadNotificationsCount', $unreadCount);
|
||||
|
|
|
|||
|
|
@ -22,9 +22,6 @@ class AuthServiceProvider extends ServiceProvider
|
|||
*/
|
||||
public function boot(): void
|
||||
{
|
||||
// Daftarkan provider kustom kita di sini
|
||||
Auth::provider('dummy', function ($app, array $config) {
|
||||
return new DummyUserProvider();
|
||||
});
|
||||
//
|
||||
}
|
||||
}
|
||||
|
|
@ -38,7 +38,7 @@
|
|||
'guards' => [
|
||||
'web' => [
|
||||
'driver' => 'session',
|
||||
'provider' => 'dummy',
|
||||
'provider' => 'users',
|
||||
],
|
||||
],
|
||||
|
||||
|
|
@ -64,10 +64,6 @@
|
|||
'driver' => 'eloquent',
|
||||
'model' => env('AUTH_MODEL', App\Models\User::class),
|
||||
],
|
||||
|
||||
'dummy' => [
|
||||
'driver' => 'dummy',
|
||||
],
|
||||
],
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -25,9 +25,12 @@ public function definition(): array
|
|||
{
|
||||
return [
|
||||
'name' => fake()->name(),
|
||||
'nama_lengkap' => fake()->name(),
|
||||
'email' => fake()->unique()->safeEmail(),
|
||||
'email_verified_at' => now(),
|
||||
'password' => static::$password ??= Hash::make('password'),
|
||||
'nomor_induk' => fake()->unique()->numerify('##########'),
|
||||
'role' => 'siswa',
|
||||
'remember_token' => Str::random(10),
|
||||
];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,9 +14,17 @@ public function up(): void
|
|||
Schema::create('users', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('name');
|
||||
$table->string('nama_lengkap')->nullable();
|
||||
$table->string('email')->unique();
|
||||
$table->timestamp('email_verified_at')->nullable();
|
||||
$table->string('password');
|
||||
$table->string('nomor_induk')->unique();
|
||||
$table->string('phone')->nullable();
|
||||
$table->string('nuptk')->nullable();
|
||||
$table->string('role');
|
||||
$table->boolean('is_banned')->default(false);
|
||||
$table->string('kelas')->nullable();
|
||||
$table->string('golongan')->nullable();
|
||||
$table->rememberToken();
|
||||
$table->timestamps();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('classes', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('class');
|
||||
$table->string('class_name');
|
||||
$table->foreignId('id_users')->nullable()->constrained('users')->onDelete('cascade');
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('classes');
|
||||
}
|
||||
};
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('categories', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('name');
|
||||
$table->string('slug')->unique();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('categories');
|
||||
}
|
||||
};
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('books', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('judul');
|
||||
$table->string('penulis');
|
||||
$table->string('cover')->nullable();
|
||||
$table->string('kode_buku')->unique();
|
||||
$table->foreignId('category_id')->constrained('categories')->onDelete('cascade');
|
||||
$table->integer('tahun');
|
||||
$table->string('status')->default('Tersedia');
|
||||
$table->boolean('is_new')->default(false);
|
||||
$table->json('tipe_akses'); // ['online', 'offline']
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('books');
|
||||
}
|
||||
};
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('loans', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('user_id')->constrained('users')->onDelete('cascade');
|
||||
$table->foreignId('book_id')->constrained('books')->onDelete('cascade');
|
||||
$table->string('loan_code')->unique();
|
||||
$table->timestamp('borrowed_at')->useCurrent();
|
||||
$table->timestamp('due_at')->nullable();
|
||||
$table->timestamp('returned_at')->nullable();
|
||||
$table->string('status'); // Dipinjam, Dikembalikan, Terlambat, Online
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('loans');
|
||||
}
|
||||
};
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('announcements', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('type'); // warning, info, success, etc.
|
||||
$table->string('icon');
|
||||
$table->string('title');
|
||||
$table->text('content');
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('announcements');
|
||||
}
|
||||
};
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('recommendations', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('judul');
|
||||
$table->string('kategori');
|
||||
$table->string('youtube_link');
|
||||
$table->text('deskripsi');
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('recommendations');
|
||||
}
|
||||
};
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use Illuminate\Database\Seeder;
|
||||
use App\Models\Announcement;
|
||||
use App\Services\DummyDataService;
|
||||
|
||||
class AnnouncementSeeder extends Seeder
|
||||
{
|
||||
/**
|
||||
* Run the database seeds.
|
||||
*/
|
||||
public function run(): void
|
||||
{
|
||||
$announcements = DummyDataService::getPengumuman();
|
||||
|
||||
foreach ($announcements as $data) {
|
||||
Announcement::updateOrCreate(
|
||||
['id' => $data['id']],
|
||||
[
|
||||
'type' => $data['type'],
|
||||
'icon' => $data['icon'],
|
||||
'title' => $data['title'],
|
||||
'content' => $data['content'],
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
<?php
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use Illuminate\Database\Seeder;
|
||||
use App\Models\Book;
|
||||
use App\Models\Category;
|
||||
use App\Services\DummyDataService;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class BookSeeder extends Seeder
|
||||
{
|
||||
/**
|
||||
* Run the database seeds.
|
||||
*/
|
||||
public function run(): void
|
||||
{
|
||||
$books = DummyDataService::getAllBooks();
|
||||
|
||||
foreach ($books as $data) {
|
||||
$category = Category::where('name', $data['kategori'])->first();
|
||||
|
||||
Book::updateOrCreate(
|
||||
['kode_buku' => $data['kode_buku']],
|
||||
[
|
||||
'id' => $data['id'],
|
||||
'judul' => $data['judul'],
|
||||
'penulis' => $data['penulis'],
|
||||
'cover' => $data['cover'],
|
||||
'category_id' => $category ? $category->id : null,
|
||||
'tahun' => $data['tahun'],
|
||||
'status' => $data['status'],
|
||||
'is_new' => $data['is_new'],
|
||||
'tipe_akses' => is_array($data['tipe_akses']) ? $data['tipe_akses'] : [$data['tipe_akses']],
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use Illuminate\Database\Seeder;
|
||||
use App\Models\Category;
|
||||
use App\Services\DummyDataService;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class CategorySeeder extends Seeder
|
||||
{
|
||||
/**
|
||||
* Run the database seeds.
|
||||
*/
|
||||
public function run(): void
|
||||
{
|
||||
$books = DummyDataService::getAllBooks();
|
||||
$recommendations = DummyDataService::getRekomendasiPembelajaran();
|
||||
|
||||
$categories = collect($books)->pluck('kategori')
|
||||
->merge(collect($recommendations)->pluck('kategori'))
|
||||
->unique()
|
||||
->filter();
|
||||
|
||||
foreach ($categories as $name) {
|
||||
Category::updateOrCreate(
|
||||
['slug' => Str::slug($name)],
|
||||
['name' => $name]
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -13,11 +13,15 @@ class DatabaseSeeder extends Seeder
|
|||
*/
|
||||
public function run(): void
|
||||
{
|
||||
// User::factory(10)->create();
|
||||
|
||||
User::factory()->create([
|
||||
'name' => 'Test User',
|
||||
'email' => 'test@example.com',
|
||||
// User seeder is already handled or skipped as per user request
|
||||
|
||||
$this->call([
|
||||
UserSeeder::class,
|
||||
CategorySeeder::class,
|
||||
BookSeeder::class,
|
||||
RecommendationSeeder::class,
|
||||
AnnouncementSeeder::class,
|
||||
LoanSeeder::class,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,49 @@
|
|||
<?php
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use Illuminate\Database\Seeder;
|
||||
use App\Models\Loan;
|
||||
use App\Models\User;
|
||||
use App\Models\Book;
|
||||
use App\Services\DummyDataService;
|
||||
use Carbon\Carbon;
|
||||
|
||||
class LoanSeeder extends Seeder
|
||||
{
|
||||
/**
|
||||
* Run the database seeds.
|
||||
*/
|
||||
public function run(): void
|
||||
{
|
||||
$books = DummyDataService::getAllBooks();
|
||||
|
||||
foreach ($books as $data) {
|
||||
if ($data['user_id'] && $data['status'] !== 'Tersedia') {
|
||||
// In dummy data, user_id is sometimes an array, handle it
|
||||
$userIds = is_array($data['user_id']) ? $data['user_id'] : [$data['user_id']];
|
||||
|
||||
foreach ($userIds as $uId) {
|
||||
$user = User::find($uId);
|
||||
$book = Book::find($data['id']);
|
||||
|
||||
if ($user && $book) {
|
||||
Loan::updateOrCreate(
|
||||
[
|
||||
'user_id' => $user->id,
|
||||
'book_id' => $book->id,
|
||||
'status' => $data['status'],
|
||||
],
|
||||
[
|
||||
'loan_code' => 'PIN-' . date('Ym') . '-' . sprintf('%03d', $book->id),
|
||||
'borrowed_at' => Carbon::now()->subDays(7),
|
||||
'due_at' => Carbon::now()->addDays($data['sisa_hari'] ?? 7),
|
||||
'status' => $data['status'],
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use Illuminate\Database\Seeder;
|
||||
use App\Models\Recommendation;
|
||||
use App\Services\DummyDataService;
|
||||
|
||||
class RecommendationSeeder extends Seeder
|
||||
{
|
||||
/**
|
||||
* Run the database seeds.
|
||||
*/
|
||||
public function run(): void
|
||||
{
|
||||
$recommendations = DummyDataService::getRekomendasiPembelajaran();
|
||||
|
||||
foreach ($recommendations as $data) {
|
||||
Recommendation::updateOrCreate(
|
||||
['id' => $data['id']],
|
||||
[
|
||||
'judul' => $data['judul'],
|
||||
'kategori' => $data['kategori'],
|
||||
'youtube_link' => $data['youtube_link'],
|
||||
'deskripsi' => $data['deskripsi'],
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
<?php
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use Illuminate\Database\Seeder;
|
||||
use App\Models\User;
|
||||
use App\Services\DummyDataService;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
|
||||
class UserSeeder extends Seeder
|
||||
{
|
||||
/**
|
||||
* Run the database seeds.
|
||||
*/
|
||||
public function run(): void
|
||||
{
|
||||
$allSiswa = DummyDataService::getAllSiswa();
|
||||
|
||||
foreach ($allSiswa as $data) {
|
||||
$nomorInduk = $data['nisn'] ?? $data['nip'] ?? null;
|
||||
|
||||
if (!$nomorInduk) continue;
|
||||
|
||||
User::updateOrCreate(
|
||||
['nomor_induk' => $nomorInduk],
|
||||
[
|
||||
'id' => $data['id'],
|
||||
'name' => $data['nama_lengkap'],
|
||||
'nama_lengkap' => $data['nama_lengkap'],
|
||||
'email' => $data['email'],
|
||||
'password' => Hash::make($data['password']),
|
||||
'phone' => $data['nomor_hp'] ?? null,
|
||||
'role' => $data['role'],
|
||||
'is_banned' => $data['is_banned'] ?? false,
|
||||
'kelas' => $data['kelas'] ?? null,
|
||||
'golongan' => $data['golongan'] ?? null,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
// Default admin
|
||||
User::updateOrCreate(
|
||||
['nomor_induk' => 'admin'],
|
||||
[
|
||||
'name' => 'Admin Perpustakaan',
|
||||
'nama_lengkap' => 'Admin Perpustakaan',
|
||||
'email' => 'admin@smkn1perpus.sch.id',
|
||||
'password' => Hash::make('password'),
|
||||
'role' => 'penjaga perpus',
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -37,7 +37,7 @@
|
|||
|
||||
// --- RUTE UNTUK PENGGUNA TERAUTENTIKASI (SISWA, GURU, & PENJAGA PERPUS) ---
|
||||
Route::middleware(['auth'])->group(function () {
|
||||
|
||||
|
||||
// Rute Umum untuk Siswa & Guru
|
||||
Route::get('/dashboard', [DashboardController::class, 'index'])->name('dashboard');
|
||||
Route::get('/katalog', [KatalogController::class, 'index'])->name('katalog.index');
|
||||
|
|
@ -80,11 +80,11 @@
|
|||
// --- GRUP RUTE KHUSUS UNTUK ADMIN / PENJAGA PERPUSTAKAAN ---
|
||||
Route::middleware(['auth', 'role:penjaga perpus'])->prefix('admin')->name('admin.')->group(function () {
|
||||
Route::get('/dashboard', [AdminDashboardController::class, 'index'])->name('dashboard');
|
||||
|
||||
|
||||
Route::get('/buku', [AdminBookController::class, 'index'])->name('buku.index');
|
||||
Route::get('/buku/tambah', [AdminBookController::class, 'create'])->name('buku.create');
|
||||
Route::get('/buku/{id}/edit', [AdminBookController::class, 'edit'])->name('buku.edit');
|
||||
|
||||
|
||||
Route::get('/pengguna', [AdminUserController::class, 'index'])->name('pengguna.index');
|
||||
Route::get('/pengguna/tambah', [AdminUserController::class, 'create'])->name('pengguna.create');
|
||||
Route::get('/pengguna/{id}/edit', [AdminUserController::class, 'edit'])->name('pengguna.edit');
|
||||
|
|
@ -98,9 +98,9 @@
|
|||
Route::get('/rekomendasi/tambah', [AdminRekomendasiController::class, 'create'])->name('rekomendasi.create');
|
||||
Route::get('/rekomendasi/{id}/edit', [AdminRekomendasiController::class, 'edit'])->name('rekomendasi.edit');
|
||||
|
||||
Route::get('/peminjaman', [AdminPeminjamanController::class, 'index'])->name('peminjaman.index');
|
||||
Route::get('/peminjaman/tambah', [AdminPeminjamanController::class, 'create'])->name('peminjaman.create');
|
||||
|
||||
Route::get('/peminjaman', [AdminPeminjamanController::class, 'index'])->name('peminjaman.index');
|
||||
Route::get('/peminjaman/tambah', [AdminPeminjamanController::class, 'create'])->name('peminjaman.create');
|
||||
|
||||
Route::get('/denda', [AdminPeminjamanController::class, 'dendaIndex'])->name('denda.index');
|
||||
Route::post('/denda/sanksi', [AdminPeminjamanController::class, 'berikanSanksi'])->name('denda.sanksi');
|
||||
});
|
||||
|
|
@ -111,4 +111,4 @@
|
|||
Route::post('/admin/login', [AdminLoginController::class, 'store'])->name('admin.login.store');
|
||||
});
|
||||
|
||||
require __DIR__ . '/auth.php';
|
||||
require __DIR__ . '/auth.php';
|
||||
|
|
|
|||
Loading…
Reference in New Issue