# Functional Specification & Technical Workflow
## Sistem Informasi Pendaftaran Mahasiswa Baru (New Student Registration System — "PMB")

**Document version:** 1.0
**Status:** As-implemented (derived from the current codebase)
**Scope:** Automated applicant registration, persistence, and an admin approval workflow (review → approve → reject).

---

## 1. Purpose & Overview

The system automates the end-to-end admission pipeline for a university:

1. **Applicants (Students)** register through a public web form. Their identity, contact, school origin, and chosen program (`jurusan`) are captured and stored in a relational database. They then upload required documents and submit the application.
2. **Admins** use a secured dashboard to review each applicant, verify uploaded documents, and **approve** or **reject** the application. On approval the applicant is accepted and a student record (NIM) is generated.

The product is a fully functional website composed of a static front end (HTML/CSS/JS) and a REST API back end (Node.js + Express + MySQL).

### 1.1 Architectural Layers

```
┌─────────────────────────────┐
│  Presentation Layer (Front) │  index.html, register.html, login.html,
│  Static HTML/CSS/JS         │  dashboard.html, admin-login.html,
│                             │  admin-dashboard.html, forgot-password.html
└──────────────┬──────────────┘
               │  HTTPS / JSON (fetch + JWT Bearer)
┌──────────────▼──────────────┐
│  API Layer (Node + Express) │  /api/auth, /api/student, /api/admin
│  backend/app.js + routes/*  │  helmet, cors, rate-limit, JWT authz
└──────────────┬──────────────┘
               │  mysql2 connection pool (promisified)
┌──────────────▼──────────────┐
│  Data Layer (MySQL)         │  sistemin_sistem_informasi_pendaftaran_mahasiswa_baru: users, profiles, documents,
│  backend/config/database.sql│  payments, registration_status
└─────────────────────────────┘
```

### 1.2 Technology Stack

| Concern | Choice (from `backend/package.json`) |
|---|---|
| Runtime | Node.js |
| Web framework | Express 4 (`express`) |
| Database | MySQL via `mysql2` pool |
| Auth | `jsonwebtoken` (JWT), `bcryptjs` (password hashing) |
| Validation | `express-validator` (auth) / `joi` |
| File uploads | `multer` (disk storage, 5 MB limit) |
| Security | `helmet`, `express-rate-limit`, `cors` |
| Config | `dotenv` |
| Dev | `nodemon` |

Front end is vanilla JS + CSS (no framework); uses `localStorage` as a simulation data layer so the dashboard is interactive without a live DB, with the intent of swapping to real `fetch` calls (see §7).

---

## 2. Database Schema Design

Database: `sistemin_sistem_informasi_pendaftaran_mahasiswa_baru`. Five normalized tables, all using surrogate `INT AUTO_INCREMENT` primary keys and `created_at`/`updated_at` audit columns. Foreign keys use `ON DELETE CASCADE` (or `SET NULL` for audit fields pointing at admins).

### 2.1 `users`
Authentication & role root for every actor.

| Column | Type | Notes |
|---|---|---|
| `id` | INT PK AUTO_INCREMENT | |
| `email` | VARCHAR(255) UNIQUE NOT NULL | login identifier |
| `password` | VARCHAR(255) NOT NULL | bcrypt hash (never plaintext) |
| `role` | ENUM('student','admin','super_admin') DEFAULT 'student' | access control |
| `is_active` | BOOLEAN DEFAULT TRUE | soft disable |
| `created_at` / `updated_at` | TIMESTAMP | audit |

### 2.2 `profiles`
Student demographic data, 1:1 with `users`.

| Column | Type | Notes |
|---|---|---|
| `id` | INT PK | |
| `user_id` | INT FK → users(id) ON DELETE CASCADE | |
| `full_name` | VARCHAR(255) NOT NULL | |
| `phone_number` | VARCHAR(20) | |
| `address` | TEXT | |
| `asal_sekolah` | VARCHAR(255) | origin school |
| `jurusan` | ENUM('Teknik Informatika','Sistem Informasi','Manajemen','Akuntansi') | chosen program |
| `status_pendaftaran` | ENUM('pending','processing','accepted','rejected') DEFAULT 'pending' | canonical admission status |
| `created_at` / `updated_at` | TIMESTAMP | |

