Files
OBE-Mapping/app/templates/beranda.html
T
Power BI Dev 9be6bd03e3 Gabungkan aplikasi RPS & Portofolio OBE (dashboard jadi bagian aplikasi)
Dashboard pemetaan statis kini dilayani aplikasi di /dashboard
(app/static/dashboard.html). Fitur lengkap Tahap 1-4 + dasbor monitoring,
menu global, filter semester, input nilai langsung, penggabungan nilai
team teaching, tabel bisa urut. Dockerfile berubah dari nginx statis ke
uvicorn/FastAPI: healthcheck, proxy-headers, volume /data (SQLite).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-18 12:05:28 +07:00

141 lines
6.5 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 isi %}
<div class="kpi-baris" title="Persentase dihitung dari MK pada semester terpilih — hijau ≥ 75 %, kuning 4074 %, merah < 40 %">
<div><b id="kpi-mk">{{ daftar|length }}</b> <span>mata kuliah</span></div>
<div><b id="kpi-rps">{{ n_selesai }}</b> <span>RPS selesai</span> <span class="kpi-pct" id="pct-rps"></span></div>
<div><b id="kpi-draft">{{ n_draft }}</b> <span>RPS draft</span></div>
<div><b id="kpi-nilai">{{ st_nilai|length }}</b> <span>MK setor nilai</span> <span class="kpi-pct" id="pct-nilai"></span></div>
<div><b id="kpi-porto">{{ st_porto.values()|selectattr('status', 'equalto', 'selesai')|list|length }}</b> <span>portofolio selesai</span> <span class="kpi-pct" id="pct-porto"></span></div>
</div>
{% if user.role == 'dosen' %}
{% if hanya_saya %}
<div class="info" style="margin-top:0">Menampilkan <b>{{ daftar|length }} MK yang Anda ampu</b>.
RPS mata kuliah lain tetap terbuka baca — <a href="/?semua=1">tampilkan seluruh {{ n_total }} MK</a>.</div>
{% elif mk_saya %}
<div class="info" style="margin-top:0">Menampilkan seluruh {{ n_total }} MK —
<a href="/">kembali ke MK yang Anda ampu saja</a>.</div>
{% else %}
<div class="peringatan" style="margin-top:0">Belum ada MK yang ditugaskan kepada
Anda — hubungi kaprodi agar menetapkan penugasan di halaman Kelola Akun.
Sementara itu seluruh MK ditampilkan (hanya-baca).</div>
{% endif %}
{% endif %}
<div class="filter-baris">
<label for="filter-smt">Tampilkan semester:</label>
<select id="filter-smt" style="width:auto">
<option value="">Semua</option>
<option value="ganjil">Ganjil (1, 3, 5, 7)</option>
<option value="genap">Genap (2, 4, 6, 8)</option>
{% for s in smt.values()|select|unique|sort %}
<option value="{{ s }}">Semester {{ s }}</option>
{% endfor %}
</select>
<span id="filter-info" class="keterangan" style="margin:0"></span>
</div>
{% macro stempel(st) -%}
{% if st and st.status == 'selesai' %}<span class="stamp stamp-ok">SELESAI</span>
{% elif st %}<span class="stamp stamp-draft">DRAFT</span>
{% else %}<span class="stamp stamp-no">BELUM</span>{% endif %}
{%- endmacro %}
<div class="gulir">
<table class="grid">
<thead>
<tr>
<th style="width:56px">No.</th>
<th style="width:112px">Kode</th>
<th class="tengah" style="width:52px">Smt</th>
<th>Mata Kuliah</th>
<th style="width:150px">Pengampu</th>
<th style="width:120px">CPL Dipikul</th>
<th class="tengah" style="width:104px">RPS</th>
<th class="tengah" style="width:104px">Nilai</th>
<th class="tengah" style="width:104px">Portofolio</th>
<th style="width:170px">Pemutakhiran Terakhir</th>
</tr>
</thead>
<tbody>
{% for mk in daftar %}
{% set st = status.get(mk.kode) %}
{% set sn = st_nilai.get(mk.kode) %}
{% set sp = st_porto.get(mk.kode) %}
<tr data-smt="{{ smt.get(mk.kode) or '' }}"
data-rps="{{ st.status if st else '' }}"
data-nilai="{{ sn.status if sn else '' }}"
data-porto="{{ sp.status if sp else '' }}">
<td class="tengah">{{ mk.no }}</td>
<td>{{ mk.kode }}</td>
<td class="tengah">{{ smt.get(mk.kode) or '—' }}</td>
<td>{{ mk.nama }}<br>
<span style="font-size:12px;color:var(--grey)">{{ mk.n_cpmk }} CPMK · {{ mk.n_sub }} Sub-CPMK</span>
</td>
<td style="font-size:13px">{{ pengampu.get(mk.kode, [])|join(', ') or '—' }}</td>
<td>{{ mk.cpls|map('replace','CPL-','')|join(', ') }}</td>
<td class="tengah"><a href="/rps/{{ mk.kode }}" style="text-decoration:none">{{ stempel(st) }}</a></td>
<td class="tengah"><a href="/nilai/{{ mk.kode }}" style="text-decoration:none">{{ stempel(sn) }}</a></td>
<td class="tengah"><a href="/portofolio/{{ mk.kode }}" style="text-decoration:none">{{ stempel(sp) }}</a></td>
<td style="font-size:12.5px">
{% set terakhir = [st, sn, sp]|select|sort(attribute='updated_at')|last %}
{% if terakhir %}{{ terakhir.updated_at }}{% if terakhir.updated_by %} — {{ terakhir.updated_by }}{% endif %}{% else %}—{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<script>
(function () {
var pilih = document.getElementById('filter-smt');
var info = document.getElementById('filter-info');
var baris = document.querySelectorAll('table.grid tbody tr');
function cocok(tr, v) {
var smt = tr.dataset.smt;
return v === '' ? true :
v === 'ganjil' ? smt !== '' && smt % 2 === 1 :
v === 'genap' ? smt !== '' && smt % 2 === 0 :
smt === v;
}
function isiPct(id, n, total) {
var el = document.getElementById(id);
if (!total) { el.textContent = ''; el.className = 'kpi-pct'; return; }
var p = Math.round(100 * n / total);
el.textContent = p + ' %';
el.className = 'kpi-pct ' +
(p >= 75 ? 'kpi-hijau' : p >= 40 ? 'kpi-kuning' : 'kpi-merah');
}
function perbarui() {
var v = pilih.value, n = 0, rSel = 0, rDraft = 0, nilai = 0, porto = 0;
baris.forEach(function (tr) {
var tampil = cocok(tr, v);
tr.style.display = tampil ? '' : 'none';
if (!tampil) return;
n++;
if (tr.dataset.rps === 'selesai') rSel++;
if (tr.dataset.rps === 'draft') rDraft++;
if (tr.dataset.nilai !== '') nilai++;
if (tr.dataset.porto === 'selesai') porto++;
});
document.getElementById('kpi-mk').textContent = n;
document.getElementById('kpi-rps').textContent = rSel;
document.getElementById('kpi-draft').textContent = rDraft;
document.getElementById('kpi-nilai').textContent = nilai;
document.getElementById('kpi-porto').textContent = porto;
isiPct('pct-rps', rSel, n);
isiPct('pct-nilai', nilai, n);
isiPct('pct-porto', porto, n);
info.textContent = v === '' ? '' : n + ' dari ' + baris.length + ' MK ditampilkan';
}
pilih.addEventListener('change', perbarui);
perbarui();
})();
</script>
<p class="keterangan" style="margin-top:10px">Klik stempel untuk membuka form terkait. Alur per MK: <b>RPS</b> (rencana &amp; bobot) → <b>Nilai</b> (unggah Excel / rata-rata) → <b>Portofolio</b> (refleksi &amp; dokumen akhir). Rekap prodi menggulung otomatis dari nilai yang masuk.</p>
{% endblock %}