Ujianonline

Platform ujian daring yang andal untuk sekolah, kampus, dan lembaga pelatihan. Didesain agar mudah digunakan, aman, dan mampu menangani skala peserta yang besar.

Fitur Utama

Kemampuan inti untuk menyusun ujian, mengawasi pelaksanaan, dan menilai hasil dengan cepat.

Bank Soal

Kelola bank soal multi-mata pelajaran, acak soal & opsi jawaban, serta dukungan berbagai tipe soal (PG, essay, matching).

Pengawasan

Batasi waktu, cegah kecurangan, dan pantau progres peserta secara real-time.

Laporan

Hasil otomatis dengan analitik nilai & export CSV untuk rekap cepat.

Simulation

Latihan menghadapi ujian sesungguhnya dengan bank soal kaya, hasil langsung, dan feedback yang membantu peningkatan belajar.

Simulation
Rp. 10.000 / Token
  • Bank Soal Lebih dari 500 Soal
  • Hasil ujian langsung di akhir
  • Feedback result & insight
  • Soal sesuai kisi-kisi resmi
  • Bayar sesuai rencana kamu
Coba Simulation
Gratis 10 Token Ujian

Flatform

Beralih dari ujian konvensional ke ujianonline terukur dengan fitur lengkap untuk administrasi, pembuatan soal, hingga pelaporan.

BASIC
/ bulan
Biaya per bulan hanya , Hemat per bulan

  • 50 Credit Token / bulan
  • Basic Feature Set
  • Maks. 50 Soal
  • Tipe soal: Multiple Choice, True/False
  • Free Subdomain
STARTER
/ bulan
Biaya per bulan hanya , Hemat per bulan

  • 100 Credit Token / bulan
  • Starter Feature Set
  • Maks. 100 Soal
  • Tipe soal: Multiple Choice, Multiple Response, True/False, Matching
  • Free Subdomain
PRO
/ bulan
Biaya per bulan hanya , Hemat per bulan

  • 200 Credit Token / bulan
  • Pro Feature Set
  • Maks. 200 Soal
  • Tipe soal: Multiple Choice, Multiple Response, True/False, Matching, Fill in the Blank, Audio / Listening Question, Essay, Ordering / Sequence
  • Free Subdomain
  • Create Event
  • Question Checker Area
  • Advance Data Analytic
  • Exam landing Page
ENTERPRISE
/ bulan
Biaya per bulan hanya , Hemat per bulan

  • 200 Credit Token / bulan
  • Pro Feature Set
  • Maks. 200 Soal
  • Tipe soal: Multiple Choice, Multiple Response, True/False, Matching, Fill in the Blank, Audio / Listening Question, Essay, Ordering / Sequence
  • Free Subdomain
  • Create Event
  • Question Checker Area
  • Advance Data Analytic
  • Exam landing Page
Biaya additional token ujian: Rp5.000 / token ujian.

Integrasi

Integrasi siap pakai untuk akses aman, pengelolaan soal massal, dan pelaporan.

Single Sign-On

Integrasi SSO (SAML/OAuth) untuk akses mudah dan aman bagi peserta & admin.

Import/Export Bank Soal

Kelola soal massal via template CSV/Excel untuk percepat persiapan ujian.

Laporan & Insight

Analitik nilai, rekap kehadiran, dan export hasil untuk keputusan cepat.

Manfaat Ujian Online

Dampak nyata bagi institusi pendidikan dan lembaga pelatihan dalam mengelola ujian.

Efisiensi Operasional

Otomatisasi penilaian, pengelolaan bank soal, dan ekspor laporan cepat.

Keamanan & Kepatuhan

Pengawasan ujian, kontrol akses, dan dukungan SSO untuk standar institusi.

Skalabilitas & Kemudahan

Mudah digunakan, ramah perangkat, dan siap untuk skala peserta besar.