### 2.3 `documents`
Uploaded applicant files, 1:N with `users`.

| Column | Type | Notes |
|---|---|---|
| `id` | INT PK | |
| `user_id` | INT FK → users(id) ON DELETE CASCADE | owner |
| `document_type` | ENUM('ktp','ijazah','foto','raport') NOT NULL | |
| `file_path` | VARCHAR(255) NOT NULL | stored under `uploads/` |
| `is_verified` | BOOLEAN DEFAULT FALSE | admin decision |
| `admin_note` | TEXT | reason on reject/approve |
| `verified_by` | INT FK → users(id) ON DELETE SET NULL | auditing admin |
| `verified_at` | TIMESTAMP NULL | |
| `created_at` / `updated_at` | TIMESTAMP | |

### 2.4 `payments`
Application fee tracking, 1:N with `users`.

| Column | Type | Notes |
|---|---|---|
| `id` | INT PK | |
| `user_id` | INT FK → users(id) | |
| `amount` | DECIMAL(10,2) NOT NULL | |
| `payment_status` | ENUM('pending','paid','failed') DEFAULT 'pending' | |
| `payment_method` | VARCHAR(50) | |
| `transaction_id` | VARCHAR(100) | |
| `paid_at` | TIMESTAMP NULL | |
| `created_at` / `updated_at` | TIMESTAMP | |

### 2.5 `registration_status`
Fine-grained multi-step registration state machine, 1:1 with `users`.

| Column | Type | Notes |
|---|---|---|
| `id` | INT PK | |
| `user_id` | INT FK → users(id) | |
| `status` | ENUM('draft','submitted','under_review','approved','rejected') DEFAULT 'draft' | workflow state |
| `last_step_completed` | INT DEFAULT 0 | wizard progress (1–4) |
| `admin_comment` | TEXT | admin feedback |
| `updated_by` | INT FK → users(id) ON DELETE SET NULL | |
| `updated_at` | TIMESTAMP ON UPDATE | |

### 2.6 Status Mapping (`STATUS_MAP`)
The design intentionally uses two parallel status enums. `registration_status.status` is the detailed workflow; `profiles.status_pendaftaran` is the student-facing canonical status. `backend/routes/admin.js` maps between them:

| registration_status | → profiles.status_pendaftaran |
|---|---|
| draft | pending |
| submitted | processing |
| under_review | processing |
| approved | accepted |
| rejected | rejected |
| pending | pending |
| processing | processing |
| accepted | accepted |

### 2.7 Seed Data
A default `super_admin` (`admin@pmb.ac.id`) is inserted by the migration. (Note: the seeded password hash in `database.sql` is a placeholder and should be regenerated with bcrypt before production.)

---

## 3. User Roles & Access Control

Two primary roles plus a privileged super-admin:

### 3.1 Student
- **Registration:** create account + profile (`POST /api/auth/register`).
- **Self-service:** view own profile/documents/status (`GET /api/student/profile`), update profile, upload documents, submit application.
- **Visibility:** sees only their own data. Identified by the `id` inside the JWT.
- **Authenticated by:** `authenticateToken` (any valid token).

### 3.2 Admin / Super Admin
- **Access:** `authenticateToken` **and** `authorizeRole('admin','super_admin')`.
- **Capabilities:**
  - List/search/filter all students (`GET /api/admin/students`).
  - View a student's documents and the aggregated document-verification queue (`GET /api/admin/documents`).
  - Preview uploaded files (`GET /api/admin/documents/:id/file`, path-traversal protected).
  - Verify or reject a document (`PUT /api/admin/documents/:id/verify`).
  - Approve or reject an application (`PUT /api/admin/students/:id/status`).
  - View dashboard statistics (`GET /api/admin/dashboard/stats`).

### 3.3 Permission Model
`backend/middleware/auth.js` provides:
- `authenticateToken` — verifies the `Authorization: Bearer <jwt>` header against `JWT_SECRET`.
- `authorizeRole(...roles)` — enforces `req.user.role` membership.

---

## 4. Key Features (Fully Functional Website)

### 4.1 Public / Applicant Side
| Feature | File(s) | Backend |
|---|---|---|
| Landing / marketing page | `index.html`, `css/style.css`, `js/main.js` | — |
| Multi-step registration form | `register.html`, `js/register.js` | `POST /api/auth/register` |
| Applicant login | `login.html`, `js/login.js` | `POST /api/auth/login` |
| Forgot password (UI) | `forgot-password.html`, `js/forgot-password.js` | — (UI stub) |
| Applicant dashboard | `dashboard.html`, `css/dashboard.css`, `js/dashboard.js` | `GET/PUT /api/student/*` |
| Profile completion (step 1) | | `PUT /api/student/profile` |
| Education history (step 2) | | `PUT /api/student/education-history` |
| Document upload (step 3) | | `POST /api/student/upload-document` (multer, ≤5 MB, jpg/png/pdf) |
| Submit application (step 4) | | `PUT /api/student/submit` |

### 4.2 Admin Side
| Feature | File(s) | Backend |
|---|---|---|
| Admin login | `admin-login.html`, `js/admin-login.js` | manual credential gate (front-end) + JWT on real API |
| Admin dashboard (stats, charts) | `admin-dashboard.html`, `css/admin-dashboard.css`, `js/admin-dashboard.js` | `GET /api/admin/dashboard/stats` |
| Student management (search/filter) | | `GET /api/admin/students?search&status&jurusan` |
| Document verification queue | | `GET /api/admin/documents?filter=waiting\|verified\|all` |
| Document preview | | `GET /api/admin/documents/:id/file` |
| Approve/reject document | | `PUT /api/admin/documents/:id/verify` |
| Approve/reject application | | `PUT /api/admin/students/:id/status` |

### 4.3 Cross-cutting / Non-functional
- **Security:** `helmet` (secure headers), CORS, 15-min/100-req rate limiting, bcrypt password hashing, JWT auth, path-traversal guard on file serving, 10 MB body limit.
- **Validation:** server-side `express-validator` rules on register/login (email format, password ≥6 chars, required fields, valid `jurusan`).
- **Audit trail:** `verified_by`, `updated_by`, and timestamps record who acted and when.
- **Health check:** `GET /health` for monitoring.

---

## 5. API Endpoint Reference

| Method | Path | Auth | Purpose |
|---|---|---|---|
| POST | `/api/auth/register` | Public | Create student + profile + initial status (transactional) |
| POST | `/api/auth/login` | Public | Authenticate, return JWT |
| GET | `/api/student/profile` | Student | Read own user/profile/docs/status |
| PUT | `/api/student/profile` | Student | Update profile (step 1) |
| PUT | `/api/student/education-history` | Student | Mark step 2 complete |
| POST | `/api/student/upload-document` | Student | Upload file (step 3) |
| PUT | `/api/student/submit` | Student | Submit application (step 4) |
| GET | `/api/admin/students` | Admin | List/search students |
| GET | `/api/admin/students/:id/documents` | Admin | Documents for one student |
| GET | `/api/admin/documents` | Admin | Aggregated verification queue |
| GET | `/api/admin/documents/:id/file` | Admin | Serve uploaded file (traversal-safe) |
| PUT | `/api/admin/documents/:id/verify` | Admin | Verify/reject document |
| PUT | `/api/admin/students/:id/status` | Admin | Approve/reject application |
| GET | `/api/admin/dashboard/stats` | Admin | Aggregate counts |
| GET | `/health` | Public | Liveness probe |

---

## 6. Logical Data Flow: Registration → Final Approval

### 6.1 Phase 1 — Applicant Registration (Automated Capture & Persistence)
1. Applicant submits `register.html`. `js/register.js` validates client-side and `POST`s to `/api/auth/register`.
2. `routes/auth.js` validates with `express-validator`; rejects on invalid input or duplicate email.
3. Password is bcrypt-hashed. A **transaction** inserts three rows atomically:
   - `users` (role=`student`, hashed password)
   - `profiles` (full_name, phone, address, asal_sekolah, jurusan)
   - `registration_status` (status=`draft`, last_step_completed=0)
4. On success a JWT (role=`student`) is returned; the account is created with status `draft`/`pending`.

