Compare commits
10 Commits
528c120fc3
...
70347b765e
| Author | SHA1 | Date |
|---|---|---|
|
|
70347b765e | |
|
|
78df5b3a72 | |
|
|
0c3369262f | |
|
|
d076baff24 | |
|
|
ff16d4e53c | |
|
|
fdb662d61c | |
|
|
bf12a38ccb | |
|
|
4c6a2f9a57 | |
|
|
3fb658fe9c | |
|
|
b0c7a8b2cf |
|
|
@ -0,0 +1,24 @@
|
|||
Nama Lengkap
|
||||
Jabatan / Peran
|
||||
Usia
|
||||
Seberapa Sering Anda Meminjam Buku Di Perpustakaan
|
||||
|
||||
1. Saya dapat login menggunakan NIP/NIK dengan mudah
|
||||
2. Saya dapat mencari dan mengakses buku untuk referensi pembelajaran
|
||||
3. Saya dapat melihat laporan minat baca siswa dengan mudah
|
||||
4. Website dapat diakses dengan cepat ( < 3 detik) saat digunakan
|
||||
5. Proses pencarian buku dan laporan berjalan dengan cepat (< 3 detik)
|
||||
6. Website tidak membutuhkan waktu lama saat membuka halaman (<5 detik)
|
||||
7. Tampilan website mudah dipahami dan digunakan
|
||||
8. Menu seperti katalog, laporan, dan riwayat mudah ditemukan
|
||||
9. Saya mudah mengingat cara menggunakan fitur dalam website
|
||||
10. Saya puas menggunakan website ini
|
||||
11. Saya mengalami kesulitan saat menggunakan website ini
|
||||
12. Saya membutuhkan bantuan orang lain untuk menggunakan website ini
|
||||
13. Fitur laporan minat baca membantu saya memantau aktivitas siswa
|
||||
14. Fitur rekomendasi membantu saya menentukan bahan pembelajaran
|
||||
15. Informasi buku membantu saya memilih referensi yang sesuai
|
||||
16. Website menjaga keamanan data akun saya dengan baik
|
||||
17. Website jarang mengalami error saat digunakan
|
||||
18. Fitur login dan logout berjalan dengan baik
|
||||
|
||||
|
|
@ -9,6 +9,9 @@
|
|||
use Carbon\Carbon;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use App\Mail\ReturnReceipt;
|
||||
|
||||
class AdminPeminjamanController extends Controller
|
||||
{
|
||||
|
|
@ -28,6 +31,7 @@ public function index(Request $request)
|
|||
'id_peminjaman' => 'PIN-ADM-'.sprintf('%03d', $userId),
|
||||
'user_id' => $userId,
|
||||
'peminjam' => $user->nama_lengkap ?? 'Unknown',
|
||||
'email' => $user->email,
|
||||
'nomor_hp' => $user->phone ?? '-',
|
||||
'tanggal_pinjam' => $firstLoan->borrowed_at,
|
||||
'tenggat_kembali' => $firstLoan->due_at,
|
||||
|
|
@ -122,9 +126,10 @@ public function store(Request $request)
|
|||
]);
|
||||
|
||||
// Update book status and decrement stock
|
||||
$newStok = $book->stok - 1;
|
||||
$book->update([
|
||||
'status' => 'Dipinjam',
|
||||
'stok' => $book->stok - 1,
|
||||
'status' => $newStok <= 0 ? 'Dipinjam' : 'Tersedia',
|
||||
'stok' => $newStok,
|
||||
]);
|
||||
}
|
||||
|
||||
|
|
@ -141,7 +146,7 @@ public function store(Request $request)
|
|||
public function export(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'bulan_laporan' => 'nullable|date',
|
||||
'bulan_laporan' => 'required|date|before_or_equal:today',
|
||||
]);
|
||||
|
||||
$query = Loan::with(['user', 'book'])->orderBy('borrowed_at', 'asc');
|
||||
|
|
@ -150,41 +155,48 @@ public function export(Request $request)
|
|||
$date = Carbon::parse($request->bulan_laporan);
|
||||
$query->whereMonth('borrowed_at', $date->month)
|
||||
->whereYear('borrowed_at', $date->year);
|
||||
$fileName = 'Laporan_Peminjaman_'.$date->format('Y-m').'.csv';
|
||||
$fileName = 'Laporan_Peminjaman_'.$date->format('Y-m').'.xls'; // Changed to .xls
|
||||
} else {
|
||||
$fileName = 'Laporan_Peminjaman_Semua.csv';
|
||||
$fileName = 'Laporan_Peminjaman_Semua.xls'; // Changed to .xls
|
||||
}
|
||||
|
||||
$loans = $query->get();
|
||||
|
||||
$headers = [
|
||||
"Content-type" => "text/csv",
|
||||
"Content-Type" => "application/vnd.ms-excel",
|
||||
"Content-Disposition" => "attachment; filename=$fileName",
|
||||
"Pragma" => "no-cache",
|
||||
"Cache-Control" => "must-revalidate, post-check=0, pre-check=0",
|
||||
"Expires" => "0"
|
||||
];
|
||||
|
||||
$columns = ['NO', 'ID PEMINJAMAN', 'PEMINJAM', 'ROLE', 'JUDUL BUKU', 'TGL PINJAM', 'T tenggat KEMBALI', 'STATUS', 'DENDA KETERLAMBATAN'];
|
||||
$columns = ['NO', 'ID PEMINJAMAN', 'PEMINJAM', 'ROLE', 'JUDUL BUKU', 'TGL PINJAM', 'TENGGAT KEMBALI', 'STATUS', 'DENDA KETERLAMBATAN'];
|
||||
|
||||
$callback = function() use($loans, $columns) {
|
||||
$file = fopen('php://output', 'w');
|
||||
fputcsv($file, $columns);
|
||||
|
||||
// Generate HTML table for Excel
|
||||
echo '<table border="1">';
|
||||
echo '<thead><tr style="background-color: #f2f2f2;">';
|
||||
foreach($columns as $col) echo "<th>$col</th>";
|
||||
echo '</tr></thead>';
|
||||
echo '<tbody>';
|
||||
|
||||
$i = 1;
|
||||
foreach ($loans as $loan) {
|
||||
$row['NO'] = $i++;
|
||||
$row['ID_PEMINJAMAN'] = $loan->loan_code;
|
||||
$row['PEMINJAM'] = $loan->user->nama_lengkap ?? 'Unknown';
|
||||
$row['ROLE'] = $loan->user->role ?? '-';
|
||||
$row['JUDUL_BUKU'] = $loan->book->judul ?? 'Unknown';
|
||||
$row['TGL_PINJAM'] = $loan->borrowed_at->format('d/m/Y');
|
||||
$row['TENGGAT_KEMBALI'] = $loan->due_at ? $loan->due_at->format('d/m/Y') : '-';
|
||||
$row['STATUS'] = $loan->status;
|
||||
$row['DENDA'] = $loan->fine_overdue ?? 0;
|
||||
|
||||
fputcsv($file, array_values($row));
|
||||
echo '<tr>';
|
||||
echo "<td>" . $i++ . "</td>";
|
||||
echo "<td>" . ($loan->loan_code ?? '-') . "</td>";
|
||||
echo "<td>" . ($loan->user->nama_lengkap ?? 'Unknown') . "</td>";
|
||||
echo "<td>" . ($loan->user->role ?? '-') . "</td>";
|
||||
echo "<td>" . ($loan->book->judul ?? 'Unknown') . "</td>";
|
||||
echo "<td>" . $loan->borrowed_at->format('d/m/Y') . "</td>";
|
||||
echo "<td>" . ($loan->due_at ? $loan->due_at->format('d/m/Y') : '-') . "</td>";
|
||||
echo "<td>" . ($loan->status ?? '-') . "</td>";
|
||||
echo "<td>" . ($loan->fine_overdue ?? 0) . "</td>";
|
||||
echo '</tr>';
|
||||
}
|
||||
echo '</tbody></table>';
|
||||
|
||||
fclose($file);
|
||||
};
|
||||
|
|
@ -293,6 +305,7 @@ public function kembalikan(Request $request)
|
|||
'returns.*.fine_damage' => 'required|integer',
|
||||
'returns.*.fine_overdue' => 'required|integer',
|
||||
'returns.*.notes' => 'nullable|string',
|
||||
'send_email' => 'nullable|boolean',
|
||||
]);
|
||||
|
||||
\DB::beginTransaction();
|
||||
|
|
@ -323,6 +336,36 @@ public function kembalikan(Request $request)
|
|||
|
||||
\DB::commit();
|
||||
|
||||
// Send email if requested
|
||||
if ($request->send_email && $request->user_id) {
|
||||
$user = User::findOrFail($request->user_id);
|
||||
$totalDenda = 0;
|
||||
$emailReturns = [];
|
||||
|
||||
foreach ($validated['returns'] as $item) {
|
||||
$book = Book::find($item['book_id']);
|
||||
$emailReturns[] = [
|
||||
'judul' => $book->judul ?? 'Buku Unknown',
|
||||
'condition' => $item['condition'],
|
||||
'fine_damage' => $item['fine_damage'],
|
||||
'fine_overdue' => $item['fine_overdue'],
|
||||
];
|
||||
$totalDenda += ($item['fine_damage'] + $item['fine_overdue']);
|
||||
}
|
||||
|
||||
try {
|
||||
Mail::to($user->email)->send(new ReturnReceipt($user, $emailReturns, $totalDenda));
|
||||
} catch (\Exception $e) {
|
||||
// Log the error but don't fail the return process
|
||||
\Log::error('Gagal mengirim email pengembalian: ' . $e->getMessage());
|
||||
return response()->json([
|
||||
'status' => 'success',
|
||||
'message' => 'Buku berhasil dikembalikan, namun gagal mengirim email.',
|
||||
'email_error' => $e->getMessage()
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
return response()->json(['status' => 'success', 'message' => 'Buku berhasil dikembalikan.']);
|
||||
} catch (\Exception $e) {
|
||||
\DB::rollBack();
|
||||
|
|
|
|||
|
|
@ -58,12 +58,13 @@ public function edit($id)
|
|||
public function store(Request $request)
|
||||
{
|
||||
$validated = $request->validate([
|
||||
'judul' => 'required|string|max:255',
|
||||
'kategori' => 'required|string|max:100',
|
||||
'judul' => 'required|string|min:3|max:50',
|
||||
'kategori' => 'required|string|min:3|max:50',
|
||||
'youtube_link' => 'required|url',
|
||||
'deskripsi' => 'required|string',
|
||||
'deskripsi' => 'required|string|max:255',
|
||||
]);
|
||||
|
||||
$validated['user_id'] = auth()->id();
|
||||
Recommendation::create($validated);
|
||||
|
||||
return redirect()->route('admin.rekomendasi.index')->with('success', 'Rekomendasi berhasil ditambahkan.');
|
||||
|
|
@ -74,10 +75,10 @@ public function update(Request $request, $id)
|
|||
$rekomendasi = Recommendation::findOrFail($id);
|
||||
|
||||
$validated = $request->validate([
|
||||
'judul' => 'required|string|max:255',
|
||||
'kategori' => 'required|string|max:100',
|
||||
'judul' => 'required|string|min:3|max:50',
|
||||
'kategori' => 'required|string|min:3|max:50',
|
||||
'youtube_link' => 'required|url',
|
||||
'deskripsi' => 'required|string',
|
||||
'deskripsi' => 'required|string|max:255',
|
||||
]);
|
||||
|
||||
$rekomendasi->update($validated);
|
||||
|
|
|
|||
|
|
@ -53,6 +53,19 @@ public function create()
|
|||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Menampilkan detail buku.
|
||||
*/
|
||||
public function show($id)
|
||||
{
|
||||
$buku = Book::with('category')->findOrFail($id);
|
||||
|
||||
return view('admin.buku.show', [
|
||||
'pageTitle' => 'Detail Buku: ' . $buku->judul,
|
||||
'buku' => $buku
|
||||
]);
|
||||
}
|
||||
|
||||
public function edit($id)
|
||||
{
|
||||
$buku = Book::with('category')->findOrFail($id);
|
||||
|
|
@ -67,15 +80,15 @@ public function edit($id)
|
|||
public function store(Request $request)
|
||||
{
|
||||
$validated = $request->validate([
|
||||
'judul' => 'required|string|max:255',
|
||||
'penulis' => 'required|string|max:255',
|
||||
'judul' => 'required|string|min:3|max:50',
|
||||
'penulis' => 'required|string|min:3|max:50|regex:/^[a-zA-Z\s.,\']+$/',
|
||||
'category_id' => 'required|exists:categories,id',
|
||||
'tahun' => 'required|integer',
|
||||
'kode_buku' => 'nullable|string',
|
||||
'stok' => 'required|integer|min:0',
|
||||
'tahun' => 'required|integer|min:1900|max:2026',
|
||||
'kode_buku' => 'required|numeric|max_digits:10',
|
||||
'stok' => 'required|integer|min:0|max:99',
|
||||
'tipe_akses' => 'required|array',
|
||||
'cover' => 'nullable|image|mimes:jpeg,png,jpg|max:2048',
|
||||
'file_pdf' => 'nullable|mimes:pdf|max:10240',
|
||||
'cover' => 'required|image|mimes:jpeg,png,jpg|max:2048',
|
||||
'file_pdf' => 'required|mimes:pdf|max:10240',
|
||||
]);
|
||||
|
||||
if ($request->hasFile('cover')) {
|
||||
|
|
@ -88,6 +101,8 @@ public function store(Request $request)
|
|||
$validated['file_pdf'] = basename($path);
|
||||
}
|
||||
|
||||
$validated['status'] = $validated['stok'] <= 0 ? 'Dipinjam' : 'Tersedia';
|
||||
|
||||
Book::create($validated);
|
||||
|
||||
return redirect()->route('admin.buku.index')->with('success', 'Buku berhasil ditambahkan.');
|
||||
|
|
@ -98,12 +113,12 @@ public function update(Request $request, $id)
|
|||
$buku = Book::findOrFail($id);
|
||||
|
||||
$validated = $request->validate([
|
||||
'judul' => 'required|string|max:255',
|
||||
'penulis' => 'required|string|max:255',
|
||||
'judul' => 'required|string|min:3|max:50',
|
||||
'penulis' => 'required|string|min:3|max:50|regex:/^[a-zA-Z\s.,\']+$/',
|
||||
'category_id' => 'required|exists:categories,id',
|
||||
'tahun' => 'required|integer',
|
||||
'kode_buku' => 'nullable|string',
|
||||
'stok' => 'required|integer|min:0',
|
||||
'tahun' => 'required|integer|min:1900|max:2026',
|
||||
'kode_buku' => 'nullable|numeric|max_digits:10',
|
||||
'stok' => 'required|integer|min:0|max:99',
|
||||
'tipe_akses' => 'required|array',
|
||||
'cover' => 'nullable|image|mimes:jpeg,png,jpg|max:2048',
|
||||
'file_pdf' => 'nullable|mimes:pdf|max:10240',
|
||||
|
|
@ -119,6 +134,8 @@ public function update(Request $request, $id)
|
|||
$validated['file_pdf'] = basename($path);
|
||||
}
|
||||
|
||||
$validated['status'] = $validated['stok'] <= 0 ? 'Dipinjam' : 'Tersedia';
|
||||
|
||||
$buku->update($validated);
|
||||
|
||||
return redirect()->route('admin.buku.index')->with('success', 'Buku berhasil diperbarui.');
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ public function index()
|
|||
['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' => 'Total Denda', 'value' => 'Rp ' . number_format(Loan::where('status', 'Terlambat')->sum('fine_overdue'), 0, ',', '.'), 'icon' => 'bi-cash-coin', 'color' => 'danger'],
|
||||
['label' => 'Total Denda', 'value' => 'Rp ' . number_format(Loan::sum('fine_overdue') + Loan::sum('fine_damage'), 0, ',', '.'), 'icon' => 'bi-cash-coin', 'color' => 'danger'],
|
||||
];
|
||||
|
||||
// Monthly stats (last 7 months)
|
||||
|
|
|
|||
|
|
@ -12,12 +12,12 @@ class MasterIndukController extends Controller
|
|||
public function store(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'nomor_induk' => 'required|unique:master_induks,nomor_induk',
|
||||
'nomor_induk' => 'required|string|max:15|unique:master_induks,nomor_induk',
|
||||
'role' => 'required|in:siswa,guru',
|
||||
'nama_pemilik' => 'required|string',
|
||||
'nama_pemilik' => 'required|string|min:3|max:50|regex:/^[a-zA-Z\s.,\']+$/',
|
||||
]);
|
||||
|
||||
MasterInduk::create($request->all());
|
||||
MasterInduk::create(array_merge($request->all(), ['user_id' => auth()->id()]));
|
||||
|
||||
return back()->with('success', 'Data Induk berhasil ditambahkan. User dengan NIP/NISN ini sekarang bisa mendaftar.');
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,8 +46,8 @@ public function edit($id)
|
|||
public function store(Request $request)
|
||||
{
|
||||
$validated = $request->validate([
|
||||
'title' => 'required|string|max:255',
|
||||
'content' => 'required|string',
|
||||
'title' => 'required|string|min:3|max:50',
|
||||
'content' => 'required|string|max:255',
|
||||
'type' => 'required|in:info,warning,success,danger',
|
||||
'icon' => 'nullable|string|max:50',
|
||||
]);
|
||||
|
|
@ -57,6 +57,7 @@ public function store(Request $request)
|
|||
$validated['icon'] = 'bi-megaphone';
|
||||
}
|
||||
|
||||
$validated['user_id'] = auth()->id();
|
||||
Announcement::create($validated);
|
||||
|
||||
return redirect()->route('admin.pengumuman.index')->with('success', 'Pengumuman berhasil ditambahkan.');
|
||||
|
|
@ -67,8 +68,8 @@ public function update(Request $request, $id)
|
|||
$pengumuman = Announcement::findOrFail($id);
|
||||
|
||||
$validated = $request->validate([
|
||||
'title' => 'required|string|max:255',
|
||||
'content' => 'required|string',
|
||||
'title' => 'required|string|min:3|max:50',
|
||||
'content' => 'required|string|max:255',
|
||||
'type' => 'required|in:info,warning,success,danger',
|
||||
'icon' => 'nullable|string|max:50',
|
||||
]);
|
||||
|
|
|
|||
|
|
@ -18,9 +18,9 @@ public function index()
|
|||
$query->where('role', request('role'));
|
||||
}
|
||||
|
||||
$users = $query->paginate(10)->appends(request()->query());
|
||||
$users = $query->paginate(10, ['*'], 'page')->appends(request()->query());
|
||||
|
||||
$whitelists = MasterInduk::orderBy('created_at', 'desc')->get();
|
||||
$whitelists = MasterInduk::orderBy('created_at', 'desc')->paginate(10, ['*'], 'whitelist_page')->appends(request()->query());
|
||||
|
||||
return view('admin.pengguna.index', [
|
||||
'pageTitle' => 'Daftar Pengguna',
|
||||
|
|
@ -29,9 +29,17 @@ public function index()
|
|||
]);
|
||||
}
|
||||
|
||||
public function create()
|
||||
public function create(Request $request)
|
||||
{
|
||||
return view('admin.pengguna.create', ['pageTitle' => 'Tambah Pengguna Baru']);
|
||||
$prefilledData = null;
|
||||
if ($request->has('nomor_induk')) {
|
||||
$prefilledData = MasterInduk::where('nomor_induk', $request->nomor_induk)->first();
|
||||
}
|
||||
|
||||
return view('admin.pengguna.create', [
|
||||
'pageTitle' => 'Tambah Pengguna Baru',
|
||||
'prefilledData' => $prefilledData
|
||||
]);
|
||||
}
|
||||
|
||||
public function edit($id)
|
||||
|
|
@ -46,16 +54,27 @@ public function edit($id)
|
|||
public function store(Request $request)
|
||||
{
|
||||
$validated = $request->validate([
|
||||
'nama_lengkap' => 'required|string|max:255',
|
||||
'nama_lengkap' => 'required|string|min:3|max:50|regex:/^[a-zA-Z\s.,\']+$/',
|
||||
'email' => 'required|email|unique:users,email',
|
||||
'nomor_induk' => 'nullable|string|max:50',
|
||||
'phone' => 'nullable|string|max:20',
|
||||
'nomor_induk' => 'required|string|max:15|unique:users,nomor_induk',
|
||||
'phone' => 'nullable|string|min:13|max:16|regex:/^\+?[0-9]+$/',
|
||||
'role' => 'required|in:siswa,guru,penjaga perpus',
|
||||
'kelas' => 'nullable|string|max:50',
|
||||
'golongan' => 'nullable|string|max:50',
|
||||
'password' => 'required|string|min:8|confirmed',
|
||||
]);
|
||||
|
||||
// Validasi Whitelist untuk Siswa & Guru
|
||||
if (in_array($validated['role'], ['siswa', 'guru'])) {
|
||||
$isWhitelisted = MasterInduk::where('nomor_induk', $validated['nomor_induk'])
|
||||
->where('role', $validated['role'])
|
||||
->exists();
|
||||
|
||||
if (!$isWhitelisted) {
|
||||
return back()->withErrors(['nomor_induk' => 'Nomor Induk ini tidak terdaftar dalam Data Induk (Whitelist) atau Role tidak sesuai.'])->withInput();
|
||||
}
|
||||
}
|
||||
|
||||
$validated['password'] = Hash::make($validated['password']);
|
||||
$validated['name'] = $validated['nama_lengkap']; // Set name field for compatibility
|
||||
|
||||
|
|
@ -69,10 +88,10 @@ public function update(Request $request, $id)
|
|||
$pengguna = User::findOrFail($id);
|
||||
|
||||
$validated = $request->validate([
|
||||
'nama_lengkap' => 'required|string|max:255',
|
||||
'nama_lengkap' => 'required|string|min:3|max:50|regex:/^[a-zA-Z\s.,\']+$/',
|
||||
'email' => 'required|email|unique:users,email,' . $id,
|
||||
'nomor_induk' => 'nullable|string|max:50',
|
||||
'phone' => 'nullable|string|max:20',
|
||||
'nomor_induk' => 'nullable|string|max:15',
|
||||
'phone' => 'nullable|string|min:13|max:16|regex:/^\+?[0-9]+$/',
|
||||
'role' => 'required|in:siswa,guru,penjaga perpus',
|
||||
'kelas' => 'nullable|string|max:50',
|
||||
'golongan' => 'nullable|string|max:50',
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ public function index()
|
|||
|
||||
$bukuPinjamOffline = $loans->map(function ($loan) {
|
||||
$dueAt = Carbon::parse($loan->due_at);
|
||||
$sisaHari = (int) now()->diffInDays($dueAt, false);
|
||||
$sisaHari = (int) now()->startOfDay()->diffInDays($dueAt->startOfDay(), false);
|
||||
|
||||
return [
|
||||
'id' => $loan->book->id,
|
||||
|
|
@ -45,7 +45,7 @@ public function index()
|
|||
// Stats calculation
|
||||
$stats = [
|
||||
['label' => 'Buku yang dipinjam', 'value' => $loans->count(), 'icon' => 'bi-book-half', 'color' => 'primary'],
|
||||
['label' => 'Tenggat Waktu', 'value' => $bukuPinjamOffline->where('sisa_hari', '<=', 3)->where('sisa_hari', '>=', 0)->count(), 'icon' => 'bi-clock-history', 'color' => 'danger'],
|
||||
['label' => 'Tenggat Waktu', 'value' => $bukuPinjamOffline->where('sisa_hari', '<=', 2)->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'],
|
||||
];
|
||||
|
|
|
|||
|
|
@ -88,7 +88,7 @@ public function form($id)
|
|||
$buku = Book::with('category')->findOrFail($id);
|
||||
|
||||
$semuaBuku = Book::whereJsonContains('tipe_akses', 'offline')
|
||||
->where('status', 'Tersedia')
|
||||
->where('stok', '>', 0)
|
||||
->get();
|
||||
|
||||
return view('peminjaman.form', compact('user', 'buku', 'semuaBuku'));
|
||||
|
|
@ -98,16 +98,22 @@ public function store(Request $request)
|
|||
{
|
||||
$request->validate([
|
||||
'buku_ids' => 'required|array|min:1|max:3',
|
||||
'buku_ids.*' => 'exists:books,id'
|
||||
'buku_ids.*' => 'exists:books,id',
|
||||
'tanggal_pinjam' => 'required',
|
||||
'tanggal_kembali' => 'required',
|
||||
]);
|
||||
|
||||
$bukuIds = $request->input('buku_ids');
|
||||
|
||||
// Parse dates: always today for borrow, and 2 days from now for due date
|
||||
$borrowedAt = now();
|
||||
$dueAt = now()->addDays(2);
|
||||
|
||||
DB::transaction(function () use ($bukuIds) {
|
||||
DB::transaction(function () use ($bukuIds, $borrowedAt, $dueAt) {
|
||||
foreach ($bukuIds as $bukuId) {
|
||||
$book = Book::lockForUpdate()->find($bukuId);
|
||||
|
||||
if ($book->status !== 'Tersedia') {
|
||||
if ($book->stok <= 0) {
|
||||
throw new \Exception("Buku '{$book->judul}' sudah tidak tersedia.");
|
||||
}
|
||||
|
||||
|
|
@ -115,12 +121,16 @@ public function store(Request $request)
|
|||
'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),
|
||||
'borrowed_at' => $borrowedAt,
|
||||
'due_at' => $dueAt,
|
||||
'status' => 'Dipinjam',
|
||||
]);
|
||||
|
||||
$book->update(['status' => 'Dipinjam']);
|
||||
$newStok = $book->stok - 1;
|
||||
$book->update([
|
||||
'stok' => $newStok,
|
||||
'status' => $newStok <= 0 ? 'Dipinjam' : 'Tersedia'
|
||||
]);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ public function index(Request $request): \Illuminate\View\View|\Illuminate\Http\
|
|||
$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),
|
||||
'sisa_hari' => (int) now()->startOfDay()->diffInDays(Carbon::parse($loan->due_at)->startOfDay(), false),
|
||||
'cover' => $loan->book->cover,
|
||||
]);
|
||||
|
||||
|
|
@ -87,7 +87,7 @@ public function index(Request $request): \Illuminate\View\View|\Illuminate\Http\
|
|||
$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),
|
||||
'sisa_hari' => (int) now()->startOfDay()->diffInDays(Carbon::parse($loan->due_at)->startOfDay(), false),
|
||||
'cover' => $loan->book->cover,
|
||||
]);
|
||||
|
||||
|
|
@ -99,11 +99,31 @@ public function index(Request $request): \Illuminate\View\View|\Illuminate\Http\
|
|||
]);
|
||||
|
||||
$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' => 'Buku yang dipinjam', 'value' => $loans->count(), 'icon' => 'bi-journal-bookmark-fill', 'color' => 'primary'],
|
||||
['label' => 'Tenggat Waktu', 'value' => $viewData['bukuOffline']->where('sisa_hari', '<=', 3)->count(), 'icon' => 'bi-clock-fill', 'color' => 'danger'],
|
||||
['label' => 'Buku dikembalikan', 'value' => Loan::where('user_id', $user->id)->where('status', 'Dikembalikan')->count(), 'icon' => 'bi-check-circle-fill', 'color' => 'success'],
|
||||
['label' => 'History Baca', 'value' => Loan::where('user_id', $user->id)->count(), 'icon' => 'bi-hourglass-split', 'color' => 'warning'],
|
||||
];
|
||||
|
||||
// Analytics for Siswa (same as Guru for consistency)
|
||||
$viewData['laporan'] = [
|
||||
'buku_terpopuler' => Book::withCount('loans')
|
||||
->orderBy('loans_count', 'desc')
|
||||
->take(3)
|
||||
->get()
|
||||
->map(fn($b) => [
|
||||
'judul' => $b->judul,
|
||||
'total_pembaca' => $b->loans_count
|
||||
]),
|
||||
'kategori_populer' => \App\Models\Category::select('categories.name as nama')
|
||||
->join('books', 'categories.id', '=', 'books.category_id')
|
||||
->join('loans', 'books.id', '=', 'loans.book_id')
|
||||
->selectRaw('count(loans.id) as total_pembaca')
|
||||
->groupBy('categories.id', 'categories.name')
|
||||
->orderBy('total_pembaca', 'desc')
|
||||
->take(3)
|
||||
->get(),
|
||||
];
|
||||
}
|
||||
|
||||
return view('profile.index', $viewData);
|
||||
|
|
@ -122,11 +142,11 @@ public function edit(Request $request): View
|
|||
/**
|
||||
* Update data profil ke Database MySQL.
|
||||
*/
|
||||
public function update(Request $request): RedirectResponse
|
||||
public function update(ProfileUpdateRequest $request): RedirectResponse
|
||||
{
|
||||
$user = $request->user();
|
||||
$user->fill($request->validated());
|
||||
$user->name = $user->nama_lengkap; // Sync for compatibility
|
||||
$user->nama_lengkap = $user->name; // Sync for compatibility
|
||||
|
||||
if ($user->isDirty('email')) {
|
||||
$user->email_verified_at = null;
|
||||
|
|
|
|||
|
|
@ -32,15 +32,27 @@ public function authenticate(): void
|
|||
$password = $this->input('password');
|
||||
|
||||
$errorField = $this->filled('nisn') ? 'nisn' : 'nip';
|
||||
$labelField = $this->filled('nisn') ? 'NISN' : 'NIP/NIK';
|
||||
|
||||
if (!Auth::attempt(['nomor_induk' => $loginIdentifier, 'password' => $password], $this->boolean('remember'))) {
|
||||
// 1. Cek keberadaan user berdasarkan nomor_induk
|
||||
$user = \App\Models\User::where('nomor_induk', $loginIdentifier)->first();
|
||||
|
||||
if (!$user) {
|
||||
RateLimiter::hit($this->throttleKey());
|
||||
|
||||
throw ValidationException::withMessages([
|
||||
$errorField => trans('auth.failed'),
|
||||
$errorField => "Nomor Induk ({$labelField}) tidak ditemukan.",
|
||||
]);
|
||||
}
|
||||
|
||||
// 2. Cek password (Auth::attempt)
|
||||
if (!Auth::attempt(['nomor_induk' => $loginIdentifier, 'password' => $password], $this->boolean('remember'))) {
|
||||
RateLimiter::hit($this->throttleKey());
|
||||
throw ValidationException::withMessages([
|
||||
'password' => 'Kata sandi yang Anda masukkan salah.',
|
||||
]);
|
||||
}
|
||||
|
||||
// Ambil user yang sudah terautentikasi
|
||||
$user = Auth::user();
|
||||
|
||||
// Cek jika role sesuai
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ class ProfileUpdateRequest extends FormRequest
|
|||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'nama_lengkap' => ['required', 'string', 'max:255'],
|
||||
'name' => ['required', 'string', 'min:3', 'max:50'],
|
||||
'email' => [
|
||||
'required',
|
||||
'string',
|
||||
|
|
@ -25,7 +25,7 @@ public function rules(): array
|
|||
'max:255',
|
||||
Rule::unique(User::class)->ignore($this->user()->id),
|
||||
],
|
||||
'phone' => ['required', 'string', 'max:20'],
|
||||
'phone' => ['required', 'string', 'min:13', 'max:16'],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,58 @@
|
|||
<?php
|
||||
|
||||
namespace App\Mail;
|
||||
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Mail\Mailable;
|
||||
use Illuminate\Mail\Mailables\Content;
|
||||
use Illuminate\Mail\Mailables\Envelope;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
class ReturnReceipt extends Mailable
|
||||
{
|
||||
use Queueable, SerializesModels;
|
||||
|
||||
public $user;
|
||||
public $returns;
|
||||
public $totalDenda;
|
||||
|
||||
/**
|
||||
* Create a new message instance.
|
||||
*/
|
||||
public function __construct($user, $returns, $totalDenda)
|
||||
{
|
||||
$this->user = $user;
|
||||
$this->returns = $returns;
|
||||
$this->totalDenda = $totalDenda;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the message envelope.
|
||||
*/
|
||||
public function envelope(): Envelope
|
||||
{
|
||||
return new Envelope(
|
||||
subject: 'Bukti Pengembalian Buku - ' . config('app.name'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the message content definition.
|
||||
*/
|
||||
public function content(): Content
|
||||
{
|
||||
return new Content(
|
||||
view: 'emails.return_receipt',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the attachments for the message.
|
||||
*
|
||||
* @return array<int, \Illuminate\Mail\Mailables\Attachment>
|
||||
*/
|
||||
public function attachments(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
|
@ -6,5 +6,10 @@
|
|||
|
||||
class Announcement extends Model
|
||||
{
|
||||
protected $fillable = ['type', 'icon', 'title', 'content'];
|
||||
protected $fillable = ['user_id', 'type', 'icon', 'title', 'content'];
|
||||
|
||||
public function user()
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
class Book extends Model
|
||||
{
|
||||
protected $fillable = [
|
||||
'judul', 'penulis', 'cover', 'kode_buku',
|
||||
'judul', 'penulis', 'cover', 'kode_buku', 'stok',
|
||||
'category_id', 'tahun', 'status', 'is_new', 'tipe_akses', 'file_pdf', 'is_arsip'
|
||||
];
|
||||
|
||||
|
|
|
|||
|
|
@ -10,4 +10,9 @@ class MasterInduk extends Model
|
|||
use HasFactory;
|
||||
|
||||
protected $guarded = ['id'];
|
||||
|
||||
public function user()
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,5 +6,10 @@
|
|||
|
||||
class Recommendation extends Model
|
||||
{
|
||||
protected $fillable = ['judul', 'kategori', 'youtube_link', 'deskripsi'];
|
||||
protected $fillable = ['user_id', 'judul', 'kategori', 'youtube_link', 'deskripsi'];
|
||||
|
||||
public function user()
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -58,4 +58,24 @@ public function getNamaLengkapAttribute()
|
|||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
public function loans()
|
||||
{
|
||||
return $this->hasMany(Loan::class);
|
||||
}
|
||||
|
||||
public function announcements()
|
||||
{
|
||||
return $this->hasMany(Announcement::class);
|
||||
}
|
||||
|
||||
public function recommendations()
|
||||
{
|
||||
return $this->hasMany(Recommendation::class);
|
||||
}
|
||||
|
||||
public function masterInduks()
|
||||
{
|
||||
return $this->hasMany(MasterInduk::class);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
use App\Models\Loan;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Pagination\Paginator;
|
||||
use Illuminate\Support\Facades\URL;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
use Illuminate\Support\Facades\View;
|
||||
|
|
@ -28,6 +29,8 @@ public function boot(): void
|
|||
URL::forceScheme('https');
|
||||
}
|
||||
|
||||
Paginator::useBootstrapFive();
|
||||
|
||||
// View Composer untuk semua view (*)
|
||||
View::composer('*', function ($view) {
|
||||
if (Auth::check()) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,8 @@
|
|||
Berdasarkan analisis awal terhadap pengelolaan perpustakaan di SMP Negeri 1 Bagor, terdapat beberapa permasalahan utama yang menjadi fokus pengembangan Perpustakaan Digital. Pengelolaan perpustakaan masih dilakukan secara manual yang menyebabkan proses menjadi lambat, rentan kesalahan dan sulit dalam status pelacakan status buku.Pengelolaan data yang tidak terintegrasi menimbulkan kesulitan dalam pencarian data peminjam, verifikasi pengembalian dan pembuatan laporan dimana risiko kehilangan data menjadi kendala utama.
|
||||
Hasil observasi tersebut menunjukkan bahwa pentingnya implementasi sistem informasi perpustakaan digital yang dapat mengelola data secara efisien dan terintegrasi untuk mengatasi permasalahan pengelolaan manual yang masih diterapakan. Tujuannya adalah membangun sistem yang mampu mengotomatisasi proses peminjaman dan pengembalian buku, mengelola database koleksi perpustakaan secara terpusat, serta menyediakan API untuk akses data secara real-time. Observasi ini melibatkan pengelola perpustakaan yaitu Ibu Yuyun Nurmawati, S.Pd. sebagai narasumber utama untuk mendukung proses implementasi sistem informasi perpustakaan digital. Beliau dipilih karena memahami kebutuhan operasional perpustakaan dan memiliki pengalaman langsung dalam mengelola proses peminjaman serta pengembalian buku. Hasil wawancara lengkap dengan pengelola perpustakaan terlampir pada lampiran 1.
|
||||
|
||||
|
||||
|
||||
|
||||
Pengumuman:
|
||||
Siswa dapat melihat pengumuman dari perpustakaan yang berisi informasi umum, seperti jadwal layanan, informasi buku baru, kegiatan perpustakaan, atau pemberitahuan lain yang ditujukan kepada seluruh pengguna.
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,25 @@
|
|||
<?php
|
||||
|
||||
namespace Database\Factories;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
|
||||
/**
|
||||
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\MasterInduk>
|
||||
*/
|
||||
class MasterIndukFactory extends Factory
|
||||
{
|
||||
/**
|
||||
* Define the model's default state.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function definition(): array
|
||||
{
|
||||
return [
|
||||
'nomor_induk' => fake()->unique()->numerify('##########'),
|
||||
'role' => 'siswa',
|
||||
'nama_pemilik' => fake()->name(),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -13,6 +13,7 @@ public function up(): void
|
|||
{
|
||||
Schema::create('announcements', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('user_id')->constrained()->onDelete('cascade');
|
||||
$table->string('type'); // warning, info, success, etc.
|
||||
$table->string('icon');
|
||||
$table->string('title');
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ public function up(): void
|
|||
{
|
||||
Schema::create('recommendations', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('user_id')->constrained()->onDelete('cascade');
|
||||
$table->string('judul');
|
||||
$table->string('kategori');
|
||||
$table->string('youtube_link');
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ public function up()
|
|||
{
|
||||
Schema::create('master_induks', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('user_id')->constrained()->onDelete('cascade');
|
||||
$table->string('nomor_induk')->unique();
|
||||
$table->enum('role', ['siswa', 'guru']);
|
||||
$table->string('nama_pemilik')->nullable();
|
||||
|
|
|
|||
|
|
@ -16,9 +16,12 @@ public function run(): void
|
|||
$announcements = DummyDataService::getPengumuman();
|
||||
|
||||
foreach ($announcements as $data) {
|
||||
$adminId = \App\Models\User::where('role', 'penjaga perpus')->first()?->id ?? 2;
|
||||
|
||||
Announcement::updateOrCreate(
|
||||
['id' => $data['id']],
|
||||
[
|
||||
'user_id' => $adminId,
|
||||
'type' => $data['type'],
|
||||
'icon' => $data['icon'],
|
||||
'title' => $data['title'],
|
||||
|
|
|
|||
|
|
@ -18,6 +18,16 @@ public function run()
|
|||
MasterInduk::truncate();
|
||||
DB::statement('SET FOREIGN_KEY_CHECKS=1;');
|
||||
|
||||
// ID 2: Budi (Admin/Penjaga)
|
||||
$admin = User::create([
|
||||
'id' => 2,
|
||||
'name' => 'Budi Santoso',
|
||||
'email' => 'budi.santoso@smkn1perpus.sch.id',
|
||||
'password' => Hash::make('password'),
|
||||
'role' => 'penjaga perpus',
|
||||
'nomor_induk' => '197812312005011',
|
||||
]);
|
||||
|
||||
$whitelist = [
|
||||
['nomor_induk' => '1234567890', 'role' => 'siswa', 'nama_pemilik' => 'Silvi Rahmawati'],
|
||||
['nomor_induk' => '9988776655', 'role' => 'siswa', 'nama_pemilik' => 'Siti Nurhaliza'],
|
||||
|
|
@ -26,7 +36,7 @@ public function run()
|
|||
];
|
||||
|
||||
foreach ($whitelist as $w) {
|
||||
MasterInduk::create($w);
|
||||
MasterInduk::create(array_merge($w, ['user_id' => $admin->id]));
|
||||
}
|
||||
|
||||
// ISI USER ASLI
|
||||
|
|
@ -44,16 +54,6 @@ public function run()
|
|||
'golongan' => 'A',
|
||||
]);
|
||||
|
||||
// ID 2: Budi (Admin/Penjaga)
|
||||
User::create([
|
||||
'id' => 2,
|
||||
'name' => 'Budi Santoso',
|
||||
'email' => 'budi.santoso@smkn1perpus.sch.id',
|
||||
'password' => Hash::make('password'),
|
||||
'role' => 'penjaga perpus',
|
||||
'nomor_induk' => '197812312005011',
|
||||
]);
|
||||
|
||||
// ID 3: Siti (Siswa)
|
||||
User::create([
|
||||
'id' => 3,
|
||||
|
|
@ -96,6 +96,7 @@ public function run()
|
|||
AnnouncementSeeder::class,
|
||||
RecommendationSeeder::class,
|
||||
LoanSeeder::class,
|
||||
UserSeeder::class,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,9 +16,12 @@ public function run(): void
|
|||
$recommendations = DummyDataService::getRekomendasiPembelajaran();
|
||||
|
||||
foreach ($recommendations as $data) {
|
||||
$adminId = \App\Models\User::where('role', 'penjaga perpus')->first()?->id ?? 2;
|
||||
|
||||
Recommendation::updateOrCreate(
|
||||
['id' => $data['id']],
|
||||
[
|
||||
'user_id' => $adminId,
|
||||
'judul' => $data['judul'],
|
||||
'kategori' => $data['kategori'],
|
||||
'youtube_link' => $data['youtube_link'],
|
||||
|
|
|
|||
|
|
@ -49,5 +49,33 @@ public function run(): void
|
|||
'role' => 'penjaga perpus',
|
||||
]
|
||||
);
|
||||
|
||||
// Generate 95 additional users to reach around 100 total
|
||||
$additionalCount = 95;
|
||||
for ($i = 0; $i < $additionalCount; $i++) {
|
||||
$nomorInduk = fake()->unique()->numerify('##########');
|
||||
$name = fake()->name();
|
||||
|
||||
$adminId = User::where('role', 'penjaga perpus')->first()?->id ?? 2;
|
||||
|
||||
\App\Models\MasterInduk::create([
|
||||
'user_id' => $adminId,
|
||||
'nomor_induk' => $nomorInduk,
|
||||
'role' => 'siswa',
|
||||
'nama_pemilik' => $name,
|
||||
]);
|
||||
|
||||
User::create([
|
||||
'name' => $name,
|
||||
'nama_lengkap' => $name,
|
||||
'email' => fake()->unique()->safeEmail(),
|
||||
'password' => Hash::make('password'),
|
||||
'nomor_induk' => $nomorInduk,
|
||||
'role' => 'siswa',
|
||||
'phone' => fake()->phoneNumber(),
|
||||
'kelas' => fake()->randomElement(['X RPL', 'XI RPL', 'XII RPL', 'X TKJ', 'XI TKJ', 'XII TKJ']),
|
||||
'golongan' => fake()->randomElement(['A', 'B', 'C']),
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,178 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
'accepted' => ':attribute harus diterima.',
|
||||
'accepted_if' => ':attribute harus diterima ketika :other adalah :value.',
|
||||
'active_url' => ':attribute bukan URL yang valid.',
|
||||
'after' => ':attribute harus berisi tanggal setelah :date.',
|
||||
'after_or_equal' => ':attribute harus berisi tanggal setelah atau sama dengan :date.',
|
||||
'alpha' => ':attribute hanya boleh berisi huruf.',
|
||||
'alpha_dash' => ':attribute hanya boleh berisi huruf, angka, strip, dan garis bawah.',
|
||||
'alpha_num' => ':attribute hanya boleh berisi huruf dan angka.',
|
||||
'array' => ':attribute harus berisi sebuah array.',
|
||||
'ascii' => ':attribute hanya boleh berisi karakter alfanumerik dan simbol satu bait.',
|
||||
'before' => ':attribute harus berisi tanggal sebelum :date.',
|
||||
'before_or_equal' => ':attribute harus berisi tanggal sebelum atau sama dengan :date.',
|
||||
'between' => [
|
||||
'array' => ':attribute harus memiliki antara :min dan :max anggota.',
|
||||
'file' => ':attribute harus berukuran antara :min dan :max kilobita.',
|
||||
'numeric' => ':attribute harus bernilai antara :min dan :max.',
|
||||
'string' => ':attribute harus berisi antara :min dan :max karakter.',
|
||||
],
|
||||
'boolean' => ':attribute harus bernilai true atau false.',
|
||||
'can' => ':attribute berisi nilai yang tidak sah.',
|
||||
'confirmed' => 'Konfirmasi :attribute tidak cocok.',
|
||||
'contains' => ':attribute tidak memiliki nilai yang dibutuhkan.',
|
||||
'current_password' => 'Kata sandi salah.',
|
||||
'date' => ':attribute bukan tanggal yang valid.',
|
||||
'date_equals' => ':attribute harus berisi tanggal yang sama dengan :date.',
|
||||
'date_format' => ':attribute tidak cocok dengan format :format.',
|
||||
'decimal' => ':attribute harus memiliki :decimal tempat desimal.',
|
||||
'declined' => ':attribute harus ditolak.',
|
||||
'declined_if' => ':attribute harus ditolak ketika :other adalah :value.',
|
||||
'different' => ':attribute dan :other harus berbeda.',
|
||||
'digits' => ':attribute harus terdiri dari :digits angka.',
|
||||
'digits_between' => ':attribute harus terdiri dari antara :min dan :max angka.',
|
||||
'dimensions' => ':attribute tidak memiliki dimensi gambar yang valid.',
|
||||
'distinct' => ':attribute memiliki nilai yang duplikat.',
|
||||
'doesnt_end_with' => ':attribute tidak boleh diakhiri dengan salah satu dari: :values.',
|
||||
'doesnt_start_with' => ':attribute tidak boleh diawali dengan salah satu dari: :values.',
|
||||
'email' => ':attribute harus berupa alamat surel yang valid.',
|
||||
'ends_with' => ':attribute harus diakhiri dengan salah satu dari: :values.',
|
||||
'enum' => ':attribute yang dipilih tidak valid.',
|
||||
'exists' => ':attribute yang dipilih tidak valid.',
|
||||
'extensions' => ':attribute harus memiliki salah satu ekstensi berikut: :values.',
|
||||
'file' => ':attribute harus berupa sebuah berkas.',
|
||||
'filled' => ':attribute harus memiliki nilai.',
|
||||
'gt' => [
|
||||
'array' => ':attribute harus memiliki lebih dari :value anggota.',
|
||||
'file' => ':attribute harus berukuran lebih dari :value kilobita.',
|
||||
'numeric' => ':attribute harus bernilai lebih dari :value.',
|
||||
'string' => ':attribute harus berisi lebih dari :value karakter.',
|
||||
],
|
||||
'gte' => [
|
||||
'array' => ':attribute harus memiliki :value anggota atau lebih.',
|
||||
'file' => ':attribute harus berukuran lebih dari atau sama dengan :value kilobita.',
|
||||
'numeric' => ':attribute harus bernilai lebih dari atau sama dengan :value.',
|
||||
'string' => ':attribute harus berisi lebih dari atau sama dengan :value karakter.',
|
||||
],
|
||||
'hex_color' => ':attribute harus berupa warna heksadesimal yang valid.',
|
||||
'image' => ':attribute harus berupa gambar.',
|
||||
'in' => ':attribute yang dipilih tidak valid.',
|
||||
'in_array' => ':attribute tidak ada di dalam :other.',
|
||||
'integer' => ':attribute harus berupa bilangan bulat.',
|
||||
'ip' => ':attribute harus berupa alamat IP yang valid.',
|
||||
'ipv4' => ':attribute harus berupa alamat IPv4 yang valid.',
|
||||
'ipv6' => ':attribute harus berupa alamat IPv6 yang valid.',
|
||||
'json' => ':attribute harus berupa string JSON yang valid.',
|
||||
'list' => ':attribute harus berupa daftar.',
|
||||
'lowercase' => ':attribute harus berupa huruf kecil.',
|
||||
'lt' => [
|
||||
'array' => ':attribute harus memiliki kurang dari :value anggota.',
|
||||
'file' => ':attribute harus berukuran kurang dari :value kilobita.',
|
||||
'numeric' => ':attribute harus bernilai kurang dari :value.',
|
||||
'string' => ':attribute harus berisi kurang dari :value karakter.',
|
||||
],
|
||||
'lte' => [
|
||||
'array' => ':attribute tidak boleh memiliki lebih dari :value anggota.',
|
||||
'file' => ':attribute harus berukuran kurang dari atau sama dengan :value kilobita.',
|
||||
'numeric' => ':attribute harus bernilai kurang dari atau sama dengan :value.',
|
||||
'string' => ':attribute harus berisi kurang dari atau sama dengan :value karakter.',
|
||||
],
|
||||
'mac_address' => ':attribute harus berupa alamat MAC yang valid.',
|
||||
'max' => [
|
||||
'array' => ':attribute maksimal terdiri dari :max anggota.',
|
||||
'file' => ':attribute maksimal berukuran :max kilobita.',
|
||||
'numeric' => ':attribute maksimal bernilai :max.',
|
||||
'string' => ':attribute maksimal berisi :max karakter.',
|
||||
],
|
||||
'max_digits' => ':attribute tidak boleh memiliki lebih dari :max digit.',
|
||||
'mimes' => ':attribute harus berupa berkas berjenis: :values.',
|
||||
'mimetypes' => ':attribute harus berupa berkas berjenis: :values.',
|
||||
'min' => [
|
||||
'array' => ':attribute minimal terdiri dari :min anggota.',
|
||||
'file' => ':attribute minimal berukuran :min kilobita.',
|
||||
'numeric' => ':attribute minimal bernilai :min.',
|
||||
'string' => ':attribute minimal berisi :min karakter.',
|
||||
],
|
||||
'min_digits' => ':attribute harus memiliki setidaknya :min digit.',
|
||||
'missing' => ':attribute harus tidak ada.',
|
||||
'missing_if' => ':attribute harus tidak ada ketika :other adalah :value.',
|
||||
'missing_unless' => ':attribute harus tidak ada kecuali :other adalah :value.',
|
||||
'missing_with' => ':attribute harus tidak ada ketika :values ada.',
|
||||
'missing_with_all' => ':attribute harus tidak ada ketika :values ada.',
|
||||
'multiple_of' => ':attribute harus merupakan kelipatan dari :value.',
|
||||
'not_in' => ':attribute yang dipilih tidak valid.',
|
||||
'not_regex' => 'Format :attribute tidak valid.',
|
||||
'numeric' => ':attribute harus berupa angka.',
|
||||
'password' => [
|
||||
'letters' => ':attribute harus berisi setidaknya satu huruf.',
|
||||
'mixed' => ':attribute harus berisi setidaknya satu huruf besar dan satu huruf kecil.',
|
||||
'numbers' => ':attribute harus berisi setidaknya satu angka.',
|
||||
'symbols' => ':attribute harus berisi setidaknya satu simbol.',
|
||||
'uncompromised' => ':attribute yang diberikan telah muncul dalam kebocoran data. Silakan pilih :attribute yang berbeda.',
|
||||
],
|
||||
'present' => ':attribute harus ada.',
|
||||
'present_if' => ':attribute harus ada ketika :other adalah :value.',
|
||||
'present_unless' => ':attribute harus ada kecuali :other adalah :value.',
|
||||
'present_with' => ':attribute harus ada ketika :values ada.',
|
||||
'present_with_all' => ':attribute harus ada ketika :values ada.',
|
||||
'prohibited' => ':attribute dilarang.',
|
||||
'prohibited_if' => ':attribute dilarang ketika :other adalah :value.',
|
||||
'prohibited_unless' => ':attribute dilarang kecuali :other adalah :value.',
|
||||
'prohibits' => ':attribute melarang :other dari ada.',
|
||||
'regex' => 'Format :attribute tidak valid.',
|
||||
'required' => ':attribute wajib diisi.',
|
||||
'required_array_keys' => ':attribute harus memiliki entri untuk: :values.',
|
||||
'required_if' => ':attribute wajib diisi ketika :other adalah :value.',
|
||||
'required_if_accepted' => ':attribute wajib diisi ketika :other diterima.',
|
||||
'required_if_declined' => ':attribute wajib diisi ketika :other ditolak.',
|
||||
'required_unless' => ':attribute wajib diisi kecuali :other memiliki nilai :values.',
|
||||
'required_with' => ':attribute wajib diisi ketika :values ada.',
|
||||
'required_with_all' => ':attribute wajib diisi ketika :values ada.',
|
||||
'required_without' => ':attribute wajib diisi ketika :values tidak ada.',
|
||||
'required_without_all' => ':attribute wajib diisi ketika tidak ada :values yang ada.',
|
||||
'same' => ':attribute dan :other harus sama.',
|
||||
'size' => [
|
||||
'array' => ':attribute harus mengandung :size anggota.',
|
||||
'file' => ':attribute harus berukuran :size kilobita.',
|
||||
'numeric' => ':attribute harus berukuran :size.',
|
||||
'string' => ':attribute harus berukuran :size karakter.',
|
||||
],
|
||||
'starts_with' => ':attribute harus diawali dengan salah satu dari: :values.',
|
||||
'string' => ':attribute harus berupa string.',
|
||||
'timezone' => ':attribute harus berisi zona waktu yang valid.',
|
||||
'unique' => ':attribute sudah ada sebelumnya.',
|
||||
'uploaded' => ':attribute gagal diunggah.',
|
||||
'uppercase' => ':attribute harus berupa huruf besar.',
|
||||
'url' => ':attribute harus berupa URL yang valid.',
|
||||
'ulid' => ':attribute harus berupa ULID yang valid.',
|
||||
'uuid' => ':attribute harus berupa UUID yang valid.',
|
||||
|
||||
'custom' => [
|
||||
'attribute-name' => [
|
||||
'rule-name' => 'custom-message',
|
||||
],
|
||||
],
|
||||
|
||||
'attributes' => [
|
||||
'judul' => 'Judul Buku',
|
||||
'penulis' => 'Penulis',
|
||||
'category_id' => 'Kategori',
|
||||
'tahun' => 'Tahun Terbit',
|
||||
'kode_buku' => 'Kode Buku',
|
||||
'stok' => 'Stok',
|
||||
'tipe_akses' => 'Tipe Akses',
|
||||
'cover' => 'Cover Buku',
|
||||
'file_pdf' => 'File PDF',
|
||||
'nama_lengkap' => 'Nama Lengkap',
|
||||
'nama_pemilik' => 'Nama Pemilik',
|
||||
'nomor_induk' => 'Nomor Induk',
|
||||
'phone' => 'Nomor Telepon',
|
||||
'title' => 'Judul',
|
||||
'content' => 'Konten',
|
||||
'type' => 'Tipe',
|
||||
'icon' => 'Ikon',
|
||||
'kategori' => 'Kategori',
|
||||
],
|
||||
];
|
||||
|
|
@ -825,6 +825,7 @@
|
|||
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
|
||||
"integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/popperjs"
|
||||
|
|
@ -2589,6 +2590,7 @@
|
|||
"resolved": "https://registry.npmjs.org/sass/-/sass-1.93.2.tgz",
|
||||
"integrity": "sha512-t+YPtOQHpGW1QWsh1CHQ5cPIr9lbbGZLZnbihP/D/qZj/yuV68m8qarcV17nvkOX81BCrvzAlq2klCQFZghyTg==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"chokidar": "^4.0.0",
|
||||
"immutable": "^5.0.2",
|
||||
|
|
@ -2675,7 +2677,8 @@
|
|||
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.16.tgz",
|
||||
"integrity": "sha512-pONL5awpaQX4LN5eiv7moSiSPd/DLDzKVRJz8Q9PgzmAdd1R4307GQS2ZpfiN7ZmekdQrfhZZiSE5jkLR4WNaA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/tapable": {
|
||||
"version": "2.3.0",
|
||||
|
|
@ -2732,6 +2735,7 @@
|
|||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
|
|
@ -2775,6 +2779,7 @@
|
|||
"integrity": "sha512-ZWyE8YXEXqJrrSLvYgrRP7p62OziLW7xI5HYGWFzOvupfAlrLvURSzv/FyGyy0eidogEM3ujU+kUG1zuHgb6Ug==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"esbuild": "^0.25.0",
|
||||
"fdir": "^6.5.0",
|
||||
|
|
@ -2879,6 +2884,7 @@
|
|||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
|
|
|
|||
Binary file not shown.
|
After Width: | Height: | Size: 27 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 180 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 33 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 478 KiB |
|
|
@ -18,39 +18,62 @@
|
|||
<div class="row">
|
||||
<div class="col-md-8">
|
||||
<div class="mb-3">
|
||||
<label for="judul" class="form-label">Judul Buku</label>
|
||||
<input type="text" name="judul" class="form-control" id="judul"
|
||||
placeholder="Masukkan judul buku" required>
|
||||
<label for="judul" class="form-label">Judul Buku <span class="text-danger">*</span></label>
|
||||
<input type="text" name="judul" class="form-control @error('judul') is-invalid @enderror" id="judul"
|
||||
placeholder="Masukkan judul buku" value="{{ old('judul') }}" maxlength="50">
|
||||
@error('judul')
|
||||
<div class="invalid-feedback">{{ $message }}</div>
|
||||
@enderror
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="penulis" class="form-label">Penulis</label>
|
||||
<input type="text" name="penulis" class="form-control" id="penulis"
|
||||
placeholder="Masukkan nama penulis" required>
|
||||
<label for="penulis" class="form-label">Penulis <span class="text-danger">*</span></label>
|
||||
<input type="text" name="penulis" class="form-control @error('penulis') is-invalid @enderror" id="penulis"
|
||||
placeholder="Masukkan nama penulis" value="{{ old('penulis') }}" maxlength="50" pattern="[a-zA-Z\s.,'\']+">
|
||||
@error('penulis')
|
||||
<div class="invalid-feedback">{{ $message }}</div>
|
||||
@enderror
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-3 mb-3">
|
||||
<label for="category_id" class="form-label">Kategori</label>
|
||||
<select name="category_id" class="form-select" id="category_id" required>
|
||||
<label for="category_id" class="form-label">Kategori <span class="text-danger">*</span></label>
|
||||
<select name="category_id" class="form-select @error('category_id') is-invalid @enderror" id="category_id">
|
||||
<option value="" disabled selected>Pilih Kategori</option>
|
||||
@foreach($categories as $category)
|
||||
<option value="{{ $category->id }}">{{ $category->name }}</option>
|
||||
<option value="{{ $category->id }}" {{ old('category_id') == $category->id ? 'selected' : '' }}>{{ $category->name }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
@error('category_id')
|
||||
<div class="invalid-feedback">{{ $message }}</div>
|
||||
@enderror
|
||||
</div>
|
||||
<div class="col-md-3 mb-3">
|
||||
<label for="tahun" class="form-label">Tahun Terbit</label>
|
||||
<input type="number" name="tahun" class="form-control" id="tahun"
|
||||
placeholder="Contoh: 2024" min="0" required>
|
||||
<label for="tahun" class="form-label">Tahun Terbit <span class="text-danger">*</span></label>
|
||||
<select name="tahun" class="form-select @error('tahun') is-invalid @enderror" id="tahun">
|
||||
<option value="" disabled selected>Pilih Tahun</option>
|
||||
@for ($year = date('Y') + 1; $year >= 1500; $year--)
|
||||
<option value="{{ $year }}" {{ old('tahun') == $year ? 'selected' : '' }}>{{ $year }}</option>
|
||||
@endfor
|
||||
</select>
|
||||
@error('tahun')
|
||||
<div class="invalid-feedback">{{ $message }}</div>
|
||||
@enderror
|
||||
</div>
|
||||
<div class="col-md-3 mb-3">
|
||||
<label for="kode_buku" class="form-label">Kode Buku</label>
|
||||
<input type="text" name="kode_buku" class="form-control" id="kode_buku"
|
||||
placeholder="Contoh: 330">
|
||||
<input type="text" name="kode_buku" class="form-control @error('kode_buku') is-invalid @enderror" id="kode_buku"
|
||||
value="{{ old('kode_buku') }}" placeholder="Contoh: 330" maxlength="10"
|
||||
inputmode="numeric" oninput="this.value = this.value.replace(/[^0-9]/g, '')">
|
||||
@error('kode_buku')
|
||||
<div class="invalid-feedback">{{ $message }}</div>
|
||||
@enderror
|
||||
</div>
|
||||
<div class="col-md-3 mb-3">
|
||||
<label for="stok" class="form-label">Stok</label>
|
||||
<input type="number" name="stok" class="form-control" id="stok"
|
||||
value="1" min="0" required>
|
||||
<label for="stok" class="form-label">Stok <span class="text-danger">*</span></label>
|
||||
<input type="number" name="stok" class="form-control @error('stok') is-invalid @enderror" id="stok"
|
||||
value="{{ old('stok', 1) }}" min="0" max="99" oninput="if(this.value.length > 2) this.value = this.value.slice(0, 2); this.value = this.value.replace(/[^0-9]/g, '')">
|
||||
@error('stok')
|
||||
<div class="invalid-feedback">{{ $message }}</div>
|
||||
@enderror
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -60,24 +83,33 @@
|
|||
<label class="form-label">Tipe Akses</label>
|
||||
<div class="d-flex gap-3">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" name="tipe_akses[]" value="offline" id="tipe_offline">
|
||||
<input class="form-check-input @error('tipe_akses') is-invalid @enderror" type="checkbox" name="tipe_akses[]" value="offline" id="tipe_offline" {{ is_array(old('tipe_akses')) && in_array('offline', old('tipe_akses')) ? 'checked' : '' }}>
|
||||
<label class="form-check-label" for="tipe_offline">Peminjaman
|
||||
Offline</label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" name="tipe_akses[]" value="online" id="tipe_online">
|
||||
<input class="form-check-input @error('tipe_akses') is-invalid @enderror" type="checkbox" name="tipe_akses[]" value="online" id="tipe_online" {{ is_array(old('tipe_akses')) && in_array('online', old('tipe_akses')) ? 'checked' : '' }}>
|
||||
<label class="form-check-label" for="tipe_online">Baca Online</label>
|
||||
</div>
|
||||
</div>
|
||||
@error('tipe_akses')
|
||||
<div class="text-danger small mt-1">{{ $message }}</div>
|
||||
@enderror
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="mb-3">
|
||||
<label for="cover" class="form-label">Cover Buku</label>
|
||||
<input type="file" name="cover" class="form-control" id="cover">
|
||||
<input type="file" name="cover" class="form-control @error('cover') is-invalid @enderror" id="cover" accept="image/*">
|
||||
@error('cover')
|
||||
<div class="invalid-feedback">{{ $message }}</div>
|
||||
@enderror
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="file_pdf" class="form-label">File PDF (untuk buku online)</label>
|
||||
<input type="file" name="file_pdf" class="form-control" id="file_pdf">
|
||||
<input type="file" name="file_pdf" class="form-control @error('file_pdf') is-invalid @enderror" id="file_pdf" accept=".pdf">
|
||||
@error('file_pdf')
|
||||
<div class="invalid-feedback">{{ $message }}</div>
|
||||
@enderror
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -97,24 +129,7 @@
|
|||
@push('scripts')
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// LOGIC SIMPAN
|
||||
document.getElementById('formTambahBuku').addEventListener('submit', function(e) {
|
||||
e.preventDefault();
|
||||
modernSwal.fire({
|
||||
title: 'Menyimpan...',
|
||||
timer: 1500,
|
||||
didOpen: () => Swal.showLoading()
|
||||
}).then(() => {
|
||||
Toast.fire({
|
||||
icon: 'success',
|
||||
title: 'Berhasil',
|
||||
text: 'Buku baru berhasil ditambahkan.'
|
||||
});
|
||||
setTimeout(() => {
|
||||
window.location.href = "{{ route('admin.buku.index') }}";
|
||||
}, 1500);
|
||||
});
|
||||
});
|
||||
// Script removed to allow standard server-side validation
|
||||
});
|
||||
</script>
|
||||
@endpush
|
||||
|
|
|
|||
|
|
@ -19,35 +19,55 @@
|
|||
<div class="row">
|
||||
<div class="col-md-8">
|
||||
<div class="mb-3">
|
||||
<label for="judul" class="form-label">Judul Buku</label>
|
||||
<input type="text" name="judul" class="form-control" id="judul"
|
||||
value="{{ old('judul', $buku->judul) }}" required>
|
||||
<label for="judul" class="form-label">Judul Buku <span class="text-danger">*</span></label>
|
||||
<input type="text" name="judul" class="form-control @error('judul') is-invalid @enderror" id="judul"
|
||||
value="{{ old('judul', $buku->judul) }}" maxlength="50">
|
||||
@error('judul')
|
||||
<div class="invalid-feedback">{{ $message }}</div>
|
||||
@enderror
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="penulis" class="form-label">Penulis</label>
|
||||
<input type="text" name="penulis" class="form-control" id="penulis"
|
||||
value="{{ old('penulis', $buku->penulis) }}" required>
|
||||
<label for="penulis" class="form-label">Penulis <span class="text-danger">*</span></label>
|
||||
<input type="text" name="penulis" class="form-control @error('penulis') is-invalid @enderror" id="penulis"
|
||||
value="{{ old('penulis', $buku->penulis) }}" maxlength="50" pattern="[a-zA-Z\s.,'\']+">
|
||||
@error('penulis')
|
||||
<div class="invalid-feedback">{{ $message }}</div>
|
||||
@enderror
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-4 mb-3">
|
||||
<label for="category_id" class="form-label">Kategori</label>
|
||||
<select name="category_id" class="form-select" id="category_id" required>
|
||||
<label for="category_id" class="form-label">Kategori <span class="text-danger">*</span></label>
|
||||
<select name="category_id" class="form-select @error('category_id') is-invalid @enderror" id="category_id">
|
||||
@foreach($categories as $category)
|
||||
<option value="{{ $category->id }}" {{ old('category_id', $buku->category_id) == $category->id ? 'selected' : '' }}>
|
||||
{{ $category->name }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
@error('category_id')
|
||||
<div class="invalid-feedback">{{ $message }}</div>
|
||||
@enderror
|
||||
</div>
|
||||
<div class="col-md-4 mb-3">
|
||||
<label for="tahun" class="form-label">Tahun Terbit</label>
|
||||
<input type="number" name="tahun" class="form-control" id="tahun"
|
||||
value="{{ old('tahun', $buku->tahun) }}" required>
|
||||
<label for="tahun" class="form-label">Tahun Terbit <span class="text-danger">*</span></label>
|
||||
<select name="tahun" class="form-select @error('tahun') is-invalid @enderror" id="tahun">
|
||||
@for ($year = date('Y') + 1; $year >= 1500; $year--)
|
||||
<option value="{{ $year }}" {{ old('tahun', $buku->tahun) == $year ? 'selected' : '' }}>
|
||||
{{ $year }}
|
||||
</option>
|
||||
@endfor
|
||||
</select>
|
||||
@error('tahun')
|
||||
<div class="invalid-feedback">{{ $message }}</div>
|
||||
@enderror
|
||||
</div>
|
||||
<div class="col-md-4 mb-3">
|
||||
<label for="stok" class="form-label">Stok</label>
|
||||
<input type="number" name="stok" class="form-control" id="stok"
|
||||
value="{{ old('stok', $buku->stok ?? 1) }}" min="0" required>
|
||||
<label for="stok" class="form-label">Stok <span class="text-danger">*</span></label>
|
||||
<input type="number" name="stok" class="form-control @error('stok') is-invalid @enderror" id="stok"
|
||||
value="{{ old('stok', $buku->stok ?? 1) }}" min="0" max="99" oninput="if(this.value.length > 2) this.value = this.value.slice(0, 2); this.value = this.value.replace(/[^0-9]/g, '')">
|
||||
@error('stok')
|
||||
<div class="invalid-feedback">{{ $message }}</div>
|
||||
@enderror
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -61,36 +81,49 @@
|
|||
{{-- Field Kode Buku - Hanya untuk Buku Offline --}}
|
||||
<div class="mb-3" id="kodeBukuWrapper">
|
||||
<label for="kode_buku" class="form-label">Kode Buku</label>
|
||||
<input type="text" name="kode_buku" class="form-control" id="kode_buku"
|
||||
value="{{ old('kode_buku', $buku->kode_buku) }}" placeholder="Masukkan kode buku">
|
||||
<input type="text" name="kode_buku" class="form-control @error('kode_buku') is-invalid @enderror" id="kode_buku"
|
||||
value="{{ old('kode_buku', $buku->kode_buku) }}" placeholder="Masukkan kode buku" maxlength="10"
|
||||
inputmode="numeric" oninput="this.value = this.value.replace(/[^0-9]/g, '')">
|
||||
@error('kode_buku')
|
||||
<div class="invalid-feedback">{{ $message }}</div>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Tipe Akses</label>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" name="tipe_akses[]" value="offline" id="tipe_offline"
|
||||
@if (in_array('offline', $tipe_akses)) checked @endif>
|
||||
<input class="form-check-input @error('tipe_akses') is-invalid @enderror" type="checkbox" name="tipe_akses[]" value="offline" id="tipe_offline"
|
||||
@if (in_array('offline', old('tipe_akses', $tipe_akses))) checked @endif>
|
||||
<label class="form-check-label" for="tipe_offline">Peminjaman
|
||||
Offline</label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" name="tipe_akses[]" value="online" id="tipe_online"
|
||||
@if (in_array('online', $tipe_akses)) checked @endif>
|
||||
<input class="form-check-input @error('tipe_akses') is-invalid @enderror" type="checkbox" name="tipe_akses[]" value="online" id="tipe_online"
|
||||
@if (in_array('online', old('tipe_akses', $tipe_akses))) checked @endif>
|
||||
<label class="form-check-label" for="tipe_online">Baca Online</label>
|
||||
</div>
|
||||
@error('tipe_akses')
|
||||
<div class="text-danger small mt-1">{{ $message }}</div>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="mb-3">
|
||||
<label for="cover" class="form-label">Cover Buku</label>
|
||||
<input type="file" name="cover" class="form-control" id="cover">
|
||||
<input type="file" name="cover" class="form-control @error('cover') is-invalid @enderror" id="cover" accept="image/*">
|
||||
@error('cover')
|
||||
<div class="invalid-feedback">{{ $message }}</div>
|
||||
@enderror
|
||||
<img src="{{ asset($buku->cover) }}" alt="Cover saat ini"
|
||||
class="img-thumbnail mt-2" width="150">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="file_pdf" class="form-label">Update File PDF (Opsional)</label>
|
||||
<input type="file" name="file_pdf" class="form-control" id="file_pdf">
|
||||
<input type="file" name="file_pdf" class="form-control @error('file_pdf') is-invalid @enderror" id="file_pdf" accept=".pdf">
|
||||
@error('file_pdf')
|
||||
<div class="invalid-feedback">{{ $message }}</div>
|
||||
@enderror
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -107,24 +140,8 @@ class="img-thumbnail mt-2" width="150">
|
|||
|
||||
@push('scripts')
|
||||
<script>
|
||||
document.getElementById('formEditBuku').addEventListener('submit', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
modernSwal.fire({
|
||||
title: 'Menyimpan Perubahan...',
|
||||
timer: 800,
|
||||
didOpen: () => Swal.showLoading()
|
||||
}).then(() => {
|
||||
Toast.fire({
|
||||
icon: 'success',
|
||||
title: 'Berhasil',
|
||||
text: 'Data buku berhasil diperbarui.'
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
window.location.href = "{{ route('admin.buku.index') }}";
|
||||
}, 1500);
|
||||
});
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// Script removed to allow standard server-side validation
|
||||
});
|
||||
</script>
|
||||
@endpush
|
||||
|
|
|
|||
|
|
@ -69,16 +69,9 @@ class="badge bg-warning-subtle text-warning-emphasis">Dipinjam</span>
|
|||
@endif
|
||||
</td>
|
||||
<td>
|
||||
<button class="btn btn-sm btn-outline-secondary" data-bs-toggle="modal"
|
||||
data-bs-target="#detailBukuModal" data-id="{{ $buku['id'] }}"
|
||||
data-cover="{{ asset($buku['cover']) }}"
|
||||
data-judul="{{ $buku['judul'] }}"
|
||||
data-kode_buku="{{ $buku['kode_buku'] }}"
|
||||
data-penulis="{{ $buku['penulis'] }}"
|
||||
data-kategori="{{ $buku->category->name ?? '-' }}"
|
||||
data-tahun="{{ $buku['tahun'] }}" data-status="{{ $buku['status'] }}" data-stok="{{ $buku['stok'] ?? 0 }}">
|
||||
<a href="{{ route('admin.buku.show', $buku->id) }}" class="btn btn-sm btn-outline-secondary">
|
||||
<i class="bi bi-eye-fill"></i> Detail
|
||||
</button>
|
||||
</a>
|
||||
<button class="btn btn-sm btn-outline-warning btn-arsipkan"
|
||||
data-id="{{ $buku->id }}" data-judul="{{ $buku->judul }}" data-penulis="{{ $buku->penulis }}" title="Arsipkan Buku">
|
||||
<i class="bi bi-archive-fill"></i> Arsip
|
||||
|
|
@ -118,15 +111,9 @@ class="badge bg-warning-subtle text-warning-emphasis">Dipinjam</span>
|
|||
class="badge bg-info-subtle text-info-emphasis">{{ $buku['file_pdf'] ?? 'N/A' }}</span>
|
||||
</td>
|
||||
<td>
|
||||
<button class="btn btn-sm btn-outline-secondary" data-bs-toggle="modal"
|
||||
data-bs-target="#detailBukuModal" data-id="{{ $buku['id'] }}"
|
||||
data-cover="{{ asset($buku['cover']) }}"
|
||||
data-judul="{{ $buku['judul'] }}"
|
||||
data-penulis="{{ $buku['penulis'] }}"
|
||||
data-kategori="{{ $buku->category->name ?? '-' }}"
|
||||
data-tahun="{{ $buku['tahun'] }}" data-status="Dapat Dibaca Online" data-stok="{{ $buku['stok'] ?? 0 }}">
|
||||
<a href="{{ route('admin.buku.show', $buku->id) }}" class="btn btn-sm btn-outline-secondary">
|
||||
<i class="bi bi-eye-fill"></i> Detail
|
||||
</button>
|
||||
</a>
|
||||
<button class="btn btn-sm btn-outline-warning btn-arsipkan"
|
||||
data-id="{{ $buku->id }}" data-judul="{{ $buku->judul }}" data-penulis="{{ $buku->penulis }}" title="Arsipkan Buku">
|
||||
<i class="bi bi-archive-fill"></i> Arsip
|
||||
|
|
@ -193,61 +180,6 @@ class="badge bg-info-subtle text-info-emphasis">{{ $buku['file_pdf'] ?? 'N/A' }}
|
|||
</div>
|
||||
</div>
|
||||
|
||||
{{-- MODAL DETAIL BUKU --}}
|
||||
<div class="modal fade" id="detailBukuModal" tabindex="-1">
|
||||
<div class="modal-dialog modal-lg">
|
||||
<div class="modal-content border-0 shadow">
|
||||
<div class="modal-header border-0">
|
||||
<h5 class="modal-title fw-bold" id="modalJudul">Detail Buku</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="row">
|
||||
<div class="col-md-4 text-center mb-3">
|
||||
<img src="" id="modalCover" class="img-fluid rounded shadow"
|
||||
style="max-height: 250px; object-fit: cover;">
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<h3 id="modalJudulContent" class="fw-bold text-dark mb-1"></h3>
|
||||
<p class="text-muted mb-3" id="modalPenulis"></p>
|
||||
|
||||
<div class="bg-light p-3 rounded-3">
|
||||
<table class="table table-sm table-borderless mb-0">
|
||||
<tr>
|
||||
<td class="text-secondary" width="130">Kategori</td>
|
||||
<td id="modalKategori" class="fw-bold"></td>
|
||||
</tr>
|
||||
<tr id="rowKodeBuku">
|
||||
<td class="text-secondary">Kode Buku</td>
|
||||
<td id="modalKode" class="fw-bold font-monospace"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="text-secondary">Tahun Terbit</td>
|
||||
<td id="modalTahun" class="fw-bold"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="text-secondary">Stok</td>
|
||||
<td id="modalStok" class="fw-bold"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="text-secondary">Status</td>
|
||||
<td><span id="modalStatus" class="badge"></span></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer border-0 bg-light">
|
||||
<button type="button" class="btn btn-secondary rounded-pill px-4"
|
||||
data-bs-dismiss="modal">Tutup</button>
|
||||
<a href="#" id="modalEditButton" class="btn btn-primary rounded-pill px-4">
|
||||
<i class="bi bi-pencil-square me-2"></i>Edit Buku
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@push('scripts')
|
||||
<script>
|
||||
|
|
@ -271,37 +203,6 @@ function updateTableNumbers() {
|
|||
});
|
||||
}
|
||||
|
||||
// LOGIC MODAL DETAIL
|
||||
$('#detailBukuModal').on('show.bs.modal', function(event) {
|
||||
const button = $(event.relatedTarget);
|
||||
|
||||
$('#modalCover').attr('src', button.data('cover'));
|
||||
$('#modalJudulContent').text(button.data('judul'));
|
||||
$('#modalPenulis').text('Penulis: ' + button.data('penulis'));
|
||||
$('#modalKategori').text(button.data('kategori'));
|
||||
$('#modalTahun').text(button.data('tahun'));
|
||||
$('#modalStok').text(button.data('stok') ? button.data('stok') + ' Buku' : '-');
|
||||
|
||||
const kodeBuku = button.data('kode_buku');
|
||||
if (kodeBuku) {
|
||||
$('#rowKodeBuku').show();
|
||||
$('#modalKode').text(kodeBuku);
|
||||
} else {
|
||||
$('#rowKodeBuku').hide();
|
||||
}
|
||||
|
||||
// Status Badge
|
||||
const statusBadge = $('#modalStatus');
|
||||
const status = button.data('status');
|
||||
statusBadge.removeClass().addClass('badge');
|
||||
if (status === 'Tersedia' || status === 'Dapat Dibaca Online' || !status) {
|
||||
statusBadge.addClass('bg-success-subtle text-success-emphasis').text('Tersedia / Online');
|
||||
} else {
|
||||
statusBadge.addClass('bg-warning-subtle text-warning-emphasis').text('Dipinjam');
|
||||
}
|
||||
|
||||
$('#modalEditButton').attr('href', "{{ url('admin/buku') }}/" + buku.id + "/edit");
|
||||
});
|
||||
|
||||
// LOGIC ARSIPKAN BUKU
|
||||
$(document).on('click', '.btn-arsipkan', function() {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,107 @@
|
|||
<x-app-layout>
|
||||
@section('page-title', $pageTitle)
|
||||
|
||||
<div class="d-flex align-items-center mb-4">
|
||||
<a href="{{ route('admin.buku.index') }}" class="btn btn-outline-secondary me-3 shadow-sm rounded-circle">
|
||||
<i class="bi bi-arrow-left"></i>
|
||||
</a>
|
||||
<h3 class="my-0 fw-bold">Detail Buku</h3>
|
||||
</div>
|
||||
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-lg-10">
|
||||
<div class="card border-0 shadow-sm overflow-hidden">
|
||||
<div class="card-body p-0">
|
||||
<div class="row g-0">
|
||||
{{-- Cover Section --}}
|
||||
<div class="col-md-4 bg-light d-flex align-items-center justify-content-center p-4 border-end">
|
||||
<div class="position-relative">
|
||||
<img src="{{ asset($buku->cover) }}" alt="{{ $buku->judul }}"
|
||||
class="img-fluid rounded shadow-lg" style="max-height: 400px; object-fit: cover;">
|
||||
@if($buku->is_arsip)
|
||||
<div class="position-absolute top-0 start-0 w-100 h-100 d-flex align-items-center justify-content-center rounded"
|
||||
style="background: rgba(0,0,0,0.5);">
|
||||
<span class="badge bg-warning text-dark fs-5 shadow">DIARSIPKAN</span>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Info Section --}}
|
||||
<div class="col-md-8">
|
||||
<div class="p-4 p-md-5">
|
||||
<div class="d-flex justify-content-between align-items-start mb-4">
|
||||
<div>
|
||||
<h2 class="fw-bold text-dark mb-1">{{ $buku->judul }}</h2>
|
||||
<p class="fs-5 text-muted mb-0">oleh <span class="text-primary fw-semibold">{{ $buku->penulis }}</span></p>
|
||||
</div>
|
||||
<div class="d-flex gap-2">
|
||||
<a href="{{ route('admin.buku.edit', $buku->id) }}" class="btn btn-warning rounded-pill">
|
||||
<i class="bi bi-pencil-square me-2"></i>Edit
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row g-4 mb-4">
|
||||
<div class="col-sm-6">
|
||||
<div class="p-3 border rounded-3 bg-light-subtle">
|
||||
<small class="text-muted d-block text-uppercase fw-bold mb-1" style="font-size: 0.7rem;">Kategori</small>
|
||||
<span class="fw-bold fs-5 text-dark">{{ $buku->category->name ?? '-' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<div class="p-3 border rounded-3 bg-light-subtle">
|
||||
<small class="text-muted d-block text-uppercase fw-bold mb-1" style="font-size: 0.7rem;">Tahun Terbit</small>
|
||||
<span class="fw-bold fs-5 text-dark">{{ $buku->tahun }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<div class="p-3 border rounded-3 bg-light-subtle">
|
||||
<small class="text-muted d-block text-uppercase fw-bold mb-1" style="font-size: 0.7rem;">Kode Buku</small>
|
||||
<code class="fw-bold fs-5 text-primary">{{ $buku->kode_buku ?? 'N/A' }}</code>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<div class="p-3 border rounded-3 bg-light-subtle">
|
||||
<small class="text-muted d-block text-uppercase fw-bold mb-1" style="font-size: 0.7rem;">Stok Tersedia</small>
|
||||
<span class="fw-bold fs-5 text-dark">{{ $buku->stok }} Buku</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
<h6 class="fw-bold text-dark mb-3">Tipe Akses:</h6>
|
||||
<div class="d-flex gap-2">
|
||||
@if(in_array('offline', $buku->tipe_akses ?? []))
|
||||
<span class="badge bg-secondary-subtle text-secondary-emphasis px-3 py-2 rounded-pill">
|
||||
<i class="bi bi-book-half me-2"></i>Peminjaman Offline
|
||||
</span>
|
||||
@endif
|
||||
@if(in_array('online', $buku->tipe_akses ?? []))
|
||||
<span class="badge bg-info-subtle text-info-emphasis px-3 py-2 rounded-pill">
|
||||
<i class="bi bi-globe2 me-2"></i>Baca Online
|
||||
</span>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if(in_array('online', $buku->tipe_akses ?? []) && $buku->file_pdf)
|
||||
<div class="border-top pt-4">
|
||||
<h6 class="fw-bold text-dark mb-2">File PDF Digital:</h6>
|
||||
<div class="d-flex align-items-center p-3 border rounded-3 bg-light">
|
||||
<i class="bi bi-file-earmark-pdf-fill text-danger fs-1 me-3"></i>
|
||||
<div>
|
||||
<p class="mb-0 fw-bold">{{ $buku->file_pdf }}</p>
|
||||
<small class="text-muted">Buku dapat diakses secara digital oleh siswa & guru.</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</x-app-layout>
|
||||
|
|
@ -9,17 +9,6 @@
|
|||
</div>
|
||||
|
||||
<form action="{{ route('admin.peminjaman.store') }}" method="POST" id="formPeminjaman" autocomplete="off"> @csrf
|
||||
@if ($errors->any())
|
||||
<div class="alert alert-danger alert-dismissible fade show" role="alert">
|
||||
<strong>Terjadi kesalahan:</strong>
|
||||
<ul class="mb-0">
|
||||
@foreach ($errors->all() as $error)
|
||||
<li>{{ $error }}</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
||||
</div>
|
||||
@endif
|
||||
<div class="row g-4">
|
||||
|
||||
<div class="col-lg-7">
|
||||
|
|
@ -28,25 +17,26 @@
|
|||
|
||||
<h5 class="fw-bold mb-3">Data Peminjaman</h5>
|
||||
<div class="mb-3">
|
||||
<label for="peminjam_id" class="form-label">Pilih Peminjam (Siswa/Guru)</label>
|
||||
<select class="form-control" id="peminjam_id" name="peminjam_id" placeholder="Cari nama atau email...">
|
||||
<label for="peminjam_id" class="form-label">Pilih Peminjam (Siswa/Guru) <span class="text-danger">*</span></label>
|
||||
<select class="form-control @error('peminjam_id') is-invalid @enderror" id="peminjam_id" name="peminjam_id" placeholder="Cari nama atau email...">
|
||||
<option value="">Pilih...</option>
|
||||
@foreach ($groupedUsers as $role => $users)
|
||||
<optgroup label="{{ ucfirst($role) }}">
|
||||
@foreach ($users as $user)
|
||||
|
||||
<option value="{{ $user['id'] }}"
|
||||
data-role="{{ strtolower($role) }}"
|
||||
data-identitas="{{ $user['nip'] ?? $user['nisn'] ?? '-' }}"
|
||||
data-kontak="{{ $user['no_hp'] ?? '-' }}"
|
||||
data-kontak="{{ $user['phone'] ?? '-' }}"
|
||||
data-info="{{ strtolower($role) == 'guru' ? ($user['jabatan'] ?? 'Guru') : ($user['kelas'] ?? 'Siswa') }}"
|
||||
{{ $user['disabled'] ? 'disabled' : '' }}>
|
||||
{{ $user['nama_lengkap'] }} {{ $user['status_text'] }}
|
||||
{{ $user['disabled'] ? 'disabled' : '' }}
|
||||
{{ old('peminjam_id') == $user['id'] ? 'selected' : '' }}>
|
||||
{{ $user['name'] }} {{ $user['status_text'] }}
|
||||
</option>
|
||||
@endforeach
|
||||
</optgroup>
|
||||
@endforeach
|
||||
</select>
|
||||
@error('peminjam_id') <div class="invalid-feedback">{{ $message }}</div> @enderror
|
||||
</div>
|
||||
|
||||
<div id="detailPeminjamCard" class="alert alert-secondary d-none mb-3">
|
||||
|
|
@ -61,12 +51,18 @@
|
|||
|
||||
<div class="row g-3">
|
||||
<div class="col-md-6">
|
||||
<label for="tanggal_pinjam" class="form-label">Tanggal Pinjam</label>
|
||||
<input type="text" class="form-control" id="tanggal_pinjam" name="tanggal_pinjam" placeholder="Pilih tanggal pinjam">
|
||||
<label for="tanggal_pinjam" class="form-label">Tanggal Pinjam <span class="text-danger">*</span></label>
|
||||
<input type="text" class="form-control @error('tanggal_pinjam') is-invalid @enderror"
|
||||
id="tanggal_pinjam" name="tanggal_pinjam" value="{{ old('tanggal_pinjam') }}"
|
||||
placeholder="Pilih tanggal pinjam" readonly>
|
||||
@error('tanggal_pinjam') <div class="invalid-feedback">{{ $message }}</div> @enderror
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label for="tanggal_kembali" class="form-label">Tenggat Kembali</label>
|
||||
<input type="text" class="form-control" id="tanggal_kembali" name="tanggal_kembali" placeholder="Pilih tenggat kembali">
|
||||
<label for="tanggal_kembali" class="form-label">Tenggat Kembali <span class="text-danger">*</span></label>
|
||||
<input type="text" class="form-control @error('tanggal_kembali') is-invalid @enderror"
|
||||
id="tanggal_kembali" name="tanggal_kembali" value="{{ old('tanggal_kembali') }}"
|
||||
placeholder="Pilih tenggat kembali" readonly>
|
||||
@error('tanggal_kembali') <div class="invalid-feedback">{{ $message }}</div> @enderror
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -77,6 +73,12 @@
|
|||
<span class="badge bg-primary-soft" id="counterBuku">0 Buku</span>
|
||||
</div>
|
||||
|
||||
@error('buku_ids')
|
||||
<div class="alert alert-danger py-2 small">
|
||||
<i class="bi bi-exclamation-triangle me-1"></i> {{ $message }}
|
||||
</div>
|
||||
@enderror
|
||||
|
||||
<div id="daftarBukuPinjam" class="mb-3">
|
||||
<div id="emptyStateBuku" class="text-center text-muted py-4">
|
||||
<i class="bi bi-collection fs-1" style="opacity: 0.25;"></i>
|
||||
|
|
@ -145,7 +147,6 @@
|
|||
create: false,
|
||||
sortField: { field: "text", direction: "asc" },
|
||||
onChange: function(value) {
|
||||
// Munculkan Kartu Detail Peminjam saat Dropdown Berubah
|
||||
const selectedOption = this.options[value];
|
||||
const detailCard = document.getElementById('detailPeminjamCard');
|
||||
|
||||
|
|
@ -162,14 +163,14 @@
|
|||
});
|
||||
|
||||
const tglPinjam = flatpickr("#tanggal_pinjam", {
|
||||
dateFormat: "Y-m-d", altInput: true, altFormat: "d/m/Y", defaultDate: "today", locale: "id",
|
||||
dateFormat: "Y-m-d", altInput: true, altFormat: "d/m/Y", defaultDate: "{{ old('tanggal_pinjam', date('Y-m-d')) }}", locale: "id",
|
||||
onChange: function(selectedDates, dateStr) {
|
||||
if (selectedDates.length > 0) tglKembali.set("minDate", new Date(selectedDates[0]).fp_incr(1));
|
||||
}
|
||||
});
|
||||
|
||||
const tglKembali = flatpickr("#tanggal_kembali", {
|
||||
dateFormat: "Y-m-d", altInput: true, altFormat: "d/m/Y", defaultDate: new Date().fp_incr(7), locale: "id", minDate: new Date().fp_incr(1)
|
||||
dateFormat: "Y-m-d", altInput: true, altFormat: "d/m/Y", defaultDate: "{{ old('tanggal_kembali', date('Y-m-d', strtotime('+7 days'))) }}", locale: "id", minDate: new Date().fp_incr(1)
|
||||
});
|
||||
|
||||
const MAX_BOOKS_SISWA = 2;
|
||||
|
|
@ -209,7 +210,6 @@ function renderSelectedBooks() {
|
|||
counterBukuEl.textContent = `${selectedBookIds.size} Buku`;
|
||||
}
|
||||
|
||||
// Global function untuk toggle selection
|
||||
window.toggleBookSelection = function(itemElement, id) {
|
||||
const selectEl = document.getElementById('peminjam_id');
|
||||
if(selectEl.value === "") {
|
||||
|
|
@ -250,7 +250,6 @@ function renderSelectedBooks() {
|
|||
renderSelectedBooks();
|
||||
}
|
||||
|
||||
// Pencarian
|
||||
document.getElementById('searchBuku').addEventListener('keyup', function() {
|
||||
const searchTerm = this.value.toLowerCase();
|
||||
document.querySelectorAll('.book-option').forEach(el => {
|
||||
|
|
@ -260,55 +259,26 @@ function renderSelectedBooks() {
|
|||
});
|
||||
});
|
||||
|
||||
document.getElementById('formPeminjaman').addEventListener('submit', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
const peminjam = document.getElementById('peminjam_id').value;
|
||||
const jumlahBuku = selectedBookIds.size;
|
||||
|
||||
// Validasi
|
||||
if (!peminjam) {
|
||||
Toast.fire({ icon: 'warning', title: 'Perhatian', text: 'Pilih peminjam terlebih dahulu!' });
|
||||
return;
|
||||
}
|
||||
if (jumlahBuku === 0) {
|
||||
Toast.fire({ icon: 'warning', title: 'Perhatian', text: 'Pilih minimal 1 buku!' });
|
||||
return;
|
||||
}
|
||||
|
||||
document.getElementById('formPeminjaman').addEventListener('submit', function() {
|
||||
modernSwal.fire({
|
||||
title: 'Proses Peminjaman?',
|
||||
text: `Anda akan memproses ${jumlahBuku} buku.`,
|
||||
icon: 'question',
|
||||
showCancelButton: true,
|
||||
confirmButtonText: 'Ya, Proses',
|
||||
cancelButtonText: 'Batal'
|
||||
}).then((result) => {
|
||||
if (result.isConfirmed) {
|
||||
// Loading
|
||||
modernSwal.fire({
|
||||
title: 'Memproses...',
|
||||
timer: 1000,
|
||||
didOpen: () => Swal.showLoading()
|
||||
}).then(() => {
|
||||
Toast.fire({
|
||||
icon: 'success',
|
||||
title: 'Berhasil',
|
||||
text: 'Transaksi peminjaman berhasil disimpan.'
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
window.location.href = "{{ route('admin.peminjaman.index') }}";
|
||||
}, 1500);
|
||||
});
|
||||
}
|
||||
title: 'Memproses...',
|
||||
allowOutsideClick: false,
|
||||
didOpen: () => Swal.showLoading()
|
||||
});
|
||||
});
|
||||
|
||||
document.getElementById('formPeminjaman').reset();
|
||||
tomSelect.clear();
|
||||
selectedBookIds.clear();
|
||||
renderSelectedBooks();
|
||||
// Restore old state if any
|
||||
@if(old('buku_ids'))
|
||||
@foreach(old('buku_ids') as $oldId)
|
||||
selectedBookIds.add("{{ $oldId }}");
|
||||
const oldItem = document.querySelector(`.book-option[data-book-id="{{ $oldId }}"] .book-item-list`);
|
||||
if(oldItem) {
|
||||
oldItem.querySelector('.book-checkbox').checked = true;
|
||||
oldItem.style.background = 'rgba(var(--bs-primary-rgb), 0.05)';
|
||||
}
|
||||
@endforeach
|
||||
renderSelectedBooks();
|
||||
@endif
|
||||
});
|
||||
</script>
|
||||
@endpush
|
||||
|
|
|
|||
|
|
@ -10,7 +10,9 @@
|
|||
<form action="{{ route('admin.peminjaman.export') }}" method="GET" class="d-flex m-0 p-0 bg-white border rounded p-1">
|
||||
<div class="input-group input-group-sm">
|
||||
<span class="input-group-text bg-transparent border-0 text-muted small">Filter Bulan:</span>
|
||||
<input type="date" name="bulan_laporan" class="form-control border-0" title="Pilih tanggal untuk filter bulan (Opsional)">
|
||||
<input type="date" name="bulan_laporan" class="form-control border-0 @error('bulan_laporan') is-invalid @enderror"
|
||||
title="Pilih tanggal untuk filter bulan"
|
||||
min="1900-01-01" max="{{ date('Y-m-d') }}">
|
||||
<button type="submit" class="btn btn-success text-nowrap">
|
||||
<i class="bi bi-file-earmark-excel-fill me-1"></i>Excel
|
||||
</button>
|
||||
|
|
@ -212,6 +214,7 @@
|
|||
<button type="button" class="btn btn-primary btn-konfirmasi-kembali"
|
||||
data-nama-peminjam="{{ $transaksi['peminjam'] }}"
|
||||
data-nomor-hp="{{ $transaksi['nomor_hp'] }}"
|
||||
data-email="{{ $transaksi['email'] }}"
|
||||
data-user-id="{{ $transaksi['user_id'] }}">
|
||||
Konfirmasi & Selesai
|
||||
</button>
|
||||
|
|
@ -346,12 +349,15 @@ function hitungTotalDenda(modal) {
|
|||
});
|
||||
|
||||
const userId = modalEl.find('.btn-konfirmasi-kembali').data('user-id');
|
||||
const userEmail = modalEl.find('.btn-konfirmasi-kembali').data('email');
|
||||
const totalDenda = dendaOverdueTotal + totalDendaRusak;
|
||||
|
||||
// Construct WA Message
|
||||
let waLink = null;
|
||||
if (isWaChecked && hp && hp !== '-') {
|
||||
let phone = hp;
|
||||
// Clean phone number from non-numeric characters
|
||||
phone = phone.replace(/[^0-9]/g, '');
|
||||
if (phone.startsWith('0')) phone = '62' + phone.substring(1);
|
||||
|
||||
let message = `*BUKTI PENGEMBALIAN BUKU*\n\n`;
|
||||
|
|
@ -371,33 +377,30 @@ function hitungTotalDenda(modal) {
|
|||
|
||||
// Loading Kirim Email flow
|
||||
if (isEmailChecked) {
|
||||
const dummyEmail = nama.replace(/\s+/g, '.').toLowerCase() + '@sekolah.sch.id';
|
||||
modernSwal.fire({
|
||||
title: 'Mengirim Email...',
|
||||
html: `Mengirim nota ke: <b>${dummyEmail}</b>`,
|
||||
timer: 2000,
|
||||
timerProgressBar: true,
|
||||
didOpen: () => Swal.showLoading()
|
||||
}).then(() => {
|
||||
finishTransaction(returnsData, userId, waLink);
|
||||
title: 'Memproses...',
|
||||
html: `Mengirim nota ke: <b>${userEmail}</b>`,
|
||||
didOpen: () => Swal.showLoading(),
|
||||
allowOutsideClick: false
|
||||
});
|
||||
} else {
|
||||
finishTransaction(returnsData, userId, waLink);
|
||||
}
|
||||
|
||||
finishTransaction(returnsData, userId, waLink, isEmailChecked);
|
||||
});
|
||||
} else {
|
||||
modalInstance.show();
|
||||
}
|
||||
});
|
||||
|
||||
function finishTransaction(returnsData, userId, waLink) {
|
||||
function finishTransaction(returnsData, userId, waLink, isEmailChecked) {
|
||||
$.ajax({
|
||||
url: "{{ route('admin.peminjaman.kembali') }}",
|
||||
method: 'POST',
|
||||
data: {
|
||||
_token: '{{ csrf_token() }}',
|
||||
user_id: userId,
|
||||
returns: returnsData
|
||||
returns: returnsData,
|
||||
send_email: isEmailChecked ? 1 : 0
|
||||
},
|
||||
success: function(response) {
|
||||
if (response.status === 'success') {
|
||||
|
|
@ -405,11 +408,19 @@ function finishTransaction(returnsData, userId, waLink) {
|
|||
window.open(waLink, '_blank');
|
||||
}
|
||||
|
||||
Toast.fire({
|
||||
icon: 'success',
|
||||
title: 'Berhasil',
|
||||
text: 'Buku berhasil dikembalikan.'
|
||||
});
|
||||
if (response.email_error) {
|
||||
Toast.fire({
|
||||
icon: 'warning',
|
||||
title: 'Berhasil (Email Gagal)',
|
||||
text: response.message
|
||||
});
|
||||
} else {
|
||||
Toast.fire({
|
||||
icon: 'success',
|
||||
title: 'Berhasil',
|
||||
text: 'Buku berhasil dikembalikan.'
|
||||
});
|
||||
}
|
||||
setTimeout(() => location.reload(), 1500);
|
||||
} else {
|
||||
modernSwal.fire('Gagal', response.message, 'error');
|
||||
|
|
|
|||
|
|
@ -7,6 +7,15 @@
|
|||
<h3 class="my-0 fw-bold">Formulir Tambah Pengguna</h3>
|
||||
</div>
|
||||
|
||||
@if($prefilledData)
|
||||
<div class="alert alert-success border-0 shadow-sm d-flex align-items-center mb-4" role="alert">
|
||||
<i class="bi bi-person-check-fill fs-4 me-3"></i>
|
||||
<div>
|
||||
Mendaftarkan user dari Data Induk: <strong>{{ $prefilledData->nama_pemilik }}</strong> ({{ $prefilledData->nomor_induk }})
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-10">
|
||||
<div class="card border-0 shadow-sm">
|
||||
|
|
@ -16,33 +25,49 @@
|
|||
|
||||
<div class="mb-3">
|
||||
<label for="role" class="form-label">Role <span class="text-danger">*</span></label>
|
||||
@if($prefilledData)
|
||||
<input type="hidden" name="role" value="{{ $prefilledData->role }}">
|
||||
<select class="form-select @error('role') is-invalid @enderror" id="role" disabled>
|
||||
<option value="siswa" {{ $prefilledData->role == 'siswa' ? 'selected' : '' }}>Siswa</option>
|
||||
<option value="guru" {{ $prefilledData->role == 'guru' ? 'selected' : '' }}>Guru</option>
|
||||
<option value="penjaga perpus" {{ $prefilledData->role == 'penjaga perpus' ? 'selected' : '' }}>Penjaga Perpustakaan</option>
|
||||
</select>
|
||||
@else
|
||||
<select class="form-select @error('role') is-invalid @enderror" id="role" name="role"
|
||||
required onchange="toggleFields()">
|
||||
onchange="toggleFields()">
|
||||
<option value="" selected disabled>Pilih role terlebih dahulu...</option>
|
||||
<option value="siswa" {{ old('role')=='siswa' ? 'selected' : '' }}>Siswa</option>
|
||||
<option value="guru" {{ old('role')=='guru' ? 'selected' : '' }}>Guru</option>
|
||||
<option value="penjaga perpus" {{ old('role')=='penjaga perpus' ? 'selected' : '' }}>
|
||||
Penjaga Perpustakaan</option>
|
||||
</select>
|
||||
@endif
|
||||
@error('role')
|
||||
<div class="invalid-feedback">{{ $message }}</div>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
{{-- Bagian Form Dinamis --}}
|
||||
<div id="dynamic-form" class="d-none">
|
||||
<div id="dynamic-form" class="{{ ($prefilledData || old('role')) ? '' : 'd-none' }}">
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="nama_lengkap" class="form-label">Nama Lengkap</label>
|
||||
<label for="nama_lengkap" class="form-label">Nama Lengkap <span class="text-danger">*</span></label>
|
||||
<input type="text" class="form-control @error('nama_lengkap') is-invalid @enderror"
|
||||
id="nama_lengkap" name="nama_lengkap" value="{{ old('nama_lengkap') }}" required>
|
||||
id="nama_lengkap" name="nama_lengkap" value="{{ old('nama_lengkap', $prefilledData->nama_pemilik ?? '') }}"
|
||||
{{ $prefilledData ? 'readonly' : '' }} maxlength="50" pattern="[a-zA-Z\s.,'\']+">
|
||||
@error('nama_lengkap')
|
||||
<div class="invalid-feedback">{{ $message }}</div>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="nomor_induk" class="form-label" id="label_nomor_induk">NISN / NIP</label>
|
||||
<input type="number" class="form-control @error('nomor_induk') is-invalid @enderror"
|
||||
id="nomor_induk" name="nomor_induk" value="{{ old('nomor_induk') }}"
|
||||
placeholder="Masukkan Nomor Induk">
|
||||
<label for="nomor_induk" class="form-label" id="label_nomor_induk">NISN / NIP <span class="text-danger">*</span></label>
|
||||
@if($prefilledData)
|
||||
<input type="hidden" name="nomor_induk" value="{{ $prefilledData->nomor_induk }}">
|
||||
@endif
|
||||
<input type="text" class="form-control @error('nomor_induk') is-invalid @enderror"
|
||||
id="nomor_induk" name="{{ $prefilledData ? '' : 'nomor_induk' }}" value="{{ old('nomor_induk', $prefilledData->nomor_induk ?? '') }}"
|
||||
placeholder="Masukkan Nomor Induk" {{ $prefilledData ? 'readonly' : '' }} maxlength="15" pattern="[0-9]*" oninput="this.value = this.value.replace(/[^0-9]/g, '')">
|
||||
@error('nomor_induk')
|
||||
<div class="invalid-feedback">{{ $message }}</div>
|
||||
@enderror
|
||||
|
|
@ -50,30 +75,40 @@
|
|||
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-3">
|
||||
<label for="email" class="form-label">Email</label>
|
||||
<label for="email" class="form-label">Email <span class="text-danger">*</span></label>
|
||||
<input type="email" class="form-control @error('email') is-invalid @enderror"
|
||||
id="email" name="email" value="{{ old('email') }}" required>
|
||||
id="email" name="email" value="{{ old('email') }}">
|
||||
@error('email')
|
||||
<div class="invalid-feedback">{{ $message }}</div>
|
||||
@enderror
|
||||
</div>
|
||||
<div class="col-md-6 mb-3">
|
||||
<label for="no_hp" class="form-label">No. Handphone</label>
|
||||
<input type="text" class="form-control" id="no_hp" name="phone"
|
||||
value="{{ old('phone') }}" placeholder="Contoh: 08123456789">
|
||||
<label for="no_hp" class="form-label">Nomor Telepon</label>
|
||||
<input type="text" class="form-control @error('phone') is-invalid @enderror" id="no_hp" name="phone"
|
||||
value="{{ old('phone') }}" placeholder="Contoh: 08123456789"
|
||||
maxlength="16" pattern="\+?[0-9]+" oninput="this.value = this.value.replace(/[^0-9+]/g, '')">
|
||||
@error('phone')
|
||||
<div class="invalid-feedback">{{ $message }}</div>
|
||||
@enderror
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row" id="field_siswa_only" style="display: none;">
|
||||
<div class="row" id="field_siswa_only" style="{{ ($prefilledData && $prefilledData->role == 'siswa') || old('role') == 'siswa' ? 'display: flex;' : 'display: none;' }}">
|
||||
<div class="col-md-6 mb-3">
|
||||
<label for="kelas" class="form-label">Kelas</label>
|
||||
<input type="text" class="form-control" id="kelas" name="kelas"
|
||||
value="{{ old('kelas') }}" placeholder="Contoh: XII RPL 1">
|
||||
<input type="text" class="form-control @error('kelas') is-invalid @enderror" id="kelas" name="kelas"
|
||||
value="{{ old('kelas') }}" placeholder="Contoh: XII RPL 1" maxlength="50">
|
||||
@error('kelas')
|
||||
<div class="invalid-feedback">{{ $message }}</div>
|
||||
@enderror
|
||||
</div>
|
||||
<div class="col-md-6 mb-3">
|
||||
<label for="golongan" class="form-label">Golongan</label>
|
||||
<input type="text" class="form-control" id="golongan" name="golongan"
|
||||
value="{{ old('golongan') }}" placeholder="Contoh: A/B">
|
||||
<input type="text" class="form-control @error('golongan') is-invalid @enderror" id="golongan" name="golongan"
|
||||
value="{{ old('golongan') }}" placeholder="Contoh: A/B" maxlength="50">
|
||||
@error('golongan')
|
||||
<div class="invalid-feedback">{{ $message }}</div>
|
||||
@enderror
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -81,7 +116,7 @@
|
|||
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-3">
|
||||
<label for="password" class="form-label">Password</label>
|
||||
<label for="password" class="form-label">Password <span class="text-danger">*</span></label>
|
||||
<div class="input-group has-validation">
|
||||
<input type="password"
|
||||
class="form-control @error('password') is-invalid @enderror" id="password"
|
||||
|
|
@ -97,7 +132,7 @@ class="form-control @error('password') is-invalid @enderror" id="password"
|
|||
</div>
|
||||
|
||||
<div class="col-md-6 mb-3">
|
||||
<label for="password_confirmation" class="form-label">Konfirmasi Password</label>
|
||||
<label for="password_confirmation" class="form-label">Konfirmasi Password <span class="text-danger">*</span></label>
|
||||
<div class="input-group">
|
||||
<input type="password" class="form-control" id="password_confirmation"
|
||||
name="password_confirmation">
|
||||
|
|
@ -122,7 +157,10 @@ class="form-control @error('password') is-invalid @enderror" id="password"
|
|||
@push('scripts')
|
||||
<script>
|
||||
function toggleFields() {
|
||||
const role = document.getElementById('role').value;
|
||||
const roleSelect = document.getElementById('role');
|
||||
if (!roleSelect) return;
|
||||
|
||||
const role = roleSelect.value;
|
||||
const dynamicForm = document.getElementById('dynamic-form');
|
||||
const labelInduk = document.getElementById('label_nomor_induk');
|
||||
const inputInduk = document.getElementById('nomor_induk');
|
||||
|
|
@ -163,21 +201,7 @@ function toggleFields() {
|
|||
});
|
||||
});
|
||||
|
||||
document.getElementById('formPengguna').addEventListener('submit', function() {
|
||||
modernSwal.fire({
|
||||
title: 'Menyimpan Data...',
|
||||
allowOutsideClick: false,
|
||||
didOpen: () => Swal.showLoading()
|
||||
});
|
||||
});
|
||||
|
||||
<?php if(session('success')): ?>
|
||||
Toast.fire({
|
||||
icon: 'success',
|
||||
title: 'Berhasil',
|
||||
text: '<?php echo session("success"); ?>'
|
||||
});
|
||||
<?php endif; ?>
|
||||
// Form submission listener removed to allow server-side validation
|
||||
</script>
|
||||
@endpush
|
||||
</x-app-layout>
|
||||
|
|
@ -17,7 +17,7 @@
|
|||
|
||||
<div class="mb-3">
|
||||
<label for="role" class="form-label">Role <span class="text-danger">*</span></label>
|
||||
<select class="form-select @error('role') is-invalid @enderror" id="role" name="role" required onchange="toggleFields()">
|
||||
<select class="form-select @error('role') is-invalid @enderror" id="role" name="role" onchange="toggleFields()">
|
||||
<option value="siswa" {{ old('role', $pengguna->role) == 'siswa' ? 'selected' : '' }}>Siswa</option>
|
||||
<option value="guru" {{ old('role', $pengguna->role) == 'guru' ? 'selected' : '' }}>Guru</option>
|
||||
<option value="penjaga perpus" {{ old('role', $pengguna->role) == 'penjaga perpus' ? 'selected' : '' }}>Penjaga Perpustakaan</option>
|
||||
|
|
@ -27,40 +27,47 @@
|
|||
|
||||
<div id="dynamic-form">
|
||||
<div class="mb-3">
|
||||
<label for="nama_lengkap" class="form-label">Nama Lengkap</label>
|
||||
<label for="nama_lengkap" class="form-label">Nama Lengkap <span class="text-danger">*</span></label>
|
||||
<input type="text" class="form-control @error('nama_lengkap') is-invalid @enderror"
|
||||
id="nama_lengkap" name="nama_lengkap" value="{{ old('nama_lengkap', $pengguna->name) }}" required>
|
||||
id="nama_lengkap" name="nama_lengkap" value="{{ old('nama_lengkap', $pengguna->name) }}"
|
||||
maxlength="50" pattern="[a-zA-Z\s.,'\']+">
|
||||
@error('nama_lengkap') <div class="invalid-feedback">{{ $message }}</div> @enderror
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
@php $oldNomorInduk = old('nomor_induk', $pengguna->nomor_induk); @endphp
|
||||
<label for="nomor_induk" class="form-label" id="label_nomor_induk">NISN / NIP</label>
|
||||
<input type="number" class="form-control @error('nomor_induk') is-invalid @enderror"
|
||||
id="nomor_induk" name="nomor_induk" value="{{ $oldNomorInduk }}" placeholder="Masukkan Nomor Induk">
|
||||
@error('nomor_induk') <div class="invalid-feedback fw-bold">{{ $message }}</div> @enderror
|
||||
<label for="nomor_induk" class="form-label" id="label_nomor_induk">NISN / NIP <span class="text-danger">*</span></label>
|
||||
<input type="text" class="form-control @error('nomor_induk') is-invalid @enderror"
|
||||
id="nomor_induk" name="nomor_induk" value="{{ old('nomor_induk', $pengguna->nomor_induk) }}"
|
||||
placeholder="Masukkan Nomor Induk" maxlength="15" pattern="[0-9]*" oninput="this.value = this.value.replace(/[^0-9]/g, '')">
|
||||
@error('nomor_induk') <div class="invalid-feedback">{{ $message }}</div> @enderror
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-3">
|
||||
<label for="email" class="form-label">Email</label>
|
||||
<label for="email" class="form-label">Email <span class="text-danger">*</span></label>
|
||||
<input type="email" class="form-control @error('email') is-invalid @enderror"
|
||||
id="email" name="email" value="{{ old('email', $pengguna->email) }}" required>
|
||||
id="email" name="email" value="{{ old('email', $pengguna->email) }}">
|
||||
@error('email') <div class="invalid-feedback">{{ $message }}</div> @enderror
|
||||
</div>
|
||||
<div class="col-md-6 mb-3">
|
||||
<label for="no_hp" class="form-label">No. Handphone</label>
|
||||
<input type="text" class="form-control" id="phone" name="phone" value="{{ old('phone', $pengguna->phone) }}">
|
||||
<input type="text" class="form-control @error('phone') is-invalid @enderror" id="phone" name="phone"
|
||||
value="{{ old('phone', $pengguna->phone) }}"
|
||||
maxlength="16" pattern="\+?[0-9]+" oninput="this.value = this.value.replace(/[^0-9+]/g, '')">
|
||||
@error('phone') <div class="invalid-feedback">{{ $message }}</div> @enderror
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row" id="field_siswa_only" style="display: none;">
|
||||
<div class="col-md-6 mb-3">
|
||||
<label for="kelas" class="form-label">Kelas</label>
|
||||
<input type="text" class="form-control" id="kelas" name="kelas" value="{{ old('kelas', $pengguna->kelas) }}">
|
||||
<input type="text" class="form-control @error('kelas') is-invalid @enderror" id="kelas" name="kelas" value="{{ old('kelas', $pengguna->kelas) }}" maxlength="50">
|
||||
@error('kelas') <div class="invalid-feedback">{{ $message }}</div> @enderror
|
||||
</div>
|
||||
<div class="col-md-6 mb-3">
|
||||
<label for="golongan" class="form-label">Golongan</label>
|
||||
<input type="text" class="form-control" id="golongan" name="golongan" value="{{ old('golongan', $pengguna->golongan) }}">
|
||||
<input type="text" class="form-control @error('golongan') is-invalid @enderror" id="golongan" name="golongan" value="{{ old('golongan', $pengguna->golongan) }}" maxlength="50">
|
||||
@error('golongan') <div class="invalid-feedback">{{ $message }}</div> @enderror
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -69,12 +76,22 @@
|
|||
<div class="row">
|
||||
<div class="col-md-6 mb-3">
|
||||
<label for="password" class="form-label">Password Baru <span class="small text-muted">(opsional)</span></label>
|
||||
<input type="password" class="form-control @error('password') is-invalid @enderror" id="password" name="password" placeholder="Kosongkan jika tidak diubah">
|
||||
@error('password') <div class="invalid-feedback">{{ $message }}</div> @enderror
|
||||
<div class="input-group">
|
||||
<input type="password" class="form-control @error('password') is-invalid @enderror" id="password" name="password" placeholder="Kosongkan jika tidak diubah" minlength="8">
|
||||
<button class="btn btn-outline-secondary toggle-password" type="button" data-target="password">
|
||||
<i class="bi bi-eye" id="icon-password"></i>
|
||||
</button>
|
||||
@error('password') <div class="invalid-feedback">{{ $message }}</div> @enderror
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 mb-3">
|
||||
<label for="password_confirmation" class="form-label">Konfirmasi Password Baru</label>
|
||||
<input type="password" class="form-control" id="password_confirmation" name="password_confirmation" placeholder="Ulangi password baru">
|
||||
<div class="input-group">
|
||||
<input type="password" class="form-control" id="password_confirmation" name="password_confirmation" placeholder="Ulangi password baru" minlength="8">
|
||||
<button class="btn btn-outline-secondary toggle-password" type="button" data-target="password_confirmation">
|
||||
<i class="bi bi-eye" id="icon-password_confirmation"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -112,21 +129,23 @@ function toggleFields() {
|
|||
}
|
||||
document.addEventListener('DOMContentLoaded', toggleFields);
|
||||
|
||||
document.getElementById('formEditPengguna').addEventListener('submit', function() {
|
||||
modernSwal.fire({
|
||||
title: 'Menyimpan Perubahan...',
|
||||
allowOutsideClick: false,
|
||||
didOpen: () => Swal.showLoading()
|
||||
document.querySelectorAll('.toggle-password').forEach(function(button) {
|
||||
button.addEventListener('click', function() {
|
||||
const targetId = this.getAttribute('data-target');
|
||||
const input = document.getElementById(targetId);
|
||||
const icon = document.getElementById('icon-' + targetId);
|
||||
|
||||
if (input.type === 'password') {
|
||||
input.type = 'text';
|
||||
icon.classList.replace('bi-eye', 'bi-eye-slash');
|
||||
} else {
|
||||
input.type = 'password';
|
||||
icon.classList.replace('bi-eye-slash', 'bi-eye');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
<?php if(session('success')): ?>
|
||||
Toast.fire({
|
||||
icon: 'success',
|
||||
title: 'Berhasil',
|
||||
text: '<?php echo session("success"); ?>'
|
||||
});
|
||||
<?php endif; ?>
|
||||
// Form submission listener removed to allow server-side validation
|
||||
</script>
|
||||
@endpush
|
||||
</x-app-layout>
|
||||
|
|
@ -6,9 +6,100 @@
|
|||
<h1 class="h3 text-gray-800">{{ $pageTitle ?? 'Manajemen Pengguna' }}</h1>
|
||||
</div>
|
||||
|
||||
{{-- BAGIAN DATA INDUK (WHITELIST) --}}
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<div>
|
||||
<h4 class="fw-bold text-success mb-1"><i class="bi bi-database-lock me-2"></i>1. Data Induk (Whitelist)
|
||||
</h4>
|
||||
<p class="text-muted mb-0">Daftar NIP/NISN/NIK yang <b>diizinkan</b> untuk mendaftar.</p>
|
||||
</div>
|
||||
<button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#modalMasterInduk">
|
||||
<i class="bi bi-plus-lg me-1"></i> Tambah Data Induk
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="card shadow mb-4 border-left-success">
|
||||
<div class="card-body">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover table-bordered align-middle mb-0">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th>No</th>
|
||||
<th>NIP / NISN</th>
|
||||
<th>Nama Pemilik</th>
|
||||
<th class="text-center">Role</th>
|
||||
<th class="text-center">Status Akun</th>
|
||||
<th class="text-center">Aksi</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@forelse($whitelists as $index => $item)
|
||||
<tr>
|
||||
<td>{{ $whitelists->firstItem() + $index }}</td>
|
||||
<td class="fw-bold font-monospace">{{ $item->nomor_induk }}</td>
|
||||
<td>{{ $item->nama_pemilik }}</td>
|
||||
<td class="text-center">
|
||||
@if($item->role == 'guru')
|
||||
<span class="badge bg-info text-dark">Guru</span>
|
||||
@elseif($item->role == 'siswa')
|
||||
<span class="badge bg-primary">Siswa</span>
|
||||
@else
|
||||
<span class="badge bg-secondary">Petugas</span>
|
||||
@endif
|
||||
</td>
|
||||
<td class="text-center">
|
||||
@php
|
||||
$isRegistered = \App\Models\User::where('nomor_induk', $item->nomor_induk)->exists();
|
||||
@endphp
|
||||
@if ($isRegistered)
|
||||
<span class="badge bg-success text-white"><i
|
||||
class="bi bi-check-circle-fill me-1"></i>Terdaftar</span>
|
||||
@else
|
||||
<span class="badge bg-warning text-dark"><i
|
||||
class="bi bi-hourglass-split me-1"></i>Belum Daftar</span>
|
||||
@endif
|
||||
</td>
|
||||
<td class="text-center">
|
||||
<div class="d-flex justify-content-center gap-1">
|
||||
@if(!$isRegistered)
|
||||
<a href="{{ route('admin.pengguna.create', ['nomor_induk' => $item->nomor_induk]) }}"
|
||||
class="btn btn-sm btn-primary" title="Daftarkan Akun">
|
||||
<i class="bi bi-person-plus-fill me-1"></i>Daftarkan
|
||||
</a>
|
||||
@endif
|
||||
<form action="{{ route('admin.master-induk.destroy', $item->id) }}" method="POST"
|
||||
class="form-delete-whitelist" data-induk="{{ $item->nomor_induk }}">
|
||||
@csrf
|
||||
@method('DELETE')
|
||||
<button type="button" class="btn btn-sm btn-outline-danger btn-hapus-whitelist" title="Hapus Data Induk">
|
||||
<i class="bi bi-trash"></i>
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr>
|
||||
<td colspan="6" class="text-center py-4 text-muted">Belum ada data whitelist. Silakan
|
||||
tambah data.</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="mt-3">
|
||||
{{ $whitelists->links() }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="my-5 border-4">
|
||||
|
||||
<h4 class="fw-bold text-primary mb-3"><i class="bi bi-people-fill me-2"></i>2. Daftar Pengguna Aktif</h4>
|
||||
|
||||
<div class="card shadow mb-5">
|
||||
<div class="card-header py-3 d-flex justify-content-between align-items-center flex-wrap gap-2">
|
||||
<h6 class="m-0 font-weight-bold text-primary">Daftar Pengguna Aktif</h6>
|
||||
<h6 class="m-0 font-weight-bold text-primary">List Pengguna Terdaftar</h6>
|
||||
|
||||
<div class="d-flex align-items-center">
|
||||
<form action="{{ route('admin.pengguna.index') }}" method="GET" class="me-2 mb-0">
|
||||
|
|
@ -22,7 +113,7 @@
|
|||
</form>
|
||||
|
||||
<a href="{{ route('admin.pengguna.create') }}" class="btn btn-sm btn-primary">
|
||||
<i class="bi bi-plus-circle-fill me-1"></i>Tambah Pengguna
|
||||
<i class="bi bi-plus-circle-fill me-1"></i>Tambah Manual
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -48,7 +139,7 @@
|
|||
<td>
|
||||
<div>{{ $user->email }}</div>
|
||||
<div class="small text-muted"><i class="bi bi-telephone me-1"></i>{{ $user->phone ??
|
||||
'-' }}</div>
|
||||
'-' }}</div>
|
||||
</td>
|
||||
<td>
|
||||
@if($user->role == 'guru')
|
||||
|
|
@ -106,90 +197,10 @@ class="form-delete-user" data-nama="{{ $user->name }}">
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="my-5 border-4">
|
||||
|
||||
{{-- BAGIAN DATA INDUK (WHITELIST) --}}
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<div>
|
||||
<h4 class="fw-bold text-success mb-1"><i class="bi bi-database-lock me-2"></i>Data Induk (Whitelist)
|
||||
</h4>
|
||||
<p class="text-muted mb-0">Daftar NIP/NISN/NIK yang <b>diizinkan</b> untuk mendaftar.</p>
|
||||
</div>
|
||||
<button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#modalMasterInduk">
|
||||
<i class="bi bi-plus-lg me-1"></i> Tambah Data Induk
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="card shadow mb-4 border-left-success">
|
||||
<div class="card-body">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover table-bordered align-middle mb-0">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th>No</th>
|
||||
<th>NIP / NISN</th>
|
||||
<th>Nama Pemilik</th>
|
||||
<th class="text-center">Role</th>
|
||||
<th class="text-center">Status Akun</th>
|
||||
<th class="text-center">Aksi</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@forelse($whitelists as $index => $item)
|
||||
<tr>
|
||||
<td>{{ $index + 1 }}</td>
|
||||
<td class="fw-bold font-monospace">{{ $item->nomor_induk }}</td>
|
||||
<td>{{ $item->nama_pemilik }}</td>
|
||||
<td class="text-center">
|
||||
@if($item->role == 'guru')
|
||||
<span class="badge bg-info text-dark">Guru</span>
|
||||
@elseif($item->role == 'siswa')
|
||||
<span class="badge bg-primary">Siswa</span>
|
||||
@else
|
||||
<span class="badge bg-secondary">Petugas</span>
|
||||
@endif
|
||||
</td>
|
||||
<td class="text-center">
|
||||
@php
|
||||
$isRegistered = \App\Models\User::where('nomor_induk', $item->nomor_induk)->exists();
|
||||
@endphp
|
||||
@if ($isRegistered)
|
||||
<span class="badge bg-success text-white"><i
|
||||
class="bi bi-check-circle-fill me-1"></i>Terdaftar</span>
|
||||
@else
|
||||
<span class="badge bg-warning text-dark"><i
|
||||
class="bi bi-hourglass-split me-1"></i>Belum Daftar</span>
|
||||
@endif
|
||||
</td>
|
||||
<td class="text-center">
|
||||
<div class="d-flex justify-content-center">
|
||||
<form action="{{ route('admin.master-induk.destroy', $item->id) }}" method="POST"
|
||||
class="form-delete-whitelist" data-induk="{{ $item->nomor_induk }}">
|
||||
@csrf
|
||||
@method('DELETE')
|
||||
<button type="button" class="btn btn-sm btn-outline-danger btn-hapus-whitelist" title="Hapus Data Induk">
|
||||
<i class="bi bi-trash"></i>
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr>
|
||||
<td colspan="6" class="text-center py-4 text-muted">Belum ada data whitelist. Silakan
|
||||
tambah data.</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- MODAL TAMBAH DATA INDUK --}}
|
||||
<div class="modal fade" id="modalMasterInduk" tabindex="-1">
|
||||
<div class="modal fade {{ $errors->hasAny(['role', 'nomor_induk', 'nama_pemilik']) ? 'show' : '' }}" id="modalMasterInduk" tabindex="-1" style="{{ $errors->hasAny(['role', 'nomor_induk', 'nama_pemilik']) ? 'display: block;' : '' }}">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
|
|
@ -205,22 +216,26 @@ class="form-delete-whitelist" data-induk="{{ $item->nomor_induk }}">
|
|||
Masukkan data siswa/guru yang valid agar mereka bisa mendaftar.
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Role</label>
|
||||
<select name="role" class="form-select" required>
|
||||
<option value="siswa">Siswa</option>
|
||||
<option value="guru">Guru</option>
|
||||
<option value="penjaga perpus">Petugas Perpustakaan</option>
|
||||
<label class="form-label">Role <span class="text-danger">*</span></label>
|
||||
<select name="role" class="form-select @error('role') is-invalid @enderror">
|
||||
<option value="" disabled selected>Pilih Role...</option>
|
||||
<option value="siswa" {{ old('role') == 'siswa' ? 'selected' : '' }}>Siswa</option>
|
||||
<option value="guru" {{ old('role') == 'guru' ? 'selected' : '' }}>Guru</option>
|
||||
</select>
|
||||
@error('role') <div class="invalid-feedback">{{ $message }}</div> @enderror
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">NIP / NISN / NIK</label>
|
||||
<input type="number" name="nomor_induk" class="form-control"
|
||||
placeholder="Contoh: 1234567890" required>
|
||||
<label class="form-label">NIP / NISN / NIK <span class="text-danger">*</span></label>
|
||||
<input type="text" name="nomor_induk" class="form-control @error('nomor_induk') is-invalid @enderror"
|
||||
placeholder="Contoh: 1234567890" maxlength="15" pattern="[0-9]*" oninput="this.value = this.value.replace(/[^0-9]/g, '')"
|
||||
value="{{ old('nomor_induk') }}">
|
||||
@error('nomor_induk') <div class="invalid-feedback">{{ $message }}</div> @enderror
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Nama Pemilik</label>
|
||||
<input type="text" name="nama_pemilik" class="form-control" placeholder="Nama Siswa/Guru..."
|
||||
required>
|
||||
<label class="form-label">Nama Pemilik <span class="text-danger">*</span></label>
|
||||
<input type="text" name="nama_pemilik" class="form-control @error('nama_pemilik') is-invalid @enderror"
|
||||
placeholder="Nama Siswa/Guru..." value="{{ old('nama_pemilik') }}" maxlength="50" pattern="[a-zA-Z\s.,'\']+">
|
||||
@error('nama_pemilik') <div class="invalid-feedback">{{ $message }}</div> @enderror
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
|
|
@ -231,6 +246,9 @@ class="form-delete-whitelist" data-induk="{{ $item->nomor_induk }}">
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@if($errors->hasAny(['role', 'nomor_induk', 'nama_pemilik']))
|
||||
<div class="modal-backdrop fade show"></div>
|
||||
@endif
|
||||
|
||||
@push('scripts')
|
||||
<script>
|
||||
|
|
@ -285,13 +303,7 @@ class="form-delete-whitelist" data-induk="{{ $item->nomor_induk }}">
|
|||
});
|
||||
});
|
||||
|
||||
$('#formWhitelist').on('submit', function() {
|
||||
modernSwal.fire({
|
||||
title: 'Menyimpan...',
|
||||
allowOutsideClick: false,
|
||||
didOpen: () => Swal.showLoading()
|
||||
});
|
||||
});
|
||||
// Form submission listener removed to allow server-side validation
|
||||
});
|
||||
|
||||
@if(session('success'))
|
||||
|
|
|
|||
|
|
@ -15,27 +15,36 @@
|
|||
<form action="{{ route('admin.pengumuman.store') }}" method="POST">
|
||||
@csrf
|
||||
<div class="mb-3">
|
||||
<label for="title" class="form-label">Judul Pengumuman</label>
|
||||
<input type="text" name="title" class="form-control" id="title"
|
||||
placeholder="Masukkan judul pengumuman" required>
|
||||
<label for="title" class="form-label">Judul Pengumuman <span class="text-danger">*</span></label>
|
||||
<input type="text" name="title" class="form-control @error('title') is-invalid @enderror" id="title"
|
||||
placeholder="Masukkan judul pengumuman" value="{{ old('title') }}" maxlength="50">
|
||||
@error('title')
|
||||
<div class="invalid-feedback">{{ $message }}</div>
|
||||
@enderror
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="type" class="form-label">Tipe Pengumuman</label>
|
||||
<select name="type" class="form-select" id="type" required>
|
||||
<option value="">Pilih tipe...</option>
|
||||
<option value="info">Info</option>
|
||||
<option value="success">Success</option>
|
||||
<option value="warning">Warning</option>
|
||||
<option value="danger">Danger</option>
|
||||
<label for="type" class="form-label">Tipe Pengumuman <span class="text-danger">*</span></label>
|
||||
<select name="type" class="form-select @error('type') is-invalid @enderror" id="type">
|
||||
<option value="" disabled selected>Pilih tipe...</option>
|
||||
<option value="info" {{ old('type') == 'info' ? 'selected' : '' }}>Info</option>
|
||||
<option value="success" {{ old('type') == 'success' ? 'selected' : '' }}>Success</option>
|
||||
<option value="warning" {{ old('type') == 'warning' ? 'selected' : '' }}>Warning</option>
|
||||
<option value="danger" {{ old('type') == 'danger' ? 'selected' : '' }}>Danger</option>
|
||||
</select>
|
||||
@error('type')
|
||||
<div class="invalid-feedback">{{ $message }}</div>
|
||||
@enderror
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="content" class="form-label">Isi Pengumuman</label>
|
||||
<textarea name="content" class="form-control" id="content" rows="5" placeholder="Tulis isi pengumuman di sini..." required></textarea>
|
||||
<label for="content" class="form-label">Isi Pengumuman <span class="text-danger">*</span></label>
|
||||
<textarea name="content" class="form-control @error('content') is-invalid @enderror" id="content" rows="5" placeholder="Tulis isi pengumuman di sini..." maxlength="255">{{ old('content') }}</textarea>
|
||||
@error('content')
|
||||
<div class="invalid-feedback">{{ $message }}</div>
|
||||
@enderror
|
||||
</div>
|
||||
<hr>
|
||||
<div class="d-flex justify-content-end">
|
||||
<button type="submit" class="btn btn-primary">Simpan Pengumuman</button>
|
||||
<button type="submit" class="btn btn-primary px-4 fw-bold">Simpan Pengumuman</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
|
@ -46,24 +55,8 @@
|
|||
|
||||
@push('scripts')
|
||||
<script>
|
||||
document.getElementById('formTambahPengumuman').addEventListener('submit', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
modernSwal.fire({
|
||||
title: 'Menyimpan...',
|
||||
timer: 800,
|
||||
didOpen: () => Swal.showLoading()
|
||||
}).then(() => {
|
||||
Toast.fire({
|
||||
icon: 'success',
|
||||
title: 'Berhasil',
|
||||
text: 'Pengumuman baru berhasil ditambahkan.'
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
window.location.href = "{{ route('admin.pengumuman.index') }}";
|
||||
}, 1500);
|
||||
});
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// Script removed to allow standard server-side validation
|
||||
});
|
||||
</script>
|
||||
@endpush
|
||||
|
|
|
|||
|
|
@ -16,31 +16,36 @@
|
|||
@csrf
|
||||
@method('PUT')
|
||||
<div class="mb-3">
|
||||
<label for="title" class="form-label">Judul Pengumuman</label>
|
||||
<input type="text" name="title" class="form-control" id="title"
|
||||
value="{{ old('title', $pengumuman->title) }}" required>
|
||||
<label for="title" class="form-label">Judul Pengumuman <span class="text-danger">*</span></label>
|
||||
<input type="text" name="title" class="form-control @error('title') is-invalid @enderror" id="title"
|
||||
value="{{ old('title', $pengumuman->title) }}" maxlength="50">
|
||||
@error('title')
|
||||
<div class="invalid-feedback">{{ $message }}</div>
|
||||
@enderror
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="type" class="form-label">Tipe Pengumuman</label>
|
||||
<select name="type" class="form-select" id="type" required>
|
||||
<option value="">Pilih tipe...</option>
|
||||
<option value="info" @if (old('type', $pengumuman->type) == 'info') selected @endif>Info
|
||||
</option>
|
||||
<option value="success" @if (old('type', $pengumuman->type) == 'success') selected @endif>Success
|
||||
</option>
|
||||
<option value="warning" @if (old('type', $pengumuman->type) == 'warning') selected @endif>Warning
|
||||
</option>
|
||||
<option value="danger" @if (old('type', $pengumuman->type) == 'danger') selected @endif>Danger
|
||||
</option>
|
||||
<label for="type" class="form-label">Tipe Pengumuman <span class="text-danger">*</span></label>
|
||||
<select name="type" class="form-select @error('type') is-invalid @enderror" id="type">
|
||||
<option value="" disabled>Pilih tipe...</option>
|
||||
<option value="info" @if (old('type', $pengumuman->type) == 'info') selected @endif>Info</option>
|
||||
<option value="success" @if (old('type', $pengumuman->type) == 'success') selected @endif>Success</option>
|
||||
<option value="warning" @if (old('type', $pengumuman->type) == 'warning') selected @endif>Warning</option>
|
||||
<option value="danger" @if (old('type', $pengumuman->type) == 'danger') selected @endif>Danger</option>
|
||||
</select>
|
||||
@error('type')
|
||||
<div class="invalid-feedback">{{ $message }}</div>
|
||||
@enderror
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="content" class="form-label">Isi Pengumuman</label>
|
||||
<textarea name="content" class="form-control" id="content" rows="5" required>{{ old('content', $pengumuman->content) }}</textarea>
|
||||
<label for="content" class="form-label">Isi Pengumuman <span class="text-danger">*</span></label>
|
||||
<textarea name="content" class="form-control @error('content') is-invalid @enderror" id="content" rows="5" maxlength="255">{{ old('content', $pengumuman->content) }}</textarea>
|
||||
@error('content')
|
||||
<div class="invalid-feedback">{{ $message }}</div>
|
||||
@enderror
|
||||
</div>
|
||||
<hr>
|
||||
<div class="d-flex justify-content-end">
|
||||
<button type="submit" class="btn btn-primary">Simpan Perubahan</button>
|
||||
<button type="submit" class="btn btn-primary px-4 fw-bold">Simpan Perubahan</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
|
@ -51,25 +56,8 @@
|
|||
|
||||
@push('scripts')
|
||||
<script>
|
||||
document.getElementById('formEditPengumuman').addEventListener('submit', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
modernSwal.fire({
|
||||
title: 'Menyimpan Perubahan...',
|
||||
timer: 800,
|
||||
didOpen: () => Swal.showLoading()
|
||||
}).then(() => {
|
||||
Toast.fire({
|
||||
icon: 'success',
|
||||
title: 'Berhasil',
|
||||
text: 'Pengumuman berhasil diperbarui.'
|
||||
});
|
||||
|
||||
// Redirect setelah 1.5 detik
|
||||
setTimeout(() => {
|
||||
window.location.href = "{{ route('admin.pengumuman.index') }}";
|
||||
}, 1500);
|
||||
});
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// Script removed to allow standard server-side validation
|
||||
});
|
||||
</script>
|
||||
@endpush
|
||||
|
|
|
|||
|
|
@ -14,18 +14,41 @@
|
|||
<div class="card-body">
|
||||
<form action="{{ route('admin.rekomendasi.store') }}" method="POST">
|
||||
@csrf
|
||||
<div class="mb-3"><label class="form-label">Judul</label><input type="text"
|
||||
name="judul" class="form-control" required></div>
|
||||
<div class="mb-3"><label class="form-label">Kategori</label><input type="text"
|
||||
name="kategori" class="form-control" placeholder="Contoh: Teknologi, Sains, Biologi" required></div>
|
||||
<div class="mb-3"><label class="form-label">Link YouTube</label><input type="url"
|
||||
name="youtube_link" class="form-control" placeholder="https://www.youtube.com/watch?v=xxxxxx" required></div>
|
||||
<div class="mb-3"><label class="form-label">Deskripsi</label>
|
||||
<textarea name="deskripsi" id="editor" required></textarea>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Judul <span class="text-danger">*</span></label>
|
||||
<input type="text" name="judul" class="form-control @error('judul') is-invalid @enderror"
|
||||
value="{{ old('judul') }}" maxlength="50">
|
||||
@error('judul')
|
||||
<div class="invalid-feedback">{{ $message }}</div>
|
||||
@enderror
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Kategori <span class="text-danger">*</span></label>
|
||||
<input type="text" name="kategori" class="form-control @error('kategori') is-invalid @enderror"
|
||||
value="{{ old('kategori') }}" placeholder="Contoh: Teknologi, Sains, Biologi" maxlength="50">
|
||||
@error('kategori')
|
||||
<div class="invalid-feedback">{{ $message }}</div>
|
||||
@enderror
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Link YouTube <span class="text-danger">*</span></label>
|
||||
<input type="url" name="youtube_link" class="form-control @error('youtube_link') is-invalid @enderror"
|
||||
value="{{ old('youtube_link') }}" placeholder="https://www.youtube.com/watch?v=xxxxxx">
|
||||
@error('youtube_link')
|
||||
<div class="invalid-feedback">{{ $message }}</div>
|
||||
@enderror
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Deskripsi <span class="text-danger">*</span></label>
|
||||
<textarea name="deskripsi" id="editor" class="@error('deskripsi') is-invalid @enderror" maxlength="255">{{ old('deskripsi') }}</textarea>
|
||||
@error('deskripsi')
|
||||
<div class="text-danger small mt-1">{{ $message }}</div>
|
||||
@enderror
|
||||
</div>
|
||||
<hr>
|
||||
<div class="d-flex justify-content-end"><button type="submit"
|
||||
class="btn btn-primary">Simpan</button></div>
|
||||
<div class="d-flex justify-content-end">
|
||||
<button type="submit" class="btn btn-primary px-4 fw-bold">Simpan</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -15,18 +15,41 @@
|
|||
<form action="{{ route('admin.rekomendasi.update', $rekomendasi->id) }}" method="POST">
|
||||
@csrf
|
||||
@method('PUT')
|
||||
<div class="mb-3"><label class="form-label">Judul</label><input type="text"
|
||||
name="judul" class="form-control" value="{{ old('judul', $rekomendasi->judul) }}" required></div>
|
||||
<div class="mb-3"><label class="form-label">Kategori</label><input type="text"
|
||||
name="kategori" class="form-control" value="{{ old('kategori', $rekomendasi->kategori) }}" required></div>
|
||||
<div class="mb-3"><label class="form-label">Link YouTube</label><input type="url"
|
||||
name="youtube_link" class="form-control" value="{{ old('youtube_link', $rekomendasi->youtube_link) }}" required></div>
|
||||
<div class="mb-3"><label class="form-label">Deskripsi</label>
|
||||
<textarea name="deskripsi" id="editor" required>{{ old('deskripsi', $rekomendasi->deskripsi) }}</textarea>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Judul <span class="text-danger">*</span></label>
|
||||
<input type="text" name="judul" class="form-control @error('judul') is-invalid @enderror"
|
||||
value="{{ old('judul', $rekomendasi->judul) }}" maxlength="50">
|
||||
@error('judul')
|
||||
<div class="invalid-feedback">{{ $message }}</div>
|
||||
@enderror
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Kategori <span class="text-danger">*</span></label>
|
||||
<input type="text" name="kategori" class="form-control @error('kategori') is-invalid @enderror"
|
||||
value="{{ old('kategori', $rekomendasi->kategori) }}" maxlength="50">
|
||||
@error('kategori')
|
||||
<div class="invalid-feedback">{{ $message }}</div>
|
||||
@enderror
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Link YouTube <span class="text-danger">*</span></label>
|
||||
<input type="url" name="youtube_link" class="form-control @error('youtube_link') is-invalid @enderror"
|
||||
value="{{ old('youtube_link', $rekomendasi->youtube_link) }}">
|
||||
@error('youtube_link')
|
||||
<div class="invalid-feedback">{{ $message }}</div>
|
||||
@enderror
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Deskripsi <span class="text-danger">*</span></label>
|
||||
<textarea name="deskripsi" id="editor" class="@error('deskripsi') is-invalid @enderror" maxlength="255">{{ old('deskripsi', $rekomendasi->deskripsi) }}</textarea>
|
||||
@error('deskripsi')
|
||||
<div class="text-danger small mt-1">{{ $message }}</div>
|
||||
@enderror
|
||||
</div>
|
||||
<hr>
|
||||
<div class="d-flex justify-content-end"><button type="submit"
|
||||
class="btn btn-primary">Simpan Perubahan</button></div>
|
||||
<div class="d-flex justify-content-end">
|
||||
<button type="submit" class="btn btn-primary px-4 fw-bold">Simpan Perubahan</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@
|
|||
<i class="bi bi-eye-slash-fill"></i>
|
||||
</button>
|
||||
</div>
|
||||
<x-input-error :messages="$errors->get('password')" class="mt-2" />
|
||||
</div>
|
||||
|
||||
<div class="d-grid mt-4">
|
||||
|
|
|
|||
|
|
@ -4,14 +4,13 @@
|
|||
</div>
|
||||
|
||||
<!-- Session Status -->
|
||||
<x-auth-session-status class="mb-4" :status="session('status')" />
|
||||
|
||||
<form method="POST" action="{{ route('password.email') }}">
|
||||
<x-auth-session-status class="mb-4" :status="session('status')" />
|
||||
<form method="POST" action="{{ route('password.email') }}">
|
||||
@csrf
|
||||
|
||||
<!-- Email Address -->
|
||||
<div>
|
||||
<x-input-label for="email" :value="__('Email')" />
|
||||
<x-input-label for="email" :value="__('Email')" />
|
||||
<x-text-input id="email" class="block mt-1 w-full" type="email" name="email" :value="old('email')" required autofocus />
|
||||
<x-input-error :messages="$errors->get('email')" class="mt-2" />
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
</div>
|
||||
<h3 class="fw-bold text-dark">Login {{ Str::title($role) }}</h3>
|
||||
<p class="text-muted small">
|
||||
@if ($role == 'siswa')
|
||||
@if ($role == 'siswa')
|
||||
Silakan masukkan <b>NISN</b> dan kata sandi.
|
||||
@else
|
||||
Silakan masukkan <b>NIP/NIK</b> dan kata sandi.
|
||||
|
|
@ -28,28 +28,28 @@
|
|||
|
||||
{{-- INPUT USERNAME (NISN/NIP) --}}
|
||||
<div class="mb-3">
|
||||
<label for="{{ $role == 'siswa' ? 'nisn' : 'nip' }}" class="form-label fw-semibold small">
|
||||
{{ $role == 'siswa' ? 'Nomor Induk Siswa Nasional (NISN)' : 'NIP/NIK' }}
|
||||
<label for="{{ $role == 'siswa' ? 'nisn' : 'nip' }}" class="form-label fw-semibold small">
|
||||
{{ $role == 'siswa' ? 'Nomor Induk Siswa Nasional (NISN)' : 'NIP/NIK' }}
|
||||
</label>
|
||||
<div class="input-group">
|
||||
@if ($role == 'siswa')
|
||||
<input id="nisn" class="form-control" type="text" name="nisn" placeholder="Masukan NISN" required autofocus />
|
||||
<input id="nisn" class="form-control" type="text" name="nisn" placeholder="Masukan NISN" required autofocus />
|
||||
@else
|
||||
<input id="nip" class="form-control" type="text" name="nip" placeholder="Masukan NIP/NIK" required autofocus />
|
||||
<input id="nip" class="form-control" type="text" name="nip" placeholder="Masukan NIP/NIK" required autofocus />
|
||||
@endif
|
||||
</div>
|
||||
{{-- Error Message --}}
|
||||
@if ($role == 'siswa')
|
||||
<x-input-error :messages="$errors->get('nisn')" class="mt-1 small text-danger" />
|
||||
<x-input-error :messages="$errors->get('nisn')" class="mt-1 small text-danger" />
|
||||
@else
|
||||
<x-input-error :messages="$errors->get('nip')" class="mt-1 small text-danger" />
|
||||
<x-input-error :messages="$errors->get('nip')" class="mt-1 small text-danger" />
|
||||
@endif
|
||||
</div>
|
||||
|
||||
{{-- INPUT PASSWORD --}}
|
||||
<div class="mb-4">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<label for="password" class="form-label fw-semibold small">Kata Sandi</label>
|
||||
<label for="password" class="form-label fw-semibold small">Kata Sandi</label>
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<input id="password" class="form-control border-end-0" type="password"
|
||||
|
|
@ -71,26 +71,26 @@
|
|||
{{-- BANTUAN / LUPA PASSWORD --}}
|
||||
<div class="bg-light p-3 rounded-3 text-center border border-dashed">
|
||||
<p class="text-muted small mb-1">Mengalami kendala login?</p>
|
||||
<a href="https://wa.me/62895618643811?text=Halo%20Admin,%20saya%20lupa%20kata%20sandi%20akun%20saya."
|
||||
<a href="https://wa.me/62895618643811?text=Halo%20Admin,%20saya%20lupa%20kata%20sandi%20akun%20saya."
|
||||
target="_blank" class="text-decoration-none fw-bold text-success d-inline-flex align-items-center">
|
||||
<i class="bi bi-whatsapp me-1"></i> Hubungi Petugas via WA
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<p class="mt-4 text-center text-muted small">
|
||||
<p class="mt-4 text-center text-muted small">
|
||||
Kembali ke <a href="/" class="fw-semibold text-decoration-none">halaman utama</a>.
|
||||
</p>
|
||||
</form>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const togglePassword = document.querySelector('#togglePassword');
|
||||
const togglePassword = document.querySelector('#togglePassword');
|
||||
const password = document.querySelector('#password');
|
||||
const icon = togglePassword.querySelector('i');
|
||||
|
||||
if (togglePassword && password) {
|
||||
togglePassword.addEventListener('click', function(e) {
|
||||
const type = password.getAttribute('type') === 'password' ? 'text' : 'password';
|
||||
const type = password.getAttribute('type') === 'password' ? 'text' : 'password';
|
||||
password.setAttribute('type', type);
|
||||
|
||||
if (type === 'password') {
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
{{ __('Thanks for signing up! Before getting started, could you verify your email address by clicking on the link we just emailed to you? If you didn\'t receive the email, we will gladly send you another.') }}
|
||||
</div>
|
||||
|
||||
@if (session('status') == 'verification-link-sent')
|
||||
@if (session('status') == 'verification-link-sent')
|
||||
<div class="mb-4 font-medium text-sm text-green-600">
|
||||
{{ __('A new verification link has been sent to the email address you provided during registration.') }}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,65 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Bukti Pengembalian Buku</title>
|
||||
<style>
|
||||
body { font-family: sans-serif; line-height: 1.6; color: #333; }
|
||||
.container { width: 100%; max-width: 600px; margin: 0 auto; padding: 20px; border: 1px solid #eee; border-radius: 8px; }
|
||||
.header { text-align: center; margin-bottom: 20px; border-bottom: 2px solid #0d6efd; padding-bottom: 10px; }
|
||||
.header h2 { color: #0d6efd; margin: 0; }
|
||||
.content { margin-bottom: 20px; }
|
||||
.item-list { width: 100%; border-collapse: collapse; margin-bottom: 20px; }
|
||||
.item-list th, .item-list td { padding: 10px; border: 1px solid #ddd; text-align: left; }
|
||||
.item-list th { background-color: #f8f9fa; }
|
||||
.footer { text-align: center; font-size: 0.8em; color: #777; margin-top: 30px; border-top: 1px solid #eee; padding-top: 10px; }
|
||||
.denda { font-weight: bold; color: #dc3545; }
|
||||
.success { color: #198754; font-weight: bold; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="header">
|
||||
<h2>BUKTI PENGEMBALIAN BUKU</h2>
|
||||
<p>{{ config('app.name') }}</p>
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
<p>Halo, <strong>{{ $user->nama_lengkap }}</strong>,</p>
|
||||
<p>Terima kasih telah mengembalikan buku tepat waktu atau telah menyelesaikan denda yang berlaku. Berikut adalah rincian buku yang Anda kembalikan:</p>
|
||||
|
||||
<table class="item-list">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Judul Buku</th>
|
||||
<th>Kondisi</th>
|
||||
<th>Denda</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach($returns as $item)
|
||||
<tr>
|
||||
<td>{{ $item['judul'] }}</td>
|
||||
<td>{{ ucfirst($item['condition']) }}</td>
|
||||
<td>Rp {{ number_format($item['fine_damage'] + $item['fine_overdue'], 0, ',', '.') }}</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<th colspan="2">TOTAL DENDA</th>
|
||||
<th class="denda">Rp {{ number_format($totalDenda, 0, ',', '.') }}</th>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
|
||||
<p>Status: <span class="success">Berhasil Dikembalikan</span></p>
|
||||
<p>Pesan ini dikirim secara otomatis sebagai bukti sah bahwa Anda telah mengembalikan buku di atas ke perpustakaan.</p>
|
||||
</div>
|
||||
|
||||
<div class="footer">
|
||||
<p>© {{ date('Y') }} {{ config('app.name') }}. Semua Hak Dilindungi.</p>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -76,7 +76,7 @@ class="badge fw-normal {{ $buku['status'] == 'Tersedia' ? 'bg-success-subtle tex
|
|||
$isOnlineAccess =
|
||||
$buku['tipe_akses'] === 'online' ||
|
||||
(is_array($buku['tipe_akses']) && in_array('online', $buku['tipe_akses']));
|
||||
$stokHabis = $buku['status'] == 'Dipinjam';
|
||||
$stokHabis = $buku['stok'] <= 0;
|
||||
@endphp
|
||||
|
||||
{{-- TOMBOL PINJAM (OFFLINE) --}}
|
||||
|
|
|
|||
|
|
@ -37,13 +37,13 @@
|
|||
@if(in_array($user->role, ['guru', 'penjaga perpus']))
|
||||
<div class="col-md-6">
|
||||
<label class="form-label text-muted small text-uppercase fw-bold">NIP / NIK</label>
|
||||
<p class="fw-semibold text-dark">{{ $user->nip ?? '-' }}</p>
|
||||
<p class="fw-semibold text-dark">{{ $user->nomor_induk ?? '-' }}</p>
|
||||
</div>
|
||||
{{-- KONDISI UNTUK SISWA --}}
|
||||
@else
|
||||
<div class="col-md-6">
|
||||
<label class="form-label text-muted small text-uppercase fw-bold">NISN</label>
|
||||
<p class="fw-semibold text-dark">{{ $user->nisn ?? '-' }}</p>
|
||||
<p class="fw-semibold text-dark">{{ $user->nomor_induk ?? '-' }}</p>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
|
|
@ -58,8 +58,8 @@
|
|||
<label class="form-label text-muted small text-uppercase fw-bold">Nomor Handphone
|
||||
(WA)</label>
|
||||
<p class="fw-semibold text-dark">
|
||||
@if($user->no_hp)
|
||||
<span>{{ $user->no_hp
|
||||
@if($user->phone)
|
||||
<span>{{ $user->phone
|
||||
}}</span>
|
||||
@else
|
||||
<span class="text-muted fst-italic">- Belum diatur -</span>
|
||||
|
|
@ -125,13 +125,13 @@ class="rounded me-3 form-book-cover">
|
|||
<label for="tanggalPinjam" class="form-label text-muted">Tanggal Pinjam</label>
|
||||
<input type="text" id="tanggalPinjam" name="tanggal_pinjam" class="form-control"
|
||||
placeholder="Pilih tanggal pinjam"
|
||||
value="{{ \Carbon\Carbon::now()->format('d F Y') }}">
|
||||
value="{{ \Carbon\Carbon::now()->format('d F Y') }}" readonly>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label for="tanggalKembali" class="form-label text-muted">Tanggal Kembali</label>
|
||||
<input type="text" id="tanggalKembali" name="tanggal_kembali"
|
||||
placeholder="Pilih tanggal kembali" class="form-control"
|
||||
value="{{ \Carbon\Carbon::now()->addDays(7)->format('d F Y') }}">
|
||||
value="{{ \Carbon\Carbon::now()->addDays(2)->format('d F Y') }}" readonly>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -295,7 +295,7 @@ class="bi bi-check-circle me-1"></i>Tersedia</span>
|
|||
<div>
|
||||
<h6 class="fw-bold text-dark mb-1">PENTING: Aturan Peminjaman!</h6>
|
||||
<p class="mb-0 text-muted small">
|
||||
Sesuai peraturan perpustakaan, durasi peminjaman buku maksimal adalah
|
||||
durasi peminjaman buku maksimal adalah
|
||||
<strong class="text-dark bg-warning-subtle px-2 py-1 rounded">2 HARI</strong>.
|
||||
<br>
|
||||
Mohon kembalikan tepat waktu untuk menghindari denda (Rp 1.000/hari).
|
||||
|
|
@ -320,37 +320,25 @@ class="bi bi-check-circle me-1"></i>Tersedia</span>
|
|||
<script>
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
|
||||
// (Default Hari Ini + 2 Hari)
|
||||
const fpKembali = flatpickr("#tanggalKembali", {
|
||||
dateFormat: "d F Y",
|
||||
dateFormat: "Y-m-d",
|
||||
altInput: true,
|
||||
altFormat: "d F Y",
|
||||
defaultDate: new Date().fp_incr(2),
|
||||
locale: "id",
|
||||
minDate: new Date().fp_incr(1),
|
||||
maxDate: new Date().fp_incr(2)
|
||||
clickOpens: false,
|
||||
allowInput: false
|
||||
});
|
||||
|
||||
// Inisialisasi Tanggal Pinjam
|
||||
flatpickr("#tanggalPinjam", {
|
||||
dateFormat: "d F Y",
|
||||
dateFormat: "Y-m-d",
|
||||
altInput: true,
|
||||
altFormat: "d F Y",
|
||||
defaultDate: "today",
|
||||
locale: "id",
|
||||
minDate: "today",
|
||||
|
||||
onChange: function(selectedDates, dateStr) {
|
||||
if (selectedDates.length > 0) {
|
||||
const tglMulai = selectedDates[0];
|
||||
const maxDateBaru = new Date(tglMulai).fp_incr(2);
|
||||
const minDateBaru = new Date(tglMulai).fp_incr(1);
|
||||
|
||||
fpKembali.set("minDate", minDateBaru);
|
||||
fpKembali.set("maxDate", maxDateBaru);
|
||||
fpKembali.setDate(maxDateBaru);
|
||||
}
|
||||
}
|
||||
clickOpens: false,
|
||||
allowInput: false
|
||||
});
|
||||
|
||||
// Inisialisasi data dari data-attributes
|
||||
|
|
|
|||
|
|
@ -119,7 +119,7 @@ class="btn btn-outline-primary rounded-pill w-100 w-sm-auto">
|
|||
</div>
|
||||
<div class="col-sm-6 col-md-4">
|
||||
<small class="text-muted d-block mb-1">Nomor HP</small>
|
||||
<p class="fw-semibold mb-0">{{ $user->no_hp ?? '-' }}</p>
|
||||
<p class="fw-semibold mb-0">{{ $user->phone ?? '-' }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -202,54 +202,59 @@ class="btn btn-outline-primary rounded-pill w-100 w-sm-auto">
|
|||
|
||||
<h5 class="fw-bold mb-3">Informasi Personal</h5>
|
||||
<div class="row g-3">
|
||||
<div class="col-sm-6 col-md-3">
|
||||
<div class="col-sm-6 col-md-6">
|
||||
<small class="text-muted d-block mb-1">NISN</small>
|
||||
<p class="fw-semibold mb-0">{{ $user->nomor_induk ?? '-' }}</p>
|
||||
</div>
|
||||
<div class="col-sm-6 col-md-3">
|
||||
<div class="col-sm-6 col-md-6">
|
||||
<small class="text-muted d-block mb-1">Email</small>
|
||||
<p class="fw-semibold mb-0 text-break">{{ $user->email }}</p>
|
||||
</div>
|
||||
<div class="col-sm-6 col-md-3">
|
||||
<div class="col-sm-6 col-md-6">
|
||||
<small class="text-muted d-block mb-1">Nomor HP</small>
|
||||
<p class="fw-semibold mb-0">{{ $user->no_hp ?? '-' }}</p>
|
||||
<p class="fw-semibold mb-0">{{ $user->phone ?? '-' }}</p>
|
||||
</div>
|
||||
<div class="col-sm-6 col-md-3">
|
||||
<div class="col-sm-6 col-md-6">
|
||||
<small class="text-muted d-block mb-1">Kelas</small>
|
||||
<p class="fw-semibold mb-0">{{ $user->kelas ?? '-' }}</p>
|
||||
</div>
|
||||
<div class="col-sm-6 col-md-6">
|
||||
<small class="text-muted d-block mb-1">Golongan</small>
|
||||
<p class="fw-semibold mb-0">{{ $user->golongan ?? '-' }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3 mb-md-4 flex-grow-1">
|
||||
<h5 class="fw-bold mb-3">Statistik Saya</h5>
|
||||
<div class="row g-3">
|
||||
@foreach ($statistik as $stat)
|
||||
<div class="col-sm-6 col-lg-3">
|
||||
<div class="card border-0 h-100">
|
||||
<div class="card-body p-3 p-md-4 text-center">
|
||||
<div class="icon-circle bg-{{ $stat['color'] }}-light mx-auto mb-3"
|
||||
style="width: 60px; height: 60px; display: flex; align-items: center; justify-content: center; border-radius: 15px;">
|
||||
<i class="bi {{ $stat['icon'] }} fs-2 text-{{ $stat['color'] }}"></i>
|
||||
</div>
|
||||
<h3 class="fw-bold mb-2">{{ $stat['value'] }}</h3>
|
||||
<p class="text-muted mb-0 small">{{ $stat['label'] }}</p>
|
||||
<div class="mb-3 mb-md-4 flex-grow-1">
|
||||
<h5 class="fw-bold mb-3">Statistik Saya</h5>
|
||||
<div class="row g-3">
|
||||
@foreach ($statistik as $stat)
|
||||
<div class="col-sm-6 col-lg-3">
|
||||
<div class="card border-0 h-100">
|
||||
<div class="card-body p-3 p-md-4 text-center">
|
||||
<div class="icon-circle bg-{{ $stat['color'] }}-light mx-auto mb-3"
|
||||
style="width: 60px; height: 60px; display: flex; align-items: center; justify-content: center; border-radius: 15px;">
|
||||
<i class="bi {{ $stat['icon'] }} fs-2 text-{{ $stat['color'] }}"></i>
|
||||
</div>
|
||||
<h3 class="fw-bold mb-2">{{ $stat['value'] }}</h3>
|
||||
<p class="text-muted mb-0 small">{{ $stat['label'] }}</p>
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-4">
|
||||
@include('profile.partials.personal-activities', [
|
||||
'bukuOffline' => $bukuOffline,
|
||||
'bukuOnline' => $bukuOnline,
|
||||
])
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="col-lg-4">
|
||||
@include('profile.partials.personal-activities', [
|
||||
'bukuOffline' => $bukuOffline,
|
||||
'bukuOnline' => $bukuOnline,
|
||||
])
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
</div>
|
||||
</x-app-layout>
|
||||
|
|
@ -7,7 +7,11 @@
|
|||
<li class="list-group-item px-0">
|
||||
<h6 class="fw-semibold mb-1">{{ $buku['judul'] }}</h6>
|
||||
<small class="text-muted">{{ $buku['penulis'] }}</small>
|
||||
<span class="badge bg-danger-subtle text-danger-emphasis float-end">Sisa {{ $buku['sisa_hari'] }} hari</span>
|
||||
@if($buku['sisa_hari'] < 0)
|
||||
<span class="badge bg-danger-subtle text-danger-emphasis float-end">Terlambat {{ abs($buku['sisa_hari']) }} hari</span>
|
||||
@else
|
||||
<span class="badge bg-success-subtle text-success-emphasis float-end">Sisa {{ $buku['sisa_hari'] }} hari</span>
|
||||
@endif
|
||||
</li>
|
||||
@empty
|
||||
<li class="list-group-item px-0 text-center text-muted small py-3">Tidak ada buku yang sedang dipinjam.</li>
|
||||
|
|
|
|||
|
|
@ -21,7 +21,8 @@
|
|||
<div class="mb-3">
|
||||
<label for="name" class="form-label">{{ __('Nama Lengkap') }}</label>
|
||||
<input id="name" name="name" type="text" class="form-control @error('name') is-invalid @enderror"
|
||||
value="{{ old('name', $user->name) }}" required autofocus autocomplete="name">
|
||||
value="{{ old('name', $user->name) }}" required autofocus autocomplete="name"
|
||||
minlength="3" maxlength="50">
|
||||
@error('name')
|
||||
<div class="invalid-feedback">{{ $message }}</div>
|
||||
@enderror
|
||||
|
|
@ -58,7 +59,8 @@ class="form-control @error('email') is-invalid @enderror" value="{{ old('email',
|
|||
<div class="mb-3">
|
||||
<label for="phone" class="form-label">{{ __('Nomor Telepon (WA)') }}</label>
|
||||
<input id="phone" name="phone" type="text" class="form-control @error('phone') is-invalid @enderror"
|
||||
value="{{ old('phone', $user->phone) }}" required>
|
||||
value="{{ old('phone', $user->phone) }}"
|
||||
maxlength="16" pattern="\+?[0-9]+" oninput="this.value = this.value.replace(/[^0-9+]/g, '')">
|
||||
@error('phone')
|
||||
<div class="invalid-feedback">{{ $message }}</div>
|
||||
@enderror
|
||||
|
|
@ -87,8 +89,8 @@ class="form-control @error('email') is-invalid @enderror" value="{{ old('email',
|
|||
@else
|
||||
{{-- Untuk Guru atau Penjaga Perpus --}}
|
||||
<div class="mb-3">
|
||||
<label for="nuptk" class="form-label">NIP / NUPTK</label>
|
||||
<input id="nuptk" type="text" class="form-control" value="{{ $user->nuptk }}" readonly disabled>
|
||||
<label for="nomor_induk" class="form-label">NIP / NUPTK</label>
|
||||
<input id="nomor_induk" type="text" class="form-control" value="{{ $user->nomor_induk }}" readonly disabled>
|
||||
<small class="text-muted">ID Kepegawaian tidak dapat diubah.</small>
|
||||
</div>
|
||||
@endif
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@
|
|||
</thead>
|
||||
<tbody>
|
||||
@php $counter = 1; @endphp
|
||||
@forelse ($riwayatOnline as $transaksi)
|
||||
@foreach ($riwayatOnline as $transaksi)
|
||||
@foreach ($transaksi['books'] as $buku)
|
||||
<tr>
|
||||
<td>{{ $counter++ }}</td>
|
||||
|
|
@ -38,11 +38,7 @@
|
|||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
@empty
|
||||
<tr>
|
||||
<td colspan="6" class="text-center">Tidak ada riwayat baca buku online.</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
|
@ -82,6 +78,9 @@ class="bi bi-book-half me-2 text-primary"></i>Detail Riwayat</h5>
|
|||
searching: false,
|
||||
pageLength: 10,
|
||||
order: [[0, 'asc']],
|
||||
language: {
|
||||
emptyTable: "Tidak ada riwayat baca buku online."
|
||||
},
|
||||
columnDefs: [
|
||||
{ responsivePriority: 1, targets: 2 },
|
||||
{ responsivePriority: 2, targets: 5 }
|
||||
|
|
|
|||
|
|
@ -86,8 +86,10 @@
|
|||
|
||||
Route::get('/buku', [AdminBookController::class, 'index'])->name('buku.index');
|
||||
Route::get('/buku/tambah', [AdminBookController::class, 'create'])->name('buku.create');
|
||||
Route::get('/buku/{id}', [AdminBookController::class, 'show'])->name('buku.show');
|
||||
Route::post('/buku', [AdminBookController::class, 'store'])->name('buku.store');
|
||||
Route::get('/buku/{id}/edit', [AdminBookController::class, 'edit'])->name('buku.edit');
|
||||
Route::put('/buku/{id}', [AdminBookController::class, 'update'])->name('buku.update');
|
||||
Route::post('/buku/arsip', [AdminBookController::class, 'arsip'])->name('buku.arsip');
|
||||
Route::post('/buku/pulihkan', [AdminBookController::class, 'pulihkan'])->name('buku.pulihkan');
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue