(done) buat 2 col di kolom pertanyaan, buat select search
This commit is contained in:
parent
f5d6ce9c2e
commit
678a388a80
@ -70,7 +70,8 @@ class SoalController extends Controller
|
|||||||
$soal->setRelation('soalDetail', $detailSoal);
|
$soal->setRelation('soalDetail', $detailSoal);
|
||||||
|
|
||||||
$halPertama = $daftarHal->first() ?? $hal;
|
$halPertama = $daftarHal->first() ?? $hal;
|
||||||
|
$pegawai = session('pegawai');
|
||||||
|
dd($pegawai);
|
||||||
return view('soal.index', [
|
return view('soal.index', [
|
||||||
'soal' => $soal,
|
'soal' => $soal,
|
||||||
'hal' => $hal,
|
'hal' => $hal,
|
||||||
@ -78,6 +79,7 @@ class SoalController extends Controller
|
|||||||
'daftarHal' => $daftarHal,
|
'daftarHal' => $daftarHal,
|
||||||
'totalHal' => $daftarHal->count(),
|
'totalHal' => $daftarHal->count(),
|
||||||
'soalId' => $soal->id,
|
'soalId' => $soal->id,
|
||||||
|
'pegawai' => $pegawai,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -3,6 +3,7 @@
|
|||||||
@section('title', 'Kuesioner Soal')
|
@section('title', 'Kuesioner Soal')
|
||||||
|
|
||||||
@section('custom_css')
|
@section('custom_css')
|
||||||
|
<link rel="stylesheet" href="{{ asset('vuexy/assets/vendor/libs/select2/select2.css') }}">
|
||||||
<style>
|
<style>
|
||||||
.question-card {
|
.question-card {
|
||||||
border: 1px solid #dee2e6;
|
border: 1px solid #dee2e6;
|
||||||
@ -12,6 +13,27 @@
|
|||||||
box-shadow: 0 4px 18px rgba(0, 0, 0, 0.04);
|
box-shadow: 0 4px 18px rgba(0, 0, 0, 0.04);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.question-layout .question-text-col {
|
||||||
|
border-right: 1px solid #f1f3f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.question-layout .answer-section {
|
||||||
|
padding-left: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 767.98px) {
|
||||||
|
.question-layout .question-text-col {
|
||||||
|
border-right: 0;
|
||||||
|
border-bottom: 1px dashed #e9ecef;
|
||||||
|
padding-bottom: 1rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.question-layout .answer-section {
|
||||||
|
padding-left: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.option-scroll {
|
.option-scroll {
|
||||||
max-height: 220px;
|
max-height: 220px;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
@ -21,6 +43,27 @@
|
|||||||
background-color: #f8f9fa;
|
background-color: #f8f9fa;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.select2-container {
|
||||||
|
width: 100% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container .select2-selection--single {
|
||||||
|
min-height: calc(2.625rem + 2px);
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
border: 1px solid #ced4da;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--default .select2-selection--single .select2-selection__rendered {
|
||||||
|
line-height: 1.6;
|
||||||
|
color: #4f4f4f;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--default .select2-selection--single .select2-selection__arrow {
|
||||||
|
top: 0.6rem;
|
||||||
|
right: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
.option-scroll::-webkit-scrollbar {
|
.option-scroll::-webkit-scrollbar {
|
||||||
width: 6px;
|
width: 6px;
|
||||||
}
|
}
|
||||||
@ -38,6 +81,10 @@
|
|||||||
display: block;
|
display: block;
|
||||||
margin-top: 0.85rem;
|
margin-top: 0.85rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.dual-form-wrapper .form-label {
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
@endsection
|
@endsection
|
||||||
|
|
||||||
@ -88,9 +135,7 @@
|
|||||||
@if ($hal === $halPertama)
|
@if ($hal === $halPertama)
|
||||||
<div class="card border-0 shadow-sm mb-4" id="head_soal">
|
<div class="card border-0 shadow-sm mb-4" id="head_soal">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<p class="text-muted mb-1">Judul Soal</p>
|
|
||||||
<h5 class="mb-3 text-primary">{{ $soal->judul_soal ?? '-' }}</h5>
|
<h5 class="mb-3 text-primary">{{ $soal->judul_soal ?? '-' }}</h5>
|
||||||
<p class="text-muted mb-2">Keterangan</p>
|
|
||||||
<div class="border rounded p-3 bg-light text-body">
|
<div class="border rounded p-3 bg-light text-body">
|
||||||
{!! $soal->keterangan_soal !!}
|
{!! $soal->keterangan_soal !!}
|
||||||
</div>
|
</div>
|
||||||
@ -123,82 +168,226 @@
|
|||||||
$pertanyaan = $detailConfig['soal'] ?? 'Pertanyaan tidak tersedia';
|
$pertanyaan = $detailConfig['soal'] ?? 'Pertanyaan tidak tersedia';
|
||||||
$type = $detailConfig['type'] ?? 'option';
|
$type = $detailConfig['type'] ?? 'option';
|
||||||
$options = $detailConfig['options'] ?? [];
|
$options = $detailConfig['options'] ?? [];
|
||||||
|
if (!is_array($options)) {
|
||||||
|
$options = [];
|
||||||
|
}
|
||||||
|
$optionValues = collect($options)->map(function ($value) {
|
||||||
|
return is_scalar($value) ? (string) $value : '';
|
||||||
|
})->all();
|
||||||
$oldAnswer = old('jawaban.' . $detail->id);
|
$oldAnswer = old('jawaban.' . $detail->id);
|
||||||
$oldOtherAnswer = old('jawaban_lainnya.' . $detail->id);
|
$oldOtherAnswer = old('jawaban_lainnya.' . $detail->id);
|
||||||
$showLainnya = $oldAnswer === 'Lainnya' || (!empty($oldOtherAnswer));
|
$hasOldAnswer = $oldAnswer !== null && $oldAnswer !== '';
|
||||||
|
$isCustomOldAnswer = $hasOldAnswer && !in_array($oldAnswer, $optionValues, true);
|
||||||
|
if (!$oldOtherAnswer && $isCustomOldAnswer) {
|
||||||
|
$oldOtherAnswer = $oldAnswer;
|
||||||
|
}
|
||||||
|
$showLainnya = $isCustomOldAnswer
|
||||||
|
|| (is_string($oldAnswer) && stripos($oldAnswer, 'lainnya') !== false)
|
||||||
|
|| (!empty($oldOtherAnswer));
|
||||||
$detailHal = $detail->hal ?? $listHal->first();
|
$detailHal = $detail->hal ?? $listHal->first();
|
||||||
$isVisible = $detailHal == $hal;
|
$isVisible = $detailHal == $hal;
|
||||||
|
$optionsCount = is_array($options) ? count($options) : 0;
|
||||||
|
$useSelectSearch = $optionsCount > 4;
|
||||||
|
$hasLainnyaOption = collect($options)->contains(function ($optionItem) {
|
||||||
|
return is_string($optionItem) && stripos($optionItem, 'lainnya') !== false;
|
||||||
|
});
|
||||||
|
if (!$hasLainnyaOption && $type === 'option_with_other') {
|
||||||
|
$hasLainnyaOption = true;
|
||||||
|
}
|
||||||
|
$isConsentQuestion = !empty($detailConfig['persetujuan_form']);
|
||||||
|
$shouldForceLainnyaSelection = $hasLainnyaOption && $showLainnya;
|
||||||
|
$dualFormConfig = $detailConfig['dual_form'] ?? null;
|
||||||
|
$useDualForm = $type === 'dual_form' || (!empty($dualFormConfig) && $dualFormConfig !== false);
|
||||||
|
$dualYearOld = null;
|
||||||
|
$dualMonthOld = null;
|
||||||
|
if ($useDualForm && $oldAnswer) {
|
||||||
|
if (preg_match('/([0-9]+)\\s*\\(Tahun\\)/i', $oldAnswer, $matchYear)) {
|
||||||
|
$dualYearOld = $matchYear[1];
|
||||||
|
}
|
||||||
|
if (preg_match('/([0-9]+)\\s*\\(Bulan\\)/i', $oldAnswer, $matchMonth)) {
|
||||||
|
$dualMonthOld = $matchMonth[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
@endphp
|
@endphp
|
||||||
|
|
||||||
<div class="question-card mb-4"
|
<div class="question-card mb-4"
|
||||||
data-hal-card="{{ $detailHal }}"
|
data-hal-card="{{ $detailHal }}"
|
||||||
|
data-detail-id="{{ $detail->id }}"
|
||||||
|
data-consent-question="{{ $isConsentQuestion ? '1' : '0' }}"
|
||||||
|
data-question-type="{{ $type }}"
|
||||||
style="{{ $isVisible ? '' : 'display: none;' }}">
|
style="{{ $isVisible ? '' : 'display: none;' }}">
|
||||||
<div class="d-flex align-items-center justify-content-between mb-2 flex-wrap gap-2">
|
<div class="row g-3 align-items-start question-layout">
|
||||||
<div class="d-flex align-items-center gap-2">
|
<div class="col-md-6 question-text-col">
|
||||||
<span class="badge rounded-pill bg-label-primary fs-6">{{ $loop->iteration }}</span>
|
<div class="d-flex align-items-center gap-3 mb-1">
|
||||||
|
<span class="badge rounded-pill bg-label-primary fs-6">{{ $loop->iteration }}</span>
|
||||||
|
<h5 class="fw-semibold mb-0">{{ $pertanyaan }}</h5>
|
||||||
|
</div>
|
||||||
|
@if ($isConsentQuestion)
|
||||||
|
<p class="text-muted small mb-0">Pertanyaan persetujuan</p>
|
||||||
|
@endif
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
<h5 class="fw-semibold mb-3">{{ $pertanyaan }}</h5>
|
|
||||||
|
|
||||||
<div>
|
<div class="col-md-6 answer-section">
|
||||||
@if ($type === 'textarea')
|
@if ($useDualForm)
|
||||||
<textarea class="form-control @error('jawaban.' . $detail->id) is-invalid @enderror"
|
<div class="dual-form-wrapper" data-dual-wrapper="{{ $detail->id }}">
|
||||||
name="jawaban[{{ $detail->id }}]" rows="4" required
|
<div class="row g-3">
|
||||||
data-field-hal="{{ $detailHal }}"
|
<div class="col-sm-6">
|
||||||
placeholder="Tulis jawaban Anda di sini">{{ old('jawaban.' . $detail->id) }}</textarea>
|
<label class="form-label small text-muted mb-1">Tahun</label>
|
||||||
@elseif ($type === 'text')
|
<input type="number" min="0" class="form-control"
|
||||||
<input type="text" class="form-control @error('jawaban.' . $detail->id) is-invalid @enderror"
|
data-dual-input="tahun"
|
||||||
name="jawaban[{{ $detail->id }}]" value="{{ old('jawaban.' . $detail->id) }}" required
|
value="{{ $dualYearOld }}"
|
||||||
data-field-hal="{{ $detailHal }}"
|
|
||||||
placeholder="Masukkan jawaban Anda">
|
|
||||||
@else
|
|
||||||
@if (!empty($options))
|
|
||||||
<div class="option-scroll">
|
|
||||||
@foreach ($options as $optionIndex => $option)
|
|
||||||
@php
|
|
||||||
$optionId = 'jawaban-' . $detail->id . '-' . $optionIndex;
|
|
||||||
$isLainnya = strtolower(trim($option)) === 'lainnya';
|
|
||||||
@endphp
|
|
||||||
<div class="form-check mb-2">
|
|
||||||
<input class="form-check-input @error('jawaban.' . $detail->id) is-invalid @enderror"
|
|
||||||
type="radio"
|
|
||||||
name="jawaban[{{ $detail->id }}]"
|
|
||||||
id="{{ $optionId }}"
|
|
||||||
value="{{ $option }}"
|
|
||||||
data-lainnya-radio="{{ $isLainnya ? $detail->id : '' }}" required
|
|
||||||
data-field-hal="{{ $detailHal }}"
|
data-field-hal="{{ $detailHal }}"
|
||||||
{{ $oldAnswer === $option ? 'checked' : '' }}>
|
placeholder="Masukkan tahun">
|
||||||
<label class="form-check-label" for="{{ $optionId }}">
|
|
||||||
{{ $option }}
|
|
||||||
</label>
|
|
||||||
</div>
|
</div>
|
||||||
@if ($isLainnya)
|
<div class="col-sm-6">
|
||||||
|
<label class="form-label small text-muted mb-1">Bulan</label>
|
||||||
|
<input type="number" min="0" class="form-control"
|
||||||
|
data-dual-input="bulan"
|
||||||
|
value="{{ $dualMonthOld }}"
|
||||||
|
data-field-hal="{{ $detailHal }}"
|
||||||
|
placeholder="Masukkan bulan">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<input type="hidden"
|
||||||
|
class="@error('jawaban.' . $detail->id) is-invalid @enderror"
|
||||||
|
name="jawaban[{{ $detail->id }}]"
|
||||||
|
value="{{ old('jawaban.' . $detail->id) }}"
|
||||||
|
data-dual-hidden="{{ $detail->id }}"
|
||||||
|
data-field-hal="{{ $detailHal }}"
|
||||||
|
@if ($isConsentQuestion) data-consent-input="1" @endif
|
||||||
|
required>
|
||||||
|
</div>
|
||||||
|
@elseif ($type === 'textarea')
|
||||||
|
<textarea class="form-control @error('jawaban.' . $detail->id) is-invalid @enderror"
|
||||||
|
name="jawaban[{{ $detail->id }}]" rows="4" required
|
||||||
|
data-field-hal="{{ $detailHal }}"
|
||||||
|
@if ($isConsentQuestion) data-consent-input="1" @endif
|
||||||
|
placeholder="Tulis jawaban Anda di sini">{{ old('jawaban.' . $detail->id) }}</textarea>
|
||||||
|
@elseif ($type === 'text')
|
||||||
|
<input type="text" class="form-control @error('jawaban.' . $detail->id) is-invalid @enderror"
|
||||||
|
name="jawaban[{{ $detail->id }}]" value="{{ old('jawaban.' . $detail->id) }}" required
|
||||||
|
data-field-hal="{{ $detailHal }}"
|
||||||
|
@if ($isConsentQuestion) data-consent-input="1" @endif
|
||||||
|
placeholder="Masukkan jawaban Anda">
|
||||||
|
@else
|
||||||
|
@if (!empty($options))
|
||||||
|
@if ($useSelectSearch)
|
||||||
|
<select class="form-select select2 @error('jawaban.' . $detail->id) is-invalid @enderror"
|
||||||
|
name="jawaban[{{ $detail->id }}]"
|
||||||
|
data-field-hal="{{ $detailHal }}"
|
||||||
|
@if ($isConsentQuestion) data-consent-input="1" @endif
|
||||||
|
@if ($hasLainnyaOption) data-lainnya-select="{{ $detail->id }}" @endif
|
||||||
|
data-select-search="true"
|
||||||
|
required>
|
||||||
|
<option value="" disabled {{ $oldAnswer ? '' : 'selected' }}>Pilih jawaban</option>
|
||||||
|
@foreach ($options as $option)
|
||||||
|
@php
|
||||||
|
$optionLabel = is_scalar($option) ? (string) $option : '';
|
||||||
|
$isLainnya = stripos($optionLabel, 'lainnya') !== false;
|
||||||
|
$optionValue = $isLainnya && $oldOtherAnswer ? $oldOtherAnswer : $optionLabel;
|
||||||
|
$shouldSelect = $oldAnswer === $optionValue || ($isLainnya && $shouldForceLainnyaSelection);
|
||||||
|
@endphp
|
||||||
|
<option value="{{ $optionValue }}"
|
||||||
|
data-original-value="{{ $optionLabel }}"
|
||||||
|
@if ($isLainnya) data-lainnya-option="{{ $detail->id }}" @endif
|
||||||
|
{{ $shouldSelect ? 'selected' : '' }}>
|
||||||
|
{{ $optionLabel }}
|
||||||
|
</option>
|
||||||
|
@endforeach
|
||||||
|
@if ($type === 'option_with_other' && !$hasLainnyaOption)
|
||||||
|
<option value="Lainnya"
|
||||||
|
data-original-value="Lainnya"
|
||||||
|
data-lainnya-option="{{ $detail->id }}">
|
||||||
|
Lainnya
|
||||||
|
</option>
|
||||||
|
@endif
|
||||||
|
</select>
|
||||||
|
@if ($hasLainnyaOption)
|
||||||
|
<div class="lainnya-input {{ $showLainnya ? 'show' : '' }}" data-lainnya-wrapper="{{ $detail->id }}">
|
||||||
|
<input type="text" class="form-control form-control-sm mt-2"
|
||||||
|
name="jawaban_lainnya[{{ $detail->id }}]"
|
||||||
|
value="{{ $oldOtherAnswer }}"
|
||||||
|
data-field-hal="{{ $detailHal }}"
|
||||||
|
data-lainnya-input="{{ $detail->id }}"
|
||||||
|
@if ($isConsentQuestion) data-consent-input="1" @endif
|
||||||
|
placeholder="Tuliskan jawaban lainnya"
|
||||||
|
{{ $showLainnya ? 'required' : '' }}>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
@else
|
||||||
|
@php
|
||||||
|
$lainnyaWrapperRendered = false;
|
||||||
|
@endphp
|
||||||
|
<div class="option-scroll">
|
||||||
|
@foreach ($options as $optionIndex => $option)
|
||||||
|
@php
|
||||||
|
$optionId = 'jawaban-' . $detail->id . '-' . $optionIndex;
|
||||||
|
$optionLabel = is_scalar($option) ? (string) $option : '';
|
||||||
|
$isLainnya = stripos($optionLabel, 'lainnya') !== false;
|
||||||
|
$optionValue = $isLainnya && $oldOtherAnswer ? $oldOtherAnswer : $optionLabel;
|
||||||
|
$shouldCheck = $oldAnswer === $optionValue || ($isLainnya && $shouldForceLainnyaSelection);
|
||||||
|
@endphp
|
||||||
|
<div class="form-check mb-2">
|
||||||
|
<input class="form-check-input @error('jawaban.' . $detail->id) is-invalid @enderror"
|
||||||
|
type="radio"
|
||||||
|
name="jawaban[{{ $detail->id }}]"
|
||||||
|
id="{{ $optionId }}"
|
||||||
|
value="{{ $optionValue }}"
|
||||||
|
data-original-value="{{ $optionLabel }}"
|
||||||
|
data-lainnya-radio="{{ $isLainnya ? $detail->id : '' }}" required
|
||||||
|
data-field-hal="{{ $detailHal }}"
|
||||||
|
@if ($isConsentQuestion) data-consent-input="1" @endif
|
||||||
|
{{ $shouldCheck ? 'checked' : '' }}>
|
||||||
|
<label class="form-check-label" for="{{ $optionId }}">
|
||||||
|
{{ $optionLabel }}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
@if ($isLainnya)
|
||||||
|
@php
|
||||||
|
$lainnyaWrapperRendered = true;
|
||||||
|
@endphp
|
||||||
|
<div class="lainnya-input {{ $showLainnya ? 'show' : '' }}" data-lainnya-wrapper="{{ $detail->id }}">
|
||||||
|
<input type="text" class="form-control form-control-sm mt-2"
|
||||||
|
name="jawaban_lainnya[{{ $detail->id }}]"
|
||||||
|
value="{{ $oldOtherAnswer }}"
|
||||||
|
data-lainnya-input="{{ $detail->id }}"
|
||||||
|
data-field-hal="{{ $detailHal }}"
|
||||||
|
@if ($isConsentQuestion) data-consent-input="1" @endif
|
||||||
|
placeholder="Tuliskan jawaban lainnya"
|
||||||
|
{{ $showLainnya ? 'required' : '' }}>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
@endforeach
|
||||||
|
</div>
|
||||||
|
@if (!$lainnyaWrapperRendered && $type === 'option_with_other')
|
||||||
<div class="lainnya-input {{ $showLainnya ? 'show' : '' }}" data-lainnya-wrapper="{{ $detail->id }}">
|
<div class="lainnya-input {{ $showLainnya ? 'show' : '' }}" data-lainnya-wrapper="{{ $detail->id }}">
|
||||||
<input type="text" class="form-control form-control-sm mt-2"
|
<input type="text" class="form-control form-control-sm mt-2"
|
||||||
name="jawaban_lainnya[{{ $detail->id }}]"
|
name="jawaban_lainnya[{{ $detail->id }}]"
|
||||||
value="{{ $oldOtherAnswer }}"
|
value="{{ $oldOtherAnswer }}"
|
||||||
data-lainnya-input="{{ $detail->id }}"
|
data-lainnya-input="{{ $detail->id }}"
|
||||||
data-field-hal="{{ $detailHal }}"
|
data-field-hal="{{ $detailHal }}"
|
||||||
|
@if ($isConsentQuestion) data-consent-input="1" @endif
|
||||||
placeholder="Tuliskan jawaban lainnya"
|
placeholder="Tuliskan jawaban lainnya"
|
||||||
{{ $showLainnya ? 'required' : '' }}>
|
{{ $showLainnya ? 'required' : '' }}>
|
||||||
</div>
|
</div>
|
||||||
@endif
|
@endif
|
||||||
@endforeach
|
@endif
|
||||||
</div>
|
@else
|
||||||
@else
|
<input type="text" class="form-control @error('jawaban.' . $detail->id) is-invalid @enderror"
|
||||||
<input type="text" class="form-control @error('jawaban.' . $detail->id) is-invalid @enderror"
|
name="jawaban[{{ $detail->id }}]" value="{{ old('jawaban.' . $detail->id) }}" required
|
||||||
name="jawaban[{{ $detail->id }}]" value="{{ old('jawaban.' . $detail->id) }}" required
|
data-field-hal="{{ $detailHal }}"
|
||||||
data-field-hal="{{ $detailHal }}"
|
@if ($isConsentQuestion) data-consent-input="1" @endif
|
||||||
placeholder="Masukkan jawaban Anda">
|
placeholder="Masukkan jawaban Anda">
|
||||||
|
@endif
|
||||||
@endif
|
@endif
|
||||||
@endif
|
|
||||||
|
|
||||||
@error('jawaban.' . $detail->id)
|
@error('jawaban.' . $detail->id)
|
||||||
<div class="invalid-feedback">{{ $message }}</div>
|
<div class="invalid-feedback d-block">{{ $message }}</div>
|
||||||
@enderror
|
@enderror
|
||||||
@error('jawaban_lainnya.' . $detail->id)
|
@error('jawaban_lainnya.' . $detail->id)
|
||||||
<div class="text-danger small mt-1">{{ $message }}</div>
|
<div class="text-danger small mt-1">{{ $message }}</div>
|
||||||
@enderror
|
@enderror
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@empty
|
@empty
|
||||||
@ -243,6 +432,7 @@
|
|||||||
@endsection
|
@endsection
|
||||||
|
|
||||||
@section('custom_js')
|
@section('custom_js')
|
||||||
|
<script src="{{ asset('vuexy/assets/vendor/libs/select2/select2.js') }}"></script>
|
||||||
<script>
|
<script>
|
||||||
document.addEventListener('DOMContentLoaded', function () {
|
document.addEventListener('DOMContentLoaded', function () {
|
||||||
const form = document.getElementById('form-soal');
|
const form = document.getElementById('form-soal');
|
||||||
@ -276,8 +466,53 @@
|
|||||||
const progressText = document.getElementById('progress-text');
|
const progressText = document.getElementById('progress-text');
|
||||||
const summaryHal = document.getElementById('summary-hal');
|
const summaryHal = document.getElementById('summary-hal');
|
||||||
const totalHal = halList.length || 1;
|
const totalHal = halList.length || 1;
|
||||||
|
const nonConsentFields = document.querySelectorAll('[data-field-hal]:not([data-consent-input="1"])');
|
||||||
|
const consentFields = document.querySelectorAll('[data-consent-input="1"]');
|
||||||
|
const headSoalCard = document.getElementById('head_soal');
|
||||||
|
const consentNegativeKeywords = ['tidak', 'tidak setuju'];
|
||||||
|
let immediateSubmitActive = false;
|
||||||
|
|
||||||
|
function normalizeOtherValue(value) {
|
||||||
|
return (value || '').toString().trim().toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
function isOtherChoice(value) {
|
||||||
|
const normalized = normalizeOtherValue(value);
|
||||||
|
if (!normalized) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return normalized === 'lainnya'
|
||||||
|
|| normalized === 'lainnya (sebutkan)'
|
||||||
|
|| normalized === 'lainnya/other'
|
||||||
|
|| normalized === 'lainnya / other'
|
||||||
|
|| normalized === 'other'
|
||||||
|
|| normalized === 'others'
|
||||||
|
|| normalized.includes('lainnya')
|
||||||
|
|| normalized.includes('other');
|
||||||
|
}
|
||||||
|
|
||||||
|
function getLabelTextByInput(input) {
|
||||||
|
if (!input || !input.id) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
const label = document.querySelector('label[for="' + input.id + '"]');
|
||||||
|
return label ? (label.textContent || '') : '';
|
||||||
|
}
|
||||||
|
|
||||||
|
halInputs.forEach(function (field) {
|
||||||
|
if (field.dataset && field.dataset.lainnyaInput) {
|
||||||
|
field.dataset.originalRequired = '0';
|
||||||
|
field.dataset.dynamicRequired = field.required ? '1' : '0';
|
||||||
|
} else {
|
||||||
|
field.dataset.originalRequired = field.required ? '1' : '0';
|
||||||
|
field.dataset.dynamicRequired = field.dataset.dynamicRequired || '0';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
initSelectSearch();
|
||||||
setupLainnyaInputs();
|
setupLainnyaInputs();
|
||||||
|
setupDualFormInputs();
|
||||||
|
setupConsentWatcher();
|
||||||
updateQuestionVisibility();
|
updateQuestionVisibility();
|
||||||
updateNavigationUI();
|
updateNavigationUI();
|
||||||
|
|
||||||
@ -300,21 +535,40 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
form.addEventListener('submit', function () {
|
||||||
|
if (!immediateSubmitActive) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
nonConsentFields.forEach(function (field) {
|
||||||
|
if (shouldDisableFieldForImmediate(field)) {
|
||||||
|
field.disabled = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
function navigateRelative(step) {
|
function navigateRelative(step) {
|
||||||
|
if (immediateSubmitActive) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
const currentIndex = halList.indexOf(currentHal);
|
const currentIndex = halList.indexOf(currentHal);
|
||||||
const targetHal = halList[currentIndex + step];
|
const targetHal = halList[currentIndex + step];
|
||||||
if (typeof targetHal === 'undefined') {
|
if (typeof targetHal === 'undefined') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(targetHal === 1){
|
if (headSoalCard) {
|
||||||
document.getElementById('head_soal').classList.remove('d-none')
|
if (targetHal === 1) {
|
||||||
}else{
|
headSoalCard.classList.remove('d-none');
|
||||||
document.getElementById('head_soal').classList.add('d-none')
|
} else {
|
||||||
|
headSoalCard.classList.add('d-none');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
changeHal(targetHal);
|
changeHal(targetHal);
|
||||||
}
|
}
|
||||||
|
|
||||||
function changeHal(targetHal) {
|
function changeHal(targetHal) {
|
||||||
|
if (immediateSubmitActive) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (targetHal === currentHal || halList.indexOf(targetHal) === -1) {
|
if (targetHal === currentHal || halList.indexOf(targetHal) === -1) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -371,20 +625,22 @@
|
|||||||
const isLast = currentIndex === halList.length - 1;
|
const isLast = currentIndex === halList.length - 1;
|
||||||
|
|
||||||
if (prevButton) {
|
if (prevButton) {
|
||||||
prevButton.disabled = isFirst;
|
prevButton.disabled = immediateSubmitActive ? true : isFirst;
|
||||||
}
|
}
|
||||||
if (nextButton) {
|
if (nextButton) {
|
||||||
nextButton.disabled = isLast;
|
if (immediateSubmitActive) {
|
||||||
|
nextButton.style.display = 'none';
|
||||||
|
} else {
|
||||||
|
nextButton.style.display = '';
|
||||||
|
nextButton.disabled = isLast;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
navHalButtons.forEach(function (button) {
|
navHalButtons.forEach(function (button) {
|
||||||
if (parseInt(button.dataset.navHal, 10) === currentHal) {
|
const isActive = parseInt(button.dataset.navHal, 10) === currentHal;
|
||||||
button.classList.remove('btn-outline-primary');
|
button.classList.toggle('btn-primary', isActive);
|
||||||
button.classList.add('btn-primary');
|
button.classList.toggle('btn-outline-primary', !isActive);
|
||||||
} else {
|
button.disabled = immediateSubmitActive;
|
||||||
button.classList.add('btn-outline-primary');
|
|
||||||
button.classList.remove('btn-primary');
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const progress = totalHal > 0 ? Math.round(((currentIndex + 1) / totalHal) * 100) : 100;
|
const progress = totalHal > 0 ? Math.round(((currentIndex + 1) / totalHal) * 100) : 100;
|
||||||
@ -402,34 +658,46 @@
|
|||||||
summaryHal.textContent = 'Halaman ' + (currentIndex + 1) + ' dari ' + totalHal;
|
summaryHal.textContent = 'Halaman ' + (currentIndex + 1) + ' dari ' + totalHal;
|
||||||
}
|
}
|
||||||
if (finalSubmitButton) {
|
if (finalSubmitButton) {
|
||||||
finalSubmitButton.style.display = isLast ? '' : 'none';
|
finalSubmitButton.style.display = (isLast || immediateSubmitActive) ? '' : 'none';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function setupLainnyaInputs() {
|
function setupLainnyaInputs() {
|
||||||
document.querySelectorAll('[data-lainnya-radio]').forEach(function (radio) {
|
document.querySelectorAll('input[type="radio"]').forEach(function (radio) {
|
||||||
radio.addEventListener('change', function (event) {
|
radio.addEventListener('change', function (event) {
|
||||||
const detailId = event.target.getAttribute('data-lainnya-radio');
|
const customDetailId = event.target.getAttribute('data-lainnya-radio');
|
||||||
if (!detailId) {
|
const detailId = customDetailId || event.target.name.replace('jawaban[', '').replace(']', '');
|
||||||
return;
|
const targetWrapper = detailId ? document.querySelector('[data-lainnya-wrapper="' + detailId + '"]') : null;
|
||||||
}
|
const labelText = getLabelTextByInput(event.target);
|
||||||
const targetWrapper = document.querySelector('[data-lainnya-wrapper="' + detailId + '"]');
|
const shouldShow = !!customDetailId
|
||||||
|
|| isOtherChoice(event.target.value)
|
||||||
|
|| isOtherChoice(event.target.dataset.originalValue)
|
||||||
|
|| isOtherChoice(labelText);
|
||||||
|
|
||||||
if (targetWrapper) {
|
if (targetWrapper) {
|
||||||
handleLainnyaInput(targetWrapper, true);
|
if (shouldShow) {
|
||||||
|
handleLainnyaInput(targetWrapper, true);
|
||||||
|
} else {
|
||||||
|
handleLainnyaInput(targetWrapper, false, true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
document.querySelectorAll('input[type="radio"]').forEach(function (radio) {
|
document.querySelectorAll('[data-lainnya-input]').forEach(function (input) {
|
||||||
radio.addEventListener('change', function (event) {
|
input.addEventListener('input', function (event) {
|
||||||
const detailId = event.target.name.replace('jawaban[', '').replace(']', '');
|
const detailId = input.getAttribute('data-lainnya-input');
|
||||||
const targetWrapper = document.querySelector('[data-lainnya-wrapper="' + detailId + '"]');
|
syncCustomOptionValue(detailId, event.target.value);
|
||||||
|
|
||||||
if (targetWrapper && event.target.value !== 'Lainnya') {
|
|
||||||
handleLainnyaInput(targetWrapper, false, true);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
document.querySelectorAll('[data-lainnya-select]').forEach(function (select) {
|
||||||
|
const detailId = select.name.replace('jawaban[', '').replace(']', '');
|
||||||
|
select.addEventListener('change', function (event) {
|
||||||
|
handleLainnyaSelect(event.target, detailId, true);
|
||||||
|
});
|
||||||
|
handleLainnyaSelect(select, detailId, false);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleLainnyaInput(wrapper, show, clearValue = false) {
|
function handleLainnyaInput(wrapper, show, clearValue = false) {
|
||||||
@ -438,15 +706,220 @@
|
|||||||
}
|
}
|
||||||
wrapper.classList.toggle('show', show);
|
wrapper.classList.toggle('show', show);
|
||||||
const input = wrapper.querySelector('input');
|
const input = wrapper.querySelector('input');
|
||||||
|
const detailId = wrapper.getAttribute('data-lainnya-wrapper');
|
||||||
if (input) {
|
if (input) {
|
||||||
input.required = show;
|
input.required = show;
|
||||||
|
input.dataset.dynamicRequired = show ? '1' : '0';
|
||||||
if (show) {
|
if (show) {
|
||||||
input.focus();
|
input.focus();
|
||||||
} else if (clearValue) {
|
syncCustomOptionValue(detailId, input.value);
|
||||||
input.value = '';
|
} else {
|
||||||
|
if (clearValue) {
|
||||||
|
input.value = '';
|
||||||
|
}
|
||||||
|
resetCustomOptionValue(detailId);
|
||||||
|
}
|
||||||
|
} else if (!show) {
|
||||||
|
resetCustomOptionValue(detailId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleLainnyaSelect(selectElement, detailId, canClear) {
|
||||||
|
if (!selectElement || !detailId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const targetWrapper = document.querySelector('[data-lainnya-wrapper="' + detailId + '"]');
|
||||||
|
if (!targetWrapper) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const selectedOption = selectElement.options[selectElement.selectedIndex];
|
||||||
|
const optionText = selectedOption ? (selectedOption.textContent || selectedOption.innerText || '') : '';
|
||||||
|
const originalValue = selectedOption ? (selectedOption.dataset.originalValue || '') : '';
|
||||||
|
const isLainnyaSelected = selectedOption && (
|
||||||
|
selectedOption.dataset.lainnyaOption
|
||||||
|
|| isOtherChoice(selectElement.value)
|
||||||
|
|| isOtherChoice(optionText)
|
||||||
|
|| isOtherChoice(originalValue)
|
||||||
|
);
|
||||||
|
if (isLainnyaSelected) {
|
||||||
|
handleLainnyaInput(targetWrapper, true);
|
||||||
|
} else {
|
||||||
|
handleLainnyaInput(targetWrapper, false, canClear);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function syncCustomOptionValue(detailId, customValue) {
|
||||||
|
if (!detailId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const trimmedValue = (customValue || '').toString().trim();
|
||||||
|
const radio = document.querySelector('input[data-lainnya-radio="' + detailId + '"]');
|
||||||
|
if (radio) {
|
||||||
|
const originalValue = radio.dataset.originalValue || 'lainnya';
|
||||||
|
radio.value = trimmedValue || originalValue;
|
||||||
|
}
|
||||||
|
const select = document.querySelector('[data-lainnya-select="' + detailId + '"]');
|
||||||
|
if (select) {
|
||||||
|
const option = select.querySelector('[data-lainnya-option="' + detailId + '"]');
|
||||||
|
if (option) {
|
||||||
|
const originalOptionValue = option.dataset.originalValue || 'lainnya';
|
||||||
|
option.value = trimmedValue || originalOptionValue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function resetCustomOptionValue(detailId) {
|
||||||
|
if (!detailId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const radio = document.querySelector('input[data-lainnya-radio="' + detailId + '"]');
|
||||||
|
if (radio && radio.dataset.originalValue) {
|
||||||
|
radio.value = radio.dataset.originalValue;
|
||||||
|
}
|
||||||
|
const select = document.querySelector('[data-lainnya-select="' + detailId + '"]');
|
||||||
|
if (select) {
|
||||||
|
const option = select.querySelector('[data-lainnya-option="' + detailId + '"]');
|
||||||
|
if (option && option.dataset.originalValue) {
|
||||||
|
option.value = option.dataset.originalValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function shouldDisableFieldForImmediate(field) {
|
||||||
|
if (!field || field.dataset.consentInput === '1') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const tagName = (field.tagName || '').toLowerCase();
|
||||||
|
const type = (field.type || '').toLowerCase();
|
||||||
|
if (type === 'radio' || type === 'checkbox') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (tagName === 'select') {
|
||||||
|
const value = field.value;
|
||||||
|
return value === null || value === '';
|
||||||
|
}
|
||||||
|
const value = (field.value || '').toString().trim();
|
||||||
|
return value === '';
|
||||||
|
}
|
||||||
|
|
||||||
|
function initSelectSearch() {
|
||||||
|
if (!window.jQuery || !window.jQuery.fn || typeof window.jQuery.fn.select2 === 'undefined') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const $ = window.jQuery;
|
||||||
|
$('[data-select-search="true"]').each(function () {
|
||||||
|
const $select = $(this);
|
||||||
|
if ($select.data('select2')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$select.wrap('<div class="position-relative w-100"></div>');
|
||||||
|
$select.select2({
|
||||||
|
dropdownParent: $select.parent(),
|
||||||
|
width: '100%',
|
||||||
|
placeholder: $select.find('option[disabled]').first().text() || 'Pilih jawaban',
|
||||||
|
minimumResultsForSearch: 0
|
||||||
|
});
|
||||||
|
if ($select.is('[data-lainnya-select]')) {
|
||||||
|
const detailId = ($select.attr('name') || '').replace('jawaban[', '').replace(']', '');
|
||||||
|
$select.on('select2:select', function () {
|
||||||
|
handleLainnyaSelect(this, detailId, true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function setupDualFormInputs() {
|
||||||
|
document.querySelectorAll('[data-dual-wrapper]').forEach(function (wrapper) {
|
||||||
|
const detailId = wrapper.getAttribute('data-dual-wrapper');
|
||||||
|
const hiddenInput = wrapper.querySelector('[data-dual-hidden]');
|
||||||
|
const yearInput = wrapper.querySelector('[data-dual-input="tahun"]');
|
||||||
|
const monthInput = wrapper.querySelector('[data-dual-input="bulan"]');
|
||||||
|
const inputs = [yearInput, monthInput].filter(Boolean);
|
||||||
|
inputs.forEach(function (input) {
|
||||||
|
input.addEventListener('input', function () {
|
||||||
|
updateDualFormAnswer(detailId);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
updateDualFormAnswer(detailId);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateDualFormAnswer(detailId) {
|
||||||
|
if (!detailId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const wrapper = document.querySelector('[data-dual-wrapper="' + detailId + '"]');
|
||||||
|
if (!wrapper) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const hiddenInput = wrapper.querySelector('[data-dual-hidden]');
|
||||||
|
if (!hiddenInput) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const yearInput = wrapper.querySelector('[data-dual-input="tahun"]');
|
||||||
|
const monthInput = wrapper.querySelector('[data-dual-input="bulan"]');
|
||||||
|
const yearValue = (yearInput && yearInput.value ? yearInput.value : '').toString().trim();
|
||||||
|
const monthValue = (monthInput && monthInput.value ? monthInput.value : '').toString().trim();
|
||||||
|
const parts = [];
|
||||||
|
if (yearValue !== '') {
|
||||||
|
parts.push(yearValue + ' (Tahun)');
|
||||||
|
}
|
||||||
|
if (monthValue !== '') {
|
||||||
|
parts.push(monthValue + ' (Bulan)');
|
||||||
|
}
|
||||||
|
const combined = parts.join(' ');
|
||||||
|
hiddenInput.value = combined;
|
||||||
|
if (hiddenInput.required) {
|
||||||
|
hiddenInput.setCustomValidity(combined ? '' : 'Harap isi minimal salah satu kolom (Tahun/Bulan).');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function setupConsentWatcher() {
|
||||||
|
if (!consentFields.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
consentFields.forEach(function (field) {
|
||||||
|
field.addEventListener('change', function (event) {
|
||||||
|
if (field.type === 'radio' && !field.checked) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
evaluateConsentValue(event.target.value);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (
|
||||||
|
(field.type === 'radio' && field.checked) ||
|
||||||
|
(field.tagName === 'SELECT' && field.value) ||
|
||||||
|
(field.type !== 'radio' && field.value)
|
||||||
|
) {
|
||||||
|
evaluateConsentValue(field.value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function evaluateConsentValue(value) {
|
||||||
|
const normalized = (value || '').toString().trim().toLowerCase();
|
||||||
|
const shouldStop = consentNegativeKeywords.some(function (keyword) {
|
||||||
|
return normalized.startsWith(keyword);
|
||||||
|
});
|
||||||
|
toggleImmediateSubmit(shouldStop);
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleImmediateSubmit(activate) {
|
||||||
|
if (immediateSubmitActive === activate) {
|
||||||
|
updateNavigationUI();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
immediateSubmitActive = activate;
|
||||||
|
halInputs.forEach(function (field) {
|
||||||
|
const originalRequired = field.dataset.originalRequired === '1';
|
||||||
|
const dynamicRequired = field.dataset.dynamicRequired === '1';
|
||||||
|
field.required = activate ? false : (originalRequired || dynamicRequired);
|
||||||
|
if (!field.required && typeof field.setCustomValidity === 'function') {
|
||||||
|
field.setCustomValidity('');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
updateNavigationUI();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
@endsection
|
@endsection
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user