# Product Requirements Document (PRD)
# Sistem Absensi Kehadiran Ujian UAS & UTS Berbasis QR Code

## 1. Overview
Sistem web application absensi kehadiran ujian UAS/UTS menggunakan QR Code dengan pendekatan responsif, aman, dan elegan. Sistem memiliki 3 role: Admin, Dosen, dan Mahasiswa.

## 2. Goals
- Mengeliminasi kecurangan absensi ujian
- Mempermudah proses absensi dengan QR Code statis
- Menyediakan rekap absensi real-time
- Membatasi jual beli/transfer QR Code (anti-fraud)
- **Mewajibkan mahasiswa melakukan pendaftaran ujian sebelum bisa mengabsen**

## 3. User Roles & Features

### 3.1 Admin
| Feature | Description |
|---------|-------------|
| Kelola Data Mahasiswa | CRUD (Create, Read, Update, Delete) data mahasiswa |
| Kelola Data Dosen | CRUD data dosen |
| Kelola Data Mata Kuliah | CRUD data mata kuliah |
| Kelola Jadwal Ujian | CRUD jadwal ujian (UAS/UTS) |
| Lihat Profile | Melihat profile semua mahasiswa dan dosen |
| Generate QR Code | Membuat QR Code absensi untuk sesi ujian |
| Tampilkan QR Code | Menampilkan QR Code yang sudah dibuat |
| Rekap Absensi | Melihat rekap kehadiran per sesi ujian |
| **Kelola QR Pendaftaran** | **Generate, aktifkan/nonaktifkan, dan hapus QR Statistik per-prodi untuk pendaftaran ujian mahasiswa** |
| **Monitoring Pendaftaran** | **Melihat jumlah mahasiswa yang sudah/ belum terdaftar ujian per prodi** |

### 3.2 Dosen
| Feature | Description |
|---------|-------------|
| Generate QR Code | Membuat QR Code untuk kelas yang diampu |
| Tampilkan QR Code | Menampilkan QR Code absensi |
| Rekap Absensi | Melihat rekap kehadiran per jadwal ujian (hanya untuk mata kuliah yang diampu), dengan filter, sidebar, dan export Excel |

### 3.3 Mahasiswa
| Feature | Description |
|---------|-------------|
| Lihat Profile | Melihat profile sendiri |
| **Daftar Ujian (Wajib)** | **Scan QR Statistik di meja pendaftaran untuk membuka akses absensi ujian** |
| Scan QR Code | Absensi ujian dengan scan QR menggunakan kamera web app (hanya bisa dilakukan setelah daftar ujian) |
| Status Absensi | Melihat status kehadiran tercatat |
| Riwayat Absensi | Melihat history kehadiran ujian |

## 4. Sistem Login (Multi-Identifier)

Login menggunakan satu field "Identitas" - **tidak menggunakan email**.

| Role | Identitas Login | Keterangan |
|------|-----------------|------------|
| **Admin** | NIP | Login dengan NIP terdaftar (contoh: `ADM001`) |
| **Dosen** | NIDN atau Inisial | Login dengan NIP/NIDN (contoh: `198501012010011001`) atau inisial (contoh: `AF`). Jika dosen belum punya NIDN, gunakan inisial |
| **Mahasiswa** | NIM | Login dengan NIM terdaftar (contoh: `20240001`) |

### 4.1 Logika autentikasi
```
Backend menerima field `identifier` dan `password`.
Query database: SELECT * FROM users WHERE nim = ? OR nip = ? OR inisial = ?
(email TIDAK digunakan untuk login)
Jika ditemukan -> cocokkan password dengan bcrypt
Jika cocok -> generate JWT token -> return user data
```

### 4.2 Frontend Login
- Form memiliki satu field "Identitas" dengan placeholder "NIM / NIDN / Inisial / NIP"
- **Tidak ada tab selector** - satu field universal untuk semua role
- **Case-insensitive**: Identifier tidak sensitif huruf besar/kecil (contoh: `adm001`, `ADM001`, `Adm001` semua bisa login)
- Tidak ada field email di login
- Jika login gagal, toast notifikasi menampilkan: "Username atau password yang anda masukkan salah"

## 5. Alur Pendaftaran Ulang Mahasiswa (Registration Gate)

> **ATURAN UTAMA: Mahasiswa TIDAK BISA mengabsen ujian sebelum melakukan pendaftaran ujian.**

### 5.1 Alur Lengkap

```
┌─────────────────────────────────────────────────────────────────┐
│                    ALUR PENDAFTARAN ULANG                        │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│  1. Admin generate QR Statistik per-prodi                       │
│     └─→ Token unik disimpan di tabel static_qr_tokens           │
│     └─→ QR dicetak/ditampilkan di meja pendaftaran              │
│                                                                  │
│  2. Mahasiswa login ke sistem                                   │
│     └─→ Dashboard menunjukkan status "Belum Daftar Ujian"       │
│     └─→ Semua jadwal ujian DITKUNCI (tidak bisa diakses)        │
│                                                                  │
│  3. Mahasiswa scan QR Statistik di meja pendaftaran             │
│     └─→ Sistem validasi: token valid + prodi sesuai             │
│     └─→ Field `is_registered` di-update ke `1`                  │
│     └─→ Notifikasi sukses ditampilkan                           │
│                                                                  │
│  4. Setelah terdaftar:                                          │
│     └─→ Dashboard menunjukkan status "Pendaftaran Ulang Aktif"  │
│     └─→ Semua jadwal ujian TERBUKA                              │
│     └─→ Mahasiswa bisa mengabsen dengan QR Absensi dari dosen/admin │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘
```

### 5.2 Detail Proses

#### Langkah 1: Admin Membuat QR Pendaftaran
- Admin pergi ke halaman **Cetak QR** (`/admin/cetak-qr`)
- Admin pilih prodi yang ingin dibuatkan QR pendaftaran
- Sistem generate token unik (crypto.randomBytes) dan simpan ke `static_qr_tokens`
- QR berisi URL: `http://localhost:5173/mahasiswa/scan-static/{token}`
- QR bisa dicetak untuk ditempel di meja pendaftaran

#### Langkah 2: Mahasiswa Login
- Mahasiswa login dengan NIM
- Sistem mengecek field `is_registered` pada tabel `users`
- Jika `is_registered = 0`:
  - Dashboard menampilkan banner peringatan "Belum Daftar Ujian"
  - Daftar jadwal ujian dikunci (tidak ditampilkan)
  - Tombol "Scan QR Dinamis" tidak tersedia
  - Hanya tombol "Scan QR Pendaftaran" yang aktif

#### Langkah 3: Mahasiswa Scan QR Pendaftaran
- Mahasiswa buka halaman `/mahasiswa/scan-static/{token}`
- Sistem otomatis memproses:
  1. Validasi token exist dan aktif di `static_qr_tokens`
  2. Cek prodi mahasiswa sesuai dengan prodi QR
  3. Jika sudah terdaftar → tampilkan pesan "Sudah Terdaftar"
  4. Jika belum terdaftar → update `is_registered = 1` di tabel `users`
  5. Tampilkan notifikasi sukses

#### Langkah 4: Mahasiswa Bisa Mengabsen
- Setelah `is_registered = 1`, semua fitur terbuka:
  - Jadwal ujian tampil di dashboard
  - Tombol "Scan QR Dinamis" aktif
  - Mahasiswa bisa absen dengan QR dinamis dari dosen
  - Riwayat absensi bisa dilihat

### 5.3 Backend Validation Points

| Endpoint | Penjagaan |
|----------|-----------|
| `POST /api/mahasiswa/absen` | Cek `is_registered` sebelum proses absensi |
| `GET /api/mahasiswa/qr/active` | Return empty jika `is_registered = 0` |
| `GET /api/mahasiswa/jadwal` | Return empty jika `is_registered = 0` |
| `POST /api/mahasiswa/scan-static/:token` | Proses pendaftaran ujian |

### 5.4 Frontend Guard Points

| Halaman | Penjagaan |
|---------|-----------|
| Dashboard Mahasiswa | Tampilkan warning + lock jadwal jika belum daftar |
| Scan Page (QR Absensi) | Tampilkan blocker "Belum Daftar Ujian" jika `is_registered = 0` |
| Scan Static Page | Halaman khusus untuk proses pendaftaran ujian |

