`, '/riwayat-rps': `

Riwayat RPS

Pantau semua RPS yang pernah dibuat.

Riwayat RPS

Daftar RPS per semester dan mata kuliah.

Pratinjau modul riwayat: status proses, lanjutkan draft, arsip, dan unduh ulang.

`, '/contoh-template': `

Contoh & Template

Lihat format RPS yang bisa dipakai.

Contoh & Template

Galeri contoh RPS dan template kampus.

Dosen bisa melihat contoh sebelum mendaftar dan memilih format kampus.

`, '/dompet': `

Dompet & Beli Koin

Pantau saldo, beli paket koin, dan lihat riwayat transaksi.

Saldo koin tersedia

4.825

Setara 193 kali generate RPS (tarif 25 koin per generate, diatur admin).

Rincian

Saldo tercatat
4.850 koin
Koin ditahan (generate berjalan)
−25 koin
Siap dipakai
4.825 koin

Terpakai 30 hari

225 koin

Koin yang sudah dipotong untuk generate RPS yang berhasil.

Total pembelian

5.000 koin

Akumulasi pembelian paket koin pada akun ini.

Total pengembalian

75 koin

Koin dikembalikan otomatis saat generate gagal atau waktu habis.

Data pada halaman ini masih tiruan untuk keperluan tampilan. Saldo nyata akan diambil dari transaksi koin saat modul backend dompet dibangun.

Notifikasi koin masuk

Saldo koin bertambah otomatis begitu pembayaran berhasil. Kabar ini muncul di sini supaya Anda tidak perlu memeriksa saldo berulang kali.

1 notifikasi baru

  • 1.000 koin sudah masuk ke saldo Baru

    Pembayaran QRIS untuk transaksi TRX-2026-1009-003 terverifikasi otomatis. Saldo koin bertambah tanpa perlu konfirmasi manual.

    1.000 koin · 09 Okt 2026, 13.00

  • 3.000 koin sudah masuk ke saldo

    Pembayaran Virtual Account BCA untuk transaksi TRX-2026-1002-001 terverifikasi otomatis. Saldo koin bertambah tanpa perlu konfirmasi manual.

    3.000 koin · 02 Okt 2026, 11.20

Notifikasi ini masih tiruan. Nanti diisi oleh callback pembayaran pada task backend, sehingga dosen menerima kabar yang sama di semua perangkat.

Beli paket koin

Pilih paket koin, lalu pilih cara pembayaran. Koin langsung masuk ke saldo setelah pembayaran berhasil.

Paket terpilih: 2.000 koin seharga Rp1.850.000 (Rp925 per koin, setara 80 kali generate RPS).

Paling populer
Hemat terbesar

Perbandingan paket koin

Harga per koin dihitung dari harga paket, sehingga paket besar terlihat lebih murah per koin. Paket yang sedang dipilih ditandai dan tombol pilihnya dinonaktifkan sampai Anda memilih paket lain.

Perbandingan empat paket koin: jumlah koin, harga, harga per koin, setara generate, dan penghematan.
PaketJumlah koinHargaHarga per koinSetara generateHematAksi
1.000 koin 1.000 Rp1.000.000 Rp1.000 40 kali Harga dasar
2.000 koinSedang dipilih 2.000 Rp1.850.000 Rp925 80 kali 8%
3.000 koin 3.000 Rp2.750.000 Rp917 120 kali 8%
5.000 koinTermurah per koin 5.000 Rp4.500.000 Rp900 200 kali 10%
Metode pembayaran

Instruksi pembayaran: QRIS

Kode QR contoh · NMID ID1024398712 · instruksi berlaku 15 menit

  1. Buka aplikasi pembayaran atau m-banking yang mendukung QRIS.
  2. Pilih menu Bayar, lalu pindai kode QR yang tampil di halaman ini.
  3. Periksa nama merchant dan nominal sebelum melanjutkan.
  4. Selesaikan pembayaran, koin otomatis masuk ke saldo dompet Anda.

Instruksi ini masih contoh untuk tampilan. Nomor tujuan dan kode pembayaran asli dibuat sistem setelah alur pembayaran aktif.

Status transaksi pembelian koin

