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;
|
namespace App\Http\Controllers\Admin;
|
||||||
|
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
use App\Services\DummyDataService;
|
use App\Models\Book;
|
||||||
use Illuminate\Http\Request;
|
use App\Models\Loan;
|
||||||
|
use App\Models\User;
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
class AdminPeminjamanController extends Controller
|
class AdminPeminjamanController extends Controller
|
||||||
{
|
{
|
||||||
public function index(Request $request)
|
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)
|
$groupedLoans = $loans->groupBy('user_id');
|
||||||
$peminjamanAktif = $peminjamanAktif->map(function ($item) {
|
|
||||||
// Hitung Denda Flat 1000/hari
|
|
||||||
$tenggat = Carbon::parse($item['tenggat_kembali']);
|
|
||||||
$now = Carbon::now();
|
|
||||||
|
|
||||||
$item['hari_terlambat'] = 0;
|
$peminjamanAktif = $groupedLoans->map(function ($userLoans, $userId) {
|
||||||
$item['total_denda'] = 0;
|
$user = $userLoans->first()->user;
|
||||||
|
$firstLoan = $userLoans->first();
|
||||||
if ($now->greaterThan($tenggat)) {
|
|
||||||
$hariTelat = $tenggat->diffInDays($now);
|
return [
|
||||||
$item['hari_terlambat'] = $hariTelat;
|
'id_peminjaman' => 'PIN-ADM-' . sprintf('%03d', $userId),
|
||||||
$item['total_denda'] = $hariTelat * 1000;
|
'user_id' => $userId,
|
||||||
$item['denda_per_hari'] = 1000;
|
'peminjam' => $user->nama_lengkap ?? 'Unknown',
|
||||||
}
|
'nomor_hp' => $user->phone ?? '-',
|
||||||
// Generate WA Link
|
'tanggal_pinjam' => $firstLoan->borrowed_at,
|
||||||
$hp = $item['nomor_hp'];
|
'tenggat_kembali' => $firstLoan->due_at,
|
||||||
if (substr($hp, 0, 1) == '0') {
|
'status' => $firstLoan->status,
|
||||||
$hp = '62' . substr($hp, 1);
|
'books' => $userLoans->map(fn($l) => [
|
||||||
}
|
'id' => $l->book->id,
|
||||||
|
'judul' => $l->book->judul,
|
||||||
$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.";
|
'cover' => $l->book->cover,
|
||||||
$item['wa_link'] = "https://wa.me/{$hp}?text=" . urlencode($pesan);
|
])->toArray(),
|
||||||
|
];
|
||||||
return $item;
|
})->values();
|
||||||
});
|
|
||||||
|
|
||||||
$daftarPeminjam = $peminjamanAktif->pluck('peminjam')->unique();
|
$daftarPeminjam = $peminjamanAktif->pluck('peminjam')->unique();
|
||||||
|
|
||||||
|
|
@ -51,44 +50,31 @@ public function index(Request $request)
|
||||||
|
|
||||||
public function create()
|
public function create()
|
||||||
{
|
{
|
||||||
$allUsers = collect(DummyDataService::getAllSiswa());
|
$users = User::whereIn('role', ['siswa', 'guru'])->get();
|
||||||
$peminjamanAktif = DummyDataService::getAdminPeminjamanAktif();
|
|
||||||
|
$groupedUsers = $users->map(function ($user) {
|
||||||
|
$jumlahPinjam = Loan::where('user_id', $user->id)
|
||||||
|
->whereIn('status', ['Dipinjam', 'Terlambat'])
|
||||||
|
->count();
|
||||||
|
|
||||||
$groupedUsers = $allUsers
|
$user->jumlah_pinjam = $jumlahPinjam;
|
||||||
->whereIn('role', ['siswa', 'guru'])
|
$user->kena_limit = $jumlahPinjam >= 2;
|
||||||
->map(function ($user) use ($peminjamanAktif) {
|
$user->disabled = $user->kena_limit || $user->is_banned;
|
||||||
|
|
||||||
// Hitung berapa buku yang sedang dipinjam
|
if ($user->is_banned) {
|
||||||
$jumlahPinjam = $peminjamanAktif->where('peminjam', $user['nama_lengkap'])->count();
|
$user->status_text = "(Akun Dibekukan)";
|
||||||
|
} elseif ($user->kena_limit) {
|
||||||
|
$user->status_text = "(Limit Penuh: 2/2)";
|
||||||
|
} else {
|
||||||
|
$user->status_text = "";
|
||||||
|
}
|
||||||
|
|
||||||
// Cek Status
|
return $user;
|
||||||
$user['jumlah_pinjam'] = $jumlahPinjam;
|
})->groupBy('role');
|
||||||
$user['kena_limit'] = $jumlahPinjam >= 2;
|
|
||||||
|
|
||||||
// Cek apakah user di-banned (Nonaktif Manual)
|
$daftarBuku = Book::where('status', 'Tersedia')
|
||||||
$user['is_banned'] = $user['is_banned'] ?? false;
|
->whereJsonContains('tipe_akses', 'offline')
|
||||||
$user['disabled'] = $user['kena_limit'] || $user['is_banned'];
|
->get();
|
||||||
|
|
||||||
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';
|
|
||||||
});
|
|
||||||
|
|
||||||
return view('admin.peminjaman.create', [
|
return view('admin.peminjaman.create', [
|
||||||
'pageTitle' => 'Buat Peminjaman Manual',
|
'pageTitle' => 'Buat Peminjaman Manual',
|
||||||
|
|
@ -97,80 +83,58 @@ public function create()
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Menampilkan halaman KHUSUS Manajemen Denda (Siswa & Guru Telat).
|
|
||||||
*/
|
|
||||||
public function dendaIndex()
|
public function dendaIndex()
|
||||||
{
|
{
|
||||||
$allData = DummyDataService::getAdminPeminjamanAktif();
|
$now = Carbon::now();
|
||||||
$allSiswaRaw = collect(DummyDataService::getAllSiswa());
|
|
||||||
$now = \Carbon\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
|
$groupedLoans = $loans->groupBy('user_id');
|
||||||
$allSiswa = $allSiswaRaw->map(function ($siswa) use ($allData, $now) {
|
|
||||||
|
|
||||||
// Cek siswa punya pinjaman yang telat
|
$siswaTelat = $groupedLoans->map(function ($userLoans, $userId) use ($now) {
|
||||||
$pinjamanUser = $allData->firstWhere('user_id', $siswa['id']);
|
$user = $userLoans->first()->user;
|
||||||
|
$firstLoan = $userLoans->first();
|
||||||
$isTelat = false;
|
|
||||||
if ($pinjamanUser) {
|
$tenggat = Carbon::parse($firstLoan->due_at);
|
||||||
$isTelat = $pinjamanUser['tenggat_kembali']->startOfDay()->lt($now->startOfDay());
|
$hariTelat = $now->greaterThan($tenggat) ? (int) $tenggat->diffInDays($now) : 0;
|
||||||
}
|
$totalDenda = $hariTelat * 1000;
|
||||||
|
|
||||||
// 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';
|
|
||||||
|
|
||||||
// Link WA
|
// Link WA
|
||||||
$hp = $item['nomor_hp'];
|
$hp = $user->phone ?? '';
|
||||||
if (substr($hp, 0, 1) == '0')
|
$waLink = '#';
|
||||||
$hp = '62' . substr($hp, 1);
|
if ($hp) {
|
||||||
|
if (substr($hp, 0, 1) == '0') $hp = '62' . substr($hp, 1);
|
||||||
if ($hariTelat > 0) {
|
$pesan = $hariTelat > 0
|
||||||
$pesan = "Halo {$item['peminjam']}, anda terlambat pengembalian buku. Total Denda: Rp " . number_format($totalDenda, 0, ',', '.');
|
? "Halo {$user->nama_lengkap}, anda terlambat pengembalian buku. Total Denda: Rp " . number_format($totalDenda, 0, ',', '.')
|
||||||
} else {
|
: "Halo {$user->nama_lengkap}, akun anda sedang dinonaktifkan sementara. Mohon hubungi petugas.";
|
||||||
$pesan = "Halo {$item['peminjam']}, 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();
|
$listKelas = $siswaTelat->pluck('kelas')->unique()->values();
|
||||||
|
|
||||||
|
|
@ -181,11 +145,9 @@ public function dendaIndex()
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Dummy function untuk tombol Sanksi
|
|
||||||
*/
|
|
||||||
public function berikanSanksi(Request $request)
|
public function berikanSanksi(Request $request)
|
||||||
{
|
{
|
||||||
|
// Actually implement banning logic here if needed
|
||||||
return response()->json(['status' => 'success']);
|
return response()->json(['status' => 'success']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
namespace App\Http\Controllers\Admin;
|
namespace App\Http\Controllers\Admin;
|
||||||
|
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
use App\Services\DummyDataService;
|
use App\Models\Recommendation;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
class AdminRekomendasiController extends Controller
|
class AdminRekomendasiController extends Controller
|
||||||
|
|
@ -19,17 +19,19 @@ private function extractYouTubeId(string $url): ?string
|
||||||
|
|
||||||
public function index()
|
public function index()
|
||||||
{
|
{
|
||||||
$rekomendasiMentah = DummyDataService::getRekomendasiPembelajaran();
|
$rekomendasiMentah = Recommendation::latest()->get();
|
||||||
|
|
||||||
// Menambahkan thumbnail YouTube ke setiap rekomendasi
|
// Menambahkan thumbnail YouTube ke setiap rekomendasi
|
||||||
$semuaRekomendasi = $rekomendasiMentah->map(function ($item) {
|
$semuaRekomendasi = $rekomendasiMentah->map(function ($item) {
|
||||||
$videoId = $this->extractYouTubeId($item['youtube_link']);
|
$videoId = $this->extractYouTubeId($item->youtube_link);
|
||||||
if ($videoId) {
|
return [
|
||||||
$item['thumbnail'] = "https://img.youtube.com/vi/{$videoId}/hqdefault.jpg";
|
'id' => $item->id,
|
||||||
} else {
|
'judul' => $item->judul,
|
||||||
$item['thumbnail'] = 'https://via.placeholder.com/150?text=No+Preview';
|
'kategori' => $item->kategori,
|
||||||
}
|
'youtube_link' => $item->youtube_link,
|
||||||
return $item;
|
'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', [
|
return view('admin.rekomendasi.index', [
|
||||||
|
|
@ -45,8 +47,11 @@ public function create()
|
||||||
|
|
||||||
public function edit($id)
|
public function edit($id)
|
||||||
{
|
{
|
||||||
$rekomendasi = DummyDataService::getRekomendasiPembelajaran()->firstWhere('id', (int)$id);
|
$rekomendasi = Recommendation::findOrFail($id);
|
||||||
abort_if(!$rekomendasi, 404);
|
|
||||||
return view('admin.rekomendasi.edit', ['pageTitle' => 'Edit Rekomendasi', 'rekomendasi' => $rekomendasi]);
|
return view('admin.rekomendasi.edit', [
|
||||||
|
'pageTitle' => 'Edit Rekomendasi: ' . $rekomendasi->judul,
|
||||||
|
'rekomendasi' => $rekomendasi
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -3,7 +3,8 @@
|
||||||
namespace App\Http\Controllers\Admin;
|
namespace App\Http\Controllers\Admin;
|
||||||
|
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
use App\Services\DummyDataService;
|
use App\Models\Book;
|
||||||
|
use App\Models\Category;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
class BookController extends Controller
|
class BookController extends Controller
|
||||||
|
|
@ -11,12 +12,19 @@ class BookController extends Controller
|
||||||
public function index(Request $request)
|
public function index(Request $request)
|
||||||
{
|
{
|
||||||
$filters = $request->only(['search']);
|
$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
|
// Memisahkan buku menjadi dua koleksi: online dan offline
|
||||||
[$bukuOnline, $bukuOffline] = $semuaBuku->partition(function ($buku) {
|
[$bukuOnline, $bukuOffline] = $semuaBuku->partition(function ($buku) {
|
||||||
$tipe = $buku['tipe_akses'];
|
$tipe = $buku->tipe_akses;
|
||||||
return $tipe === 'online' || (is_array($tipe) && in_array('online', $tipe));
|
return in_array('online', $tipe ?? []);
|
||||||
});
|
});
|
||||||
|
|
||||||
return view('admin.buku.index', [
|
return view('admin.buku.index', [
|
||||||
|
|
@ -33,18 +41,21 @@ public function index(Request $request)
|
||||||
public function create()
|
public function create()
|
||||||
{
|
{
|
||||||
return view('admin.buku.create', [
|
return view('admin.buku.create', [
|
||||||
'pageTitle' => 'Tambah Buku Baru'
|
'pageTitle' => 'Tambah Buku Baru',
|
||||||
|
'categories' => Category::all()
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function edit($id)
|
public function edit($id)
|
||||||
{
|
{
|
||||||
$buku = DummyDataService::getKatalogBuku()->firstWhere('id', (int)$id);
|
$buku = Book::with('category')->findOrFail($id);
|
||||||
abort_if(!$buku, 404);
|
|
||||||
|
|
||||||
return view('admin.buku.edit', [
|
return view('admin.buku.edit', [
|
||||||
'pageTitle' => 'Edit Buku: ' . $buku['judul'],
|
'pageTitle' => 'Edit Buku: ' . $buku->judul,
|
||||||
'buku' => $buku
|
'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;
|
namespace App\Http\Controllers\Admin;
|
||||||
|
|
||||||
use App\Http\Controllers\Controller;
|
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
|
class DashboardController extends Controller
|
||||||
{
|
{
|
||||||
public function index()
|
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', [
|
return view('admin.dashboard', [
|
||||||
'pageTitle' => 'Beranda',
|
'pageTitle' => 'Beranda',
|
||||||
'user' => auth()->user(),
|
'user' => Auth::user(),
|
||||||
'greeting' => 'Selamat Datang',
|
'greeting' => $greeting,
|
||||||
'stats' => DummyDataService::getAdminDashboardStats(),
|
'stats' => $stats,
|
||||||
'statistikBulanan' => DummyDataService::getStatistikPeminjamanAdmin(),
|
'statistikBulanan' => $statistikBulanan,
|
||||||
'komposisiBuku' => DummyDataService::getKomposisiBukuAdmin(),
|
'komposisiBuku' => $komposisiBuku,
|
||||||
'pengumuman' => DummyDataService::getPengumuman(),
|
'pengumuman' => $pengumuman,
|
||||||
'aktivitasTerakhir' => DummyDataService::getAktivitasTerakhir(),
|
'aktivitasTerakhir' => $aktivitasTerakhir,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
namespace App\Http\Controllers\Admin;
|
namespace App\Http\Controllers\Admin;
|
||||||
|
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
use App\Services\DummyDataService;
|
use App\Models\Announcement;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
class PengumumanController extends Controller
|
class PengumumanController extends Controller
|
||||||
|
|
@ -13,7 +13,7 @@ class PengumumanController extends Controller
|
||||||
*/
|
*/
|
||||||
public function index()
|
public function index()
|
||||||
{
|
{
|
||||||
$semuaPengumuman = DummyDataService::getPengumuman();
|
$semuaPengumuman = Announcement::latest()->get();
|
||||||
return view('admin.pengumuman.index', [
|
return view('admin.pengumuman.index', [
|
||||||
'pageTitle' => 'Manajemen Pengumuman',
|
'pageTitle' => 'Manajemen Pengumuman',
|
||||||
'semuaPengumuman' => $semuaPengumuman,
|
'semuaPengumuman' => $semuaPengumuman,
|
||||||
|
|
@ -35,13 +35,10 @@ public function create()
|
||||||
*/
|
*/
|
||||||
public function edit($id)
|
public function edit($id)
|
||||||
{
|
{
|
||||||
$pengumuman = collect(DummyDataService::getPengumuman())->firstWhere('id', (int)$id);
|
$pengumuman = Announcement::findOrFail($id);
|
||||||
|
|
||||||
// Hentikan jika pengumuman tidak ditemukan
|
|
||||||
abort_if(!$pengumuman, 404);
|
|
||||||
|
|
||||||
return view('admin.pengumuman.edit', [
|
return view('admin.pengumuman.edit', [
|
||||||
'pageTitle' => 'Edit Pengumuman',
|
'pageTitle' => 'Edit Pengumuman: ' . $pengumuman->title,
|
||||||
'pengumuman' => $pengumuman,
|
'pengumuman' => $pengumuman,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,13 +3,14 @@
|
||||||
namespace App\Http\Controllers\Admin;
|
namespace App\Http\Controllers\Admin;
|
||||||
|
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
use App\Services\DummyDataService;
|
use App\Models\User;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
class UserController extends Controller
|
class UserController extends Controller
|
||||||
{
|
{
|
||||||
public function index()
|
public function index()
|
||||||
{
|
{
|
||||||
$semuaSiswa = DummyDataService::getAllSiswa();
|
$semuaSiswa = User::whereIn('role', ['siswa', 'guru', 'penjaga perpus'])->latest()->get();
|
||||||
return view('admin.pengguna.index', [
|
return view('admin.pengguna.index', [
|
||||||
'pageTitle' => 'Manajemen Pengguna',
|
'pageTitle' => 'Manajemen Pengguna',
|
||||||
'semuaSiswa' => $semuaSiswa
|
'semuaSiswa' => $semuaSiswa
|
||||||
|
|
@ -31,12 +32,13 @@ public function create()
|
||||||
*/
|
*/
|
||||||
public function edit($id)
|
public function edit($id)
|
||||||
{
|
{
|
||||||
$pengguna = collect(DummyDataService::getAllSiswa())->firstWhere('id', (int)$id);
|
$pengguna = User::findOrFail($id);
|
||||||
abort_if(!$pengguna, 404);
|
|
||||||
|
|
||||||
return view('admin.pengguna.edit', [
|
return view('admin.pengguna.edit', [
|
||||||
'pageTitle' => 'Edit Pengguna',
|
'pageTitle' => 'Edit Pengguna: ' . $pengguna->nama_lengkap,
|
||||||
'pengguna' => $pengguna,
|
'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
|
public function store(Request $request): RedirectResponse
|
||||||
{
|
{
|
||||||
// Bagian Validasi Dinamis
|
|
||||||
$role = $request->input('role');
|
$role = $request->input('role');
|
||||||
|
|
||||||
$rules = [
|
$rules = [
|
||||||
'name' => ['required', 'string', 'max:255'],
|
'name' => ['required', 'string', 'max:255'],
|
||||||
'password' => ['required', 'confirmed', Rules\Password::defaults()],
|
'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') {
|
if ($role === 'siswa') {
|
||||||
$rules['nisn'] = ['required', 'string', 'max:255']; // Tambahkan 'unique:users' jika perlu
|
$rules['nisn'] = ['required', 'string', 'max:255', 'unique:users,nomor_induk'];
|
||||||
} else { // Asumsi 'guru'
|
} else {
|
||||||
$rules['nip'] = ['required', 'string', 'max:255']; // Tambahkan 'unique:users' jika perlu
|
$rules['nip'] = ['required', 'string', 'max:255', 'unique:users,nomor_induk'];
|
||||||
}
|
}
|
||||||
|
|
||||||
$request->validate($rules);
|
$request->validate($rules);
|
||||||
|
|
||||||
// Bagian Pembuatan User
|
$user = User::create([
|
||||||
$userArray = [
|
|
||||||
'id' => rand(100, 999), // ID unik sementara
|
|
||||||
'nama_lengkap' => $request->name,
|
|
||||||
'name' => $request->name,
|
'name' => $request->name,
|
||||||
'password' => Hash::make($request->password), // Gunakan Hash jika login Anda sudah pakai Hash
|
'nama_lengkap' => $request->name,
|
||||||
// 'password' => $request->password, // Gunakan ini jika login (LoginRequest) masih cek teks biasa
|
'email' => ($request->nisn ?: $request->nip) . '@smkn1perpus.sch.id',
|
||||||
|
'password' => Hash::make($request->password),
|
||||||
'role' => $request->role,
|
'role' => $request->role,
|
||||||
];
|
'nomor_induk' => $request->nisn ?: $request->nip,
|
||||||
|
]);
|
||||||
// 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
|
|
||||||
|
|
||||||
event(new Registered($user));
|
event(new Registered($user));
|
||||||
|
|
||||||
Auth::login($user);
|
Auth::login($user);
|
||||||
|
|
||||||
// Bagian Redirect
|
|
||||||
if ($user->role === 'penjaga perpus') {
|
if ($user->role === 'penjaga perpus') {
|
||||||
return redirect()->route('admin.dashboard');
|
return redirect()->route('admin.dashboard');
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,13 @@
|
||||||
|
|
||||||
namespace App\Http\Controllers;
|
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\RedirectResponse;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Http\Response;
|
use Illuminate\Http\Response;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
use Illuminate\View\View;
|
use Illuminate\View\View;
|
||||||
use Symfony\Component\HttpFoundation\BinaryFileResponse;
|
use Symfony\Component\HttpFoundation\BinaryFileResponse;
|
||||||
|
|
@ -15,9 +18,34 @@ class BacaOnlineController extends Controller
|
||||||
public function index(Request $request): View
|
public function index(Request $request): View
|
||||||
{
|
{
|
||||||
$filters = $request->only(['search', 'kategori', 'tahun', 'penulis']);
|
$filters = $request->only(['search', 'kategori', 'tahun', 'penulis']);
|
||||||
$filters['tipe_akses'] = 'online';
|
|
||||||
$semuaBuku = DummyDataService::getKatalogBuku($filters);
|
$query = Book::with('category')->whereJsonContains('tipe_akses', 'online');
|
||||||
$filterOptions = DummyDataService::getFilterOptions();
|
|
||||||
|
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', [
|
return view('katalog.index', [
|
||||||
'semuaBuku' => $semuaBuku,
|
'semuaBuku' => $semuaBuku,
|
||||||
|
|
@ -30,7 +58,7 @@ public function index(Request $request): View
|
||||||
|
|
||||||
public function ringkasan(int $id): View
|
public function ringkasan(int $id): View
|
||||||
{
|
{
|
||||||
$book = $this->getBookOrFail($id);
|
$book = Book::with('category')->findOrFail($id);
|
||||||
|
|
||||||
return view('katalog.ringkasan', [
|
return view('katalog.ringkasan', [
|
||||||
'buku' => $book,
|
'buku' => $book,
|
||||||
|
|
@ -44,13 +72,13 @@ public function ringkasan(int $id): View
|
||||||
|
|
||||||
public function showCodeRequestPage(int $id): View
|
public function showCodeRequestPage(int $id): View
|
||||||
{
|
{
|
||||||
$book = $this->getBookOrFail($id);
|
$book = Book::findOrFail($id);
|
||||||
$sessionKey = 'access_code_for_book_' . $id;
|
$sessionKey = 'access_code_for_book_' . $id;
|
||||||
|
|
||||||
if (session()->has($sessionKey)) {
|
if (session()->has($sessionKey)) {
|
||||||
$accessCode = session($sessionKey);
|
$accessCode = session($sessionKey);
|
||||||
} else {
|
} 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]);
|
session([$sessionKey => $accessCode]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -69,6 +97,20 @@ public function verifyCode(Request $request, int $id): RedirectResponse
|
||||||
|
|
||||||
if ($request->input('kode_akses') === $correctCode) {
|
if ($request->input('kode_akses') === $correctCode) {
|
||||||
session(['book_verified_' . $id => true]);
|
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);
|
session()->forget('access_code_for_book_' . $id);
|
||||||
return redirect()->route('baca.view_book', ['id' => $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])
|
return redirect()->route('baca.request_code', ['id' => $id])
|
||||||
->with('error', 'Silakan masukkan kode akses terlebih dahulu.');
|
->with('error', 'Silakan masukkan kode akses terlebih dahulu.');
|
||||||
}
|
}
|
||||||
$book = $this->getBookOrFail($id);
|
$book = Book::findOrFail($id);
|
||||||
return view('baca.view_book', ['book' => $book]);
|
return view('baca.view_book', ['book' => $book]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -91,23 +133,16 @@ public function streamPdf(int $id): BinaryFileResponse|Response
|
||||||
if (!session('book_verified_' . $id, false)) {
|
if (!session('book_verified_' . $id, false)) {
|
||||||
abort(403, 'Akses Ditolak.');
|
abort(403, 'Akses Ditolak.');
|
||||||
}
|
}
|
||||||
$book = $this->getBookOrFail($id);
|
$book = Book::findOrFail($id);
|
||||||
$filePath = 'books/' . $book['file_pdf'];
|
$filePath = 'books/' . ($book->file_pdf ?? 'sample.pdf');
|
||||||
$absolutePath = storage_path('app/' . $filePath);
|
$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)) {
|
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);
|
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;
|
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\Http\Request;
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
|
||||||
|
|
@ -11,50 +15,122 @@ class DashboardController extends Controller
|
||||||
public function index()
|
public function index()
|
||||||
{
|
{
|
||||||
$user = Auth::user();
|
$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) {
|
$bukuPinjamOffline = $loans->map(function ($loan) {
|
||||||
return $buku['sisa_hari'] < 0;
|
$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') {
|
if ($isTelat && $user->role === 'siswa') {
|
||||||
$user->is_banned = true;
|
$user->update(['is_banned' => true]);
|
||||||
}
|
}
|
||||||
|
|
||||||
$stats = DummyDataService::getDashboardStats();
|
// Stats calculation
|
||||||
$pengumuman = DummyDataService::getPengumuman();
|
$stats = [
|
||||||
$pemberitahuan = DummyDataService::getPemberitahuan();
|
['label' => 'Buku yang dipinjam', 'value' => $loans->count(), 'icon' => 'bi-book-half', 'color' => 'primary'],
|
||||||
$progressMembaca = DummyDataService::getProgressMembaca();
|
['label' => 'Tenggat Waktu', 'value' => $bukuPinjamOffline->where('sisa_hari', '<=', 3)->where('sisa_hari', '>=', 0)->count(), 'icon' => 'bi-clock-history', 'color' => 'danger'],
|
||||||
$statistikBulanan = DummyDataService::getStatistikBulanan();
|
['label' => 'Buku dikembalikan', 'value' => Loan::where('user_id', $user->id)->where('status', 'Dikembalikan')->count(), 'icon' => 'bi-check-circle', 'color' => 'success'],
|
||||||
$bukuPinjamOffline = $bukuPinjam;
|
['label' => 'History Baca', 'value' => Loan::where('user_id', $user->id)->count(), 'icon' => 'bi-hourglass-split', 'color' => 'warning'],
|
||||||
$bacaBukuOnline = DummyDataService::getBacaBukuOnline($user);
|
];
|
||||||
$rekomendasiPembelajaran = DummyDataService::getRekomendasiPembelajaran();
|
|
||||||
$personalNotif = DummyDataService::getNotifikasiForUser($user);
|
|
||||||
|
|
||||||
// Cek apakah ada notifikasi denda aktif
|
$pengumuman = Announcement::latest()->take(5)->get();
|
||||||
$dendaAlert = collect($personalNotif)->where('type', 'denda_active');
|
|
||||||
|
// 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
|
$progressMembaca = ['selesai' => 70, 'sisa' => 30]; // Still dummy as we don't track pages yet
|
||||||
$rekomendasiPembelajaran = $rekomendasiPembelajaran->map(function ($item) {
|
$statistikBulanan = [
|
||||||
$videoId = $this->extractYouTubeId($item['youtube_link']);
|
'labels' => ['Jan', 'Feb', 'Mar', 'Apr', 'Mei', 'Jun', 'Jul'],
|
||||||
if ($videoId) {
|
'data' => [10, 15, 8, 20, 18, 25, 22],
|
||||||
$item['thumbnail'] = "https://img.youtube.com/vi/{$videoId}/hqdefault.jpg";
|
];
|
||||||
} else {
|
|
||||||
$item['thumbnail'] = 'https://via.placeholder.com/150?text=No+Preview';
|
// Online books (books with 'online' in tipe_akses)
|
||||||
}
|
$bacaBukuOnline = $loans->filter(function ($loan) {
|
||||||
return $item;
|
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');
|
$hour = date('H');
|
||||||
$greeting = "Selamat Pagi";
|
$greeting = "Selamat Pagi";
|
||||||
if ($hour >= 12 && $hour < 15)
|
if ($hour >= 12 && $hour < 15) $greeting = "Selamat Siang";
|
||||||
$greeting = "Selamat Siang";
|
elseif ($hour >= 15 && $hour < 18) $greeting = "Selamat Sore";
|
||||||
elseif ($hour >= 15 && $hour < 18)
|
elseif ($hour >= 18) $greeting = "Selamat Malam";
|
||||||
$greeting = "Selamat Sore";
|
|
||||||
elseif ($hour >= 18)
|
|
||||||
$greeting = "Selamat Malam";
|
|
||||||
|
|
||||||
return view('dashboard', compact(
|
return view('dashboard', compact(
|
||||||
'user',
|
'user',
|
||||||
|
|
@ -70,10 +146,7 @@ public function index()
|
||||||
'rekomendasiPembelajaran'
|
'rekomendasiPembelajaran'
|
||||||
))->with('notifikasi', $personalNotif);
|
))->with('notifikasi', $personalNotif);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Helper function untuk mengekstrak ID video dari URL YouTube.
|
|
||||||
*/
|
|
||||||
private function extractYouTubeId(string $url): ?string
|
private function extractYouTubeId(string $url): ?string
|
||||||
{
|
{
|
||||||
preg_match('/(v=|vi=|youtu.be\/|embed\/|\/v\/|\?v=|\&v=)(.+?)\b/i', $url, $matches);
|
preg_match('/(v=|vi=|youtu.be\/|embed\/|\/v\/|\?v=|\&v=)(.+?)\b/i', $url, $matches);
|
||||||
|
|
|
||||||
|
|
@ -3,16 +3,74 @@
|
||||||
namespace App\Http\Controllers\Guru;
|
namespace App\Http\Controllers\Guru;
|
||||||
|
|
||||||
use App\Http\Controllers\Controller;
|
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\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
|
||||||
class LaporanController extends Controller
|
class LaporanController extends Controller
|
||||||
{
|
{
|
||||||
public function index()
|
public function index()
|
||||||
{
|
{
|
||||||
$laporan = DummyDataService::getLaporanMinatBaca();
|
// 1. Laporan Minat Baca (Buku Terpopuler & Kategori Populer)
|
||||||
$siswaTeraktif = DummyDataService::getSiswaTeraktif();
|
$bukuTerpopuler = Book::withCount('loans')
|
||||||
$aktivitasMingguan = DummyDataService::getAktivitasMingguan();
|
->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', [
|
return view('guru.laporan.index', [
|
||||||
'pageTitle' => 'Laporan Minat Baca Siswa',
|
'pageTitle' => 'Laporan Minat Baca Siswa',
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,10 @@
|
||||||
|
|
||||||
namespace App\Http\Controllers;
|
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\Http\Request;
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
|
||||||
|
|
@ -14,15 +17,46 @@ public function index(Request $request)
|
||||||
|
|
||||||
$isBanned = false;
|
$isBanned = false;
|
||||||
if ($user && $user->role === 'siswa') {
|
if ($user && $user->role === 'siswa') {
|
||||||
$bukuPinjam = DummyDataService::getBukuPinjamOffline($user);
|
// Check for overdue loans
|
||||||
$isTelat = collect($bukuPinjam)->contains(fn($b) => $b['sisa_hari'] < 0);
|
$hasOverdue = Loan::where('user_id', $user->id)
|
||||||
$isBannedManual = $user->is_banned ?? false;
|
->whereIn('status', ['Dipinjam', 'Terlambat'])
|
||||||
$isBanned = $isTelat || $isBannedManual;
|
->where('due_at', '<', now())
|
||||||
|
->exists();
|
||||||
|
|
||||||
|
$isBanned = $hasOverdue || ($user->is_banned ?? false);
|
||||||
}
|
}
|
||||||
|
|
||||||
$filters = $request->only(['search', 'kategori', 'tahun', 'penulis']);
|
$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', [
|
return view('katalog.index', [
|
||||||
'semuaBuku' => $semuaBuku,
|
'semuaBuku' => $semuaBuku,
|
||||||
|
|
|
||||||
|
|
@ -2,30 +2,60 @@
|
||||||
|
|
||||||
namespace App\Http\Controllers;
|
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\Http\Request;
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
class PeminjamanController extends Controller
|
class PeminjamanController extends Controller
|
||||||
{
|
{
|
||||||
public function index(Request $request)
|
public function index(Request $request)
|
||||||
{
|
{
|
||||||
$user = \Illuminate\Support\Facades\Auth::user();
|
$user = Auth::user();
|
||||||
$bukuPinjam = \App\Services\DummyDataService::getBukuPinjamOffline($user);
|
|
||||||
$isTelat = collect($bukuPinjam)->contains(function ($buku) {
|
|
||||||
return $buku['sisa_hari'] < 0;
|
|
||||||
});
|
|
||||||
|
|
||||||
$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!');
|
return redirect()->route('dashboard')->with('error', 'AKSES DITOLAK: Akun Anda sedang dibekukan karena ada buku terlambat!');
|
||||||
}
|
}
|
||||||
|
|
||||||
$filters = $request->only(['search', 'kategori', 'tahun', 'penulis']);
|
$filters = $request->only(['search', 'kategori', 'tahun', 'penulis']);
|
||||||
$filters['tipe_akses'] = 'offline';
|
|
||||||
$semuaBuku = DummyDataService::getKatalogBuku($filters);
|
$query = Book::with('category')->whereJsonContains('tipe_akses', 'offline');
|
||||||
$filterOptions = DummyDataService::getFilterOptions();
|
|
||||||
|
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', [
|
return view('katalog.index', [
|
||||||
'semuaBuku' => $semuaBuku,
|
'semuaBuku' => $semuaBuku,
|
||||||
|
|
@ -39,7 +69,7 @@ public function index(Request $request)
|
||||||
public function ringkasan($id)
|
public function ringkasan($id)
|
||||||
{
|
{
|
||||||
$user = Auth::user();
|
$user = Auth::user();
|
||||||
$buku = DummyDataService::getKatalogBuku()->firstWhere('id', $id);
|
$buku = Book::with('category')->findOrFail($id);
|
||||||
|
|
||||||
return view('katalog.ringkasan', [
|
return view('katalog.ringkasan', [
|
||||||
'user' => $user,
|
'user' => $user,
|
||||||
|
|
@ -52,13 +82,14 @@ public function ringkasan($id)
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public function form($id)
|
public function form($id)
|
||||||
{
|
{
|
||||||
$user = Auth::user();
|
$user = Auth::user();
|
||||||
$buku = DummyDataService::getKatalogBuku()->firstWhere('id', $id);
|
$buku = Book::with('category')->findOrFail($id);
|
||||||
$filters = ['tipe_akses' => 'offline'];
|
|
||||||
$semuaBuku = DummyDataService::getKatalogBuku($filters);
|
$semuaBuku = Book::whereJsonContains('tipe_akses', 'offline')
|
||||||
|
->where('status', 'Tersedia')
|
||||||
|
->get();
|
||||||
|
|
||||||
return view('peminjaman.form', compact('user', 'buku', 'semuaBuku'));
|
return view('peminjaman.form', compact('user', 'buku', 'semuaBuku'));
|
||||||
}
|
}
|
||||||
|
|
@ -67,21 +98,31 @@ public function store(Request $request)
|
||||||
{
|
{
|
||||||
$request->validate([
|
$request->validate([
|
||||||
'buku_ids' => 'required|array|min:1|max:3',
|
'buku_ids' => 'required|array|min:1|max:3',
|
||||||
'buku_ids.*' => 'integer'
|
'buku_ids.*' => 'exists:books,id'
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$bukuIds = $request->input('buku_ids');
|
$bukuIds = $request->input('buku_ids');
|
||||||
|
|
||||||
foreach ($bukuIds as $bukuId) {
|
DB::transaction(function () use ($bukuIds) {
|
||||||
// Di backend nanti kayak gini
|
foreach ($bukuIds as $bukuId) {
|
||||||
// Peminjaman::create([
|
$book = Book::lockForUpdate()->find($bukuId);
|
||||||
// 'user_id' => auth()->id(),
|
|
||||||
// 'book_id' => $bukuId,
|
if ($book->status !== 'Tersedia') {
|
||||||
// 'tanggal_pinjam' => now(),
|
throw new \Exception("Buku '{$book->judul}' sudah tidak tersedia.");
|
||||||
// 'tanggal_kembali' => now()->addDays(7),
|
}
|
||||||
// 'status' => 'dipinjam'
|
|
||||||
// ]);
|
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')
|
return redirect()->route('dashboard')
|
||||||
->with('success', 'Berhasil meminjam ' . count($bukuIds) . ' buku!');
|
->with('success', 'Berhasil meminjam ' . count($bukuIds) . ' buku!');
|
||||||
|
|
|
||||||
|
|
@ -3,12 +3,15 @@
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
use App\Http\Requests\ProfileUpdateRequest;
|
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\RedirectResponse;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
use Illuminate\Support\Facades\Redirect;
|
use Illuminate\Support\Facades\Redirect;
|
||||||
use Illuminate\View\View;
|
use Illuminate\View\View;
|
||||||
use App\Services\DummyDataService;
|
|
||||||
|
|
||||||
class ProfileController extends Controller
|
class ProfileController extends Controller
|
||||||
{
|
{
|
||||||
|
|
@ -19,7 +22,7 @@ public function index(): RedirectResponse|View
|
||||||
{
|
{
|
||||||
$user = Auth::user();
|
$user = Auth::user();
|
||||||
if (!$user) {
|
if (!$user) {
|
||||||
return redirect()->route(route: 'login');
|
return redirect()->route('login');
|
||||||
}
|
}
|
||||||
|
|
||||||
$viewData = ['user' => $user];
|
$viewData = ['user' => $user];
|
||||||
|
|
@ -27,20 +30,68 @@ public function index(): RedirectResponse|View
|
||||||
// Menyiapkan data berdasarkan role pengguna
|
// Menyiapkan data berdasarkan role pengguna
|
||||||
if ($user->role === 'penjaga perpus') {
|
if ($user->role === 'penjaga perpus') {
|
||||||
// Data untuk Penjaga Perpus: Statistik global & aktivitas terkini
|
// Data untuk Penjaga Perpus: Statistik global & aktivitas terkini
|
||||||
$viewData['statistik'] = DummyDataService::getAdminDashboardStats();
|
$viewData['statistik'] = [
|
||||||
$viewData['aktivitasTerakhir'] = DummyDataService::getAktivitasTerakhir();
|
['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') {
|
} elseif ($user->role === 'guru') {
|
||||||
// Data untuk Guru: Data personal + ringkasan laporan minat baca
|
// Data untuk Guru
|
||||||
$viewData['bukuOffline'] = DummyDataService::getBukuPinjamOffline($user);
|
$loans = Loan::with('book')->where('user_id', $user->id)->whereIn('status', ['Dipinjam', 'Terlambat'])->get();
|
||||||
$viewData['bukuOnline'] = DummyDataService::getBacaBukuOnline($user);
|
|
||||||
$viewData['laporan'] = DummyDataService::getLaporanMinatBaca();
|
$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 {
|
} else {
|
||||||
// Data default untuk Siswa
|
// Data untuk Siswa
|
||||||
$viewData['bukuOffline'] = DummyDataService::getBukuPinjamOffline($user);
|
$loans = Loan::with('book')->where('user_id', $user->id)->whereIn('status', ['Dipinjam', 'Terlambat'])->get();
|
||||||
$viewData['bukuOnline'] = DummyDataService::getBacaBukuOnline($user);
|
|
||||||
$viewData['statistik'] = DummyDataService::getDashboardStats();
|
$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);
|
return view('profile.index', $viewData);
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
use App\Services\DummyDataService;
|
use App\Models\Recommendation;
|
||||||
|
|
||||||
class RekomendasiController extends Controller
|
class RekomendasiController extends Controller
|
||||||
{
|
{
|
||||||
|
|
@ -17,20 +17,20 @@ private function extractYouTubeId(string $url): ?string
|
||||||
|
|
||||||
public function show($id)
|
public function show($id)
|
||||||
{
|
{
|
||||||
$rekomendasi = DummyDataService::getRekomendasiPembelajaran()->firstWhere('id', (int)$id);
|
$rekomendasi = Recommendation::findOrFail($id);
|
||||||
abort_if(!$rekomendasi, 404);
|
|
||||||
|
|
||||||
// Menambahkan thumbnail YouTube ke setiap rekomendasi
|
|
||||||
$embedLink = null;
|
$embedLink = null;
|
||||||
$videoId = $this->extractYouTubeId($rekomendasi['youtube_link']);
|
$videoId = $this->extractYouTubeId($rekomendasi->youtube_link);
|
||||||
if ($videoId) {
|
if ($videoId) {
|
||||||
$embedLink = "https://www.youtube.com/embed/" . $videoId;
|
$embedLink = "https://www.youtube.com/embed/" . $videoId;
|
||||||
}
|
}
|
||||||
$rekomendasi['youtube_embed_link'] = $embedLink;
|
|
||||||
|
$data = $rekomendasi->toArray();
|
||||||
|
$data['youtube_embed_link'] = $embedLink;
|
||||||
|
|
||||||
return view('rekomendasiShow', [
|
return view('rekomendasiShow', [
|
||||||
'pageTitle' => $rekomendasi['judul'],
|
'pageTitle' => $rekomendasi->judul,
|
||||||
'rekomendasi' => $rekomendasi,
|
'rekomendasi' => $data,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2,18 +2,43 @@
|
||||||
|
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
use App\Services\DummyDataService;
|
use App\Models\Loan;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Facades\Auth; // Tambahkan ini
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
|
||||||
class RiwayatController extends Controller
|
class RiwayatController extends Controller
|
||||||
{
|
{
|
||||||
public function offlineIndex()
|
public function offlineIndex()
|
||||||
{
|
{
|
||||||
$user = \Illuminate\Support\Facades\Auth::user();
|
$user = Auth::user();
|
||||||
if (!$user) $user = (object) ['id' => 1];
|
|
||||||
|
$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', [
|
return view('riwayat.offline', [
|
||||||
'pageTitle' => 'Riwayat Peminjaman Offline',
|
'pageTitle' => 'Riwayat Peminjaman Offline',
|
||||||
|
|
@ -23,7 +48,32 @@ public function offlineIndex()
|
||||||
|
|
||||||
public function onlineIndex()
|
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', [
|
return view('riwayat.online', [
|
||||||
'pageTitle' => 'Riwayat Baca Online',
|
'pageTitle' => 'Riwayat Baca Online',
|
||||||
|
|
|
||||||
|
|
@ -52,64 +52,50 @@ public function rules(): array
|
||||||
*/
|
*/
|
||||||
public function authenticate(): void
|
public function authenticate(): void
|
||||||
{
|
{
|
||||||
// Pastikan pengguna tidak mencoba login terlalu sering (mencegah brute-force).
|
|
||||||
$this->ensureIsNotRateLimited();
|
$this->ensureIsNotRateLimited();
|
||||||
|
|
||||||
// Ambil data yang dikirim dari form login.
|
|
||||||
$roleDariForm = $this->input('role');
|
$roleDariForm = $this->input('role');
|
||||||
$allUsers = DummyDataService::getAllSiswa();
|
$loginIdentifier = $this->input('nisn') ?: $this->input('nip');
|
||||||
$inputPassword = $this->input('password');
|
$password = $this->input('password');
|
||||||
$userArray = null;
|
|
||||||
|
|
||||||
// Tentukan field mana yang akan menerima pesan error jika gagal (nisn atau email).
|
|
||||||
$errorField = $this->filled('nisn') ? 'nisn' : 'nip';
|
$errorField = $this->filled('nisn') ? 'nisn' : 'nip';
|
||||||
|
|
||||||
// Cari data pengguna berdasarkan input yang diberikan.
|
if (!Auth::attempt(['nomor_induk' => $loginIdentifier, 'password' => $password], $this->boolean('remember'))) {
|
||||||
if ($this->filled('nisn')) {
|
RateLimiter::hit($this->throttleKey());
|
||||||
// Jika form diisi dengan 'nisn', cari pengguna berdasarkan 'nisn'.
|
|
||||||
$userArray = collect($allUsers)->firstWhere('nisn', $this->input('nisn'));
|
throw ValidationException::withMessages([
|
||||||
} else {
|
$errorField => trans('auth.failed'),
|
||||||
// Jika tidak, cari pengguna berdasarkan 'nip'.
|
]);
|
||||||
$userArray = collect($allUsers)->firstWhere('nip', $this->input('nip'));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Lakukan Pengecekan Kredensial dan Role.
|
$user = Auth::user();
|
||||||
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);
|
|
||||||
|
|
||||||
// Loginkan pengguna secara resmi ke dalam sistem.
|
// Cek jika role sesuai
|
||||||
Auth::login($userModel);
|
if ($user->role !== $roleDariForm) {
|
||||||
|
Auth::logout();
|
||||||
|
$this->session()->invalidate();
|
||||||
|
$this->session()->regenerateToken();
|
||||||
|
|
||||||
// Reset hitungan percobaan login yang gagal.
|
RateLimiter::hit($this->throttleKey());
|
||||||
RateLimiter::clear($this->throttleKey());
|
|
||||||
return; // Proses autentikasi berhasil.
|
|
||||||
|
|
||||||
} else {
|
$actualRole = Str::title($user->role ?? 'Tidak Dikenal');
|
||||||
// Tambah hitungan percobaan login yang gagal.
|
throw ValidationException::withMessages([
|
||||||
RateLimiter::hit($this->throttleKey());
|
'forbidden' => "Akses ditolak. Akun ini terdaftar sebagai {$actualRole}.",
|
||||||
|
]);
|
||||||
// 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}.",
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Jika pengguna tidak ditemukan atau password salah.
|
// Cek jika akun di-banned
|
||||||
RateLimiter::hit($this->throttleKey());
|
if ($user->is_banned) {
|
||||||
throw ValidationException::withMessages([
|
Auth::logout();
|
||||||
$errorField => trans('auth.failed'), // Pesan error umum "These credentials do not match...".
|
$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 = [
|
protected $fillable = [
|
||||||
'name',
|
'name',
|
||||||
|
'nama_lengkap',
|
||||||
'email',
|
'email',
|
||||||
'password',
|
'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;
|
namespace App\Providers;
|
||||||
|
|
||||||
use App\Services\DummyDataService;
|
use App\Models\Loan;
|
||||||
|
use Carbon\Carbon;
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
use Illuminate\Support\Facades\URL;
|
use Illuminate\Support\Facades\URL;
|
||||||
use Illuminate\Support\ServiceProvider;
|
use Illuminate\Support\ServiceProvider;
|
||||||
|
|
@ -31,7 +32,42 @@ public function boot(): void
|
||||||
View::composer('*', function ($view) {
|
View::composer('*', function ($view) {
|
||||||
if (Auth::check()) {
|
if (Auth::check()) {
|
||||||
$user = Auth::user();
|
$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();
|
$unreadCount = $notifikasi->where('read', false)->count();
|
||||||
$view->with('notifikasi', $notifikasi);
|
$view->with('notifikasi', $notifikasi);
|
||||||
$view->with('unreadNotificationsCount', $unreadCount);
|
$view->with('unreadNotificationsCount', $unreadCount);
|
||||||
|
|
|
||||||
|
|
@ -22,9 +22,6 @@ class AuthServiceProvider extends ServiceProvider
|
||||||
*/
|
*/
|
||||||
public function boot(): void
|
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' => [
|
'guards' => [
|
||||||
'web' => [
|
'web' => [
|
||||||
'driver' => 'session',
|
'driver' => 'session',
|
||||||
'provider' => 'dummy',
|
'provider' => 'users',
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
|
|
||||||
|
|
@ -64,10 +64,6 @@
|
||||||
'driver' => 'eloquent',
|
'driver' => 'eloquent',
|
||||||
'model' => env('AUTH_MODEL', App\Models\User::class),
|
'model' => env('AUTH_MODEL', App\Models\User::class),
|
||||||
],
|
],
|
||||||
|
|
||||||
'dummy' => [
|
|
||||||
'driver' => 'dummy',
|
|
||||||
],
|
|
||||||
],
|
],
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
||||||
|
|
@ -25,9 +25,12 @@ public function definition(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'name' => fake()->name(),
|
'name' => fake()->name(),
|
||||||
|
'nama_lengkap' => fake()->name(),
|
||||||
'email' => fake()->unique()->safeEmail(),
|
'email' => fake()->unique()->safeEmail(),
|
||||||
'email_verified_at' => now(),
|
'email_verified_at' => now(),
|
||||||
'password' => static::$password ??= Hash::make('password'),
|
'password' => static::$password ??= Hash::make('password'),
|
||||||
|
'nomor_induk' => fake()->unique()->numerify('##########'),
|
||||||
|
'role' => 'siswa',
|
||||||
'remember_token' => Str::random(10),
|
'remember_token' => Str::random(10),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,9 +14,17 @@ public function up(): void
|
||||||
Schema::create('users', function (Blueprint $table) {
|
Schema::create('users', function (Blueprint $table) {
|
||||||
$table->id();
|
$table->id();
|
||||||
$table->string('name');
|
$table->string('name');
|
||||||
|
$table->string('nama_lengkap')->nullable();
|
||||||
$table->string('email')->unique();
|
$table->string('email')->unique();
|
||||||
$table->timestamp('email_verified_at')->nullable();
|
$table->timestamp('email_verified_at')->nullable();
|
||||||
$table->string('password');
|
$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->rememberToken();
|
||||||
$table->timestamps();
|
$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
|
public function run(): void
|
||||||
{
|
{
|
||||||
// User::factory(10)->create();
|
// User seeder is already handled or skipped as per user request
|
||||||
|
|
||||||
User::factory()->create([
|
$this->call([
|
||||||
'name' => 'Test User',
|
UserSeeder::class,
|
||||||
'email' => 'test@example.com',
|
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) ---
|
// --- RUTE UNTUK PENGGUNA TERAUTENTIKASI (SISWA, GURU, & PENJAGA PERPUS) ---
|
||||||
Route::middleware(['auth'])->group(function () {
|
Route::middleware(['auth'])->group(function () {
|
||||||
|
|
||||||
// Rute Umum untuk Siswa & Guru
|
// Rute Umum untuk Siswa & Guru
|
||||||
Route::get('/dashboard', [DashboardController::class, 'index'])->name('dashboard');
|
Route::get('/dashboard', [DashboardController::class, 'index'])->name('dashboard');
|
||||||
Route::get('/katalog', [KatalogController::class, 'index'])->name('katalog.index');
|
Route::get('/katalog', [KatalogController::class, 'index'])->name('katalog.index');
|
||||||
|
|
@ -80,11 +80,11 @@
|
||||||
// --- GRUP RUTE KHUSUS UNTUK ADMIN / PENJAGA PERPUSTAKAAN ---
|
// --- GRUP RUTE KHUSUS UNTUK ADMIN / PENJAGA PERPUSTAKAAN ---
|
||||||
Route::middleware(['auth', 'role:penjaga perpus'])->prefix('admin')->name('admin.')->group(function () {
|
Route::middleware(['auth', 'role:penjaga perpus'])->prefix('admin')->name('admin.')->group(function () {
|
||||||
Route::get('/dashboard', [AdminDashboardController::class, 'index'])->name('dashboard');
|
Route::get('/dashboard', [AdminDashboardController::class, 'index'])->name('dashboard');
|
||||||
|
|
||||||
Route::get('/buku', [AdminBookController::class, 'index'])->name('buku.index');
|
Route::get('/buku', [AdminBookController::class, 'index'])->name('buku.index');
|
||||||
Route::get('/buku/tambah', [AdminBookController::class, 'create'])->name('buku.create');
|
Route::get('/buku/tambah', [AdminBookController::class, 'create'])->name('buku.create');
|
||||||
Route::get('/buku/{id}/edit', [AdminBookController::class, 'edit'])->name('buku.edit');
|
Route::get('/buku/{id}/edit', [AdminBookController::class, 'edit'])->name('buku.edit');
|
||||||
|
|
||||||
Route::get('/pengguna', [AdminUserController::class, 'index'])->name('pengguna.index');
|
Route::get('/pengguna', [AdminUserController::class, 'index'])->name('pengguna.index');
|
||||||
Route::get('/pengguna/tambah', [AdminUserController::class, 'create'])->name('pengguna.create');
|
Route::get('/pengguna/tambah', [AdminUserController::class, 'create'])->name('pengguna.create');
|
||||||
Route::get('/pengguna/{id}/edit', [AdminUserController::class, 'edit'])->name('pengguna.edit');
|
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/tambah', [AdminRekomendasiController::class, 'create'])->name('rekomendasi.create');
|
||||||
Route::get('/rekomendasi/{id}/edit', [AdminRekomendasiController::class, 'edit'])->name('rekomendasi.edit');
|
Route::get('/rekomendasi/{id}/edit', [AdminRekomendasiController::class, 'edit'])->name('rekomendasi.edit');
|
||||||
|
|
||||||
Route::get('/peminjaman', [AdminPeminjamanController::class, 'index'])->name('peminjaman.index');
|
Route::get('/peminjaman', [AdminPeminjamanController::class, 'index'])->name('peminjaman.index');
|
||||||
Route::get('/peminjaman/tambah', [AdminPeminjamanController::class, 'create'])->name('peminjaman.create');
|
Route::get('/peminjaman/tambah', [AdminPeminjamanController::class, 'create'])->name('peminjaman.create');
|
||||||
|
|
||||||
Route::get('/denda', [AdminPeminjamanController::class, 'dendaIndex'])->name('denda.index');
|
Route::get('/denda', [AdminPeminjamanController::class, 'dendaIndex'])->name('denda.index');
|
||||||
Route::post('/denda/sanksi', [AdminPeminjamanController::class, 'berikanSanksi'])->name('denda.sanksi');
|
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');
|
Route::post('/admin/login', [AdminLoginController::class, 'store'])->name('admin.login.store');
|
||||||
});
|
});
|
||||||
|
|
||||||
require __DIR__ . '/auth.php';
|
require __DIR__ . '/auth.php';
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue