363 lines
18 KiB
PHP
363 lines
18 KiB
PHP
{{-- resources/views/admin/kepulangan/over-limit.blade.php --}}
|
|
|
|
@extends('layouts.app')
|
|
|
|
@section('title', 'Santri Over Limit Kuota')
|
|
|
|
@section('content')
|
|
<div class="page-header">
|
|
<h2><i class="fas fa-exclamation-triangle"></i> Santri Over Limit Kuota</h2>
|
|
</div>
|
|
|
|
{{-- Info Periode --}}
|
|
<div style="background: linear-gradient(135deg, #ff5252 0%, #f48fb1 100%); color: white; padding: 14px; border-radius: 12px; margin-bottom: 14px;">
|
|
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 20px; align-items: center;">
|
|
<div>
|
|
<h4 style="margin: 0 0 5px 0; opacity: 0.9;">⚠︠Total Santri Over Limit</h4>
|
|
<p style="margin: 0; font-size: 2rem; font-weight: 700;">{{ $santriList->count() }}</p>
|
|
</div>
|
|
<div>
|
|
<h4 style="margin: 0 0 5px 0; opacity: 0.9;">📅 Periode Kuota</h4>
|
|
<p style="margin: 0; font-size: 1.1rem; font-weight: 600;">
|
|
{{ $settings->periode_mulai->format('d M Y') }} - {{ $settings->periode_akhir->format('d M Y') }}
|
|
</p>
|
|
</div>
|
|
<div>
|
|
<h4 style="margin: 0 0 5px 0; opacity: 0.9;">📊Kuota Maksimal</h4>
|
|
<p style="margin: 0; font-size: 1.1rem; font-weight: 600;">{{ $settings->kuota_maksimal }} Hari / Tahun</p>
|
|
</div>
|
|
<div style="text-align: right;">
|
|
<a href="{{ route('admin.kepulangan.index') }}" class="btn btn-light" style="background: white; color: #dc3545; font-weight: 600;">
|
|
<i class="fas fa-arrow-left"></i> Kembali
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{{-- Alert Info --}}
|
|
<div style="background: #fff3cd; padding: 15px; border-radius: 8px; margin-bottom: 14px; border-left: 4px solid #ffc107;">
|
|
<strong>ℹ︠Informasi:</strong>
|
|
<p style="margin: 10px 0 0 0;">
|
|
Berikut adalah daftar santri yang telah melebihi kuota maksimal <strong>{{ $settings->kuota_maksimal }} hari</strong> dalam periode ini.
|
|
Santri tetap bisa mengajukan izin, namun akan mendapat peringatan visual.
|
|
</p>
|
|
</div>
|
|
|
|
<div class="content-box">
|
|
@if($santriList->count() > 0)
|
|
<div style="overflow-x: auto;">
|
|
<div class="table-wrapper">
|
|
<table class="data-table">
|
|
<thead>
|
|
<tr>
|
|
<th>No</th>
|
|
<th>ID Santri</th>
|
|
<th>Nama Santri</th>
|
|
<th>Kelas</th>
|
|
<th>Total Hari Terpakai</th>
|
|
<th>Kuota Maksimal</th>
|
|
<th>Kelebihan</th>
|
|
<th>Persentase</th>
|
|
<th>Jumlah Izin</th>
|
|
<th>Aksi</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
@foreach($santriList as $index => $santri)
|
|
@php
|
|
$kelebihan = $santri->total_hari_izin - $settings->kuota_maksimal;
|
|
@endphp
|
|
<tr style="background-color: rgba(220, 53, 69, 0.05);">
|
|
<td>{{ $index + 1 }}</td>
|
|
<td><strong>{{ $santri->id_santri }}</strong></td>
|
|
<td>
|
|
<div>
|
|
<strong>{{ $santri->nama_lengkap }}</strong><br>
|
|
<small style="color: #7F8C8D;">NIS: {{ $santri->nis ?? '-' }}</small>
|
|
</div>
|
|
</td>
|
|
<td>{{ $santri->kelas }}</td>
|
|
<td>
|
|
<span style="display: inline-block; background: #dc3545; color: white; padding: 6px 12px; border-radius: 6px; font-size: 1rem; font-weight: 700;">
|
|
{{ $santri->total_hari_izin }} hari
|
|
</span>
|
|
</td>
|
|
<td>
|
|
<span style="color: #7F8C8D;">{{ $settings->kuota_maksimal }} hari</span>
|
|
</td>
|
|
<td>
|
|
<span style="display: inline-block; background: #721c24; color: white; padding: 4px 10px; border-radius: 4px; font-size: 0.9rem; font-weight: 600;">
|
|
+{{ $kelebihan }} hari
|
|
</span>
|
|
</td>
|
|
<td>
|
|
<div style="display: flex; align-items: center; gap: 10px;">
|
|
<div style="flex: 1; height: 20px; background: #ffebee; border-radius: 10px; overflow: hidden; position: relative;">
|
|
<div style="height: 100%; width: {{ min(200, $santri->kuota_info['persentase']) }}%; background: linear-gradient(90deg, #dc3545, #c62828); transition: width 0.3s ease; display: flex; align-items: center; justify-content: center;">
|
|
<span style="font-size: 0.75rem; font-weight: 600; color: white; z-index: 1;">
|
|
{{ $santri->kuota_info['persentase'] }}%
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</td>
|
|
<td>
|
|
<span style="display: inline-block; background: #6c757d; color: white; padding: 4px 8px; border-radius: 4px; font-size: 0.85rem;">
|
|
{{ $santri->kepulangan->count() }} kali izin
|
|
</span>
|
|
</td>
|
|
<td>
|
|
<div style="display: flex; gap: 5px; flex-wrap: wrap;">
|
|
<button type="button"
|
|
class="btn btn-sm btn-primary"
|
|
onclick="showDetailSantri('{{ $santri->id_santri }}')"
|
|
title="Detail">
|
|
<i class="fas fa-eye"></i>
|
|
</button>
|
|
<button type="button"
|
|
class="btn btn-sm btn-warning"
|
|
onclick="resetKuotaSantri('{{ $santri->id_santri }}', '{{ $santri->nama_lengkap }}')"
|
|
title="Reset Kuota">
|
|
<i class="fas fa-sync-alt"></i>
|
|
</button>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
@endforeach
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
{{-- Summary Statistics --}}
|
|
<div style="margin-top: 22px; padding: 14px; background: #f8f9fa; border-radius: 8px;">
|
|
<h4 style="margin: 0 0 15px 0; color: #2C3E50;">📊Ringkasan Statistik</h4>
|
|
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 20px;">
|
|
<div style="text-align: center; padding: 15px; background: white; border-radius: 8px; border: 2px solid #dc3545;">
|
|
<div style="font-size: 0.85rem; color: #7F8C8D; margin-bottom: 5px;">Total Santri Over Limit</div>
|
|
<div style="font-size: 2rem; font-weight: 700; color: #dc3545;">{{ $santriList->count() }}</div>
|
|
</div>
|
|
<div style="text-align: center; padding: 15px; background: white; border-radius: 8px; border: 2px solid #ff9800;">
|
|
<div style="font-size: 0.85rem; color: #7F8C8D; margin-bottom: 5px;">Rata-rata Kelebihan</div>
|
|
<div style="font-size: 2rem; font-weight: 700; color: #ff9800;">
|
|
{{ round($santriList->avg(function($s) use ($settings) { return $s->total_hari_izin - $settings->kuota_maksimal; }), 1) }} hari
|
|
</div>
|
|
</div>
|
|
<div style="text-align: center; padding: 15px; background: white; border-radius: 8px; border: 2px solid #f44336;">
|
|
<div style="font-size: 0.85rem; color: #7F8C8D; margin-bottom: 5px;">Tertinggi</div>
|
|
<div style="font-size: 2rem; font-weight: 700; color: #f44336;">
|
|
{{ $santriList->max('total_hari_izin') }} hari
|
|
</div>
|
|
</div>
|
|
<div style="text-align: center; padding: 15px; background: white; border-radius: 8px; border: 2px solid #9c27b0;">
|
|
<div style="font-size: 0.85rem; color: #7F8C8D; margin-bottom: 5px;">Total Kelebihan</div>
|
|
<div style="font-size: 2rem; font-weight: 700; color: #9c27b0;">
|
|
{{ $santriList->sum(function($s) use ($settings) { return $s->total_hari_izin - $settings->kuota_maksimal; }) }} hari
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{{-- Action Buttons --}}
|
|
<div style="margin-top: 14px; display: flex; gap: 10px; flex-wrap: wrap;">
|
|
<button type="button" class="btn btn-danger" onclick="showResetSemuaOverLimit()">
|
|
<i class="fas fa-sync-alt"></i> Reset Semua Santri Over Limit
|
|
</button>
|
|
<a href="{{ route('admin.kepulangan.settings') }}" class="btn btn-primary">
|
|
<i class="fas fa-cog"></i> Pengaturan Kuota
|
|
</a>
|
|
</div>
|
|
@else
|
|
<div style="text-align: center; padding: 44px; color: #28a745;">
|
|
<i class="fas fa-check-circle" style="font-size: 4rem; margin-bottom: 14px; display: block;"></i>
|
|
<h3 style="margin: 0 0 10px 0;">Tidak Ada Santri Over Limit!</h3>
|
|
<p style="color: #7F8C8D;">Semua santri masih dalam batas kuota yang ditentukan.</p>
|
|
<a href="{{ route('admin.kepulangan.index') }}" class="btn btn-primary" style="margin-top: 14px;">
|
|
<i class="fas fa-arrow-left"></i> Kembali ke Kepulangan
|
|
</a>
|
|
</div>
|
|
@endif
|
|
</div>
|
|
|
|
{{-- Modal Detail Santri --}}
|
|
<div class="modal fade" id="detailSantriModal" tabindex="-1" style="display: none;">
|
|
<div class="modal-dialog modal-lg">
|
|
<div class="modal-content" style="background: white; border-radius: 12px; padding: 14px;">
|
|
<div style="margin-bottom: 14px;">
|
|
<h3 style="margin: 0; color: #2C3E50;">Detail Riwayat Izin Santri</h3>
|
|
</div>
|
|
<div id="detailSantriContent">
|
|
<div style="text-align: center; padding: 22px;">
|
|
<i class="fas fa-spinner fa-spin" style="font-size: 2rem;"></i>
|
|
<p style="margin-top: 10px;">Memuat data...</p>
|
|
</div>
|
|
</div>
|
|
<div style="display: flex; gap: 10px; justify-content: flex-end; margin-top: 14px;">
|
|
<button type="button" class="btn btn-secondary" onclick="closeModal('detailSantriModal')">Tutup</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{{-- Modal Reset Kuota --}}
|
|
<div class="modal fade" id="resetKuotaModal" tabindex="-1" style="display: none;">
|
|
<div class="modal-dialog">
|
|
<div class="modal-content" style="background: white; border-radius: 12px; padding: 14px;">
|
|
<form id="resetKuotaForm">
|
|
@csrf
|
|
<div style="margin-bottom: 14px;">
|
|
<h3 style="margin: 0; color: #2C3E50;">Reset Kuota Santri</h3>
|
|
</div>
|
|
<div style="padding: 15px; background: #fff3cd; border: 1px solid #ffeaa7; border-radius: 6px; margin-bottom: 15px;">
|
|
<p style="margin: 0; color: #856404;">
|
|
<i class="fas fa-exclamation-triangle"></i>
|
|
Anda akan mereset kuota untuk santri: <strong id="resetSantriName"></strong>
|
|
</p>
|
|
</div>
|
|
<div class="form-group">
|
|
<label>Catatan Reset (Opsional):</label>
|
|
<textarea name="catatan" class="form-control" rows="2" placeholder="Alasan reset kuota..."></textarea>
|
|
</div>
|
|
<div style="display: flex; gap: 10px; justify-content: flex-end; margin-top: 14px;">
|
|
<button type="button" class="btn btn-secondary" onclick="closeModal('resetKuotaModal')">Batal</button>
|
|
<button type="submit" class="btn btn-warning"><i class="fas fa-sync-alt"></i> Reset Kuota</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<style>
|
|
.modal.fade { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.5); z-index: 1000; align-items: center; justify-content: center; }
|
|
.modal-dialog { max-width: 500px; width: 90%; margin: auto; }
|
|
.modal-dialog.modal-lg { max-width: 800px; }
|
|
.modal-content { max-height: 90vh; overflow-y: auto; }
|
|
</style>
|
|
|
|
<script>
|
|
let currentResetSantriId = null;
|
|
|
|
function showDetailSantri(idSantri) {
|
|
document.getElementById('detailSantriModal').style.display = 'flex';
|
|
|
|
// Load detail via AJAX
|
|
fetch(`/admin/api/kepulangan/santri/${idSantri}`)
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if (data.success) {
|
|
const santri = data.santri;
|
|
const kuota = data.penggunaan_izin;
|
|
|
|
document.getElementById('detailSantriContent').innerHTML = `
|
|
<div style="background: #f8f9fa; padding: 14px; border-radius: 8px; margin-bottom: 14px;">
|
|
<h4 style="margin: 0 0 15px 0;">${santri.nama_lengkap}</h4>
|
|
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 11px;">
|
|
<div>
|
|
<div style="font-size: 0.85rem; color: #7F8C8D;">ID Santri</div>
|
|
<div style="font-weight: 600;">${santri.id_santri}</div>
|
|
</div>
|
|
<div>
|
|
<div style="font-size: 0.85rem; color: #7F8C8D;">Kelas</div>
|
|
<div style="font-weight: 600;">${santri.kelas}</div>
|
|
</div>
|
|
<div>
|
|
<div style="font-size: 0.85rem; color: #7F8C8D;">Total Terpakai</div>
|
|
<div style="font-weight: 600; color: #dc3545;">${kuota.total_terpakai} hari</div>
|
|
</div>
|
|
<div>
|
|
<div style="font-size: 0.85rem; color: #7F8C8D;">Persentase</div>
|
|
<div style="font-weight: 600; color: #dc3545;">${kuota.persentase}%</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div style="padding: 15px; background: #ffebee; border-radius: 8px;">
|
|
<p style="margin: 0; color: #c62828;">
|
|
<i class="fas fa-exclamation-triangle"></i>
|
|
<strong>Over Limit!</strong> Santri ini telah melebihi kuota maksimal ${kuota.kuota_maksimal} hari.
|
|
</p>
|
|
</div>
|
|
`;
|
|
} else {
|
|
document.getElementById('detailSantriContent').innerHTML = `
|
|
<div class="alert alert-danger">${data.message}</div>
|
|
`;
|
|
}
|
|
})
|
|
.catch(error => {
|
|
document.getElementById('detailSantriContent').innerHTML = `
|
|
<div class="alert alert-danger">Error: ${error.message}</div>
|
|
`;
|
|
});
|
|
}
|
|
|
|
function resetKuotaSantri(idSantri, namaSantri) {
|
|
currentResetSantriId = idSantri;
|
|
document.getElementById('resetSantriName').textContent = namaSantri;
|
|
document.getElementById('resetKuotaModal').style.display = 'flex';
|
|
}
|
|
|
|
document.getElementById('resetKuotaForm').addEventListener('submit', function(e) {
|
|
e.preventDefault();
|
|
const formData = new FormData(this);
|
|
const submitBtn = this.querySelector('button[type="submit"]');
|
|
const originalText = submitBtn.innerHTML;
|
|
|
|
submitBtn.disabled = true;
|
|
submitBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Mereset...';
|
|
|
|
fetch(`/admin/kepulangan/reset/santri/${currentResetSantriId}`, {
|
|
method: 'POST',
|
|
body: formData,
|
|
headers: { 'X-CSRF-TOKEN': '{{ csrf_token() }}' }
|
|
})
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if (data.success) {
|
|
closeModal('resetKuotaModal');
|
|
showAlert('success', data.message);
|
|
setTimeout(() => window.location.reload(), 1500);
|
|
} else {
|
|
showAlert('danger', data.message);
|
|
}
|
|
})
|
|
.catch(error => showAlert('danger', 'Error: ' + error.message))
|
|
.finally(() => {
|
|
submitBtn.disabled = false;
|
|
submitBtn.innerHTML = originalText;
|
|
});
|
|
});
|
|
|
|
function showResetSemuaOverLimit() {
|
|
if (confirm('Apakah Anda yakin ingin mereset kuota SEMUA santri yang over limit ({{ $santriList->count() }} santri)?')) {
|
|
window.location.href = '{{ route("admin.kepulangan.settings") }}#reset-section';
|
|
}
|
|
}
|
|
|
|
function closeModal(modalId) {
|
|
document.getElementById(modalId).style.display = 'none';
|
|
}
|
|
|
|
function showAlert(type, message) {
|
|
const alertDiv = document.createElement('div');
|
|
alertDiv.className = `alert alert-${type}`;
|
|
alertDiv.innerHTML = `<i class="fas fa-${type === 'success' ? 'check' : 'exclamation'}-circle"></i> ${message}`;
|
|
const pageHeader = document.querySelector('.page-header');
|
|
pageHeader.insertAdjacentElement('afterend', alertDiv);
|
|
setTimeout(() => alertDiv.remove(), 5000);
|
|
}
|
|
|
|
document.addEventListener('keydown', function(e) {
|
|
if (e.key === 'Escape') {
|
|
document.querySelectorAll('.modal.fade').forEach(modal => modal.style.display = 'none');
|
|
}
|
|
});
|
|
|
|
document.querySelectorAll('.modal.fade').forEach(modal => {
|
|
modal.addEventListener('click', function(e) {
|
|
if (e.target === this) {
|
|
closeModal(this.id);
|
|
}
|
|
});
|
|
});
|
|
</script>
|
|
@endsection |