Files
OBE-Mapping/app/templates/form_nilai_komponen.html
Power BI Dev 9c0fe8baa0 Tambah filter kelas di halaman Input Nilai
- /nilai/<kode>: dropdown "Tampilkan kelas" menyaring tabel Nilai per
  Komponen & Nilai per Mahasiswa sekaligus (tanpa reload); baris
  ringkasan (rata-rata/% lulus MK) ikut disembunyikan saat filter aktif
  karena angkanya dihitung dari seluruh MK, bukan per kelas
- /nilai/<kode>/komponen/<i>: filter serupa + kolom Kelas ditambahkan
  di tabel Nilai Mahasiswa (halaman paling sering dipakai dosen isi
  nilai per sesi/tugas)
- Muncul otomatis hanya bila MK punya >1 kelas

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-09 18:35:43 +07:00

234 lines
13 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{% extends "base.html" %}
{% block judul %}Nilai {{ komponen.nama }} — {{ peta.nama }} — Informatika UNTAN{% endblock %}
{% block isi %}
<div class="nav-baris">
<div>MK ini: <a href="/rps/{{ peta.kode }}">RPS</a> · <a href="/rps/{{ peta.kode }}/rtm">RTM</a> · <a href="/nilai/{{ peta.kode }}">Input Nilai</a> · <b>{{ komponen.nama }}</b> · <a href="/portofolio/{{ peta.kode }}">Portofolio</a></div>
</div>
<h1>Isi Nilai — {{ komponen.nama }} <span style="color:var(--grey)">({{ peta.nama }})</span></h1>
<p class="keterangan">
Nilai di sini digabung ke Sub-CPMK yang diukur komponen ini:
{% for s in komponen.subs %}<b>{{ s }}</b>{{ ', ' if not loop.last }}{% endfor %}
{% if komponen.bobot %}(bobot komponen {{ komponen.bobot }}%){% endif %}.
{% if komponen.ulang %}
Komponen ini <b>boleh diisi berkali-kali</b> (mis. beberapa kali kuis sepanjang
semester) — nilai tiap sesi dirata-rata otomatis per Sub-CPMK. Bila satu Sub-CPMK
juga diukur komponen lain, hasil akhirnya dirata tertimbang bobot antar-komponen.
{% else %}
Komponen ini <b>hanya satu sesi per semester</b> — menyimpan lagi memperbarui
(menimpa) sesi yang sudah ada, bukan menambah sesi baru.
{% endif %}
Jika mahasiswa perlu nilai berbeda antar Sub-CPMK dalam komponen yang sama
(mis. UTS dengan beberapa kelompok soal), isi lewat
<a href="/nilai/{{ peta.kode }}">grid per Sub-CPMK</a> di halaman utama nilai, bukan di sini.
</p>
{% if pesan %}<div class="info">{{ pesan }}</div>{% endif %}
{% if komponen.ulang and daftar_sesi %}
<h2 class="bagian">SESI TERSIMPAN</h2>
<table class="grid" style="max-width:{{ '900px' if komponen.tugas else '560px' }}">
<thead><tr><th>Sesi</th>{% if komponen.tugas %}<th>Sub-CPMK</th><th style="width:130px">Jadwal</th>{% endif %}<th class="tengah" style="width:120px">Mahasiswa Terisi</th><th style="width:60px"></th></tr></thead>
<tbody>
{% for s in daftar_sesi %}
<tr{% if loop.index0 == edit_id %} style="background:var(--blue-fill)"{% endif %}>
<td>{{ s.label }}{% if komponen.tugas and s.deskripsi %}<br><span style="font-size:12px;color:var(--grey);font-style:italic">{{ s.deskripsi }}</span>{% endif %}</td>
{% if komponen.tugas %}<td>{{ (s.subs or komponen.subs)|join(', ') }}</td><td>{{ s.jadwal or '—' }}</td>{% endif %}
<td class="tengah">{{ (s.nilai or {})|length }}</td>
<td><a href="/nilai/{{ peta.kode }}/komponen/{{ i }}?sesi_id={{ loop.index0 }}">Edit</a></td>
</tr>
{% endfor %}
</tbody>
</table>
{% endif %}
<h2 class="bagian">{{ 'EDIT SESI' if edit_id < daftar_sesi|length else 'SESI BARU' }} {{ label_awal }}</h2>
<form method="post" action="/nilai/{{ peta.kode }}/komponen/{{ i }}">
<input type="hidden" name="edit_id" value="{{ edit_id }}">
{% if komponen.ulang %}
<label style="display:block;margin-bottom:10px">{{ 'Judul Tugas' if komponen.tugas else 'Nama sesi' }}
<input type="text" name="label" value="{{ label_awal }}" style="display:block;margin-top:4px;max-width:480px">
</label>
{% else %}
<input type="hidden" name="label" value="{{ label_awal }}">
{% endif %}
{% if komponen.tugas %}
<div style="margin-bottom:10px">
<div>Sub Capaian Pembelajaran Mata Kuliah <span style="font-weight:normal;color:var(--grey)">(bawaan semua tercentang, sesuai yang ditentukan RPS untuk komponen ini — kurangi bila tugas lain akan menyasar sisanya; bobot komponen ini tetap {{ komponen.bobot }}%)</span></div>
<div class="pilih-sub">
{% for s in subs_pilihan %}
<label><input type="checkbox" name="subs" value="{{ s.kode }}" {% if s.kode in subs_terpilih %}checked{% endif %}>{{ s.kode }} — {{ s.uraian }}</label>
{% endfor %}
</div>
</div>
<label style="display:block;margin-bottom:10px">Bentuk Tugas
<span style="display:flex;align-items:center;gap:8px;margin-top:4px">
<select name="bentuk_tugas" id="bentuk_tugas" onchange="toggleIsiOtomatis()" style="max-width:280px">
<option value="">— pilih —</option>
{% for opsi in ["Diskusi", "PjBL", "CBL", "Esai", "Tes"] %}
<option value="{{ opsi }}" {% if rtm.bentuk_tugas == opsi %}selected{% endif %}>{{ opsi }}</option>
{% endfor %}
</select>
<button type="button" class="tombol garis" id="tombol_isi_otomatis" onclick="isiOtomatisTugas()" {% if not rtm.bentuk_tugas %}hidden{% endif %}>Isi Otomatis</button>
</span>
</label>
<label style="display:block;margin-bottom:10px">Deskripsi Tugas <span style="font-weight:normal;color:var(--grey)">(opsional — soal cerita, esai, kasus, atau proyek yang harus diselesaikan mahasiswa)</span>
<textarea name="deskripsi" id="rtm_deskripsi" rows="4" style="display:block;margin-top:4px;max-width:760px;width:100%">{{ rtm.deskripsi }}</textarea>
</label>
<label style="display:block;margin-bottom:10px">Metode Pengerjaan Tugas <span style="font-weight:normal;color:var(--grey)">(opsional — langkah-langkah pengerjaan)</span>
<textarea name="metode_pengerjaan" id="rtm_metode" rows="3" style="display:block;margin-top:4px;max-width:760px;width:100%">{{ rtm.metode_pengerjaan }}</textarea>
</label>
<label style="display:block;margin-bottom:10px">Bentuk dan Format Luaran <span style="font-weight:normal;color:var(--grey)">(opsional)</span>
<textarea name="luaran" id="rtm_luaran" rows="2" style="display:block;margin-top:4px;max-width:760px;width:100%">{{ rtm.luaran }}</textarea>
</label>
<label style="display:block;margin-bottom:10px">Indikator, Kriteria dan Bobot Penilaian <span style="font-weight:normal;color:var(--grey)">(opsional — dokumentasi rubrik; bobot komponen di aplikasi tetap {{ komponen.bobot }}%, tak dihitung dari sini)</span>
<textarea name="indikator_bobot" id="rtm_indikator" rows="4" style="display:block;margin-top:4px;max-width:760px;width:100%">{{ rtm.indikator_bobot }}</textarea>
</label>
<label style="display:block;margin-bottom:10px">Jadwal Pelaksanaan <span style="font-weight:normal;color:var(--grey)">(opsional)</span>
<input type="text" name="jadwal" id="rtm_jadwal" value="{{ rtm.jadwal }}" style="display:block;margin-top:4px;max-width:480px">
</label>
<label style="display:block;margin-bottom:10px">Lain-lain <span style="font-weight:normal;color:var(--grey)">(opsional)</span>
<textarea name="lain_lain" rows="2" style="display:block;margin-top:4px;max-width:760px;width:100%">{{ rtm.lain_lain }}</textarea>
</label>
<label style="display:block;margin-bottom:10px">Daftar Rujukan <span style="font-weight:normal;color:var(--grey)">(opsional)</span>
<textarea name="rujukan" rows="2" style="display:block;margin-top:4px;max-width:760px;width:100%">{{ rtm.rujukan }}</textarea>
</label>
{% endif %}
<h2 class="bagian">NILAI MAHASISWA</h2>
{% set ada_kelas = grid|selectattr('kelas')|list|length > 0 %}
{% if ada_kelas %}
{% set semua_kelas = grid|map(attribute='kelas')|select|unique|sort|list %}
<div class="filter-baris">
<label for="filter-kelas">Tampilkan kelas:</label>
<select id="filter-kelas" style="width:auto">
<option value="">Semua</option>
{% for k in semua_kelas %}
<option value="{{ k }}">Kelas {{ k }}</option>
{% endfor %}
</select>
<span id="filter-kelas-info" class="keterangan" style="margin:0"></span>
</div>
{% endif %}
<div class="gulir" style="max-height:480px;overflow-y:auto">
<table class="grid">
<thead>
<tr>
<th style="width:44px" class="tengah">No.</th>
<th style="width:112px">NIM</th>
<th style="min-width:150px">Nama</th>
{% if ada_kelas %}<th style="width:60px" class="tengah">Kelas</th>{% endif %}
<th class="tengah" style="width:140px">Nilai (0100)</th>
</tr>
</thead>
<tbody>
{% for m in grid %}
<tr data-kelas="{{ m.kelas or '' }}">
<td class="tengah">{{ loop.index }}
<input type="hidden" name="nim_{{ loop.index0 }}" value="{{ m.nim }}">
<input type="hidden" name="nama_{{ loop.index0 }}" value="{{ m.nama }}"></td>
<td>{{ m.nim }}</td>
<td>{{ m.nama }}</td>
{% if ada_kelas %}<td class="tengah">{{ m.kelas or '—' }}</td>{% endif %}
<td class="isian"><input type="number" name="nilai_{{ loop.index0 }}"
min="0" max="100" step="any"
value="{{ '%g'|format(m.nilai_awal) if m.nilai_awal is not none else '' }}"></td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<div class="aksi-baris">
<span style="color:var(--grey);font-size:13px">Disimpan atas nama <b>{{ user.nama }}</b></span>
<span class="spacer"></span>
<button class="tombol" type="submit">Simpan Sesi Ini</button>
</div>
</form>
{% if ada_kelas %}
<script>
(function () {
var pilih = document.getElementById('filter-kelas');
var info = document.getElementById('filter-kelas-info');
var baris = document.querySelectorAll('table.grid tbody tr[data-kelas]');
function perbarui() {
var v = pilih.value, n = 0;
baris.forEach(function (tr) {
var tampil = v === '' || tr.dataset.kelas === v;
tr.style.display = tampil ? '' : 'none';
if (tampil) n++;
});
info.textContent = v === '' ? '' : n + ' dari ' + baris.length + ' mahasiswa ditampilkan';
}
pilih.addEventListener('change', perbarui);
perbarui();
})();
</script>
{% endif %}
{% if komponen.tugas %}
<script>
const BENTUK_TEMPLATE = {
Diskusi: {
deskripsi: 'Diskusikan {SUBCPMK} melalui studi kasus/topik terkait materi ini. Kelompok kecil (3-5 orang) menganalisis permasalahan dan menyusun rekomendasi.',
metode: '1) Baca materi & bahan diskusi;\n2) Diskusi kelompok;\n3) Susun poin-poin kesimpulan;\n4) Presentasikan hasil diskusi.',
luaran: 'Notulen/rangkuman diskusi kelompok dalam bentuk dokumen tertulis (maks. 3 halaman).',
indikator: 'Ketepatan analisis (30%)\nKeaktifan berdiskusi (30%)\nKejelasan kesimpulan (20%)\nKerja sama kelompok (20%)\nBobot: 100%',
jadwal: '1 minggu',
},
PjBL: {
deskripsi: 'Kerjakan proyek yang menghasilkan solusi nyata terkait {SUBCPMK}, mulai dari perencanaan hingga penyerahan hasil akhir.',
metode: '1) Identifikasi masalah;\n2) Rencanakan solusi;\n3) Kerjakan proyek secara bertahap;\n4) Uji & perbaiki;\n5) Serahkan hasil akhir beserta laporan.',
luaran: 'Produk/prototipe beserta laporan tertulis yang menjelaskan proses pengerjaan.',
indikator: 'Perencanaan (20%)\nProses pengerjaan (30%)\nKualitas hasil akhir (30%)\nLaporan (20%)\nBobot: 100%',
jadwal: '2-3 minggu',
},
CBL: {
deskripsi: 'Analisis studi kasus terkait {SUBCPMK}; identifikasi permasalahan dan usulkan solusi berbasis konsep yang dipelajari.',
metode: '1) Baca & pahami kasus;\n2) Identifikasi masalah inti;\n3) Terapkan konsep untuk analisis;\n4) Rumuskan solusi;\n5) Tulis laporan.',
luaran: 'Laporan analisis kasus tertulis, memuat identifikasi masalah, analisis, dan solusi yang diusulkan.',
indikator: 'Ketepatan identifikasi masalah (25%)\nKedalaman analisis (35%)\nKetepatan solusi (25%)\nSistematika laporan (15%)\nBobot: 100%',
jadwal: '1-2 minggu',
},
Esai: {
deskripsi: 'Tulis esai mengenai {SUBCPMK} sepanjang 800-1200 kata, disertai argumen dan referensi yang relevan.',
metode: '1) Tentukan tesis/argumen utama;\n2) Kumpulkan referensi pendukung;\n3) Susun kerangka esai;\n4) Tulis draf;\n5) Revisi & finalisasi.',
luaran: 'Esai tertulis (800-1200 kata) dalam format dokumen (PDF/Word).',
indikator: 'Ketepatan argumen (30%)\nKedalaman analisis (30%)\nSistematika penulisan (20%)\nPenggunaan referensi (20%)\nBobot: 100%',
jadwal: '1 minggu',
},
Tes: {
deskripsi: 'Kerjakan soal-soal tes tertulis terkait {SUBCPMK} sesuai instruksi yang diberikan.',
metode: '1) Baca instruksi & soal dengan teliti;\n2) Kerjakan sesuai alokasi waktu;\n3) Periksa kembali jawaban sebelum dikumpulkan.',
luaran: 'Lembar jawaban tes (tertulis/daring).',
indikator: 'Ketepatan jawaban per butir soal, dijumlah sesuai bobot masing-masing.\nBobot: 100%',
jadwal: '1x pertemuan (sesuai jadwal tes)',
},
};
function isiOtomatisTugas() {
const bentuk = document.getElementById('bentuk_tugas').value;
const tmpl = BENTUK_TEMPLATE[bentuk];
if (!tmpl) return;
const checked = Array.from(document.querySelectorAll('.pilih-sub input[name="subs"]:checked')).map(cb => cb.value);
const subcpmk = checked.length ? checked.join(', ') : 'Sub-CPMK terkait';
const isi = (id, teks) => {
const el = document.getElementById(id);
if (el && !el.value.trim()) el.value = teks.replace('{SUBCPMK}', subcpmk);
};
isi('rtm_deskripsi', tmpl.deskripsi);
isi('rtm_metode', tmpl.metode);
isi('rtm_luaran', tmpl.luaran);
isi('rtm_indikator', tmpl.indikator);
isi('rtm_jadwal', tmpl.jadwal);
}
function toggleIsiOtomatis() {
const bentuk = document.getElementById('bentuk_tugas').value;
document.getElementById('tombol_isi_otomatis').hidden = !bentuk;
}
</script>
{% endif %}
{% endblock %}