Files
OBE-Mapping/app/templates/admin.html
T
Power BI Dev 5f615aa7e8 Rapikan tampilan nilai, ganti NPM->NIM, fitur ganti password, kunci RPS view-only
- Tampilan "Isi Per Komponen Penilaian" diganti dari tombol bertumpuk teks
  jadi tabel (Komponen/Sub-CPMK/Bobot/Status/Aksi) dengan status berupa
  stempel, lebih mudah dipindai.
- Filter pencarian MK (ketik kode/nama) di kotak penugasan dosen /admin.
- Istilah NPM diseragamkan jadi NIM di seluruh aplikasi (label, header,
  template Excel, dokumen portofolio), termasuk migrasi kolom database
  mahasiswa.npm -> nim yang aman (ALTER TABLE RENAME COLUMN, otomatis saat
  startup, tanpa kehilangan data; parser Excel tetap terima file lama
  berheader NPM).
- Fitur ganti password mandiri (/akun): verifikasi password lama, sesi lain
  otomatis keluar setelah berhasil, sesi yang dipakai tetap aktif.
- RPS: dosen RPS+MK kini bisa menyesuaikan redaksi kalimat Sub-CPMK (bagian
  D) untuk MK yang diampu -- kode & kaitan CPMK/CPL tetap dari kurikulum,
  hanya teksnya yang bisa disesuaikan. Penyesuaian otomatis konsisten di
  dropdown mingguan (F), checklist komponen (G), serta unduhan RPS &
  portofolio, tanpa menyentuh data kurikulum induk atau halaman nilai/
  dashboard prodi.
- Form RPS dibungkus <fieldset disabled> untuk dosen tanpa hak edit --
  seluruh field benar-benar terkunci (bukan cuma tombol simpan hilang).
2026-07-22 08:37:54 +07:00

137 lines
6.6 KiB
HTML