Setiap percobaan pembelian koin punya status yang jelas, supaya Anda tahu apakah koin sudah masuk, masih menunggu, atau perlu dibuat ulang.

1 transaksi menunggu pembayaran · 2 transaksi berhasil · 1 transaksi kedaluwarsa.

  • 5.000 koin · Rp4.500.000

    TRX-2026-1010-004 · 10 Okt 2026, 08.55 · Virtual Account BCA

    Menunggu pembayaran

    Instruksi masih berlaku sampai pukul 11:55. Koin belum bertambah sebelum pembayaran terverifikasi.

  • 1.000 koin · Rp1.000.000

    TRX-2026-1009-003 · 09 Okt 2026, 13.00 · QRIS

    Berhasil

    Pembayaran terverifikasi pukul 13:05 dan 1.000 koin langsung masuk ke saldo.

  • 2.000 koin · Rp1.850.000

    TRX-2026-1008-002 · 08 Okt 2026, 09.30 · Dompet digital

    Kedaluwarsa

    Tidak dibayar sampai masa berlaku habis. Tidak ada koin terpotong, transaksi bisa dibuat ulang.

  • 3.000 koin · Rp2.750.000

    TRX-2026-1002-001 · 02 Okt 2026, 11.20 · Virtual Account BCA

    Berhasil

    Pembayaran terverifikasi pukul 11:24 dan 3.000 koin masuk ke saldo.

Arti status

Menunggu pembayaran
Instruksi pembayaran masih berlaku, koin belum bertambah.
Berhasil
Pembayaran terverifikasi dan koin sudah masuk ke saldo.
Kedaluwarsa
Instruksi tidak dibayar sampai masa berlaku habis, tidak ada koin terpotong.
Gagal
Pembayaran ditolak, silakan coba lagi dengan metode lain.

Riwayat pemakaian koin per RPS

Ketahui berapa koin yang terpakai untuk tiap RPS, termasuk RPS yang gagal dan koinnya sudah dikembalikan, serta rekapnya per semester dan program studi.

10 generate tercatat.

Koin terpakai

150 koin

Dari 10 kali generate yang berhasil.

Koin dikembalikan

75 koin

Generate gagal, koin kembali penuh ke saldo.

Koin ditahan

25 koin

Masih menunggu hasil generate yang sedang berjalan.

Rekap per semester

Pemakaian koin dikelompokkan per semester.
SemesterGenerateKoin terpakaiDikembalikanDitahan
Ganjil 2026/2027 9 125 75 25
Genap 2026/2027 1 25 0 0

Rekap per program studi

Pemakaian koin dikelompokkan per program studi.
Program studiGenerateKoin terpakaiDikembalikanDitahan
Teknik Informatika 5 75 25 25
Akuntansi 1 25 0 0
Ilmu Keperawatan 1 0 25 0
Ilmu Komunikasi 1 25 0 0
Manajemen 1 25 0 0
Teknik Elektro 1 0 25 0

Rincian tiap generate

Daftar pemakaian koin untuk setiap RPS yang pernah digenerate.
Mata kuliahProgram studiSemesterTanggalKoinStatus
Struktur Data LanjutTeknik InformatikaGanjil 2026/202710 Okt 202625 koinMasih diproses
Pemrograman Web LanjutTeknik InformatikaGanjil 2026/202709 Okt 202625 koinKoin dikembalikan
Pemrograman Berorientasi ObjekTeknik InformatikaGanjil 2026/202709 Okt 202625 koinSelesai
Sistem Basis DataTeknik InformatikaGanjil 2026/202708 Okt 202625 koinSelesai
Keperawatan DasarIlmu KeperawatanGanjil 2026/202707 Okt 202625 koinKoin dikembalikan
Manajemen KeuanganManajemenGenap 2026/202705 Okt 202625 koinSelesai
Metodologi Penelitian SosialIlmu KomunikasiGanjil 2026/202703 Okt 202625 koinSelesai
Sistem Kendali OtomatisTeknik ElektroGanjil 2026/202728 Sep 202625 koinKoin dikembalikan
Akuntansi BiayaAkuntansiGanjil 2026/202725 Sep 202625 koinSelesai
Algoritma dan Struktur DataTeknik InformatikaGanjil 2026/202722 Sep 202625 koinSelesai

