458 lines
27 KiB
PHP
458 lines
27 KiB
PHP
@extends('layout.app')
|
|
|
|
@section('content')
|
|
@php
|
|
$saatyLabels = [
|
|
1 => 'Sama Penting (1)',
|
|
2 => 'Mendekati Sedikit Lebih Penting (2)',
|
|
3 => 'Sedikit Lebih Penting (3)',
|
|
4 => 'Mendekati Lebih Penting (4)',
|
|
5 => 'Lebih Penting (5)',
|
|
6 => 'Mendekati Sangat Penting (6)',
|
|
7 => 'Sangat Penting (7)',
|
|
8 => 'Mendekati Mutlak Penting (8)',
|
|
9 => 'Mutlak Lebih Penting (9)',
|
|
];
|
|
$bOptions = [
|
|
['val' => 1/2, 'lbl' => '1/2', 'name' => 'Mendekati Sedikit Lebih Penting (2)'],
|
|
['val' => 1/3, 'lbl' => '1/3', 'name' => 'Sedikit Lebih Penting (3)'],
|
|
['val' => 1/4, 'lbl' => '1/4', 'name' => 'Mendekati Lebih Penting (4)'],
|
|
['val' => 1/5, 'lbl' => '1/5', 'name' => 'Lebih Penting (5)'],
|
|
['val' => 1/6, 'lbl' => '1/6', 'name' => 'Mendekati Sangat Penting (6)'],
|
|
['val' => 1/7, 'lbl' => '1/7', 'name' => 'Sangat Penting (7)'],
|
|
['val' => 1/8, 'lbl' => '1/8', 'name' => 'Mendekati Mutlak Penting (8)'],
|
|
['val' => 1/9, 'lbl' => '1/9', 'name' => 'Mutlak Lebih Penting (9)'],
|
|
];
|
|
@endphp
|
|
|
|
<div class="page-header">
|
|
<ol class="breadcrumb">
|
|
<li class="breadcrumb-item">Home</li>
|
|
<li class="breadcrumb-item active">Perbandingan Sub-Kriteria</li>
|
|
</ol>
|
|
</div>
|
|
|
|
{{-- Notifikasi --}}
|
|
@if (session('success'))
|
|
<script>
|
|
setTimeout(() => {
|
|
Swal.fire('Berhasil!', "{{ session('success') }}", 'success')
|
|
}, 100);
|
|
</script>
|
|
@endif
|
|
@if (session('error'))
|
|
<script>
|
|
setTimeout(() => {
|
|
Swal.fire('Gagal!', "{{ session('error') }}", 'error')
|
|
}, 100);
|
|
</script>
|
|
@endif
|
|
|
|
<div class="row gutters">
|
|
|
|
{{-- PANEL PEMILIHAN KRITERIA --}}
|
|
<div class="col-12 mb-3">
|
|
<div class="card shadow-sm border-0">
|
|
<div class="card-body bg-light">
|
|
<form action="{{ route('perbandingansub.index') }}" method="GET" class="form-inline">
|
|
<label class="mr-3 font-weight-bold">Pilih Kriteria Induk:</label>
|
|
<select name="kriteria_id" id="selectKriteria" class="form-control select2 mr-3" onchange="this.form.submit()"
|
|
style="min-width: 300px;">
|
|
<option value="">-- Kembali ke Daftar Status --</option>
|
|
@foreach ($kriterias as $k)
|
|
<option value="{{ $k->id }}" {{ $selectedKriteriaId == $k->id ? 'selected' : '' }}>
|
|
{{ $k->kode }} - {{ $k->nama }}
|
|
</option>
|
|
@endforeach
|
|
</select>
|
|
@if ($selectedKriteriaId)
|
|
<a href="{{ route('perbandingansub.index') }}" class="btn btn-outline-secondary btn-sm mr-2">
|
|
<i class="icon-arrow-left"></i> Kembali
|
|
</a>
|
|
<a href="{{ route('perbandingansub.detail', ['kriteria_id' => $selectedKriteriaId]) }}"
|
|
class="btn btn-outline-info btn-sm font-weight-bold">
|
|
<i class="icon-info"></i> Detail Perhitungan
|
|
</a>
|
|
@endif
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
@if (!$selectedKriteriaId)
|
|
{{-- VIEW DASHBOARD STATUS (Jika Kriteria Belum Dipilih) --}}
|
|
<div class="col-12">
|
|
<div class="card shadow-sm border-0">
|
|
<div class="card-header bg-dark text-white">
|
|
<h6 class="mb-0 font-weight-bold"><i class="icon-list mr-2"></i> Daftar Status Perbandingan Sub-Kriteria</h6>
|
|
</div>
|
|
<div class="card-body p-0">
|
|
<div class="table-responsive">
|
|
<table class="table table-hover align-middle mb-0">
|
|
<thead class="table-light">
|
|
<tr>
|
|
<th class="pl-4">Kode</th>
|
|
<th>Nama Kriteria Induk</th>
|
|
<th class="text-center">Status Data</th>
|
|
<th class="text-center">Jumlah Pakar</th>
|
|
<th class="text-center">Aksi</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
@foreach ($kriterias as $k)
|
|
@php
|
|
$checkData = \App\Models\PerbandinganSub::where('kriteria_id', $k->id);
|
|
$countPakar = $checkData->distinct('pakar_ke')->count();
|
|
@endphp
|
|
<tr>
|
|
<td class="pl-4"><span class="badge badge-info">{{ $k->kode }}</span></td>
|
|
<td class="font-weight-bold text-dark">{{ $k->nama }}</td>
|
|
<td class="text-center">
|
|
@if ($countPakar > 0)
|
|
<span class="badge badge-success px-3"><i class="icon-check"></i> Sudah Diisi</span>
|
|
@else
|
|
<span class="badge badge-danger px-3"><i class="icon-x"></i> Belum Diisi</span>
|
|
@endif
|
|
</td>
|
|
<td class="text-center font-weight-bold">{{ $countPakar }} Pakar</td>
|
|
<td class="text-center pr-4">
|
|
<div class="btn-group">
|
|
<a href="{{ route('perbandingansub.index', ['kriteria_id' => $k->id]) }}"
|
|
class="btn btn-primary btn-sm rounded-pill px-3 mr-1">
|
|
Pilih <i class="icon-chevron-right"></i>
|
|
</a>
|
|
@if ($countPakar > 0)
|
|
<a href="{{ route('perbandingansub.detail', ['kriteria_id' => $k->id]) }}"
|
|
class="btn btn-outline-info btn-sm rounded-pill"
|
|
title="Lihat Tahapan Rumus">
|
|
<i class="icon-info"></i>
|
|
</a>
|
|
@endif
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
@endforeach
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
@else
|
|
{{-- VIEW FORM INPUT SUB-KRITERIA --}}
|
|
<div class="col-xl-8 col-lg-8 col-md-12">
|
|
<div class="card shadow-sm border-0 h-100">
|
|
<div class="card-header bg-success text-white d-flex justify-content-between align-items-center">
|
|
<h5 class="mb-0"><i class="icon-shield mr-2"></i> Kuesioner Sub-Kriteria</h5>
|
|
<div>
|
|
<button type="button" class="btn btn-warning btn-sm font-weight-bold mr-2 shadow-sm"
|
|
onclick="unlockInput()"><i class="icon-lock-open"></i> Buka Akses</button>
|
|
<button type="button" class="btn btn-light btn-sm font-weight-bold shadow-sm" id="btnAddPakar">
|
|
<i class="icon-plus"></i> Pakar Baru
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div class="card-body">
|
|
<div id="lockOverlayMsg" class="alert alert-info py-2 small mb-3 text-center">
|
|
<i class="icon-info"></i> <strong>Akses Terkunci:</strong> PIN diperlukan untuk sub-kriteria
|
|
<b>{{ $kriterias->find($selectedKriteriaId)->nama }}</b>.
|
|
</div>
|
|
|
|
<ul class="nav nav-pills mb-4 bg-light p-2 rounded shadow-sm" id="pakarTab" role="tablist">
|
|
@foreach ($existingPakar as $index => $p)
|
|
<li class="nav-item">
|
|
<a class="nav-link {{ $index == 0 ? 'active' : '' }} font-weight-bold"
|
|
id="pakar{{ $p }}-tab" data-toggle="pill"
|
|
href="#pakar{{ $p }}" role="tab">Pakar {{ $p }}</a>
|
|
</li>
|
|
@endforeach
|
|
</ul>
|
|
|
|
<div class="tab-content" id="pakarTabContent">
|
|
@foreach ($existingPakar as $index => $pakar)
|
|
<div class="tab-pane fade {{ $index == 0 ? 'show active' : '' }}"
|
|
id="pakar{{ $pakar }}" role="tabpanel">
|
|
|
|
{{-- HEADER TAB & TOMBOL HAPUS (Ditambahkan) --}}
|
|
<div class="d-flex justify-content-between align-items-center mb-3">
|
|
<h6 class="font-weight-bold mb-0 text-success">Form Input Pakar {{ $pakar }}</h6>
|
|
@if ($pakar > 1)
|
|
<form action="{{ route('perbandingansub.hapus_pakar') }}" method="POST" id="deleteForm{{ $pakar }}">
|
|
@csrf
|
|
<input type="hidden" name="kriteria_id" value="{{ $selectedKriteriaId }}">
|
|
<input type="hidden" name="pakar_dihapus" value="{{ $pakar }}">
|
|
<button type="button" class="btn btn-outline-danger btn-sm" onclick="confirmDeletePakar({{ $pakar }})">
|
|
<i class="icon-trash"></i> Hapus Pakar {{ $pakar }}
|
|
</button>
|
|
</form>
|
|
@endif
|
|
</div>
|
|
|
|
<form action="{{ route('perbandingansub.simpan') }}" method="POST"
|
|
id="formPakar{{ $pakar }}" class="form-perbandingan">
|
|
@csrf
|
|
<input type="hidden" name="pakar_ke" value="{{ $pakar }}">
|
|
<input type="hidden" name="kriteria_id" value="{{ $selectedKriteriaId }}">
|
|
<input type="hidden" name="pin_rahasia" class="final-pin-input">
|
|
|
|
<div class="table-responsive border rounded"
|
|
style="max-height: 450px; overflow-y: auto;">
|
|
<table class="table table-hover align-middle mb-0">
|
|
<thead class="table-light sticky-top shadow-sm">
|
|
<tr>
|
|
<th class="py-3 px-4 text-dark">Sub Kiri</th>
|
|
<th width="350" class="text-center">Skala</th>
|
|
<th class="py-3 px-4 text-right text-dark">Sub Kanan</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
@foreach ($pairs as $pair)
|
|
@php
|
|
$savedValue = $allData
|
|
->where('pakar_ke', $pakar)
|
|
->where('sub_kiri_id', $pair['kiri']->id)
|
|
->where('sub_kanan_id', $pair['kanan']->id)
|
|
->first()->nilai_skala ?? 1;
|
|
@endphp
|
|
<tr>
|
|
<td class="py-3 px-4 small">
|
|
<input type="hidden" name="sub_kiri_id[]" value="{{ $pair['kiri']->id }}">
|
|
<span class="badge badge-info mr-1">{{ $pair['kiri']->kode }}</span>
|
|
<b>{{ $pair['kiri']->nama_sub }}</b>
|
|
</td>
|
|
<td>
|
|
<select name="nilai_skala[]" class="form-control form-control-sm compare-input shadow-xs" disabled>
|
|
<option value="1" {{ $savedValue == 1 ? 'selected' : '' }}>1 - Sama Penting</option>
|
|
<optgroup label="Kiri Lebih Penting">
|
|
@foreach ($saatyLabels as $val => $label)
|
|
@if ($val > 1)
|
|
<option value="{{ $val }}" {{ $savedValue == $val ? 'selected' : '' }}>{{ $label }}</option>
|
|
@endif
|
|
@endforeach
|
|
</optgroup>
|
|
<optgroup label="Kanan Lebih Penting">
|
|
@foreach ($bOptions as $opt)
|
|
<option value="{{ $opt['val'] }}" {{ abs($savedValue - $opt['val']) < 0.01 ? 'selected' : '' }}>{{ $opt['lbl'] }} - {{ $opt['name'] }}</option>
|
|
@endforeach
|
|
</optgroup>
|
|
</select>
|
|
</td>
|
|
<td class="py-3 px-4 text-right small">
|
|
<input type="hidden" name="sub_kanan_id[]" value="{{ $pair['kanan']->id }}">
|
|
<b>{{ $pair['kanan']->nama_sub }}</b>
|
|
<span class="badge badge-info ml-1">{{ $pair['kanan']->kode }}</span>
|
|
</td>
|
|
</tr>
|
|
@endforeach
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
<div class="text-center mt-4">
|
|
<button type="button" class="btn btn-success btn-simpan px-5 font-weight-bold"
|
|
onclick="submitWithConfirm('formPakar{{ $pakar }}')" disabled>
|
|
<i class="icon-save"></i> Simpan Pakar {{ $pakar }}
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
@endforeach
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{{-- PANEL BOBOT LOKAL --}}
|
|
<div class="col-xl-4 col-lg-4 col-md-12">
|
|
<div class="card shadow-sm border-0 border-top-info" style="border-top: 3px solid #17a2b8;">
|
|
<div class="card-header bg-white d-flex justify-content-between align-items-center">
|
|
<h5 class="mb-0 text-info font-weight-bold"><i class="icon-pie-chart"></i> Hasil Bobot Lokal</h5>
|
|
<a href="{{ route('perbandingansub.detail', ['kriteria_id' => $selectedKriteriaId]) }}"
|
|
class="btn btn-outline-info btn-xs rounded-pill" title="Lihat Cara Perhitungan">
|
|
<i class="icon-info"></i> Rumus
|
|
</a>
|
|
</div>
|
|
<div class="card-body">
|
|
@php $orderedSubs = $subKriterias->sortByDesc('bobot_lokal'); @endphp
|
|
@if ($subKriterias->sum('bobot_lokal') > 0)
|
|
<div class="alert alert-light border small text-center mb-4">
|
|
<b>Kriteria Induk:</b><br>{{ $kriterias->find($selectedKriteriaId)->nama }}
|
|
</div>
|
|
@foreach ($orderedSubs as $s)
|
|
<div class="mb-3">
|
|
<div class="d-flex justify-content-between align-items-center mb-1">
|
|
<span class="small font-weight-bold text-dark">{{ $s->kode }}</span>
|
|
<div class="text-right">
|
|
<span class="text-dark font-weight-bold small">{{ number_format($s->bobot_lokal, 4) }}</span>
|
|
<span class="badge badge-primary ml-1" style="font-size: 0.6rem;">{{ number_format($s->bobot_lokal * 100, 2) }}%</span>
|
|
</div>
|
|
</div>
|
|
<div class="progress" style="height: 6px; border-radius: 10px;">
|
|
<div class="progress-bar bg-info progress-bar-striped" style="width: {{ $s->bobot_lokal * 100 }}%"></div>
|
|
</div>
|
|
<div class="small text-muted mt-1" style="font-size: 0.7rem;">{{ $s->nama_sub }}</div>
|
|
</div>
|
|
@endforeach
|
|
<hr>
|
|
<a href="{{ route('perbandingansub.detail', ['kriteria_id' => $selectedKriteriaId]) }}"
|
|
class="btn btn-outline-primary btn-block btn-sm mb-2 font-weight-bold">
|
|
<i class="icon-layers"></i> Lihat Tahapan Perhitungan
|
|
</a>
|
|
<a href="{{ route('perbandingan.index') }}"
|
|
class="btn btn-outline-secondary btn-block btn-sm font-weight-bold">Bandingkan Kriteria Global</a>
|
|
@else
|
|
<div class="text-center py-5 text-muted small">Bobot belum tersedia untuk sub-kriteria ini.</div>
|
|
@endif
|
|
</div>
|
|
</div>
|
|
</div>
|
|
@endif
|
|
</div>
|
|
|
|
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
|
|
<script>
|
|
let globalPin = "";
|
|
const validPin = "{{ $pinSistem }}";
|
|
|
|
async function unlockInput() {
|
|
const { value: pin } = await Swal.fire({
|
|
title: 'Verifikasi PIN',
|
|
input: 'password',
|
|
inputLabel: 'Masukkan PIN Pakar untuk Membuka Akses',
|
|
inputPlaceholder: 'PIN...',
|
|
showCancelButton: true,
|
|
confirmButtonColor: '#28a745',
|
|
});
|
|
|
|
if (pin) {
|
|
if (pin === validPin) {
|
|
globalPin = pin;
|
|
document.querySelectorAll('.compare-input').forEach(el => el.disabled = false);
|
|
document.querySelectorAll('.btn-simpan').forEach(el => el.disabled = false);
|
|
document.querySelectorAll('.final-pin-input').forEach(el => el.value = pin);
|
|
|
|
const msgBox = document.getElementById('lockOverlayMsg');
|
|
msgBox.className = "alert alert-success py-2 small mb-3 text-center";
|
|
msgBox.innerHTML = '<i class="icon-unlock"></i> <strong>Akses Terbuka:</strong> PIN terverifikasi. Silakan isi data.';
|
|
Swal.fire('Akses Dibuka!', 'PIN Benar. Silakan isi data perbandingan.', 'success');
|
|
} else {
|
|
Swal.fire('Akses Ditolak!', 'PIN yang Anda masukkan SALAH!', 'error');
|
|
}
|
|
}
|
|
}
|
|
|
|
// PERBAIKAN: Fungsi Submit yang Aman (Sama seperti Kriteria Utama)
|
|
async function submitWithConfirm(formId) {
|
|
if (!globalPin) {
|
|
Swal.fire('Terkunci!', 'Buka akses input dengan PIN terlebih dahulu.', 'error');
|
|
return;
|
|
}
|
|
|
|
const { value: confirmPin } = await Swal.fire({
|
|
title: 'Konfirmasi PIN',
|
|
text: "Masukkan PIN kembali untuk menyimpan data.",
|
|
input: 'password',
|
|
showCancelButton: true,
|
|
confirmButtonColor: '#28a745',
|
|
confirmButtonText: 'Verifikasi & Simpan'
|
|
});
|
|
|
|
if (confirmPin === globalPin) {
|
|
Swal.fire({
|
|
title: 'Memproses...',
|
|
text: 'Harap tunggu.',
|
|
allowOutsideClick: false,
|
|
didOpen: () => {
|
|
Swal.showLoading();
|
|
}
|
|
});
|
|
document.getElementById(formId).submit();
|
|
} else if (confirmPin) {
|
|
Swal.fire('Gagal!', 'PIN salah. Penyimpanan dibatalkan.', 'error');
|
|
}
|
|
}
|
|
|
|
function confirmDeletePakar(pakarNum) {
|
|
Swal.fire({
|
|
title: 'Apakah Anda yakin?',
|
|
text: `Seluruh data penilaian Pakar ${pakarNum} akan dihapus permanen!`,
|
|
icon: 'warning',
|
|
showCancelButton: true,
|
|
confirmButtonColor: '#d33',
|
|
cancelButtonColor: '#3085d6',
|
|
confirmButtonText: 'Ya, Hapus!',
|
|
cancelButtonText: 'Batal'
|
|
}).then((result) => {
|
|
if (result.isConfirmed) {
|
|
Swal.fire({
|
|
title: 'Menghapus...',
|
|
allowOutsideClick: false,
|
|
didOpen: () => { Swal.showLoading(); }
|
|
});
|
|
document.getElementById('deleteForm' + pakarNum).submit();
|
|
}
|
|
});
|
|
}
|
|
|
|
// PERBAIKAN: Fungsi Tambah Pakar Dinamis + CSRF Token Fix
|
|
let btnAddPakar = document.getElementById('btnAddPakar');
|
|
if(btnAddPakar) {
|
|
btnAddPakar.addEventListener('click', function() {
|
|
let pakarTab = document.getElementById('pakarTab');
|
|
let pakarTabContent = document.getElementById('pakarTabContent');
|
|
let nextPakar = pakarTab.querySelectorAll('li').length + 1;
|
|
|
|
pakarTab.insertAdjacentHTML('beforeend',
|
|
`<li class="nav-item"><a class="nav-link font-weight-bold" id="pakar${nextPakar}-tab" data-toggle="pill" href="#pakar${nextPakar}" role="tab">Pakar ${nextPakar}</a></li>`
|
|
);
|
|
|
|
let firstPane = document.querySelector('.tab-pane');
|
|
let newPane = firstPane.cloneNode(true);
|
|
newPane.id = `pakar${nextPakar}`;
|
|
newPane.classList.remove('show', 'active');
|
|
|
|
// Tarik Token dan ID secara Real-time agar Anti-Error 419
|
|
let csrfToken = document.querySelector('input[name="_token"]').value;
|
|
let kriteriaId = document.getElementById('selectKriteria').value;
|
|
|
|
let headerDiv = newPane.querySelector('.d-flex.justify-content-between.align-items-center.mb-3');
|
|
headerDiv.innerHTML = `
|
|
<h6 class="font-weight-bold mb-0 text-success">Form Input Pakar ${nextPakar}</h6>
|
|
<form action="{{ route('perbandingansub.hapus_pakar') }}" method="POST" id="deleteForm${nextPakar}">
|
|
<input type="hidden" name="_token" value="${csrfToken}">
|
|
<input type="hidden" name="kriteria_id" value="${kriteriaId}">
|
|
<input type="hidden" name="pakar_dihapus" value="${nextPakar}">
|
|
<button type="button" class="btn btn-outline-danger btn-sm" onclick="confirmDeletePakar(${nextPakar})">
|
|
<i class="icon-trash"></i> Hapus Pakar ${nextPakar}
|
|
</button>
|
|
</form>
|
|
`;
|
|
|
|
let form = newPane.querySelector('form.form-perbandingan');
|
|
form.id = `formPakar${nextPakar}`;
|
|
newPane.querySelector('input[name="pakar_ke"]').value = nextPakar;
|
|
newPane.querySelector('.final-pin-input').value = globalPin;
|
|
|
|
newPane.querySelectorAll('select').forEach(s => {
|
|
s.value = "1";
|
|
s.disabled = (globalPin === "");
|
|
});
|
|
|
|
let btn = newPane.querySelector('.btn-simpan');
|
|
btn.innerHTML = `<i class="icon-save"></i> Simpan Pakar ${nextPakar}`;
|
|
btn.disabled = (globalPin === "");
|
|
btn.setAttribute('onclick', `submitWithConfirm('formPakar${nextPakar}')`);
|
|
|
|
pakarTabContent.appendChild(newPane);
|
|
$(`#pakar${nextPakar}-tab`).tab('show');
|
|
});
|
|
}
|
|
</script>
|
|
|
|
<style>
|
|
.compare-input:disabled { background-color: #f8f9fa; cursor: not-allowed; }
|
|
.sticky-top { position: sticky; top: 0; background: white; z-index: 10; }
|
|
.table-responsive::-webkit-scrollbar { width: 6px; }
|
|
.table-responsive::-webkit-scrollbar-thumb { background: #17a2b8; border-radius: 10px; }
|
|
</style>
|
|
@endsection
|