## 6. Anti-Fraud System

### 6.1 QR Code Absensi (Statis)
- QR Code bersifat **statis** (tidak kedaluwarsa otomatis)
- QR tetap aktif sampai ditutup manual oleh admin/dosen
- QR Code berisi payload dengan token unik per sesi
- Setiap scan hanya bisa digunakan 1 kali per mahasiswa

### 6.2 QR Pendaftaran (Statis)
- QR bersifat statis (tidak kedaluwarsa) per-prodi
- Hanya bisa digunakan oleh mahasiswa dengan prodi yang sesuai
- Satu token per prodi, bisa diaktifkan/nonaktifkan oleh admin
- Memastikan mahasiswa hadir fisik di lokasi pendaftaran

### 6.3 Batasan Kamera
- Hanya dapat menggunakan **kamera bawaan web app** (getUserMedia API)
- Tidak mengizinkan penggunaan kamera external/lain
- Kamera hanya aktif saat halaman scan aktif

### 6.4 Validasi Geolokasi
- **Batas jarak maksimal: 15 meter** dari lokasi QR Code ditampilkan
- Menggunakan Geolocation API browser
- Validasi dilakukan setiap kali mahasiswa melakukan scan
- Jika jarak > 15 meter, scan ditolak dengan pesan error
- Konfigurasi via env: `QR_MAX_DISTANCE`

### 6.5 Validasi Tambahan
- Binding NIM ke token sesi (tidak bisa di-share)
- Timestamp scan tercatat untuk audit trail
- Device fingerprint untuk mendeteksi multiple device
- **Registration gate: mahasiswa harus daftar ujian dulu sebelum bisa absen**

## 7. Tech Stack

### 7.1 Frontend
- **React.js** (Vite)
- **React Router v6** - Routing
- **Axios** - HTTP Client
- **TailwindCSS** - Styling
- **html5-qrcode** - QR Scanner (kamera web)
- **qrcode** - QR Generator
- **qrcode.react** - QR Code rendering
- **React Hot Toast** - Notifikasi
- **Recharts** - Visualisasi data rekap
- **xlsx** - Excel import/export
- **react-icons/hi2** - Icon library (menggantikan emoji)

### 7.2 Backend
- **Node.js** + **Express.js**
- **MySQL** + **mysql2** - Database (XAMPP/phpMyAdmin)
- **JWT** - Authentication
- **bcryptjs** - Password hashing
- **qrcode** - QR Code generation
- **crypto** - Token generation
- **cors** - Cross-origin
- **express-rate-limit** - Rate limiting
- **express-validator** - Input validation
- **multer** - File upload (Excel import)
- **xlsx** - Excel parsing

## 8. Database Schema (MySQL)

### 8.1 Users
```sql
CREATE TABLE users (
  id INT AUTO_INCREMENT PRIMARY KEY,
  nama VARCHAR(100) NOT NULL,
  email VARCHAR(100) NOT NULL UNIQUE,
  password VARCHAR(255) NOT NULL,
  role ENUM('admin','dosen','mahasiswa') NOT NULL,
  nim VARCHAR(20) DEFAULT NULL UNIQUE,
  nip VARCHAR(20) DEFAULT NULL UNIQUE,
  prodi VARCHAR(100) DEFAULT NULL,
  jurusan VARCHAR(100) DEFAULT NULL,
  angkatan VARCHAR(10) DEFAULT NULL,
  inisial VARCHAR(20) DEFAULT NULL,
  jenis_kelamin ENUM('Laki-laki','Perempuan') DEFAULT NULL,
  no_telp VARCHAR(20) DEFAULT NULL,
  alamat TEXT DEFAULT NULL,
  foto_profil VARCHAR(255) DEFAULT NULL,
  is_registered TINYINT(1) DEFAULT 0,
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
)
```

> **Field `is_registered`:** Menandakan apakah mahasiswa sudah melakukan pendaftaran ujian. Default `0` (belum terdaftar). Diupdate ke `1` setelah scan QR Statistik.