6 selesai · 3 dikembalikan · 1 masih diproses.

Riwayat koin

Catatan setiap koin yang masuk dari pembelian atau pengembalian, dan koin yang terpakai untuk generate RPS.

Mutasi koin terbaru beserta saldo setelah setiap transaksi.
WaktuKeteranganJenisKoinSaldo
2026-10-10 08:40 Pengembalian koinGenerate Pemrograman Web Lanjut gagal (layanan AI tidak merespons) Pengembalian koin +25 4.850
2026-10-09 14:20 Generate RPSPemrograman Web Lanjut Pemakaian generate −25 4.825
2026-10-09 13:05 Generate RPSPemrograman Berorientasi Objek Pemakaian generate −25 4.850
2026-10-08 14:02 Generate RPSSistem Basis Data Pemakaian generate −25 4.875
2026-10-07 09:41 Pengembalian koinGenerate Keperawatan Dasar gagal (layanan AI tidak merespons) Pengembalian koin +25 4.900
2026-10-07 09:12 Generate RPSKeperawatan Dasar Pemakaian generate −25 4.875
2026-10-05 16:33 Generate RPSManajemen Keuangan Pemakaian generate −25 4.900
2026-10-03 15:30 Generate RPSMetodologi Penelitian Sosial Pemakaian generate −25 4.925
2026-10-02 11:20 Pembelian Paket 3.000 koinVirtual Account BCA Pembelian koin +3.000 4.950
2026-09-28 09:41 Pengembalian koinGenerate Sistem Kendali Otomatis gagal (waktu habis) Pengembalian koin +25 1.950
2026-09-28 09:12 Generate RPSSistem Kendali Otomatis Pemakaian generate −25 1.925
2026-09-25 13:40 Generate RPSAkuntansi Biaya Pemakaian generate −25 1.950
2026-09-22 10:05 Generate RPSAlgoritma dan Struktur Data Pemakaian generate −25 1.975
2026-09-20 08:15 Pembelian Paket 2.000 koinQRIS Pembelian koin +2.000 2.000

Koin dikembalikan otomatis bila pembuatan RPS gagal atau terlalu lama, sehingga tidak ada koin yang hilang tanpa hasil.

`, '/profil': `

Profil & Institusi

Kelola identitas dosen dan kampus.

Profil & Institusi

Data dosen dan kampus.

Simpan profil agar data RPS otomatis terisi di generate berikutnya.