{% extends "base.html" %}
{% block judul %}Kelola Akun &amp; Mahasiswa — RPS &amp; Portofolio UNTAN{% endblock %}
{% block isi %}
<h1>Kelola Akun &amp; Mahasiswa</h1>
{% if pesan %}<div class="info">{{ pesan }}</div>{% endif %}
<h2 class="bagian">A. Akun Pengguna &amp; Penugasan Mata Kuliah</h2>
<p class="keterangan"><b>Dosen RPS + MK</b> dapat mengedit RPS, nilai, dan portofolio
pada MK yang ditugaskan. <b>Dosen MK</b> hanya dapat mengedit nilai &amp; portofolio
pada MK yang ditugaskan — halaman RPS terbuka hanya untuk dibaca. Kaprodi dapat
mengakses semuanya. Kosongkan kolom kelas bila dosen mengampu <b>seluruh kelas</b>
MK itu; isi (mis. "A" atau "A,B") bila dosen hanya boleh melihat &amp; menilai
mahasiswa kelas tertentu — cocok untuk MK dengan beberapa kelas paralel yang
diampu dosen berbeda.</p>
<div class="gulir">
<table class="grid">
<thead>
<tr>
<th style="width:170px">Nama</th>
<th style="width:160px">NIP</th>
<th style="width:130px">Peran</th>
<th>MK yang Diampu</th>
<th style="width:340px">Tindakan</th>
</tr>
</thead>
<tbody>
{% for p in pengguna %}
<tr>
<td>{{ p.nama }}</td>
<td>{{ p.username }}</td>
<td>{{ {'kaprodi': 'Kaprodi', 'dosen_rps_mk': 'Dosen RPS + MK', 'dosen_mk': 'Dosen MK'}[p.role] }}</td>
<td>
{% if p.role == 'kaprodi' %}
<i style="color:var(--grey)">semua MK</i>
{% else %}
{% if p.mk %}
{% for kode in p.mk %}{{ kode }}{% if p.kelas_map.get(kode) %} (kelas {{ p.kelas_map[kode] }}){% endif %}{{ ', ' if not loop.last }}{% endfor %}
{% else %}—{% endif %}
<details style="margin-top:6px">
<summary style="cursor:pointer;color:var(--blue);font-size:13px">Ubah penugasan…</summary>
<form method="post" action="/admin/pengguna/{{ p.id }}/tugas">
<input type="text" class="cari-mk" placeholder="Cari kode/nama MK…"
style="width:260px;margin:6px 0 4px;font-size:13px">
<div class="pilih-sub" style="max-height:220px;margin:0 0 6px">
{% for mk in daftar %}
<div class="baris-mk" data-cari="{{ (mk.kode ~ ' ' ~ mk.nama)|lower }}">
<label><input type="checkbox" name="mk" value="{{ mk.kode }}"
{% if mk.kode in p.mk %}checked{% endif %}> {{ mk.kode }} — {{ mk.nama }}</label>
<input type="text" name="kelas_{{ mk.kode }}" placeholder="kelas (kosong = semua)"
value="{{ p.kelas_map.get(mk.kode, '') }}" style="width:150px;margin:0 0 4px 22px;font-size:12px">
</div>
{% endfor %}
<p class="cari-kosong" style="display:none;color:var(--grey);font-size:13px;margin:4px 0">Tidak ada MK yang cocok.</p>
</div>
<button class="tombol garis" type="submit" style="padding:4px 12px;font-size:13px">Simpan Penugasan</button>
</form>
</details>
{% endif %}
</td>
<td>
<form method="post" action="/admin/pengguna/{{ p.id }}/reset"
style="display:inline">
<button class="tombol garis" type="submit"
style="padding:4px 10px;font-size:13px">Reset Password ke NIP</button>
</form>
{% if p.id != user.id %}
<form method="post" action="/admin/pengguna/{{ p.id }}/hapus"
style="display:inline" onsubmit="return confirm('Hapus akun {{ p.nama }} ({{ p.username }})?')">
<button class="tombol garis" type="submit"
style="padding:4px 10px;font-size:13px;color:var(--red);border-color:var(--red)">Hapus</button>
</form>
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<h2 class="bagian">B. Buat Akun Baru</h2>
<p class="keterangan">NIP menjadi username sekaligus password awal — dosen langsung
bisa masuk dengan NIP-nya.</p>
<form method="post" action="/admin/pengguna" class="aksi-baris"
style="border-top:none;padding-top:0;margin-top:8px">
<label>Nama lengkap <input type="text" name="nama" required style="width:230px"></label>
<label>NIP <input type="text" name="nip" required style="width:180px"></label>
<label>Peran
<select name="role" style="width:150px">
<option value="dosen_mk" selected>Dosen MK</option>
<option value="dosen_rps_mk">Dosen RPS + MK</option>
<option value="kaprodi">Kaprodi</option>
</select>
</label>
<button class="tombol" type="submit">Buat Akun</button>
</form>
<h2 class="bagian">C. Daftar Mahasiswa Mengontrak MK — {{ tahun }}</h2>
<p class="keterangan">Unggah satu file Excel berisi seluruh kontrak semester ini
(kolom: NIM, Nama, Kode MK, Kelas — satu baris per kontrak). Impor baru
<b>menggantikan seluruh daftar</b> tahun ajaran aktif. Daftar ini dipakai untuk
mengisi otomatis template nilai tiap MK.</p>
<div class="aksi-baris" style="border-top:none;padding-top:0;margin-top:8px">
<a class="tombol garis" href="/admin/mahasiswa/template.xlsx">Unduh Template Daftar Mahasiswa (.xlsx)</a>
<form method="post" action="/admin/mahasiswa" enctype="multipart/form-data"
style="display:inline-flex;gap:10px;align-items:center">
<input type="file" name="berkas" accept=".xlsx" required>
<button class="tombol" type="submit">Impor &amp; Ganti Daftar</button>
</form>
</div>
{% if mhs %}
<div class="gulir" style="margin-top:12px">
<table class="grid" style="max-width:720px">
<thead>
<tr><th style="width:120px">Kode MK</th><th class="tengah" style="width:130px">Mahasiswa</th><th>Kelas</th></tr>
</thead>
<tbody>
{% for mk in daftar if mk.kode in mhs %}
<tr>
<td>{{ mk.kode }} — {{ mk.nama }}</td>
<td class="tengah">{{ mhs[mk.kode].n }}</td>
<td>{{ mhs[mk.kode].kelas|join(', ') if mhs[mk.kode].kelas else '—' }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% else %}
<p class="keterangan">Belum ada daftar mahasiswa untuk tahun ajaran ini.</p>
{% endif %}
<h2 class="bagian">D. Cadangan Data</h2>
<p class="keterangan">Unduh salinan seluruh isian secara berkala dan simpan di
tempat aman — terutama sebelum pemutakhiran aplikasi atau pergantian server.</p>
<a class="tombol garis" href="/cadangan.json">Unduh cadangan JSON (seluruh isian)</a>
<a class="tombol garis" href="/cadangan.db">Unduh cadangan SQLite (file DB mentah)</a>
{% endblock %}