fix: bug that happen on the list

This commit is contained in:
cukiprit 2026-02-10 12:52:54 +07:00
parent 22d0c2f645
commit 1a356692dd
31 changed files with 726 additions and 150 deletions

View File

@ -31,6 +31,7 @@ public function index(Request $request)
'tanggal_pinjam' => $firstLoan->borrowed_at,
'tenggat_kembali' => $firstLoan->due_at,
'status' => $firstLoan->status,
'role' => $user->role,
'books' => $userLoans->map(fn($l) => [
'id' => $l->book->id,
'judul' => $l->book->judul,
@ -148,26 +149,36 @@ public function dendaIndex()
$tenggat = Carbon::parse($firstLoan->due_at);
$hariTelat = $now->greaterThan($tenggat) ? (int) $tenggat->diffInDays($now) : 0;
$totalDenda = $hariTelat * 1000;
$isGuru = $user->role === 'guru';
$totalDenda = $isGuru ? 0 : ($hariTelat * 1000);
// Link WA
$hp = $user->phone ?? '';
$waLink = '#';
if ($hp) {
if (substr($hp, 0, 1) == '0') $hp = '62' . substr($hp, 1);
$pesan = $hariTelat > 0
? "Halo {$user->nama_lengkap}, anda terlambat pengembalian buku. Total Denda: Rp " . number_format($totalDenda, 0, ',', '.')
: "Halo {$user->nama_lengkap}, akun anda sedang dinonaktifkan sementara. Mohon hubungi petugas.";
if ($isGuru && $hariTelat > 0) {
$pesan = "Halo Bapak/Ibu {$user->nama_lengkap}, anda terlambat pengembalian buku selama {$hariTelat} hari. Mohon segera dikembalikan ke perpustakaan. Terima kasih.";
} else {
$pesan = $hariTelat > 0
? "Halo {$user->nama_lengkap}, anda terlambat pengembalian buku. Total Denda: Rp " . number_format($totalDenda, 0, ',', '.')
: "Halo {$user->nama_lengkap}, akun anda sedang dinonaktifkan sementara. Mohon hubungi petugas.";
}
$waLink = "https://wa.me/{$hp}?text=" . urlencode($pesan);
}
return [
'id' => $firstLoan->id,
'user_id' => $userId,
'peminjam' => $user->nama_lengkap,
'nomor_hp' => $user->phone ?? '-',
'kelas' => $user->kelas ?? 'Guru',
'hari_terlambat' => $hariTelat,
'total_denda' => $totalDenda,
'is_guru' => $isGuru,
'wa_link' => $waLink,
'is_banned' => $user->is_banned,
'tenggat_kembali' => $firstLoan->due_at,
@ -189,7 +200,61 @@ public function dendaIndex()
public function berikanSanksi(Request $request)
{
// Actually implement banning logic here if needed
return response()->json(['status' => 'success']);
$validated = $request->validate([
'user_id' => 'required|exists:users,id',
'action' => 'required|in:ban,unban',
]);
$user = User::findOrFail($validated['user_id']);
$user->is_banned = ($validated['action'] === 'ban');
$user->save();
return response()->json([
'status' => 'success',
'message' => $user->is_banned ? "Akun {$user->nama_lengkap} berhasil dibekukan." : "Akun {$user->nama_lengkap} telah diaktifkan kembali."
]);
}
public function kembalikan(Request $request)
{
$validated = $request->validate([
'user_id' => 'required|exists:users,id',
'returns' => 'required|array',
'returns.*.book_id' => 'required|exists:books,id',
'returns.*.condition' => 'required|string',
'returns.*.fine_damage' => 'required|integer',
'returns.*.fine_overdue' => 'required|integer',
'returns.*.notes' => 'nullable|string',
]);
\DB::beginTransaction();
try {
foreach ($validated['returns'] as $item) {
$loan = Loan::where('user_id', $validated['user_id'])
->where('book_id', $item['book_id'])
->whereIn('status', ['Dipinjam', 'Terlambat'])
->first();
if ($loan) {
$loan->update([
'status' => 'Dikembalikan',
'returned_at' => now(),
'condition' => $item['condition'],
'fine_damage' => $item['fine_damage'],
'fine_overdue' => $item['fine_overdue'],
'return_notes' => $item['notes'],
]);
// Update book status
$loan->book->update(['status' => 'Tersedia']);
}
}
\DB::commit();
return response()->json(['status' => 'success', 'message' => 'Buku berhasil dikembalikan.']);
} catch (\Exception $e) {
\DB::rollBack();
return response()->json(['status' => 'error', 'message' => $e->getMessage()], 500);
}
}
}

View File

@ -21,10 +21,13 @@ public function index(Request $request)
$semuaBuku = $query->latest()->get();
// Memisahkan buku menjadi dua koleksi: online dan offline
[$bukuOnline, $bukuOffline] = $semuaBuku->partition(function ($buku) {
$tipe = $buku->tipe_akses;
return in_array('online', $tipe ?? []);
// Memisahkan buku menjadi dua koleksi: online dan offline secara independen
$bukuOnline = $semuaBuku->filter(function ($buku) {
return in_array('online', $buku->tipe_akses ?? []);
});
$bukuOffline = $semuaBuku->filter(function ($buku) {
return in_array('offline', $buku->tipe_akses ?? []);
});
return view('admin.buku.index', [

View File

@ -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' => 'Denda Menunggu', 'value' => Loan::where('status', 'Terlambat')->count(), 'icon' => 'bi-cash-coin', 'color' => 'danger'],
['label' => 'Total Denda', 'value' => 'Rp ' . number_format(Loan::where('status', 'Terlambat')->sum('fine_overdue'), 0, ',', '.'), 'icon' => 'bi-cash-coin', 'color' => 'danger'],
];
// Monthly stats (last 7 months)

View File

@ -9,6 +9,7 @@
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
use Illuminate\View\View;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
@ -134,15 +135,15 @@ public function streamPdf(int $id): BinaryFileResponse|Response
abort(403, 'Akses Ditolak.');
}
$book = Book::findOrFail($id);
$filePath = 'books/' . ($book->file_pdf ?? 'sample.pdf');
$absolutePath = storage_path('app/' . $filePath);
// For demo purposes, if file doesn't exist, we might want to use a placeholder or handle it gracefully
if (!file_exists($absolutePath)) {
// Create a dummy file for testing if it doesn't exist? (Optional, maybe just abort)
$fileName = $book->file_pdf ?? 'sample.pdf';
$filePath = 'books/' . $fileName;
if (!Storage::disk('local')->exists($filePath)) {
abort(404, 'File PDF tidak ditemukan di server.');
}
$absolutePath = Storage::disk('local')->path($filePath);
return response()->file($absolutePath);
}
}

View File

@ -57,10 +57,28 @@ public function index()
['type' => 'info', 'icon' => 'bi-bell-fill', 'title' => 'Selamat Datang', 'content' => 'Selamat datang di perpustakaan digital SMKN 1.', 'badge' => 'Baru']
]);
$progressMembaca = ['selesai' => 70, 'sisa' => 30]; // Still dummy as we don't track pages yet
// Dynamic reading progress based on returned vs total books
$totalUserLoans = Loan::where('user_id', $user->id)->count();
$returnedLoans = Loan::where('user_id', $user->id)->where('status', 'Dikembalikan')->count();
$progressSelesai = $totalUserLoans > 0 ? round(($returnedLoans / $totalUserLoans) * 100) : 0;
$progressMembaca = ['selesai' => $progressSelesai, 'sisa' => 100 - $progressSelesai];
// Dynamic monthly stats for the last 7 months
$labels = [];
$data = [];
for ($i = 6; $i >= 0; $i--) {
$month = Carbon::now()->subMonths($i);
$labels[] = $month->translatedFormat('M');
$data[] = Loan::where('user_id', $user->id)
->whereMonth('borrowed_at', $month->month)
->whereYear('borrowed_at', $month->year)
->count();
}
$statistikBulanan = [
'labels' => ['Jan', 'Feb', 'Mar', 'Apr', 'Mei', 'Jun', 'Jul'],
'data' => [10, 15, 8, 20, 18, 25, 22],
'labels' => $labels,
'data' => $data,
];
// Online books (books with 'online' in tipe_akses)

View File

@ -112,13 +112,15 @@ public function edit(Request $request): View
*/
public function update(ProfileUpdateRequest $request): RedirectResponse
{
$request->user()->fill($request->validated());
$user = $request->user();
$user->fill($request->validated());
$user->name = $user->nama_lengkap; // Sync for compatibility
if ($request->user()->isDirty('email')) {
$request->user()->email_verified_at = null;
if ($user->isDirty('email')) {
$user->email_verified_at = null;
}
$request->user()->save();
$user->save();
return Redirect::route('profile.edit')->with('status', 'profile-updated');
}

View File

@ -16,7 +16,7 @@ class ProfileUpdateRequest extends FormRequest
public function rules(): array
{
return [
'name' => ['required', 'string', 'max:255'],
'nama_lengkap' => ['required', 'string', 'max:255'],
'email' => [
'required',
'string',
@ -25,6 +25,7 @@ public function rules(): array
'max:255',
Rule::unique(User::class)->ignore($this->user()->id),
],
'phone' => ['required', 'string', 'max:20'],
];
}
}

View File

@ -8,7 +8,7 @@ class Book extends Model
{
protected $fillable = [
'judul', 'penulis', 'cover', 'kode_buku',
'category_id', 'tahun', 'status', 'is_new', 'tipe_akses'
'category_id', 'tahun', 'status', 'is_new', 'tipe_akses', 'file_pdf'
];
protected $casts = [

View File

@ -8,7 +8,8 @@ class Loan extends Model
{
protected $fillable = [
'user_id', 'book_id', 'loan_code',
'borrowed_at', 'due_at', 'returned_at', 'status'
'borrowed_at', 'due_at', 'returned_at', 'status',
'fine_overdue', 'fine_damage', 'condition', 'return_notes'
];
protected $casts = [

View File

@ -0,0 +1,28 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('books', function (Blueprint $table) {
$table->string('file_pdf')->nullable()->after('tipe_akses');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('books', function (Blueprint $table) {
$table->dropColumn('file_pdf');
});
}
};

View File

@ -0,0 +1,31 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('loans', function (Blueprint $table) {
$table->integer('fine_overdue')->default(0)->after('status');
$table->integer('fine_damage')->default(0)->after('fine_overdue');
$table->string('condition')->nullable()->after('fine_damage');
$table->text('return_notes')->nullable()->after('condition');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('loans', function (Blueprint $table) {
$table->dropColumn(['fine_overdue', 'fine_damage', 'condition', 'return_notes']);
});
}
};

View File

@ -40,7 +40,7 @@
<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"
placeholder="Contoh: 2024" required>
placeholder="Contoh: 2024" min="0" required>
</div>
<div class="col-md-4 mb-3">
<label for="kode_buku" class="form-label">Kode Buku</label>

View File

@ -62,7 +62,7 @@ class="badge bg-warning-subtle text-warning-emphasis">Dipinjam</span>
data-judul="{{ $buku['judul'] }}"
data-kode_buku="{{ $buku['kode_buku'] }}"
data-penulis="{{ $buku['penulis'] }}"
data-kategori="{{ $buku['kategori'] }}"
data-kategori="{{ $buku->category->name ?? '-' }}"
data-tahun="{{ $buku['tahun'] }}" data-status="{{ $buku['status'] }}">
<i class="bi bi-eye-fill"></i> Detail
</button>
@ -109,7 +109,7 @@ class="badge bg-info-subtle text-info-emphasis">{{ $buku['file_pdf'] ?? 'N/A' }}
data-cover="{{ asset($buku['cover']) }}"
data-judul="{{ $buku['judul'] }}"
data-penulis="{{ $buku['penulis'] }}"
data-kategori="{{ $buku['kategori'] }}"
data-kategori="{{ $buku->category->name ?? '-' }}"
data-tahun="{{ $buku['tahun'] }}" data-status="Dapat Dibaca Online">
<i class="bi bi-eye-fill"></i> Detail
</button>

View File

@ -82,9 +82,14 @@ class="badge bg-danger-subtle text-danger border border-danger-subtle rounded-pi
</td>
<td data-order="{{ $item['total_denda'] }}">
<div class="fw-bold text-danger">Rp
{{ number_format($item['total_denda'], 0, ',', '.') }}</div>
<small class="text-muted" style="font-size: 0.75rem;">Rp 1.000/hari</small>
@if($item['is_guru'])
<div class="fw-bold text-success">Bebas Denda</div>
<small class="text-muted" style="font-size: 0.75rem;">Kebijakan Guru</small>
@else
<div class="fw-bold text-danger">Rp
{{ number_format($item['total_denda'], 0, ',', '.') }}</div>
<small class="text-muted" style="font-size: 0.75rem;">Rp 1.000/hari</small>
@endif
</td>
<td>
<div class="d-flex gap-2">
@ -98,12 +103,14 @@ class="btn btn-sm btn-success text-white" title="Tagih via WhatsApp">
@if ($item['is_banned'])
{{-- Jika sudah dibekukan (Otomatis/Manual), muncul tombol AKTIFKAN --}}
<button class="btn btn-sm btn-outline-success btn-aktifkan"
data-user-id="{{ $item['user_id'] }}"
data-nama="{{ $item['peminjam'] }}" title="Aktifkan Kembali Akun">
<i class="bi bi-shield-check"></i> Aktifkan
</button>
@else
{{-- Jika belum dibekukan, muncul tombol SANKSI (Manual) --}}
<button class="btn btn-sm btn-outline-danger btn-sanksi"
data-user-id="{{ $item['user_id'] }}"
data-nama="{{ $item['peminjam'] }}" title="Berikan Sanksi">
<i class="bi bi-slash-circle"></i> Sanksi
</button>
@ -183,11 +190,12 @@ function(settings, data, dataIndex) {
});
});
// --- LOGIC TOMBOL SANKSI ---
$(document).on('click', '.btn-sanksi', function() {
const nama = $(this).data('nama');
const userId = $(this).data('user-id');
modernSwal.fire({
title: 'Nonaktifkan Guru?',
title: 'Bekukan Akun?',
text: `Apakah Anda yakin ingin memberikan sanksi pembekuan akun kepada ${nama}?`,
icon: 'warning',
showCancelButton: true,
@ -198,23 +206,40 @@ function(settings, data, dataIndex) {
if (result.isConfirmed) {
modernSwal.fire({
title: 'Memproses...',
timer: 1000,
didOpen: () => Swal.showLoading()
})
.then(() => {
Toast.fire({
icon: 'success',
title: 'Sanksi Diterapkan',
text: `Akun ${nama} berhasil dibekukan.`
});
});
$.ajax({
url: '{{ route('admin.denda.sanksi') }}',
method: 'POST',
data: {
_token: '{{ csrf_token() }}',
user_id: userId,
action: 'ban'
},
success: function(response) {
if (response.status === 'success') {
Toast.fire({
icon: 'success',
title: 'Sanksi Diterapkan',
text: response.message
});
setTimeout(() => location.reload(), 1500);
} else {
modernSwal.fire('Gagal', response.message, 'error');
}
},
error: function() {
modernSwal.fire('Gagal', 'Terjadi kesalahan saat memproses data.', 'error');
}
});
}
});
});
// Logic Tombol Aktifkan (Unban)
$(document).on('click', '.btn-aktifkan', function() {
const nama = $(this).data('nama');
const userId = $(this).data('user-id');
modernSwal.fire({
title: 'Aktifkan Akun?',
@ -226,20 +251,35 @@ function(settings, data, dataIndex) {
cancelButtonText: 'Batal'
}).then((result) => {
if (result.isConfirmed) {
// Simulasi Loading & Sukses
modernSwal.fire({
title: 'Memproses...',
timer: 1000,
didOpen: () => Swal.showLoading()
})
.then(() => {
Toast.fire({
icon: 'success',
title: 'Akun Diaktifkan',
text: `Status sanksi pada ${nama} telah dicabut.`
});
$(this).closest('td').html('<span class="badge bg-success">Aktif</span>');
});
$.ajax({
url: '{{ route('admin.denda.sanksi') }}',
method: 'POST',
data: {
_token: '{{ csrf_token() }}',
user_id: userId,
action: 'unban'
},
success: function(response) {
if (response.status === 'success') {
Toast.fire({
icon: 'success',
title: 'Akun Diaktifkan',
text: response.message
});
setTimeout(() => location.reload(), 1500);
} else {
modernSwal.fire('Gagal', response.message, 'error');
}
},
error: function() {
modernSwal.fire('Gagal', 'Terjadi kesalahan saat memproses data.', 'error');
}
});
}
});
});

View File

@ -108,7 +108,7 @@ class="badge bg-primary-subtle text-primary fw-bold">{{ $transaksi['id_peminjama
<h6 class="fw-bold mb-3">Daftar Buku yang Dikembalikan:</h6>
@foreach ($transaksi['books'] as $buku)
<div class="card mb-3 border bg-light">
<div class="card mb-3 border bg-light book-return-item" data-book-id="{{ $buku['id'] }}">
<div class="card-body p-3">
<h6 class="fw-bold text-primary mb-2">{{ $buku['judul'] }}</h6>
<div class="row">
@ -159,17 +159,25 @@ class="form-control form-control-sm denda-rusak-input"
<div class="d-flex justify-content-between align-items-center mb-3">
<span>Denda Keterlambatan ({{ $statusText }})</span>
@php
$isGuru = ($transaksi['role'] ?? '') === 'guru';
$dendaTelat = 0;
if ($isTerlambat) {
if ($isTerlambat && !$isGuru) {
$hari = $tenggat->startOfDay()->diffInDays($now->startOfDay());
$dendaTelat = $hari * 1000;
}
@endphp
{{-- Data Attribute untuk JS --}}
<strong class="text-danger denda-keterlambatan-display"
data-denda-keterlambatan="{{ $dendaTelat }}">
Rp {{ number_format($dendaTelat, 0, ',', '.') }}
</strong>
@if($isGuru && $isTerlambat)
<strong class="text-success denda-keterlambatan-display"
data-denda-keterlambatan="0">
Bebas Denda
</strong>
@else
<strong class="text-danger denda-keterlambatan-display"
data-denda-keterlambatan="{{ $dendaTelat }}">
Rp {{ number_format($dendaTelat, 0, ',', '.') }}
</strong>
@endif
</div>
<div
@ -203,7 +211,8 @@ class="d-flex justify-content-between align-items-center mb-3 bg-danger bg-opaci
data-bs-dismiss="modal">Batal</button>
<button type="button" class="btn btn-primary btn-konfirmasi-kembali"
data-nama-peminjam="{{ $transaksi['peminjam'] }}"
data-nomor-hp="{{ $transaksi['nomor_hp'] }}">
data-nomor-hp="{{ $transaksi['nomor_hp'] }}"
data-user-id="{{ $transaksi['user_id'] }}">
Konfirmasi & Selesai
</button>
</div>
@ -291,9 +300,11 @@ function hitungTotalDenda(modal) {
$(document).on('click', '.btn-konfirmasi-kembali', function() {
const nama = $(this).data('nama-peminjam');
const hp = $(this).data('nomor-hp');
const modalEl = $(this).closest('.modal');
const modalInstance = bootstrap.Modal.getInstance(modalEl[0]);
const isEmailChecked = modalEl.find('.email-toggle').is(':checked');
const isWaChecked = modalEl.find('input[role="switch"][id^="waStrukToggle"]').is(':checked');
modalInstance.hide();
@ -313,10 +324,58 @@ function hitungTotalDenda(modal) {
timerProgressBar: true,
didOpen: () => Swal.showLoading()
}).then(() => {
// Loading Kirim Email (Jika dicentang)
// Prepare Request Data
const returnsData = [];
const booksList = [];
const dendaOverdueTotal = parseInt(modalEl.find('.denda-keterlambatan-display').data('denda-keterlambatan'));
const dendaOverduePerBook = dendaOverdueTotal / modalEl.find('.book-return-item').length;
let totalDendaRusak = 0;
modalEl.find('.book-return-item').each(function() {
const bookId = $(this).data('book-id');
const judul = $(this).find('h6').text();
const condition = $(this).find('.radio-kondisi:checked').val();
const fineDamage = parseInt($(this).find('.denda-rusak-input').val()) || 0;
const notes = $(this).find('textarea').val();
totalDendaRusak += fineDamage;
booksList.push(judul);
returnsData.push({
book_id: bookId,
condition: condition,
fine_damage: fineDamage,
fine_overdue: Math.round(dendaOverduePerBook),
notes: notes
});
});
const userId = modalEl.find('.btn-konfirmasi-kembali').data('user-id');
const totalDenda = dendaOverdueTotal + totalDendaRusak;
// Construct WA Message
let waLink = null;
if (isWaChecked && hp && hp !== '-') {
let phone = hp;
if (phone.startsWith('0')) phone = '62' + phone.substring(1);
let message = `*BUKTI PENGEMBALIAN BUKU*\n\n`;
message += `Halo ${nama},\n`;
message += `Terima kasih telah mengembalikan buku:\n`;
booksList.forEach(b => message += `- ${b}\n`);
message += `\n*Status:* Berhasil Dikembalikan\n`;
if (totalDenda > 0) {
message += `*Total Denda:* Rp ${new Intl.NumberFormat('id-ID').format(totalDenda)}\n`;
} else {
message += `*Denda:* Bebas Denda\n`;
}
message += `\n_Simpan pesan ini sebagai bukti pengembalian._`;
waLink = `https://wa.me/${phone}?text=${encodeURIComponent(message)}`;
}
// Loading Kirim Email flow
if (isEmailChecked) {
const dummyEmail = nama.replace(/\s+/g, '.').toLowerCase() +
'@sekolah.sch.id';
const dummyEmail = nama.replace(/\s+/g, '.').toLowerCase() + '@sekolah.sch.id';
modernSwal.fire({
title: 'Mengirim Email...',
html: `Mengirim nota ke: <b>${dummyEmail}</b>`,
@ -324,10 +383,10 @@ function hitungTotalDenda(modal) {
timerProgressBar: true,
didOpen: () => Swal.showLoading()
}).then(() => {
finishTransaction(true);
finishTransaction(returnsData, userId, waLink);
});
} else {
finishTransaction(false);
finishTransaction(returnsData, userId, waLink);
}
});
} else {
@ -335,13 +394,35 @@ function hitungTotalDenda(modal) {
}
});
function finishTransaction(withEmail) {
Toast.fire({
icon: 'success',
title: 'Berhasil',
text: withEmail ? 'Buku kembali & Email terkirim.' : 'Buku berhasil dikembalikan.'
function finishTransaction(returnsData, userId, waLink) {
$.ajax({
url: '{{ route('admin.peminjaman.kembali') }}',
method: 'POST',
data: {
_token: '{{ csrf_token() }}',
user_id: userId,
returns: returnsData
},
success: function(response) {
if (response.status === 'success') {
if (waLink) {
window.open(waLink, '_blank');
}
Toast.fire({
icon: 'success',
title: 'Berhasil',
text: 'Buku berhasil dikembalikan.'
});
setTimeout(() => location.reload(), 1500);
} else {
modernSwal.fire('Gagal', response.message, 'error');
}
},
error: function(xhr) {
modernSwal.fire('Gagal', 'Terjadi kesalahan saat memproses data.', 'error');
}
});
setTimeout(() => location.reload(), 1500);
}
});
</script>

View File

@ -22,19 +22,12 @@
<div class="mb-3">
<label for="email" class="form-label">Email</label>
<input type="email" name="email" class="form-control" id="email"
placeholder="Masukkan alamat email" required>
placeholder="Masukkan alamat email" required pattern="[^@\s]+@[^@\s]+\.[^@\s]+" title="Masukkan alamat email yang valid">
</div>
<div class="mb-3">
<label for="nomor_induk" class="form-label">Nomor Induk (NISN/NUPTK)</label>
<input type="text" name="nomor_induk" class="form-control" id="nomor_induk" placeholder="Masukkan NISN atau NUPTK">
</div>
<div class="mb-3">
<label for="phone" class="form-label">Nomor HP</label>
<input type="text" name="phone" class="form-control" id="phone" placeholder="Masukkan nomor HP">
</div>
<div class="mb-3">
<label for="kelas" class="form-label">Kelas (jika siswa)</label>
<input type="text" name="kelas" class="form-control" id="kelas" placeholder="Contoh: X IPA 1">
<input type="text" name="nomor_induk" class="form-control numeric-only" id="nomor_induk"
placeholder="Masukkan NISN atau NUPTK" inputmode="numeric">
</div>
<div class="mb-3">
<label for="role" class="form-label">Role</label>
@ -45,6 +38,14 @@
<option value="penjaga perpus">Penjaga Perpus</option>
</select>
</div>
<div class="mb-3" id="kelas-container" style="display: none;">
<label for="kelas" class="form-label">Kelas (jika siswa)</label>
<input type="text" name="kelas" class="form-control" id="kelas" placeholder="Contoh: X IPA 1">
</div>
<div class="mb-3">
<label for="phone" class="form-label">Nomor HP</label>
<input type="text" name="phone" class="form-control numeric-only" id="phone" placeholder="Masukkan nomor HP" inputmode="numeric">
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label for="password" class="form-label">Password</label>
@ -60,6 +61,33 @@
<button type="submit" class="btn btn-primary">Simpan Pengguna</button>
</div>
</form>
<script>
document.addEventListener('DOMContentLoaded', function() {
// Numeric only input
document.querySelectorAll('.numeric-only').forEach(function(input) {
input.addEventListener('input', function(e) {
this.value = this.value.replace(/[^0-9]/g, '');
});
});
// Dynamic Kelas Input
const roleSelect = document.getElementById('role');
const kelasContainer = document.getElementById('kelas-container');
function toggleKelas() {
if (roleSelect.value === 'siswa') {
kelasContainer.style.display = 'block';
} else {
kelasContainer.style.display = 'none';
document.getElementById('kelas').value = ''; // Reset value
}
}
roleSelect.addEventListener('change', toggleKelas);
toggleKelas(); // Run on load
});
</script>
</div>
</div>
</div>

View File

@ -23,22 +23,12 @@
<div class="mb-3">
<label for="email" class="form-label">Email</label>
<input type="email" name="email" class="form-control" id="email"
value="{{ old('email', $pengguna->email) }}" required>
value="{{ old('email', $pengguna->email) }}" required pattern="[^@\s]+@[^@\s]+\.[^@\s]+" title="Masukkan alamat email yang valid">
</div>
<div class="mb-3">
<label for="nomor_induk" class="form-label">Nomor Induk (NISN/NUPTK)</label>
<input type="text" name="nomor_induk" class="form-control" id="nomor_induk"
value="{{ old('nomor_induk', $pengguna->nomor_induk) }}">
</div>
<div class="mb-3">
<label for="phone" class="form-label">Nomor HP</label>
<input type="text" name="phone" class="form-control" id="phone"
value="{{ old('phone', $pengguna->phone) }}">
</div>
<div class="mb-3">
<label for="kelas" class="form-label">Kelas (jika siswa)</label>
<input type="text" name="kelas" class="form-control" id="kelas"
value="{{ old('kelas', $pengguna->kelas) }}">
<input type="text" name="nomor_induk" class="form-control numeric-only" id="nomor_induk"
value="{{ old('nomor_induk', $pengguna->nomor_induk) }}" inputmode="numeric">
</div>
<div class="mb-3">
<label for="role" class="form-label">Role</label>
@ -52,6 +42,16 @@
Penjaga Perpus</option>
</select>
</div>
<div class="mb-3" id="kelas-container" style="display: none;">
<label for="kelas" class="form-label">Kelas (jika siswa)</label>
<input type="text" name="kelas" class="form-control" id="kelas"
value="{{ old('kelas', $pengguna->kelas) }}">
</div>
<div class="mb-3">
<label for="phone" class="form-label">Nomor HP</label>
<input type="text" name="phone" class="form-control numeric-only" id="phone"
value="{{ old('phone', $pengguna->phone) }}" inputmode="numeric">
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label for="password" class="form-label">Password Baru</label>
@ -68,6 +68,44 @@
<button type="submit" class="btn btn-primary">Simpan Perubahan</button>
</div>
</form>
<script>
document.addEventListener('DOMContentLoaded', function() {
// Numeric only input
document.querySelectorAll('.numeric-only').forEach(function(input) {
input.addEventListener('input', function(e) {
this.value = this.value.replace(/[^0-9]/g, '');
});
});
// Dynamic Kelas Input
const roleSelect = document.getElementById('role');
const kelasContainer = document.getElementById('kelas-container');
function toggleKelas() {
if (roleSelect.value === 'siswa') {
kelasContainer.style.display = 'block';
} else {
kelasContainer.style.display = 'none';
// Optional: Clear value only if changing to non-student to prevent accidental data loss on edit?
// For edit, maybe better NOT to clear immediately unless user saves?
// But per request "hide input", clearing might be expected behavior if they change role.
// I'll keep it simple: just hide. If they change role to Teacher, they probably want to remove class.
// But resetting value on edit might exist data. Let's just hide for now.
// Actually, if they SUBMIT, and it is hidden, logic might be needed to clear it in backend or here.
// For now, mirroring create behavior (reset logic mainly).
// Wait, on Edit, if I load as Guru, it hides. If I change to Siswa, it shows (empty or old value).
// If I change Siswa -> Guru, it hides.
// For safety in Edit, I won't force clear value on toggle to avoid losing data if they accidentally switch.
// Just hide.
}
}
roleSelect.addEventListener('change', toggleKelas);
toggleKelas(); // Run on load
});
</script>
</div>
</div>
</div>

View File

@ -39,7 +39,7 @@
data-nama="{{ $siswa['nama_lengkap'] }}"
data-email="{{ $siswa['email'] }}"
data-role="{{ Str::title($siswa['role']) }}"
data-nisn="{{ $siswa['nisn'] ?? 'N/A' }}">
data-nisn="{{ $siswa['nomor_induk'] ?? 'N/A' }}">
<i class="bi bi-eye-fill"></i> Detail
</button>
</td>

View File

@ -32,12 +32,20 @@
{{ $item['content'] }}
</td>
<td>
<a href="{{ route('admin.pengumuman.edit', $item['id']) }}" class="btn btn-sm btn-outline-secondary">
<i class="bi bi-pencil-fill"></i>
</a>
<button class="btn btn-sm btn-outline-danger" disabled>
<i class="bi bi-trash3-fill"></i>
</button>
<div class="d-flex gap-2">
<a href="{{ route('admin.pengumuman.edit', $item['id']) }}" class="btn btn-sm btn-outline-secondary">
<i class="bi bi-pencil-fill"></i>
</a>
<button class="btn btn-sm btn-outline-danger btn-delete"
data-id="{{ $item['id'] }}"
data-title="{{ $item['title'] }}">
<i class="bi bi-trash3-fill"></i>
</button>
<form id="delete-form-{{ $item['id'] }}" action="{{ route('admin.pengumuman.destroy', $item['id']) }}" method="POST" style="display: none;">
@csrf
@method('DELETE')
</form>
</div>
</td>
</tr>
@empty
@ -52,4 +60,27 @@
</div>
</div>
</div>
@push('scripts')
<script>
$(document).on('click', '.btn-delete', function() {
const id = $(this).data('id');
const title = $(this).data('title');
modernSwal.fire({
title: 'Hapus Pengumuman?',
text: `Apakah Anda yakin ingin menghapus pengumuman "${title}"?`,
icon: 'warning',
showCancelButton: true,
confirmButtonText: 'Ya, Hapus',
confirmButtonColor: '#dc3545',
cancelButtonText: 'Batal'
}).then((result) => {
if (result.isConfirmed) {
$(`#delete-form-${id}`).submit();
}
});
});
</script>
@endpush
</x-app-layout>

View File

@ -17,7 +17,20 @@
<td>{{ $item['judul'] }}</td>
<td>{{ $item['kategori'] }}</td>
<td>
<a href="{{ route('admin.rekomendasi.edit', $item['id']) }}" class="btn btn-sm btn-outline-secondary"><i class="bi bi-pencil-fill"></i></a>
<div class="d-flex gap-2">
<a href="{{ route('admin.rekomendasi.edit', $item['id']) }}" class="btn btn-sm btn-outline-secondary">
<i class="bi bi-pencil-fill"></i>
</a>
<button class="btn btn-sm btn-outline-danger btn-delete"
data-id="{{ $item['id'] }}"
data-title="{{ $item['judul'] }}">
<i class="bi bi-trash3-fill"></i>
</button>
<form id="delete-form-{{ $item['id'] }}" action="{{ route('admin.rekomendasi.destroy', $item['id']) }}" method="POST" style="display: none;">
@csrf
@method('DELETE')
</form>
</div>
</td>
</tr>
@endforeach
@ -26,4 +39,27 @@
</div>
</div>
</div>
@push('scripts')
<script>
$(document).on('click', '.btn-delete', function() {
const id = $(this).data('id');
const title = $(this).data('title');
modernSwal.fire({
title: 'Hapus Rekomendasi?',
text: `Apakah Anda yakin ingin menghapus rekomendasi "${title}"?`,
icon: 'warning',
showCancelButton: true,
confirmButtonText: 'Ya, Hapus',
confirmButtonColor: '#dc3545',
cancelButtonText: 'Batal'
}).then((result) => {
if (result.isConfirmed) {
$(`#delete-form-${id}`).submit();
}
});
});
</script>
@endpush
</x-app-layout>

View File

@ -1,8 +1,8 @@
<x-guest-layout>
@if ($errors->has('forbidden'))
<div class="alert alert-danger d-flex align-items-center" role="alert">
<i class="bi bi-exclamation-triangle-fill me-2"></i>
<div>{{ $errors->first('forbidden') }}</div>
<div class="alert alert-danger d-flex align-items-center mb-4" role="alert">
<i class="bi bi-exclamation-triangle-fill me-2 fs-5"></i>
<div class="fw-bold">{{ $errors->first('forbidden') }}</div>
</div>
@endif

View File

@ -16,7 +16,10 @@
<label for="name" class="form-label">Nama Lengkap</label>
<input id="name" class="form-control bg-body-tertiary @error('name') is-invalid @enderror" type="text" name="name" value="{{ old('name') }}" required autofocus autocomplete="name" />
@error('name')
<div class="invalid-feedback">{{ $message }}</div>
<div class="invalid-feedback fw-semibold">
<i class="bi bi-exclamation-circle-fill me-1"></i>
{{ $message }}
</div>
@enderror
</div>
@ -26,7 +29,10 @@
<label for="nisn" class="form-label">Nomor Induk Siswa Nasional (NISN)</label>
<input id="nisn" class="form-control bg-body-tertiary @error('nisn') is-invalid @enderror" type="text" name="nisn" value="{{ old('nisn') }}" required autocomplete="username" />
@error('nisn')
<div class="invalid-feedback">{{ $message }}</div>
<div class="invalid-feedback fw-semibold">
<i class="bi bi-exclamation-circle-fill me-1"></i>
{{ $message }}
</div>
@enderror
</div>
@else
@ -34,7 +40,10 @@
<label for="nip" class="form-label">Nomor Induk Pegawai (NIP)</label>
<input id="nip" class="form-control bg-body-tertiary @error('nip') is-invalid @enderror" type="text" name="nip" value="{{ old('nip') }}" required autocomplete="username" />
@error('nip')
<div class="invalid-feedback">{{ $message }}</div>
<div class="invalid-feedback fw-semibold">
<i class="bi bi-exclamation-circle-fill me-1"></i>
{{ $message }}
</div>
@enderror
</div>
@endif
@ -44,7 +53,10 @@
<label for="password" class="form-label">Password</label>
<input id="password" class="form-control bg-body-tertiary @error('password') is-invalid @enderror" type="password" name="password" required autocomplete="new-password" />
@error('password')
<div class="invalid-feedback">{{ $message }}</div>
<div class="invalid-feedback fw-semibold">
<i class="bi bi-exclamation-circle-fill me-1"></i>
{{ $message }}
</div>
@enderror
</div>

View File

@ -36,8 +36,9 @@
<p class="small text-danger mt-1">Jangan bagikan kode ini kepada orang lain.</p>
</div>
@if (session('error'))
<div class="alert alert-danger">
{{ session('error') }}
<div class="alert alert-danger border-0 shadow-sm rounded-3 d-flex align-items-center mb-4">
<i class="bi bi-exclamation-circle-fill me-2 fs-5"></i>
<div class="small fw-bold">{{ session('error') }}</div>
</div>
@endif

View File

@ -1,7 +1,8 @@
@props(['status'])
@if ($status)
<div {{ $attributes->merge(['class' => 'font-medium text-sm text-green-600']) }}>
{{ $status }}
<div {{ $attributes->merge(['class' => 'alert alert-success d-flex align-items-center mb-4']) }} role="alert">
<i class="bi bi-check-circle-fill me-2"></i>
<div class="small fw-semibold">{{ $status }}</div>
</div>
@endif

View File

@ -1,9 +1,12 @@
@props(['messages'])
@if ($messages)
<ul {{ $attributes->merge(['class' => 'text-sm text-red-600 space-y-1']) }}>
<ul {{ $attributes->merge(['class' => 'list-unstyled mt-1 mb-0']) }}>
@foreach ((array) $messages as $message)
<li>{{ $message }}</li>
<li class="text-danger small fw-semibold">
<i class="bi bi-exclamation-circle-fill me-1"></i>
{{ $message }}
</li>
@endforeach
</ul>
@endif

View File

@ -113,11 +113,16 @@ class="btn btn-sm btn-primary w-100">
</div>
@empty
<div class="col-12">
<div class="alert alert-warning text-center">
<h4 class="alert-heading">Tidak Ada Hasil</h4>
<p>Tidak ada buku yang cocok dengan kriteria filter Anda. Coba reset atau ubah filter.</p>
<hr>
<a href="{{ route(request()->route()->getName()) }}" class="btn btn-primary">Reset Filter</a>
<div class="alert alert-warning text-center border-0 shadow-sm rounded-4 py-5 mb-0">
<div class="icon-circle bg-warning-subtle text-warning mx-auto mb-3" style="width: 80px; height: 80px; border-radius: 20px;">
<i class="bi bi-search fs-1"></i>
</div>
<h4 class="fw-bold mb-2" style="color: #856404;">Tidak Ada Hasil</h4>
<p class="text-muted">Tidak ada buku yang cocok dengan kriteria filter Anda.<br>Coba reset atau ubah filter pencarian Anda.</p>
<hr class="w-25 mx-auto my-4 opacity-10">
<a href="{{ route(request()->route()->getName()) }}" class="btn btn-warning px-4 rounded-pill fw-bold">
<i class="bi bi-arrow-counterclockwise me-1"></i> Reset Filter
</a>
</div>
</div>
@endforelse

View File

@ -33,9 +33,40 @@
@include('layouts.navigation')
<main class="container-fluid py-4 px-4">
@if (session('error'))
<div class="alert alert-danger alert-dismissible fade show m-3" role="alert">
<i class="bi bi-exclamation-octagon-fill me-2"></i>
<strong>DITOLAK!</strong> {{ session('error') }}
<div class="alert alert-danger alert-dismissible fade show m-3 border-0 shadow-sm rounded-4" role="alert">
<div class="d-flex align-items-center">
<i class="bi bi-exclamation-octagon-fill me-2 fs-4"></i>
<div>
<strong class="text-danger h6 mb-0 d-block">DITOLAK!</strong>
<span class="small">{{ session('error') }}</span>
</div>
</div>
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
@endif
@if (session('success'))
<div class="alert alert-success alert-dismissible fade show m-3 border-0 shadow-sm rounded-4" role="alert">
<div class="d-flex align-items-center">
<i class="bi bi-check-circle-fill me-2 fs-4"></i>
<div>
<strong class="text-success h6 mb-0 d-block">BERHASIL!</strong>
<span class="small">{{ session('success') }}</span>
</div>
</div>
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
@endif
@if (session('warning'))
<div class="alert alert-warning alert-dismissible fade show m-3 border-0 shadow-sm rounded-4" role="alert">
<div class="d-flex align-items-center">
<i class="bi bi-exclamation-triangle-fill me-2 fs-4"></i>
<div>
<strong class="h6 mb-0 d-block" style="color: #856404;">PERINGATAN!</strong>
<span class="small">{{ session('warning') }}</span>
</div>
</div>
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
@endif

View File

@ -29,7 +29,7 @@
</div>
<div class="col-md-6">
<label class="form-label text-muted small">Nomor Handphone</label>
<p class="fw-semibold">{{ $user['nomor_hp'] }}</p>
<p class="fw-semibold">{{ $user->phone ?? 'N/A' }}</p>
</div>
<div class="col-md-6">
<label class="form-label text-muted small">Kelas</label>
@ -275,9 +275,9 @@ class="bi bi-star-fill me-1"></i>Buku Utama</span>
{{-- <button type="button" class="btn btn-primary px-4" onclick="kirimForm()">Setuju</button> --}}
{{-- Button Setuju jika belum ada backend, akan ter direct ke peminjaman.index --}}
<a href="{{ route('peminjaman.index') }}" class="btn btn-primary px-4">
<button type="submit" form="formPeminjaman" class="btn btn-primary px-4">
Setuju
</a>
</button>
</div>
</div>
</div>
@ -286,7 +286,6 @@ class="bi bi-star-fill me-1"></i>Buku Utama</span>
document.addEventListener("DOMContentLoaded", function() {
// (Default Hari Ini + 2 Hari)
// Simpan instance-nya ke variabel
const fpKembali = flatpickr("#tanggalKembali", {
dateFormat: "d F Y",
altInput: true,
@ -306,7 +305,6 @@ class="bi bi-star-fill me-1"></i>Buku Utama</span>
locale: "id",
minDate: "today",
// LOGIC : Saat Tanggal Pinjam Berubah
onChange: function(selectedDates, dateStr) {
if (selectedDates.length > 0) {
const tglMulai = selectedDates[0];
@ -315,11 +313,117 @@ class="bi bi-star-fill me-1"></i>Buku Utama</span>
fpKembali.set("minDate", minDateBaru);
fpKembali.set("maxDate", maxDateBaru);
fpKembali.setDate(maxDateBaru);
}
}
});
// Inisialisasi data dari data-attributes
const form = document.getElementById('formPeminjaman');
const arrayBuku = JSON.parse(form.dataset.semuaBuku);
const bukuUtama = JSON.parse(form.dataset.bukuAwal);
let selectedIds = [bukuUtama.id];
const MAX_BOOKS = 2;
// Global functions
window.toggleBookSelection = function(id) {
const index = selectedIds.indexOf(id);
const checkbox = document.getElementById('book' + id);
if (index > -1) {
if (id === bukuUtama.id) return;
selectedIds.splice(index, 1);
if (checkbox) checkbox.checked = false;
} else {
if (selectedIds.length >= MAX_BOOKS) {
alert('Maksimal peminjaman adalah ' + MAX_BOOKS + ' buku.');
return;
}
selectedIds.push(id);
if (checkbox) checkbox.checked = true;
}
updateCounter();
};
window.konfirmasiPilihanBuku = function() {
renderDaftarBuku();
const modal = bootstrap.Modal.getInstance(document.getElementById('pilihBukuModal'));
modal.hide();
};
function updateCounter() {
const counterEl = document.getElementById('counterBuku');
const sisaSlotEl = document.getElementById('sisaSlot');
if (counterEl) counterEl.textContent = selectedIds.length;
if (sisaSlotEl) sisaSlotEl.textContent = MAX_BOOKS - selectedIds.length;
}
function renderDaftarBuku() {
const container = document.getElementById('daftarBukuPinjam');
const hiddenInputs = document.getElementById('hiddenInputs');
const ringkasan = document.getElementById('ringkasanBuku');
if (!container || !hiddenInputs || !ringkasan) return;
container.innerHTML = '';
hiddenInputs.innerHTML = '';
ringkasan.innerHTML = '<ul class="mb-0 text-start">';
selectedIds.forEach(id => {
const buku = arrayBuku.find(b => b.id == id);
if (!buku) return;
const isUtama = id === (typeof bukuUtama.id === 'string' ? parseInt(bukuUtama.id) : bukuUtama.id);
const itemHtml = `
<div class="book-item border rounded p-3 mb-3" data-book-id="${buku.id}">
<div class="d-flex align-items-start text-start">
<img src="/${buku.cover}" alt="Cover" class="rounded me-3 form-book-cover" style="width: 50px; height: 70px; object-fit: cover;">
<div class="flex-grow-1">
<h6 class="fw-bold mb-1">${buku.judul}</h6>
<p class="text-muted small mb-1">${buku.penulis}</p>
<span class="badge bg-info">${buku.kategori || 'Kategori'}</span>
</div>
${isUtama ? '<span class="badge bg-success">Buku Utama</span>' :
'<button type="button" class="btn btn-sm btn-outline-danger" onclick="toggleSelectionAndRender(' + buku.id + ')"><i class="bi bi-trash"></i></button>'}
</div>
</div>
`;
container.insertAdjacentHTML('beforeend', itemHtml);
const input = document.createElement('input');
input.type = 'hidden';
input.name = 'buku_ids[]';
input.value = buku.id;
hiddenInputs.appendChild(input);
ringkasan.innerHTML += `<li>${buku.judul}</li>`;
});
ringkasan.innerHTML += '</ul>';
}
window.toggleSelectionAndRender = function(id) {
window.toggleBookSelection(id);
renderDaftarBuku();
};
document.getElementById('searchBuku').addEventListener('input', function(e) {
const keyword = e.target.value.toLowerCase();
document.querySelectorAll('.book-option').forEach(el => {
const title = el.dataset.bookTitle;
const author = el.dataset.bookAuthor;
if (title.includes(keyword) || author.includes(keyword)) {
el.style.display = 'block';
} else {
el.style.display = 'none';
}
});
});
renderDaftarBuku();
updateCounter();
});
</script>

View File

@ -110,6 +110,22 @@ class="btn btn-outline-primary rounded-pill w-100 w-md-auto ms-md-auto">
<i class="bi bi-pencil-square me-2"></i>Edit Profil
</a>
</div>
<hr class="my-3 my-md-4">
<h5 class="fw-bold mb-3 px-4">Informasi Personal</h5>
<div class="row g-3 px-4 pb-4">
<div class="col-sm-6">
<small class="text-muted d-block mb-1">NIP / NUPTK</small>
<p class="fw-semibold mb-0">{{ $user->nuptk ?? ($user->nomor_induk ?? 'N/A') }}</p>
</div>
<div class="col-sm-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">
<small class="text-muted d-block mb-1">Nomor HP</small>
<p class="fw-semibold mb-0">{{ $user->phone ?? 'N/A' }}</p>
</div>
</div>
</div>
{{-- Ringkasan Laporan Minat Baca --}}
@ -192,7 +208,7 @@ class="btn btn-outline-primary rounded-pill ms-md-auto">
<div class="row g-3">
<div class="col-sm-6">
<small class="text-muted d-block mb-1">NISN</small>
<p class="fw-semibold mb-0">{{ $user->nisn ?? 'N/A' }}</p>
<p class="fw-semibold mb-0">{{ $user->nomor_induk ?? 'N/A' }}</p>
</div>
<div class="col-sm-6">
<small class="text-muted d-block mb-1">Email</small>
@ -200,7 +216,7 @@ class="btn btn-outline-primary rounded-pill ms-md-auto">
</div>
<div class="col-sm-6">
<small class="text-muted d-block mb-1">Nomor HP</small>
<p class="fw-semibold mb-0">{{ $user->nomor_hp ?? 'N/A' }}</p>
<p class="fw-semibold mb-0">{{ $user->phone ?? 'N/A' }}</p>
</div>
<div class="col-sm-6">
<small class="text-muted d-block mb-1">Kelas</small>

View File

@ -55,43 +55,41 @@ class="form-control @error('email') is-invalid @enderror" value="{{ old('email',
</div>
{{-- Bagian Nomor Telepon --}}
@if ($user->nomor_hp)
<div class="mb-3">
<label for="nomor_hp" class="form-label">{{ __('Nomor Telepon (WA)') }}</label>
<input id="nomor_hp" name="nomor_hp" type="text" class="form-control @error('nomor_hp') is-invalid @enderror"
value="{{ old('nomor_hp', $user->nomor_hp) }}" required>
@error('nomor_hp')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
<small class="text-muted">*Nomor telepon wajib disi</small>
</div>
@endif
<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>
@error('phone')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
<small class="text-muted">*Nomor telepon wajib diisi</small>
</div>
<hr class="my-4">
{{-- Bagian Info Spesifik Role (Tidak Dapat Diubah) --}}
@if ($user->role == 'siswa')
<div class="mb-3">
<label for="nisn" class="form-label">NISN (Nomor Induk Siswa Nasional)</label>
<input id="nisn" type="text" class="form-control" value="{{ $user->nisn }}" readonly disabled>
<label for="nomor_induk" class="form-label">NISN (Nomor Induk Siswa Nasional)</label>
<input id="nomor_induk" type="text" class="form-control" value="{{ $user->nomor_induk }}" readonly disabled>
<small class="text-muted">NISN tidak dapat diubah.</small>
</div>
<div class="row g-3">
<div class="col-md-6 mb-3">
<label for="kelas" class="form-label">Kelas</label>
<input id="kelas" type="text" class="form-control" value="{{ $user->kelas }}">
<input id="kelas" type="text" class="form-control" value="{{ $user->kelas }}" readonly disabled>
</div>
<div class="col-md-6 mb-3">
<label for="golongan" class="form-label">Golongan</label>
<input id="golongan" type="text" class="form-control" value="{{ $user->golongan }}" >
<input id="golongan" type="text" class="form-control" value="{{ $user->golongan }}" readonly disabled>
</div>
</div>
@else
{{-- Untuk Guru atau Penjaga Perpus --}}
<div class="mb-3">
<label for="nip" class="form-label">NIP (Nomor Induk Pegawai)</label>
<input id="nip" type="text" class="form-control" value="{{ $user->nip }}" readonly disabled>
<small class="text-muted">NIP tidak dapat diubah.</small>
<label for="nuptk" class="form-label">NIP / NUPTK</label>
<input id="nuptk" type="text" class="form-control" value="{{ $user->nuptk }}" readonly disabled>
<small class="text-muted">ID Kepegawaian tidak dapat diubah.</small>
</div>
@endif

View File

@ -112,6 +112,7 @@
Route::get('/peminjaman', [AdminPeminjamanController::class, 'index'])->name('peminjaman.index');
Route::get('/peminjaman/tambah', [AdminPeminjamanController::class, 'create'])->name('peminjaman.create');
Route::post('/peminjaman', [AdminPeminjamanController::class, 'store'])->name('peminjaman.store');
Route::post('/peminjaman/kembali', [AdminPeminjamanController::class, 'kembalikan'])->name('peminjaman.kembali');
Route::get('/denda', [AdminPeminjamanController::class, 'dendaIndex'])->name('denda.index');
Route::post('/denda/sanksi', [AdminPeminjamanController::class, 'berikanSanksi'])->name('denda.sanksi');