684 lines
22 KiB
PHP
684 lines
22 KiB
PHP
@extends('siswa.layouts.app')
|
|
|
|
@section('title', 'Kerjakan Challenge')
|
|
|
|
@push('styles')
|
|
<style>
|
|
/* Sembunyikan sidebar navigasi app */
|
|
.siswa-wrapper .nav-sidebar,
|
|
.siswa-wrapper aside.sidebar,
|
|
.sidebar-toggle-btn { display: none !important; }
|
|
.siswa-wrapper .main,
|
|
.siswa-wrapper .main-content {
|
|
margin-left: 0 !important;
|
|
width: 100% !important;
|
|
max-width: 100% !important;
|
|
}
|
|
|
|
/* ─────────────────────────────────────────
|
|
LAYOUT
|
|
───────────────────────────────────────── */
|
|
.quiz-page {
|
|
display: grid;
|
|
grid-template-columns: 1fr 272px;
|
|
gap: 20px;
|
|
max-width: 1080px;
|
|
margin: 0 auto;
|
|
padding: 20px 16px 48px;
|
|
align-items: start;
|
|
}
|
|
.quiz-main {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 14px;
|
|
min-width: 0;
|
|
}
|
|
|
|
/* Header */
|
|
.quiz-header {
|
|
background: #fff;
|
|
border: 1.5px solid #e2e8f0;
|
|
border-radius: 18px;
|
|
padding: 20px 24px;
|
|
text-align: center;
|
|
}
|
|
.quiz-title {
|
|
font-size: 19px;
|
|
font-weight: 800;
|
|
color: #1e293b;
|
|
margin-bottom: 10px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
gap: 8px;
|
|
}
|
|
.quiz-title img { width: 22px; height: 22px; object-fit: contain; }
|
|
.quiz-meta {
|
|
display: flex;
|
|
justify-content: center;
|
|
flex-wrap: wrap;
|
|
gap: 8px;
|
|
}
|
|
.quiz-meta span {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 5px;
|
|
font-size: 12px;
|
|
color: #475569;
|
|
background: #f8fafc;
|
|
border: 1px solid #e2e8f0;
|
|
border-radius: 99px;
|
|
padding: 4px 12px;
|
|
font-weight: 500;
|
|
}
|
|
.quiz-meta img { width: 13px; height: 13px; object-fit: contain; }
|
|
|
|
/* Durasi badge di header */
|
|
.durasi-meta-badge {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 5px;
|
|
font-size: 12px;
|
|
font-weight: 700;
|
|
color: #5b21b6;
|
|
background: #ede9fe;
|
|
border: 1px solid #c4b5fd;
|
|
border-radius: 99px;
|
|
padding: 4px 12px;
|
|
}
|
|
|
|
/* Progress */
|
|
.progress-card {
|
|
background: #fff;
|
|
border: 1.5px solid #e2e8f0;
|
|
border-radius: 14px;
|
|
padding: 12px 20px;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 14px;
|
|
}
|
|
.progress-label {
|
|
font-size: 12px;
|
|
font-weight: 600;
|
|
color: #64748b;
|
|
white-space: nowrap;
|
|
flex-shrink: 0;
|
|
}
|
|
.progress-bar-wrap {
|
|
flex: 1;
|
|
background: #e2e8f0;
|
|
border-radius: 99px;
|
|
height: 7px;
|
|
overflow: hidden;
|
|
}
|
|
.progress-bar-fill {
|
|
height: 100%;
|
|
background: linear-gradient(90deg, #667eea, #764ba2);
|
|
border-radius: 99px;
|
|
transition: width 0.4s ease;
|
|
}
|
|
|
|
/* Soal card */
|
|
.soal-card {
|
|
background: #fff;
|
|
border: 1.5px solid #e2e8f0;
|
|
border-radius: 18px;
|
|
padding: 24px 26px;
|
|
display: none;
|
|
}
|
|
.soal-card.active { display: block; }
|
|
.soal-number {
|
|
display: inline-block;
|
|
background: linear-gradient(135deg, #667eea, #764ba2);
|
|
color: #fff;
|
|
font-size: 11px;
|
|
font-weight: 700;
|
|
padding: 3px 13px;
|
|
border-radius: 99px;
|
|
margin-bottom: 14px;
|
|
}
|
|
.soal-pertanyaan {
|
|
font-size: 15px;
|
|
font-weight: 600;
|
|
color: #1e293b;
|
|
line-height: 1.7;
|
|
margin-bottom: 18px;
|
|
}
|
|
.opsi-list { display: flex; flex-direction: column; gap: 9px; }
|
|
.opsi-item {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 12px;
|
|
background: #f8fafc;
|
|
border: 1.5px solid #e2e8f0;
|
|
border-radius: 12px;
|
|
padding: 12px 16px;
|
|
cursor: pointer;
|
|
transition: border-color 0.15s, background 0.15s;
|
|
font-size: 14px;
|
|
color: #334155;
|
|
user-select: none;
|
|
}
|
|
.opsi-item:hover { border-color: #667eea; background: #f0eeff; }
|
|
.opsi-item.selected {
|
|
border-color: #667eea;
|
|
background: #ede9fe;
|
|
color: #4c1d95;
|
|
font-weight: 600;
|
|
}
|
|
.opsi-item input[type="radio"] { display: none; }
|
|
.opsi-label-circle {
|
|
width: 30px; height: 30px;
|
|
border-radius: 50%;
|
|
background: #e2e8f0;
|
|
display: flex; align-items: center; justify-content: center;
|
|
font-size: 12px; font-weight: 700;
|
|
flex-shrink: 0;
|
|
transition: background 0.15s, color 0.15s;
|
|
}
|
|
.opsi-item.selected .opsi-label-circle { background: #667eea; color: #fff; }
|
|
|
|
/* Nav buttons */
|
|
.nav-buttons-card {
|
|
background: #fff;
|
|
border: 1.5px solid #e2e8f0;
|
|
border-radius: 14px;
|
|
padding: 14px 20px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
gap: 10px;
|
|
}
|
|
.btn-nav {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 6px;
|
|
padding: 10px 22px;
|
|
border-radius: 10px;
|
|
border: none;
|
|
font-size: 13px;
|
|
font-weight: 600;
|
|
cursor: pointer;
|
|
transition: all 0.18s;
|
|
font-family: 'Poppins', sans-serif;
|
|
}
|
|
.btn-prev { background: #f1f5f9; color: #475569; }
|
|
.btn-prev:hover:not(:disabled) { background: #e2e8f0; }
|
|
.btn-prev:disabled { opacity: 0.4; cursor: not-allowed; }
|
|
.btn-next { background: linear-gradient(135deg, #667eea, #764ba2); color: #fff; }
|
|
.btn-next:hover { opacity: 0.88; }
|
|
.btn-submit { background: linear-gradient(135deg, #22c55e, #16a34a); color: #fff; display: none; }
|
|
.btn-submit:hover { opacity: 0.88; }
|
|
.target-selesai{ width: 15px; height: 15px; object-fit: contain; }
|
|
|
|
/* Warning */
|
|
.warning-box {
|
|
display: none;
|
|
align-items: center;
|
|
gap: 8px;
|
|
background: #fff7ed;
|
|
border: 1px solid #fed7aa;
|
|
border-radius: 11px;
|
|
padding: 11px 15px;
|
|
font-size: 13px;
|
|
color: #c2410c;
|
|
font-weight: 500;
|
|
margin-bottom: 4px;
|
|
}
|
|
.warning-box img { width: 15px; height: 15px; object-fit: contain; flex-shrink: 0; }
|
|
|
|
/* ─────────────────────────────────────────
|
|
SIDEBAR QUIZ
|
|
───────────────────────────────────────── */
|
|
.quiz-sidebar-panel {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 14px;
|
|
position: sticky;
|
|
top: 20px;
|
|
}
|
|
|
|
/* Timer */
|
|
.timer-card {
|
|
background: #fff;
|
|
border: 1.5px solid #e2e8f0;
|
|
border-radius: 18px;
|
|
padding: 18px 20px;
|
|
text-align: center;
|
|
}
|
|
.timer-label {
|
|
font-size: 10px;
|
|
font-weight: 700;
|
|
color: #94a3b8;
|
|
text-transform: uppercase;
|
|
letter-spacing: 1px;
|
|
margin-bottom: 4px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
gap: 6px;
|
|
}
|
|
.timer-label img { width: 13px; height: 13px; object-fit: contain; }
|
|
|
|
/* Baris total durasi (kecil, di bawah label) */
|
|
.timer-total-info {
|
|
font-size: 11px;
|
|
color: #a78bfa;
|
|
font-weight: 600;
|
|
margin-bottom: 8px;
|
|
}
|
|
|
|
.timer-display {
|
|
font-size: 34px;
|
|
font-weight: 800;
|
|
color: #1e293b;
|
|
letter-spacing: 3px;
|
|
font-variant-numeric: tabular-nums;
|
|
transition: color 0.3s;
|
|
}
|
|
.timer-display.warning { color: #d97706; }
|
|
.timer-display.danger { color: #dc2626; animation: heartbeat 0.8s ease-in-out infinite; }
|
|
@keyframes heartbeat {
|
|
0%, 100% { transform: scale(1); }
|
|
50% { transform: scale(1.06); }
|
|
}
|
|
.timer-bar-wrap {
|
|
background: #e2e8f0;
|
|
border-radius: 99px;
|
|
height: 5px;
|
|
margin-top: 10px;
|
|
overflow: hidden;
|
|
}
|
|
.timer-bar-fill {
|
|
height: 100%;
|
|
background: linear-gradient(90deg, #22c55e, #667eea);
|
|
border-radius: 99px;
|
|
transition: width 1s linear, background 0.3s;
|
|
}
|
|
.timer-bar-fill.warning { background: linear-gradient(90deg, #f59e0b, #d97706); }
|
|
.timer-bar-fill.danger { background: linear-gradient(90deg, #ef4444, #dc2626); }
|
|
|
|
/* Nav card */
|
|
.nav-card {
|
|
background: #fff;
|
|
border: 1.5px solid #e2e8f0;
|
|
border-radius: 18px;
|
|
padding: 18px 20px;
|
|
}
|
|
.nav-card-label {
|
|
font-size: 10px; font-weight: 700;
|
|
color: #94a3b8;
|
|
text-transform: uppercase;
|
|
letter-spacing: 1px;
|
|
margin-bottom: 12px;
|
|
display: flex; align-items: center; gap: 6px;
|
|
}
|
|
.nav-card-label img { width: 13px; height: 13px; object-fit: contain; }
|
|
.nav-summary {
|
|
display: grid;
|
|
grid-template-columns: 1fr 1fr;
|
|
gap: 8px; margin-bottom: 14px;
|
|
}
|
|
.summary-item {
|
|
text-align: center;
|
|
background: #f8fafc;
|
|
border: 1px solid #e2e8f0;
|
|
border-radius: 10px;
|
|
padding: 8px 4px;
|
|
}
|
|
.summary-num { font-size: 22px; font-weight: 800; }
|
|
.summary-label { font-size: 10px; color: #94a3b8; font-weight: 500; margin-top: 2px; }
|
|
.summary-item.answered-sum .summary-num { color: #22c55e; }
|
|
.summary-item.unanswered-sum .summary-num { color: #94a3b8; }
|
|
.nav-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(5, 1fr);
|
|
gap: 7px; margin-bottom: 14px;
|
|
}
|
|
.nav-btn {
|
|
aspect-ratio: 1;
|
|
border-radius: 9px;
|
|
border: 1.5px solid #e2e8f0;
|
|
background: #f8fafc;
|
|
font-size: 12px; font-weight: 700;
|
|
color: #64748b; cursor: pointer;
|
|
display: flex; align-items: center; justify-content: center;
|
|
transition: all 0.15s;
|
|
font-family: 'Poppins', sans-serif;
|
|
}
|
|
.nav-btn:hover { border-color: #667eea; background: #f0eeff; color: #667eea; }
|
|
.nav-btn.active { background: #667eea; border-color: #667eea; color: #fff; }
|
|
.nav-btn.answered { background: #dcfce7; border-color: #22c55e; color: #16a34a; }
|
|
.nav-btn.active.answered { background: #22c55e; border-color: #22c55e; color: #fff; }
|
|
.nav-legend {
|
|
display: flex; flex-direction: column;
|
|
gap: 5px; padding-top: 12px;
|
|
border-top: 1px solid #f1f5f9;
|
|
}
|
|
.legend-item {
|
|
display: flex; align-items: center;
|
|
gap: 7px; font-size: 11px; color: #64748b;
|
|
}
|
|
.legend-dot { width: 13px; height: 13px; border-radius: 4px; flex-shrink: 0; }
|
|
.legend-dot.current { background: #667eea; }
|
|
.legend-dot.done { background: #dcfce7; border: 1.5px solid #22c55e; }
|
|
.legend-dot.undone { background: #f8fafc; border: 1.5px solid #e2e8f0; }
|
|
|
|
@media (max-width: 768px) {
|
|
.quiz-page { grid-template-columns: 1fr; }
|
|
.quiz-sidebar-panel { position: static; order: -1; }
|
|
.nav-grid { grid-template-columns: repeat(8, 1fr); }
|
|
}
|
|
</style>
|
|
@endpush
|
|
|
|
@section('content')
|
|
|
|
<div class="quiz-page">
|
|
|
|
{{-- KOLOM KIRI --}}
|
|
<div class="quiz-main">
|
|
|
|
<div class="quiz-header">
|
|
<div class="quiz-title">
|
|
<img src="{{ asset('images/icon/siswac/piala.png') }}" alt="">
|
|
{{ $challenge->judul_challenge }}
|
|
</div>
|
|
<div class="quiz-meta">
|
|
<span>
|
|
<img src="{{ asset('images/icon/siswac/buku1.png') }}" alt="">
|
|
{{ $challenge->soal->count() }} Soal
|
|
</span>
|
|
<span>
|
|
<img src="{{ asset('images/icon/siswac/star.png') }}" alt="">
|
|
{{ $challenge->exp }} EXP
|
|
</span>
|
|
<span>
|
|
<img src="{{ asset('images/icon/siswac/alarm.png') }}" alt="">
|
|
Tenggat: {{ \Carbon\Carbon::parse($challenge->tenggat_waktu)->format('d M Y, H:i') }}
|
|
</span>
|
|
{{-- Badge durasi --}}
|
|
@if($challenge->durasi_pengerjaan)
|
|
<span class="durasi-meta-badge">
|
|
⏱ {{ $challenge->durasi_pengerjaan }} menit pengerjaan
|
|
</span>
|
|
@endif
|
|
</div>
|
|
</div>
|
|
|
|
<div class="progress-card">
|
|
<span class="progress-label" id="progressLabel">
|
|
Soal 1 dari {{ $challenge->soal->count() }}
|
|
</span>
|
|
<div class="progress-bar-wrap">
|
|
<div class="progress-bar-fill" id="progressBar"
|
|
style="width: {{ round(1 / $challenge->soal->count() * 100) }}%">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<form action="{{ route('siswa.challenge.submit', $challenge->id_challenge) }}"
|
|
method="POST" id="quizForm">
|
|
@csrf
|
|
|
|
@foreach($challenge->soal as $i => $soal)
|
|
<div class="soal-card {{ $i === 0 ? 'active' : '' }}" id="soal-{{ $i }}">
|
|
<span class="soal-number">Soal {{ $i + 1 }}</span>
|
|
<p class="soal-pertanyaan">{{ $soal->pertanyaan }}</p>
|
|
<div class="opsi-list">
|
|
@foreach(['A','B','C','D'] as $opsi)
|
|
@php $key = 'opsi_' . strtolower($opsi); @endphp
|
|
<label class="opsi-item" id="label-{{ $i }}-{{ $opsi }}"
|
|
onclick="pilihJawaban({{ $i }}, '{{ $opsi }}', {{ $soal->id_soal }})">
|
|
<input type="radio" name="jawaban[{{ $soal->id_soal }}]"
|
|
value="{{ $opsi }}" id="radio-{{ $i }}-{{ $opsi }}">
|
|
<span class="opsi-label-circle">{{ $opsi }}</span>
|
|
<span>{{ $soal->$key }}</span>
|
|
</label>
|
|
@endforeach
|
|
</div>
|
|
</div>
|
|
@endforeach
|
|
|
|
<div class="warning-box" id="warningBox">
|
|
<img src="{{ asset('images/icon/siswac/alert.png') }}" alt="">
|
|
Masih ada <span id="warningCount"></span> soal yang belum dijawab. Yakin ingin submit?
|
|
</div>
|
|
|
|
<div class="nav-buttons-card">
|
|
<button type="button" class="btn-nav btn-prev" id="btnPrev"
|
|
onclick="prevSoal()" disabled>← Sebelumnya</button>
|
|
<button type="button" class="btn-nav btn-next" id="btnNext"
|
|
onclick="nextSoal()">Selanjutnya →</button>
|
|
<button type="submit" class="btn-nav btn-submit" id="btnSubmit"
|
|
onclick="return konfirmasiSubmit()">
|
|
<img src="{{ asset('images/icon/siswac/target.png') }}" class="target-selesai" alt="Submit">
|
|
Selesai & Submit
|
|
</button>
|
|
</div>
|
|
</form>
|
|
|
|
</div>{{-- /quiz-main --}}
|
|
|
|
|
|
{{-- KOLOM KANAN --}}
|
|
<div class="quiz-sidebar-panel">
|
|
|
|
<div class="timer-card">
|
|
<div class="timer-label">
|
|
<img src="{{ asset('images/icon/siswac/alarm.png') }}" alt="">
|
|
Sisa Waktu
|
|
</div>
|
|
{{-- Info total durasi --}}
|
|
@if($challenge->durasi_pengerjaan)
|
|
<div class="timer-total-info">
|
|
dari {{ $challenge->durasi_pengerjaan }} menit
|
|
</div>
|
|
@endif
|
|
<div class="timer-display" id="timerDisplay">--:--</div>
|
|
<div class="timer-bar-wrap">
|
|
<div class="timer-bar-fill" id="timerBar" style="width:100%"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="nav-card">
|
|
<div class="nav-card-label">
|
|
<img src="{{ asset('images/icon/siswac/buku1.png') }}" alt="">
|
|
Navigasi Soal
|
|
</div>
|
|
<div class="nav-summary">
|
|
<div class="summary-item answered-sum">
|
|
<div class="summary-num" id="summaryAnswered">0</div>
|
|
<div class="summary-label">Terjawab</div>
|
|
</div>
|
|
<div class="summary-item unanswered-sum">
|
|
<div class="summary-num" id="summaryUnanswered">{{ $challenge->soal->count() }}</div>
|
|
<div class="summary-label">Belum</div>
|
|
</div>
|
|
</div>
|
|
<div class="nav-grid" id="navGrid">
|
|
@foreach($challenge->soal as $i => $soal)
|
|
<button type="button"
|
|
class="nav-btn {{ $i === 0 ? 'active' : '' }}"
|
|
id="navBtn-{{ $i }}"
|
|
onclick="goToSoal({{ $i }})">
|
|
{{ $i + 1 }}
|
|
</button>
|
|
@endforeach
|
|
</div>
|
|
<div class="nav-legend">
|
|
<div class="legend-item"><div class="legend-dot current"></div><span>Soal aktif</span></div>
|
|
<div class="legend-item"><div class="legend-dot done"></div><span>Sudah dijawab</span></div>
|
|
<div class="legend-item"><div class="legend-dot undone"></div><span>Belum dijawab</span></div>
|
|
</div>
|
|
</div>
|
|
|
|
</div>{{-- /quiz-sidebar-panel --}}
|
|
|
|
</div>{{-- /quiz-page --}}
|
|
|
|
@endsection
|
|
|
|
@push('scripts')
|
|
<script>
|
|
const totalSoal = {{ $challenge->soal->count() }};
|
|
let currentSoal = 0;
|
|
let jawaban = {};
|
|
|
|
// ═══════════════════════════════════════════════════════════════════
|
|
// TIMER — menggunakan durasi_pengerjaan (menit) dari database
|
|
//
|
|
// Logika:
|
|
// 1. Jika ada durasi_pengerjaan → hitung mundur dari durasi tsb
|
|
// 2. Jika tidak ada durasi_pengerjaan (null) → fallback ke tenggat
|
|
// 3. Ambil waktu mulai dari sessionStorage agar konsisten
|
|
// jika halaman di-refresh (timer tidak reset)
|
|
// ═══════════════════════════════════════════════════════════════════
|
|
@if($challenge->durasi_pengerjaan)
|
|
// Durasi dalam detik
|
|
const DURASI_DETIK = {{ (int)$challenge->durasi_pengerjaan * 60 }};
|
|
|
|
// Simpan waktu mulai ke sessionStorage agar tidak reset saat refresh
|
|
const SESSION_KEY = 'quiz_start_{{ $challenge->id_challenge }}';
|
|
let startTime = parseInt(sessionStorage.getItem(SESSION_KEY) || '0');
|
|
if (!startTime) {
|
|
startTime = Date.now();
|
|
sessionStorage.setItem(SESSION_KEY, startTime);
|
|
}
|
|
|
|
// Hitung sisa detik berdasarkan waktu mulai
|
|
const totalDetik = DURASI_DETIK;
|
|
let sisaDetik = Math.max(0, DURASI_DETIK - Math.floor((Date.now() - startTime) / 1000));
|
|
|
|
@else
|
|
// Fallback: pakai tenggat waktu
|
|
const tenggatMs = {{ \Carbon\Carbon::parse($challenge->tenggat_waktu)->timestamp }} * 1000;
|
|
const totalDetik = Math.max(0, Math.floor((tenggatMs - Date.now()) / 1000));
|
|
let sisaDetik = totalDetik;
|
|
@endif
|
|
|
|
const elTimer = document.getElementById('timerDisplay');
|
|
const elTimerBar = document.getElementById('timerBar');
|
|
|
|
function formatWaktu(s) {
|
|
if (s <= 0) return '00:00';
|
|
const j = Math.floor(s / 3600);
|
|
const m = Math.floor((s % 3600) / 60);
|
|
const d = s % 60;
|
|
const mm = String(m).padStart(2, '0');
|
|
const ss = String(d).padStart(2, '0');
|
|
if (j > 0) return `${String(j).padStart(2,'0')}:${mm}:${ss}`;
|
|
return `${mm}:${ss}`;
|
|
}
|
|
|
|
function updateTimer() {
|
|
elTimer.textContent = formatWaktu(sisaDetik);
|
|
|
|
const pct = totalDetik > 0 ? (sisaDetik / totalDetik) * 100 : 0;
|
|
elTimerBar.style.width = pct + '%';
|
|
|
|
if (sisaDetik <= 60) {
|
|
elTimer.className = 'timer-display danger';
|
|
elTimerBar.className = 'timer-bar-fill danger';
|
|
} else if (sisaDetik <= Math.floor(totalDetik * 0.25)) {
|
|
// Kuning saat sisa ≤ 25% dari total durasi
|
|
elTimer.className = 'timer-display warning';
|
|
elTimerBar.className = 'timer-bar-fill warning';
|
|
} else {
|
|
elTimer.className = 'timer-display';
|
|
elTimerBar.className = 'timer-bar-fill';
|
|
}
|
|
|
|
if (sisaDetik <= 0) {
|
|
clearInterval(timerInterval);
|
|
elTimer.textContent = '00:00';
|
|
@if($challenge->durasi_pengerjaan)
|
|
sessionStorage.removeItem('quiz_start_{{ $challenge->id_challenge }}');
|
|
@endif
|
|
alert('Waktu habis! Jawaban kamu akan otomatis dikumpulkan.');
|
|
document.getElementById('quizForm').submit();
|
|
return;
|
|
}
|
|
sisaDetik--;
|
|
}
|
|
|
|
updateTimer();
|
|
const timerInterval = setInterval(updateTimer, 1000);
|
|
|
|
|
|
// ═══════════════════════════════════════════════════════════════════
|
|
// NAVIGASI SOAL
|
|
// ═══════════════════════════════════════════════════════════════════
|
|
function goToSoal(index) {
|
|
document.getElementById(`soal-${currentSoal}`).classList.remove('active');
|
|
document.getElementById(`navBtn-${currentSoal}`).classList.remove('active');
|
|
currentSoal = index;
|
|
document.getElementById(`soal-${currentSoal}`).classList.add('active');
|
|
document.getElementById(`navBtn-${currentSoal}`).classList.add('active');
|
|
updateNav();
|
|
updateProgress();
|
|
}
|
|
|
|
function nextSoal() { if (currentSoal < totalSoal - 1) goToSoal(currentSoal + 1); }
|
|
function prevSoal() { if (currentSoal > 0) goToSoal(currentSoal - 1); }
|
|
|
|
function pilihJawaban(soalIndex, opsi, idSoal) {
|
|
jawaban[soalIndex] = opsi;
|
|
['A','B','C','D'].forEach(o => {
|
|
document.getElementById(`label-${soalIndex}-${o}`)?.classList.remove('selected');
|
|
});
|
|
document.getElementById(`label-${soalIndex}-${opsi}`)?.classList.add('selected');
|
|
document.getElementById(`radio-${soalIndex}-${opsi}`).checked = true;
|
|
document.getElementById(`navBtn-${soalIndex}`)?.classList.add('answered');
|
|
updateProgress();
|
|
updateSummary();
|
|
}
|
|
|
|
function updateNav() {
|
|
const btnPrev = document.getElementById('btnPrev');
|
|
const btnNext = document.getElementById('btnNext');
|
|
const btnSubmit = document.getElementById('btnSubmit');
|
|
btnPrev.disabled = (currentSoal === 0);
|
|
if (currentSoal === totalSoal - 1) {
|
|
btnNext.style.display = 'none';
|
|
btnSubmit.style.display = 'inline-flex';
|
|
} else {
|
|
btnNext.style.display = 'inline-flex';
|
|
btnSubmit.style.display = 'none';
|
|
}
|
|
}
|
|
|
|
function updateProgress() {
|
|
const answered = Object.keys(jawaban).length;
|
|
const pct = Math.round(((currentSoal + 1) / totalSoal) * 100);
|
|
document.getElementById('progressBar').style.width = pct + '%';
|
|
document.getElementById('progressLabel').textContent =
|
|
`Soal ${currentSoal + 1} dari ${totalSoal} · ${answered} terjawab`;
|
|
}
|
|
|
|
function updateSummary() {
|
|
const answered = Object.keys(jawaban).length;
|
|
document.getElementById('summaryAnswered').textContent = answered;
|
|
document.getElementById('summaryUnanswered').textContent = totalSoal - answered;
|
|
}
|
|
|
|
function konfirmasiSubmit() {
|
|
const belum = totalSoal - Object.keys(jawaban).length;
|
|
if (belum > 0) {
|
|
document.getElementById('warningCount').textContent = `${belum} soal`;
|
|
document.getElementById('warningBox').style.display = 'flex';
|
|
return confirm(`Masih ada ${belum} soal yang belum dijawab. Yakin ingin submit?`);
|
|
}
|
|
@if($challenge->durasi_pengerjaan)
|
|
// Bersihkan session timer setelah submit
|
|
sessionStorage.removeItem('quiz_start_{{ $challenge->id_challenge }}');
|
|
@endif
|
|
return confirm('Yakin ingin submit jawaban? Jawaban tidak bisa diubah setelah submit.');
|
|
}
|
|
|
|
// Init
|
|
updateNav();
|
|
updateProgress();
|
|
updateSummary();
|
|
</script>
|
|
@endpush |