try { // Only update billing on plan selection buttons (data-billing="true"); // Skip Simulation button so it doesn't get ?billing=monthly document.querySelectorAll('.btn-choose[data-billing="true"]').forEach(function(anchor){ (function(){ const fmt = (n) => new Intl.NumberFormat('id-ID').format(Number(n)); const setMode = (mode) => { document.querySelectorAll('.price-value').forEach(el => { const m = el.dataset.priceMonthly ? Number(el.dataset.priceMonthly) : 0; const yTotal = el.dataset.priceYearlyTotal ? Number(el.dataset.priceYearlyTotal) : 0; const yMonth = el.dataset.priceYearlyMonthly ? Number(el.dataset.priceYearlyMonthly) : 0; const pM = el.dataset.promoPriceMonthly ? Number(el.dataset.promoPriceMonthly) : 0; const pYTotal = el.dataset.promoPriceYearlyTotal ? Number(el.dataset.promoPriceYearlyTotal) : 0; const pYMonth = el.dataset.promoPriceYearlyMonthly ? Number(el.dataset.promoPriceYearlyMonthly) : 0; const card = el.closest('.card-body'); const cardRoot = el.closest('.card') || el.closest('.pricing-card'); const originalEl = card?.querySelector('.original-price'); if (mode === 'monthly') { // Prefer promo monthly price when available if (pM && pM > 0) { el.textContent = 'Rp' + fmt(pM); if (originalEl) { originalEl.textContent = 'Rp' + fmt(m); originalEl.classList.remove('d-none'); } } else { if (m && m > 0) { el.textContent = 'Rp' + fmt(m); if (originalEl) originalEl.classList.add('d-none'); } } const period = el.closest('.card-body')?.querySelector('.price-period'); if (period) period.textContent = '/ bulan'; // Ribbon: show promo or discounts when in monthly view if promo reduces monthly price const ribbonElMonthly = cardRoot?.querySelector('.ribbon'); if (ribbonElMonthly) { const monthlyPrice = m || 0; // For monthly view show ONLY the promotion discount (if any) const promoPct = (pM && pM > 0 && monthlyPrice) ? Math.round(((monthlyPrice - pM) / monthlyPrice) * 100) : 0; if (promoPct > 0) { ribbonElMonthly.textContent = `Diskon ${promoPct}%`; ribbonElMonthly.classList.remove('d-none'); ribbonElMonthly.setAttribute('aria-hidden', 'false'); } else { ribbonElMonthly.classList.add('d-none'); ribbonElMonthly.setAttribute('aria-hidden', 'true'); } } } else { // annual mode: show yearly total (or promo yearly total) if (pYTotal && pYTotal > 0) { el.textContent = 'Rp' + fmt(pYTotal); if (originalEl) { originalEl.textContent = 'Rp' + fmt(yTotal); originalEl.classList.remove('d-none'); } } else if (yTotal && yTotal > 0) { el.textContent = 'Rp' + fmt(yTotal); if (originalEl) originalEl.classList.add('d-none'); } const period = el.closest('.card-body')?.querySelector('.price-period'); if (period) period.textContent = '/ tahun'; // annual promo (extra discount) shown separately if specific promo yearly monthly exists // Extra-annual block removed: we'll surface any extra savings inside the annual-savings notification below const annBlock = card?.querySelector('.annual-savings'); if (annBlock) { const priceYearlyMonthly = yMonth || (yTotal ? Math.round(yTotal / 12) : 0); const promoYearlyMonthly = pYMonth || (pYTotal ? Math.round(pYTotal / 12) : 0); const promoMonthly = pM || 0; const monthlyPrice = m || 0; // Effective per-month price when paying yearly: prefer promo yearly monthly if available const effectiveMonthly = (promoYearlyMonthly && promoYearlyMonthly > 0) ? promoYearlyMonthly : priceYearlyMonthly; const nominalSaving = monthlyPrice > 0 ? Math.max(0, monthlyPrice - effectiveMonthly) : 0; const percentSaving = monthlyPrice > 0 ? Math.round((nominalSaving / monthlyPrice) * 100) : 0; const annMonthlyEl = annBlock.querySelector('.ann-monthly'); const savingsEl = annBlock.querySelector('.ann-savings-nominal'); if (annMonthlyEl) annMonthlyEl.textContent = 'Rp' + fmt(effectiveMonthly); if (savingsEl) savingsEl.textContent = 'Rp' + fmt(nominalSaving); // Ribbon: show composition of intrinsic annual discount + promo when applicable const ribbonEl = cardRoot?.querySelector('.ribbon'); if (ribbonEl) { const annualIntrinsicPct = priceYearlyMonthly && monthlyPrice && priceYearlyMonthly < monthlyPrice ? Math.round(((monthlyPrice - priceYearlyMonthly) / monthlyPrice) * 100) : 0; const promoPct = promoMonthly && monthlyPrice ? Math.round(((monthlyPrice - promoMonthly) / monthlyPrice) * 100) : (promoYearlyMonthly && monthlyPrice ? Math.round(((monthlyPrice - promoYearlyMonthly) / monthlyPrice) * 100) : 0); let text = ''; if (annualIntrinsicPct > 0 && promoPct > 0) text = `Diskon ${annualIntrinsicPct}% + ${promoPct}%`; else if (promoPct > 0) text = `Diskon ${promoPct}%`; else if (annualIntrinsicPct > 0) text = `Diskon ${annualIntrinsicPct}%`; if (text) { ribbonEl.textContent = text; ribbonEl.classList.remove('d-none'); ribbonEl.setAttribute('aria-hidden', 'false'); } else { ribbonEl.classList.add('d-none'); ribbonEl.setAttribute('aria-hidden', 'true'); } } } } }); // Update annual savings blocks: show per-month equivalent and percent saved vs monthly // Use Bootstrap's d-none class to reliably hide/show the notification (some CSS may force display) document.querySelectorAll('.annual-savings').forEach(block => { // Visibility only: content is populated per-card above when in annual mode if (mode === 'annual') { block.classList.remove('d-none'); block.setAttribute('aria-hidden', 'false'); } else { block.classList.add('d-none'); block.setAttribute('aria-hidden', 'true'); } }); // Update checkout links to include selected billing period so checkout step 1 can show correct price try { document.querySelectorAll('.btn-choose').forEach(function(anchor){ try { // Use the anchor href as base; create a URL and set the billing param var u = new URL(anchor.href, window.location.origin); u.searchParams.set('billing', mode === 'annual' ? 'annual' : 'monthly'); anchor.href = u.toString(); } catch(e) { // Fallback for unusual hrefs: replace or append billing param try { var raw = anchor.getAttribute('href') || ''; // remove existing billing param raw = raw.replace(/([?&])billing=[^&]*/i, '$1'); // clean trailing ? or & if left alone raw = raw.replace(/[?&]$/,''); if (raw.indexOf('?') === -1) anchor.setAttribute('href', raw + '?billing=' + (mode === 'annual' ? 'annual' : 'monthly')); else anchor.setAttribute('href', raw + '&billing=' + (mode === 'annual' ? 'annual' : 'monthly')); } catch(_){} } }); } catch(e){} }; const monthlyRadio = document.getElementById('billing-monthly'); const annualRadio = document.getElementById('billing-annual'); if (monthlyRadio && annualRadio) { const monthlyLabel = document.querySelector('label[for="billing-monthly"]'); const annualLabel = document.querySelector('label[for="billing-annual"]'); const updateToggleUI = () => { if (monthlyRadio.checked) { monthlyLabel.classList.add('active'); annualLabel.classList.remove('active'); } else { annualLabel.classList.add('active'); monthlyLabel.classList.remove('active'); } }; monthlyRadio.addEventListener('change', () => { if (monthlyRadio.checked) { setMode('monthly'); updateToggleUI(); } }); annualRadio.addEventListener('change', () => { if (annualRadio.checked) { setMode('annual'); updateToggleUI(); } }); // initial setMode(monthlyRadio.checked ? 'monthly' : 'annual'); updateToggleUI(); } })();