feat: Implement Master Induk (whitelist) for user registration and add return receipt email functionality.

This commit is contained in:
cukiprit 2026-03-14 01:16:16 +07:00
parent 4c6a2f9a57
commit bf12a38ccb
17 changed files with 396 additions and 120 deletions

View File

@ -9,6 +9,9 @@
use Carbon\Carbon; use Carbon\Carbon;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Log;
use App\Mail\ReturnReceipt;
class AdminPeminjamanController extends Controller class AdminPeminjamanController extends Controller
{ {
@ -301,6 +304,7 @@ public function kembalikan(Request $request)
'returns.*.fine_damage' => 'required|integer', 'returns.*.fine_damage' => 'required|integer',
'returns.*.fine_overdue' => 'required|integer', 'returns.*.fine_overdue' => 'required|integer',
'returns.*.notes' => 'nullable|string', 'returns.*.notes' => 'nullable|string',
'send_email' => 'nullable|boolean',
]); ]);
\DB::beginTransaction(); \DB::beginTransaction();
@ -331,6 +335,36 @@ public function kembalikan(Request $request)
\DB::commit(); \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.']); return response()->json(['status' => 'success', 'message' => 'Buku berhasil dikembalikan.']);
} catch (\Exception $e) { } catch (\Exception $e) {
\DB::rollBack(); \DB::rollBack();

View File

@ -18,9 +18,9 @@ public function index()
$query->where('role', request('role')); $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', [ return view('admin.pengguna.index', [
'pageTitle' => 'Daftar Pengguna', '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) public function edit($id)
@ -48,7 +56,7 @@ public function store(Request $request)
$validated = $request->validate([ $validated = $request->validate([
'nama_lengkap' => 'required|string|max:255', 'nama_lengkap' => 'required|string|max:255',
'email' => 'required|email|unique:users,email', 'email' => 'required|email|unique:users,email',
'nomor_induk' => 'nullable|string|max:50', 'nomor_induk' => 'required|string|max:50|unique:users,nomor_induk',
'phone' => 'nullable|string|max:20', 'phone' => 'nullable|string|max:20',
'role' => 'required|in:siswa,guru,penjaga perpus', 'role' => 'required|in:siswa,guru,penjaga perpus',
'kelas' => 'nullable|string|max:50', 'kelas' => 'nullable|string|max:50',
@ -56,6 +64,17 @@ public function store(Request $request)
'password' => 'required|string|min:8|confirmed', '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['password'] = Hash::make($validated['password']);
$validated['name'] = $validated['nama_lengkap']; // Set name field for compatibility $validated['name'] = $validated['nama_lengkap']; // Set name field for compatibility

View File

@ -49,7 +49,7 @@ public function index(Request $request): \Illuminate\View\View|\Illuminate\Http\
$viewData['bukuOffline'] = $loans->map(fn($loan) => [ $viewData['bukuOffline'] = $loans->map(fn($loan) => [
'judul' => $loan->book->judul, 'judul' => $loan->book->judul,
'penulis' => $loan->book->penulis, '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, 'cover' => $loan->book->cover,
]); ]);
@ -87,7 +87,7 @@ public function index(Request $request): \Illuminate\View\View|\Illuminate\Http\
$viewData['bukuOffline'] = $loans->map(fn($loan) => [ $viewData['bukuOffline'] = $loans->map(fn($loan) => [
'judul' => $loan->book->judul, 'judul' => $loan->book->judul,
'penulis' => $loan->book->penulis, '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, 'cover' => $loan->book->cover,
]); ]);

View File

@ -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 [];
}
}

View File

@ -7,7 +7,7 @@
class Book extends Model class Book extends Model
{ {
protected $fillable = [ 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' 'category_id', 'tahun', 'status', 'is_new', 'tipe_akses', 'file_pdf', 'is_arsip'
]; ];

View File

@ -5,6 +5,7 @@
use App\Models\Loan; use App\Models\Loan;
use Carbon\Carbon; use Carbon\Carbon;
use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Auth;
use Illuminate\Pagination\Paginator;
use Illuminate\Support\Facades\URL; use Illuminate\Support\Facades\URL;
use Illuminate\Support\ServiceProvider; use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\View; use Illuminate\Support\Facades\View;
@ -28,6 +29,8 @@ public function boot(): void
URL::forceScheme('https'); URL::forceScheme('https');
} }
Paginator::useBootstrapFive();
// View Composer untuk semua view (*) // View Composer untuk semua view (*)
View::composer('*', function ($view) { View::composer('*', function ($view) {
if (Auth::check()) { if (Auth::check()) {

View File

@ -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(),
];
}
}

View File

@ -96,6 +96,7 @@ public function run()
AnnouncementSeeder::class, AnnouncementSeeder::class,
RecommendationSeeder::class, RecommendationSeeder::class,
LoanSeeder::class, LoanSeeder::class,
UserSeeder::class,
]); ]);
} }
} }