### 8.2 Static QR Tokens
```sql
CREATE TABLE static_qr_tokens (
  id INT AUTO_INCREMENT PRIMARY KEY,
  token VARCHAR(64) NOT NULL UNIQUE,
  prodi VARCHAR(100) NOT NULL,
  is_active TINYINT(1) DEFAULT 1,
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
```

> **Tabel ini menyimpan QR token statis per-prodi untuk pendaftaran ujian mahasiswa.** Setiap prodi memiliki satu token unik yang bisa diaktifkan/nonaktifkan oleh admin.

### 8.3 Mata Kuliah
```sql
CREATE TABLE mata_kuliah (
  id INT AUTO_INCREMENT PRIMARY KEY,
  nama VARCHAR(100) NOT NULL,
  kode VARCHAR(20) NOT NULL UNIQUE,
  sks INT NOT NULL DEFAULT 3,
  dosen_id INT NOT NULL,
  prodi VARCHAR(100) DEFAULT NULL,
  semester VARCHAR(20) DEFAULT NULL,
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  FOREIGN KEY (dosen_id) REFERENCES users(id) ON DELETE CASCADE
)
```

### 8.4 Jadwal Ujian
```sql
CREATE TABLE jadwal_ujian (
  id INT AUTO_INCREMENT PRIMARY KEY,
  mata_kuliah_id INT NOT NULL,
  tipe ENUM('UTS','UAS') NOT NULL,
  tanggal DATE NOT NULL,
  jam_mulai VARCHAR(10) NOT NULL,
  jam_selesai VARCHAR(10) NOT NULL,
  ruangan VARCHAR(50) NOT NULL,
  semester VARCHAR(20) DEFAULT NULL,
  angkatan VARCHAR(10) DEFAULT NULL,
  status ENUM('upcoming','active','completed') DEFAULT 'upcoming',
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  FOREIGN KEY (mata_kuliah_id) REFERENCES mata_kuliah(id) ON DELETE CASCADE
)
```

### 8.5 QR Sessions
```sql
CREATE TABLE qr_sessions (
  id INT AUTO_INCREMENT PRIMARY KEY,
  jadwal_ujian_id INT NOT NULL,
  dosen_id INT NOT NULL,
  token VARCHAR(64) NOT NULL UNIQUE,
  expiry_duration INT DEFAULT 0,
  is_active TINYINT(1) DEFAULT 1,
  expires_at DATETIME NOT NULL,
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  FOREIGN KEY (jadwal_ujian_id) REFERENCES jadwal_ujian(id) ON DELETE CASCADE,
  FOREIGN KEY (dosen_id) REFERENCES users(id) ON DELETE CASCADE,
  INDEX idx_token (token),
  INDEX idx_jadwal (jadwal_ujian_id)
)
```

### 8.6 Absensi
```sql
CREATE TABLE absensi (
  id INT AUTO_INCREMENT PRIMARY KEY,
  mahasiswa_id INT NOT NULL,
  jadwal_ujian_id INT NOT NULL,
  qr_session_id INT NOT NULL,
  status ENUM('hadir','tidak_hadir') DEFAULT 'hadir',
  scan_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  latitude DOUBLE DEFAULT NULL,
  longitude DOUBLE DEFAULT NULL,
  distance_from_qr DOUBLE DEFAULT NULL,
  device_fingerprint VARCHAR(255) DEFAULT NULL,
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  FOREIGN KEY (mahasiswa_id) REFERENCES users(id) ON DELETE CASCADE,
  FOREIGN KEY (jadwal_ujian_id) REFERENCES jadwal_ujian(id) ON DELETE CASCADE,
  FOREIGN KEY (qr_session_id) REFERENCES qr_sessions(id) ON DELETE CASCADE,
  UNIQUE KEY unique_mahasiswa_jadwal (mahasiswa_id, jadwal_ujian_id),
  INDEX idx_jadwal (jadwal_ujian_id)
)
```

### 8.7 Mata Kuliah Mahasiswa (Plotting)
```sql
CREATE TABLE matakuliah_mahasiswa (
  id INT AUTO_INCREMENT PRIMARY KEY,
  mahasiswa_id INT NOT NULL,
  mata_kuliah_id INT NOT NULL,
  semester VARCHAR(10) DEFAULT NULL,
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  FOREIGN KEY (mahasiswa_id) REFERENCES users(id) ON DELETE CASCADE,
  FOREIGN KEY (mata_kuliah_id) REFERENCES mata_kuliah(id) ON DELETE CASCADE,
  UNIQUE KEY unique_mk_mhs (mahasiswa_id, mata_kuliah_id)
)
```

