Aplikasi kuis pembelajaran interaktif untuk latihan Ujian Sekolah Dasar, dibangun dengan Next.js 16 (App Router), TypeScript, Supabase, dan Auth.js.
- β Dashboard mata pelajaran & pertemuan dengan tampilan Accordion
- β Kuis interaktif dengan navigasi soal bebas (maju & mundur)
- β Dukungan soal bergambar
- β Halaman hasil dengan skor, nilai persentase, dan pembahasan per soal
- β Token JWT unik per sesi kuis (berlaku 7 hari)
- β Responsive design β mobile & desktop friendly
- β Login aman dengan Auth.js (JWT session)
- β
Proteksi route
/admin/*viaproxy.ts - β CRUD lengkap: Pelajaran β Pertemuan β Bank Soal
- β Dashboard statistik: total soal, pelajaran, pertemuan, attempt
- β Riwayat pengerjaan kuis seluruh siswa
- β Validasi input server-side menggunakan Zod
- β
Next.js 16
async paramspattern (await params) - β Supabase Row Level Security (RLS) β data terlindungi
- β API Routes terstruktur dengan error handling konsisten
- β 17 unit test dengan Vitest (token, validasi, utils)
- β shadcn/ui component library
| Kategori | Teknologi |
|---|---|
| Framework | Next.js 16 (App Router) |
| Language | TypeScript 5.x |
| UI | shadcn/ui + Tailwind CSS v4 |
| Database | Supabase (PostgreSQL) |
| Auth | Auth.js v5 (NextAuth) β Credentials Provider |
| Validasi | Zod |
| Token | JSON Web Token (jsonwebtoken) |
| Testing | Vitest + React Testing Library |
- Node.js 18+
- npm
- Akun Supabase (gratis di supabase.com)
git clone https://github.com/Yasin-efendi/quiz-sd.git
cd quiz-sd
npm installBuat file .env.local di root proyek:
# Supabase
NEXT_PUBLIC_SUPABASE_URL=https://xxxxxxxx.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJhbGci...
SUPABASE_SERVICE_ROLE_KEY=eyJhbGci...
# Auth.js
NEXTAUTH_URL=http://localhost:3000
NEXTAUTH_SECRET=isi_dengan_random_string_panjang
# Admin credentials
ADMIN_USERNAME=admin
ADMIN_PASSWORD=admin123Generate NEXTAUTH_SECRET dengan perintah:
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"Buka SQL Editor di dashboard Supabase, jalankan dua query berikut secara berurutan:
Migration β membuat 4 tabel:
CREATE TABLE subjects (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
name TEXT NOT NULL,
slug TEXT NOT NULL UNIQUE,
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- (dan tabel meetings, questions, quiz_attempts)Lihat file lengkap di
supabase/migration.sqldansupabase/seed.sql
npm run dev| Username | Password |
|---|---|
admin |
admin123 |
β οΈ Ganti kredensial ini di.env.localsebelum digunakan di lingkungan produksi.
Akses panel admin: http://localhost:3000/admin
| Halaman | URL | Keterangan |
|---|---|---|
| Beranda | / |
Daftar pelajaran & pertemuan |
| Kuis | /quiz/[slug] |
Input nama β kerjakan soal |
| Hasil | /result/[token] |
Skor + pembahasan lengkap |
| Halaman | URL | Keterangan |
|---|---|---|
| Login | /admin/login |
Form autentikasi admin |
| Dashboard | /admin |
Statistik & 5 attempt terbaru |
| Pelajaran | /admin/subjects |
CRUD mata pelajaran |
| Pertemuan | /admin/meetings |
CRUD pertemuan |
| Bank Soal | /admin/questions |
CRUD soal + filter |
| Riwayat | /admin/attempts |
Semua riwayat kuis siswa |
| Method | Endpoint | Deskripsi |
|---|---|---|
GET |
/api/quiz |
Semua pelajaran beserta pertemuannya |
GET |
/api/quiz/[slug] |
Soal berdasarkan slug pertemuan |
POST |
/api/quiz/[slug]/submit |
Submit jawaban β dapat token JWT |
GET |
/api/result/[token] |
Hasil kuis berdasarkan token |
| Method | Endpoint | Deskripsi |
|---|---|---|
GET/POST |
/api/admin/subjects |
List & tambah pelajaran |
PUT/DELETE |
/api/admin/subjects/[id] |
Edit & hapus pelajaran |
GET/POST |
/api/admin/meetings |
List & tambah pertemuan |
PUT/DELETE |
/api/admin/meetings/[id] |
Edit & hapus pertemuan |
GET/POST |
/api/admin/questions |
List & tambah soal |
PUT/DELETE |
/api/admin/questions/[id] |
Edit & hapus soal |
quiz-sd/
βββ app/
β βββ admin/
β β βββ (protected)/ # Route group β butuh login
β β β βββ layout.tsx # Layout + navbar admin
β β β βββ page.tsx # Dashboard statistik
β β β βββ subjects/ # CRUD pelajaran
β β β βββ meetings/ # CRUD pertemuan
β β β βββ questions/ # Bank soal + tambah + edit
β β β βββ attempts/ # Riwayat kuis
β β βββ login/page.tsx # Halaman login
β βββ api/
β β βββ auth/[...nextauth]/ # Auth.js handler
β β βββ quiz/ # Endpoint kuis publik
β β βββ result/[token]/ # Endpoint hasil kuis
β β βββ admin/ # Endpoint CRUD admin
β βββ quiz/[slug]/page.tsx # Halaman kuis siswa
β βββ result/[token]/page.tsx # Halaman hasil siswa
β βββ layout.tsx
β βββ globals.css
βββ components/
β βββ admin/
β β βββ DeleteButton.tsx # Client component hapus soal
β βββ quiz/
β β βββ QuestionCard.tsx # Tampilan soal + opsi
β β βββ QuizNavigation.tsx # Navigasi + progress
β β βββ ResultDisplay.tsx # Tampilan hasil + pembahasan
β βββ ui/ # shadcn/ui components
βββ lib/
β βββ supabase/
β β βββ client.ts # Supabase browser client
β β βββ server.ts # Supabase admin client
β βββ validations/ # Zod schemas
β β βββ quiz.ts
β β βββ subject.ts
β β βββ meeting.ts
β β βββ question.ts
β βββ auth.ts # Auth.js config
β βββ token.ts # JWT create & verify
β βββ utils.ts # Helper functions
βββ tests/
β βββ setup.ts
β βββ quiz-flow.test.ts # Test utils kuis
β βββ token.test.ts # Test JWT token
β βββ validations.test.ts # Test Zod schema
βββ types/
β βββ next-auth.d.ts # Type extension session
βββ proxy.ts # Route protection middleware
βββ .env.local # Environment variables
βββ vitest.config.ts
Jalankan seluruh test:
npx vitest runHasil yang diharapkan:
β tests/quiz-flow.test.ts (5 tests)
β tests/token.test.ts (4 tests)
β tests/validations.test.ts (8 tests)
Test Files 3 passed
Tests 17 passed
| File | Yang Diuji |
|---|---|
quiz-flow.test.ts |
calculateScore, getScoreMessage, generateSlug |
token.test.ts |
Buat token, decode token valid, tolak token palsu |
validations.test.ts |
Zod schema quiz, subject, question |
// β
Benar: params wajib di-await di Next.js 16
export async function GET(
request: Request,
{ params }: { params: Promise<{ slug: string }> }
) {
const { slug } = await params
}Semua tabel menggunakan Row Level Security:
- Public (
anon) β hanya bisaSELECTtabelsubjects,meetings,questions - Public β bisa
INSERTkequiz_attempts(submit kuis) - Service Role β akses penuh untuk operasi admin
Di Next.js 16, middleware.ts telah diganti menjadi proxy.ts. Jika Anda melihat warning middleware deprecated, rename file tersebut ke proxy.ts.
| Masalah | Solusi |
|---|---|
Invalid path specified in request URL |
Pastikan NEXT_PUBLIC_SUPABASE_URL tidak mengandung /rest/v1/ di akhir |
Redirect loop di /admin/login |
Pastikan halaman login berada di luar route group (protected) |
Event handlers cannot be passed to Client Component |
Tambahkan 'use client' atau pisahkan ke file komponen tersendiri |
Property 'errors' does not exist on ZodError |
Ganti .errors dengan .issues (Zod versi terbaru) |
| Test tidak ditemukan | Pastikan folder bernama tests/ (dengan s) dan file setup.ts ada di dalamnya |
rm -rf .next
npm run dev- Fork repository ini
- Buat branch fitur baru:
git checkout -b fitur/nama-fitur - Commit perubahan:
git commit -m 'feat: deskripsi fitur' - Push ke branch:
git push origin fitur/nama-fitur - Buat Pull Request
Dibagikan di bawah lisensi MIT. Bebas digunakan, dimodifikasi, dan didistribusikan untuk tujuan edukasi.
π― Dibuat dengan β€οΈ untuk mendukung pembelajaran digital siswa SD Indonesia. Next.js 16 β’ TypeScript β’ Supabase β’ Auth.js β’ shadcn/ui β’ Vitest