View File

@ -49,5 +49,30 @@ public function run(): void
'role' => 'penjaga perpus', '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();
\App\Models\MasterInduk::create([
'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']),
]);
}
} }
} }

View File

@ -376,31 +376,29 @@ function hitungTotalDenda(modal) {
// Loading Kirim Email flow // Loading Kirim Email flow
if (isEmailChecked) { if (isEmailChecked) {
modernSwal.fire({ modernSwal.fire({
title: 'Mengirim Email...', title: 'Memproses...',
html: `Mengirim nota ke: <b>${userEmail}</b>`, html: `Mengirim nota ke: <b>${userEmail}</b>`,
timer: 2000, didOpen: () => Swal.showLoading(),
timerProgressBar: true, allowOutsideClick: false
didOpen: () => Swal.showLoading()
}).then(() => {
finishTransaction(returnsData, userId, waLink);
}); });
} else {
finishTransaction(returnsData, userId, waLink);
} }
finishTransaction(returnsData, userId, waLink, isEmailChecked);
}); });
} else { } else {
modalInstance.show(); modalInstance.show();
} }
}); });
function finishTransaction(returnsData, userId, waLink) { function finishTransaction(returnsData, userId, waLink, isEmailChecked) {
$.ajax({ $.ajax({
url: "{{ route('admin.peminjaman.kembali') }}", url: "{{ route('admin.peminjaman.kembali') }}",
method: 'POST', method: 'POST',
data: { data: {
_token: '{{ csrf_token() }}', _token: '{{ csrf_token() }}',
user_id: userId, user_id: userId,
returns: returnsData returns: returnsData,
send_email: isEmailChecked ? 1 : 0
}, },
success: function(response) { success: function(response) {
if (response.status === 'success') { if (response.status === 'success') {
@ -408,11 +406,19 @@ function finishTransaction(returnsData, userId, waLink) {
window.open(waLink, '_blank'); window.open(waLink, '_blank');
} }
Toast.fire({ if (response.email_error) {
icon: 'success', Toast.fire({
title: 'Berhasil', icon: 'warning',
text: 'Buku berhasil dikembalikan.' title: 'Berhasil (Email Gagal)',
}); text: response.message
});
} else {
Toast.fire({
icon: 'success',
title: 'Berhasil',
text: 'Buku berhasil dikembalikan.'
});
}
setTimeout(() => location.reload(), 1500); setTimeout(() => location.reload(), 1500);
} else { } else {
modernSwal.fire('Gagal', response.message, 'error'); modernSwal.fire('Gagal', response.message, 'error');

View File

@ -7,6 +7,15 @@
<h3 class="my-0 fw-bold">Formulir Tambah Pengguna</h3> <h3 class="my-0 fw-bold">Formulir Tambah Pengguna</h3>
</div> </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="row justify-content-center">
<div class="col-md-10"> <div class="col-md-10">
<div class="card border-0 shadow-sm"> <div class="card border-0 shadow-sm">
@ -16,6 +25,14 @@
<div class="mb-3"> <div class="mb-3">
<label for="role" class="form-label">Role <span class="text-danger">*</span></label> <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" 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" <select class="form-select @error('role') is-invalid @enderror" id="role" name="role"
required onchange="toggleFields()"> required onchange="toggleFields()">
<option value="" selected disabled>Pilih role terlebih dahulu...</option> <option value="" selected disabled>Pilih role terlebih dahulu...</option>
@ -27,22 +44,27 @@
@error('role') @error('role')
<div class="invalid-feedback">{{ $message }}</div> <div class="invalid-feedback">{{ $message }}</div>
@enderror @enderror
@endif
</div> </div>
{{-- Bagian Form Dinamis --}} {{-- Bagian Form Dinamis --}}
<div id="dynamic-form" class="d-none"> <div id="dynamic-form" class="{{ $prefilledData ? '' : 'd-none' }}">
<div class="mb-3"> <div class="mb-3">
<label for="nama_lengkap" class="form-label">Nama Lengkap</label> <label for="nama_lengkap" class="form-label">Nama Lengkap</label>
<input type="text" class="form-control @error('nama_lengkap') is-invalid @enderror" <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 ?? '') }}" required
{{ $prefilledData ? 'readonly' : '' }}>
</div> </div>
<div class="mb-3"> <div class="mb-3">
<label for="nomor_induk" class="form-label" id="label_nomor_induk">NISN / NIP</label> <label for="nomor_induk" class="form-label" id="label_nomor_induk">NISN / NIP</label>
@if($prefilledData)
<input type="hidden" name="nomor_induk" value="{{ $prefilledData->nomor_induk }}">
@endif
<input type="number" class="form-control @error('nomor_induk') is-invalid @enderror" <input type="number" class="form-control @error('nomor_induk') is-invalid @enderror"
id="nomor_induk" name="nomor_induk" value="{{ old('nomor_induk') }}" id="nomor_induk" name="{{ $prefilledData ? '' : 'nomor_induk' }}" value="{{ old('nomor_induk', $prefilledData->nomor_induk ?? '') }}"
placeholder="Masukkan Nomor Induk"> placeholder="Masukkan Nomor Induk" {{ $prefilledData ? 'readonly' : '' }}>
@error('nomor_induk') @error('nomor_induk')
<div class="invalid-feedback">{{ $message }}</div> <div class="invalid-feedback">{{ $message }}</div>
@enderror @enderror
@ -64,7 +86,7 @@
</div> </div>
</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"> <div class="col-md-6 mb-3">
<label for="kelas" class="form-label">Kelas</label> <label for="kelas" class="form-label">Kelas</label>
<input type="text" class="form-control" id="kelas" name="kelas" <input type="text" class="form-control" id="kelas" name="kelas"
@ -122,7 +144,10 @@ class="form-control @error('password') is-invalid @enderror" id="password"
@push('scripts') @push('scripts')
<script> <script>
function toggleFields() { 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 dynamicForm = document.getElementById('dynamic-form');
const labelInduk = document.getElementById('label_nomor_induk'); const labelInduk = document.getElementById('label_nomor_induk');
const inputInduk = document.getElementById('nomor_induk'); const inputInduk = document.getElementById('nomor_induk');

View File

@ -6,9 +6,100 @@
<h1 class="h3 text-gray-800">{{ $pageTitle ?? 'Manajemen Pengguna' }}</h1> <h1 class="h3 text-gray-800">{{ $pageTitle ?? 'Manajemen Pengguna' }}</h1>
</div> </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 shadow mb-5">
<div class="card-header py-3 d-flex justify-content-between align-items-center flex-wrap gap-2"> <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"> <div class="d-flex align-items-center">
<form action="{{ route('admin.pengguna.index') }}" method="GET" class="me-2 mb-0"> <form action="{{ route('admin.pengguna.index') }}" method="GET" class="me-2 mb-0">
@ -22,7 +113,7 @@
</form> </form>
<a href="{{ route('admin.pengguna.create') }}" class="btn btn-sm btn-primary"> <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> </a>
</div> </div>
</div> </div>
@ -48,7 +139,7 @@
<td> <td>
<div>{{ $user->email }}</div> <div>{{ $user->email }}</div>
<div class="small text-muted"><i class="bi bi-telephone me-1"></i>{{ $user->phone ?? <div class="small text-muted"><i class="bi bi-telephone me-1"></i>{{ $user->phone ??
'-' }}</div> '-' }}</div>
</td> </td>
<td> <td>
@if($user->role == 'guru') @if($user->role == 'guru')
@ -106,86 +197,6 @@ class="form-delete-user" data-nama="{{ $user->name }}">
</div> </div>
</div> </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> </div>
{{-- MODAL TAMBAH DATA INDUK --}} {{-- MODAL TAMBAH DATA INDUK --}}

View File

@ -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>&copy; {{ date('Y') }} {{ config('app.name') }}. Semua Hak Dilindungi.</p>
</div>
</div>
</body>
</html>

View File

@ -37,13 +37,13 @@
@if(in_array($user->role, ['guru', 'penjaga perpus'])) @if(in_array($user->role, ['guru', 'penjaga perpus']))
<div class="col-md-6"> <div class="col-md-6">
<label class="form-label text-muted small text-uppercase fw-bold">NIP / NIK</label> <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> </div>
{{-- KONDISI UNTUK SISWA --}} {{-- KONDISI UNTUK SISWA --}}
@else @else
<div class="col-md-6"> <div class="col-md-6">
<label class="form-label text-muted small text-uppercase fw-bold">NISN</label> <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> </div>
@endif @endif
@ -58,8 +58,8 @@
<label class="form-label text-muted small text-uppercase fw-bold">Nomor Handphone <label class="form-label text-muted small text-uppercase fw-bold">Nomor Handphone
(WA)</label> (WA)</label>
<p class="fw-semibold text-dark"> <p class="fw-semibold text-dark">
@if($user->no_hp) @if($user->phone)
<span>{{ $user->no_hp <span>{{ $user->phone
}}</span> }}</span>
@else @else
<span class="text-muted fst-italic">- Belum diatur -</span> <span class="text-muted fst-italic">- Belum diatur -</span>

View File

@ -119,7 +119,7 @@ class="btn btn-outline-primary rounded-pill w-100 w-sm-auto">
</div> </div>
<div class="col-sm-6 col-md-4"> <div class="col-sm-6 col-md-4">
<small class="text-muted d-block mb-1">Nomor HP</small> <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> </div>
</div> </div>

View File

@ -7,7 +7,11 @@
<li class="list-group-item px-0"> <li class="list-group-item px-0">
<h6 class="fw-semibold mb-1">{{ $buku['judul'] }}</h6> <h6 class="fw-semibold mb-1">{{ $buku['judul'] }}</h6>
<small class="text-muted">{{ $buku['penulis'] }}</small> <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> </li>
@empty @empty
<li class="list-group-item px-0 text-center text-muted small py-3">Tidak ada buku yang sedang dipinjam.</li> <li class="list-group-item px-0 text-center text-muted small py-3">Tidak ada buku yang sedang dipinjam.</li>

View File

@ -87,8 +87,8 @@ class="form-control @error('email') is-invalid @enderror" value="{{ old('email',
@else @else
{{-- Untuk Guru atau Penjaga Perpus --}} {{-- Untuk Guru atau Penjaga Perpus --}}
<div class="mb-3"> <div class="mb-3">
<label for="nuptk" class="form-label">NIP / NUPTK</label> <label for="nomor_induk" class="form-label">NIP / NUPTK</label>
<input id="nuptk" type="text" class="form-control" value="{{ $user->nuptk }}" readonly disabled> <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> <small class="text-muted">ID Kepegawaian tidak dapat diubah.</small>
</div> </div>
@endif @endif