## 9. API Endpoints

### Auth
- `POST /api/auth/login` - Login (field: `identifier`, `password`)
- `GET /api/auth/me` - Get current user
- `PUT /api/auth/password` - Update password

### Admin - Mahasiswa
- `GET /api/admin/mahasiswa` - List all (filter: search, prodi, angkatan)
- `POST /api/admin/mahasiswa` - Create
- `PUT /api/admin/mahasiswa/:id` - Update
- `DELETE /api/admin/mahasiswa/:id` - Delete
- `GET /api/admin/mahasiswa/template` - Download template Excel
- `POST /api/admin/mahasiswa/import` - Import dari Excel
- `PUT /api/admin/mahasiswa/bulk` - Bulk edit
- `DELETE /api/admin/mahasiswa/bulk` - Bulk delete
- `GET /api/admin/mahasiswa/:id` - Get detail + riwayat absensi

### Admin - Dosen
- `GET /api/admin/dosen` - List all (filter: search)
- `POST /api/admin/dosen` - Create
- `PUT /api/admin/dosen/:id` - Update
- `DELETE /api/admin/dosen/:id` - Delete
- `GET /api/admin/dosen/template` - Download template Excel
- `POST /api/admin/dosen/import` - Import dari Excel
- `PUT /api/admin/dosen/bulk` - Bulk edit
- `DELETE /api/admin/dosen/bulk` - Bulk delete
- `GET /api/admin/dosen/:id` - Get detail + mata kuliah

### Admin - Mata Kuliah
- `GET /api/admin/matakuliah` - List all (filter: search, prodi)
- `POST /api/admin/matakuliah` - Create
- `PUT /api/admin/matakuliah/:id` - Update
- `DELETE /api/admin/matakuliah/:id` - Delete
- `GET /api/admin/matakuliah/template` - Download template Excel
- `POST /api/admin/matakuliah/import` - Import dari Excel
- `PUT /api/admin/matakuliah/bulk` - Bulk edit
- `DELETE /api/admin/matakuliah/bulk` - Bulk delete
- `GET /api/admin/matakuliah/:id` - Get detail + jumlah mahasiswa

### Admin - Plotting
- `GET /api/admin/plot/mahasiswa` - List mahasiswa + plotting info
- `POST /api/admin/plot` - Plot mahasiswa ke mata kuliah
- `DELETE /api/admin/plot` - Unplot mahasiswa dari mata kuliah

### Admin - Jadwal Ujian
- `GET /api/admin/jadwal` - List all (filter: tipe, status, prodi, semester, angkatan)
- `POST /api/admin/jadwal` - Create
- `PUT /api/admin/jadwal/:id` - Update
- `DELETE /api/admin/jadwal/:id` - Delete
- `GET /api/admin/jadwal/template` - Download template Excel
- `POST /api/admin/jadwal/import` - Import dari Excel

### Admin - QR & Rekap
- `POST /api/admin/qr/generate` - Generate QR
- `GET /api/admin/qr/:jadwalId` - Get QR info
- `PUT /api/admin/qr/refresh` - Refresh QR
- `PUT /api/admin/qr/close` - Close QR session
- `GET /api/admin/rekap` - List rekap (filter: tipe, prodi, semester, angkatan)
- `GET /api/admin/rekap/:jadwalId` - Get rekap detail

### Admin - Static QR (Pendaftaran)
- `GET /api/admin/static-qr` - List semua QR pendaftaran
- `POST /api/admin/static-qr` - Generate QR baru per-prodi
- `DELETE /api/admin/static-qr/:tokenId` - Hapus QR
- `PUT /api/admin/static-qr/:tokenId/toggle` - Aktifkan/Nonaktifkan QR
- `GET /api/admin/static-qr/:tokenId/stats` - Jumlah mahasiswa terdaftar

