https://share.gemini.google/Tzy2486AYpjD
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>E-Learning Informatika - Semester 1 & 2</title>
<!-- Tailwind CSS -->
<script src="https://cdn.tailwindcss.com"></script>
<!-- FontAwesome Icons -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<!-- Google Fonts: Plus Jakarta Sans -->
<link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@300;400;500;600;700;800&display=swap" rel="stylesheet">
<script>
tailwind.config = {
theme: {
extend: {
fontFamily: {
sans: ['Plus Jakarta Sans', 'sans-serif'],
},
colors: {
brand: {
50: '#f0f7ff',
100: '#e0effe',
500: '#3b82f6',
600: '#2563eb',
700: '#1d4ed8',
900: '#1e3a8a',
},
cyber: {
dark: '#0f172a',
card: '#1e293b',
accent: '#10b981'
}
}
}
}
}
</script>
<style>
.glass-panel {
background: rgba(30, 41, 59, 0.7);
backdrop-filter: blur(12px);
border: 1px solid rgba(255, 255, 255, 0.08);
}
.custom-scrollbar::-webkit-scrollbar {
width: 6px;
height: 6px;
}
.custom-scrollbar::-webkit-scrollbar-track {
background: rgba(15, 23, 42, 0.5);
}
.custom-scrollbar::-webkit-scrollbar-thumb {
background: rgba(59, 130, 246, 0.5);
border-radius: 3px;
}
</style>
</head>
<body class="bg-[#0b0f19] text-slate-100 min-h-screen font-sans antialiased selection:bg-brand-500 selection:text-white">
<!-- GATEKEEPER LOCK SCREEN OVERLAY -->
<div id="gatekeeperOverlay" class="fixed inset-0 z-50 bg-[#070a13] flex flex-col items-center justify-center p-4 transition-all duration-500">
<div class="absolute inset-0 bg-[radial-gradient(circle_at_50%_40%,rgba(59,130,246,0.15),transparent_60%)]"></div>
<div class="absolute w-72 h-72 bg-emerald-500/5 rounded-full blur-3xl top-10 left-10"></div>
<div class="relative z-10 w-full max-w-md bg-slate-900/80 backdrop-blur-xl border border-slate-800 rounded-3xl p-6 sm:p-8 shadow-2xl text-center space-y-6">
<div class="w-16 h-16 bg-gradient-to-tr from-brand-600 to-emerald-500 rounded-2xl flex items-center justify-center mx-auto shadow-lg shadow-brand-500/20">
<i class="fa-solid fa-laptop-code text-white text-2xl"></i>
</div>
<div>
<h2 class="text-2xl font-extrabold tracking-tight bg-gradient-to-r from-white via-slate-200 to-brand-400 bg-clip-text text-transparent">INFO-LEARN</h2>
<p class="text-xs text-slate-400 mt-1 uppercase tracking-widest font-bold">Portal Informatika Interaktif</p>
</div>
<!-- Role Selector -->
<div class="space-y-2 text-left">
<label class="block text-xs font-semibold text-slate-400 uppercase tracking-wider">Pilih Peran Masuk:</label>
<div class="grid grid-cols-2 gap-3">
<button type="button" id="roleBtnSiswa" onclick="selectGatekeeperRole('siswa')" class="flex flex-col items-center justify-center p-4 rounded-2xl border transition-all duration-300 bg-brand-500/10 border-brand-500/50 text-white">
<i class="fa-solid fa-user-graduate text-xl mb-1 text-brand-400"></i>
<span class="text-xs font-bold">Siswa</span>
</button>
<button type="button" id="roleBtnGuru" onclick="selectGatekeeperRole('guru')" class="flex flex-col items-center justify-center p-4 rounded-2xl border transition-all duration-300 bg-slate-950 border-slate-800 text-slate-400 hover:border-slate-700 hover:text-slate-200">
<i class="fa-solid fa-user-tie text-xl mb-1 text-slate-500"></i>
<span class="text-xs font-bold">Guru</span>
</button>
</div>
</div>
<!-- Password Input -->
<div class="space-y-2 text-left">
<label id="gatekeeperLabel" class="block text-xs font-semibold text-slate-400 uppercase tracking-wider">Kode Akses Siswa:</label>
<input type="password" id="gatekeeperCodeInput" onkeydown="if(event.key === 'Enter') checkGatekeeperCode()" class="w-full px-4 py-3 bg-slate-950 border border-slate-800 rounded-xl outline-none focus:border-brand-500 text-slate-100 text-center font-bold tracking-widest text-xl placeholder:tracking-normal placeholder:font-normal" placeholder="12345">
<p id="gatekeeperError" class="text-xs text-red-400 hidden flex items-center justify-center gap-1.5 mt-2">
<i class="fa-solid fa-circle-exclamation"></i> Kode akses tidak valid! Silakan periksa kembali.
</p>
</div>
<div class="pt-2">
<button onclick="checkGatekeeperCode()" class="w-full py-3 bg-gradient-to-r from-brand-600 to-brand-700 hover:from-brand-500 hover:to-brand-600 text-white font-bold rounded-xl transition shadow-lg shadow-brand-500/25">
Mulai Masuk <i class="fa-solid fa-arrow-right ml-1"></i>
</button>
</div>
<div class="text-[10px] text-slate-500">
Akses Siswa: <span class="font-mono text-slate-400">12345</span> | Akses Guru: <span class="font-mono text-slate-400">5678</span>
</div>
</div>
</div>
<!-- App Container -->
<div class="flex flex-col min-h-screen">
<!-- Header / Navigation -->
<header class="sticky top-0 z-40 glass-panel border-b border-slate-800">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 h-20 flex items-center justify-between">
<div class="flex items-center gap-3">
<div class="w-11 h-11 bg-gradient-to-tr from-brand-600 to-emerald-500 rounded-xl flex items-center justify-center shadow-lg shadow-brand-500/20">
<i class="fa-solid fa-laptop-code text-white text-xl"></i>
</div>
<div>
<h1 class="font-extrabold text-lg sm:text-xl tracking-tight bg-gradient-to-r from-white via-slate-200 to-brand-400 bg-clip-text text-transparent">
INFO-LEARN
</h1>
<p class="text-xs text-slate-400 font-medium">Platform Informatika Interaktif</p>
</div>
</div>
<div class="flex items-center gap-4">
<!-- Dynamic Role Switcher (Siswa/Guru Switcher) -->
<div class="flex items-center gap-2 bg-slate-950 p-1 rounded-2xl border border-slate-800">
<button id="headerBtnSiswa" onclick="switchHeaderRole('siswa')" class="px-3.5 py-1.5 rounded-xl text-xs font-bold transition-all duration-300 flex items-center gap-1.5 bg-brand-600 text-white shadow-md">
<i class="fa-solid fa-user-graduate"></i> Siswa
</button>
<button id="headerBtnGuru" onclick="switchHeaderRole('guru')" class="px-3.5 py-1.5 rounded-xl text-xs font-bold transition-all duration-300 flex items-center gap-1.5 text-slate-400 hover:text-slate-200">
<i class="fa-solid fa-user-tie"></i> Guru
</button>
</div>
<!-- Progres Global Ring -->
<div class="hidden md:flex items-center gap-3 bg-slate-900/60 py-1.5 px-3.5 rounded-xl border border-slate-800">
<div class="text-right">
<span class="block text-xs text-slate-400">Selesai Belajar</span>
<span id="globalPercentText" class="text-sm font-bold text-emerald-400">0%</span>
</div>
<div class="relative w-10 h-10">
<svg class="w-full h-full transform -rotate-90">
<circle cx="20" cy="20" r="16" stroke="currentColor" stroke-width="4" class="text-slate-800" fill="transparent" />
<circle id="progressCircle" cx="20" cy="20" r="16" stroke="currentColor" stroke-width="4" class="text-emerald-500" fill="transparent"
stroke-dasharray="100.5" stroke-dashoffset="100.5" stroke-linecap="round" />
</svg>
</div>
</div>
</div>
</div>
</header>
<!-- Main Content Section -->
<main class="flex-grow max-w-7xl w-full mx-auto px-4 sm:px-6 lg:px-8 py-8">
<!-- Hero Banner -->
<div class="relative overflow-hidden rounded-3xl bg-gradient-to-r from-slate-900 via-brand-950 to-slate-900 border border-slate-800 p-6 sm:p-10 mb-6 shadow-2xl">
<div class="absolute inset-0 bg-[radial-gradient(circle_at_30%_30%,rgba(59,130,246,0.1),transparent_50%)]"></div>
<div class="absolute -right-10 -bottom-10 w-48 h-48 bg-emerald-500/10 rounded-full blur-3xl"></div>
<div class="relative z-10 grid md:grid-cols-3 gap-6 items-center">
<div class="md:col-span-2 space-y-3">
<span class="inline-flex items-center gap-1.5 py-1 px-3 rounded-full text-xs font-semibold bg-brand-500/10 text-brand-400 border border-brand-500/20">
<span class="w-2 h-2 rounded-full bg-brand-500 animate-pulse"></span>
Tahun Ajaran Aktif
</span>
<h2 class="text-2xl sm:text-4xl font-extrabold tracking-tight text-white leading-tight">
E-Learning Informatika <br class="hidden sm:inline">Kurikulum Merdeka 1 Semester
</h2>
<p class="text-sm sm:text-base text-slate-300 max-w-xl">
Selamat datang di portal belajar interaktif. Akses modul, tonton materi pembelajaran, uji pemahaman Anda, serta kumpulkan tugas di sini secara instan.
</p>
</div>
<!-- Stats Card -->
<div class="bg-slate-950/60 border border-slate-800 rounded-2xl p-4 sm:p-5 flex flex-col justify-between gap-4">
<div>
<span class="text-xs text-slate-400 block mb-1">PROGRES BELAJAR SISWA</span>
<div class="flex items-baseline gap-2">
<span id="completedCount" class="text-3xl font-extrabold text-white">0</span>
<span class="text-slate-500">/</span>
<span id="totalCount" class="text-lg font-bold text-slate-400">17</span>
<span class="text-xs text-slate-400 ml-1">Sub-Bab selesai</span>
</div>
</div>
<div class="w-full bg-slate-800 rounded-full h-2">
<div id="globalProgressBar" class="bg-gradient-to-r from-brand-500 to-emerald-400 h-2 rounded-full transition-all duration-500" style="width: 0%"></div>
</div>
</div>
</div>
</div>
<!-- Utama Blog / Materi Utama Section (Materi Penting) -->
<div class="bg-gradient-to-r from-slate-900 to-slate-950 border border-slate-800 rounded-2xl p-5 mb-8 flex flex-col sm:flex-row items-center justify-between gap-4 shadow-xl">
<div class="flex items-center gap-4">
<div class="w-12 h-12 bg-amber-500/10 rounded-xl flex items-center justify-center border border-amber-500/20 text-amber-400">
<i class="fa-solid fa-star text-xl animate-pulse"></i>
</div>
<div>
<span class="text-xs font-semibold uppercase tracking-wider text-amber-400 block">Pusat Informasi & Blog Pembelajaran</span>
<h4 class="text-base font-bold text-white">Blog Portal Pembelajaran Informatika Utama</h4>
<p class="text-xs text-slate-400">Gunakan link ini untuk membaca ringkasan materi terlengkap semester ini.</p>
</div>
</div>
<a href="https://suryogarage.blogspot.com/2025/07/informatika.html" target="_blank" class="w-full sm:w-auto text-center px-5 py-3 rounded-xl bg-gradient-to-r from-amber-500 to-orange-500 hover:from-amber-600 hover:to-orange-600 text-slate-950 font-bold text-xs sm:text-sm tracking-wide shadow-lg shadow-amber-500/10 transition">
<i class="fa-solid fa-book-open mr-1.5"></i> Buka Materi Blog Utama
</a>
</div>
<!-- Toolbar & Search -->
<div class="flex flex-col sm:flex-row gap-4 items-center justify-between mb-8">
<!-- Search bar -->
<div class="relative w-full sm:max-w-md">
<span class="absolute inset-y-0 left-0 flex items-center pl-3.5 pointer-events-none text-slate-400">
<i class="fa-solid fa-magnifying-glass"></i>
</span>
<input type="text" id="searchInput" onkeyup="filterContent()" placeholder="Cari materi, video, kuis..."
class="w-full pl-10 pr-4 py-3 bg-slate-900/60 border border-slate-800 rounded-xl focus:ring-2 focus:ring-brand-500 focus:border-transparent outline-none transition text-sm text-slate-200 placeholder:text-slate-500" />
</div>
<!-- Category Filters -->
<div class="flex items-center gap-2 overflow-x-auto w-full sm:w-auto pb-2 sm:pb-0 custom-scrollbar">
<button onclick="filterType('all')" class="type-filter-btn px-4 py-2 rounded-xl text-xs sm:text-sm font-semibold bg-brand-600 text-white shadow-lg shadow-brand-500/20 whitespace-nowrap transition">
Semua Tipe
</button>
<button onclick="filterType('Materi')" class="type-filter-btn px-4 py-2 rounded-xl text-xs sm:text-sm font-medium bg-slate-800 text-slate-400 hover:text-white hover:bg-slate-700 whitespace-nowrap transition">
<i class="fa-regular fa-file-lines text-blue-400 mr-1"></i> Materi (PDF)
</button>
<button onclick="filterType('Video')" class="type-filter-btn px-4 py-2 rounded-xl text-xs sm:text-sm font-medium bg-slate-800 text-slate-400 hover:text-white hover:bg-slate-700 whitespace-nowrap transition">
<i class="fa-regular fa-circle-play text-red-400 mr-1"></i> Video
</button>
<button onclick="filterType('Kuis')" class="type-filter-btn px-4 py-2 rounded-xl text-xs sm:text-sm font-medium bg-slate-800 text-slate-400 hover:text-white hover:bg-slate-700 whitespace-nowrap transition">
<i class="fa-solid fa-laptop-code text-amber-400 mr-1"></i> Kuis/Tugas
</button>
</div>
</div>
<!-- Teacher Control Panel (Hanya muncul jika Mode Guru Aktif) -->
<div id="teacherPanel" class="hidden bg-slate-900 border border-amber-500/30 rounded-3xl p-6 mb-8 relative">
<div class="absolute -top-3 left-6 px-3 py-1 bg-amber-500 text-slate-950 font-bold text-xs rounded-full uppercase tracking-wider animate-bounce">
Panel Kontrol Guru
</div>
<div class="flex flex-col gap-6">
<div class="grid md:grid-cols-2 gap-6 items-center border-b border-slate-800/80 pb-6">
<div>
<h3 class="text-lg font-bold text-white mb-2">Kelola Link & Pembelajaran</h3>
<p class="text-sm text-slate-300">
Sebagai guru, Anda dapat menekan tombol edit <i class="fa-solid fa-pen text-amber-400"></i> pada setiap sub-bab di bawah untuk menyematkan link materi atau memeriksa rekap jawaban siswa yang masuk di tabel bawah.
</p>
</div>
<div class="flex flex-wrap gap-3 md:justify-end">
<button onclick="exportData()" class="flex items-center gap-2 px-4 py-2.5 bg-emerald-600 hover:bg-emerald-500 text-white font-semibold rounded-xl text-sm transition shadow-lg shadow-emerald-600/10">
<i class="fa-solid fa-download"></i> Ekspor Link (.json)
</button>
<button onclick="triggerImport()" class="flex items-center gap-2 px-4 py-2.5 bg-slate-800 hover:bg-slate-700 border border-slate-700 text-white font-semibold rounded-xl text-sm transition">
<i class="fa-solid fa-upload"></i> Impor Link (.json)
</button>
<input type="file" id="importFileInput" onchange="importData(event)" class="hidden" accept=".json">
</div>
</div>
<!-- Rekap Jawaban Siswa -->
<div>
<div class="flex flex-col sm:flex-row sm:items-center justify-between gap-4 mb-4">
<div class="flex items-center gap-2">
<span class="p-2 bg-brand-500/10 text-brand-400 rounded-lg">
<i class="fa-solid fa-user-graduate"></i>
</span>
<h3 class="text-base font-bold text-white">Daftar & Rekap Jawaban Siswa</h3>
</div>
<!-- Filter Kelas Guru -->
<div class="flex gap-2">
<select id="teacherClassFilter" onchange="renderSubmissions()" class="px-3 py-1.5 bg-slate-950 border border-slate-800 rounded-lg text-xs font-semibold text-slate-300 focus:border-brand-500 outline-none">
<option value="all">Semua Kelas</option>
<option value="X OTO1">X OTO1</option>
<option value="AV">AV</option>
<option value="MPLB">MPLB</option>
<option value="AKL">AKL</option>
<option value="X OTO2">X OTO2</option>
</select>
</div>
</div>
<!-- Table Rekap Jawaban -->
<div class="overflow-x-auto custom-scrollbar border border-slate-800 rounded-2xl bg-slate-950/40">
<table class="w-full text-left border-collapse">
<thead>
<tr class="border-b border-slate-800 bg-slate-900/60 text-xs font-bold text-slate-400 uppercase tracking-wider">
<th class="p-4">Siswa & Kelas</th>
<th class="p-4">Sub-Bab</th>
<th class="p-4">Tanggal Pengiriman</th>
<th class="p-4">Link Lampiran</th>
<th class="p-4">Status & Nilai</th>
<th class="p-4 text-center">Aksi</th>
</tr>
</thead>
<tbody id="submissionsTableBody" class="divide-y divide-slate-800 text-sm text-slate-300">
<!-- Dynamic rows from js -->
</tbody>
</table>
</div>
</div>
</div>
</div>
<!-- Chapters Grid Container -->
<div class="grid grid-cols-1 lg:grid-cols-2 gap-8" id="chaptersContainer">
<!-- Chapters will be injected dynamically via JavaScript -->
</div>
</main>
<!-- Footer -->
<footer class="mt-auto border-t border-slate-800/80 bg-slate-950 py-8 text-center text-slate-500">
<div class="max-w-7xl mx-auto px-4">
<p class="text-sm">© 2026 E-Learning Informatika. Semua Hak Cipta Dilindungi.</p>
<p class="text-xs text-slate-600 mt-2">Didesain untuk keaktifan belajar siswa secara mandiri & terstruktur.</p>
</div>
</footer>
</div>
<!-- Notification Container -->
<div id="toast" class="fixed bottom-6 right-6 z-50 transform translate-y-12 opacity-0 transition-all duration-300 pointer-events-none">
<div class="flex items-center gap-3 bg-slate-900 border border-brand-500 text-slate-100 py-3.5 px-6 rounded-xl shadow-2xl">
<div class="w-2.5 h-2.5 rounded-full bg-emerald-500 animate-pulse"></div>
<span id="toastMsg" class="text-sm font-semibold">Tautan berhasil disimpan!</span>
</div>
</div>
<!-- Edit Link Modal (Guru Mode) -->
<div id="editModal" class="fixed inset-0 z-50 hidden bg-slate-950/80 backdrop-filter backdrop-blur-sm flex items-center justify-center p-4">
<div class="bg-slate-900 border border-slate-800 w-full max-w-lg rounded-3xl overflow-hidden shadow-2xl relative">
<div class="p-6 border-b border-slate-800 flex items-center justify-between">
<h3 class="text-lg font-bold text-white flex items-center gap-2">
<i class="fa-solid fa-edit text-amber-500"></i> Edit Materi Pembelajaran
</h3>
<button onclick="closeEditModal()" class="text-slate-400 hover:text-white transition">
<i class="fa-solid fa-xmark text-xl"></i>
</button>
</div>
<div class="p-6 space-y-4">
<div>
<label class="block text-xs font-semibold text-slate-400 uppercase tracking-wider mb-1.5">Nama Sub-Bab</label>
<input type="text" id="editSubName" class="w-full px-4 py-2.5 bg-slate-950 border border-slate-800 rounded-xl outline-none focus:border-brand-500 text-slate-100 text-sm" placeholder="Contoh: Algoritma Pencarian">
</div>
<div class="grid grid-cols-2 gap-4">
<div>
<label class="block text-xs font-semibold text-slate-400 uppercase tracking-wider mb-1.5">Tipe Materi</label>
<select id="editSubCategory" class="w-full px-4 py-2.5 bg-slate-950 border border-slate-800 rounded-xl outline-none focus:border-brand-500 text-slate-100 text-sm">
<option value="Materi">Materi (PDF/Doc)</option>
<option value="Video">Video Tutorial</option>
<option value="Kuis">Kuis & Tugas</option>
</select>
</div>
<div>
<label class="block text-xs font-semibold text-slate-400 uppercase tracking-wider mb-1.5">Estimasi Waktu</label>
<input type="text" id="editSubDuration" class="w-full px-4 py-2.5 bg-slate-950 border border-slate-800 rounded-xl outline-none focus:border-brand-500 text-slate-100 text-sm" placeholder="Contoh: 15 Menit">
</div>
</div>
<div>
<label class="block text-xs font-semibold text-slate-400 uppercase tracking-wider mb-1.5">Tautan / Link Sumber</label>
<input type="text" id="editSubLink" class="w-full px-4 py-2.5 bg-slate-950 border border-slate-800 rounded-xl outline-none focus:border-brand-500 text-slate-100 text-sm" placeholder="Masukkan Link Drive/YouTube/Wordwall">
</div>
<div>
<label class="block text-xs font-semibold text-slate-400 uppercase tracking-wider mb-1.5">Deskripsi Singkat / Instruksi Tugas</label>
<textarea id="editSubDesc" rows="3" class="w-full px-4 py-2.5 bg-slate-950 border border-slate-800 rounded-xl outline-none focus:border-brand-500 text-slate-100 text-sm" placeholder="Berikan instruksi belajar untuk siswa..."></textarea>
</div>
</div>
<div class="p-6 bg-slate-950 border-t border-slate-800 flex justify-end gap-3">
<button onclick="closeEditModal()" class="px-4 py-2 text-sm font-semibold text-slate-400 hover:text-white transition">
Batal
</button>
<button onclick="saveEditData()" class="px-5 py-2 text-sm font-semibold bg-brand-600 hover:bg-brand-500 text-white rounded-xl transition">
Simpan Perubahan
</button>
</div>
</div>
</div>
<!-- Kirim Tugas Modal (Siswa View) -->
<div id="submitTaskModal" class="fixed inset-0 z-50 hidden bg-slate-950/80 backdrop-filter backdrop-blur-sm flex items-center justify-center p-4">
<div class="bg-slate-900 border border-slate-800 w-full max-w-lg rounded-3xl overflow-hidden shadow-2xl relative">
<div class="p-6 border-b border-slate-800 flex items-center justify-between">
<h3 class="text-lg font-bold text-white flex items-center gap-2">
<i class="fa-solid fa-paper-plane text-brand-400"></i> Kirim Jawaban & Tugas Siswa
</h3>
<button onclick="closeSubmitTaskModal()" class="text-slate-400 hover:text-white transition">
<i class="fa-solid fa-xmark text-xl"></i>
</button>
</div>
<div class="p-6 space-y-4">
<div class="p-3 bg-slate-950 border border-slate-800 rounded-2xl">
<span class="text-[10px] font-bold text-brand-400 uppercase tracking-wider block">Sub-Bab Tugas</span>
<span id="submitModalSubTitle" class="text-sm font-semibold text-white">Judul Sub-Bab</span>
</div>
<div class="grid grid-cols-2 gap-4">
<div>
<label class="block text-xs font-semibold text-slate-400 uppercase tracking-wider mb-1.5">Nama Lengkap</label>
<input type="text" id="studentNameInput" class="w-full px-4 py-2.5 bg-slate-950 border border-slate-800 rounded-xl outline-none focus:border-brand-500 text-slate-100 text-sm" placeholder="Nama Anda">
</div>
<div>
<label class="block text-xs font-semibold text-slate-400 uppercase tracking-wider mb-1.5">Kelas</label>
<select id="studentClassInput" class="w-full px-4 py-2.5 bg-slate-950 border border-slate-800 rounded-xl outline-none focus:border-brand-500 text-slate-100 text-sm">
<option value="X OTO1">X OTO1</option>
<option value="AV">AV</option>
<option value="MPLB">MPLB</option>
<option value="AKL">AKL</option>
<option value="X OTO2">X OTO2</option>
</select>
</div>
</div>
<div>
<label class="block text-xs font-semibold text-slate-400 uppercase tracking-wider mb-1.5">Teks Jawaban / Keterangan</label>
<textarea id="studentNotesInput" rows="3" class="w-full px-4 py-2.5 bg-slate-950 border border-slate-800 rounded-xl outline-none focus:border-brand-500 text-slate-100 text-sm" placeholder="Tuliskan esai jawaban atau ringkasan pengerjaan tugas..."></textarea>
</div>
<div>
<label class="block text-xs font-semibold text-slate-400 uppercase tracking-wider mb-1.5">Link Lampiran Tugas (Opsional)</label>
<input type="text" id="studentLinkInput" class="w-full px-4 py-2.5 bg-slate-950 border border-slate-800 rounded-xl outline-none focus:border-brand-500 text-slate-100 text-sm" placeholder="https://drive.google.com/contoh-tugas-anda">
<span class="text-[10px] text-slate-500 mt-1 block">Sertakan link Google Drive, Docs, Wordwall, atau blog hasil pengerjaan Anda.</span>
</div>
</div>
<div class="p-6 bg-slate-950 border-t border-slate-800 flex justify-end gap-3">
<button onclick="closeSubmitTaskModal()" class="px-4 py-2 text-sm font-semibold text-slate-400 hover:text-white transition">
Batal
</button>
<button onclick="submitStudentTask()" class="px-5 py-2 text-sm font-semibold bg-brand-600 hover:bg-brand-500 text-white rounded-xl transition">
Kirim Jawaban
</button>
</div>
</div>
</div>
<!-- Penilaian Guru Modal -->
<div id="gradeModal" class="fixed inset-0 z-50 hidden bg-slate-950/80 backdrop-filter backdrop-blur-sm flex items-center justify-center p-4">
<div class="bg-slate-900 border border-slate-800 w-full max-w-md rounded-3xl overflow-hidden shadow-2xl relative">
<div class="p-6 border-b border-slate-800 flex items-center justify-between">
<h3 class="text-lg font-bold text-white flex items-center gap-2">
<i class="fa-solid fa-graduation-cap text-emerald-400"></i> Form Penilaian Guru
</h3>
<button onclick="closeGradeModal()" class="text-slate-400 hover:text-white transition">
<i class="fa-solid fa-xmark text-xl"></i>
</button>
</div>
<div class="p-6 space-y-4">
<div class="p-4 bg-slate-950 border border-slate-800 rounded-2xl space-y-2">
<div class="flex justify-between items-center text-xs">
<span id="gradeModalClass" class="px-2.5 py-0.5 rounded-full bg-slate-800 text-slate-300 font-bold">X-A</span>
<span id="gradeModalDate" class="text-slate-500">2026-06-26</span>
</div>
<h4 id="gradeModalStudentName" class="font-bold text-white text-base">Nama Siswa</h4>
<p id="gradeModalSubName" class="text-xs text-brand-400 font-semibold">Nama Sub-Bab</p>
<div class="mt-3 pt-3 border-t border-slate-800/80">
<span class="text-[10px] font-bold text-slate-500 uppercase block mb-1">Jawaban Siswa:</span>
<p id="gradeModalResponseText" class="text-xs text-slate-300 italic leading-relaxed whitespace-pre-wrap">Isi jawaban...</p>
</div>
</div>
<div>
<label class="block text-xs font-semibold text-slate-400 uppercase tracking-wider mb-1.5">Nilai Tugas (0 - 100)</label>
<input type="number" id="gradeScoreInput" min="0" max="100" class="w-full px-4 py-2.5 bg-slate-950 border border-slate-800 rounded-xl outline-none focus:border-brand-500 text-slate-100 text-sm font-bold text-emerald-400" placeholder="Masukkan Angka">
</div>
<div>
<label class="block text-xs font-semibold text-slate-400 uppercase tracking-wider mb-1.5">Umpan Balik / Catatan Guru</label>
<textarea id="gradeFeedbackInput" rows="3" class="w-full px-4 py-2.5 bg-slate-950 border border-slate-800 rounded-xl outline-none focus:border-brand-500 text-slate-100 text-sm" placeholder="Tulis masukan konstruktif untuk siswa..."></textarea>
</div>
</div>
<div class="p-6 bg-slate-950 border-t border-slate-800 flex justify-end gap-3">
<button onclick="closeGradeModal()" class="px-4 py-2 text-sm font-semibold text-slate-400 hover:text-white transition">
Batal
</button>
<button onclick="saveStudentGrade()" class="px-5 py-2 text-sm font-semibold bg-emerald-600 hover:bg-emerald-500 text-white rounded-xl transition">
Simpan Nilai
</button>
</div>
</div>
</div>
<!-- Password Verifikasi Modal (Saat ganti ke mode guru dari header) -->
<div id="passwordModal" class="fixed inset-0 z-50 hidden bg-slate-950/80 backdrop-filter backdrop-blur-sm flex items-center justify-center p-4">
<div class="bg-slate-900 border border-slate-800 w-full max-w-sm rounded-3xl overflow-hidden shadow-2xl relative">
<div class="p-6 border-b border-slate-800 flex items-center justify-between">
<h3 class="text-lg font-bold text-white flex items-center gap-2">
<i class="fa-solid fa-key text-amber-500"></i> Verifikasi Akses Guru
</h3>
<button onclick="closePasswordModal()" class="text-slate-400 hover:text-white transition">
<i class="fa-solid fa-xmark text-xl"></i>
</button>
</div>
<div class="p-6 space-y-4">
<p class="text-xs text-slate-400 leading-relaxed">Silakan masukkan kata sandi khusus Guru untuk mengakses Panel Kontrol dan mengelola tautan pembelajaran semester ini.</p>
<div>
<label class="block text-xs font-semibold text-slate-400 uppercase tracking-wider mb-1.5">Kata Sandi Guru</label>
<input type="password" id="teacherPasswordInput" onkeydown="if(event.key === 'Enter') verifyTeacherPassword()" class="w-full px-4 py-2.5 bg-slate-950 border border-slate-800 rounded-xl outline-none focus:border-brand-500 text-slate-100 text-center font-bold tracking-widest text-lg" placeholder="••••">
<p id="passwordError" class="text-xs text-red-400 mt-1.5 hidden flex items-center gap-1.5">
<i class="fa-solid fa-circle-exclamation"></i> Kata sandi salah! Hubungi Administrator.
</p>
</div>
</div>
<div class="p-6 bg-slate-950 border-t border-slate-800 flex justify-end gap-3">
<button onclick="closePasswordModal()" class="px-4 py-2 text-sm font-semibold text-slate-400 hover:text-white transition">
Batal
</button>
<button onclick="verifyTeacherPassword()" class="px-5 py-2 text-sm font-semibold bg-amber-500 hover:bg-amber-600 text-slate-950 rounded-xl transition font-bold">
Masuk Mode Guru
</button>
</div>
</div>
</div>
<!-- JavaScript Data and Interaction Logic -->
<script>
// Data Struktur Bab 1 - 5 Kurikulum Merdeka Informatika
let informaticsData = [
{
id: "bab1",
num: "Bab 1",
title: "Berpikir Komputasional",
desc: "Fondasi dalam memecahkan masalah kompleks menggunakan konsep ilmu komputer.",
color: "from-blue-600/20 to-indigo-600/20 border-blue-500/30",
icon: "fa-solid fa-brain",
subs: [
{ id: "1_1", name: "Pencarian (Searching)", cat: "Materi", link: "https://suryogarage.blogspot.com/2025/07/informatika.html", duration: "10 Menit", desc: "Membahas konsep pencarian data secara komprehensif pada portal utama." },
{ id: "1_2", name: "Pengurutan (Sorting)", cat: "Video", link: "https://youtube.com", duration: "15 Menit", desc: "Menjelaskan visualisasi Bubble Sort, Selection Sort, dan Insertion Sort." },
{ id: "1_3", name: "Tumpukan (Stack) & Antrean (Queue)", cat: "Materi", link: "https://suryogarage.blogspot.com/2025/07/informatika.html", duration: "12 Menit", desc: "Memahami struktur penyimpanan LIFO dan FIFO dalam kehidupan nyata." },
{ id: "1_4", name: "Kuis Berpikir Komputasional", cat: "Kuis", link: "https://quizizz.com", duration: "20 Menit", desc: "Asah logikamu lewat tantangan Bebras dan kuis komputasi." }
]
},
{
id: "bab2",
num: "Bab 2",
title: "Teknologi Informasi & Komunikasi",
desc: "Pemanfaatan aplikasi perkantoran terintegrasi dan kolaborasi awan.",
color: "from-emerald-600/20 to-teal-600/20 border-emerald-500/30",
icon: "fa-solid fa-folder-open",
subs: [
{ id: "2_1", name: "Integrasi Konten Aplikasi Perkantoran", cat: "Materi", link: "https://suryogarage.blogspot.com/2025/07/informatika.html", duration: "10 Menit", desc: "Menghubungkan Word, Excel, dan PowerPoint dengan teknik OLE." },
{ id: "2_2", name: "Fitur Lanjut Mail Merge", cat: "Video", link: "https://youtube.com", duration: "8 Menit", desc: "Cara membuat surat massal secara cepat memanfaatkan pangkalan data." },
{ id: "2_3", name: "Kolaborasi Dokumen di Cloud Storage", cat: "Kuis", link: "https://drive.google.com", duration: "15 Menit", desc: "Praktik membuat dokumen bersama menggunakan Google Docs / OneDrive." }
]
},
{
id: "bab3",
num: "Bab 3",
title: "Sistem Komputer",
desc: "Memahami interaksi komponen perangkat keras, lunak, dan pengguna.",
color: "from-purple-600/20 to-pink-600/20 border-purple-500/30",
icon: "fa-solid fa-microchip",
subs: [
{ id: "3_1", name: "Perangkat Keras (Hardware)", cat: "Materi", link: "https://suryogarage.blogspot.com/2025/07/informatika.html", duration: "12 Menit", desc: "Mengenal unit pemrosesan, input, output, dan media penyimpanan sekunder." },
{ id: "3_2", name: "Perangkat Lunak & Sistem Operasi", cat: "Video", link: "https://youtube.com", duration: "15 Menit", desc: "Bagaimana Sistem Operasi (Windows, Linux, macOS) mengelola sumber daya komputer." },
{ id: "3_3", name: "Interaksi Manusia dan Komputer (IMK)", cat: "Materi", link: "https://suryogarage.blogspot.com/2025/07/informatika.html", duration: "10 Menit", desc: "Mempelajari antarmuka grafis (GUI) dan Command Line Interface (CLI)." },
{ id: "3_4", name: "Evaluasi Komponen Komputer", cat: "Kuis", link: "https://quizizz.com", duration: "15 Menit", desc: "Uji pemahamanmu mengenai cara kerja otak komputer." }
]
},
{
id: "bab4",
num: "Bab 4",
title: "Jaringan Komputer & Internet",
desc: "Mempelajari cara kerja transfer data global dan proteksi privasi digital.",
color: "from-sky-600/20 to-blue-600/20 border-sky-500/30",
icon: "fa-solid fa-wifi",
subs: [
{ id: "4_1", name: "Pengantar Jaringan Komputer & Topologi", cat: "Materi", link: "https://suryogarage.blogspot.com/2025/07/informatika.html", duration: "15 Menit", desc: "Belajar topologi jaringan lokal (LAN), metropolitan (MAN), dan global (WAN)." },
{ id: "4_2", name: "Enkripsi & Proteksi Data di Internet", cat: "Video", link: "https://youtube.com", duration: "11 Menit", desc: "Mengapa penting menjaga data pribadi dan bagaimana teknik kriptografi dasar bekerja." },
{ id: "4_3", name: "Kuis Keamanan Jaringan", cat: "Kuis", link: "https://quizizz.com", duration: "15 Menit", desc: "Praktik mengenali phishing dan situs web aman (HTTPS)." }
]
},
{
id: "bab5",
num: "Bab 5",
title: "Analisis Data",
desc: "Mengolah kumpulan informasi mentah menjadi keputusan terstruktur bernilai.",
color: "from-amber-600/20 to-orange-600/20 border-amber-500/30",
icon: "fa-solid fa-chart-pie",
subs: [
{ id: "5_1", name: "Pengumpulan Data & Pembersihan (Cleaning)", cat: "Materi", link: "https://suryogarage.blogspot.com/2025/07/informatika.html", duration: "10 Menit", desc: "Teknik menyaring data mentah agar siap dianalisis di aplikasi spreadsheet." },
{ id: "5_2", name: "Visualisasi Data Interaktif", cat: "Video", link: "https://youtube.com", duration: "12 Menit", desc: "Membangun diagram batang, garis, dan lingkaran yang informatif." },
{ id: "5_3", name: "Aspek Privasi dan Keamanan Analisis Data", cat: "Kuis", link: "https://quizizz.com", duration: "15 Menit", desc: "Ujian modul akhir mengolah tabel data dan menyajikannya secara etis." }
]
}
];
// Simulasi Jawaban Siswa (Mock Data)
let studentSubmissions = [
{
id: "sub_1",
studentName: "Dafa Ramadhan",
studentClass: "X-A",
subId: "1_4",
subName: "Kuis Berpikir Komputasional",
notes: "Saya sudah menyelesaikan latihan bebras di link quizizz yang bapak berikan. Logika dekomposisi sangat membantu.",
link: "https://quizizz.com/join",
submittedAt: "2026-06-25 14:22",
status: "Sudah Dinilai",
score: "92",
feedback: "Luar biasa Dafa! Analisis dekomposisinya sudah sangat runtut."
},
{
id: "sub_2",
studentName: "Amelia Putri",
studentClass: "X-B",
subId: "2_3",
subName: "Kolaborasi Dokumen di Cloud Storage",
notes: "Berikut adalah tugas folder integrasi kelompok B, kami menggunakan Google Docs.",
link: "https://drive.google.com/drive/folders/test12345",
submittedAt: "2026-06-26 09:10",
status: "Belum Dinilai",
score: "",
feedback: ""
},
{
id: "sub_3",
studentName: "Rian Hidayat",
studentClass: "X-A",
subId: "3_4",
subName: "Evaluasi Komponen Komputer",
notes: "Jawaban esai: CPU bertugas memproses data, RAM menyimpan sementara, dan harddisk menyimpan permanen.",
link: "",
submittedAt: "2026-06-26 11:30",
status: "Belum Dinilai",
score: "",
feedback: ""
}
];
let selectedGatekeeperRole = 'siswa'; // Default role di Gatekeeper
let teacherMode = false;
let selectedSubToEdit = null;
let selectedSubToSubmit = null;
let selectedSubmissionToGrade = null;
let completedSubs = [];
let currentFilter = 'all';
// Load data dari Session/Local Storage jika ada
window.onload = function() {
const savedData = localStorage.getItem('informatics_elearning_data');
if (savedData) {
informaticsData = JSON.parse(savedData);
}
const savedProgress = localStorage.getItem('informatics_completed_progress');
if (savedProgress) {
completedSubs = JSON.parse(savedProgress);
}
const savedSubmissions = localStorage.getItem('informatics_student_submissions');
if (savedSubmissions) {
studentSubmissions = JSON.parse(savedSubmissions);
}
// Periksa Status Keamanan Gerbang (Gatekeeper)
const unlockedState = sessionStorage.getItem('info_learn_unlocked');
if (unlockedState === 'student') {
document.getElementById('gatekeeperOverlay').style.display = 'none';
teacherMode = false;
applyRoleState();
} else if (unlockedState === 'teacher') {
document.getElementById('gatekeeperOverlay').style.display = 'none';
teacherMode = true;
applyRoleState();
} else {
// Focus ke password input jika belum di-unlock
document.getElementById('gatekeeperCodeInput').focus();
}
renderDashboard();
updateProgress();
renderSubmissions();
}
// Simpan data lokal
function saveToLocalStorage() {
localStorage.setItem('informatics_elearning_data', JSON.stringify(informaticsData));
localStorage.setItem('informatics_completed_progress', JSON.stringify(completedSubs));
localStorage.setItem('informatics_student_submissions', JSON.stringify(studentSubmissions));
}
// ================= GATEKEEPER (SCREEN KUNCI) LOGIC =================
// Memilih peran di Layar Kunci
function selectGatekeeperRole(role) {
selectedGatekeeperRole = role;
const btnSiswa = document.getElementById('roleBtnSiswa');
const btnGuru = document.getElementById('roleBtnGuru');
const label = document.getElementById('gatekeeperLabel');
const input = document.getElementById('gatekeeperCodeInput');
// Reset error
document.getElementById('gatekeeperError').classList.add('hidden');
if (role === 'siswa') {
btnSiswa.className = "flex flex-col items-center justify-center p-4 rounded-2xl border transition-all duration-300 bg-brand-500/10 border-brand-500/50 text-white";
btnGuru.className = "flex flex-col items-center justify-center p-4 rounded-2xl border transition-all duration-300 bg-slate-950 border-slate-800 text-slate-400 hover:border-slate-700 hover:text-slate-200";
label.innerText = "Kode Akses Siswa:";
input.placeholder = "12345";
} else {
btnGuru.className = "flex flex-col items-center justify-center p-4 rounded-2xl border transition-all duration-300 bg-amber-500/10 border-amber-500/50 text-white";
btnSiswa.className = "flex flex-col items-center justify-center p-4 rounded-2xl border transition-all duration-300 bg-slate-950 border-slate-800 text-slate-400 hover:border-slate-700 hover:text-slate-200";
label.innerText = "Kode Akses Guru:";
input.placeholder = "5678";
}
input.value = '';
input.focus();
}
// Verifikasi kode akses di Layar Kunci
function checkGatekeeperCode() {
const codeInput = document.getElementById('gatekeeperCodeInput');
const code = codeInput.value.trim();
const errorElement = document.getElementById('gatekeeperError');
if (selectedGatekeeperRole === 'siswa' && code === '12345') {
sessionStorage.setItem('info_learn_unlocked', 'student');
document.getElementById('gatekeeperOverlay').classList.add('opacity-0', 'pointer-events-none');
teacherMode = false;
applyRoleState();
showToast("Selamat belajar! Kode akses siswa berhasil diterima.");
} else if (selectedGatekeeperRole === 'guru' && code === '5678') {
sessionStorage.setItem('info_learn_unlocked', 'teacher');
document.getElementById('gatekeeperOverlay').classList.add('opacity-0', 'pointer-events-none');
teacherMode = true;
applyRoleState();
showToast("Selamat datang Guru! Masuk langsung ke Panel Kontrol.");
} else {
errorElement.classList.remove('hidden');
codeInput.classList.add('border-red-500');
setTimeout(() => {
codeInput.classList.remove('border-red-500');
}, 500);
}
}
// ================= HEADER ROLE SWITCHER LOGIC =================
// Berpindah peran melalui Segmented Control di Header
function switchHeaderRole(role) {
if (role === 'siswa') {
if (teacherMode) {
teacherMode = false;
sessionStorage.setItem('info_learn_unlocked', 'student');
applyRoleState();
showToast("Beralih ke Mode Siswa.");
}
} else {
if (!teacherMode) {
openPasswordModal(); // Minta kata sandi jika mau masuk mode Guru
}
}
}
// Buka modal pengaman ganti peran
function openPasswordModal() {
document.getElementById('teacherPasswordInput').value = '';
document.getElementById('passwordError').classList.add('hidden');
document.getElementById('passwordModal').classList.remove('hidden');
setTimeout(() => {
document.getElementById('teacherPasswordInput').focus();
}, 150);
}
function closePasswordModal() {
document.getElementById('passwordModal').classList.add('hidden');
}
// Verifikasi password ganti peran
function verifyTeacherPassword() {
const password = document.getElementById('teacherPasswordInput').value;
const errorElement = document.getElementById('passwordError');
if (password === '5678') {
teacherMode = true;
sessionStorage.setItem('info_learn_unlocked', 'teacher');
closePasswordModal();
applyRoleState();
showToast("Mode Guru Berhasil Diaktifkan!");
} else {
errorElement.classList.remove('hidden');
const input = document.getElementById('teacherPasswordInput');
input.classList.add('border-red-500');
setTimeout(() => {
input.classList.remove('border-red-500');
}, 500);
}
}
// Terapkan penyesuaian visual berdasarkan Peran Aktif
function applyRoleState() {
const btnSiswa = document.getElementById('headerBtnSiswa');
const btnGuru = document.getElementById('headerBtnGuru');
const panel = document.getElementById('teacherPanel');
if (teacherMode) {
// Style Header Switcher ke Guru
btnGuru.className = "px-3.5 py-1.5 rounded-xl text-xs font-bold transition-all duration-300 flex items-center gap-1.5 bg-amber-500 text-slate-950 shadow-md";
btnSiswa.className = "px-3.5 py-1.5 rounded-xl text-xs font-bold transition-all duration-300 flex items-center gap-1.5 text-slate-400 hover:text-slate-200";
panel.classList.remove('hidden');
renderSubmissions();
} else {
// Style Header Switcher ke Siswa
btnSiswa.className = "px-3.5 py-1.5 rounded-xl text-xs font-bold transition-all duration-300 flex items-center gap-1.5 bg-brand-600 text-white shadow-md";
btnGuru.className = "px-3.5 py-1.5 rounded-xl text-xs font-bold transition-all duration-300 flex items-center gap-1.5 text-slate-400 hover:text-slate-200";
panel.classList.add('hidden');
}
renderDashboard();
}
// ================= DASHBOARD & CONTENT LOGIC =================
// Render Dashboard Bab & Sub-Bab
function renderDashboard() {
const container = document.getElementById('chaptersContainer');
container.innerHTML = '';
informaticsData.forEach((bab) => {
let cardHTML = `
<div class="glass-panel border rounded-3xl overflow-hidden shadow-lg transition-transform duration-300 hover:scale-[1.01] flex flex-col justify-between ${bab.color}">
<div class="p-6">
<!-- Bab Header -->
<div class="flex items-start justify-between gap-4 mb-4">
<div class="space-y-1">
<span class="text-xs font-bold text-slate-400 tracking-widest uppercase">${bab.num}</span>
<h3 class="text-xl font-extrabold text-white leading-tight">${bab.title}</h3>
</div>
<div class="w-12 h-12 rounded-2xl bg-slate-900/80 border border-slate-700/50 flex items-center justify-center text-slate-300">
<i class="${bab.icon} text-lg"></i>
</div>
</div>
<p class="text-xs sm:text-sm text-slate-300 mb-6 font-medium leading-relaxed">${bab.desc}</p>
<!-- Sub Bab List -->
<div class="space-y-3.5">
`;
bab.subs.forEach((sub) => {
const isChecked = completedSubs.includes(sub.id);
const isMatchedFilter = currentFilter === 'all' || sub.cat === currentFilter;
const visibilityClass = isMatchedFilter ? 'block' : 'hidden';
// Badge Color
let badgeColor = "bg-blue-500/10 text-blue-400 border-blue-500/20";
let badgeIcon = "fa-regular fa-file-lines";
if (sub.cat === "Video") {
badgeColor = "bg-red-500/10 text-red-400 border-red-500/20";
badgeIcon = "fa-regular fa-circle-play";
} else if (sub.cat === "Kuis") {
badgeColor = "bg-amber-500/10 text-amber-400 border-amber-500/20";
badgeIcon = "fa-solid fa-laptop-code";
}
// Badge status jawaban terkirim
const mySubmissions = studentSubmissions.filter(s => s.subId === sub.id);
let submissionBadge = "";
if (mySubmissions.length > 0) {
const graded = mySubmissions.find(s => s.status === "Sudah Dinilai");
if (graded) {
submissionBadge = `<span class="inline-flex items-center gap-1 px-2 py-0.5 rounded-full text-[10px] font-bold bg-emerald-500/10 text-emerald-400 border border-emerald-500/20"><i class="fa-solid fa-award"></i> Nilai: ${graded.score}</span>`;
} else {
submissionBadge = `<span class="inline-flex items-center gap-1 px-2 py-0.5 rounded-full text-[10px] font-bold bg-purple-500/10 text-purple-400 border border-purple-500/20"><i class="fa-solid fa-spinner animate-spin"></i> Terkirim</span>`;
}
}
cardHTML += `
<div class="sub-item bg-slate-900/50 border border-slate-800/80 rounded-2xl p-3.5 hover:bg-slate-900 transition flex items-start justify-between gap-3 ${visibilityClass}" data-title="${sub.name.toLowerCase()}">
<div class="flex items-start gap-3 flex-grow">
<!-- Checkbox Siswa -->
<div class="pt-0.5">
<button onclick="toggleSubComplete('${sub.id}')" class="w-5.5 h-5.5 rounded-md border flex items-center justify-center transition-all ${isChecked ? 'bg-emerald-500 border-emerald-500 text-slate-900' : 'border-slate-700 hover:border-slate-500 text-transparent'}" title="Tandai selesai">
<i class="fa-solid fa-check text-xs font-black"></i>
</button>
</div>
<div class="space-y-1 w-full">
<div class="flex flex-wrap items-center gap-2">
<h4 class="text-xs sm:text-sm font-semibold text-slate-200">${sub.name}</h4>
<span class="inline-flex items-center gap-1 px-2 py-0.5 rounded-full text-[10px] font-bold border ${badgeColor}">
<i class="${badgeIcon}"></i> ${sub.cat}
</span>
<span class="text-[10px] text-slate-500"><i class="fa-regular fa-clock mr-1"></i>${sub.duration}</span>
${submissionBadge}
</div>
<p class="text-xs text-slate-400 line-clamp-1">${sub.desc || 'Materi pembelajaran siap diakses.'}</p>
</div>
</div>
<!-- Tindakan berdasarkan peran -->
<div class="flex items-center gap-2">
${teacherMode ? `
<button onclick="openEditModal('${bab.id}', '${sub.id}')" class="p-1.5 rounded-lg bg-amber-500/10 text-amber-400 hover:bg-amber-500/20 transition" title="Edit sub-bab">
<i class="fa-solid fa-pen text-xs"></i>
</button>
` : `
<button onclick="openSubmitTaskModal('${sub.id}', '${sub.name}')" class="flex items-center gap-1 px-2 py-1.5 rounded-xl bg-brand-500/10 border border-brand-500/20 text-brand-400 hover:bg-brand-500 hover:text-white font-bold text-xs transition duration-300" title="Kirim Jawaban Tugas">
<i class="fa-solid fa-paper-plane"></i> <span class="hidden sm:inline">Kirim Tugas</span>
</button>
`}
<a href="${sub.link}" target="_blank" class="flex items-center gap-1.5 px-3 py-1.5 rounded-xl bg-slate-800 hover:bg-brand-600 hover:text-white border border-slate-700/80 text-slate-300 font-bold text-xs transition duration-300 shadow-md">
<i class="fa-solid fa-arrow-up-right-from-square"></i>
Akses
</a>
</div>
</div>
`;
});
cardHTML += `
</div>
</div>
</div>
`;
container.innerHTML += cardHTML;
});
}
// Render rekap pengiriman tugas siswa
function renderSubmissions() {
const tableBody = document.getElementById('submissionsTableBody');
if (!tableBody) return;
tableBody.innerHTML = '';
const selectedClass = document.getElementById('teacherClassFilter').value;
const filteredSubmissions = studentSubmissions.filter(sub => {
return selectedClass === 'all' || sub.studentClass === selectedClass;
});
if (filteredSubmissions.length === 0) {
tableBody.innerHTML = `
<tr>
<td colspan="6" class="p-8 text-center text-slate-500 font-medium">
<i class="fa-regular fa-folder-open text-2xl block mb-2"></i> Belum ada jawaban siswa yang dikirimkan.
</td>
</tr>
`;
return;
}
filteredSubmissions.forEach(sub => {
const statusBadge = sub.status === "Sudah Dinilai"
? `<span class="inline-flex items-center gap-1 px-2.5 py-1 rounded-full text-xs font-bold bg-emerald-500/10 text-emerald-400 border border-emerald-500/20"><i class="fa-solid fa-check"></i> Nilai: ${sub.score}</span>`
: `<span class="inline-flex items-center gap-1 px-2.5 py-1 rounded-full text-xs font-bold bg-amber-500/10 text-amber-400 border border-amber-500/20"><i class="fa-solid fa-circle-exclamation"></i> Belum Dinilai</span>`;
const linkElement = sub.link
? `<a href="${sub.link}" target="_blank" class="text-brand-400 hover:underline flex items-center gap-1 text-xs"><i class="fa-solid fa-link"></i> Buka Lampiran</a>`
: `<span class="text-slate-600 text-xs italic">Tanpa Lampiran</span>`;
tableBody.innerHTML += `
<tr class="hover:bg-slate-900/40 transition">
<td class="p-4 font-semibold text-white">
<div>${sub.studentName}</div>
<div class="text-[10px] text-slate-500">Kelas: ${sub.studentClass}</div>
</td>
<td class="p-4 text-xs max-w-xs truncate" title="${sub.subName}">
${sub.subName}
</td>
<td class="p-4 text-xs text-slate-400">
${sub.submittedAt}
</td>
<td class="p-4">
${linkElement}
</td>
<td class="p-4">
${statusBadge}
</td>
<td class="p-4 text-center">
<div class="flex justify-center gap-2">
<button onclick="openGradeModal('${sub.id}')" class="px-2.5 py-1.5 rounded-lg bg-emerald-600/10 hover:bg-emerald-600/20 text-emerald-400 font-semibold text-xs flex items-center gap-1.5 transition">
<i class="fa-solid fa-graduation-cap"></i> Beri Nilai
</button>
<button onclick="deleteSubmission('${sub.id}')" class="p-1.5 rounded-lg bg-red-500/10 hover:bg-red-500/20 text-red-400 transition" title="Hapus Jawaban">
<i class="fa-solid fa-trash-can text-xs"></i>
</button>
</div>
</td>
</tr>
`;
});
}
// Progres Tracker Siswa
function toggleSubComplete(subId) {
const index = completedSubs.indexOf(subId);
if (index > -1) {
completedSubs.splice(index, 1);
} else {
completedSubs.push(subId);
}
saveToLocalStorage();
updateProgress();
renderDashboard();
}
function updateProgress() {
let totalSubCount = 0;
informaticsData.forEach(bab => {
totalSubCount += bab.subs.length;
});
const completedCount = completedSubs.length;
const percentage = totalSubCount > 0 ? Math.round((completedCount / totalSubCount) * 100) : 0;
document.getElementById('completedCount').innerText = completedCount;
document.getElementById('totalCount').innerText = totalSubCount;
document.getElementById('globalPercentText').innerText = percentage + "%";
document.getElementById('globalProgressBar').style.width = percentage + "%";
const circle = document.getElementById('progressCircle');
const circumference = 2 * Math.PI * 16;
const offset = circumference - (percentage / 100) * circumference;
circle.style.strokeDasharray = `${circumference} ${circumference}`;
circle.style.strokeDashoffset = offset;
}
// Pencarian Filter Realtime
function filterContent() {
const query = document.getElementById('searchInput').value.toLowerCase();
const items = document.querySelectorAll('.sub-item');
items.forEach(item => {
const title = item.getAttribute('data-title');
if (title.includes(query)) {
item.classList.remove('hidden');
} else {
item.classList.add('hidden');
}
});
}
// Filter Tipe Kategori
function filterType(type) {
currentFilter = type;
const buttons = document.querySelectorAll('.type-filter-btn');
buttons.forEach(btn => {
if (btn.innerText.includes(type === 'all' ? 'Semua' : type)) {
btn.className = "type-filter-btn px-4 py-2 rounded-xl text-xs sm:text-sm font-semibold bg-brand-600 text-white shadow-lg shadow-brand-500/20 whitespace-nowrap transition";
} else {
btn.className = "type-filter-btn px-4 py-2 rounded-xl text-xs sm:text-sm font-medium bg-slate-800 text-slate-400 hover:text-white hover:bg-slate-700 whitespace-nowrap transition";
}
});
renderDashboard();
}
// Tampilkan Notifikasi Toast
function showToast(message) {
const toast = document.getElementById('toast');
const toastMsg = document.getElementById('toastMsg');
toastMsg.innerText = message;
toast.classList.remove('translate-y-12', 'opacity-0');
toast.classList.add('translate-y-0', 'opacity-100');
setTimeout(() => {
toast.classList.remove('translate-y-0', 'opacity-100');
toast.classList.add('translate-y-12', 'opacity-0');
}, 3000);
}
// Buka Modal Edit Sub-Bab (Mode Guru)
function openEditModal(babId, subId) {
const bab = informaticsData.find(b => b.id === babId);
const sub = bab.subs.find(s => s.id === subId);
selectedSubToEdit = { babId, subId };
document.getElementById('editSubName').value = sub.name;
document.getElementById('editSubCategory').value = sub.cat;
document.getElementById('editSubDuration').value = sub.duration;
document.getElementById('editSubLink').value = sub.link;
document.getElementById('editSubDesc').value = sub.desc || '';
document.getElementById('editModal').classList.remove('hidden');
}
function closeEditModal() {
document.getElementById('editModal').classList.add('hidden');
selectedSubToEdit = null;
}
function saveEditData() {
if (!selectedSubToEdit) return;
const { babId, subId } = selectedSubToEdit;
const bab = informaticsData.find(b => b.id === babId);
const sub = bab.subs.find(s => s.id === subId);
sub.name = document.getElementById('editSubName').value;
sub.cat = document.getElementById('editSubCategory').value;
sub.duration = document.getElementById('editSubDuration').value;
sub.link = document.getElementById('editSubLink').value;
sub.desc = document.getElementById('editSubDesc').value;
saveToLocalStorage();
renderDashboard();
updateProgress();
closeEditModal();
showToast("Perubahan sub-bab berhasil disimpan!");
}
// ================= SISWA: SUBMIT TUGAS =================
function openSubmitTaskModal(subId, subName) {
selectedSubToSubmit = { subId, subName };
document.getElementById('submitModalSubTitle').innerText = subName;
document.getElementById('studentNameInput').value = '';
document.getElementById('studentNotesInput').value = '';
document.getElementById('studentLinkInput').value = '';
document.getElementById('submitTaskModal').classList.remove('hidden');
}
function closeSubmitTaskModal() {
document.getElementById('submitTaskModal').classList.add('hidden');
selectedSubToSubmit = null;
}
function submitStudentTask() {
const name = document.getElementById('studentNameInput').value.trim();
const kelas = document.getElementById('studentClassInput').value;
const notes = document.getElementById('studentNotesInput').value.trim();
const link = document.getElementById('studentLinkInput').value.trim();
if (!name) {
showToast("Harap masukkan Nama Lengkap Anda!");
return;
}
if (!notes) {
showToast("Harap berikan keterangan atau jawaban tugas!");
return;
}
const now = new Date();
const timestamp = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')} ${String(now.getHours()).padStart(2, '0')}:${String(now.getMinutes()).padStart(2, '0')}`;
const newSubmission = {
id: "sub_" + Date.now(),
studentName: name,
studentClass: kelas,
subId: selectedSubToSubmit.subId,
subName: selectedSubToSubmit.subName,
notes: notes,
link: link,
submittedAt: timestamp,
status: "Belum Dinilai",
score: "",
feedback: ""
};
studentSubmissions.unshift(newSubmission);
saveToLocalStorage();
if (!completedSubs.includes(selectedSubToSubmit.subId)) {
completedSubs.push(selectedSubToSubmit.subId);
}
updateProgress();
renderDashboard();
renderSubmissions();
closeSubmitTaskModal();
showToast("Jawaban tugas Anda berhasil dikirimkan ke Guru!");
}
// ================= GURU: BERI NILAI =================
function openGradeModal(submissionId) {
const submission = studentSubmissions.find(s => s.id === submissionId);
if (!submission) return;
selectedSubmissionToGrade = submission;
document.getElementById('gradeModalStudentName').innerText = submission.studentName;
document.getElementById('gradeModalClass').innerText = submission.studentClass;
document.getElementById('gradeModalDate').innerText = submission.submittedAt;
document.getElementById('gradeModalSubName').innerText = submission.subName;
document.getElementById('gradeModalResponseText').innerText = submission.notes;
document.getElementById('gradeScoreInput').value = submission.score;
document.getElementById('gradeFeedbackInput').value = submission.feedback || '';
document.getElementById('gradeModal').classList.remove('hidden');
}
function closeGradeModal() {
document.getElementById('gradeModal').classList.add('hidden');
selectedSubmissionToGrade = null;
}
function saveStudentGrade() {
if (!selectedSubmissionToGrade) return;
const score = document.getElementById('gradeScoreInput').value.trim();
const feedback = document.getElementById('gradeFeedbackInput').value.trim();
if (score === "" || isNaN(score) || score < 0 || score > 100) {
showToast("Masukkan nilai valid antara 0 s.d. 100!");
return;
}
selectedSubmissionToGrade.score = score;
selectedSubmissionToGrade.feedback = feedback;
selectedSubmissionToGrade.status = "Sudah Dinilai";
saveToLocalStorage();
renderSubmissions();
renderDashboard();
closeGradeModal();
showToast(`Siswa ${selectedSubmissionToGrade.studentName} berhasil dinilai: ${score}!`);
}
function deleteSubmission(submissionId) {
studentSubmissions = studentSubmissions.filter(s => s.id !== submissionId);
saveToLocalStorage();
renderSubmissions();
renderDashboard();
showToast("Data pengiriman siswa telah dihapus.");
}
// ================= IMPOR/EKSPOR DATA GURU =================
function exportData() {
const dataToExport = {
informaticsData: informaticsData,
studentSubmissions: studentSubmissions
};
const dataStr = JSON.stringify(dataToExport, null, 2);
const dataUri = 'data:application/json;charset=utf-8,'+ encodeURIComponent(dataStr);
const exportFileDefaultName = 'materi-dan-jawaban-informatika.json';
const linkElement = document.createElement('a');
linkElement.setAttribute('href', dataUri);
linkElement.setAttribute('download', exportFileDefaultName);
linkElement.click();
showToast("Semua data materi & jawaban siswa berhasil diunduh!");
}
function triggerImport() {
document.getElementById('importFileInput').click();
}
function importData(event) {
const file = event.target.files[0];
if (!file) return;
const reader = new FileReader();
reader.onload = function(e) {
try {
const parsedData = JSON.parse(e.target.result);
if (parsedData.informaticsData && parsedData.studentSubmissions) {
informaticsData = parsedData.informaticsData;
studentSubmissions = parsedData.studentSubmissions;
} else if (Array.isArray(parsedData)) {
informaticsData = parsedData;
} else {
showToast("Format file salah. Gunakan file ekspor dari sistem ini.");
return;
}
saveToLocalStorage();
renderDashboard();
updateProgress();
renderSubmissions();
showToast("Semua data materi dan jawaban siswa berhasil diimpor!");
} catch (error) {
showToast("Gagal membaca file konfigurasi.");
}
};
reader.readAsText(file);
}
</script>
</body>
</html>
Tentu! Saya telah menambahkan mekanisme keamanan khusus menggunakan kata sandi 5678 untuk masuk ke Mode Guru.
Kode Akses Siswa (12345): Siswa wajib memasukkan kode 12345
0 Comments