### 6.2 Phase 2 — Application Completion (Multi-step Wizard)
Driven by `js/dashboard.js` against `/api/student/*`, advancing `last_step_completed`:
- **Step 1** `PUT /profile` → updates `profiles`, sets `last_step_completed ≥ 1`.
- **Step 2** `PUT /education-history` → sets `last_step_completed ≥ 2`.
- **Step 3** `POST /upload-document` (multer) → inserts `documents` rows (`is_verified=0`), sets `last_step_completed ≥ 3`.
- **Step 4** `PUT /submit` → `registration_status.status = 'submitted'`, `last_step_completed = 4`. Mapped to `profiles.status_pendaftaran = 'processing'`.

### 6.3 Phase 3 — Admin Review & Approval Workflow
Initiated from `admin-dashboard.html` (`js/admin-dashboard.js`) after admin login.

1. **Queue:** `GET /api/admin/documents?filter=waiting` returns applicants grouped by student with their pending documents.
2. **Preview:** `GET /api/admin/documents/:id/file` lets the admin inspect each file.
3. **Per-document decision:** `PUT /api/admin/documents/:id/verify` with `{ is_verified, admin_note }` updates `documents` (`is_verified`, `admin_note`, `verified_by`, `verified_at`).
4. **Final decision:** Once all required documents are verified, admin issues `PUT /api/admin/students/:id/status` with `{ status: 'approved' | 'rejected', admin_comment }`.
   - `approved` → `registration_status.status='approved'` and `profiles.status_pendaftaran='accepted'`; NIM is generated.
   - `rejected` → `registration_status.status='rejected'` and `profiles.status_pendaftaran='rejected'`.
5. **Outcome reflected** to the applicant in their dashboard via `GET /api/student/profile`.

### 6.4 End-to-End Sequence

```
Applicant            Front-end            API                MySQL
   │  fill form         │                   │                  │
   ├──────────────────► │ POST /register    │                  │
   │                    ├─────────────────► │ BEGIN TX         │
   │                    │                   ├─ insert users    ├─►
   │                    │                   ├─ insert profiles ├─►
   │                    │                   ├─ insert reg_stat ├─►
   │                    │                   │ COMMIT           │
   │                    │ ◄── JWT ──────────┤                  │
   ├─ steps 1-3 ──────► │ PUT profile /     │                  │
   │                    │ upload-document   ├─ UPDATE profiles ├─►
   │                    │                   ├─ INSERT documents├─►
   ├─ submit ─────────► │ PUT /submit       ├─ reg_stat=subm.  ├─►
   │                    │                   │ (status=processing)
   │              (admin logs in, opens dashboard)
   Admin               admin-dashboard      │                  │
   ├─ review queue ──► │ GET /documents?    ├─ SELECT docs     ├─►
   ├─ preview file ──► │ GET /documents/:id/file (traversal-safe)
   ├─ verify docs ──► │ PUT /documents/:id/verify ├─ UPDATE docs ├─►
   ├─ approve/reject ► │ PUT /students/:id/status            │
   │                    │                   ├─ UPDATE profiles ├─► (accepted/rejected)
   │                    │                   ├─ UPDATE reg_stat ├─►
   │  (applicant sees final status via GET /student/profile)
```

---

## 7. Front-end Data Layer & Going Live

The admin dashboard currently runs in **simulation mode** using `localStorage` (`getDocs()`, `setDocDecision()`, `setStudentRegistration()`, `seedDocuments()`) so it is interactive without a database. To connect the real backend:

1. Run migration: execute `backend/config/database.sql` in MySQL.
2. `cd backend && npm install && npm start` (set a strong `JWT_SECRET` in `backend/.env`).
3. Replace the localStorage data layer in `js/admin-dashboard.js` with `fetch` calls to the §5 endpoints (pattern already sketched in `DOCUMENT_APPROVAL_GUIDE.md` §4), sending `Authorization: Bearer <adminToken>`.

---

## 8. Open Items / Recommendations
- Regenerate the seeded `super_admin` bcrypt hash; current value is a placeholder.
- Implement real `forgot-password` flow (currently UI-only).
- Add an `education_history` table (step 2 is currently only tracked via `last_step_completed`, no persisted data).
- Persist/admin-generate the NIM in the database rather than only client-side.
- Enforce document **completeness** before allowing application approval (server-side guard).
- Move JWT to http-only cookies for XSS resistance if the Bearer pattern is dropped.