### Admin - Lainnya
- `GET /api/admin/prodi` - List prodi dari data mahasiswa
- `GET /api/admin/profile/:id` - Profile user lain
- `PUT /api/admin/profile` - Update profile admin sendiri (nama, email, foto_profil, password)

### Dosen
- `GET /api/dosen/matakuliah` - Mata kuliah yang diampu
- `GET /api/dosen/jadwal` - Jadwal ujian terkait
- `GET /api/dosen/mahasiswa` - List mahasiswa
- `POST /api/dosen/qr/generate` - Generate QR
- `GET /api/dosen/qr/:jadwalId` - Get QR info
- `PUT /api/dosen/qr/:jadwalId/refresh` - Refresh QR
- `GET /api/dosen/rekap` - List rekap (hanya jadwal mata kuliah yang diampu, filter: tipe, prodi, semester, angkatan)
- `GET /api/dosen/rekap/:jadwalId` - Get rekap detail (berdasarkan plotting matakuliah_mahasiswa)

### Mahasiswa
- `GET /api/mahasiswa/profile` - Get profile
- `GET /api/mahasiswa/registration-status` - Cek status pendaftaran
- `GET /api/mahasiswa/jadwal` - Daftar jadwal (hanya jika sudah daftar)
- `POST /api/mahasiswa/absen` - Scan & submit absensi (**wajib sudah daftar**)
- `GET /api/mahasiswa/absensi` - Riwayat absensi
- `GET /api/mahasiswa/qr/active` - Get active QR sessions (hanya jika sudah daftar)
- `POST /api/mahasiswa/scan-static/:token` - **Scan QR pendaftaran ujian**

## 10. Import Excel

### 10.1 Kolom Wajib & Opsional

#### Mahasiswa
| Kolom | Wajib | Keterangan |
|-------|-------|------------|
| nama | ✅ | Nama lengkap mahasiswa |
| nim | ✅ | Nomor Induk Mahasiswa (unik) |
| password | ✅ | Password login. Jika kosong = `password123` |
| angkatan | ✅ | Tahun masuk (contoh: 2024) |
| email | - | Jika kosong = `[nim]@university.ac.id` |
| prodi | - | Program Studi |
| jurusan | - | Jurusan (fokus studi) |
| jenis_kelamin | - | Laki-laki / Perempuan |
| no_telp | - | Nomor telepon |

#### Dosen
| Kolom | Wajib | Keterangan |
|-------|-------|------------|
| nama | ✅ | Nama lengkap dosen |
| password | ✅ | Password login. Jika kosong = `password123` |
| nip | - | Nomor Induk Pegawai. Jika diisi & sudah ada → update |
| inisial | - | Inisial nama dosen |
| jenis_kelamin | - | Laki-laki / Perempuan |
| email | - | Jika kosong = `[nip/nama]@university.ac.id` |
| no_telp | - | Nomor telepon |

#### Mata Kuliah
| Kolom | Wajib | Keterangan |
|-------|-------|------------|
| nama | ✅ | Nama mata kuliah |
| kode | - | Kode MK. Jika diisi & sudah ada → update |
| sks | - | Jumlah SKS (default: 3) |
| dosen | - | Nama dosen pengampu |
| prodi | - | Program Studi |
| semester | - | Ganjil / Genap |

### 10.2 Logika Import
- **Upsert by unique field**: NIM (mahasiswa), NIP (dosen), Kode (mata kuliah)
- Jika unique field sudah ada → update data
- Jika unique field kosong → insert baru
- Password selalu di-hash dengan bcrypt
- Header Excel harus sesuai nama kolom (case-insensitive)
- Setiap file memiliki sheet "Ketentuan" dengan panduan lengkap

## 11. Demo Accounts

> **Note**: Demo accounts tidak ditampilkan di halaman login. Informasi di bawah ini untuk keperluan dokumentasi/testing.

| Role | Identitas Login | Password |
|------|-----------------|----------|
| Admin | ADM001 | password123 |
| Dosen (NIDN) | 198501012010011001 | password123 |
| Dosen (Inisial) | AF | password123 |
| Dosen 2 (NIDN) | 198005052005011002 | password123 |
| Dosen 2 (Inisial) | BS | password123 |
| Mahasiswa | 20240001 | password123 |
| Mahasiswa | 20240002 | password123 |