` }; function routePath(){return location.hash.replace('#','')||'/';} function render(){const app=document.getElementById('app'); const p=routes[routePath()]?routePath():'/'; app.innerHTML=routes[p]; const layout=app; const page=app; try{ const shell = layout.querySelector('.shell'); const toggle = layout.querySelector('[data-action="rps-shell:nav-toggle"]'); const closeNav = () => { shell.dataset.nav = 'closed'; if (toggle) toggle.setAttribute('aria-expanded', 'false'); }; layout.addEventListener('click', (event) => { const trigger = event.target instanceof Element ? event.target.closest('[data-action]') : null; if (!trigger) return; if (trigger.dataset.action === 'rps-shell:nav-toggle') { const open = shell.dataset.nav !== 'open'; shell.dataset.nav = open ? 'open' : 'closed'; if (toggle) toggle.setAttribute('aria-expanded', String(open)); return; } if (trigger.dataset.action === 'rps-shell:nav-close') { closeNav(); return; } if (trigger.closest('.shell-link') && matchMedia('(max-width:1023px)').matches) closeNav(); }); }catch(e){} try{if(p==='/'){ const site = root.querySelector('.site'); const dialog = root.querySelector('.dialog'); const gallery = root.querySelector('[data-node-id="rps-beranda.contoh.gallery"]'); const empty = root.querySelector('[data-empty]'); const status = root.querySelector('[data-result-count]'); const chips = root.querySelectorAll('[data-action="rps:filter"]'); const WEEK_PLAN = [ 'Kontrak kuliah & pengantar', 'Konsep dasar dan ruang lingkup', 'Analisis kasus terpandu', 'Praktikum / tugas terstruktur', 'Presentasi kelompok', 'Ujian Tengah Semester', 'Studi kasus lanjutan', 'Ujian Akhir Semester' ]; const setCounts = () => { const cards = gallery.querySelectorAll('.sample'); const totals = { Semua: cards.length }; cards.forEach((card) => { const field = card.dataset.field; totals[field] = (totals[field] || 0) + 1; }); root.querySelectorAll('[data-count]').forEach((el) => { const key = el.dataset.count; el.textContent = String(totals[key] ?? 0); }); }; const applyFilter = (field, announce = true) => { let visible = 0; gallery.querySelectorAll('.sample').forEach((card) => { const match = field === 'Semua' || card.dataset.field === field; card.hidden = !match; if (match) visible += 1; }); chips.forEach((chip) => chip.setAttribute('aria-pressed', String(chip.dataset.field === field))); const isFiltered = field !== 'Semua'; empty.hidden = visible > 0; gallery.hidden = visible === 0; if (announce && status) { status.textContent = visible === 0 ? 'Tidak ada contoh RPS untuk bidang ' + field + '.' : 'Menampilkan ' + visible + ' contoh RPS' + (isFiltered ? ' bidang ' + field : '') + '.'; } }; const row = (label, value, head = false) => '
' + '' + label + '' + '' + value + '' + '' + (head ? 'Bobot' : '') + '' + '
'; const openPreview = (card) => { const data = card.dataset; dialog.querySelector('[data-preview="title"]').textContent = data.sample; dialog.querySelector('[data-preview="meta"]').textContent = data.prodi + ' · ' + data.semester + ' · ' + data.weeks + ' minggu perkuliahan'; dialog.querySelector('[data-preview="identity"]').innerHTML = '
Mata kuliah
' + data.sample + '
' + '
Program studi
' + data.prodi + '
' + '
Semester
' + data.semester + '
' + '
Jumlah minggu
' + data.weeks + ' minggu
'; dialog.querySelector('[data-preview="outcomes"]').innerHTML = '
CPL
' + data.cpl + ' capaian pembelajaran lulusan
' + '
CPMK
' + data.cpmk + ' capaian pembelajaran mata kuliah
' + '
Sub-CPMK
Diturunkan per minggu mengikuti CPMK
'; const weekCount = Number(data.weeks); const shown = Math.min(8, weekCount); let rows = row('Minggu', 'Materi pembelajaran', true); for (let i = 1; i <= shown; i += 1) { const title = WEEK_PLAN[(i - 1) % WEEK_PLAN.length]; rows += '
' + i + '' + title + '' + (i === 6 ? 'UTS' : i === weekCount ? 'UAS' : (i % 2 === 0 ? '5%' : '—')) + '
'; } dialog.querySelector('[data-preview="weeks"]').innerHTML = rows; dialog.querySelector('[data-preview="assessment"]').textContent = 'Komposisi penilaian: ' + data.assessment + '. Bobot akhir mengikuti ketentuan program studi.'; dialog.showModal(); }; root.addEventListener('click', (event) => { const trigger = event.target instanceof Element ? event.target.closest('[data-action]') : null; if (!trigger || !root.contains(trigger)) return; const action = trigger.dataset.action; if (action === 'rps:filter') { applyFilter(trigger.dataset.field); return; } if (action === 'rps:preview') { const card = trigger.closest('.sample'); if (card) openPreview(card); return; } if (action === 'rps:close') { if (dialog.open) dialog.close(); } }, { signal }); dialog.addEventListener('click', (event) => { if (event.target === dialog) dialog.close(); }, { signal }); root.querySelectorAll('a[href^="#"]').forEach((link) => { if (link.dataset.routeLink !== undefined) return; link.addEventListener('click', (event) => { const target = root.querySelector(link.getAttribute('href')); if (!target) return; event.preventDefault(); const reduce = matchMedia('(prefers-reduced-motion: reduce)').matches; target.scrollIntoView({ behavior: reduce ? 'auto' : 'smooth', block: 'start' }); }, { signal }); }); setCounts(); applyFilter('Semua', false); }}catch(e){console.warn(e)} try{if(p==='/dompet'){ const dompet = root.querySelector('.dompet'); const paketButtons = dompet.querySelectorAll('[data-action="dompet:pilih-paket"]'); const ringkasan = dompet.querySelector('[data-node-id="rps-dompet.paket.ringkasan"]'); const notice = ringkasan.querySelector('[data-ringkasan="notice"]'); const statusLine = dompet.querySelector('[data-ringkasan="status"]'); const TARIF = 25; const INSTRUKSI = { 'QRIS': { tujuan: 'Kode QR contoh · NMID ID1024398712', masa: 15, langkah: [ 'Buka aplikasi pembayaran atau m-banking yang mendukung QRIS.', 'Pilih menu Bayar, lalu pindai kode QR yang tampil di halaman ini.', 'Periksa nama merchant dan nominal sebelum melanjutkan.', 'Selesaikan pembayaran, koin otomatis masuk ke saldo dompet Anda.' ] }, 'Virtual Account': { tujuan: 'Nomor VA contoh 8808 0812 3456 7890 (BCA)', masa: 180, langkah: [ 'Pilih bank yang akan dipakai, lalu salin nomor Virtual Account.', 'Bayar lewat m-banking, ATM, atau kantor cabang dengan nominal tepat.', 'Simpan bukti transfer sampai koin masuk ke saldo.', 'Koin masuk otomatis maksimal 3 menit setelah pembayaran terverifikasi.' ] }, 'Dompet digital': { tujuan: 'Nomor tujuan contoh 0812-3456-7890', masa: 30, langkah: [ 'Pilih aplikasi dompet digital yang ingin dipakai.', 'Tekan tombol bayar, lalu konfirmasi nominal di aplikasi tersebut.', 'Masukkan PIN atau verifikasi sidik jari sesuai kebijakan aplikasi.', 'Kembali ke halaman dompet, saldo koin bertambah otomatis.' ] } }; const sinkronNotif = () => { const item = [...dompet.querySelectorAll('[data-notif-item]')]; const belumDibaca = item.filter((el) => el.dataset.baru === 'true'); const statusNotif = dompet.querySelector('[data-notif="status"]'); const kosong = dompet.querySelector('[data-notif="kosong"]'); const tombolSemua = dompet.querySelector('[data-action="dompet:baca-semua"]'); if (statusNotif) { statusNotif.textContent = belumDibaca.length > 0 ? belumDibaca.length + ' notifikasi baru' : 'Semua notifikasi sudah dibaca'; } if (kosong) kosong.hidden = belumDibaca.length > 0; if (tombolSemua) tombolSemua.disabled = belumDibaca.length === 0; item.forEach((el) => { const baru = el.dataset.baru === 'true'; const tombol = el.querySelector('[data-action="dompet:baca-notif"]'); const tag = el.querySelector('.notif-title .status'); if (tombol) tombol.hidden = !baru; if (tag) tag.hidden = !baru; }); }; const sinkronInstruksi = () => { const metode = dompet.querySelector('[data-action="dompet:pilih-metode"]:checked'); if (!metode) return; const nama = metode.dataset.metode; const isi = INSTRUKSI[nama]; if (!isi) return; const panel = dompet.querySelector('[data-node-id="rps-dompet.paket.instruksi"]'); panel.querySelector('[data-instruksi="nama"]').textContent = nama; panel.querySelector('[data-instruksi="tujuan"]').textContent = isi.tujuan; panel.querySelector('[data-instruksi="masa"]').textContent = String(isi.masa); panel.querySelector('[data-instruksi="langkah"]').innerHTML = isi.langkah.map((langkah) => '
  • ' + langkah + '
  • ').join(''); }; const angka = (teks) => Number(String(teks).replace(/\./g, '')); const rupiah = (nilai) => 'Rp' + new Intl.NumberFormat('id-ID').format(nilai); const sinkronRingkasan = () => { const paket = dompet.querySelector('[data-action="dompet:pilih-paket"][aria-checked="true"]'); const metode = dompet.querySelector('[data-action="dompet:pilih-metode"]:checked'); if (!paket || !metode) return; const koin = angka(paket.dataset.koin); const harga = angka(paket.dataset.harga); const biaya = Number(metode.dataset.biaya); const rate = Math.round(harga / koin); const generate = Math.floor(koin / TARIF); ringkasan.querySelector('[data-ringkasan="koin"]').textContent = paket.dataset.koin + ' koin'; ringkasan.querySelector('[data-ringkasan="harga"]').textContent = rupiah(harga); ringkasan.querySelector('[data-ringkasan="rate"]').textContent = rupiah(rate); ringkasan.querySelector('[data-ringkasan="generate"]').textContent = generate + ' kali'; ringkasan.querySelector('[data-ringkasan="metode"]').textContent = metode.dataset.metode; ringkasan.querySelector('[data-ringkasan="biaya"]').textContent = biaya > 0 ? rupiah(biaya) : 'Gratis'; ringkasan.querySelector('[data-ringkasan="total"]').textContent = rupiah(harga + biaya); if (statusLine) { statusLine.textContent = 'Paket terpilih: ' + paket.dataset.koin + ' koin seharga ' + rupiah(harga) + ' (' + rupiah(rate) + ' per koin, setara ' + generate + ' kali generate RPS).'; } }; const pilihPaket = (id) => { paketButtons.forEach((tombol) => { const dipilih = tombol.dataset.paket === id; const label = tombol.querySelector('.pill'); if (label) { tombol.setAttribute('aria-checked', String(dipilih)); label.textContent = dipilih ? '✓ Dipilih' : 'Pilih paket'; return; } tombol.setAttribute('aria-pressed', String(dipilih)); tombol.disabled = dipilih; tombol.textContent = dipilih ? 'Dipilih' : 'Pilih'; tombol.title = dipilih ? 'Paket ini sedang dipilih' : 'Pilih paket ini'; }); dompet.querySelectorAll('[data-paket-row]').forEach((baris) => { const dipilih = baris.dataset.paketRow === id; baris.setAttribute('aria-selected', String(dipilih)); const tag = baris.querySelector('[data-tag-pilih]'); if (tag) tag.hidden = !dipilih; }); notice.hidden = true; sinkronRingkasan(); }; dompet.addEventListener('click', (event) => { const trigger = event.target instanceof Element ? event.target.closest('[data-action]') : null; if (!trigger || !dompet.contains(trigger)) return; if (trigger.dataset.action === 'dompet:pilih-paket') { pilihPaket(trigger.dataset.paket); return; } if (trigger.dataset.action === 'dompet:baca-notif') { const item = trigger.closest('[data-notif-item]'); if (item) item.dataset.baru = 'false'; sinkronNotif(); return; } if (trigger.dataset.action === 'dompet:baca-semua') { dompet.querySelectorAll('[data-notif-item]').forEach((el) => { el.dataset.baru = 'false'; }); sinkronNotif(); return; } if (trigger.dataset.action === 'dompet:lanjut') { const paket = dompet.querySelector('[data-action="dompet:pilih-paket"][aria-checked="true"]'); const metode = dompet.querySelector('[data-action="dompet:pilih-metode"]:checked'); if (!paket || !metode) return; const total = Number(paket.dataset.harga) + Number(metode.dataset.biaya); notice.hidden = false; notice.textContent = 'Paket ' + paket.dataset.koin + ' koin dengan metode ' + metode.dataset.metode + ' menunggu total ' + rupiah(total) + '. Alur pembayaran dan penambahan saldo otomatis dibangun pada task berikutnya, jadi belum ada transaksi yang dijalankan.'; } }, { signal }); dompet.addEventListener('change', (event) => { const input = event.target instanceof Element ? event.target.closest('[data-action="dompet:pilih-metode"]') : null; if (!input) return; notice.hidden = true; sinkronRingkasan(); sinkronInstruksi(); }, { signal }); sinkronInstruksi(); sinkronNotif(); }}catch(e){console.warn(e)} document.title=p==='/'?'Generator RPS Dosen':'Generator RPS - '+(document.querySelector('.shell-heading h1')?.textContent||'Aplikasi');} window.addEventListener('hashchange',render); render();