Files
OBE-Mapping/app/templates/form_nilai_komponen.html
T
Power BI Dev d5edbe2134 Tambah ringkasan nilai per komponen, rincian tugas ala RTM, auto-save RPS
- Halaman Nilai: tabel "Nilai per Komponen" (rata-rata skor mentah tiap
  sesi, bisa diedit langsung) berdampingan dengan grid Sub-CPMK yang tetap
  presisi; kolom persentase ternilai di Hasil Ketercapaian.
- Komponen Tugas: sesi bisa menyasar subset Sub-CPMK (RPS tetap batasannya),
  lengkap dengan field ala RTM (bentuk tugas, deskripsi, metode, luaran,
  indikator/bobot, jadwal) dan tombol isi-otomatis per bentuk tugas.
- RPS: auto-save berkala sebagai draft, tombol ke atas, validasi total
  bobot komponen 100% saat ditandai selesai, komponen berbobot kosong
  disembunyikan dari halaman Nilai.
- Admin: tampilkan kelas yang tersedia dari data mahasiswa saat mengatur
  penugasan dosen per kelas.
- Perbaikan step="any" pada input nilai (mencegah validasi HTML5 menolak
  nilai desimal hasil roll-up).
- Tambah METODOLOGI_ASESMEN_KOMPONEN.md: dokumentasi metodologi Sub-CPMK
  vs komponen, termasuk skenario tugas menyasar sebagian Sub-CPMK.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-23 23:28:46 +07:00

195 lines
12 KiB
HTML
Raw 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="/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>
<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>
<th class="tengah" style="width:140px">Nilai (0100)</th>
</tr>
</thead>
<tbody>
{% for m in grid %}
<tr>
<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>
<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 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 %}