## 11. Security Measures
1. JWT Token dengan expiry time
2. Password hashing dengan bcrypt (10 rounds)
3. Rate limiting untuk prevent brute force
4. CORS configuration
5. Input validation & sanitization
6. QR Code payload encryption
7. Geolocation validation (15m radius, configurable via `QR_MAX_DISTANCE`)
8. Single-use token per mahasiswa per sesi
9. **Registration gate: mahasiswa wajib daftar ujian sebelum bisa absen**
10. **Prodi validation: QR pendaftaran hanya bisa digunakan oleh mahasiswa prodi yang sesuai**

## 13. UI/UX Requirements
- Responsive design (mobile-first)
- Clean, modern interface dengan gradient & rounded corners
- Glass-morphism design pada halaman login
- **react-icons/hi2 digunakan sebagai pengganti emoji di seluruh halaman**
- Loading states & error handling
- Toast notifications untuk feedback
- Dashboard dengan statistik
- Tabel interaktif dengan search & filter
- Tidak ada tab selector di login - satu field universal
- **Login case-insensitive** - huruf besar/kecil tidak mempengaruhi pencocokan identitas
- **Admin bisa mengelola profile sendiri** (nama, email, foto, password) dari sidebar
- **Banner peringatan "Belum Daftar Ujian" di dashboard mahasiswa**
- **Status indicator "Pendaftaran Ulang Aktif" di dashboard**
- **Blocker page jika mencoba akses scan tanpa daftar ujian**

## 14. Non-Functional Requirements
- Page load < 3 detik
- QR Code generation < 1 detik
- Scan & validation < 2 detik
- Support Chrome, Firefox, Safari, Edge
- Mobile responsive

## 15. Flow Diagram: Login sampai Absensi

```
┌──────────────┐     ┌──────────────┐     ┌──────────────────┐
│   Mahasiswa  │────>│    Login     │────>│    Dashboard     │
│              │     │  (NIM + PW)  │     │                  │
└──────────────┘     └──────────────┘     └────────┬─────────┘
                                                    │
                                           ┌────────┴─────────┐
                                           │  is_registered?  │
                                           └────────┬─────────┘
                                                    │
                              ┌──────────────────────┼──────────────────────┐
                              │                      │                      │
                         TIDAK (0)               SUDAH (1)            SUDAH (1)
                              │                      │                      │
                              ▼                      ▼                      ▼
                    ┌─────────────────┐   ┌─────────────────┐   ┌─────────────────┐
                    │ Banner Warning  │   │ Jadwal List     │   │ Scan QR Absensi │
                    │ Jadwal Dikunci  │   │ (Terbuka)       │   │ (Absensi)       │
                    │ Tombol: Scan QR │   │ Tombol: Scan    │   │                 │
                    │ Pendaftaran     │   │ QR Absensi      │   │                 │
                    └────────┬────────┘   └────────┬────────┘   └────────┬────────┘
                             │                      │                      │
                             ▼                      ▼                      ▼
                    ┌─────────────────┐   ┌─────────────────┐   ┌─────────────────┐
                    │ Scan QR Static  │   │ Pilih Jadwal    │   │ Validasi:       │
                    │ (Pendaftaran)   │   │ → Scan QR       │   │ - Token valid   │
                    │                 │   │   Absensi       │   │ - Geolocation   │
                    │ Validasi:       │   │                 │   │ - is_registered │
                    │ - Token valid   │   │                 │   │ - Sudah absen?  │
                    │ - Prodi sesuai  │   │                 │   │                 │
                    └────────┬────────┘   └────────┬────────┘   └────────┬────────┘
                             │                      │                      │
                             ▼                      ▼                      ▼
                    ┌─────────────────┐   ┌─────────────────┐   ┌─────────────────┐
                    │ is_registered   │   │ Absensi         │   │ Absensi         │
                    │ = 1             │   │ Tercatat ✅     │   │ Tercatat ✅     │
                    │                 │   │                 │   │                 │
                    │ Semua fitur     │   │                 │   │                 │
                    │ terbuka ✅      │   │                 │   │                 │
                    └─────────────────┘   └─────────────────┘   └─────────────────┘
```
