MIF_E31230680/resources/views/seleksi/index.blade.php

623 lines
34 KiB
PHP

@extends('layout.app')
@section('content')
<div class="page-header">
<ol class="breadcrumb">
<li class="breadcrumb-item">Home</li>
<li class="breadcrumb-item active">Hasil Seleksi SPK</li>
</ol>
</div>
{{-- NOTIFIKASI ERROR --}}
@if (session('error'))
<div class="alert alert-danger alert-dismissible fade show" role="alert">
<i class="icon-warning"></i> {{ session('error') }}
<button type="button" class="close" data-dismiss="alert"><span>&times;</span></button>
</div>
@endif
<div class="row gutters">
<div class="col-xl-12">
{{-- PERINGATAN DATA BASI (STALE DATA) --}}
@if (isset($butuhHitungUlang) && $butuhHitungUlang)
<div class="alert alert-warning border-0 shadow-sm d-flex flex-wrap align-items-center p-4 mb-4"
style="border-left: 5px solid #ffc107 !important; border-radius: 8px; background-color: #fffdf5;">
<i class="icon-alert-triangle text-warning mr-3 mb-2 mb-md-0" style="font-size: 2.5rem;"></i>
<div class="flex-grow-1">
<h5 class="alert-heading font-weight-bold mb-1 text-dark">Perhatian: Perhitungan Ulang Diperlukan!
</h5>
<p class="mb-0 text-dark" style="font-size: 0.9rem;">
Terdapat perubahan data warga (edit nilai/penambahan) yang sudah divalidasi sejak perhitungan
terakhir.
<strong>Urutan ranking di bawah ini mungkin sudah tidak akurat.</strong>
</p>
</div>
@if (auth()->user()->role == 'admin')
<div class="ml-auto pl-md-3 mt-3 mt-md-0 w-100 w-md-auto">
<button type="button"
class="btn btn-warning btn-block font-weight-bold shadow-sm rounded-pill px-4 py-2"
data-toggle="modal" data-target="#modalHitungSPK">
<i class="icon-refresh-cw mr-1"></i> Hitung Ulang Sekarang
</button>
</div>
@endif
</div>
@endif
{{-- Card Info & Statistik --}}
<div class="card shadow-sm border-0 mb-4">
<div class="card-body">
<div class="row align-items-center">
<div class="col-md-7">
<h4 class="text-dark font-weight-bold mb-1">
<i class="icon-award text-warning"></i> Hasil Perangkingan Fuzzy AHP
</h4>
@if ($periode)
<p class="text-muted mb-3 mt-2">
Periode Aktif: <strong class="text-primary">{{ $periode->nama_periode }}</strong> |
Kuota: <span class="badge badge-info">{{ $periode->kuota }} KK</span>
</p>
{{-- Ringkasan Statistik --}}
<div class="d-flex flex-wrap gap-3">
{{-- Prioritas Utama --}}
<div class="stats-box bg-light-success text-center px-3 py-2 rounded">
<h6 class="mb-0 text-success font-weight-bold">{{ $countUtama }}</h6>
<small class="text-muted font-weight-bold">Prioritas Utama</small>
</div>
{{-- Layak Cadangan --}}
<div class="stats-box bg-light-info text-center px-3 py-2 rounded">
<h6 class="mb-0 text-info font-weight-bold">{{ $countCadangan }}</h6>
<small class="text-muted font-weight-bold">Layak Cadangan</small>
</div>
{{-- Tidak Layak --}}
<div class="stats-box bg-light-danger text-center px-3 py-2 rounded">
<h6 class="mb-0 text-danger font-weight-bold">{{ $countTidakLayak }}</h6>
<small class="text-muted font-weight-bold">Tidak Layak</small>
</div>
{{-- Box Dialihkan --}}
<div class="stats-box text-center px-3 py-2 rounded"
style="background-color: #fff3cd; border-color: #ffeeba;">
<h6 class="mb-0 font-weight-bold" style="color: #856404;">{{ $countDialihkan ?? 0 }}
</h6>
<small class="font-weight-bold" style="color: #856404;">Dialihkan</small>
</div>
{{-- Rerata Periode Aktif (Warna Hitam) --}}
<div class="stats-box bg-dark text-center px-3 py-2 rounded">
<h6 class="mb-0 text-white font-weight-bold">{{ number_format($mean, 4) }}</h6>
<small class="text-light" style="font-size: 10px;">Rerata Periode</small>
</div>
{{-- STANDAR KELAYAKAN DESA / PASSING GRADE (Warna Ungu) --}}
{{-- Ini yang menjadi penentu kenapa warga seperti Elisa (0.29) Tidak Layak --}}
<div class="stats-box text-center px-3 py-2 rounded"
style="background-color: #6f42c1; border: 1px solid #5a32a3;">
<h6 class="mb-0 text-white font-weight-bold">{{ number_format($passingGrade, 4) }}
</h6>
<small class="text-white" style="font-size: 10px;">Passing Grade Desa</small>
</div>
</div>
@else
<div class="alert alert-danger py-2 px-3 mb-0 mt-3">
<i class="icon-alert-triangle"></i> Peringatan: Tidak ada periode yang aktif saat ini!
Silakan buat atau aktifkan periode di menu Manajemen Periode.
</div>
@endif
</div>
<div class="col-md-5 text-md-right mt-3 mt-md-0">
@if ($periode)
{{-- Proteksi Role: Hanya Admin yang bisa melihat panel perhitungan --}}
@if (auth()->user()->role == 'admin')
<div class="p-3 border rounded bg-light mb-3 text-center shadow-sm">
<span class="d-block text-muted small text-uppercase font-weight-bold">Total Warga
Siap Dihitung</span>
<h3 class="mb-0 text-dark font-weight-bold">{{ $jumlahWargaSiap }} <small
class="text-muted" style="font-size: 14px;">Kepala Keluarga</small></h3>
</div>
{{-- TOMBOL PEMICU MODAL HITUNG --}}
<button type="button"
class="btn btn-primary btn-block shadow-sm rounded-pill font-weight-bold py-2"
data-toggle="modal" data-target="#modalHitungSPK">
<i class="icon-refresh-cw"></i> MULAI PERHITUNGAN SPK
</button>
@else
{{-- Info untuk Verifikator --}}
<div class="p-4 border rounded bg-light text-center">
<i class="icon-info1 h2 text-primary"></i>
<p class="mb-0 mt-2 font-weight-bold">Mode Lihat Data</p>
<small class="text-muted">Anda masuk sebagai Verifikator. Proses perhitungan hanya
dapat dilakukan oleh Admin.</small>
</div>
@endif
@endif
</div>
</div>
</div>
</div>
{{-- Fitur Tambahan: Search, Limit & Export (Hanya muncul jika ada periode aktif) --}}
@if ($periode)
<div class="card shadow-sm border-0 mb-3">
<div class="card-body">
<form action="{{ route('seleksi.index') }}" method="GET" class="row align-items-end">
<div class="col-md-2">
<label class="small font-weight-bold">Tampilkan</label>
<select name="per_page" class="form-control" onchange="this.form.submit()">
@foreach ([10, 25, 50, 100, 500] as $val)
<option value="{{ $val }}" {{ $perPage == $val ? 'selected' : '' }}>
{{ $val }} Data</option>
@endforeach
</select>
</div>
<div class="col-md-4">
<label class="small font-weight-bold">Cari Nama/NIK</label>
<div class="input-group">
<input type="text" name="search" class="form-control"
placeholder="Ketik nama atau NIK..." value="{{ $search }}">
<div class="input-group-append">
<button class="btn btn-primary" type="submit">
<i class="icon-search"></i>
</button>
</div>
</div>
</div>
<div class="col-md-6 text-md-right mt-3">
<div class="btn-group">
<button type="button" class="btn btn-success dropdown-toggle shadow-sm"
data-toggle="dropdown">
<i class="icon-file-text"></i> Export Excel
</button>
<div class="dropdown-menu">
<a class="dropdown-item" href="javascript:void(0)"
onclick="confirmExport('{{ route('seleksi.exportExcel') }}', 'Excel', 'Semua Data')">
Semua Data
</a>
{{-- Hanya muncul jika ada data hasil seleksi --}}
@if ($results->total() > 0)
<a class="dropdown-item" href="javascript:void(0)"
onclick="confirmExport('{{ route('seleksi.exportExcel', ['limit' => 'kuota']) }}', 'Excel', 'Sesuai Kuota')">
Sesuai Kuota ({{ $periode->kuota ?? 0 }})
</a>
<div class="dropdown-divider"></div>
@endif
{{-- Filter Berdasarkan Status --}}
@if ($countUtama > 0)
<a class="dropdown-item" href="javascript:void(0)"
onclick="confirmExport('{{ route('seleksi.exportExcel', ['status' => 'Layak Prioritas Utama']) }}', 'Excel', 'Prioritas Utama')">
Hanya Prioritas Utama
</a>
@endif
@if ($countCadangan > 0)
<a class="dropdown-item" href="javascript:void(0)"
onclick="confirmExport('{{ route('seleksi.exportExcel', ['status' => 'Layak Cadangan']) }}', 'Excel', 'Cadangan')">
Hanya Cadangan
</a>
@endif
@if ($countTidakLayak > 0)
<a class="dropdown-item" href="javascript:void(0)"
onclick="confirmExport('{{ route('seleksi.exportExcel', ['status' => 'Tidak Layak']) }}', 'Excel', 'Tidak Layak')">
Hanya Tidak Layak
</a>
@endif
</div>
</div>
<div class="btn-group">
<button type="button" class="btn btn-danger dropdown-toggle shadow-sm"
data-toggle="dropdown">
<i class="icon-file-pdf"></i> Export PDF
</button>
<div class="dropdown-menu">
<a class="dropdown-item" href="javascript:void(0)"
onclick="confirmExport('{{ route('seleksi.exportPdf') }}', 'PDF', 'Semua Data')">
Semua Data
</a>
{{-- Hanya muncul jika ada data hasil seleksi --}}
@if ($results->total() > 0)
<a class="dropdown-item" href="javascript:void(0)"
onclick="confirmExport('{{ route('seleksi.exportPdf', ['limit' => 'kuota']) }}', 'PDF', 'Sesuai Kuota')">
Sesuai Kuota ({{ $periode->kuota ?? 0 }})
</a>
<div class="dropdown-divider"></div>
@endif
{{-- Filter Berdasarkan Status --}}
@if ($countUtama > 0)
<a class="dropdown-item" href="javascript:void(0)"
onclick="confirmExport('{{ route('seleksi.exportPdf', ['status' => 'Layak Prioritas Utama']) }}', 'PDF', 'Prioritas Utama')">
Hanya Prioritas Utama
</a>
@endif
@if ($countCadangan > 0)
<a class="dropdown-item" href="javascript:void(0)"
onclick="confirmExport('{{ route('seleksi.exportPdf', ['status' => 'Layak Cadangan']) }}', 'PDF', 'Cadangan')">
Hanya Cadangan
</a>
@endif
@if ($countTidakLayak > 0)
<a class="dropdown-item" href="javascript:void(0)"
onclick="confirmExport('{{ route('seleksi.exportPdf', ['status' => 'Tidak Layak']) }}', 'PDF', 'Tidak Layak')">
Hanya Tidak Layak
</a>
@endif
</div>
</div>
<a href="{{ route('seleksi.index') }}" class="btn btn-light border shadow-sm">
<i class="icon-refresh-ccw"></i> Reset
</a>
</div>
</form>
</div>
</div>
{{-- Tabel Hasil --}}
<div class="card shadow-sm border-0 border-top-primary">
<div class="card-header bg-white">
<h5 class="mb-0 text-dark font-weight-bold">Daftar Urutan Kelayakan</h5>
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-hover mb-0" id="tableSeleksi">
<thead>
<tr class="text-center bg-light text-uppercase small font-weight-bold">
<th width="50">Rank</th>
<th class="text-left">Nama Warga</th>
<th>NIK</th>
<th>Skor (V)</th>
<th>Status Keputusan</th>
<th>Aksi</th>
</tr>
</thead>
<tbody>
@forelse ($results as $res)
<tr class="text-center">
<td class="font-weight-bold">
@if ($res->ranking <= 3)
<span
class="rank-badge rank-{{ $res->ranking }}">{{ $res->ranking }}</span>
@else
{{ $res->ranking }}
@endif
</td>
<td class="text-left font-weight-bold text-dark">{{ $res->warga->nama }}</td>
<td><code>{{ $res->warga->nik }}</code></td>
<td class="text-primary font-weight-bold">
{{ number_format($res->skor_akhir, 4) }}
</td>
<td>
@if ($res->status_keputusan == 'Layak Prioritas Utama')
<span class="status-label status-utama">Layak Prioritas Utama</span>
@elseif($res->status_keputusan == 'Layak Cadangan')
<span class="status-label status-cadangan">Layak Cadangan</span>
@elseif($res->status_keputusan == 'Dialihkan (Pemerataan)')
<span class="status-label status-dialihkan">Dialihkan
(Pemerataan)
</span>
@else
<span class="status-label status-tidak">Tidak Layak</span>
@endif
</td>
<td>
<a href="{{ route('seleksi.show', $res->id) }}"
class="btn btn-info btn-sm rounded-circle shadow-sm"
title="Lihat Detail & Rincian Skor">
<i class="icon-info"></i>
</a>
</td>
</tr>
@empty
<tr>
<td colspan="6" class="text-center py-5">
<p class="text-muted font-italic">Belum ada data perhitungan di periode
aktif ini.</p>
</td>
</tr>
@endforelse
</tbody>
</table>
</div>
{{-- Link Pagination --}}
@if ($results instanceof \Illuminate\Pagination\LengthAwarePaginator)
<div class="mt-4 d-flex justify-content-between align-items-center">
<div class="small text-muted">
Menampilkan {{ $results->firstItem() }} - {{ $results->lastItem() }} dari
{{ $results->total() }} data
</div>
<div>
{{ $results->links() }}
</div>
</div>
@endif
</div>
</div>
@endif
</div>
</div>
{{-- MODAL HITUNG SPK (Hanya muncul jika periode aktif dan role admin) --}}
@if ($periode && auth()->user()->role == 'admin')
<div class="modal fade" id="modalHitungSPK" tabindex="-1" role="dialog" data-backdrop="static">
<div class="modal-dialog modal-lg" role="document">
<form action="{{ route('seleksi.proses') }}" method="POST" id="formHitungLengkap">
@csrf
<div class="modal-content border-0 shadow-lg">
<div class="modal-header bg-primary text-white">
<h5 class="modal-title text-white"><i class="icon-settings"></i> Setup Perhitungan F-AHP</h5>
<button type="button" class="close text-white"
data-dismiss="modal"><span>&times;</span></button>
</div>
<div class="modal-body px-4 pt-4">
<div class="row">
<div class="col-md-6">
<div class="form-group mb-4">
<label class="font-weight-bold text-dark">Mode Perangkingan</label>
<select name="mode_hitung" id="mode_hitung"
class="form-control border-primary shadow-sm">
<option value="pemerataan">Pemerataan (Geser Jatah Penerima Lama)</option>
<option value="murni">Murni (Ranking Bebas Sesuai Skor)</option>
</select>
<small class="text-muted d-block mt-1">Pemerataan disarankan agar bantuan
merata.</small>
</div>
</div>
<div class="col-md-6">
<div class="form-group mb-4">
<label class="font-weight-bold text-dark">Target Warga</label>
<select name="target_warga" id="target_warga"
class="form-control border-primary shadow-sm" onchange="toggleWargaSearch()">
<option value="semua">Hitung Semua ({{ $jumlahWargaSiap }} Warga)</option>
<option value="pilih">Hitung Warga Tertentu / Darurat</option>
</select>
</div>
</div>
</div>
{{-- Wrapper Pencarian & Checkbox Warga --}}
<div id="wrapper_pilih_warga" style="display: none;">
<hr class="mt-0">
<div class="form-group">
<label class="font-weight-bold text-dark">Cari Warga Target:</label>
<input type="text" id="liveSearchWarga" class="form-control border-info mb-2"
placeholder="Ketik nama untuk mencari...">
</div>
<div class="border rounded bg-light p-2 shadow-sm"
style="max-height: 250px; overflow-y: auto;">
@if (isset($wargaSiap) && $wargaSiap->count() > 0)
@foreach ($wargaSiap as $w)
<div class="custom-control custom-checkbox mb-2 warga-item">
<input type="checkbox" name="selected_warga[]"
value="{{ $w->id }}"
class="custom-control-input warga-target-checkbox"
id="wargamodal_{{ $w->id }}">
<label class="custom-control-label text-dark font-weight-bold"
for="wargamodal_{{ $w->id }}" style="cursor: pointer;">
<span class="warga-nama">{{ $w->nama }}</span>
<small class="text-muted">({{ $w->nik }})</small>
</label>
</div>
@endforeach
@else
<small class="text-danger d-block p-2">Tidak ada data warga yang
terverifikasi.</small>
@endif
</div>
<small class="text-info mt-2 d-block"><i class="icon-info"></i> Centang nama-nama yang
ingin dimasukkan dalam seleksi khusus ini.</small>
</div>
</div>
<div class="modal-footer bg-light">
<button type="button" class="btn btn-secondary px-4" data-dismiss="modal">Batal</button>
<button type="button" class="btn btn-primary px-4 font-weight-bold"
onclick="submitHitungAkhir({{ $jumlahWargaSiap }})">
<i class="icon-play"></i> Eksekusi Sekarang
</button>
</div>
</div>
</form>
</div>
</div>
@endif
<style>
.gap-3 {
gap: 1rem;
}
.border-top-primary {
border-top: 4px solid #3085d6 !important;
}
.stats-box {
min-width: 110px;
border: 1px solid rgba(0, 0, 0, 0.05);
}
.rank-badge {
width: 28px;
height: 28px;
display: inline-flex;
align-items: center;
justify-content: center;
border-radius: 50%;
font-weight: bold;
color: white;
font-size: 12px;
}
.rank-1 {
background: #FFD700;
box-shadow: 0 2px 5px rgba(255, 215, 0, 0.5);
}
.rank-2 {
background: #C0C0C0;
}
.rank-3 {
background: #CD7F32;
}
.status-label {
padding: 5px 12px;
border-radius: 50px;
font-size: 10px;
font-weight: bold;
text-transform: uppercase;
white-space: nowrap;
}
.status-utama {
background: #d4edda;
color: #155724;
}
.status-cadangan {
background: #cce5ff;
color: #004085;
}
.status-tidak {
background: #f8d7da;
color: #721c24;
}
.status-dialihkan {
background: #fff3cd;
color: #856404;
}
.pagination {
margin-bottom: 0;
}
</style>
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
<script>
// Fitur Live Search Warga di dalam Modal
document.getElementById('liveSearchWarga')?.addEventListener('keyup', function() {
let filter = this.value.toLowerCase();
let items = document.querySelectorAll('.warga-item');
items.forEach(function(item) {
let text = item.querySelector('.warga-nama').innerText.toLowerCase();
if (text.includes(filter)) {
item.style.display = 'block';
} else {
item.style.display = 'none';
}
});
});
// Menampilkan/Menyembunyikan Checkbox Pencarian
function toggleWargaSearch() {
let target = document.getElementById('target_warga').value;
let wrapper = document.getElementById('wrapper_pilih_warga');
if (target === 'pilih') {
wrapper.style.display = 'block';
} else {
wrapper.style.display = 'none';
}
}
// Eksekusi Submit dengan Alert
function submitHitungAkhir(jumlahTotal) {
let target = document.getElementById('target_warga').value;
let selectedCount = document.querySelectorAll('.warga-target-checkbox:checked').length;
let modeText = document.getElementById('mode_hitung').options[document.getElementById('mode_hitung')
.selectedIndex].text;
if (target === 'pilih' && selectedCount === 0) {
Swal.fire('Peringatan', 'Silakan centang minimal 1 warga dari daftar!', 'warning');
return;
}
let jumlahHitung = (target === 'semua') ? jumlahTotal : selectedCount;
Swal.fire({
title: 'Konfirmasi Perhitungan',
html: `Sistem akan memproses <strong>${jumlahHitung} warga</strong>.<br><br>
<div class="text-left bg-light p-3 mt-2 rounded border">
Target: <strong>${target === 'semua' ? 'Semua Warga (Global)' : 'Warga Terpilih Saja'}</strong><br>
Mode: <strong class="text-primary">${modeText}</strong>
</div>`,
icon: 'question',
showCancelButton: true,
confirmButtonText: 'Ya, Proses Sekarang!',
cancelButtonText: 'Batal',
confirmButtonColor: '#3085d6',
}).then((result) => {
if (result.isConfirmed) {
$('#modalHitungSPK').modal('hide');
Swal.fire({
title: 'Memproses Data...',
html: 'Algoritma F-AHP sedang berjalan, mohon tunggu.',
allowOutsideClick: false,
didOpen: () => {
Swal.showLoading()
}
});
document.getElementById('formHitungLengkap').submit();
}
});
}
// Fungsi Konfirmasi Export (Tetap)
function confirmExport(url, tipe, kategori) {
Swal.fire({
title: 'Konfirmasi Export ' + tipe,
html: `Anda akan mengunduh file <strong>${tipe}</strong> untuk kategori: <span class="text-primary"><strong>${kategori}</strong></span>.`,
icon: 'info',
showCancelButton: true,
confirmButtonText: 'Unduh',
cancelButtonText: 'Batal',
confirmButtonColor: tipe === 'Excel' ? '#28a745' : '#dc3545',
}).then((result) => {
if (result.isConfirmed) {
Swal.mixin({
toast: true,
position: 'top-end',
showConfirmButton: false,
timer: 3000
}).fire({
icon: 'success',
title: 'File ' + tipe + ' disiapkan...'
});
window.location.href = url;
}
});
}
// Notifikasi Sukses Setelah Hitung
document.addEventListener('DOMContentLoaded', function() {
@if (session('success'))
Swal.fire({
icon: 'success',
title: 'Selesai!',
text: "{{ session('success') }}",
confirmButtonColor: '#28a745'
});
@endif
});
</script>
@endsection