MIF_E31222658/resources/views/admin/ranking/index.blade.php

743 lines
33 KiB
PHP

@extends('layouts.app')
@section('title', 'Ranking Hasil SAW')
@section('content')
<div class="container-fluid">
<!-- Header Cards -->
<div class="row mb-3">
<div class="col-md-4 mb-3">
<div class="card bg-primary text-white h-100">
<div class="card-body py-3">
<div class="d-flex justify-content-between align-items-center">
<div>
<h6 class="card-title mb-1">Total Mahasiswa</h6>
<h4 class="mb-0">{{ $countMahasiswa }}</h4>
</div>
<i class="fas fa-users fa-2x opacity-50"></i>
</div>
</div>
</div>
</div>
<div class="col-md-4 mb-3">
<div class="card bg-success text-white h-100">
<div class="card-body py-3">
<div class="d-flex justify-content-between align-items-center">
<div>
<h6 class="card-title mb-1">Total Penyesuaian UKT</h6>
<h4 class="mb-0" id="total-penyesuaian">Rp&nbsp;{{ number_format($totalUktPenyesuaian, 0, ',', '.') }}</h4>
</div>
<i class="fas fa-money-bill-wave fa-2x opacity-50"></i>
</div>
</div>
</div>
</div>
<!-- Add this card to your header cards section -->
<div class="col-md-4 mb-3">
<div class="card bg-warning text-white h-100">
<div class="card-body py-3">
<div class="d-flex justify-content-between align-items-center">
<div>
<h6 class="card-title mb-1">Budget Tersedia</h6>
<h4 class="mb-0">Rp&nbsp;{{ number_format($budget, 0, ',', '.') }}</h4>
@if($selectedForm = $forms->where('status', 'Dibuka')->first())
<small class="opacity-75">{{ $selectedForm->nama_form }}</small>
@endif
</div>
<i class="fas fa-wallet fa-2x opacity-50"></i>
</div>
</div>
</div>
</div>
<div class="col-md-4 mb-3">
<div class="card bg-{{ $selisihUkt >= 0 ? 'info' : 'danger' }} text-white h-100">
<div class="card-body py-3">
<div class="d-flex justify-content-between align-items-center">
<div>
<h6 class="card-title mb-1">Selisih UKT</h6>
<h4 class="mb-0">Rp&nbsp;{{ number_format(abs($selisihUkt), 0, ',', '.') }}</h4>
</div>
<i class="fas fa-exchange-alt fa-2x opacity-50"></i>
</div>
</div>
</div>
</div>
</div>
<!-- Action Buttons -->
<div class="row mb-3">
<div class="col-12">
<div class="card shadow-sm">
<div class="card-body py-2">
<div class="d-flex flex-wrap justify-content-between align-items-center gap-2">
<div class="d-flex flex-wrap gap-2">
<form action="{{ route('admin.ranking.recalculate') }}" method="POST">
@csrf
<button type="submit" class="btn btn-warning">
<i class="fas fa-sync-alt me-1"></i> Hitung Ulang
</button>
</form>
<form id="exportPdfForm" action="{{ route('admin.ranking.export') }}" method="GET">
<input type="hidden" name="status_form_id" value="{{ request('status_form_id', $forms->first()->id ?? '') }}">
<button type="submit" class="btn btn-primary" {{ !request('status_form_id') && !($forms->first()->id ?? false) ? 'disabled' : '' }}>
<i class="fas fa-file-pdf me-1"></i> Export PDF
</button>
</form>
</div>
<button class="btn btn-info" data-bs-toggle="modal" data-bs-target="#activeBudgetModal">
<i class="fas fa-wallet me-1"></i>&nbsp;Input Budget
</button>
</div>
</div>
</div>
</div>
</div>
<!-- Filter Section -->
<div class="card shadow-sm mb-4">
<div class="card-header bg-primary text-white py-3">
<h5 class="mb-0 d-flex align-items-center">
<i class="fas fa-filter me-2"></i>&nbsp;Filter Data
</h5>
</div>
<div class="card-body">
<form>
<div class="container-fluid px-0">
<div class="row gy-3 gx-4">
<!-- Jenis Form -->
<div class="col-12 col-md-4">
<label for="jenis_form" class="form-label fw-semibold text-dark">Jenis Form</label>
<select name="jenis_form" id="jenis_form" class="form-select w-100">
<option value="">Semua Jenis</option>
<option value="penurunan" {{ request('jenis_form') == 'penurunan' ? 'selected' : '' }}>Penurunan UKT</option>
<option value="pengangsuran" {{ request('jenis_form') == 'pengangsuran' ? 'selected' : '' }}>Pengangsuran UKT</option>
</select>
</div>
<!-- Nama Form -->
<div class="col-12 col-md-4">
<label for="status_form_id" class="form-label fw-semibold text-dark">Nama Form</label>
<select name="status_form_id" id="status_form_id" class="form-select w-100">
<option value="">Semua Form</option>
@foreach($forms as $form)
<option value="{{ $form->id }}" {{ request('status_form_id') == $form->id ? 'selected' : '' }}>
{{ $form->nama_form }} ({{ $form->semester }} {{ $form->tahun }})
</option>
@endforeach
</select>
</div>
<!-- Status Kuota -->
<div class="col-12 col-md-4">
<label for="kuota_filter" class="form-label fw-semibold text-dark">Status Kuota</label>
<select name="kuota_filter" id="kuota_filter" class="form-select w-100" {{ !request('status_form_id') ? 'disabled' : '' }}>
<option value="">Semua Status</option>
<option value="available" {{ request('kuota_filter') == 'available' ? 'selected' : '' }}>Dalam Kuota</option>
<option value="exceeded" {{ request('kuota_filter') == 'exceeded' ? 'selected' : '' }}>Melebihi Kuota</option>
</select>
</div>
</div>
</div>
</form>
</div>
</div>
<!-- Results Table -->
<div class="card shadow-sm mb-3">
<div class="card-header bg-primary text-white py-2">
<div class="d-flex justify-content-between align-items-center">
<h5 class="mb-0"></i>Daftar Ranking</h5>
<div>
@if(request('kuota_filter') == 'available')
<span class="badge bg-success me-2">Menampilkan: Dalam Kuota</span>
@elseif(request('kuota_filter') == 'exceeded')
<span class="badge bg-secondary me-2">Menampilkan: Melebihi Kuota</span>
@endif
<span class="badge bg-white text-primary me-2">Total: {{ $rankings->total() }}</span>
<span class="badge bg-white text-primary">Halaman: {{ $rankings->currentPage() }}/{{ $rankings->lastPage() }}</span>
</div>
</div>
</div>
<div class="card-body p-0">
<div class="table-responsive">
<table class="table table-hover mb-0">
<thead class="table-light">
<tr>
<th width="50" class="text-center">Rank</th>
<th>Mahasiswa</th>
<th>Formulir</th>
<th class="text-end">UKT Saat Ini</th>
<th class="text-end">UKT Penyesuaian</th>
<th>Status</th>
<th class="text-end">Nilai</th>
<th width="100">Aksi</th>
</tr>
</thead>
<tbody>
@forelse($rankings as $ranking)
@php
$cannotAdjust = in_array(strtolower($ranking->rekomendasi_ukt), ['ukt tetap', 'tidak dapat mengangsur']);
$form = $ranking->pengajuan->form ?? null;
$isBeyondQuota = $ranking->is_beyond_quota ?? false;
$currentRank = $ranking->current_rank ?? ($rankings->firstItem() + $loop->index);
// Determine CSS classes based on status
$rowClass = '';
$textClass = '';
$badgeClass = 'success';
$quotaStatus = '';
if ($cannotAdjust) {
// Red for "UKT Tetap" or "tidak dapat mengangsur"
$rowClass = 'table-danger';
$badgeClass = 'danger';
$quotaStatus = 'Tidak Diterima';
} elseif ($isBeyondQuota) {
// Gray for beyond quota
$rowClass = 'table-secondary';
$textClass = 'text-dark';
$badgeClass = 'secondary';
$quotaStatus = 'Kuota Penuh';
} else {
// Green for within quota and adjustable
$quotaStatus = 'Dalam Kuota';
}
@endphp
<tr class="{{ $rowClass }}">
<td class="text-center fw-bold align-middle {{ $textClass }}">
{{ $currentRank }}
</td>
<td class="align-middle">
<div class="fw-bold {{ $textClass }}">{{ $ranking->pengajuan?->mahasiswa?->user?->name ?? '-' }}</div>
<small class="text-dark">{{ $ranking->pengajuan?->mahasiswa?->nim ?? '-' }}</small>
</td>
<td class="align-middle">
@if($form)
<span class="badge bg-{{ $form->jenis_form == 'penurunan' ? 'primary text-white' : 'warning text-dark fw-bold' }}">
{{ $form->nama_form }}
</span>
<div class="small text-dark">
{{ $form->semester }} {{ $form->tahun }}
@if($form->kuota > 0)
<br><small>(Kuota: {{ $currentRank }}/{{ $form->kuota }})</small>
@endif
</div>
@else
-
@endif
</td>
<td class="text-end align-middle {{ $textClass }}">
Rp{{ number_format($ranking->pengajuan?->ukt_saat_ini ?? 0, 0, ',', '.') }}
</td>
<td class="text-end align-middle">
<div class="d-flex justify-content-end align-items-center gap-2">
@if($ranking->ukt_penyesuaian)
<span class="fw-bold {{ $textClass }}">
Rp{{ number_format($ranking->ukt_penyesuaian, 0, ',', '.') }}
</span>
@if(!$cannotAdjust && !$isBeyondQuota)
<button class="btn btn-sm btn-link p-0 text-primary"
data-bs-toggle="modal"
data-bs-target="#editUktModal"
data-id="{{ $ranking->id }}"
data-ukt="{{ $ranking->ukt_penyesuaian }}"
title="Edit UKT">
<i class="fas fa-edit"></i>
</button>
@endif
@else
@if(!$cannotAdjust && !$isBeyondQuota)
<span class="text-dark">Belum diatur</span>
<button class="btn btn-sm btn-link p-0 text-success"
data-bs-toggle="modal"
data-bs-target="#editUktModal"
data-id="{{ $ranking->id }}"
title="Tambah UKT">
<i class="fas fa-plus-circle"></i>
</button>
@else
<span class="text-dark">-</span>
@endif
@endif
</div>
</td>
<td class="align-middle">
@php
$statusText = $ranking->rekomendasi_ukt;
$isRejected = in_array(strtolower($statusText), ['ukt tetap', 'tidak dapat mengangsur']);
$badgeClass = $isRejected ? 'danger' : ($isBeyondQuota ? 'secondary' : 'success');
$quotaStatus = $isRejected ? 'Tidak Diterima' : ($isBeyondQuota ? 'Kuota Penuh' : 'Dalam Kuota');
@endphp
<span class="badge bg-{{ $badgeClass }} text-white fw-bold">
{{ $statusText }}
@if($quotaStatus) ({{ $quotaStatus }}) @endif
</span>
</td>
<td class="text-end align-middle {{ $textClass }}">
{{ number_format($ranking->nilai_preferensi, 5) }}
</td>
<td class="align-middle">
<div class="d-flex justify-content-center gap-1">
<a href="{{ route('admin.pengajuan.detail', $ranking->pengajuan_id) }}"
class="btn btn-sm btn-primary"
title="Detail">
<i class="fas fa-eye"></i>
</a>
@if(!$cannotAdjust && !$isBeyondQuota)
<button class="btn btn-sm btn-success"
data-bs-toggle="modal"
data-bs-target="#editUktModal"
data-id="{{ $ranking->id }}"
data-ukt="{{ $ranking->ukt_penyesuaian ?? '' }}"
title="Atur UKT">
<i class="fas fa-edit"></i>
</button>
@endif
</div>
</td>
</tr>
@empty
<tr>
<td colspan="8" class="text-center py-4">
<div class="text-muted">
<i class="fas fa-exclamation-circle fa-2x mb-3"></i>
<h5>Tidak ada data</h5>
<p>Gunakan filter yang berbeda</p>
</div>
</td>
</tr>
@endforelse
</tbody>
</table>
</div>
@if($rankings->hasPages())
<div class="card-footer d-flex justify-content-between align-items-center py-2">
<div class="text-muted small">
Menampilkan {{ $rankings->firstItem() }} sampai {{ $rankings->lastItem() }} dari {{ $rankings->total() }}
</div>
<div>
{{ $rankings->onEachSide(1)->links() }}
</div>
</div>
@endif
</div>
</div>
<!-- UKT Adjustment Modal -->
<div class="modal fade" id="editUktModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header bg-primary text-white">
<h5 class="modal-title">
<i class="fas fa-edit me-2"></i>
Input UKT Penyesuaian
</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<form id="formUktPenyesuaian" method="POST">
@csrf
<div class="modal-body">
<div class="mb-3">
<label class="form-label fw-bold">Nominal UKT Penyesuaian</label>
<div class="input-group">
<span class="input-group-text">Rp</span>
<input type="text" name="ukt_penyesuaian" class="form-control ukt-input" required>
</div>
<small class="text-muted">Masukkan nominal penyesuaian UKT</small>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">
<i class="fas fa-times me-1"></i> Batal
</button>
<button type="submit" class="btn btn-primary">
<i class="fas fa-save me-1"></i> Simpan
</button>
</div>
</form>
</div>
</div>
</div>
<!-- Budget Modal for Specific Semester -->
<div class="modal fade" id="budgetModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header bg-info text-white">
<h5 class="modal-title">
<i class="fas fa-wallet me-2"></i>
Atur Budget Semester
</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<form action="{{ route('admin.ranking.set-budget') }}" method="POST">
@csrf
<div class="modal-body">
<input type="hidden" name="semester" id="modalSemester">
<input type="hidden" name="tahun" id="modalTahun">
<div class="mb-3">
<label class="form-label fw-bold">Semester</label>
<input type="text" class="form-control" id="displaySemester" readonly>
</div>
<div class="mb-3">
<label class="form-label fw-bold">Budget (Rp)</label>
<div class="input-group">
<span class="input-group-text">Rp</span>
<input type="text" name="budget" class="form-control budget-input" required>
</div>
<small class="text-muted">Masukkan budget untuk semester ini</small>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">
<i class="fas fa-times me-1"></i> Batal
</button>
<button type="submit" class="btn btn-info text-white">
<i class="fas fa-save me-1"></i> Simpan Budget
</button>
</div>
</form>
</div>
</div>
</div>
<!-- Active Budget Modal -->
<div class="modal fade" id="activeBudgetModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header bg-primary text-white">
<h5 class="modal-title">
<i class="fas fa-wallet me-2"></i>
Input Budget Form Aktif
</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<form action="{{ route('admin.ranking.set-active-budget') }}" method="POST">
@csrf
<div class="modal-body">
<div class="mb-3">
<label class="form-label fw-bold">Form Aktif</label>
<select name="status_form_id" class="form-select" required> <!-- Changed from form_id to status_form_id -->
<option value="">-- Pilih Form Aktif --</option>
@foreach($forms->where('status', 'Dibuka') as $form)
<option value="{{ $form->id }}" @if(request('status_form_id') == $form->id) selected @endif>
{{ $form->nama_form }} ({{ $form->semester }} {{ $form->tahun }})
</option>
@endforeach
</select>
</div>
<div class="mb-3">
<label class="form-label fw-bold">Budget (Rp)</label>
<div class="input-group">
<span class="input-group-text">Rp</span>
<input type="text" name="budget" class="form-control budget-input" required>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">
<i class="fas fa-times me-1"></i> Batal
</button>
<button type="submit" class="btn btn-primary">
<i class="fas fa-save me-1"></i> Simpan Budget
</button>
</div>
</form>
</div>
</div>
</div>
@endsection
<style>
.card {
border: none;
box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);
}
.card-header {
border-bottom: none;
}
.table {
font-size: 0.9rem;
}
.table th {
font-weight: 600;
background-color: #f8f9fa;
}
.table-secondary {
background-color: #f8f9fa;
}
.table-secondary td {
color: #6c757d;
}
.table-danger {
background-color: rgba(220, 53, 69, 0.05);
}
/* Stat card improvements */
.bg-primary-light {
background-color: rgba(13, 110, 253, 0.1);
}
.bg-success-light {
background-color: rgba(25, 135, 84, 0.1);
}
.bg-info-light {
background-color: rgba(13, 202, 240, 0.1);
}
.bg-danger-light {
background-color: rgba(220, 53, 69, 0.1);
}
.bg-warning-light {
background-color: rgba(255, 193, 7, 0.1);
}
/* Responsive adjustments */
@media (max-width: 768px) {
.card-header h5 {
font-size: 1rem;
}
.table th, .table td {
padding: 0.5rem;
font-size: 0.85rem;
}
.badge {
font-size: 0.75rem;
}
}
@media (max-width: 576px) {
.action-buttons .btn {
width: 100%;
margin-bottom: 0.5rem;
}
.table-responsive {
font-size: 0.8rem;
}
select.form-select {
font-size: 0.9rem;
}
.form-label {
font-size: 0.85rem;
}
}
</style>
@section('js')
<script>
document.addEventListener('DOMContentLoaded', function() {
// Auto-format currency inputs
const formatCurrency = (input) => {
let value = input.value.replace(/\D/g, '');
value = new Intl.NumberFormat('id-ID').format(value);
input.value = value;
};
// Initialize currency inputs
document.querySelectorAll('.ukt-input, .budget-input').forEach(input => {
// Format on input
input.addEventListener('input', function() {
formatCurrency(this);
});
// Format on focus out
input.addEventListener('blur', function() {
formatCurrency(this);
});
});
// Auto-filter functionality
const jenisForm = document.getElementById('jenis_form');
const statusForm = document.getElementById('status_form_id');
const kuotaFilter = document.getElementById('kuota_filter');
function applyFilters() {
const params = new URLSearchParams();
if (jenisForm.value) params.set('jenis_form', jenisForm.value);
if (statusForm.value) params.set('status_form_id', statusForm.value);
if (kuotaFilter.value && statusForm.value) params.set('kuota_filter', kuotaFilter.value);
// Reset pagination to first page when filtering
params.delete('page');
window.location.href = window.location.pathname + '?' + params.toString();
}
// Enable/disable quota filter based on form selection
statusForm.addEventListener('change', function() {
kuotaFilter.disabled = !this.value;
if (!this.value) {
kuotaFilter.value = '';
}
applyFilters();
});
// Apply filters when jenis form changes
jenisForm.addEventListener('change', applyFilters);
// Apply filters when kuota filter changes
kuotaFilter.addEventListener('change', function() {
if (statusForm.value) {
applyFilters();
}
});
// Initialize filters based on current URL params
function initializeFilters() {
const params = new URLSearchParams(window.location.search);
if (params.has('status_form_id')) {
statusForm.value = params.get('status_form_id');
kuotaFilter.disabled = false;
}
if (params.has('kuota_filter')) {
kuotaFilter.value = params.get('kuota_filter');
}
if (params.has('jenis_form')) {
jenisForm.value = params.get('jenis_form');
}
}
initializeFilters();
// UKT Adjustment Modal
const uktModal = document.getElementById('editUktModal');
if (uktModal) {
uktModal.addEventListener('show.bs.modal', function(event) {
const button = event.relatedTarget;
const id = button.getAttribute('data-id');
const ukt = button.getAttribute('data-ukt') || '';
const form = uktModal.querySelector('#formUktPenyesuaian');
form.action = `/admin/ranking/${id}/update-ukt`;
const input = form.querySelector('input[name="ukt_penyesuaian"]');
if (ukt) {
input.value = new Intl.NumberFormat('id-ID').format(ukt);
} else {
input.value = '';
}
});
// Handle form submission - remove formatting before submitting
document.getElementById('formUktPenyesuaian').addEventListener('submit', function(e) {
const input = this.querySelector('input[name="ukt_penyesuaian"]');
// Remove thousand separators before submitting
input.value = input.value.replace(/\./g, '');
});
}
// Budget Modal for specific semester
const budgetModal = document.getElementById('budgetModal');
if (budgetModal) {
budgetModal.addEventListener('show.bs.modal', function(event) {
const button = event.relatedTarget;
const semester = button.getAttribute('data-semester');
const tahun = button.getAttribute('data-tahun');
budgetModal.querySelector('#modalSemester').value = semester;
budgetModal.querySelector('#modalTahun').value = tahun;
budgetModal.querySelector('#displaySemester').value = `Semester ${semester} Tahun ${tahun}`;
});
// Handle budget form submission
budgetModal.querySelector('form').addEventListener('submit', function(e) {
const input = this.querySelector('input[name="budget"]');
input.value = input.value.replace(/\./g, '');
});
}
// Active Budget Modal
const activeBudgetModal = document.getElementById('activeBudgetModal');
if (activeBudgetModal) {
activeBudgetModal.querySelector('form').addEventListener('submit', function(e) {
const input = this.querySelector('input[name="budget"]');
input.value = input.value.replace(/\./g, '');
});
}
// Tooltips
const tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'));
tooltipTriggerList.map(function (tooltipTriggerEl) {
return new bootstrap.Tooltip(tooltipTriggerEl);
});
document.getElementById('exportPdfForm').addEventListener('submit', async function(e) {
e.preventDefault();
const form = this;
const button = form.querySelector('button[type="submit"]');
const originalText = button.innerHTML;
try {
// Show loading
button.disabled = true;
button.innerHTML = '<i class="fas fa-spinner fa-spin me-1"></i> Membuat PDF...';
// Submit form normally (let the browser handle the download)
form.submit();
} catch (error) {
console.error('Export Error:', error);
alert(`Gagal export PDF: ${error.message}`);
} finally {
setTimeout(() => {
button.disabled = false;
button.innerHTML = originalText;
}, 3000);
}
});
// Add this to your existing JavaScript
document.querySelectorAll('.budget-input').forEach(input => {
input.addEventListener('input', function() {
let value = this.value.replace(/\D/g, '');
value = new Intl.NumberFormat('id-ID').format(value);
this.value = value;
});
input.addEventListener('blur', function() {
let value = this.value.replace(/\D/g, '');
value = new Intl.NumberFormat('id-ID').format(value);
this.value = value;
});
});
// Update form submission to clean budget values
document.querySelector('#activeBudgetModal form').addEventListener('submit', function(e) {
const input = this.querySelector('input[name="budget"]');
input.value = input.value.replace(/\./g, '');
});
});
</script>
@endsection